diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | ad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /chrome | |
parent | 15477ed9af4859dacb069040b5d4de600803d3bc (diff) | |
download | uxp-ad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'chrome')
46 files changed, 4320 insertions, 0 deletions
diff --git a/chrome/RegistryMessageUtils.h b/chrome/RegistryMessageUtils.h new file mode 100644 index 0000000000..a9dc813314 --- /dev/null +++ b/chrome/RegistryMessageUtils.h @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_RegistryMessageUtils_h +#define mozilla_RegistryMessageUtils_h + +#include "ipc/IPCMessageUtils.h" +#include "nsString.h" + +struct SerializedURI +{ + nsCString spec; + nsCString charset; + + bool operator ==(const SerializedURI& rhs) const + { + return spec.Equals(rhs.spec) && + charset.Equals(rhs.charset); + } +}; + +struct ChromePackage +{ + nsCString package; + SerializedURI contentBaseURI; + SerializedURI localeBaseURI; + SerializedURI skinBaseURI; + uint32_t flags; + + bool operator ==(const ChromePackage& rhs) const + { + return package.Equals(rhs.package) && + contentBaseURI == rhs.contentBaseURI && + localeBaseURI == rhs.localeBaseURI && + skinBaseURI == rhs.skinBaseURI && + flags == rhs.flags; + } +}; + +struct SubstitutionMapping +{ + nsCString scheme; + nsCString path; + SerializedURI resolvedURI; + + bool operator ==(const SubstitutionMapping& rhs) const + { + return scheme.Equals(rhs.scheme) && + path.Equals(rhs.path) && + resolvedURI == rhs.resolvedURI; + } +}; + +struct OverrideMapping +{ + SerializedURI originalURI; + SerializedURI overrideURI; + + bool operator==(const OverrideMapping& rhs) const + { + return originalURI == rhs.originalURI && + overrideURI == rhs.overrideURI; + } +}; + +namespace IPC { + +template<> +struct ParamTraits<SerializedURI> +{ + typedef SerializedURI paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.spec); + WriteParam(aMsg, aParam.charset); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + nsCString spec, charset; + if (ReadParam(aMsg, aIter, &spec) && + ReadParam(aMsg, aIter, &charset)) { + aResult->spec = spec; + aResult->charset = charset; + return true; + } + return false; + } +}; + +template <> +struct ParamTraits<ChromePackage> +{ + typedef ChromePackage paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.package); + WriteParam(aMsg, aParam.contentBaseURI); + WriteParam(aMsg, aParam.localeBaseURI); + WriteParam(aMsg, aParam.skinBaseURI); + WriteParam(aMsg, aParam.flags); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + nsCString package; + SerializedURI contentBaseURI, localeBaseURI, skinBaseURI; + uint32_t flags; + + if (ReadParam(aMsg, aIter, &package) && + ReadParam(aMsg, aIter, &contentBaseURI) && + ReadParam(aMsg, aIter, &localeBaseURI) && + ReadParam(aMsg, aIter, &skinBaseURI) && + ReadParam(aMsg, aIter, &flags)) { + aResult->package = package; + aResult->contentBaseURI = contentBaseURI; + aResult->localeBaseURI = localeBaseURI; + aResult->skinBaseURI = skinBaseURI; + aResult->flags = flags; + return true; + } + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(StringPrintf(L"[%s, %s, %s, %s, %u]", aParam.package.get(), + aParam.contentBaseURI.spec.get(), + aParam.localeBaseURI.spec.get(), + aParam.skinBaseURI.spec.get(), aParam.flags)); + } +}; + +template <> +struct ParamTraits<SubstitutionMapping> +{ + typedef SubstitutionMapping paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.scheme); + WriteParam(aMsg, aParam.path); + WriteParam(aMsg, aParam.resolvedURI); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + nsCString scheme, path; + SerializedURI resolvedURI; + + if (ReadParam(aMsg, aIter, &scheme) && + ReadParam(aMsg, aIter, &path) && + ReadParam(aMsg, aIter, &resolvedURI)) { + aResult->scheme = scheme; + aResult->path = path; + aResult->resolvedURI = resolvedURI; + return true; + } + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(StringPrintf(L"[%s://%s, %s, %u]", + aParam.scheme.get(), + aParam.path.get(), + aParam.resolvedURI.spec.get())); + } +}; + +template <> +struct ParamTraits<OverrideMapping> +{ + typedef OverrideMapping paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.originalURI); + WriteParam(aMsg, aParam.overrideURI); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + SerializedURI originalURI; + SerializedURI overrideURI; + + if (ReadParam(aMsg, aIter, &originalURI) && + ReadParam(aMsg, aIter, &overrideURI)) { + aResult->originalURI = originalURI; + aResult->overrideURI = overrideURI; + return true; + } + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(StringPrintf(L"[%s, %s, %u]", aParam.originalURI.spec.get(), + aParam.overrideURI.spec.get())); + } +}; + +} // namespace IPC + +#endif // RegistryMessageUtils_h diff --git a/chrome/moz.build b/chrome/moz.build new file mode 100644 index 0000000000..b75e9435d4 --- /dev/null +++ b/chrome/moz.build @@ -0,0 +1,40 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +TEST_DIRS += ['test'] + +XPIDL_SOURCES += [ + 'nsIChromeRegistry.idl', + 'nsIToolkitChromeRegistry.idl', +] + +XPIDL_MODULE = 'chrome' + +EXPORTS.mozilla.chrome += [ + 'RegistryMessageUtils.h', +] + +UNIFIED_SOURCES += [ + 'nsChromeProtocolHandler.cpp', + 'nsChromeRegistry.cpp', + 'nsChromeRegistryChrome.cpp', + 'nsChromeRegistryContent.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '!/xpcom', + '/dom/base', + '/netwerk/base', + '/netwerk/protocol/res', + '/xpcom/components' +] + +if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: + CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/chrome/nsChromeProtocolHandler.cpp b/chrome/nsChromeProtocolHandler.cpp new file mode 100644 index 0000000000..f66c6d362f --- /dev/null +++ b/chrome/nsChromeProtocolHandler.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et cin: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + A protocol handler for ``chrome:'' + +*/ + +#include "nsChromeProtocolHandler.h" +#include "nsChromeRegistry.h" +#include "nsCOMPtr.h" +#include "nsThreadUtils.h" +#include "nsIChannel.h" +#include "nsIChromeRegistry.h" +#include "nsIFile.h" +#include "nsIFileChannel.h" +#include "nsIIOService.h" +#include "nsILoadGroup.h" +#include "nsIScriptSecurityManager.h" +#include "nsIStandardURL.h" +#include "nsNetUtil.h" +#include "nsNetCID.h" +#include "nsIURL.h" +#include "nsString.h" +#include "nsStandardURL.h" + +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS(nsChromeProtocolHandler, + nsIProtocolHandler, + nsISupportsWeakReference) + +//////////////////////////////////////////////////////////////////////////////// +// nsIProtocolHandler methods: + +NS_IMETHODIMP +nsChromeProtocolHandler::GetScheme(nsACString &result) +{ + result.AssignLiteral("chrome"); + return NS_OK; +} + +NS_IMETHODIMP +nsChromeProtocolHandler::GetDefaultPort(int32_t *result) +{ + *result = -1; // no port for chrome: URLs + return NS_OK; +} + +NS_IMETHODIMP +nsChromeProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) +{ + // don't override anything. + *_retval = false; + return NS_OK; +} + +NS_IMETHODIMP +nsChromeProtocolHandler::GetProtocolFlags(uint32_t *result) +{ + *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE; + return NS_OK; +} + +NS_IMETHODIMP +nsChromeProtocolHandler::NewURI(const nsACString &aSpec, + const char *aCharset, + nsIURI *aBaseURI, + nsIURI **result) +{ + + // Chrome: URLs (currently) have no additional structure beyond that provided + // by standard URLs, so there is no "outer" given to CreateInstance + + RefPtr<mozilla::net::nsStandardURL> surl = new mozilla::net::nsStandardURL(); + + nsresult rv = surl->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec, + aCharset, aBaseURI); + if (NS_FAILED(rv)) + return rv; + + // Canonify the "chrome:" URL; e.g., so that we collapse + // "chrome://navigator/content/" and "chrome://navigator/content" + // and "chrome://navigator/content/navigator.xul". + + rv = nsChromeRegistry::Canonify(surl); + if (NS_FAILED(rv)) + return rv; + + surl->SetMutable(false); + + surl.forget(result); + return NS_OK; +} + +NS_IMETHODIMP +nsChromeProtocolHandler::NewChannel2(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIChannel** aResult) +{ + nsresult rv; + + NS_ENSURE_ARG_POINTER(aURI); + NS_PRECONDITION(aResult, "Null out param"); + +#ifdef DEBUG + // Check that the uri we got is already canonified + nsresult debug_rv; + nsCOMPtr<nsIURI> debugClone; + debug_rv = aURI->Clone(getter_AddRefs(debugClone)); + if (NS_SUCCEEDED(debug_rv)) { + nsCOMPtr<nsIURL> debugURL (do_QueryInterface(debugClone)); + debug_rv = nsChromeRegistry::Canonify(debugURL); + if (NS_SUCCEEDED(debug_rv)) { + bool same; + debug_rv = aURI->Equals(debugURL, &same); + if (NS_SUCCEEDED(debug_rv)) { + NS_ASSERTION(same, "Non-canonified chrome uri passed to nsChromeProtocolHandler::NewChannel!"); + } + } + } +#endif + + nsCOMPtr<nsIChannel> result; + + if (!nsChromeRegistry::gChromeRegistry) { + // We don't actually want this ref, we just want the service to + // initialize if it hasn't already. + nsCOMPtr<nsIChromeRegistry> reg = + mozilla::services::GetChromeRegistryService(); + NS_ENSURE_TRUE(nsChromeRegistry::gChromeRegistry, NS_ERROR_FAILURE); + } + + nsCOMPtr<nsIURI> resolvedURI; + rv = nsChromeRegistry::gChromeRegistry->ConvertChromeURL(aURI, getter_AddRefs(resolvedURI)); + if (NS_FAILED(rv)) { +#ifdef DEBUG + printf("Couldn't convert chrome URL: %s\n", + aURI->GetSpecOrDefault().get()); +#endif + return rv; + } + + rv = NS_NewChannelInternal(getter_AddRefs(result), + resolvedURI, + aLoadInfo); + NS_ENSURE_SUCCESS(rv, rv); + +#ifdef DEBUG + nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(result)); + if (fileChan) { + nsCOMPtr<nsIFile> file; + fileChan->GetFile(getter_AddRefs(file)); + + bool exists = false; + file->Exists(&exists); + if (!exists) { + nsAutoCString path; + file->GetNativePath(path); + printf("Chrome file doesn't exist: %s\n", path.get()); + } + } +#endif + + // Make sure that the channel remembers where it was + // originally loaded from. + nsLoadFlags loadFlags = 0; + result->GetLoadFlags(&loadFlags); + result->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE); + rv = result->SetOriginalURI(aURI); + if (NS_FAILED(rv)) return rv; + + // Get a system principal for content files and set the owner + // property of the result + nsCOMPtr<nsIURL> url = do_QueryInterface(aURI); + nsAutoCString path; + rv = url->GetPath(path); + if (StringBeginsWith(path, NS_LITERAL_CSTRING("/content/"))) + { + nsCOMPtr<nsIScriptSecurityManager> securityManager = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIPrincipal> principal; + rv = securityManager->GetSystemPrincipal(getter_AddRefs(principal)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsISupports> owner = do_QueryInterface(principal); + result->SetOwner(owner); + } + + // XXX Removed dependency-tracking code from here, because we're not + // tracking them anyways (with fastload we checked only in DEBUG + // and with startupcache not at all), but this is where we would start + // if we need to re-add. + // See bug 531886, bug 533038. + result->SetContentCharset(NS_LITERAL_CSTRING("UTF-8")); + + *aResult = result; + NS_ADDREF(*aResult); + return NS_OK; +} + +NS_IMETHODIMP +nsChromeProtocolHandler::NewChannel(nsIURI* aURI, + nsIChannel* *aResult) +{ + return NewChannel2(aURI, nullptr, aResult); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/nsChromeProtocolHandler.h b/chrome/nsChromeProtocolHandler.h new file mode 100644 index 0000000000..42c3f52be0 --- /dev/null +++ b/chrome/nsChromeProtocolHandler.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsChromeProtocolHandler_h___ +#define nsChromeProtocolHandler_h___ + +#include "nsIProtocolHandler.h" +#include "nsWeakReference.h" +#include "mozilla/Attributes.h" + +#define NS_CHROMEPROTOCOLHANDLER_CID \ +{ /* 61ba33c0-3031-11d3-8cd0-0060b0fc14a3 */ \ + 0x61ba33c0, \ + 0x3031, \ + 0x11d3, \ + {0x8c, 0xd0, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ +} + +class nsChromeProtocolHandler final : public nsIProtocolHandler, + public nsSupportsWeakReference +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + // nsIProtocolHandler methods: + NS_DECL_NSIPROTOCOLHANDLER + + // nsChromeProtocolHandler methods: + nsChromeProtocolHandler() {} + +private: + ~nsChromeProtocolHandler() {} +}; + +#endif /* nsChromeProtocolHandler_h___ */ diff --git a/chrome/nsChromeRegistry.cpp b/chrome/nsChromeRegistry.cpp new file mode 100644 index 0000000000..0aa7f3f149 --- /dev/null +++ b/chrome/nsChromeRegistry.cpp @@ -0,0 +1,744 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=78: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsChromeRegistry.h" +#include "nsChromeRegistryChrome.h" +#include "nsChromeRegistryContent.h" + +#include "prprf.h" + +#include "nsCOMPtr.h" +#include "nsError.h" +#include "nsEscape.h" +#include "nsNetUtil.h" +#include "nsString.h" +#include "nsQueryObject.h" + +#include "mozilla/dom/URL.h" +#include "nsIConsoleService.h" +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIDOMLocation.h" +#include "nsIDOMWindowCollection.h" +#include "nsIDOMWindow.h" +#include "nsIObserverService.h" +#include "nsIPresShell.h" +#include "nsIScriptError.h" +#include "nsIWindowMediator.h" +#include "nsIPrefService.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" + +#ifdef ENABLE_INTL_API +#include "unicode/uloc.h" +#endif + +nsChromeRegistry* nsChromeRegistry::gChromeRegistry; + +// DO NOT use namespace mozilla; it'll break due to a naming conflict between +// mozilla::TextRange and a TextRange in OSX headers. +using mozilla::StyleSheet; +using mozilla::dom::IsChromeURI; + +//////////////////////////////////////////////////////////////////////////////// + +void +nsChromeRegistry::LogMessage(const char* aMsg, ...) +{ + nsCOMPtr<nsIConsoleService> console + (do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + if (!console) + return; + + va_list args; + va_start(args, aMsg); + char* formatted = PR_vsmprintf(aMsg, args); + va_end(args); + if (!formatted) + return; + + console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted).get()); + PR_smprintf_free(formatted); +} + +void +nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber, uint32_t flags, + const char* aMsg, ...) +{ + nsresult rv; + + nsCOMPtr<nsIConsoleService> console + (do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + + nsCOMPtr<nsIScriptError> error + (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); + if (!console || !error) + return; + + va_list args; + va_start(args, aMsg); + char* formatted = PR_vsmprintf(aMsg, args); + va_end(args); + if (!formatted) + return; + + nsCString spec; + if (aURL) + aURL->GetSpec(spec); + + rv = error->Init(NS_ConvertUTF8toUTF16(formatted), + NS_ConvertUTF8toUTF16(spec), + EmptyString(), + aLineNumber, 0, flags, "chrome registration"); + PR_smprintf_free(formatted); + + if (NS_FAILED(rv)) + return; + + console->LogMessage(error); +} + +nsChromeRegistry::~nsChromeRegistry() +{ + gChromeRegistry = nullptr; +} + +NS_INTERFACE_MAP_BEGIN(nsChromeRegistry) + NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry) + NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry) + NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry) +#ifdef MOZ_XUL + NS_INTERFACE_MAP_ENTRY(nsIXULOverlayProvider) +#endif + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChromeRegistry) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsChromeRegistry) +NS_IMPL_RELEASE(nsChromeRegistry) + +//////////////////////////////////////////////////////////////////////////////// +// nsIChromeRegistry methods: + +already_AddRefed<nsIChromeRegistry> +nsChromeRegistry::GetService() +{ + if (!gChromeRegistry) + { + // We don't actually want this ref, we just want the service to + // initialize if it hasn't already. + nsCOMPtr<nsIChromeRegistry> reg( + do_GetService(NS_CHROMEREGISTRY_CONTRACTID)); + if (!gChromeRegistry) + return nullptr; + } + nsCOMPtr<nsIChromeRegistry> registry = gChromeRegistry; + return registry.forget(); +} + +nsresult +nsChromeRegistry::Init() +{ + // This initialization process is fairly complicated and may cause reentrant + // getservice calls to resolve chrome URIs (especially locale files). We + // don't want that, so we inform the protocol handler about our existence + // before we are actually fully initialized. + gChromeRegistry = this; + + mInitialized = true; + + return NS_OK; +} + +nsresult +nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL, + nsACString& aProvider, nsACString& aPath) +{ + nsresult rv; + +#ifdef DEBUG + bool isChrome; + aChromeURL->SchemeIs("chrome", &isChrome); + NS_ASSERTION(isChrome, "Non-chrome URI?"); +#endif + + nsAutoCString path; + rv = aChromeURL->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + if (path.Length() < 3) { + LogMessage("Invalid chrome URI: %s", path.get()); + return NS_ERROR_FAILURE; + } + + path.SetLength(nsUnescapeCount(path.BeginWriting())); + NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!"); + + int32_t slash = path.FindChar('/', 1); + if (slash == 1) { + LogMessage("Invalid chrome URI: %s", path.get()); + return NS_ERROR_FAILURE; + } + + if (slash == -1) { + aPath.Truncate(); + } + else { + if (slash == (int32_t) path.Length() - 1) + aPath.Truncate(); + else + aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1); + + --slash; + } + + aProvider.Assign(path.get() + 1, slash); + return NS_OK; +} + + +nsresult +nsChromeRegistry::Canonify(nsIURL* aChromeURL) +{ + NS_NAMED_LITERAL_CSTRING(kSlash, "/"); + + nsresult rv; + + nsAutoCString provider, path; + rv = GetProviderAndPath(aChromeURL, provider, path); + NS_ENSURE_SUCCESS(rv, rv); + + if (path.IsEmpty()) { + nsAutoCString package; + rv = aChromeURL->GetHost(package); + NS_ENSURE_SUCCESS(rv, rv); + + // we re-use the "path" local string to build a new URL path + path.Assign(kSlash + provider + kSlash + package); + if (provider.EqualsLiteral("content")) { + path.AppendLiteral(".xul"); + } + else if (provider.EqualsLiteral("locale")) { + path.AppendLiteral(".dtd"); + } + else if (provider.EqualsLiteral("skin")) { + path.AppendLiteral(".css"); + } + else { + return NS_ERROR_INVALID_ARG; + } + aChromeURL->SetPath(path); + } + else { + // prevent directory traversals ("..") + // path is already unescaped once, but uris can get unescaped twice + const char* pos = path.BeginReading(); + const char* end = path.EndReading(); + while (pos < end) { + switch (*pos) { + case ':': + return NS_ERROR_DOM_BAD_URI; + case '.': + if (pos[1] == '.') + return NS_ERROR_DOM_BAD_URI; + break; + case '%': + // chrome: URIs with double-escapes are trying to trick us. + // watch for %2e, and %25 in case someone triple unescapes + if (pos[1] == '2' && + ( pos[2] == 'e' || pos[2] == 'E' || + pos[2] == '5' )) + return NS_ERROR_DOM_BAD_URI; + break; + case '?': + case '#': + pos = end; + continue; + } + ++pos; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult) +{ + nsresult rv; + if (NS_WARN_IF(!aChromeURI)) { + return NS_ERROR_INVALID_ARG; + } + + if (mOverrideTable.Get(aChromeURI, aResult)) + return NS_OK; + + nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aChromeURI)); + NS_ENSURE_TRUE(chromeURL, NS_NOINTERFACE); + + nsAutoCString package, provider, path; + rv = chromeURL->GetHostPort(package); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetProviderAndPath(chromeURL, provider, path); + NS_ENSURE_SUCCESS(rv, rv); + + nsIURI* baseURI = GetBaseURIFromPackage(package, provider, path); + + uint32_t flags; + rv = GetFlagsFromPackage(package, &flags); + if (NS_FAILED(rv)) + return rv; + + if (flags & PLATFORM_PACKAGE) { +#if defined(XP_WIN) + path.Insert("win/", 0); +#elif defined(XP_MACOSX) + path.Insert("mac/", 0); +#else + path.Insert("unix/", 0); +#endif + } + + if (!baseURI) { + LogMessage("No chrome package registered for chrome://%s/%s/%s", + package.get(), provider.get(), path.get()); + return NS_ERROR_FILE_NOT_FOUND; + } + + return NS_NewURI(aResult, path, nullptr, baseURI); +} + +//////////////////////////////////////////////////////////////////////// + +// theme stuff + + +static void FlushSkinBindingsForWindow(nsPIDOMWindowOuter* aWindow) +{ + // Get the document. + nsCOMPtr<nsIDocument> document = aWindow->GetDoc(); + if (!document) + return; + + // Annihilate all XBL bindings. + document->FlushSkinBindings(); +} + +// XXXbsmedberg: move this to nsIWindowMediator +NS_IMETHODIMP nsChromeRegistry::RefreshSkins() +{ + nsCOMPtr<nsIWindowMediator> windowMediator + (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (!windowMediator) + return NS_OK; + + nsCOMPtr<nsISimpleEnumerator> windowEnumerator; + windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); + bool more; + windowEnumerator->HasMoreElements(&more); + while (more) { + nsCOMPtr<nsISupports> protoWindow; + windowEnumerator->GetNext(getter_AddRefs(protoWindow)); + if (protoWindow) { + nsCOMPtr<nsPIDOMWindowOuter> domWindow = do_QueryInterface(protoWindow); + if (domWindow) + FlushSkinBindingsForWindow(domWindow); + } + windowEnumerator->HasMoreElements(&more); + } + + FlushSkinCaches(); + + windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); + windowEnumerator->HasMoreElements(&more); + while (more) { + nsCOMPtr<nsISupports> protoWindow; + windowEnumerator->GetNext(getter_AddRefs(protoWindow)); + if (protoWindow) { + nsCOMPtr<nsPIDOMWindowOuter> domWindow = do_QueryInterface(protoWindow); + if (domWindow) + RefreshWindow(domWindow); + } + windowEnumerator->HasMoreElements(&more); + } + + return NS_OK; +} + +void +nsChromeRegistry::FlushSkinCaches() +{ + nsCOMPtr<nsIObserverService> obsSvc = + mozilla::services::GetObserverService(); + NS_ASSERTION(obsSvc, "Couldn't get observer service."); + + obsSvc->NotifyObservers(static_cast<nsIChromeRegistry*>(this), + NS_CHROME_FLUSH_SKINS_TOPIC, nullptr); +} + +// XXXbsmedberg: move this to windowmediator +nsresult nsChromeRegistry::RefreshWindow(nsPIDOMWindowOuter* aWindow) +{ + // Deal with our subframes first. + nsCOMPtr<nsIDOMWindowCollection> frames = aWindow->GetFrames(); + uint32_t length; + frames->GetLength(&length); + uint32_t j; + for (j = 0; j < length; j++) { + nsCOMPtr<mozIDOMWindowProxy> childWin; + frames->Item(j, getter_AddRefs(childWin)); + nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(childWin); + RefreshWindow(piWindow); + } + + nsresult rv; + // Get the document. + nsCOMPtr<nsIDocument> document = aWindow->GetDoc(); + if (!document) + return NS_OK; + + // Deal with the agent sheets first. Have to do all the style sets by hand. + nsCOMPtr<nsIPresShell> shell = document->GetShell(); + if (shell) { + // Reload only the chrome URL agent style sheets. + nsTArray<RefPtr<StyleSheet>> agentSheets; + rv = shell->GetAgentStyleSheets(agentSheets); + NS_ENSURE_SUCCESS(rv, rv); + + nsTArray<RefPtr<StyleSheet>> newAgentSheets; + for (StyleSheet* sheet : agentSheets) { + nsIURI* uri = sheet->GetSheetURI(); + + if (IsChromeURI(uri)) { + // Reload the sheet. + RefPtr<StyleSheet> newSheet; + rv = document->LoadChromeSheetSync(uri, true, &newSheet); + if (NS_FAILED(rv)) return rv; + if (newSheet) { + rv = newAgentSheets.AppendElement(newSheet) ? NS_OK : NS_ERROR_FAILURE; + if (NS_FAILED(rv)) return rv; + } + } + else { // Just use the same sheet. + rv = newAgentSheets.AppendElement(sheet) ? NS_OK : NS_ERROR_FAILURE; + if (NS_FAILED(rv)) return rv; + } + } + + rv = shell->SetAgentStyleSheets(newAgentSheets); + NS_ENSURE_SUCCESS(rv, rv); + } + + int32_t count = document->GetNumberOfStyleSheets(); + + // Build an array of style sheets we need to reload. + nsTArray<RefPtr<StyleSheet>> oldSheets(count); + nsTArray<RefPtr<StyleSheet>> newSheets(count); + + // Iterate over the style sheets. + for (int32_t i = 0; i < count; i++) { + // Get the style sheet + StyleSheet* styleSheet = document->GetStyleSheetAt(i); + oldSheets.AppendElement(styleSheet); + } + + // Iterate over our old sheets and kick off a sync load of the new + // sheet if and only if it's a non-inline sheet with a chrome URL. + for (StyleSheet* sheet : oldSheets) { + MOZ_ASSERT(sheet, "GetStyleSheetAt shouldn't return nullptr for " + "in-range sheet indexes"); + nsIURI* uri = sheet->GetSheetURI(); + + if (!sheet->IsInline() && IsChromeURI(uri)) { + // Reload the sheet. + RefPtr<StyleSheet> newSheet; + // XXX what about chrome sheets that have a title or are disabled? This + // only works by sheer dumb luck. + document->LoadChromeSheetSync(uri, false, &newSheet); + // Even if it's null, we put in in there. + newSheets.AppendElement(newSheet); + } else { + // Just use the same sheet. + newSheets.AppendElement(sheet); + } + } + + // Now notify the document that multiple sheets have been added and removed. + document->UpdateStyleSheets(oldSheets, newSheets); + return NS_OK; +} + +void +nsChromeRegistry::FlushAllCaches() +{ + nsCOMPtr<nsIObserverService> obsSvc = + mozilla::services::GetObserverService(); + NS_ASSERTION(obsSvc, "Couldn't get observer service."); + + obsSvc->NotifyObservers((nsIChromeRegistry*) this, + NS_CHROME_FLUSH_TOPIC, nullptr); +} + +// xxxbsmedberg Move me to nsIWindowMediator +NS_IMETHODIMP +nsChromeRegistry::ReloadChrome() +{ + UpdateSelectedLocale(); + FlushAllCaches(); + // Do a reload of all top level windows. + nsresult rv = NS_OK; + + // Get the window mediator + nsCOMPtr<nsIWindowMediator> windowMediator + (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (windowMediator) { + nsCOMPtr<nsISimpleEnumerator> windowEnumerator; + + rv = windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); + if (NS_SUCCEEDED(rv)) { + // Get each dom window + bool more; + rv = windowEnumerator->HasMoreElements(&more); + if (NS_FAILED(rv)) return rv; + while (more) { + nsCOMPtr<nsISupports> protoWindow; + rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsPIDOMWindowOuter> domWindow = do_QueryInterface(protoWindow); + if (domWindow) { + nsIDOMLocation* location = domWindow->GetLocation(); + if (location) { + rv = location->Reload(false); + if (NS_FAILED(rv)) return rv; + } + } + } + rv = windowEnumerator->HasMoreElements(&more); + if (NS_FAILED(rv)) return rv; + } + } + } + return rv; +} + +NS_IMETHODIMP +nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, bool *aResult) +{ + nsresult rv; + *aResult = false; + +#ifdef DEBUG + bool isChrome; + aChromeURI->SchemeIs("chrome", &isChrome); + NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!"); +#endif + + nsCOMPtr<nsIURL> url (do_QueryInterface(aChromeURI)); + NS_ENSURE_TRUE(url, NS_NOINTERFACE); + + nsAutoCString provider, file; + rv = GetProviderAndPath(url, provider, file); + NS_ENSURE_SUCCESS(rv, rv); + + if (!provider.EqualsLiteral("skin")) + *aResult = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistry::AllowContentToAccess(nsIURI *aURI, bool *aResult) +{ + nsresult rv; + + *aResult = false; + +#ifdef DEBUG + bool isChrome; + aURI->SchemeIs("chrome", &isChrome); + NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowContentToAccess!"); +#endif + + nsCOMPtr<nsIURL> url = do_QueryInterface(aURI); + if (!url) { + NS_ERROR("Chrome URL doesn't implement nsIURL."); + return NS_ERROR_UNEXPECTED; + } + + nsAutoCString package; + rv = url->GetHostPort(package); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t flags; + rv = GetFlagsFromPackage(package, &flags); + + if (NS_SUCCEEDED(rv)) { + *aResult = !!(flags & CONTENT_ACCESSIBLE); + } + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistry::CanLoadURLRemotely(nsIURI *aURI, bool *aResult) +{ + nsresult rv; + + *aResult = false; + +#ifdef DEBUG + bool isChrome; + aURI->SchemeIs("chrome", &isChrome); + NS_ASSERTION(isChrome, "Non-chrome URI passed to CanLoadURLRemotely!"); +#endif + + nsCOMPtr<nsIURL> url = do_QueryInterface(aURI); + if (!url) { + NS_ERROR("Chrome URL doesn't implement nsIURL."); + return NS_ERROR_UNEXPECTED; + } + + nsAutoCString package; + rv = url->GetHostPort(package); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t flags; + rv = GetFlagsFromPackage(package, &flags); + + if (NS_SUCCEEDED(rv)) { + *aResult = !!(flags & REMOTE_ALLOWED); + } + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistry::MustLoadURLRemotely(nsIURI *aURI, bool *aResult) +{ + nsresult rv; + + *aResult = false; + +#ifdef DEBUG + bool isChrome; + aURI->SchemeIs("chrome", &isChrome); + NS_ASSERTION(isChrome, "Non-chrome URI passed to MustLoadURLRemotely!"); +#endif + + nsCOMPtr<nsIURL> url = do_QueryInterface(aURI); + if (!url) { + NS_ERROR("Chrome URL doesn't implement nsIURL."); + return NS_ERROR_UNEXPECTED; + } + + nsAutoCString package; + rv = url->GetHostPort(package); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t flags; + rv = GetFlagsFromPackage(package, &flags); + + if (NS_SUCCEEDED(rv)) { + *aResult = !!(flags & REMOTE_REQUIRED); + } + return NS_OK; +} + +bool +nsChromeRegistry::GetDirectionForLocale(const nsACString& aLocale) +{ + // first check the intl.uidirection.<locale> preference, and if that is not + // set, check the same preference but with just the first two characters of + // the locale. If that isn't set, default to left-to-right. + nsAutoCString prefString = NS_LITERAL_CSTRING("intl.uidirection.") + aLocale; + nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (!prefBranch) { + return false; + } + + nsXPIDLCString dir; + prefBranch->GetCharPref(prefString.get(), getter_Copies(dir)); + if (dir.IsEmpty()) { + int32_t hyphen = prefString.FindChar('-'); + if (hyphen >= 1) { + nsAutoCString shortPref(Substring(prefString, 0, hyphen)); + prefBranch->GetCharPref(shortPref.get(), getter_Copies(dir)); + } + } + + return dir.EqualsLiteral("rtl"); +} + +NS_IMETHODIMP_(bool) +nsChromeRegistry::WrappersEnabled(nsIURI *aURI) +{ + nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aURI)); + if (!chromeURL) + return false; + + bool isChrome = false; + nsresult rv = chromeURL->SchemeIs("chrome", &isChrome); + if (NS_FAILED(rv) || !isChrome) + return false; + + nsAutoCString package; + rv = chromeURL->GetHostPort(package); + if (NS_FAILED(rv)) + return false; + + uint32_t flags; + rv = GetFlagsFromPackage(package, &flags); + return NS_SUCCEEDED(rv) && (flags & XPCNATIVEWRAPPERS); +} + +already_AddRefed<nsChromeRegistry> +nsChromeRegistry::GetSingleton() +{ + if (gChromeRegistry) { + RefPtr<nsChromeRegistry> registry = gChromeRegistry; + return registry.forget(); + } + + RefPtr<nsChromeRegistry> cr; + if (GeckoProcessType_Content == XRE_GetProcessType()) + cr = new nsChromeRegistryContent(); + else + cr = new nsChromeRegistryChrome(); + + if (NS_FAILED(cr->Init())) + return nullptr; + + return cr.forget(); +} + +void +nsChromeRegistry::SanitizeForBCP47(nsACString& aLocale) +{ +#ifdef ENABLE_INTL_API + // Currently, the only locale code we use that's not BCP47-conformant is + // "ja-JP-mac" on OS X, but let's try to be more general than just + // hard-coding that here. + const int32_t LANG_TAG_CAPACITY = 128; + char langTag[LANG_TAG_CAPACITY]; + nsAutoCString locale(aLocale); + UErrorCode err = U_ZERO_ERROR; + // This is a fail-safe method that will set langTag to "und" if it cannot + // match any part of the input locale code. + int32_t len = uloc_toLanguageTag(locale.get(), langTag, LANG_TAG_CAPACITY, + false, &err); + if (U_SUCCESS(err) && len > 0) { + aLocale.Assign(langTag, len); + } +#else + // This is only really needed for Intl API purposes, AFAIK, + // so probably won't be used in a non-ENABLE_INTL_API build. + // But let's fix up the single anomalous code we actually ship, + // just in case: + if (aLocale.EqualsLiteral("ja-JP-mac")) { + aLocale.AssignLiteral("ja-JP"); + } +#endif +} diff --git a/chrome/nsChromeRegistry.h b/chrome/nsChromeRegistry.h new file mode 100644 index 0000000000..f47c8e6050 --- /dev/null +++ b/chrome/nsChromeRegistry.h @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsChromeRegistry_h +#define nsChromeRegistry_h + +#include "nsIToolkitChromeRegistry.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +#ifdef MOZ_XUL +#include "nsIXULOverlayProvider.h" +#endif + +#include "nsString.h" +#include "nsURIHashKey.h" +#include "nsInterfaceHashtable.h" +#include "nsXULAppAPI.h" +#include "nsIXPConnect.h" + +#include "mozilla/FileLocation.h" + +class nsPIDOMWindowOuter; +class nsIPrefBranch; +class nsIURL; + +// The chrome registry is actually split between nsChromeRegistryChrome and +// nsChromeRegistryContent. The work/data that is common to both resides in +// the shared nsChromeRegistry implementation, with operations that only make +// sense for one side erroring out in the other. + +// for component registration +// {47049e42-1d87-482a-984d-56ae185e367a} +#define NS_CHROMEREGISTRY_CID \ +{ 0x47049e42, 0x1d87, 0x482a, { 0x98, 0x4d, 0x56, 0xae, 0x18, 0x5e, 0x36, 0x7a } } + +class nsChromeRegistry : public nsIToolkitChromeRegistry, +#ifdef MOZ_XUL + public nsIXULOverlayProvider, +#endif + public nsIObserver, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + + // nsIXULChromeRegistry methods: + NS_IMETHOD ReloadChrome() override; + NS_IMETHOD RefreshSkins() override; + NS_IMETHOD AllowScriptsForPackage(nsIURI* url, + bool* _retval) override; + NS_IMETHOD AllowContentToAccess(nsIURI* url, + bool* _retval) override; + NS_IMETHOD CanLoadURLRemotely(nsIURI* url, + bool* _retval) override; + NS_IMETHOD MustLoadURLRemotely(nsIURI* url, + bool* _retval) override; + + // nsIChromeRegistry methods: + NS_IMETHOD_(bool) WrappersEnabled(nsIURI *aURI) override; + NS_IMETHOD ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult) override; + + // nsChromeRegistry methods: + nsChromeRegistry() : mInitialized(false) { } + + virtual nsresult Init(); + + static already_AddRefed<nsIChromeRegistry> GetService(); + + static nsChromeRegistry* gChromeRegistry; + + static nsresult Canonify(nsIURL* aChromeURL); + +protected: + virtual ~nsChromeRegistry(); + + void FlushSkinCaches(); + void FlushAllCaches(); + + // Update the selected locale used by the chrome registry, and fire a + // notification about this change + virtual nsresult UpdateSelectedLocale() = 0; + + static void LogMessage(const char* aMsg, ...); + static void LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber, uint32_t flags, + const char* aMsg, ...); + + virtual nsIURI* GetBaseURIFromPackage(const nsCString& aPackage, + const nsCString& aProvider, + const nsCString& aPath) = 0; + virtual nsresult GetFlagsFromPackage(const nsCString& aPackage, + uint32_t* aFlags) = 0; + + nsresult SelectLocaleFromPref(nsIPrefBranch* prefs); + + static nsresult RefreshWindow(nsPIDOMWindowOuter* aWindow); + static nsresult GetProviderAndPath(nsIURL* aChromeURL, + nsACString& aProvider, nsACString& aPath); + + bool GetDirectionForLocale(const nsACString& aLocale); + + void SanitizeForBCP47(nsACString& aLocale); + +public: + static already_AddRefed<nsChromeRegistry> GetSingleton(); + + struct ManifestProcessingContext + { + ManifestProcessingContext(NSLocationType aType, mozilla::FileLocation &aFile) + : mType(aType) + , mFile(aFile) + { } + + ~ManifestProcessingContext() + { } + + nsIURI* GetManifestURI(); + nsIXPConnect* GetXPConnect(); + + already_AddRefed<nsIURI> ResolveURI(const char* uri); + + NSLocationType mType; + mozilla::FileLocation mFile; + nsCOMPtr<nsIURI> mManifestURI; + nsCOMPtr<nsIXPConnect> mXPConnect; + }; + + virtual void ManifestContent(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) = 0; + virtual void ManifestLocale(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) = 0; + virtual void ManifestSkin(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) = 0; + virtual void ManifestOverlay(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) = 0; + virtual void ManifestStyle(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) = 0; + virtual void ManifestOverride(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) = 0; + virtual void ManifestResource(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) = 0; + + // Available flags + enum { + // This is a "platform" package (e.g. chrome://global-platform/). + // Appends one of win/ unix/ mac/ to the base URI. + PLATFORM_PACKAGE = 1 << 0, + + // This package should use the new XPCNativeWrappers to separate + // content from chrome. This flag is currently unused (because we call + // into xpconnect at registration time). + XPCNATIVEWRAPPERS = 1 << 1, + + // Content script may access files in this package + CONTENT_ACCESSIBLE = 1 << 2, + + // Package may be loaded remotely + REMOTE_ALLOWED = 1 << 3, + + // Package must be loaded remotely + REMOTE_REQUIRED = 1 << 4, + }; + + bool mInitialized; + + // "Override" table (chrome URI string -> real URI) + nsInterfaceHashtable<nsURIHashKey, nsIURI> mOverrideTable; +}; + +#endif // nsChromeRegistry_h diff --git a/chrome/nsChromeRegistryChrome.cpp b/chrome/nsChromeRegistryChrome.cpp new file mode 100644 index 0000000000..037e866317 --- /dev/null +++ b/chrome/nsChromeRegistryChrome.cpp @@ -0,0 +1,991 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sts=2 sw=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/ContentParent.h" +#include "RegistryMessageUtils.h" +#include "nsResProtocolHandler.h" + +#include "nsChromeRegistryChrome.h" + +#if defined(XP_WIN) +#include <windows.h> +#elif defined(XP_MACOSX) +#include <CoreServices/CoreServices.h> +#endif + +#include "nsArrayEnumerator.h" +#include "nsComponentManager.h" +#include "nsEnumeratorUtils.h" +#include "nsNetUtil.h" +#include "nsStringEnumerator.h" +#include "nsTextFormatter.h" +#include "nsXPCOMCIDInternal.h" + +#include "mozilla/LookAndFeel.h" +#include "mozilla/Unused.h" + +#include "nsICommandLine.h" +#include "nsILocaleService.h" +#include "nsIObserverService.h" +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "mozilla/Preferences.h" +#include "nsIResProtocolHandler.h" +#include "nsIScriptError.h" +#include "nsIXPConnect.h" +#include "nsIXULRuntime.h" + +#define UILOCALE_CMD_LINE_ARG "UILocale" + +#define MATCH_OS_LOCALE_PREF "intl.locale.matchOS" +#define SELECTED_LOCALE_PREF "general.useragent.locale" +#define SELECTED_SKIN_PREF "general.skins.selectedSkin" +#define PACKAGE_OVERRIDE_BRANCH "chrome.override_package." + +using namespace mozilla; +using mozilla::dom::ContentParent; +using mozilla::dom::PContentParent; + +// We use a "best-fit" algorithm for matching locales and themes. +// 1) the exact selected locale/theme +// 2) (locales only) same language, different country +// e.g. en-GB is the selected locale, only en-US is available +// 3) any available locale/theme + +/** + * Match the language-part of two lang-COUNTRY codes, hopefully but + * not guaranteed to be in the form ab-CD or abz-CD. "ab" should also + * work, any other garbage-in will produce undefined results as long + * as it does not crash. + */ +static bool +LanguagesMatch(const nsACString& a, const nsACString& b) +{ + if (a.Length() < 2 || b.Length() < 2) + return false; + + nsACString::const_iterator as, ae, bs, be; + a.BeginReading(as); + a.EndReading(ae); + b.BeginReading(bs); + b.EndReading(be); + + while (*as == *bs) { + if (*as == '-') + return true; + + ++as; ++bs; + + // reached the end + if (as == ae && bs == be) + return true; + + // "a" is short + if (as == ae) + return (*bs == '-'); + + // "b" is short + if (bs == be) + return (*as == '-'); + } + + return false; +} + +nsChromeRegistryChrome::nsChromeRegistryChrome() + : mProfileLoaded(false) + , mDynamicRegistration(true) +{ +} + +nsChromeRegistryChrome::~nsChromeRegistryChrome() +{ +} + +nsresult +nsChromeRegistryChrome::Init() +{ + nsresult rv = nsChromeRegistry::Init(); + if (NS_FAILED(rv)) + return rv; + + mSelectedLocale = NS_LITERAL_CSTRING("en-US"); + mSelectedSkin = NS_LITERAL_CSTRING("classic/1.0"); + + bool safeMode = false; + nsCOMPtr<nsIXULRuntime> xulrun (do_GetService(XULAPPINFO_SERVICE_CONTRACTID)); + if (xulrun) + xulrun->GetInSafeMode(&safeMode); + + nsCOMPtr<nsIPrefService> prefserv (do_GetService(NS_PREFSERVICE_CONTRACTID)); + nsCOMPtr<nsIPrefBranch> prefs; + + if (prefserv) { + if (safeMode) { + prefserv->GetDefaultBranch(nullptr, getter_AddRefs(prefs)); + } else { + prefs = do_QueryInterface(prefserv); + } + } + + if (!prefs) { + NS_WARNING("Could not get pref service!"); + } else { + nsXPIDLCString provider; + rv = prefs->GetCharPref(SELECTED_SKIN_PREF, getter_Copies(provider)); + if (NS_SUCCEEDED(rv)) + mSelectedSkin = provider; + + SelectLocaleFromPref(prefs); + + rv = prefs->AddObserver(MATCH_OS_LOCALE_PREF, this, true); + rv = prefs->AddObserver(SELECTED_LOCALE_PREF, this, true); + rv = prefs->AddObserver(SELECTED_SKIN_PREF, this, true); + } + + nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService(); + if (obsService) { + obsService->AddObserver(this, "command-line-startup", true); + obsService->AddObserver(this, "profile-initial-state", true); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::CheckForOSAccessibility() +{ + int32_t useAccessibilityTheme = + LookAndFeel::GetInt(LookAndFeel::eIntID_UseAccessibilityTheme, 0); + + if (useAccessibilityTheme) { + /* Set the skin to classic and remove pref observers */ + if (!mSelectedSkin.EqualsLiteral("classic/1.0")) { + mSelectedSkin.AssignLiteral("classic/1.0"); + RefreshSkins(); + } + + nsCOMPtr<nsIPrefBranch> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) { + prefs->RemoveObserver(SELECTED_SKIN_PREF, this); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::GetLocalesForPackage(const nsACString& aPackage, + nsIUTF8StringEnumerator* *aResult) +{ + nsCString realpackage; + nsresult rv = OverrideLocalePackage(aPackage, realpackage); + if (NS_FAILED(rv)) + return rv; + + nsTArray<nsCString> *a = new nsTArray<nsCString>; + if (!a) + return NS_ERROR_OUT_OF_MEMORY; + + PackageEntry* entry; + if (mPackagesHash.Get(realpackage, &entry)) { + entry->locales.EnumerateToArray(a); + } + + rv = NS_NewAdoptingUTF8StringEnumerator(aResult, a); + if (NS_FAILED(rv)) + delete a; + + return rv; +} + +static nsresult +getUILangCountry(nsACString& aUILang) +{ + nsresult rv; + + nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString uiLang; + rv = localeService->GetLocaleComponentForUserAgent(uiLang); + NS_ENSURE_SUCCESS(rv, rv); + + CopyUTF16toUTF8(uiLang, aUILang); + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::IsLocaleRTL(const nsACString& package, bool *aResult) +{ + *aResult = false; + + nsAutoCString locale; + GetSelectedLocale(package, false, locale); + if (locale.Length() < 2) + return NS_OK; + + *aResult = GetDirectionForLocale(locale); + return NS_OK; +} + +nsresult +nsChromeRegistryChrome::GetSelectedLocale(const nsACString& aPackage, + bool aAsBCP47, + nsACString& aLocale) +{ + nsCString realpackage; + nsresult rv = OverrideLocalePackage(aPackage, realpackage); + if (NS_FAILED(rv)) + return rv; + PackageEntry* entry; + if (!mPackagesHash.Get(realpackage, &entry)) + return NS_ERROR_FILE_NOT_FOUND; + + aLocale = entry->locales.GetSelected(mSelectedLocale, nsProviderArray::LOCALE); + if (aLocale.IsEmpty()) + return NS_ERROR_FAILURE; + + if (aAsBCP47) { + SanitizeForBCP47(aLocale); + } + + return NS_OK; +} + +nsresult +nsChromeRegistryChrome::OverrideLocalePackage(const nsACString& aPackage, + nsACString& aOverride) +{ + const nsACString& pref = NS_LITERAL_CSTRING(PACKAGE_OVERRIDE_BRANCH) + aPackage; + nsAdoptingCString override = mozilla::Preferences::GetCString(PromiseFlatCString(pref).get()); + if (override) { + aOverride = override; + } + else { + aOverride = aPackage; + } + return NS_OK; +} + +nsresult +nsChromeRegistryChrome::SelectLocaleFromPref(nsIPrefBranch* prefs) +{ + nsresult rv; + bool matchOSLocale = false; + rv = prefs->GetBoolPref(MATCH_OS_LOCALE_PREF, &matchOSLocale); + + if (NS_SUCCEEDED(rv) && matchOSLocale) { + // compute lang and region code only when needed! + nsAutoCString uiLocale; + rv = getUILangCountry(uiLocale); + if (NS_SUCCEEDED(rv)) + mSelectedLocale = uiLocale; + } + else { + nsXPIDLCString provider; + rv = prefs->GetCharPref(SELECTED_LOCALE_PREF, getter_Copies(provider)); + if (NS_SUCCEEDED(rv)) { + mSelectedLocale = provider; + } + } + + if (NS_FAILED(rv)) + NS_ERROR("Couldn't select locale from pref!"); + + return rv; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::Observe(nsISupports *aSubject, const char *aTopic, + const char16_t *someData) +{ + nsresult rv = NS_OK; + + if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) { + nsCOMPtr<nsIPrefBranch> prefs (do_QueryInterface(aSubject)); + NS_ASSERTION(prefs, "Bad observer call!"); + + NS_ConvertUTF16toUTF8 pref(someData); + + if (pref.EqualsLiteral(MATCH_OS_LOCALE_PREF) || + pref.EqualsLiteral(SELECTED_LOCALE_PREF)) { + rv = UpdateSelectedLocale(); + if (NS_SUCCEEDED(rv) && mProfileLoaded) + FlushAllCaches(); + } + else if (pref.EqualsLiteral(SELECTED_SKIN_PREF)) { + nsXPIDLCString provider; + rv = prefs->GetCharPref(pref.get(), getter_Copies(provider)); + if (NS_FAILED(rv)) { + NS_ERROR("Couldn't get new skin pref!"); + return rv; + } + + mSelectedSkin = provider; + RefreshSkins(); + } else { + NS_ERROR("Unexpected pref!"); + } + } + else if (!strcmp("command-line-startup", aTopic)) { + nsCOMPtr<nsICommandLine> cmdLine (do_QueryInterface(aSubject)); + if (cmdLine) { + nsAutoString uiLocale; + rv = cmdLine->HandleFlagWithParam(NS_LITERAL_STRING(UILOCALE_CMD_LINE_ARG), + false, uiLocale); + if (NS_SUCCEEDED(rv) && !uiLocale.IsEmpty()) { + CopyUTF16toUTF8(uiLocale, mSelectedLocale); + nsCOMPtr<nsIPrefBranch> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) { + prefs->RemoveObserver(SELECTED_LOCALE_PREF, this); + } + } + } + } + else if (!strcmp("profile-initial-state", aTopic)) { + mProfileLoaded = true; + } + else { + NS_ERROR("Unexpected observer topic!"); + } + + return rv; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::CheckForNewChrome() +{ + mPackagesHash.Clear(); + mOverlayHash.Clear(); + mStyleHash.Clear(); + mOverrideTable.Clear(); + + mDynamicRegistration = false; + + nsComponentManagerImpl::gComponentManager->RereadChromeManifests(); + + mDynamicRegistration = true; + + SendRegisteredChrome(nullptr); + return NS_OK; +} + +nsresult nsChromeRegistryChrome::UpdateSelectedLocale() +{ + nsresult rv = NS_OK; + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) { + rv = SelectLocaleFromPref(prefs); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIObserverService> obsSvc = + mozilla::services::GetObserverService(); + NS_ASSERTION(obsSvc, "Couldn't get observer service."); + obsSvc->NotifyObservers((nsIChromeRegistry*) this, + "selected-locale-has-changed", nullptr); + } + } + + return rv; +} + +static void +SerializeURI(nsIURI* aURI, + SerializedURI& aSerializedURI) +{ + if (!aURI) + return; + + aURI->GetSpec(aSerializedURI.spec); + aURI->GetOriginCharset(aSerializedURI.charset); +} + +void +nsChromeRegistryChrome::SendRegisteredChrome( + mozilla::dom::PContentParent* aParent) +{ + InfallibleTArray<ChromePackage> packages; + InfallibleTArray<SubstitutionMapping> resources; + InfallibleTArray<OverrideMapping> overrides; + + for (auto iter = mPackagesHash.Iter(); !iter.Done(); iter.Next()) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(iter.Key(), iter.UserData(), &chromePackage, + mSelectedLocale, mSelectedSkin); + packages.AppendElement(chromePackage); + } + + // If we were passed a parent then a new child process has been created and + // has requested all of the chrome so send it the resources too. Otherwise + // resource mappings are sent by the resource protocol handler dynamically. + if (aParent) { + nsCOMPtr<nsIIOService> io (do_GetIOService()); + NS_ENSURE_TRUE_VOID(io); + + nsCOMPtr<nsIProtocolHandler> ph; + nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph)); + NS_ENSURE_SUCCESS_VOID(rv); + + nsCOMPtr<nsIResProtocolHandler> irph (do_QueryInterface(ph)); + nsResProtocolHandler* rph = static_cast<nsResProtocolHandler*>(irph.get()); + rv = rph->CollectSubstitutions(resources); + NS_ENSURE_SUCCESS_VOID(rv); + } + + for (auto iter = mOverrideTable.Iter(); !iter.Done(); iter.Next()) { + SerializedURI chromeURI, overrideURI; + + SerializeURI(iter.Key(), chromeURI); + SerializeURI(iter.UserData(), overrideURI); + + OverrideMapping override = { chromeURI, overrideURI }; + overrides.AppendElement(override); + } + + if (aParent) { + bool success = aParent->SendRegisterChrome(packages, resources, overrides, + mSelectedLocale, false); + NS_ENSURE_TRUE_VOID(success); + } else { + nsTArray<ContentParent*> parents; + ContentParent::GetAll(parents); + if (!parents.Length()) + return; + + for (uint32_t i = 0; i < parents.Length(); i++) { + DebugOnly<bool> success = + parents[i]->SendRegisterChrome(packages, resources, overrides, + mSelectedLocale, true); + NS_WARNING_ASSERTION(success, + "couldn't reset a child's registered chrome"); + } + } +} + +/* static */ void +nsChromeRegistryChrome::ChromePackageFromPackageEntry(const nsACString& aPackageName, + PackageEntry* aPackage, + ChromePackage* aChromePackage, + const nsCString& aSelectedLocale, + const nsCString& aSelectedSkin) +{ + SerializeURI(aPackage->baseURI, aChromePackage->contentBaseURI); + SerializeURI(aPackage->locales.GetBase(aSelectedLocale, + nsProviderArray::LOCALE), + aChromePackage->localeBaseURI); + SerializeURI(aPackage->skins.GetBase(aSelectedSkin, nsProviderArray::ANY), + aChromePackage->skinBaseURI); + aChromePackage->package = aPackageName; + aChromePackage->flags = aPackage->flags; +} + +static bool +CanLoadResource(nsIURI* aResourceURI) +{ + bool isLocalResource = false; + (void)NS_URIChainHasFlags(aResourceURI, + nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, + &isLocalResource); + return isLocalResource; +} + +nsIURI* +nsChromeRegistryChrome::GetBaseURIFromPackage(const nsCString& aPackage, + const nsCString& aProvider, + const nsCString& aPath) +{ + PackageEntry* entry; + if (!mPackagesHash.Get(aPackage, &entry)) { + if (!mInitialized) + return nullptr; + + LogMessage("No chrome package registered for chrome://%s/%s/%s", + aPackage.get(), aProvider.get(), aPath.get()); + + return nullptr; + } + + if (aProvider.EqualsLiteral("locale")) { + return entry->locales.GetBase(mSelectedLocale, nsProviderArray::LOCALE); + } + else if (aProvider.EqualsLiteral("skin")) { + return entry->skins.GetBase(mSelectedSkin, nsProviderArray::ANY); + } + else if (aProvider.EqualsLiteral("content")) { + return entry->baseURI; + } + return nullptr; +} + +nsresult +nsChromeRegistryChrome::GetFlagsFromPackage(const nsCString& aPackage, + uint32_t* aFlags) +{ + PackageEntry* entry; + if (!mPackagesHash.Get(aPackage, &entry)) + return NS_ERROR_FILE_NOT_FOUND; + + *aFlags = entry->flags; + return NS_OK; +} + +nsChromeRegistryChrome::ProviderEntry* +nsChromeRegistryChrome::nsProviderArray::GetProvider(const nsACString& aPreferred, MatchType aType) +{ + size_t i = mArray.Length(); + if (!i) + return nullptr; + + ProviderEntry* found = nullptr; // Only set if we find a partial-match locale + ProviderEntry* entry = nullptr; + + while (i--) { + entry = &mArray[i]; + if (aPreferred.Equals(entry->provider)) + return entry; + + if (aType != LOCALE) + continue; + + if (LanguagesMatch(aPreferred, entry->provider)) { + found = entry; + continue; + } + + if (!found && entry->provider.EqualsLiteral("en-US")) + found = entry; + } + + if (!found && aType != EXACT) + return entry; + + return found; +} + +nsIURI* +nsChromeRegistryChrome::nsProviderArray::GetBase(const nsACString& aPreferred, MatchType aType) +{ + ProviderEntry* provider = GetProvider(aPreferred, aType); + + if (!provider) + return nullptr; + + return provider->baseURI; +} + +const nsACString& +nsChromeRegistryChrome::nsProviderArray::GetSelected(const nsACString& aPreferred, MatchType aType) +{ + ProviderEntry* entry = GetProvider(aPreferred, aType); + + if (entry) + return entry->provider; + + return EmptyCString(); +} + +void +nsChromeRegistryChrome::nsProviderArray::SetBase(const nsACString& aProvider, nsIURI* aBaseURL) +{ + ProviderEntry* provider = GetProvider(aProvider, EXACT); + + if (provider) { + provider->baseURI = aBaseURL; + return; + } + + // no existing entries, add a new one + mArray.AppendElement(ProviderEntry(aProvider, aBaseURL)); +} + +void +nsChromeRegistryChrome::nsProviderArray::EnumerateToArray(nsTArray<nsCString> *a) +{ + int32_t i = mArray.Length(); + while (i--) { + a->AppendElement(mArray[i].provider); + } +} + +void +nsChromeRegistryChrome::OverlayListEntry::AddURI(nsIURI* aURI) +{ + int32_t i = mArray.Count(); + while (i--) { + bool equals; + if (NS_SUCCEEDED(aURI->Equals(mArray[i], &equals)) && equals) + return; + } + + mArray.AppendObject(aURI); +} + +void +nsChromeRegistryChrome::OverlayListHash::Add(nsIURI* aBase, nsIURI* aOverlay) +{ + OverlayListEntry* entry = mTable.PutEntry(aBase); + if (entry) + entry->AddURI(aOverlay); +} + +const nsCOMArray<nsIURI>* +nsChromeRegistryChrome::OverlayListHash::GetArray(nsIURI* aBase) +{ + OverlayListEntry* entry = mTable.GetEntry(aBase); + if (!entry) + return nullptr; + + return &entry->mArray; +} + +#ifdef MOZ_XUL +NS_IMETHODIMP +nsChromeRegistryChrome::GetStyleOverlays(nsIURI *aChromeURL, + nsISimpleEnumerator **aResult) +{ + nsCOMPtr<nsIURI> chromeURLWithoutHash; + if (aChromeURL) { + aChromeURL->CloneIgnoringRef(getter_AddRefs(chromeURLWithoutHash)); + } + const nsCOMArray<nsIURI>* parray = mStyleHash.GetArray(chromeURLWithoutHash); + if (!parray) + return NS_NewEmptyEnumerator(aResult); + + return NS_NewArrayEnumerator(aResult, *parray); +} + +NS_IMETHODIMP +nsChromeRegistryChrome::GetXULOverlays(nsIURI *aChromeURL, + nsISimpleEnumerator **aResult) +{ + nsCOMPtr<nsIURI> chromeURLWithoutHash; + if (aChromeURL) { + aChromeURL->CloneIgnoringRef(getter_AddRefs(chromeURLWithoutHash)); + } + const nsCOMArray<nsIURI>* parray = mOverlayHash.GetArray(chromeURLWithoutHash); + if (!parray) + return NS_NewEmptyEnumerator(aResult); + + return NS_NewArrayEnumerator(aResult, *parray); +} +#endif // MOZ_XUL + +nsIURI* +nsChromeRegistry::ManifestProcessingContext::GetManifestURI() +{ + if (!mManifestURI) { + nsCString uri; + mFile.GetURIString(uri); + NS_NewURI(getter_AddRefs(mManifestURI), uri); + } + return mManifestURI; +} + +nsIXPConnect* +nsChromeRegistry::ManifestProcessingContext::GetXPConnect() +{ + if (!mXPConnect) + mXPConnect = do_GetService("@mozilla.org/js/xpc/XPConnect;1"); + + return mXPConnect; +} + +already_AddRefed<nsIURI> +nsChromeRegistry::ManifestProcessingContext::ResolveURI(const char* uri) +{ + nsIURI* baseuri = GetManifestURI(); + if (!baseuri) + return nullptr; + + nsCOMPtr<nsIURI> resolved; + nsresult rv = NS_NewURI(getter_AddRefs(resolved), uri, baseuri); + if (NS_FAILED(rv)) + return nullptr; + + return resolved.forget(); +} + +static void +EnsureLowerCase(char *aBuf) +{ + for (; *aBuf; ++aBuf) { + char ch = *aBuf; + if (ch >= 'A' && ch <= 'Z') + *aBuf = ch + 'a' - 'A'; + } +} + +static void +SendManifestEntry(const ChromeRegistryItem &aItem) +{ + nsTArray<ContentParent*> parents; + ContentParent::GetAll(parents); + if (!parents.Length()) + return; + + for (uint32_t i = 0; i < parents.Length(); i++) { + Unused << parents[i]->SendRegisterChromeItem(aItem); + } +} + +void +nsChromeRegistryChrome::ManifestContent(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + char* package = argv[0]; + char* uri = argv[1]; + + EnsureLowerCase(package); + + nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri); + if (!resolved) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "During chrome registration, unable to create URI '%s'.", uri); + return; + } + + if (!CanLoadResource(resolved)) { + LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag, + "During chrome registration, cannot register non-local URI '%s' as content.", + uri); + return; + } + + nsDependentCString packageName(package); + PackageEntry* entry = mPackagesHash.LookupOrAdd(packageName); + entry->baseURI = resolved; + entry->flags = flags; + + if (mDynamicRegistration) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(packageName, entry, &chromePackage, + mSelectedLocale, mSelectedSkin); + SendManifestEntry(chromePackage); + } +} + +void +nsChromeRegistryChrome::ManifestLocale(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + char* package = argv[0]; + char* provider = argv[1]; + char* uri = argv[2]; + + EnsureLowerCase(package); + + nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri); + if (!resolved) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "During chrome registration, unable to create URI '%s'.", uri); + return; + } + + if (!CanLoadResource(resolved)) { + LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag, + "During chrome registration, cannot register non-local URI '%s' as content.", + uri); + return; + } + + nsDependentCString packageName(package); + PackageEntry* entry = mPackagesHash.LookupOrAdd(packageName); + entry->locales.SetBase(nsDependentCString(provider), resolved); + + if (mDynamicRegistration) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(packageName, entry, &chromePackage, + mSelectedLocale, mSelectedSkin); + SendManifestEntry(chromePackage); + } +} + +void +nsChromeRegistryChrome::ManifestSkin(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + char* package = argv[0]; + char* provider = argv[1]; + char* uri = argv[2]; + + EnsureLowerCase(package); + + nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri); + if (!resolved) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "During chrome registration, unable to create URI '%s'.", uri); + return; + } + + if (!CanLoadResource(resolved)) { + LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag, + "During chrome registration, cannot register non-local URI '%s' as content.", + uri); + return; + } + + nsDependentCString packageName(package); + PackageEntry* entry = mPackagesHash.LookupOrAdd(packageName); + entry->skins.SetBase(nsDependentCString(provider), resolved); + + if (mDynamicRegistration) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(packageName, entry, &chromePackage, + mSelectedLocale, mSelectedSkin); + SendManifestEntry(chromePackage); + } +} + +void +nsChromeRegistryChrome::ManifestOverlay(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + char* base = argv[0]; + char* overlay = argv[1]; + + nsCOMPtr<nsIURI> baseuri = cx.ResolveURI(base); + nsCOMPtr<nsIURI> overlayuri = cx.ResolveURI(overlay); + if (!baseuri || !overlayuri) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "During chrome registration, unable to create URI."); + return; + } + + if (!CanLoadResource(overlayuri)) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "Cannot register non-local URI '%s' as an overlay.", overlay); + return; + } + + nsCOMPtr<nsIURI> baseuriWithoutHash; + baseuri->CloneIgnoringRef(getter_AddRefs(baseuriWithoutHash)); + + mOverlayHash.Add(baseuriWithoutHash, overlayuri); +} + +void +nsChromeRegistryChrome::ManifestStyle(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + char* base = argv[0]; + char* overlay = argv[1]; + + nsCOMPtr<nsIURI> baseuri = cx.ResolveURI(base); + nsCOMPtr<nsIURI> overlayuri = cx.ResolveURI(overlay); + if (!baseuri || !overlayuri) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "During chrome registration, unable to create URI."); + return; + } + + if (!CanLoadResource(overlayuri)) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "Cannot register non-local URI '%s' as a style overlay.", overlay); + return; + } + + nsCOMPtr<nsIURI> baseuriWithoutHash; + baseuri->CloneIgnoringRef(getter_AddRefs(baseuriWithoutHash)); + + mStyleHash.Add(baseuriWithoutHash, overlayuri); +} + +void +nsChromeRegistryChrome::ManifestOverride(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + char* chrome = argv[0]; + char* resolved = argv[1]; + + nsCOMPtr<nsIURI> chromeuri = cx.ResolveURI(chrome); + nsCOMPtr<nsIURI> resolveduri = cx.ResolveURI(resolved); + if (!chromeuri || !resolveduri) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "During chrome registration, unable to create URI."); + return; + } + + if (cx.mType == NS_SKIN_LOCATION) { + bool chromeSkinOnly = false; + nsresult rv = chromeuri->SchemeIs("chrome", &chromeSkinOnly); + chromeSkinOnly = chromeSkinOnly && NS_SUCCEEDED(rv); + if (chromeSkinOnly) { + rv = resolveduri->SchemeIs("chrome", &chromeSkinOnly); + chromeSkinOnly = chromeSkinOnly && NS_SUCCEEDED(rv); + } + if (chromeSkinOnly) { + nsAutoCString chromePath, resolvedPath; + chromeuri->GetPath(chromePath); + resolveduri->GetPath(resolvedPath); + chromeSkinOnly = StringBeginsWith(chromePath, NS_LITERAL_CSTRING("/skin/")) && + StringBeginsWith(resolvedPath, NS_LITERAL_CSTRING("/skin/")); + } + if (!chromeSkinOnly) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "Cannot register non-chrome://.../skin/ URIs '%s' and '%s' as overrides and/or to be overridden from a skin manifest.", + chrome, resolved); + return; + } + } + + if (!CanLoadResource(resolveduri)) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "Cannot register non-local URI '%s' for an override.", resolved); + return; + } + mOverrideTable.Put(chromeuri, resolveduri); + + if (mDynamicRegistration) { + SerializedURI serializedChrome; + SerializedURI serializedOverride; + + SerializeURI(chromeuri, serializedChrome); + SerializeURI(resolveduri, serializedOverride); + + OverrideMapping override = { serializedChrome, serializedOverride }; + SendManifestEntry(override); + } +} + +void +nsChromeRegistryChrome::ManifestResource(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + char* package = argv[0]; + char* uri = argv[1]; + + EnsureLowerCase(package); + nsDependentCString host(package); + + nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService(); + if (!io) { + NS_WARNING("No IO service trying to process chrome manifests"); + return; + } + + nsCOMPtr<nsIProtocolHandler> ph; + nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph)); + if (NS_FAILED(rv)) + return; + + nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph); + + nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri); + if (!resolved) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "During chrome registration, unable to create URI '%s'.", uri); + return; + } + + if (!CanLoadResource(resolved)) { + LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, + "Warning: cannot register non-local URI '%s' as a resource.", + uri); + return; + } + + rph->SetSubstitution(host, resolved); +} diff --git a/chrome/nsChromeRegistryChrome.h b/chrome/nsChromeRegistryChrome.h new file mode 100644 index 0000000000..4d12ca7830 --- /dev/null +++ b/chrome/nsChromeRegistryChrome.h @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsChromeRegistryChrome_h +#define nsChromeRegistryChrome_h + +#include "nsCOMArray.h" +#include "nsChromeRegistry.h" +#include "nsTArray.h" +#include "mozilla/Move.h" +#include "nsClassHashtable.h" + +namespace mozilla { +namespace dom { +class PContentParent; +} // namespace dom +} // namespace mozilla + +class nsIPrefBranch; +struct ChromePackage; + +class nsChromeRegistryChrome : public nsChromeRegistry +{ + public: + nsChromeRegistryChrome(); + ~nsChromeRegistryChrome(); + + nsresult Init() override; + + NS_IMETHOD CheckForNewChrome() override; + NS_IMETHOD CheckForOSAccessibility() override; + NS_IMETHOD GetLocalesForPackage(const nsACString& aPackage, + nsIUTF8StringEnumerator* *aResult) override; + NS_IMETHOD IsLocaleRTL(const nsACString& package, + bool *aResult) override; + NS_IMETHOD GetSelectedLocale(const nsACString& aPackage, + bool aAsBCP47, + nsACString& aLocale) override; + NS_IMETHOD Observe(nsISupports *aSubject, const char *aTopic, + const char16_t *someData) override; + +#ifdef MOZ_XUL + NS_IMETHOD GetXULOverlays(nsIURI *aURI, + nsISimpleEnumerator **_retval) override; + NS_IMETHOD GetStyleOverlays(nsIURI *aURI, + nsISimpleEnumerator **_retval) override; +#endif + + // If aChild is non-null then it is a new child to notify. If aChild is + // null, then we have installed new chrome and we are resetting all of our + // children's registered chrome. + void SendRegisteredChrome(mozilla::dom::PContentParent* aChild); + + private: + struct PackageEntry; + static void ChromePackageFromPackageEntry(const nsACString& aPackageName, + PackageEntry* aPackage, + ChromePackage* aChromePackage, + const nsCString& aSelectedLocale, + const nsCString& aSelectedSkin); + + nsresult OverrideLocalePackage(const nsACString& aPackage, + nsACString& aOverride); + nsresult SelectLocaleFromPref(nsIPrefBranch* prefs); + nsresult UpdateSelectedLocale() override; + nsIURI* GetBaseURIFromPackage(const nsCString& aPackage, + const nsCString& aProvider, + const nsCString& aPath) override; + nsresult GetFlagsFromPackage(const nsCString& aPackage, + uint32_t* aFlags) override; + + struct ProviderEntry + { + ProviderEntry(const nsACString& aProvider, nsIURI* aBase) : + provider(aProvider), + baseURI(aBase) { } + + nsCString provider; + nsCOMPtr<nsIURI> baseURI; + }; + + class nsProviderArray + { + public: + nsProviderArray() : + mArray(1) { } + ~nsProviderArray() { } + + // When looking up locales and skins, the "selected" locale is not always + // available. This enum identifies what kind of match is desired/found. + enum MatchType { + EXACT = 0, + LOCALE = 1, // "en-GB" is selected, we found "en-US" + ANY = 2 + }; + + nsIURI* GetBase(const nsACString& aPreferred, MatchType aType); + const nsACString& GetSelected(const nsACString& aPreferred, MatchType aType); + void SetBase(const nsACString& aProvider, nsIURI* base); + void EnumerateToArray(nsTArray<nsCString> *a); + + private: + ProviderEntry* GetProvider(const nsACString& aPreferred, MatchType aType); + + nsTArray<ProviderEntry> mArray; + }; + + struct PackageEntry : public PLDHashEntryHdr + { + PackageEntry() + : flags(0) { } + ~PackageEntry() { } + + nsCOMPtr<nsIURI> baseURI; + uint32_t flags; + nsProviderArray locales; + nsProviderArray skins; + }; + + class OverlayListEntry : public nsURIHashKey + { + public: + typedef nsURIHashKey::KeyType KeyType; + typedef nsURIHashKey::KeyTypePointer KeyTypePointer; + + explicit OverlayListEntry(KeyTypePointer aKey) : nsURIHashKey(aKey) { } + OverlayListEntry(OverlayListEntry&& toMove) : nsURIHashKey(mozilla::Move(toMove)), + mArray(mozilla::Move(toMove.mArray)) { } + ~OverlayListEntry() { } + + void AddURI(nsIURI* aURI); + + nsCOMArray<nsIURI> mArray; + }; + + class OverlayListHash + { + public: + OverlayListHash() { } + ~OverlayListHash() { } + + void Add(nsIURI* aBase, nsIURI* aOverlay); + void Clear() { mTable.Clear(); } + const nsCOMArray<nsIURI>* GetArray(nsIURI* aBase); + + private: + nsTHashtable<OverlayListEntry> mTable; + }; + + // Hashes on the file to be overlaid (chrome://browser/content/browser.xul) + // to a list of overlays/stylesheets + OverlayListHash mOverlayHash; + OverlayListHash mStyleHash; + + bool mProfileLoaded; + bool mDynamicRegistration; + + nsCString mSelectedLocale; + nsCString mSelectedSkin; + + // Hash of package names ("global") to PackageEntry objects + nsClassHashtable<nsCStringHashKey, PackageEntry> mPackagesHash; + + virtual void ManifestContent(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestLocale(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestSkin(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestOverlay(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestStyle(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestOverride(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestResource(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; +}; + +#endif // nsChromeRegistryChrome_h diff --git a/chrome/nsChromeRegistryContent.cpp b/chrome/nsChromeRegistryContent.cpp new file mode 100644 index 0000000000..321f3fbe18 --- /dev/null +++ b/chrome/nsChromeRegistryContent.cpp @@ -0,0 +1,318 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sts=2 sw=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RegistryMessageUtils.h" +#include "nsChromeRegistryContent.h" +#include "nsString.h" +#include "nsNetUtil.h" +#include "nsIResProtocolHandler.h" + +nsChromeRegistryContent::nsChromeRegistryContent() +{ +} + +void +nsChromeRegistryContent::RegisterRemoteChrome( + const InfallibleTArray<ChromePackage>& aPackages, + const InfallibleTArray<SubstitutionMapping>& aSubstitutions, + const InfallibleTArray<OverrideMapping>& aOverrides, + const nsACString& aLocale, + bool aReset) +{ + MOZ_ASSERT(aReset || mLocale.IsEmpty(), + "RegisterChrome twice?"); + + if (aReset) { + mPackagesHash.Clear(); + mOverrideTable.Clear(); + // XXX Can't clear resources. + } + + for (uint32_t i = aPackages.Length(); i > 0; ) { + --i; + RegisterPackage(aPackages[i]); + } + + for (uint32_t i = aSubstitutions.Length(); i > 0; ) { + --i; + RegisterSubstitution(aSubstitutions[i]); + } + + for (uint32_t i = aOverrides.Length(); i > 0; ) { + --i; + RegisterOverride(aOverrides[i]); + } + + mLocale = aLocale; +} + +void +nsChromeRegistryContent::RegisterPackage(const ChromePackage& aPackage) +{ + nsCOMPtr<nsIIOService> io (do_GetIOService()); + if (!io) + return; + + nsCOMPtr<nsIURI> content, locale, skin; + + if (aPackage.contentBaseURI.spec.Length()) { + nsresult rv = NS_NewURI(getter_AddRefs(content), + aPackage.contentBaseURI.spec, + aPackage.contentBaseURI.charset.get(), + nullptr, io); + if (NS_FAILED(rv)) + return; + } + if (aPackage.localeBaseURI.spec.Length()) { + nsresult rv = NS_NewURI(getter_AddRefs(locale), + aPackage.localeBaseURI.spec, + aPackage.localeBaseURI.charset.get(), + nullptr, io); + if (NS_FAILED(rv)) + return; + } + if (aPackage.skinBaseURI.spec.Length()) { + nsresult rv = NS_NewURI(getter_AddRefs(skin), + aPackage.skinBaseURI.spec, + aPackage.skinBaseURI.charset.get(), + nullptr, io); + if (NS_FAILED(rv)) + return; + } + + PackageEntry* entry = new PackageEntry; + entry->flags = aPackage.flags; + entry->contentBaseURI = content; + entry->localeBaseURI = locale; + entry->skinBaseURI = skin; + + mPackagesHash.Put(aPackage.package, entry); +} + +void +nsChromeRegistryContent::RegisterSubstitution(const SubstitutionMapping& aSubstitution) +{ + nsCOMPtr<nsIIOService> io (do_GetIOService()); + if (!io) + return; + + nsCOMPtr<nsIProtocolHandler> ph; + nsresult rv = io->GetProtocolHandler(aSubstitution.scheme.get(), getter_AddRefs(ph)); + if (NS_FAILED(rv)) + return; + + nsCOMPtr<nsISubstitutingProtocolHandler> sph (do_QueryInterface(ph)); + if (!sph) + return; + + nsCOMPtr<nsIURI> resolvedURI; + if (aSubstitution.resolvedURI.spec.Length()) { + rv = NS_NewURI(getter_AddRefs(resolvedURI), + aSubstitution.resolvedURI.spec, + aSubstitution.resolvedURI.charset.get(), + nullptr, io); + if (NS_FAILED(rv)) + return; + } + + rv = sph->SetSubstitution(aSubstitution.path, resolvedURI); + if (NS_FAILED(rv)) + return; +} + +void +nsChromeRegistryContent::RegisterOverride(const OverrideMapping& aOverride) +{ + nsCOMPtr<nsIIOService> io (do_GetIOService()); + if (!io) + return; + + nsCOMPtr<nsIURI> chromeURI, overrideURI; + nsresult rv = NS_NewURI(getter_AddRefs(chromeURI), + aOverride.originalURI.spec, + aOverride.originalURI.charset.get(), + nullptr, io); + if (NS_FAILED(rv)) + return; + + rv = NS_NewURI(getter_AddRefs(overrideURI), aOverride.overrideURI.spec, + aOverride.overrideURI.charset.get(), nullptr, io); + if (NS_FAILED(rv)) + return; + + mOverrideTable.Put(chromeURI, overrideURI); +} + +nsIURI* +nsChromeRegistryContent::GetBaseURIFromPackage(const nsCString& aPackage, + const nsCString& aProvider, + const nsCString& aPath) +{ + PackageEntry* entry; + if (!mPackagesHash.Get(aPackage, &entry)) { + return nullptr; + } + + if (aProvider.EqualsLiteral("locale")) { + return entry->localeBaseURI; + } + else if (aProvider.EqualsLiteral("skin")) { + return entry->skinBaseURI; + } + else if (aProvider.EqualsLiteral("content")) { + return entry->contentBaseURI; + } + return nullptr; +} + +nsresult +nsChromeRegistryContent::GetFlagsFromPackage(const nsCString& aPackage, + uint32_t* aFlags) +{ + PackageEntry* entry; + if (!mPackagesHash.Get(aPackage, &entry)) { + return NS_ERROR_FAILURE; + } + *aFlags = entry->flags; + return NS_OK; +} + +// All functions following only make sense in chrome, and therefore assert + +#define CONTENT_NOTREACHED() \ + NS_NOTREACHED("Content should not be calling this") + +#define CONTENT_NOT_IMPLEMENTED() \ + CONTENT_NOTREACHED(); \ + return NS_ERROR_NOT_IMPLEMENTED; + +NS_IMETHODIMP +nsChromeRegistryContent::GetLocalesForPackage(const nsACString& aPackage, + nsIUTF8StringEnumerator* *aResult) +{ + CONTENT_NOT_IMPLEMENTED(); +} + +NS_IMETHODIMP +nsChromeRegistryContent::CheckForOSAccessibility() +{ + CONTENT_NOT_IMPLEMENTED(); +} + +NS_IMETHODIMP +nsChromeRegistryContent::CheckForNewChrome() +{ + CONTENT_NOT_IMPLEMENTED(); +} + +NS_IMETHODIMP +nsChromeRegistryContent::IsLocaleRTL(const nsACString& aPackage, + bool *aResult) +{ + if (aPackage != nsDependentCString("global")) { + NS_ERROR("Packages other than global unavailable"); + return NS_ERROR_NOT_AVAILABLE; + } + *aResult = GetDirectionForLocale(mLocale); + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistryContent::GetSelectedLocale(const nsACString& aPackage, + bool aAsBCP47, + nsACString& aLocale) +{ + if (aPackage != nsDependentCString("global")) { + NS_ERROR("Uh-oh, caller wanted something other than 'some local'"); + return NS_ERROR_NOT_AVAILABLE; + } + aLocale = mLocale; + if (aAsBCP47) { + SanitizeForBCP47(aLocale); + } + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistryContent::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + CONTENT_NOT_IMPLEMENTED(); +} + +NS_IMETHODIMP +nsChromeRegistryContent::GetStyleOverlays(nsIURI *aChromeURL, + nsISimpleEnumerator **aResult) +{ + CONTENT_NOT_IMPLEMENTED(); +} + +NS_IMETHODIMP +nsChromeRegistryContent::GetXULOverlays(nsIURI *aChromeURL, + nsISimpleEnumerator **aResult) +{ + CONTENT_NOT_IMPLEMENTED(); +} + +nsresult nsChromeRegistryContent::UpdateSelectedLocale() +{ + CONTENT_NOT_IMPLEMENTED(); +} + +void +nsChromeRegistryContent::ManifestContent(ManifestProcessingContext& cx, + int lineno, char *const * argv, + int flags) +{ + CONTENT_NOTREACHED(); +} + +void +nsChromeRegistryContent::ManifestLocale(ManifestProcessingContext& cx, + int lineno, + char *const * argv, int flags) +{ + CONTENT_NOTREACHED(); +} + +void +nsChromeRegistryContent::ManifestSkin(ManifestProcessingContext& cx, + int lineno, + char *const * argv, int flags) +{ + CONTENT_NOTREACHED(); +} + +void +nsChromeRegistryContent::ManifestOverlay(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) +{ + CONTENT_NOTREACHED(); +} + +void +nsChromeRegistryContent::ManifestStyle(ManifestProcessingContext& cx, + int lineno, + char *const * argv, int flags) +{ + CONTENT_NOTREACHED(); +} + +void +nsChromeRegistryContent::ManifestOverride(ManifestProcessingContext& cx, + int lineno, + char *const * argv, int flags) +{ + CONTENT_NOTREACHED(); +} + +void +nsChromeRegistryContent::ManifestResource(ManifestProcessingContext& cx, + int lineno, + char *const * argv, int flags) +{ + CONTENT_NOTREACHED(); +} diff --git a/chrome/nsChromeRegistryContent.h b/chrome/nsChromeRegistryContent.h new file mode 100644 index 0000000000..0a74d13db2 --- /dev/null +++ b/chrome/nsChromeRegistryContent.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsChromeRegistryContent_h +#define nsChromeRegistryContent_h + +#include "nsChromeRegistry.h" +#include "nsClassHashtable.h" + +struct ChromePackage; +struct SubstitutionMapping; +struct OverrideMapping; + +class nsChromeRegistryContent : public nsChromeRegistry +{ + public: + nsChromeRegistryContent(); + + void RegisterRemoteChrome(const InfallibleTArray<ChromePackage>& aPackages, + const InfallibleTArray<SubstitutionMapping>& aResources, + const InfallibleTArray<OverrideMapping>& aOverrides, + const nsACString& aLocale, + bool aReset); + + NS_IMETHOD GetLocalesForPackage(const nsACString& aPackage, + nsIUTF8StringEnumerator* *aResult) override; + NS_IMETHOD CheckForNewChrome() override; + NS_IMETHOD CheckForOSAccessibility() override; + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override; + NS_IMETHOD IsLocaleRTL(const nsACString& package, + bool *aResult) override; + NS_IMETHOD GetSelectedLocale(const nsACString& aPackage, + bool aAsBCP47, + nsACString& aLocale) override; + NS_IMETHOD GetStyleOverlays(nsIURI *aChromeURL, + nsISimpleEnumerator **aResult) override; + NS_IMETHOD GetXULOverlays(nsIURI *aChromeURL, + nsISimpleEnumerator **aResult) override; + + void RegisterPackage(const ChromePackage& aPackage); + void RegisterOverride(const OverrideMapping& aOverride); + void RegisterSubstitution(const SubstitutionMapping& aResource); + + private: + struct PackageEntry + { + PackageEntry() : flags(0) { } + ~PackageEntry() { } + + nsCOMPtr<nsIURI> contentBaseURI; + nsCOMPtr<nsIURI> localeBaseURI; + nsCOMPtr<nsIURI> skinBaseURI; + uint32_t flags; + }; + + nsresult UpdateSelectedLocale() override; + nsIURI* GetBaseURIFromPackage(const nsCString& aPackage, + const nsCString& aProvider, + const nsCString& aPath) override; + nsresult GetFlagsFromPackage(const nsCString& aPackage, uint32_t* aFlags) override; + + nsClassHashtable<nsCStringHashKey, PackageEntry> mPackagesHash; + nsCString mLocale; + + virtual void ManifestContent(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestLocale(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestSkin(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestOverlay(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestStyle(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestOverride(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; + virtual void ManifestResource(ManifestProcessingContext& cx, int lineno, + char *const * argv, int flags) override; +}; + +#endif // nsChromeRegistryContent_h diff --git a/chrome/nsIChromeRegistry.idl b/chrome/nsIChromeRegistry.idl new file mode 100644 index 0000000000..8df3a8bdf9 --- /dev/null +++ b/chrome/nsIChromeRegistry.idl @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIURI; + +[scriptable, uuid(249fb5ad-ae29-4e2c-a728-ba5cf464d188)] +interface nsIChromeRegistry : nsISupports +{ + const int32_t NONE = 0; + const int32_t PARTIAL = 1; + const int32_t FULL = 2; + + /** + * Resolve a chrome URL to an loadable URI using the information in the + * registry. Does not modify aChromeURL. + * + * Chrome URLs are allowed to be specified in "shorthand", leaving the + * "file" portion off. In that case, the URL is expanded to: + * + * chrome://package/provider/package.ext + * + * where "ext" is: + * + * "xul" for a "content" package, + * "css" for a "skin" package, and + * "dtd" for a "locale" package. + * + * @param aChromeURL the URL that is to be converted. + */ + nsIURI convertChromeURL(in nsIURI aChromeURL); + + /** + * refresh the chrome list at runtime, looking for new packages/etc + */ + void checkForNewChrome(); + + /** + * returns whether XPCNativeWrappers are enabled for aURI. + */ + [notxpcom] boolean wrappersEnabled(in nsIURI aURI); +}; + +[scriptable, uuid(93251ddf-5e85-4172-ac2a-31780562974f)] +interface nsIXULChromeRegistry : nsIChromeRegistry +{ + /* Should be called when locales change to reload all chrome (including XUL). */ + void reloadChrome(); + + // If the optional asBCP47 parameter is true, the locale code will be + // converted to a BCP47 language tag; in particular, this means that + // "ja-JP-mac" will be returned as "ja-JP-x-lvariant-mac", which can be + // passed to ECMA402 Intl API methods without throwing a RangeError. + ACString getSelectedLocale(in ACString packageName, + [optional] in boolean asBCP47); + + // Get the direction of the locale via the intl.uidirection.<locale> pref + boolean isLocaleRTL(in ACString package); + + /* Should be called when skins change. Reloads only stylesheets. */ + void refreshSkins(); + + /** + * Installable skin XBL is not always granted the same privileges as other + * chrome. This asks the chrome registry whether scripts are allowed to be + * run for a particular chrome URI. Do not pass non-chrome URIs to this + * method. + */ + boolean allowScriptsForPackage(in nsIURI url); + + /** + * Content should only be allowed to load chrome JS from certain packages. + * This method reflects the contentaccessible flag on packages. + * Do not pass non-chrome URIs to this method. + */ + boolean allowContentToAccess(in nsIURI url); + + /** + * Returns true if the passed chrome URL is allowed to be loaded in a remote + * process. This reflects the remoteenabled flag on packages. + * Do not pass non-chrome URIs to this method. + */ + boolean canLoadURLRemotely(in nsIURI url); + + /** + * Returns true if the passed chrome URL must be loaded in a remote process. + * This reflects the remoterequired flag on packages. + * Do not pass non-chrome URIs to this method. + */ + boolean mustLoadURLRemotely(in nsIURI url); +}; + +%{ C++ + +#define NS_CHROMEREGISTRY_CONTRACTID \ + "@mozilla.org/chrome/chrome-registry;1" + +/** + * Chrome registry will notify various caches that all chrome files need + * flushing. + */ +#define NS_CHROME_FLUSH_TOPIC \ + "chrome-flush-caches" + +/** + * Chrome registry will notify various caches that skin files need flushing. + * If "chrome-flush-caches" is notified, this topic will *not* be notified. + */ +#define NS_CHROME_FLUSH_SKINS_TOPIC \ + "chrome-flush-skin-caches" + +%} diff --git a/chrome/nsIToolkitChromeRegistry.idl b/chrome/nsIToolkitChromeRegistry.idl new file mode 100644 index 0000000000..3a9884517f --- /dev/null +++ b/chrome/nsIToolkitChromeRegistry.idl @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width; 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIChromeRegistry.idl" + +interface nsIURI; +interface nsIUTF8StringEnumerator; + +[scriptable, uuid(8727651c-9530-45a0-b81e-0e0690c30c50)] +interface nsIToolkitChromeRegistry : nsIXULChromeRegistry +{ + /** + * If the OS has a "high-visibility" or "disabled-friendly" theme set, + * we want to force mozilla into the classic theme, which (for the most part + * obeys the system color/font settings. We cannot do this at initialization, + * because it depends on the toolkit (GTK2) being initialized, which is + * not the case in some embedding situations. Embedders have to manually + * call this method during the startup process. + */ + void checkForOSAccessibility(); + + /** + * Get a list of locales available for the specified package. + */ + nsIUTF8StringEnumerator getLocalesForPackage(in AUTF8String aPackage); +}; diff --git a/chrome/test/moz.build b/chrome/test/moz.build new file mode 100644 index 0000000000..ed3bd5bbe7 --- /dev/null +++ b/chrome/test/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPCSHELL_TESTS_MANIFESTS += [ + 'unit/xpcshell.ini', + 'unit_ipc/xpcshell.ini', +] diff --git a/chrome/test/unit/data/test_abi.manifest b/chrome/test/unit/data/test_abi.manifest new file mode 100644 index 0000000000..6d265fc99f --- /dev/null +++ b/chrome/test/unit/data/test_abi.manifest @@ -0,0 +1,4 @@ +category abitest test1 found ABI=XPCShell_noarch-spidermonkey +category abitest test2 notfound ABI=WINNT_noarch-spidermonkey +category abitest test3 found ABI!=WINNT_noarch-spidermonkey +category abitest test4 notfound ABI!=XPCShell_noarch-spidermonkey diff --git a/chrome/test/unit/data/test_bug292789.manifest b/chrome/test/unit/data/test_bug292789.manifest new file mode 100644 index 0000000000..df67aa15c8 --- /dev/null +++ b/chrome/test/unit/data/test_bug292789.manifest @@ -0,0 +1,4 @@ +content test1 test/ +content test2 test/ contentaccessible=yes +content test3 test/ contentaccessible=no +content test4 test/ contentaccessible diff --git a/chrome/test/unit/data/test_bug380398.manifest b/chrome/test/unit/data/test_bug380398.manifest new file mode 100644 index 0000000000..a2349c793f --- /dev/null +++ b/chrome/test/unit/data/test_bug380398.manifest @@ -0,0 +1,14 @@ +# Success cases +content test1 test/ appversion=5 +content test2 test/ appversion<6 +content test3 test/ appversion>4 +content test4 test/ appversion>=5 +content test5 test/ appversion<=5 + +# Failure cases +content test6 test/ appversion>5 +content test7 test/ appversion<5 +content test8 test/ appversion> +content test9 test/ appversion< +content test10 test/ appversion= +content test11 test/ appversion diff --git a/chrome/test/unit/data/test_bug397073.manifest b/chrome/test/unit/data/test_bug397073.manifest new file mode 100644 index 0000000000..a219e8c7eb --- /dev/null +++ b/chrome/test/unit/data/test_bug397073.manifest @@ -0,0 +1,6 @@ +# Success cases +content test1 test/ os=xpcshell +content test2 test/ os=XPCshell + +# Failure cases +content test3 test/ os=FOOshell diff --git a/chrome/test/unit/data/test_bug399707.manifest b/chrome/test/unit/data/test_bug399707.manifest new file mode 100644 index 0000000000..ac0d972039 --- /dev/null +++ b/chrome/test/unit/data/test_bug399707.manifest @@ -0,0 +1,11 @@ +# Success cases +content test1 test/ application={39885e5f-f6b4-4e2a-87e5-6259ecf79011} +content test2 test/ application!={d51eecae-eab5-4779-85cb-45f1ec8657c8} +content test3 test/ application!= + +# Failure cases +content test4 test/ application= +content test5 test/ application={d51eecae-eab5-4779-85cb-45f1ec8657c8} +content test6 test/ application!={39885e5f-f6b4-4e2a-87e5-6259ecf79011} +content test7 test/ appversion=={39885e5f-f6b4-4e2a-87e5-6259ecf79011} +content test8 test/ application!!{d51eecae-eab5-4779-85cb-45f1ec8657c8} diff --git a/chrome/test/unit/data/test_bug401153.manifest b/chrome/test/unit/data/test_bug401153.manifest new file mode 100644 index 0000000000..4fcba1bdc1 --- /dev/null +++ b/chrome/test/unit/data/test_bug401153.manifest @@ -0,0 +1,11 @@ +# Should work +resource test1 test1/ + +# Mapping into jar files should work +resource test3 jar:test3.jar!/resources/ + +# Invalid line +resource test4 + +# Check we made it through the whole manifest +resource test5 test5/ diff --git a/chrome/test/unit/data/test_bug519468.manifest b/chrome/test/unit/data/test_bug519468.manifest new file mode 100644 index 0000000000..36f0a18f2a --- /dev/null +++ b/chrome/test/unit/data/test_bug519468.manifest @@ -0,0 +1,4 @@ +locale testmatchos en-US jar:en-US.jar!/locale/en-US/global/ +locale testmatchos fr-FR jar:en-US.jar!/locale/en-US/global/ +locale testmatchos de-DE jar:en-US.jar!/locale/en-US/global/ +locale testmatchos xx-AA jar:en-US.jar!/locale/en-US/global/ diff --git a/chrome/test/unit/data/test_bug564667.xpi b/chrome/test/unit/data/test_bug564667.xpi Binary files differnew file mode 100644 index 0000000000..be1632d8ca --- /dev/null +++ b/chrome/test/unit/data/test_bug564667.xpi diff --git a/chrome/test/unit/data/test_bug564667/chrome.manifest b/chrome/test/unit/data/test_bug564667/chrome.manifest new file mode 100644 index 0000000000..b35692764b --- /dev/null +++ b/chrome/test/unit/data/test_bug564667/chrome.manifest @@ -0,0 +1,16 @@ +# Locally defined URLs +content test1 test/ +locale test1 en-US test/ +skin test1 test test/ + +# Test Override +content testOverride test/ +override chrome://testOverride/content file:///test1/override + + +# Load external manifest +manifest loaded.manifest + +# Failure Cases +overlay chrome://test1/content/overlay.xul chrome://test1/content/test1.xul +style chrome://test1/content/style.xul chrome://test1/content/test1.css diff --git a/chrome/test/unit/data/test_bug564667/loaded.manifest b/chrome/test/unit/data/test_bug564667/loaded.manifest new file mode 100644 index 0000000000..2f231856a4 --- /dev/null +++ b/chrome/test/unit/data/test_bug564667/loaded.manifest @@ -0,0 +1,2 @@ +content test2 test/ +locale test2 en-US test/ diff --git a/chrome/test/unit/data/test_bug848297.manifest b/chrome/test/unit/data/test_bug848297.manifest new file mode 100644 index 0000000000..d9730a9ce7 --- /dev/null +++ b/chrome/test/unit/data/test_bug848297.manifest @@ -0,0 +1,4 @@ +locale basepack en-US jar:en-US.jar!/locale/en-US/global/ +locale basepack fr jar:en-US.jar!/locale/en-US/global/ +locale overpack en-US jar:en-US.jar!/locale/en-US/global/ +locale overpack de jar:en-US.jar!/locale/en-US/global/ diff --git a/chrome/test/unit/data/test_crlf.manifest b/chrome/test/unit/data/test_crlf.manifest new file mode 100644 index 0000000000..0f3032112e --- /dev/null +++ b/chrome/test/unit/data/test_crlf.manifest @@ -0,0 +1,3 @@ +# Note: the following lines begins/end with a CR +
+
content test_crlf .
diff --git a/chrome/test/unit/data/test_data_protocol_registration.manifest b/chrome/test/unit/data/test_data_protocol_registration.manifest new file mode 100644 index 0000000000..4effa6ee65 --- /dev/null +++ b/chrome/test/unit/data/test_data_protocol_registration.manifest @@ -0,0 +1,5 @@ +# package used only for valid override +content good-package bar/ + +# Local resource (should work) +override chrome://good-package/content/test.xul data:application/vnd.mozilla.xul+xml, diff --git a/chrome/test/unit/data/test_no_remote_registration.manifest b/chrome/test/unit/data/test_no_remote_registration.manifest new file mode 100644 index 0000000000..b677850ca2 --- /dev/null +++ b/chrome/test/unit/data/test_no_remote_registration.manifest @@ -0,0 +1,32 @@ +# package used only for valid overlaying and overrides +content good-package bar/ + +# UI Resource URIs (should not work) +content moz-protocol-ui-resource moz-protocol-ui-resource://foo/ +locale moz-protocol-ui-resource en-us moz-protocol-ui-resource://foo/ +skin moz-protocol-ui-resource skin1 moz-protocol-ui-resource://foo/ +override chrome://good-package/content/override-moz-protocol-ui-resource.xul moz-protocol-ui-resource://foo/ +resource moz-protocol-ui-resource moz-protocol-ui-resource://foo/ + +# Local file URIs (should not work) +content moz-protocol-local-file moz-protocol-local-file://foo/ +locale moz-protocol-local-file en-us moz-protocol-local-file://foo/ +skin moz-protocol-local-file skin1 moz-protocol-local-file://foo/ +override chrome://good-package/content/override-moz-protocol-local-file.xul moz-protocol-local-file://foo/ +resource moz-protocol-local-file moz-protocol-local-file://foo/ + +# Loadable by anyone URIs (should not work) +content moz-protocol-loadable-by-anyone moz-protocol-loadable-by-anyone://foo/ +locale moz-protocol-loadable-by-anyone en-us moz-protocol-loadable-by-anyone://foo/ +skin moz-protocol-loadable-by-anyone skin1 moz-protocol-loadable-by-anyone://foo/ +override chrome://good-package/content/override-moz-protocol-loadable-by-anyone.xul moz-protocol-loadable-by-anyone://foo/ +resource moz-protocol-loadable-by-anyone moz-protocol-loadable-by-anyone://foo/ + +# Working protocols should be after this point. Failing ones should be before. + +# Local resource (should work) +content moz-protocol-local-resource moz-protocol-local-resource://foo/ +locale moz-protocol-local-resource en-us moz-protocol-local-resource://foo/ +skin moz-protocol-local-resource skin1 moz-protocol-local-resource://foo/ +override chrome://good-package/content/override-moz-protocol-local-resource.xul moz-protocol-local-resource://foo/ +resource moz-protocol-local-resource moz-protocol-local-resource://foo/ diff --git a/chrome/test/unit/data/test_resolve_uris.manifest b/chrome/test/unit/data/test_resolve_uris.manifest new file mode 100644 index 0000000000..43b497e67d --- /dev/null +++ b/chrome/test/unit/data/test_resolve_uris.manifest @@ -0,0 +1,5 @@ +resource foo . +content foo resource://foo/foo-content/ +locale foo foo resource://foo/foo-locale/ +skin foo foo resource://foo/foo-skin/ +override chrome://good-package/content/override-me.xul resource://foo/foo-override/override-me.xul diff --git a/chrome/test/unit/head_crtestutils.js b/chrome/test/unit/head_crtestutils.js new file mode 100644 index 0000000000..e3be96fd23 --- /dev/null +++ b/chrome/test/unit/head_crtestutils.js @@ -0,0 +1,14 @@ +const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; +const XULAPPINFO_CID = Components.ID("{4ba645d3-be6f-40d6-a42a-01b2f40091b8}"); + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cr = Components.results; + + +function registerManifests(manifests) +{ + var reg = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + for (var manifest of manifests) + reg.autoRegister(manifest); +} diff --git a/chrome/test/unit/test_abi.js b/chrome/test/unit/test_abi.js new file mode 100644 index 0000000000..076fd3215f --- /dev/null +++ b/chrome/test/unit/test_abi.js @@ -0,0 +1,29 @@ +Components.utils.import("resource://testing-common/AppInfo.jsm", this); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +registerManifests([do_get_file("data/test_abi.manifest")]); + +const catman = Components.classes["@mozilla.org/categorymanager;1"]. + getService(Components.interfaces.nsICategoryManager); + +function is_registered(name) { + try { + var d = catman.getCategoryEntry("abitest", name); + return d == "found"; + } + catch (e) { + return false; + } +} + +function run_test() { + do_check_true(is_registered("test1")); + do_check_false(is_registered("test2")); + do_check_true(is_registered("test3")); + do_check_false(is_registered("test4")); +} diff --git a/chrome/test/unit/test_bug292789.js b/chrome/test/unit/test_bug292789.js new file mode 100644 index 0000000000..e57c8ac121 --- /dev/null +++ b/chrome/test/unit/test_bug292789.js @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +const MANIFESTS = [ + do_get_file("data/test_bug292789.manifest") +]; + +registerManifests(MANIFESTS); + +var gIOS; +var gCR; + +function check_accessibility(spec, desired) +{ + var uri = gIOS.newURI(spec, null, null); + var actual = gCR.allowContentToAccess(uri); + do_check_eq(desired, actual); +} + +function run_test() +{ + gIOS = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + gCR = Cc["@mozilla.org/chrome/chrome-registry;1"]. + getService(Ci.nsIXULChromeRegistry); + gCR.checkForNewChrome(); + + check_accessibility("chrome://test1/content/", false); + check_accessibility("chrome://test1/content/foo.js", false); + check_accessibility("chrome://test2/content/", true); + check_accessibility("chrome://test2/content/foo.js", true); + check_accessibility("chrome://test3/content/", false); + check_accessibility("chrome://test3/content/foo.js", false); + check_accessibility("chrome://test4/content/", true); +} diff --git a/chrome/test/unit/test_bug380398.js b/chrome/test/unit/test_bug380398.js new file mode 100644 index 0000000000..ce0b6233ec --- /dev/null +++ b/chrome/test/unit/test_bug380398.js @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + + +var MANIFESTS = [ + do_get_file("data/test_bug380398.manifest") +]; + +registerManifests(MANIFESTS); + +Components.utils.import("resource://testing-common/AppInfo.jsm", this); +updateAppInfo({ + name: "XPCShell", + id: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var gIOS = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIChromeRegistry); +chromeReg.checkForNewChrome(); + +var target = gIOS.newFileURI(do_get_file("data")); +target = target.spec + "test/test.xul"; + +function test_succeeded_mapping(namespace) +{ + var uri = gIOS.newURI("chrome://" + namespace + "/content/test.xul", + null, null); + try { + var result = chromeReg.convertChromeURL(uri); + do_check_eq(result.spec, target); + } + catch (ex) { + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) +{ + var uri = gIOS.newURI("chrome://" + namespace + "/content/test.xul", + null, null); + try { + var result = chromeReg.convertChromeURL(uri); + do_throw(namespace); + } + catch (ex) { + } +} + +function run_test() +{ + test_succeeded_mapping("test1"); + test_succeeded_mapping("test2"); + test_succeeded_mapping("test3"); + test_succeeded_mapping("test4"); + test_succeeded_mapping("test5"); + test_failed_mapping("test6"); + test_failed_mapping("test7"); + test_failed_mapping("test8"); + test_failed_mapping("test9"); + test_failed_mapping("test10"); + test_failed_mapping("test11"); +} diff --git a/chrome/test/unit/test_bug397073.js b/chrome/test/unit/test_bug397073.js new file mode 100644 index 0000000000..fb9c6c0953 --- /dev/null +++ b/chrome/test/unit/test_bug397073.js @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +var MANIFESTS = [ + do_get_file("data/test_bug397073.manifest") +]; + + +registerManifests(MANIFESTS); + +Components.utils.import("resource://testing-common/AppInfo.jsm", this); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var gIOS = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIChromeRegistry); +chromeReg.checkForNewChrome(); + +var target = gIOS.newFileURI(do_get_file("data")); +target = target.spec + "test/test.xul"; + +function test_succeeded_mapping(namespace) +{ + var uri = gIOS.newURI("chrome://" + namespace + "/content/test.xul", + null, null); + try { + var result = chromeReg.convertChromeURL(uri); + do_check_eq(result.spec, target); + } + catch (ex) { + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) +{ + var uri = gIOS.newURI("chrome://" + namespace + "/content/test.xul", + null, null); + try { + var result = chromeReg.convertChromeURL(uri); + do_throw(namespace); + } + catch (ex) { + } +} + +function run_test() +{ + test_succeeded_mapping("test1"); + test_succeeded_mapping("test2"); + + test_failed_mapping("test3"); +} diff --git a/chrome/test/unit/test_bug399707.js b/chrome/test/unit/test_bug399707.js new file mode 100644 index 0000000000..05273550c3 --- /dev/null +++ b/chrome/test/unit/test_bug399707.js @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +var MANIFESTS = [ + do_get_file("data/test_bug399707.manifest") +]; + +registerManifests(MANIFESTS); + +Components.utils.import("resource://testing-common/AppInfo.jsm", this); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var gIOS = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIChromeRegistry); +chromeReg.checkForNewChrome(); + +var target = gIOS.newFileURI(do_get_file("data")); +target = target.spec + "test/test.xul"; + +function test_succeeded_mapping(namespace) +{ + var uri = gIOS.newURI("chrome://" + namespace + "/content/test.xul", + null, null); + try { + var result = chromeReg.convertChromeURL(uri); + do_check_eq(result.spec, target); + } + catch (ex) { + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) +{ + var uri = gIOS.newURI("chrome://" + namespace + "/content/test.xul", + null, null); + try { + var result = chromeReg.convertChromeURL(uri); + do_throw(namespace); + } + catch (ex) { + } +} + +function run_test() +{ + test_succeeded_mapping("test1"); + test_succeeded_mapping("test2"); + test_succeeded_mapping("test3"); + test_failed_mapping("test4"); + test_failed_mapping("test5"); + test_failed_mapping("test6"); + test_failed_mapping("test7"); + test_failed_mapping("test8"); +} diff --git a/chrome/test/unit/test_bug401153.js b/chrome/test/unit/test_bug401153.js new file mode 100644 index 0000000000..3317d8b1e3 --- /dev/null +++ b/chrome/test/unit/test_bug401153.js @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +var MANIFESTS = [ + do_get_file("data/test_bug401153.manifest") +]; + +registerManifests(MANIFESTS); + +Components.utils.import("resource://testing-common/AppInfo.jsm", this); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var gIOS = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIChromeRegistry); +chromeReg.checkForNewChrome(); + +var rph = gIOS.getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); + +function test_succeeded_mapping(namespace, target) +{ + try { + do_check_true(rph.hasSubstitution(namespace)); + var uri = gIOS.newURI("resource://" + namespace, null, null); + dump("### checking for " + target + ", getting " + rph.resolveURI(uri) + "\n"); + do_check_eq(rph.resolveURI(uri), target); + } + catch (ex) { + dump(ex + "\n"); + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) +{ + do_check_false(rph.hasSubstitution(namespace)); +} + +function run_test() +{ + var data = gIOS.newFileURI(do_get_file("data")).spec; + test_succeeded_mapping("test1", data + "test1/"); + test_succeeded_mapping("test3", "jar:" + data + "test3.jar!/resources/"); + test_failed_mapping("test4"); + test_succeeded_mapping("test5", data + "test5/"); +} diff --git a/chrome/test/unit/test_bug415367.js b/chrome/test/unit/test_bug415367.js new file mode 100644 index 0000000000..6d02223c0e --- /dev/null +++ b/chrome/test/unit/test_bug415367.js @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +var gIOS = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + +function test_uri(obj) +{ + var uri = null; + var failed = false; + var message = ""; + try { + uri = gIOS.newURI(obj.uri, null, null); + if (!obj.result) { + failed = true; + message = obj.uri + " should not be accepted as a valid URI"; + } + } + catch (ex) { + if (obj.result) { + failed = true; + message = obj.uri + " should be accepted as a valid URI"; + } + } + if (failed) + do_throw(message); + if (obj.result) { + do_check_true(uri != null); + do_check_eq(uri.spec, obj.uri); + } +} + +function run_test() +{ + var tests = [ + {uri: "chrome://blah/content/blah.xul", result: true}, + {uri: "chrome://blah/content/:/blah/blah.xul", result: false}, + {uri: "chrome://blah/content/%252e./blah/blah.xul", result: false}, + {uri: "chrome://blah/content/%252e%252e/blah/blah.xul", result: false}, + {uri: "chrome://blah/content/blah.xul?param=%252e./blah/", result: true}, + {uri: "chrome://blah/content/blah.xul?param=:/blah/", result: true}, + {uri: "chrome://blah/content/blah.xul?param=%252e%252e/blah/", result: true}, + ]; + for (var i = 0; i < tests.length; ++ i) + test_uri(tests[i]); +} diff --git a/chrome/test/unit/test_bug519468.js b/chrome/test/unit/test_bug519468.js new file mode 100644 index 0000000000..69c5c86220 --- /dev/null +++ b/chrome/test/unit/test_bug519468.js @@ -0,0 +1,94 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +var MANIFESTS = [ + do_get_file("data/test_bug519468.manifest") +]; + +Components.utils.import("resource://testing-common/MockRegistrar.jsm"); +// Stub in the locale service so we can control what gets returned as the OS locale setting +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +var stubOSLocale = null; + +StubLocaleService = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsILocaleService, Ci.nsISupports]), + + getLocaleComponentForUserAgent: function SLS_getLocaleComponentForUserAgent() + { + return stubOSLocale; + } +} + +MockRegistrar.register("@mozilla.org/intl/nslocaleservice;1", StubLocaleService); + +// Now fire up the test +do_test_pending() +registerManifests(MANIFESTS); + +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIXULChromeRegistry) + .QueryInterface(Ci.nsIToolkitChromeRegistry); +chromeReg.checkForNewChrome(); + +var prefService = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService) + .QueryInterface(Ci.nsIPrefBranch); +var os = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + +var testsLocale = [ + // These tests cover when the OS local is included in our manifest + {matchOS: false, selected: "en-US", osLocale: "xx-AA", locale: "en-US"}, + {matchOS: true, selected: "en-US", osLocale: "xx-AA", locale: "xx-AA"}, + {matchOS: false, selected: "fr-FR", osLocale: "xx-AA", locale: "fr-FR"}, + {matchOS: true, selected: "fr-FR", osLocale: "xx-AA", locale: "xx-AA"}, + {matchOS: false, selected: "de-DE", osLocale: "xx-AA", locale: "de-DE"}, + {matchOS: true, selected: "de-DE", osLocale: "xx-AA", locale: "xx-AA"}, + // these tests cover the case where the OS locale is not available in our manifest, but the + // base language is (ie, substitute xx-AA which we have for xx-BB which we don't) + {matchOS: false, selected: "en-US", osLocale: "xx-BB", locale: "en-US"}, + {matchOS: true, selected: "en-US", osLocale: "xx-BB", locale: "xx-AA"}, + {matchOS: false, selected: "fr-FR", osLocale: "xx-BB", locale: "fr-FR"}, + {matchOS: true, selected: "fr-FR", osLocale: "xx-BB", locale: "xx-AA"}, + // These tests cover where the language is not available + {matchOS: false, selected: "en-US", osLocale: "xy-BB", locale: "en-US"}, + {matchOS: true, selected: "en-US", osLocale: "xy-BB", locale: "en-US"}, + {matchOS: false, selected: "fr-FR", osLocale: "xy-BB", locale: "fr-FR"}, + {matchOS: true, selected: "fr-FR", osLocale: "xy-BB", locale: "en-US"}, +]; + +var observedLocale = null; + +function test_locale(aTest) { + observedLocale = null; + + stubOSLocale = aTest.osLocale; + prefService.setBoolPref("intl.locale.matchOS", aTest.matchOS); + prefService.setCharPref("general.useragent.locale", aTest.selected); + + chromeReg.reloadChrome(); + + do_check_eq(observedLocale, aTest.locale); +} + +// Callback function for observing locale change. May be called more than once +// per test iteration. +function checkValidity() { + observedLocale = chromeReg.getSelectedLocale("testmatchos"); + dump("checkValidity called back with locale = " + observedLocale + "\n"); +} + +function run_test() { + os.addObserver(checkValidity, "selected-locale-has-changed", false); + + for (let count = 0 ; count < testsLocale.length ; count++) { + dump("count = " + count + " " + testsLocale[count].toSource() + "\n"); + test_locale(testsLocale[count]); + } + + os.removeObserver(checkValidity, "selected-locale-has-changed"); + do_test_finished(); +} diff --git a/chrome/test/unit/test_bug564667.js b/chrome/test/unit/test_bug564667.js new file mode 100644 index 0000000000..5f5e9940dc --- /dev/null +++ b/chrome/test/unit/test_bug564667.js @@ -0,0 +1,121 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +const UNPACKAGED_ADDON = do_get_file("data/test_bug564667"); +const PACKAGED_ADDON = do_get_file("data/test_bug564667.xpi"); + +var gIOS = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + +var gCR = Cc["@mozilla.org/chrome/chrome-registry;1"]. + getService(Ci.nsIChromeRegistry). + QueryInterface(Ci.nsIXULOverlayProvider); + +/* + * Checks that a mapping was added + */ +function test_mapping(chromeURL, target) { + var uri = gIOS.newURI(chromeURL, null, null); + + try { + var result = gCR.convertChromeURL(uri); + do_check_eq(result.spec, target); + } + catch (ex) { + do_throw(chromeURL + " not Registered"); + } +} + +/* + * Checks that a mapping was removed + */ +function test_removed_mapping(chromeURL, target) { + var uri = gIOS.newURI(chromeURL, null, null); + try { + var result = gCR.convertChromeURL(uri); + do_throw(chromeURL + " not removed"); + } + catch (ex) { + // This should throw + } +} + +/* + * Checks if any overlay was added after loading + * the manifest files + * + * @param type The type of overlay: overlay|style + */ +function test_no_overlays(chromeURL, target, type) { + var type = type || "overlay"; + var uri = gIOS.newURI(chromeURL, null, null); + var present = false, elem; + + var overlays = (type == "overlay") ? + gCR.getXULOverlays(uri) : gCR.getStyleOverlays(uri); + + // We shouldn't be allowed to register overlays nor styles + if (overlays.hasMoreElements()) { + if (type == "styles") + do_throw("Style Registered: " + chromeURL); + else + do_throw("Overlay Registered: " + chromeURL); + } +} + +function testManifest(manifestPath, baseURI) { + + // ------------------ Add manifest file ------------------------ + Components.manager.addBootstrappedManifestLocation(manifestPath); + + // Test Adding Content URL + test_mapping("chrome://test1/content", baseURI + "test/test1.xul"); + + // Test Adding Locale URL + test_mapping("chrome://test1/locale", baseURI + "test/test1.dtd"); + + // Test Adding Skin URL + test_mapping("chrome://test1/skin", baseURI + "test/test1.css"); + + // Test Adding Manifest URL + test_mapping("chrome://test2/content", baseURI + "test/test2.xul"); + test_mapping("chrome://test2/locale", baseURI + "test/test2.dtd"); + + // Test Adding Override + test_mapping("chrome://testOverride/content", 'file:///test1/override') + + // Test Not-Adding Overlays + test_no_overlays("chrome://test1/content/overlay.xul", + "chrome://test1/content/test1.xul"); + + // Test Not-Adding Styles + test_no_overlays("chrome://test1/content/style.xul", + "chrome://test1/content/test1.css", "styles"); + + + // ------------------ Remove manifest file ------------------------ + Components.manager.removeBootstrappedManifestLocation(manifestPath); + + // Test Removing Content URL + test_removed_mapping("chrome://test1/content", baseURI + "test/test1.xul"); + + // Test Removing Content URL + test_removed_mapping("chrome://test1/locale", baseURI + "test/test1.dtd"); + + // Test Removing Skin URL + test_removed_mapping("chrome://test1/skin", baseURI + "test/test1.css"); + + // Test Removing Manifest URL + test_removed_mapping("chrome://test2/content", baseURI + "test/test2.xul"); + test_removed_mapping("chrome://test2/locale", baseURI + "test/test2.dtd"); +} + +function run_test() { + // Test an unpackaged addon + testManifest(UNPACKAGED_ADDON, gIOS.newFileURI(UNPACKAGED_ADDON).spec); + + // Test a packaged addon + testManifest(PACKAGED_ADDON, "jar:" + gIOS.newFileURI(PACKAGED_ADDON).spec + "!/"); +} diff --git a/chrome/test/unit/test_bug848297.js b/chrome/test/unit/test_bug848297.js new file mode 100644 index 0000000000..0875370f79 --- /dev/null +++ b/chrome/test/unit/test_bug848297.js @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +var MANIFESTS = [ + do_get_file("data/test_bug848297.manifest") +]; + +// Stub in the locale service so we can control what gets returned as the OS locale setting +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +registerManifests(MANIFESTS); + +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIXULChromeRegistry) + .QueryInterface(Ci.nsIToolkitChromeRegistry); +chromeReg.checkForNewChrome(); + +var prefService = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService) + .QueryInterface(Ci.nsIPrefBranch); + +function enum_to_array(strings) { + let rv = []; + while (strings.hasMore()) { + rv.push(strings.getNext()); + } + rv.sort(); + return rv; +} + +function run_test() { + + // without override + prefService.setCharPref("general.useragent.locale", "de"); + do_check_eq(chromeReg.getSelectedLocale("basepack"), "en-US"); + do_check_eq(chromeReg.getSelectedLocale("overpack"), "de"); + do_check_matches(enum_to_array(chromeReg.getLocalesForPackage("basepack")), + ['en-US', 'fr']); + + // with override + prefService.setCharPref("chrome.override_package.basepack", "overpack"); + do_check_eq(chromeReg.getSelectedLocale("basepack"), "de"); + do_check_matches(enum_to_array(chromeReg.getLocalesForPackage("basepack")), + ['de', 'en-US']); + +} diff --git a/chrome/test/unit/test_crlf.js b/chrome/test/unit/test_crlf.js new file mode 100644 index 0000000000..b577c49a9c --- /dev/null +++ b/chrome/test/unit/test_crlf.js @@ -0,0 +1,15 @@ +registerManifests([do_get_file("data/test_crlf.manifest")]); + +function run_test() +{ + let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]. + getService(Ci.nsIChromeRegistry); + + let ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + let sourceURI = ios.newURI("chrome://test_crlf/content/", null, null); + // this throws for packages that are not registered + let file = cr.convertChromeURL(sourceURI).QueryInterface(Ci.nsIFileURL).file; + + do_check_true(file.equals(do_get_file("data/test_crlf.xul", true))); +} diff --git a/chrome/test/unit/test_data_protocol_registration.js b/chrome/test/unit/test_data_protocol_registration.js new file mode 100644 index 0000000000..71138bd929 --- /dev/null +++ b/chrome/test/unit/test_data_protocol_registration.js @@ -0,0 +1,65 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 sts=2 tw=78 expandtab : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var manifests = [ + do_get_file("data/test_data_protocol_registration.manifest"), +]; +registerManifests(manifests); + +var XULAppInfoFactory = { + // These two are used when we register all our factories (and unregister) + CID: XULAPPINFO_CID, + scheme: "XULAppInfo", + contractID: XULAPPINFO_CONTRACTID, + createInstance: function (outer, iid) { + if (outer != null) + throw Cr.NS_ERROR_NO_AGGREGATION; + return XULAppInfo.QueryInterface(iid); + } +}; + +function run_test() +{ + // Add our XULAppInfo factory + let factories = [XULAppInfoFactory]; + + // Register our factories + for (let i = 0; i < factories.length; i++) { + let factory = factories[i]; + Components.manager.QueryInterface(Ci.nsIComponentRegistrar) + .registerFactory(factory.CID, "test-" + factory.scheme, + factory.contractID, factory); + } + + // Check for new chrome + let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]. + getService(Ci.nsIChromeRegistry); + cr.checkForNewChrome(); + + // Check that our override worked + let expectedURI = "data:application/vnd.mozilla.xul+xml,"; + let sourceURI = "chrome://good-package/content/test.xul"; + try { + let ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + sourceURI = ios.newURI(sourceURI, null, null); + // this throws for packages that are not registered + let uri = cr.convertChromeURL(sourceURI).spec; + + do_check_eq(expectedURI, uri); + } + catch (e) { + dump(e + "\n"); + do_throw("Should have registered our URI!"); + } + + // Unregister our factories so we do not leak + for (let i = 0; i < factories.length; i++) { + let factory = factories[i]; + Components.manager.QueryInterface(Ci.nsIComponentRegistrar) + .unregisterFactory(factory.CID, factory); + } +} diff --git a/chrome/test/unit/test_no_remote_registration.js b/chrome/test/unit/test_no_remote_registration.js new file mode 100644 index 0000000000..281a79dd32 --- /dev/null +++ b/chrome/test/unit/test_no_remote_registration.js @@ -0,0 +1,202 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 sts=2 tw=78 expandtab : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var manifests = [ + do_get_file("data/test_no_remote_registration.manifest"), +]; +registerManifests(manifests); + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +Components.utils.import("resource://testing-common/AppInfo.jsm", this); +var XULAppInfo = newAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var XULAppInfoFactory = { + // These two are used when we register all our factories (and unregister) + CID: Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"), + scheme: "XULAppInfo", + contractID: "@mozilla.org/xre/app-info;1", + createInstance: function (outer, iid) { + if (outer != null) + throw Cr.NS_ERROR_NO_AGGREGATION; + return XULAppInfo.QueryInterface(iid); + } +}; + +function ProtocolHandler(aScheme, aFlags) +{ + this.scheme = aScheme; + this.protocolFlags = aFlags; + this.contractID = "@mozilla.org/network/protocol;1?name=" + aScheme; +} + +ProtocolHandler.prototype = +{ + defaultPort: -1, + allowPort: () => false, + newURI: function(aSpec, aCharset, aBaseURI) + { + let uri = Cc["@mozilla.org/network/standard-url;1"]. + createInstance(Ci.nsIURI); + uri.spec = aSpec; + if (!uri.scheme) { + // We got a partial uri, so let's resolve it with the base one + uri.spec = aBaseURI.resolve(aSpec); + } + return uri; + }, + newChannel2: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED }, + newChannel: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED }, + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIProtocolHandler + ]) +}; + +var testProtocols = [ + // It doesn't matter if it has this flag - the only flag we accept is + // URI_IS_LOCAL_RESOURCE. + {scheme: "moz-protocol-ui-resource", + flags: Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE, + CID: Components.ID("{d6dedc93-558f-44fe-90f4-3b4bba8a0b14}"), + shouldRegister: false + }, + // It doesn't matter if it has this flag - the only flag we accept is + // URI_IS_LOCAL_RESOURCE. + {scheme: "moz-protocol-local-file", + flags: Ci.nsIProtocolHandler.URI_IS_LOCAL_FILE, + CID: Components.ID("{ee30d594-0a2d-4f47-89cc-d4cde320ab69}"), + shouldRegister: false + }, + // This clearly is non-local + {scheme: "moz-protocol-loadable-by-anyone", + flags: Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE, + CID: Components.ID("{c3735f23-3b0c-4a33-bfa0-79436dcd40b2}"), + shouldRegister: false + }, + // This should always be last (unless we add more flags that are OK) + {scheme: "moz-protocol-local-resource", + flags: Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE, + CID: Components.ID("{b79e977c-f840-469a-b413-0125cc1b62a5}"), + shouldRegister: true + }, +]; +function run_test() +{ + // Create factories + let factories = []; + for (let i = 0; i < testProtocols.length; i++) { + factories[i] = { + scheme: testProtocols[i].scheme, + flags: testProtocols[i].flags, + CID: testProtocols[i].CID, + contractID: "@mozilla.org/network/protocol;1?name=" + testProtocols[i].scheme, + createInstance: function(aOuter, aIID) + { + if (aOuter != null) + throw Cr.NS_ERROR_NO_AGGREGATION; + let handler = new ProtocolHandler(this.scheme, this.flags, this.CID); + return handler.QueryInterface(aIID); + } + }; + } + // Add our XULAppInfo factory + factories.push(XULAppInfoFactory); + + // Register our factories + for (let i = 0; i < factories.length; i++) { + let factory = factories[i]; + Components.manager.QueryInterface(Ci.nsIComponentRegistrar) + .registerFactory(factory.CID, "test-" + factory.scheme, + factory.contractID, factory); + } + + // Check for new chrome + let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]. + getService(Ci.nsIChromeRegistry); + cr.checkForNewChrome(); + + // See if our various things were able to register + let registrationTypes = [ + "content", + "locale", + "skin", + "override", + "resource", + ]; + for (let i = 0; i < testProtocols.length; i++) { + let protocol = testProtocols[i]; + for (let j = 0; j < registrationTypes.length; j++) { + let type = registrationTypes[j]; + dump("Testing protocol '" + protocol.scheme + "' with type '" + type + + "'\n"); + let expectedURI = protocol.scheme + "://foo/"; + let sourceURI = "chrome://" + protocol.scheme + "/" + type + "/"; + switch (type) { + case "content": + expectedURI += protocol.scheme + ".xul"; + break; + case "locale": + expectedURI += protocol.scheme + ".dtd"; + break; + case "skin": + expectedURI += protocol.scheme + ".css"; + break; + case "override": + sourceURI = "chrome://good-package/content/override-" + + protocol.scheme + ".xul"; + break; + case "resource": + sourceURI = "resource://" + protocol.scheme + "/"; + break; + }; + try { + let ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + sourceURI = ios.newURI(sourceURI, null, null); + let uri; + if (type == "resource") { + // resources go about a slightly different way than everything else + let rph = ios.getProtocolHandler("resource"). + QueryInterface(Ci.nsIResProtocolHandler); + // this throws for packages that are not registered + uri = rph.resolveURI(sourceURI); + } + else { + // this throws for packages that are not registered + uri = cr.convertChromeURL(sourceURI).spec; + } + + if (protocol.shouldRegister) { + do_check_eq(expectedURI, uri); + } + else { + // Overrides will not throw, so we'll get to here. We want to make + // sure that the two strings are not the same in this situation. + do_check_neq(expectedURI, uri); + } + } + catch (e) { + if (protocol.shouldRegister) { + dump(e + "\n"); + do_throw("Should have registered our URI for protocol " + + protocol.scheme); + } + } + } + } + + // Unregister our factories so we do not leak + for (let i = 0; i < factories.length; i++) { + let factory = factories[i]; + Components.manager.QueryInterface(Ci.nsIComponentRegistrar) + .unregisterFactory(factory.CID, factory); + } +} diff --git a/chrome/test/unit/test_resolve_uris.js b/chrome/test/unit/test_resolve_uris.js new file mode 100644 index 0000000000..30ae9bc268 --- /dev/null +++ b/chrome/test/unit/test_resolve_uris.js @@ -0,0 +1,93 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// head_crtestutils.js doesn't get included in the child by default +if (typeof registerManifests === "undefined") { + load("../unit/head_crtestutils.js"); +} + +var manifestFile = do_get_file("../unit/data/test_resolve_uris.manifest"); + +var manifests = [ manifestFile ]; +registerManifests(manifests); + +var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + +function do_run_test() +{ + let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]. + getService(Ci.nsIChromeRegistry); + + // If we don't have libxul or e10s then we don't have process separation, so + // we don't need to worry about checking for new chrome. + var appInfo = Cc["@mozilla.org/xre/app-info;1"]; + if (!appInfo || + (appInfo.getService(Ci.nsIXULRuntime).processType == + Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)) { + cr.checkForNewChrome(); + } + + // See if our various things were able to register + let registrationTypes = [ + "content", + "locale", + "skin", + "override", + "resource", + ]; + + for (let j = 0; j < registrationTypes.length; j++) { + let type = registrationTypes[j]; + dump("Testing type '" + type + "'\n"); + let expectedURI = "resource://foo/foo-" + type + "/"; + let sourceURI = "chrome://foo/" + type + "/"; + switch (type) { + case "content": + expectedURI += "foo.xul"; + break; + case "locale": + expectedURI += "foo.dtd"; + break; + case "skin": + expectedURI += "foo.css"; + break; + case "override": + sourceURI = "chrome://good-package/content/override-me.xul"; + expectedURI += "override-me.xul"; + break; + case "resource": + expectedURI = ios.newFileURI(manifestFile.parent).spec; + sourceURI = "resource://foo/"; + break; + }; + try { + sourceURI = ios.newURI(sourceURI, null, null); + let uri; + if (type == "resource") { + // resources go about a slightly different way than everything else + let rph = ios.getProtocolHandler("resource"). + QueryInterface(Ci.nsIResProtocolHandler); + uri = rph.resolveURI(sourceURI); + } + else { + uri = cr.convertChromeURL(sourceURI).spec; + } + + do_check_eq(expectedURI, uri); + } + catch (e) { + dump(e + "\n"); + do_throw("Should have registered a handler for type '" + + type + "'\n"); + } + } +} + +if (typeof run_test === "undefined") { + run_test = function() { + do_run_test(); + }; +} diff --git a/chrome/test/unit/xpcshell.ini b/chrome/test/unit/xpcshell.ini new file mode 100644 index 0000000000..3b9f84547f --- /dev/null +++ b/chrome/test/unit/xpcshell.ini @@ -0,0 +1,20 @@ +[DEFAULT] +head = head_crtestutils.js +tail = +support-files = data/** + +[test_abi.js] +[test_bug292789.js] +[test_bug380398.js] +[test_bug397073.js] +[test_bug399707.js] +[test_bug401153.js] +[test_bug415367.js] +[test_bug519468.js] +[test_bug564667.js] +tags = addons +[test_bug848297.js] +[test_crlf.js] +[test_data_protocol_registration.js] +[test_no_remote_registration.js] +[test_resolve_uris.js] diff --git a/chrome/test/unit_ipc/test_resolve_uris_ipc.js b/chrome/test/unit_ipc/test_resolve_uris_ipc.js new file mode 100644 index 0000000000..8babc23fd9 --- /dev/null +++ b/chrome/test/unit_ipc/test_resolve_uris_ipc.js @@ -0,0 +1,9 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// + +function run_test() { + load("../unit/test_resolve_uris.js"); + do_run_test(); + run_test_in_child("../unit/test_resolve_uris.js"); +} diff --git a/chrome/test/unit_ipc/xpcshell.ini b/chrome/test/unit_ipc/xpcshell.ini new file mode 100644 index 0000000000..1633bf995c --- /dev/null +++ b/chrome/test/unit_ipc/xpcshell.ini @@ -0,0 +1,10 @@ +[DEFAULT] +head = +tail = +skip-if = toolkit == 'android' +support-files = + !/chrome/test/unit/data/** + !/chrome/test/unit/test_resolve_uris.js + !/chrome/test/unit/head_crtestutils.js + +[test_resolve_uris_ipc.js] |