summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /chrome
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'chrome')
-rw-r--r--chrome/RegistryMessageUtils.h159
-rw-r--r--chrome/moz.build47
-rw-r--r--chrome/nsChromeProtocolHandler.cpp166
-rw-r--r--chrome/nsChromeProtocolHandler.h37
-rw-r--r--chrome/nsChromeRegistry.cpp403
-rw-r--r--chrome/nsChromeRegistry.h140
-rw-r--r--chrome/nsChromeRegistryChrome.cpp680
-rw-r--r--chrome/nsChromeRegistryChrome.h121
-rw-r--r--chrome/nsChromeRegistryContent.cpp203
-rw-r--r--chrome/nsChromeRegistryContent.h68
-rw-r--r--chrome/nsIChromeRegistry.idl91
-rw-r--r--chrome/nsIToolkitChromeRegistry.idl18
-rw-r--r--chrome/test/moz.build10
-rw-r--r--chrome/test/unit/data/test_abi.manifest4
-rw-r--r--chrome/test/unit/data/test_bug292789.manifest4
-rw-r--r--chrome/test/unit/data/test_bug380398.manifest14
-rw-r--r--chrome/test/unit/data/test_bug397073.manifest6
-rw-r--r--chrome/test/unit/data/test_bug399707.manifest11
-rw-r--r--chrome/test/unit/data/test_bug401153.manifest11
-rw-r--r--chrome/test/unit/data/test_bug519468.manifest4
-rw-r--r--chrome/test/unit/data/test_bug564667.xpibin0 -> 569 bytes
-rw-r--r--chrome/test/unit/data/test_bug564667/chrome.manifest12
-rw-r--r--chrome/test/unit/data/test_bug564667/loaded.manifest2
-rw-r--r--chrome/test/unit/data/test_bug848297.manifest4
-rw-r--r--chrome/test/unit/data/test_crlf.manifest3
-rw-r--r--chrome/test/unit/data/test_data_protocol_registration.manifest5
-rw-r--r--chrome/test/unit/data/test_no_remote_registration.manifest32
-rw-r--r--chrome/test/unit/data/test_resolve_uris.manifest5
-rw-r--r--chrome/test/unit/head_crtestutils.js9
-rw-r--r--chrome/test/unit/test_abi.js27
-rw-r--r--chrome/test/unit/test_bug292789.js31
-rw-r--r--chrome/test/unit/test_bug380398.js58
-rw-r--r--chrome/test/unit/test_bug397073.js51
-rw-r--r--chrome/test/unit/test_bug399707.js55
-rw-r--r--chrome/test/unit/test_bug401153.js53
-rw-r--r--chrome/test/unit/test_bug415367.js47
-rw-r--r--chrome/test/unit/test_bug564667.js86
-rw-r--r--chrome/test/unit/test_create_channel_chrome_url.js38
-rw-r--r--chrome/test/unit/test_crlf.js13
-rw-r--r--chrome/test/unit/test_data_protocol_registration.js115
-rw-r--r--chrome/test/unit/test_no_remote_registration.js202
-rw-r--r--chrome/test/unit/test_resolve_uris.js86
-rw-r--r--chrome/test/unit/xpcshell.toml30
-rw-r--r--chrome/test/unit_ipc/test_resolve_uris_ipc.js9
-rw-r--r--chrome/test/unit_ipc/xpcshell.toml10
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
new file mode 100644
index 0000000000..be1632d8ca
--- /dev/null
+++ b/chrome/test/unit/data/test_bug564667.xpi
Binary files differ
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"]