diff options
Diffstat (limited to 'chrome')
45 files changed, 3180 insertions, 0 deletions
diff --git a/chrome/RegistryMessageUtils.h b/chrome/RegistryMessageUtils.h new file mode 100644 index 0000000000..53ef7defc2 --- /dev/null +++ b/chrome/RegistryMessageUtils.h @@ -0,0 +1,159 @@ +/* -*- 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 "mozilla/ipc/IPDLParamTraits.h" +#include "nsString.h" + +struct SerializedURI { + nsCString spec; + + bool operator==(const SerializedURI& rhs) const { + return spec.Equals(rhs.spec); + } +}; + +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; + uint32_t flags; + + bool operator==(const SubstitutionMapping& rhs) const { + return scheme.Equals(rhs.scheme) && path.Equals(rhs.path) && + resolvedURI == rhs.resolvedURI && flags == rhs.flags; + } +}; + +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(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.spec); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + nsCString spec; + if (ReadParam(aReader, &spec)) { + aResult->spec = spec; + return true; + } + return false; + } +}; + +template <> +struct ParamTraits<ChromePackage> { + typedef ChromePackage paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.package); + WriteParam(aWriter, aParam.contentBaseURI); + WriteParam(aWriter, aParam.localeBaseURI); + WriteParam(aWriter, aParam.skinBaseURI); + WriteParam(aWriter, aParam.flags); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + nsCString package; + SerializedURI contentBaseURI, localeBaseURI, skinBaseURI; + uint32_t flags; + + if (ReadParam(aReader, &package) && ReadParam(aReader, &contentBaseURI) && + ReadParam(aReader, &localeBaseURI) && + ReadParam(aReader, &skinBaseURI) && ReadParam(aReader, &flags)) { + aResult->package = package; + aResult->contentBaseURI = contentBaseURI; + aResult->localeBaseURI = localeBaseURI; + aResult->skinBaseURI = skinBaseURI; + aResult->flags = flags; + return true; + } + return false; + } +}; + +template <> +struct ParamTraits<SubstitutionMapping> { + typedef SubstitutionMapping paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.scheme); + WriteParam(aWriter, aParam.path); + WriteParam(aWriter, aParam.resolvedURI); + WriteParam(aWriter, aParam.flags); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + nsCString scheme, path; + SerializedURI resolvedURI; + uint32_t flags; + + if (ReadParam(aReader, &scheme) && ReadParam(aReader, &path) && + ReadParam(aReader, &resolvedURI) && ReadParam(aReader, &flags)) { + aResult->scheme = scheme; + aResult->path = path; + aResult->resolvedURI = resolvedURI; + aResult->flags = flags; + return true; + } + return false; + } +}; + +template <> +struct ParamTraits<OverrideMapping> { + typedef OverrideMapping paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.originalURI); + WriteParam(aWriter, aParam.overrideURI); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + SerializedURI originalURI; + SerializedURI overrideURI; + + if (ReadParam(aReader, &originalURI) && ReadParam(aReader, &overrideURI)) { + aResult->originalURI = originalURI; + aResult->overrideURI = overrideURI; + return true; + } + return false; + } +}; + +} // namespace IPC + +#endif // RegistryMessageUtils_h diff --git a/chrome/moz.build b/chrome/moz.build new file mode 100644 index 0000000000..ddca6edbe4 --- /dev/null +++ b/chrome/moz.build @@ -0,0 +1,47 @@ +# -*- 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 += [ + "nsChromeProtocolHandler.h", +] + +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 CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] + +with Files("**"): + BUG_COMPONENT = ("Toolkit", "Startup and Profile System") diff --git a/chrome/nsChromeProtocolHandler.cpp b/chrome/nsChromeProtocolHandler.cpp new file mode 100644 index 0000000000..b2e1e4d361 --- /dev/null +++ b/chrome/nsChromeProtocolHandler.cpp @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=4 sw=2 sts=2 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 "nsContentUtils.h" +#include "nsThreadUtils.h" +#include "nsIChannel.h" +#include "nsIChromeRegistry.h" +#include "nsIFile.h" +#include "nsIFileChannel.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::AllowPort(int32_t port, const char* scheme, + bool* _retval) { + // don't override anything. + *_retval = false; + return NS_OK; +} + +/* static */ nsresult nsChromeProtocolHandler::CreateNewURI( + 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 + nsresult rv; + nsCOMPtr<nsIURI> surl; + rv = + NS_MutateURI(new mozilla::net::nsStandardURL::Mutator()) + .Apply(&nsIStandardURLMutator::Init, nsIStandardURL::URLTYPE_STANDARD, + -1, aSpec, aCharset, aBaseURI, nullptr) + + .Finalize(surl); + 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); + mozilla::Unused << NS_WARN_IF(NS_FAILED(rv)); + + surl.forget(result); + return NS_OK; +} + +NS_IMETHODIMP +nsChromeProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, + nsIChannel** aResult) { + nsresult rv; + + NS_ENSURE_ARG_POINTER(aURI); + NS_ENSURE_ARG_POINTER(aLoadInfo); + + MOZ_ASSERT(aResult, "Null out param"); + + nsCOMPtr<nsIURI> debugURL = aURI; + rv = nsChromeRegistry::Canonify(debugURL); + NS_ENSURE_SUCCESS(rv, rv); + + 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::GetChromeRegistry(); + 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; + } + + // We don't want to allow the inner protocol handler modify the result + // principal URI since we want either |aURI| or anything pre-set by upper + // layers to prevail. + nsCOMPtr<nsIURI> savedResultPrincipalURI; + rv = + aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI)); + NS_ENSURE_SUCCESS(rv, 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) { + printf("Chrome file doesn't exist: %s\n", + file->HumanReadablePath().get()); + } + } +#endif + + // Make sure that the channel remembers where it was + // originally loaded from. + rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI); + NS_ENSURE_SUCCESS(rv, rv); + rv = result->SetOriginalURI(aURI); + if (NS_FAILED(rv)) return rv; + + // Use a system principal for /content and /skin files. + // See bug 1855225 for discussion about whether to extend it more generally + // to other chrome:// URIs. + nsAutoCString path; + aURI->GetPathQueryRef(path); + if (StringBeginsWith(path, "/content/"_ns) || + StringBeginsWith(path, "/skin/"_ns)) { + result->SetOwner(nsContentUtils::GetSystemPrincipal()); + } + + // 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("UTF-8"_ns); + + result.forget(aResult); + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/nsChromeProtocolHandler.h b/chrome/nsChromeProtocolHandler.h new file mode 100644 index 0000000000..1fcced3dac --- /dev/null +++ b/chrome/nsChromeProtocolHandler.h @@ -0,0 +1,37 @@ +/* -*- 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 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() {} + static nsresult CreateNewURI(const nsACString& aSpec, const char* aCharset, + nsIURI* aBaseURI, nsIURI** result); + + private: + ~nsChromeProtocolHandler() {} +}; + +#endif /* nsChromeProtocolHandler_h___ */ diff --git a/chrome/nsChromeRegistry.cpp b/chrome/nsChromeRegistry.cpp new file mode 100644 index 0000000000..05f4d3dfe6 --- /dev/null +++ b/chrome/nsChromeRegistry.cpp @@ -0,0 +1,403 @@ +/* -*- 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 "nsCOMPtr.h" +#include "nsComponentManagerUtils.h" +#include "nsError.h" +#include "nsEscape.h" +#include "nsNetUtil.h" +#include "nsString.h" +#include "nsQueryObject.h" +#include "nsIURIMutator.h" +#include "nsIURL.h" + +#include "mozilla/dom/URL.h" +#include "nsIConsoleService.h" +#include "mozilla/dom/Document.h" +#include "nsIObserverService.h" +#include "nsIScriptError.h" +#include "mozilla/Preferences.h" +#include "mozilla/PresShell.h" +#include "mozilla/Printf.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/dom/Location.h" + +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::PresShell; +using mozilla::StyleSheet; +using mozilla::dom::Document; +using mozilla::dom::Location; + +//////////////////////////////////////////////////////////////////////////////// + +void nsChromeRegistry::LogMessage(const char* aMsg, ...) { + nsCOMPtr<nsIConsoleService> console( + do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + if (!console) return; + + va_list args; + va_start(args, aMsg); + mozilla::SmprintfPointer formatted = mozilla::Vsmprintf(aMsg, args); + va_end(args); + if (!formatted) return; + + console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted.get()).get()); +} + +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); + mozilla::SmprintfPointer formatted = mozilla::Vsmprintf(aMsg, args); + va_end(args); + if (!formatted) return; + + nsCString spec; + if (aURL) aURL->GetSpec(spec); + + rv = error->Init(NS_ConvertUTF8toUTF16(formatted.get()), + NS_ConvertUTF8toUTF16(spec), u""_ns, aLineNumber, 0, flags, + "chrome registration"_ns, false /* from private window */, + true /* from chrome context */); + + 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) + 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(nsIURI* aChromeURL, + nsACString& aProvider, + nsACString& aPath) { + nsresult rv; + + NS_ASSERTION(aChromeURL->SchemeIs("chrome"), "Non-chrome URI?"); + + nsAutoCString path; + rv = aChromeURL->GetPathQueryRef(path); + NS_ENSURE_SUCCESS(rv, rv); + + if (path.Length() < 3) { +#ifdef DEBUG + LogMessage("Invalid chrome URI (need path): %s", + aChromeURL->GetSpecOrDefault().get()); +#endif + 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) { +#ifdef DEBUG + LogMessage("Invalid chrome URI (path cannot start with another slash): %s", + aChromeURL->GetSpecOrDefault().get()); +#endif + 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(nsCOMPtr<nsIURI>& aChromeURL) { + constexpr auto kSlash = "/"_ns; + + 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; + } + return NS_MutateURI(aChromeURL).SetPathQueryRef(path).Finalize(aChromeURL); + } + + // prevent directory traversals ("..") + // path is already unescaped once, but uris can get unescaped twice + const char* pos = path.BeginReading(); + const char* end = path.EndReading(); + // Must start with [a-zA-Z0-9]. + if (!('a' <= *pos && *pos <= 'z') && !('A' <= *pos && *pos <= 'Z') && + !('0' <= *pos && *pos <= '9')) { + return NS_ERROR_DOM_BAD_URI; + } + 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 (!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); +} + +//////////////////////////////////////////////////////////////////////// + +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); +} + +NS_IMETHODIMP +nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, bool* aResult) { + nsresult rv; + *aResult = false; + + NS_ASSERTION(aChromeURI->SchemeIs("chrome"), + "Non-chrome URI passed to AllowScriptsForPackage!"); + + 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; + + NS_ASSERTION(aURI->SchemeIs("chrome"), + "Non-chrome URI passed to AllowContentToAccess!"); + + 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; + + NS_ASSERTION(aURI->SchemeIs("chrome"), + "Non-chrome URI passed to CanLoadURLRemotely!"); + + 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; + + NS_ASSERTION(aURI->SchemeIs("chrome"), + "Non-chrome URI passed to MustLoadURLRemotely!"); + + 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; +} + +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(); +} diff --git a/chrome/nsChromeRegistry.h b/chrome/nsChromeRegistry.h new file mode 100644 index 0000000000..e7cf735aa9 --- /dev/null +++ b/chrome/nsChromeRegistry.h @@ -0,0 +1,140 @@ +/* -*- 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" + +#include "nsString.h" +#include "nsURIHashKey.h" +#include "nsInterfaceHashtable.h" +#include "nsXULAppAPI.h" + +#include "mozilla/FileLocation.h" +#include "mozilla/intl/LocaleService.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, + public nsIObserver, + public nsSupportsWeakReference { + public: + NS_DECL_ISUPPORTS + + // nsIXULChromeRegistry methods: + 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; + + NS_IMETHOD ConvertChromeURL(nsIURI* aChromeURI, nsIURI** aResult) override; + + // nsChromeRegistry methods: + nsChromeRegistry() : mInitialized(false) {} + + virtual nsresult Init(); + + static already_AddRefed<nsIChromeRegistry> GetService(); + + static nsChromeRegistry* gChromeRegistry; + + // This method can change its parameter, so due to thread safety issues + // it should only be called for nsCOMPtr<nsIURI> that is on the stack, + // unless you know what you are doing. + static nsresult Canonify(nsCOMPtr<nsIURI>& aChromeURL); + + protected: + virtual ~nsChromeRegistry(); + + void FlushSkinCaches(); + void FlushAllCaches(); + + static void LogMessage(const char* aMsg, ...) MOZ_FORMAT_PRINTF(1, 2); + static void LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber, + uint32_t flags, const char* aMsg, ...) + MOZ_FORMAT_PRINTF(4, 5); + + virtual nsIURI* GetBaseURIFromPackage(const nsCString& aPackage, + const nsCString& aProvider, + const nsCString& aPath) = 0; + virtual nsresult GetFlagsFromPackage(const nsCString& aPackage, + uint32_t* aFlags) = 0; + + static nsresult RefreshWindow(nsPIDOMWindowOuter* aWindow); + static nsresult GetProviderAndPath(nsIURI* aChromeURL, nsACString& aProvider, + nsACString& aPath); + + public: + static already_AddRefed<nsChromeRegistry> GetSingleton(); + + struct ManifestProcessingContext { + ManifestProcessingContext(NSLocationType aType, + mozilla::FileLocation& aFile) + : mType(aType), mFile(aFile) {} + + ~ManifestProcessingContext() {} + + nsIURI* GetManifestURI(); + already_AddRefed<nsIURI> ResolveURI(const char* uri); + + NSLocationType mType; + mozilla::FileLocation mFile; + nsCOMPtr<nsIURI> mManifestURI; + }; + + 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 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 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..0bb93b4cfa --- /dev/null +++ b/chrome/nsChromeRegistryChrome.cpp @@ -0,0 +1,680 @@ +/* -*- 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 "nsIObserverService.h" +#include "mozilla/AppShutdown.h" +#include "mozilla/Components.h" +#include "mozilla/Preferences.h" +#include "nsIResProtocolHandler.h" +#include "nsIScriptError.h" +#include "nsIXULRuntime.h" + +#define PACKAGE_OVERRIDE_BRANCH "chrome.override_package." +#define SKIN "classic/1.0"_ns + +using namespace mozilla; +using mozilla::dom::ContentParent; +using mozilla::dom::PContentParent; +using mozilla::intl::LocaleService; + +// 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; + + bool safeMode = false; + nsCOMPtr<nsIXULRuntime> xulrun(do_GetService(XULAPPINFO_SERVICE_CONTRACTID)); + if (xulrun) xulrun->GetInSafeMode(&safeMode); + + nsCOMPtr<nsIObserverService> obsService = + mozilla::services::GetObserverService(); + if (obsService) { + obsService->AddObserver(this, "profile-initial-state", true); + obsService->AddObserver(this, "intl:app-locales-changed", true); + } + + 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; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::IsLocaleRTL(const nsACString& package, bool* aResult) { + *aResult = false; + + nsAutoCString locale; + GetSelectedLocale(package, locale); + if (locale.Length() < 2) return NS_OK; + + *aResult = LocaleService::IsLocaleRTL(locale); + return NS_OK; +} + +/** + * This method negotiates only between the app locale and the available + * chrome packages. + * + * If you want to get the current application's UI locale, please use + * LocaleService::GetAppLocaleAsBCP47. + */ +nsresult nsChromeRegistryChrome::GetSelectedLocale(const nsACString& aPackage, + nsACString& aLocale) { + nsAutoCString reqLocale; + if (aPackage.EqualsLiteral("global")) { + LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale); + } else { + AutoTArray<nsCString, 10> requestedLocales; + LocaleService::GetInstance()->GetRequestedLocales(requestedLocales); + reqLocale.Assign(requestedLocales[0]); + } + + 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(reqLocale, nsProviderArray::LOCALE); + if (aLocale.IsEmpty()) return NS_ERROR_FAILURE; + + return NS_OK; +} + +nsresult nsChromeRegistryChrome::OverrideLocalePackage( + const nsACString& aPackage, nsACString& aOverride) { + const nsACString& pref = nsLiteralCString(PACKAGE_OVERRIDE_BRANCH) + aPackage; + nsAutoCString override; + nsresult rv = mozilla::Preferences::GetCString(PromiseFlatCString(pref).get(), + override); + if (NS_SUCCEEDED(rv)) { + aOverride = override; + } else { + aOverride = aPackage; + } + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* someData) { + nsresult rv = NS_OK; + + if (!strcmp("profile-initial-state", aTopic)) { + mProfileLoaded = true; + } else if (!strcmp("intl:app-locales-changed", aTopic)) { + if (mProfileLoaded) { + FlushAllCaches(); + } + } else { + NS_ERROR("Unexpected observer topic!"); + } + + return rv; +} + +NS_IMETHODIMP +nsChromeRegistryChrome::CheckForNewChrome() { + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + MOZ_ASSERT(false, "checking for new chrome during shutdown"); + return NS_ERROR_UNEXPECTED; + } + + mPackagesHash.Clear(); + mOverrideTable.Clear(); + + mDynamicRegistration = false; + + nsComponentManagerImpl::gComponentManager->RereadChromeManifests(); + + mDynamicRegistration = true; + + SendRegisteredChrome(nullptr); + return NS_OK; +} + +static void SerializeURI(nsIURI* aURI, SerializedURI& aSerializedURI) { + if (!aURI) return; + + aURI->GetSpec(aSerializedURI.spec); +} + +void nsChromeRegistryChrome::SendRegisteredChrome( + mozilla::dom::PContentParent* aParent) { + nsTArray<ChromePackage> packages; + nsTArray<SubstitutionMapping> resources; + nsTArray<OverrideMapping> overrides; + + for (const auto& entry : mPackagesHash) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(entry.GetKey(), entry.GetWeak(), + &chromePackage, SKIN); + 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 (const auto& entry : mOverrideTable) { + SerializedURI chromeURI, overrideURI; + + SerializeURI(entry.GetKey(), chromeURI); + SerializeURI(entry.GetWeak(), overrideURI); + + overrides.AppendElement( + OverrideMapping{std::move(chromeURI), std::move(overrideURI)}); + } + + nsAutoCString appLocale; + LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale); + + if (aParent) { + bool success = aParent->SendRegisterChrome(packages, resources, overrides, + appLocale, 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, appLocale, 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& aSelectedSkin) { + nsAutoCString appLocale; + LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale); + + SerializeURI(aPackage->baseURI, aChromePackage->contentBaseURI); + SerializeURI(aPackage->locales.GetBase(appLocale, 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")) { + nsAutoCString appLocale; + LocaleService::GetInstance()->GetAppLocaleAsLangTag(appLocale); + return entry->locales.GetBase(appLocale, nsProviderArray::LOCALE); + } + + if (aProvider.EqualsLiteral("skin")) { + return entry->skins.GetBase(SKIN, nsProviderArray::ANY); + } + + 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); + } +} + +nsIURI* nsChromeRegistry::ManifestProcessingContext::GetManifestURI() { + if (!mManifestURI) { + nsCString uri; + mFile.GetURIString(uri); + NS_NewURI(getter_AddRefs(mManifestURI), uri); + } + return mManifestURI; +} + +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.GetOrInsertNew(packageName); + entry->baseURI = resolved; + entry->flags = flags; + + if (mDynamicRegistration) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN); + 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.GetOrInsertNew(packageName); + entry->locales.SetBase(nsDependentCString(provider), resolved); + + if (mDynamicRegistration) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN); + SendManifestEntry(chromePackage); + } + + // We use mainPackage as the package we track for reporting new locales being + // registered. For most cases it will be "global", but for Fennec it will be + // "browser". + nsAutoCString mainPackage; + nsresult rv = OverrideLocalePackage("global"_ns, mainPackage); + if (NS_FAILED(rv)) { + return; + } +} + +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.GetOrInsertNew(packageName); + entry->skins.SetBase(nsDependentCString(provider), resolved); + + if (mDynamicRegistration) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN); + SendManifestEntry(chromePackage); + } +} + +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 = + chromeuri->SchemeIs("chrome") && resolveduri->SchemeIs("chrome"); + if (chromeSkinOnly) { + nsAutoCString chromePath, resolvedPath; + chromeuri->GetPathQueryRef(chromePath); + resolveduri->GetPathQueryRef(resolvedPath); + chromeSkinOnly = StringBeginsWith(chromePath, "/skin/"_ns) && + StringBeginsWith(resolvedPath, "/skin/"_ns); + } + 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.InsertOrUpdate(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::components::IO::Service(); + 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; + } + + // By default, Firefox resources are not content-accessible unless the + // manifests opts in. + bool contentAccessible = (flags & nsChromeRegistry::CONTENT_ACCESSIBLE); + + uint32_t substitutionFlags = 0; + if (contentAccessible) { + substitutionFlags |= nsIResProtocolHandler::ALLOW_CONTENT_ACCESS; + } + rv = rph->SetSubstitutionWithFlags(host, resolved, substitutionFlags); + if (NS_FAILED(rv)) { + LogMessageWithContext(cx.GetManifestURI(), lineno, + nsIScriptError::warningFlag, + "Warning: cannot set substitution for '%s'.", uri); + } +} diff --git a/chrome/nsChromeRegistryChrome.h b/chrome/nsChromeRegistryChrome.h new file mode 100644 index 0000000000..df100bc3df --- /dev/null +++ b/chrome/nsChromeRegistryChrome.h @@ -0,0 +1,121 @@ +/* -*- 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 <utility> + +#include "nsCOMArray.h" +#include "nsChromeRegistry.h" +#include "nsClassHashtable.h" +#include "nsTArray.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 GetLocalesForPackage(const nsACString& aPackage, + nsIUTF8StringEnumerator** aResult) override; + NS_IMETHOD IsLocaleRTL(const nsACString& package, bool* aResult) override; + nsresult GetSelectedLocale(const nsACString& aPackage, nsACString& aLocale); + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* someData) override; + + // 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& aSelectedSkin); + + nsresult OverrideLocalePackage(const nsACString& aPackage, + nsACString& aOverride); + 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; + }; + + bool mProfileLoaded; + bool mDynamicRegistration; + + // 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 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..9b213a3ab9 --- /dev/null +++ b/chrome/nsChromeRegistryContent.cpp @@ -0,0 +1,203 @@ +/* -*- 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 "nsISubstitutingProtocolHandler.h" +#include "nsString.h" +#include "nsNetUtil.h" +#include "mozilla/UniquePtr.h" + +nsChromeRegistryContent::nsChromeRegistryContent() {} + +void nsChromeRegistryContent::RegisterRemoteChrome( + const nsTArray<ChromePackage>& aPackages, + const nsTArray<SubstitutionMapping>& aSubstitutions, + const nsTArray<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<nsIURI> content, locale, skin; + + if (aPackage.contentBaseURI.spec.Length()) { + nsresult rv = + NS_NewURI(getter_AddRefs(content), aPackage.contentBaseURI.spec); + if (NS_FAILED(rv)) return; + } + if (aPackage.localeBaseURI.spec.Length()) { + nsresult rv = + NS_NewURI(getter_AddRefs(locale), aPackage.localeBaseURI.spec); + if (NS_FAILED(rv)) return; + } + if (aPackage.skinBaseURI.spec.Length()) { + nsresult rv = NS_NewURI(getter_AddRefs(skin), aPackage.skinBaseURI.spec); + if (NS_FAILED(rv)) return; + } + + mozilla::UniquePtr<PackageEntry> entry = mozilla::MakeUnique<PackageEntry>(); + entry->flags = aPackage.flags; + entry->contentBaseURI = content; + entry->localeBaseURI = locale; + entry->skinBaseURI = skin; + + mPackagesHash.InsertOrUpdate(aPackage.package, std::move(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); + if (NS_FAILED(rv)) return; + } + + rv = sph->SetSubstitutionWithFlags(aSubstitution.path, resolvedURI, + aSubstitution.flags); + if (NS_FAILED(rv)) return; +} + +void nsChromeRegistryContent::RegisterOverride( + const OverrideMapping& aOverride) { + nsCOMPtr<nsIURI> chromeURI, overrideURI; + nsresult rv = + NS_NewURI(getter_AddRefs(chromeURI), aOverride.originalURI.spec); + if (NS_FAILED(rv)) return; + + rv = NS_NewURI(getter_AddRefs(overrideURI), aOverride.overrideURI.spec); + if (NS_FAILED(rv)) return; + + mOverrideTable.InsertOrUpdate(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; + } + + if (aProvider.EqualsLiteral("skin")) { + return entry->skinBaseURI; + } + + 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() \ + MOZ_ASSERT_UNREACHABLE("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::CheckForNewChrome() { CONTENT_NOT_IMPLEMENTED(); } + +NS_IMETHODIMP +nsChromeRegistryContent::IsLocaleRTL(const nsACString& aPackage, + bool* aResult) { + *aResult = mozilla::intl::LocaleService::IsLocaleRTL(mLocale); + return NS_OK; +} + +NS_IMETHODIMP +nsChromeRegistryContent::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + 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::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..7d16eedc51 --- /dev/null +++ b/chrome/nsChromeRegistryContent.h @@ -0,0 +1,68 @@ +/* -*- 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 nsTArray<ChromePackage>& aPackages, + const nsTArray<SubstitutionMapping>& aResources, + const nsTArray<OverrideMapping>& aOverrides, + const nsACString& aLocale, bool aReset); + + NS_IMETHOD GetLocalesForPackage(const nsACString& aPackage, + nsIUTF8StringEnumerator** aResult) override; + NS_IMETHOD CheckForNewChrome() override; + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override; + NS_IMETHOD IsLocaleRTL(const nsACString& package, bool* 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; + }; + + 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 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..7a1c56de4e --- /dev/null +++ b/chrome/nsIChromeRegistry.idl @@ -0,0 +1,91 @@ +/* -*- 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(); +}; + +[scriptable, uuid(93251ddf-5e85-4172-ac2a-31780562974f)] +interface nsIXULChromeRegistry : nsIChromeRegistry +{ + // Get whether the default writing direction of the locale is RTL + boolean isLocaleRTL(in ACString package); + + /** + * 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" + +%} diff --git a/chrome/nsIToolkitChromeRegistry.idl b/chrome/nsIToolkitChromeRegistry.idl new file mode 100644 index 0000000000..080dd33555 --- /dev/null +++ b/chrome/nsIToolkitChromeRegistry.idl @@ -0,0 +1,18 @@ +/* -*- 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 +{ + /** + * 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..3f3720e8eb --- /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.toml", + "unit_ipc/xpcshell.toml", +] 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..dd7fbd7796 --- /dev/null +++ b/chrome/test/unit/data/test_bug564667/chrome.manifest @@ -0,0 +1,12 @@ +# 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 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..ceaa80c509 --- /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/xhtml+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..d20486c385 --- /dev/null +++ b/chrome/test/unit/head_crtestutils.js @@ -0,0 +1,9 @@ +const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; +const XULAPPINFO_CID = Components.ID("{4ba645d3-be6f-40d6-a42a-01b2f40091b8}"); + +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..13eaac36ab --- /dev/null +++ b/chrome/test/unit/test_abi.js @@ -0,0 +1,27 @@ +const { updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +registerManifests([do_get_file("data/test_abi.manifest")]); + +function is_registered(name) { + try { + var d = Services.catMan.getCategoryEntry("abitest", name); + return d == "found"; + } catch (e) { + return false; + } +} + +function run_test() { + Assert.ok(is_registered("test1")); + Assert.ok(!is_registered("test2")); + Assert.ok(is_registered("test3")); + Assert.ok(!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..5bc6ac417d --- /dev/null +++ b/chrome/test/unit/test_bug292789.js @@ -0,0 +1,31 @@ +/* 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 gCR; + +function check_accessibility(spec, desired) { + var uri = Services.io.newURI(spec); + var actual = gCR.allowContentToAccess(uri); + Assert.equal(desired, actual); +} + +function run_test() { + 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..5229f82918 --- /dev/null +++ b/chrome/test/unit/test_bug380398.js @@ -0,0 +1,58 @@ +/* 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); + +const { updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); +updateAppInfo({ + name: "XPCShell", + id: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( + Ci.nsIChromeRegistry +); +chromeReg.checkForNewChrome(); + +var target = Services.io.newFileURI(do_get_file("data")); +target = target.spec + "test/test.xul"; + +function test_succeeded_mapping(namespace) { + var uri = Services.io.newURI("chrome://" + namespace + "/content/test.xul"); + try { + var result = chromeReg.convertChromeURL(uri); + Assert.equal(result.spec, target); + } catch (ex) { + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) { + var uri = Services.io.newURI("chrome://" + namespace + "/content/test.xul"); + try { + 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..0fe47ae69d --- /dev/null +++ b/chrome/test/unit/test_bug397073.js @@ -0,0 +1,51 @@ +/* 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); + +const { updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( + Ci.nsIChromeRegistry +); +chromeReg.checkForNewChrome(); + +var target = Services.io.newFileURI(do_get_file("data")); +target = target.spec + "test/test.xul"; + +function test_succeeded_mapping(namespace) { + var uri = Services.io.newURI("chrome://" + namespace + "/content/test.xul"); + try { + var result = chromeReg.convertChromeURL(uri); + Assert.equal(result.spec, target); + } catch (ex) { + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) { + var uri = Services.io.newURI("chrome://" + namespace + "/content/test.xul"); + try { + 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..c36ec49927 --- /dev/null +++ b/chrome/test/unit/test_bug399707.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_bug399707.manifest")]; + +registerManifests(MANIFESTS); + +const { updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( + Ci.nsIChromeRegistry +); +chromeReg.checkForNewChrome(); + +var target = Services.io.newFileURI(do_get_file("data")); +target = target.spec + "test/test.xul"; + +function test_succeeded_mapping(namespace) { + var uri = Services.io.newURI("chrome://" + namespace + "/content/test.xul"); + try { + var result = chromeReg.convertChromeURL(uri); + Assert.equal(result.spec, target); + } catch (ex) { + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) { + var uri = Services.io.newURI("chrome://" + namespace + "/content/test.xul"); + try { + 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..efc4028c2f --- /dev/null +++ b/chrome/test/unit/test_bug401153.js @@ -0,0 +1,53 @@ +/* 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); + +const { updateAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" +); +updateAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", +}); + +var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( + Ci.nsIChromeRegistry +); +chromeReg.checkForNewChrome(); + +var rph = Services.io + .getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); + +function test_succeeded_mapping(namespace, target) { + try { + Assert.ok(rph.hasSubstitution(namespace)); + var uri = Services.io.newURI("resource://" + namespace); + dump( + "### checking for " + target + ", getting " + rph.resolveURI(uri) + "\n" + ); + Assert.equal(rph.resolveURI(uri), target); + } catch (ex) { + dump(ex + "\n"); + do_throw(namespace); + } +} + +function test_failed_mapping(namespace) { + Assert.ok(!rph.hasSubstitution(namespace)); +} + +function run_test() { + var data = Services.io.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..7af77165a7 --- /dev/null +++ b/chrome/test/unit/test_bug415367.js @@ -0,0 +1,47 @@ +/* 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/. + */ + +function test_uri(obj) { + var uri = null; + var failed = false; + var message = ""; + try { + uri = Services.io.newURI(obj.uri); + 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) { + Assert.ok(uri != null); + Assert.equal(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: true }, + { uri: "chrome://blah/content/%252e./blah/blah.xul", result: true }, + { uri: "chrome://blah/content/%252e%252e/blah/blah.xul", result: true }, + { 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_bug564667.js b/chrome/test/unit/test_bug564667.js new file mode 100644 index 0000000000..7ee5229827 --- /dev/null +++ b/chrome/test/unit/test_bug564667.js @@ -0,0 +1,86 @@ +/* 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 gCR = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( + Ci.nsIChromeRegistry +); + +/* + * Checks that a mapping was added + */ +function test_mapping(chromeURL, target) { + var uri = Services.io.newURI(chromeURL); + + try { + var result = gCR.convertChromeURL(uri); + Assert.equal(result.spec, target); + } catch (ex) { + do_throw(chromeURL + " not Registered"); + } +} + +/* + * Checks that a mapping was removed + */ +function test_removed_mapping(chromeURL, target) { + var uri = Services.io.newURI(chromeURL); + try { + gCR.convertChromeURL(uri); + do_throw(chromeURL + " not removed"); + } catch (ex) { + // This should throw + } +} + +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"); + + // ------------------ 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, Services.io.newFileURI(UNPACKAGED_ADDON).spec); + + // Test a packaged addon + testManifest( + PACKAGED_ADDON, + "jar:" + Services.io.newFileURI(PACKAGED_ADDON).spec + "!/" + ); +} diff --git a/chrome/test/unit/test_create_channel_chrome_url.js b/chrome/test/unit/test_create_channel_chrome_url.js new file mode 100644 index 0000000000..2d1c727b3b --- /dev/null +++ b/chrome/test/unit/test_create_channel_chrome_url.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +const { NetUtil } = ChromeUtils.importESModule( + "resource://gre/modules/NetUtil.sys.mjs" +); + +function testURL(url) { + Services.io.newChannelFromURI( + NetUtil.newURI(url), + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + Ci.nsIContentPolicy.TYPE_OTHER + ); +} + +add_task(async function test_create_channel_with_chrome_url() { + try { + testURL("chrome://path"); + Assert.ok(false); + } catch (e) { + // Chrome url fails canonicalization + Assert.equal(e.result, Cr.NS_ERROR_FAILURE); + } + + try { + testURL("chrome://path/path/path"); + Assert.ok(false); + } catch (e) { + // Chrome url passes canonicalization + Assert.equal(e.result, Cr.NS_ERROR_FILE_NOT_FOUND); + } +}); diff --git a/chrome/test/unit/test_crlf.js b/chrome/test/unit/test_crlf.js new file mode 100644 index 0000000000..73045d3201 --- /dev/null +++ b/chrome/test/unit/test_crlf.js @@ -0,0 +1,13 @@ +registerManifests([do_get_file("data/test_crlf.manifest")]); + +function run_test() { + let cr = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( + Ci.nsIChromeRegistry + ); + + let sourceURI = Services.io.newURI("chrome://test_crlf/content/"); + // this throws for packages that are not registered + let file = cr.convertChromeURL(sourceURI).QueryInterface(Ci.nsIFileURL).file; + + Assert.ok(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..871f0e8cb3 --- /dev/null +++ b/chrome/test/unit/test_data_protocol_registration.js @@ -0,0 +1,115 @@ +/* -*- 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); + +function run_test() { + const uuidGenerator = Services.uuid; + + let { newAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" + ); + let XULAppInfo = newAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", + }); + + let XULAppInfoFactory = { + // These two are used when we register all our factories (and unregister) + CID: uuidGenerator.generateUUID(), + scheme: "XULAppInfo", + contractID: XULAPPINFO_CONTRACTID, + createInstance(iid) { + return XULAppInfo.QueryInterface(iid); + }, + }; + + // Add our XULAppInfo factory + let factories = [XULAppInfoFactory]; + let old_factories = []; + let old_factories_inds = []; + + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + // Register our factories + for (let i = 0; i < factories.length; i++) { + let factory = factories[i]; + + // Make sure the class ID has not already been registered + if (!registrar.isCIDRegistered(factory.CID)) { + // Check to see if a contract was already registered and + // register it if it is not. Otherwise, store the previous one + // to be restored later and register the new one. + if (registrar.isContractIDRegistered(factory.contractID)) { + dump( + factory.scheme + + " is already registered. Storing currently registered object for restoration later." + ); + old_factories.push({ + CID: registrar.contractIDToCID(factory.contractID), + factory: Components.manager.getClassObject( + Cc[factory.contractID], + Ci.nsIFactory + ), + }); + old_factories_inds.push(true); + } else { + dump(factory.scheme + " has never been registered. Registering..."); + old_factories.push({ CID: "", factory: null }); + old_factories_inds.push(false); + } + + registrar.registerFactory( + factory.CID, + "test-" + factory.scheme, + factory.contractID, + factory + ); + } else { + do_throw("CID " + factory.CID + " has already been registered!"); + } + } + + // 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/xhtml+xml,"; + let sourceURI = "chrome://good-package/content/test.xul"; + try { + sourceURI = Services.io.newURI(sourceURI); + // this throws for packages that are not registered + let uri = cr.convertChromeURL(sourceURI).spec; + + Assert.equal(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]; + let ind = old_factories_inds[i]; + registrar.unregisterFactory(factory.CID, factory); + + if (ind) { + let old_factory = old_factories[i]; + registrar.registerFactory( + old_factory.CID, + factory.scheme, + factory.contractID, + null + ); + } + } +} 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..46303ef312 --- /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); + +function ProtocolHandler(aScheme) { + this.scheme = aScheme; +} + +ProtocolHandler.prototype = { + allowPort: () => false, + newChannel() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + QueryInterface: ChromeUtils.generateQI(["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, + 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, + shouldRegister: false, + }, + // This clearly is non-local + { + scheme: "moz-protocol-loadable-by-anyone", + flags: Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE, + 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, + shouldRegister: true, + }, +]; +function run_test() { + const { newAppInfo } = ChromeUtils.importESModule( + "resource://testing-common/AppInfo.sys.mjs" + ); + let XULAppInfo = newAppInfo({ + name: "XPCShell", + ID: "{39885e5f-f6b4-4e2a-87e5-6259ecf79011}", + version: "5", + platformVersion: "1.9", + }); + + const uuidGenerator = Services.uuid; + + let XULAppInfoFactory = { + // These two are used when we register all our factories (and unregister) + CID: uuidGenerator.generateUUID(), + scheme: "XULAppInfo", + contractID: "@mozilla.org/xre/app-info;1", + createInstance(iid) { + return XULAppInfo.QueryInterface(iid); + }, + }; + + for (let protocol of testProtocols) { + Services.io.registerProtocolHandler( + protocol.scheme, + new ProtocolHandler(protocol.scheme), + protocol.flags, + -1 + ); + } + + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + // Register the XULAppInfoFactory + // Make sure the class ID has not already been registered + let old_factory = { CID: "", factory: null }; + if (!registrar.isCIDRegistered(XULAppInfoFactory.CID)) { + // Check to see if a contract was already registered and + // register it if it is not. Otherwise, store the previous one + // to be restored later and register the new one. + if (registrar.isContractIDRegistered(XULAppInfoFactory.contractID)) { + dump( + XULAppInfoFactory.scheme + + " is already registered. Storing currently registered object for restoration later." + ); + old_factory.CID = registrar.contractIDToCID(XULAppInfoFactory.contractID); + old_factory.factory = Components.manager.getClassObject( + Cc[XULAppInfoFactory.contractID], + Ci.nsIFactory + ); + } else { + dump( + XULAppInfoFactory.scheme + " has never been registered. Registering..." + ); + } + + registrar.registerFactory( + XULAppInfoFactory.CID, + "test-" + XULAppInfoFactory.scheme, + XULAppInfoFactory.contractID, + XULAppInfoFactory + ); + } else { + do_throw("CID " + XULAppInfoFactory.CID + " has already been registered!"); + } + + // 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 { + sourceURI = Services.io.newURI(sourceURI); + let uri; + if (type == "resource") { + // resources go about a slightly different way than everything else + let rph = Services.io + .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) { + Assert.equal(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. + Assert.notEqual(expectedURI, uri); + } + } catch (e) { + if (protocol.shouldRegister) { + dump(e + "\n"); + do_throw( + "Should have registered our URI for protocol " + protocol.scheme + ); + } + } + } + } + + // Unregister our protocol handlers so we do not leak + for (let protocol of testProtocols) { + Services.io.unregisterProtocolHandler(protocol.scheme); + } + + // Unregister XULAppInfoFactory + registrar.unregisterFactory(XULAppInfoFactory.CID, XULAppInfoFactory); + if (old_factory.factory != null) { + registrar.registerFactory( + old_factory.CID, + "", + XULAppInfoFactory.contractID, + null + ); + } +} diff --git a/chrome/test/unit/test_resolve_uris.js b/chrome/test/unit/test_resolve_uris.js new file mode 100644 index 0000000000..638a0f9b09 --- /dev/null +++ b/chrome/test/unit/test_resolve_uris.js @@ -0,0 +1,86 @@ +/* -*- 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); + +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 || + // eslint-disable-next-line mozilla/use-services + 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 = Services.io.newFileURI(manifestFile.parent).spec; + sourceURI = "resource://foo/"; + break; + } + try { + sourceURI = Services.io.newURI(sourceURI); + let uri; + if (type == "resource") { + // resources go about a slightly different way than everything else + let rph = Services.io + .getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); + uri = rph.resolveURI(sourceURI); + } else { + uri = cr.convertChromeURL(sourceURI).spec; + } + + Assert.equal(expectedURI, uri); + } catch (e) { + dump(e + "\n"); + do_throw("Should have registered a handler for type '" + type + "'\n"); + } + } +} + +if (typeof run_test === "undefined") { + // eslint-disable-next-line no-global-assign + run_test = function () { + do_run_test(); + }; +} diff --git a/chrome/test/unit/xpcshell.toml b/chrome/test/unit/xpcshell.toml new file mode 100644 index 0000000000..3a94c7b5aa --- /dev/null +++ b/chrome/test/unit/xpcshell.toml @@ -0,0 +1,30 @@ +[DEFAULT] +head = "head_crtestutils.js" +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_bug564667.js"] +tags = "addons" + +["test_create_channel_chrome_url.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..35eef62d1c --- /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() { + /* globals do_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.toml b/chrome/test/unit_ipc/xpcshell.toml new file mode 100644 index 0000000000..2beb2c849d --- /dev/null +++ b/chrome/test/unit_ipc/xpcshell.toml @@ -0,0 +1,10 @@ +[DEFAULT] +head = "" +skip-if = ["os == '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"] |