Bug 863246 - Content can only load resource:// URIs declared content-accessible in manifests r=billm,bz
authorChung-Sheng Fu <cfu@mozilla.com>
Thu, 08 Jun 2017 17:44:09 +0800
changeset 426731 a676a11885728e790fc81eebb60b8f574ff100d6
parent 426730 40bc1d4ce54b34b3851d52576e1b16f68af3bd48
child 426732 ac41228312b850e5ee20936dfb1c1236788e7ec9
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm, bz
bugs863246
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 863246 - Content can only load resource:// URIs declared content-accessible in manifests r=billm,bz bz: caps/nsScriptSecurityManager.cpp billm: browser/extensions/activity-stream/jar.mn browser/extensions/onboarding/jar.mn chrome/RegistryMessageUtils.h chrome/nsChromeRegistry.h chrome/nsChromeRegistryChrome.cpp chrome/nsChromeRegistryContent.cpp netwerk/protocol/res/SubstitutingProtocolHandler.cpp netwerk/protocol/res/SubstitutingProtocolHandler.h netwerk/protocol/res/nsIResProtocolHandler.idl netwerk/protocol/res/nsISubstitutingProtocolHandler.idl netwerk/protocol/res/nsResProtocolHandler.cpp netwerk/protocol/res/nsResProtocolHandler.h xpcom/components/ManifestParser.cpp MozReview-Commit-ID: 1RXeNn7jdBf
browser/extensions/activity-stream/jar.mn
browser/extensions/onboarding/jar.mn
browser/extensions/shield-recipe-client/jar.mn
caps/nsScriptSecurityManager.cpp
chrome/RegistryMessageUtils.h
chrome/nsChromeRegistryChrome.cpp
chrome/nsChromeRegistryContent.cpp
netwerk/protocol/res/SubstitutingProtocolHandler.cpp
netwerk/protocol/res/SubstitutingProtocolHandler.h
netwerk/protocol/res/nsIResProtocolHandler.idl
netwerk/protocol/res/nsISubstitutingProtocolHandler.idl
netwerk/protocol/res/nsResProtocolHandler.cpp
netwerk/protocol/res/nsResProtocolHandler.h
xpcom/components/ManifestParser.cpp
--- a/browser/extensions/activity-stream/jar.mn
+++ b/browser/extensions/activity-stream/jar.mn
@@ -1,14 +1,14 @@
 # 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/.
 
 [features/activity-stream@mozilla.org] chrome.jar:
-% resource activity-stream %content/
+% resource activity-stream %content/ contentaccessible=yes
   content/lib/ (./lib/*)
   content/common/ (./common/*)
   content/vendor/Redux.jsm (./vendor/Redux.jsm)
   content/vendor/react.js (./vendor/react.js)
   content/vendor/react-dom.js (./vendor/react-dom.js)
   content/vendor/react-intl.js (./vendor/react-intl.js)
   content/vendor/redux.js (./vendor/redux.js)
   content/vendor/react-redux.js (./vendor/react-redux.js)
--- a/browser/extensions/onboarding/jar.mn
+++ b/browser/extensions/onboarding/jar.mn
@@ -1,12 +1,14 @@
 # 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/.
 
 [features/onboarding@mozilla.org] chrome.jar:
-% resource onboarding %content/
+  # resource://onboarding/ is referenced in about:home and about:newtab,
+  # so make it content-accessible.
+% resource onboarding %content/ contentaccessible=yes
   content/ (content/*)
   # Package UITour-lib.js in here rather than under
   # /browser/components/uitour to avoid "unreferenced files" error when
   # Onboarding extension is not built.
   content/lib/UITour-lib.js (/browser/components/uitour/UITour-lib.js)
   content/modules/ (*.jsm)
--- a/browser/extensions/shield-recipe-client/jar.mn
+++ b/browser/extensions/shield-recipe-client/jar.mn
@@ -2,12 +2,12 @@
 # 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/.
 
 [features/shield-recipe-client@mozilla.org] chrome.jar:
 % resource shield-recipe-client %
   lib/ (./lib/*)
   data/ (./data/*)
   skin/  (skin/*)
-% resource shield-recipe-client-content %content/
+% resource shield-recipe-client-content %content/ contentaccessible=yes
   content/ (./content/*)
-% resource shield-recipe-client-vendor %vendor/
+% resource shield-recipe-client-vendor %vendor/ contentaccessible=yes
   vendor/ (./vendor/*)
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -48,16 +48,17 @@
 #include "nsIOService.h"
 #include "nsIContent.h"
 #include "nsDOMJSUtils.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsIClassInfo.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIChromeRegistry.h"
+#include "nsIResProtocolHandler.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include <stdint.h>
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
@@ -910,20 +911,19 @@ nsScriptSecurityManager::CheckLoadURIFla
     // Check for chrome target URI
     bool hasFlags = false;
     rv = NS_URIChainHasFlags(aTargetBaseURI,
                              nsIProtocolHandler::URI_IS_UI_RESOURCE,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     if (hasFlags) {
         if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
-
-            // For now, don't change behavior for resource:// or moz-icon:// and
-            // just allow them.
-            if (!targetScheme.EqualsLiteral("chrome")) {
+            // For now, don't change behavior for moz-icon:// and just allow it.
+            if (!targetScheme.EqualsLiteral("chrome")
+                    && !targetScheme.EqualsLiteral("resource")) {
                 return NS_OK;
             }
 
             // Allow a URI_IS_UI_RESOURCE source to link to a URI_IS_UI_RESOURCE
             // target if ALLOW_CHROME is set.
             //
             // ALLOW_CHROME is a flag that we pass on all loads _except_ docshell
             // loads (since docshell loads run the loaded content with its origin
@@ -934,25 +934,61 @@ nsScriptSecurityManager::CheckLoadURIFla
             rv = NS_URIChainHasFlags(aSourceBaseURI,
                                      nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                      &sourceIsUIResource);
             NS_ENSURE_SUCCESS(rv, rv);
             if (sourceIsUIResource) {
                 return NS_OK;
             }
 
-            // Allow the load only if the chrome package is whitelisted.
-            nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(
-                                                 NS_CHROMEREGISTRY_CONTRACTID));
-            if (reg) {
+            if (targetScheme.EqualsLiteral("resource")) {
+                // Mochitests that need to load resource:// URIs not declared
+                // content-accessible in manifests should set the preference
+                // "security.all_resource_uri_content_accessible" true.
+                static bool sSecurityPrefCached = false;
+                static bool sAllResourceUriContentAccessible = false;
+                if (!sSecurityPrefCached) {
+                    sSecurityPrefCached = true;
+                    Preferences::AddBoolVarCache(
+                            &sAllResourceUriContentAccessible,
+                            "security.all_resource_uri_content_accessible",
+                            false);
+                }
+                if (sAllResourceUriContentAccessible) {
+                    return NS_OK;
+                }
+
+                nsCOMPtr<nsIProtocolHandler> ph;
+                rv = sIOService->GetProtocolHandler("resource", getter_AddRefs(ph));
+                NS_ENSURE_SUCCESS(rv, rv);
+                if (!ph) {
+                    return NS_ERROR_DOM_BAD_URI;
+                }
+
+                nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph);
+                if (!rph) {
+                    return NS_ERROR_DOM_BAD_URI;
+                }
+
                 bool accessAllowed = false;
-                reg->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
+                rph->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
                 if (accessAllowed) {
                     return NS_OK;
                 }
+            } else {
+                // Allow the load only if the chrome package is whitelisted.
+                nsCOMPtr<nsIXULChromeRegistry> reg(
+                        do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
+                if (reg) {
+                    bool accessAllowed = false;
+                    reg->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
+                    if (accessAllowed) {
+                        return NS_OK;
+                    }
+                }
             }
         }
 
         static bool sCanLoadChromeInContent = false;
         static bool sCachedCanLoadChromeInContentPref = false;
         if (!sCachedCanLoadChromeInContentPref) {
             sCachedCanLoadChromeInContentPref = true;
             mozilla::Preferences::AddBoolVarCache(&sCanLoadChromeInContent,
--- a/chrome/RegistryMessageUtils.h
+++ b/chrome/RegistryMessageUtils.h
@@ -37,22 +37,24 @@ struct ChromePackage
   }
 };
 
 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;
+           resolvedURI == rhs.resolvedURI &&
+           flags == rhs.flags;
   }
 };
 
 struct OverrideMapping
 {
   SerializedURI originalURI;
   SerializedURI overrideURI;
 
@@ -135,29 +137,33 @@ struct ParamTraits<SubstitutionMapping>
 {
   typedef SubstitutionMapping paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.scheme);
     WriteParam(aMsg, aParam.path);
     WriteParam(aMsg, aParam.resolvedURI);
+    WriteParam(aMsg, aParam.flags);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     nsCString scheme, path;
     SerializedURI resolvedURI;
+    uint32_t flags;
 
     if (ReadParam(aMsg, aIter, &scheme) &&
         ReadParam(aMsg, aIter, &path) &&
-        ReadParam(aMsg, aIter, &resolvedURI)) {
+        ReadParam(aMsg, aIter, &resolvedURI) &&
+        ReadParam(aMsg, aIter, &flags)) {
       aResult->scheme = scheme;
       aResult->path = path;
       aResult->resolvedURI = resolvedURI;
+      aResult->flags = flags;
       return true;
     }
     return false;
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     aLog->append(StringPrintf(L"[%s://%s, %s, %u]",
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -922,15 +922,23 @@ nsChromeRegistryChrome::ManifestResource
 
   if (!CanLoadResource(resolved)) {
     LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
                           "Warning: cannot register non-local URI '%s' as a resource.",
                           uri);
     return;
   }
 
-  rv = rph->SetSubstitution(host, resolved);
+  // 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);
   }
 }
--- a/chrome/nsChromeRegistryContent.cpp
+++ b/chrome/nsChromeRegistryContent.cpp
@@ -109,17 +109,17 @@ nsChromeRegistryContent::RegisterSubstit
   if (aSubstitution.resolvedURI.spec.Length()) {
     rv = NS_NewURI(getter_AddRefs(resolvedURI),
                    aSubstitution.resolvedURI.spec,
                    nullptr, nullptr, io);
     if (NS_FAILED(rv))
       return;
   }
 
-  rv = sph->SetSubstitution(aSubstitution.path, resolvedURI);
+  rv = sph->SetSubstitutionWithFlags(aSubstitution.path, resolvedURI, aSubstitution.flags);
   if (NS_FAILED(rv))
     return;
 }
 
 void
 nsChromeRegistryContent::RegisterOverride(const OverrideMapping& aOverride)
 {
   nsCOMPtr<nsIIOService> io (do_GetIOService());
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -112,31 +112,32 @@ SubstitutingProtocolHandler::ConstructIn
 //
 // IPC marshalling.
 //
 
 nsresult
 SubstitutingProtocolHandler::CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aMappings)
 {
   for (auto iter = mSubstitutions.ConstIter(); !iter.Done(); iter.Next()) {
-    nsCOMPtr<nsIURI> uri = iter.Data();
+    SubstitutionEntry& entry = iter.Data();
+    nsCOMPtr<nsIURI> uri = entry.baseURI;
     SerializedURI serialized;
     if (uri) {
       nsresult rv = uri->GetSpec(serialized.spec);
       NS_ENSURE_SUCCESS(rv, rv);
     }
-    SubstitutionMapping substitution = { mScheme, nsCString(iter.Key()), serialized };
+    SubstitutionMapping substitution = { mScheme, nsCString(iter.Key()), serialized, entry.flags };
     aMappings.AppendElement(substitution);
   }
 
   return NS_OK;
 }
 
 nsresult
-SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI)
+SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI, uint32_t aFlags)
 {
   if (GeckoProcessType_Content == XRE_GetProcessType()) {
     return NS_OK;
   }
 
   nsTArray<ContentParent*> parents;
   ContentParent::GetAll(parents);
   if (!parents.Length()) {
@@ -145,16 +146,17 @@ SubstitutingProtocolHandler::SendSubstit
 
   SubstitutionMapping mapping;
   mapping.scheme = mScheme;
   mapping.path = aRoot;
   if (aBaseURI) {
     nsresult rv = aBaseURI->GetSpec(mapping.resolvedURI.spec);
     NS_ENSURE_SUCCESS(rv, rv);
   }
+  mapping.flags = aFlags;
 
   for (uint32_t i = 0; i < parents.Length(); i++) {
     Unused << parents[i]->SendRegisterChromeItem(mapping);
   }
 
   return NS_OK;
 }
 
@@ -288,61 +290,92 @@ SubstitutingProtocolHandler::AllowPort(i
 
 //----------------------------------------------------------------------------
 // nsISubstitutingProtocolHandler
 //----------------------------------------------------------------------------
 
 nsresult
 SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
 {
+  // Add-ons use this API but they should not be able to make anything
+  // content-accessible.
+  return SetSubstitutionWithFlags(root, baseURI, 0);
+}
+
+nsresult
+SubstitutingProtocolHandler::SetSubstitutionWithFlags(const nsACString& root, nsIURI *baseURI, uint32_t flags)
+{
   if (!baseURI) {
     mSubstitutions.Remove(root);
     NotifyObservers(root, baseURI);
-    return SendSubstitution(root, baseURI);
+    return SendSubstitution(root, baseURI, flags);
   }
 
   // If baseURI isn't a same-scheme URI, we can set the substitution immediately.
   nsAutoCString scheme;
   nsresult rv = baseURI->GetScheme(scheme);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!scheme.Equals(mScheme)) {
     if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")
         && !scheme.EqualsLiteral("app")) {
       NS_WARNING("Refusing to create substituting URI to non-file:// target");
       return NS_ERROR_INVALID_ARG;
     }
 
-    mSubstitutions.Put(root, baseURI);
+    SubstitutionEntry& entry = mSubstitutions.GetOrInsert(root);
+    entry.baseURI = baseURI;
+    entry.flags = flags;
     NotifyObservers(root, baseURI);
-    return SendSubstitution(root, baseURI);
+    return SendSubstitution(root, baseURI, flags);
   }
 
   // baseURI is a same-type substituting URI, let's resolve it first.
   nsAutoCString newBase;
   rv = ResolveURI(baseURI, newBase);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIURI> newBaseURI;
   rv = mIOService->NewURI(newBase, nullptr, nullptr, getter_AddRefs(newBaseURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mSubstitutions.Put(root, newBaseURI);
+  SubstitutionEntry& entry = mSubstitutions.GetOrInsert(root);
+  entry.baseURI = newBaseURI;
+  entry.flags = flags;
   NotifyObservers(root, baseURI);
-  return SendSubstitution(root, newBaseURI);
+  return SendSubstitution(root, newBaseURI, flags);
 }
 
 nsresult
 SubstitutingProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
 {
   NS_ENSURE_ARG_POINTER(result);
 
-  if (mSubstitutions.Get(root, result))
+  SubstitutionEntry entry;
+  if (mSubstitutions.Get(root, &entry)) {
+    nsCOMPtr<nsIURI> baseURI = entry.baseURI;
+    baseURI.forget(result);
     return NS_OK;
+  }
 
-  return GetSubstitutionInternal(root, result);
+  uint32_t flags;
+  return GetSubstitutionInternal(root, result, &flags);
+}
+
+nsresult
+SubstitutingProtocolHandler::GetSubstitutionFlags(const nsACString& root, uint32_t* flags)
+{
+  *flags = 0;
+  SubstitutionEntry entry;
+  if (mSubstitutions.Get(root, &entry)) {
+    *flags = entry.flags;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIURI> baseURI;
+  return GetSubstitutionInternal(root, getter_AddRefs(baseURI), flags);
 }
 
 nsresult
 SubstitutingProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
 {
   NS_ENSURE_ARG_POINTER(result);
   *result = HasSubstitution(root);
   return NS_OK;
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.h
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.h
@@ -4,19 +4,19 @@
  * 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 SubstitutingProtocolHandler_h___
 #define SubstitutingProtocolHandler_h___
 
 #include "nsISubstitutingProtocolHandler.h"
 
-#include "nsInterfaceHashtable.h"
 #include "nsIOService.h"
 #include "nsISubstitutionObserver.h"
+#include "nsDataHashtable.h"
 #include "nsStandardURL.h"
 #include "mozilla/chrome/RegistryMessageUtils.h"
 #include "mozilla/Maybe.h"
 
 class nsIIOService;
 
 namespace mozilla {
 namespace net {
@@ -39,23 +39,26 @@ public:
   bool HasSubstitution(const nsACString& aRoot) const { return mSubstitutions.Get(aRoot, nullptr); }
 
   MOZ_MUST_USE nsresult CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aResources);
 
 protected:
   virtual ~SubstitutingProtocolHandler() {}
   void ConstructInternal();
 
-  MOZ_MUST_USE nsresult SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI);
+  MOZ_MUST_USE nsresult SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI, uint32_t aFlags);
+
+  nsresult GetSubstitutionFlags(const nsACString& root, uint32_t* flags);
 
   // Override this in the subclass to try additional lookups after checking
   // mSubstitutions.
-  virtual MOZ_MUST_USE nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult)
+  virtual MOZ_MUST_USE nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult, uint32_t* aFlags)
   {
     *aResult = nullptr;
+    *aFlags = 0;
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Override this in the subclass to check for special case when resolving URIs
   // _before_ checking substitutions.
   virtual MOZ_MUST_USE bool ResolveSpecialCases(const nsACString& aHost,
                                                 const nsACString& aPath,
                                                 const nsACString& aPathname,
@@ -69,23 +72,38 @@ protected:
   virtual MOZ_MUST_USE nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result)
   {
     return NS_OK;
   }
 
   nsIIOService* IOService() { return mIOService; }
 
 private:
+  struct SubstitutionEntry
+  {
+    SubstitutionEntry()
+        : flags(0)
+    {
+    }
+
+    ~SubstitutionEntry()
+    {
+    }
+
+    nsCOMPtr<nsIURI> baseURI;
+    uint32_t flags;
+  };
+
   // Notifies all observers that a new substitution from |aRoot| to
   // |aBaseURI| has been set/installed for this protocol handler.
   void NotifyObservers(const nsACString& aRoot, nsIURI* aBaseURI);
 
   nsCString mScheme;
   Maybe<uint32_t> mFlags;
-  nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
+  nsDataHashtable<nsCStringHashKey, SubstitutionEntry> mSubstitutions;
   nsCOMPtr<nsIIOService> mIOService;
 
   // The list of observers added with AddObserver that will be
   // notified when substitutions are set or unset.
   nsTArray<nsCOMPtr<nsISubstitutionObserver>> mObservers;
 
   // In general, we expect the principal of a document loaded from a
   // substituting URI to be a codebase principal for that URI (rather than
--- a/netwerk/protocol/res/nsIResProtocolHandler.idl
+++ b/netwerk/protocol/res/nsIResProtocolHandler.idl
@@ -6,9 +6,10 @@
 #include "nsISubstitutingProtocolHandler.idl"
 
 /**
  * Protocol handler interface for the resource:// protocol
  */
 [scriptable, uuid(241d34ac-9ed5-46d7-910c-7a9d914aa0c5)]
 interface nsIResProtocolHandler : nsISubstitutingProtocolHandler
 {
+  boolean allowContentToAccess(in nsIURI url);
 };
--- a/netwerk/protocol/res/nsISubstitutingProtocolHandler.idl
+++ b/netwerk/protocol/res/nsISubstitutingProtocolHandler.idl
@@ -10,27 +10,37 @@ interface nsISubstitutionObserver;
 /**
  * Protocol handler superinterface for a protocol which performs substitutions
  * from URIs of its scheme to URIs of another scheme.
  */
 [scriptable, uuid(154c64fd-a69e-4105-89f8-bd7dfe621372)]
 interface nsISubstitutingProtocolHandler : nsIProtocolHandler
 {
   /**
+   * Content script may access files in this package.
+   */
+  const short ALLOW_CONTENT_ACCESS = 1;
+
+  /**
    * Sets the substitution for the root key:
    *   resource://root/path ==> baseURI.resolve(path)
    *
    * A null baseURI removes the specified substitution.
    *
    * A root key should always be lowercase; however, this may not be
    * enforced.
    */
   [must_use] void setSubstitution(in ACString root, in nsIURI baseURI);
 
   /**
+   * Same as setSubstitution, but with specific flags.
+   */
+  [must_use] void setSubstitutionWithFlags(in ACString root, in nsIURI baseURI, in uint32_t flags);
+
+  /**
    * Gets the substitution for the root key.
    *
    * @throws NS_ERROR_NOT_AVAILABLE if none exists.
    */
   [must_use] nsIURI getSubstitution(in ACString root);
 
   /**
    * Returns TRUE if the substitution exists and FALSE otherwise.
--- a/netwerk/protocol/res/nsResProtocolHandler.cpp
+++ b/netwerk/protocol/res/nsResProtocolHandler.cpp
@@ -56,26 +56,46 @@ nsResProtocolHandler::Init()
 //----------------------------------------------------------------------------
 
 NS_IMPL_QUERY_INTERFACE(nsResProtocolHandler, nsIResProtocolHandler,
                         nsISubstitutingProtocolHandler, nsIProtocolHandler,
                         nsISupportsWeakReference)
 NS_IMPL_ADDREF_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
 NS_IMPL_RELEASE_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
 
+NS_IMETHODIMP
+nsResProtocolHandler::AllowContentToAccess(nsIURI *aURI, bool *aResult)
+{
+    *aResult = false;
+
+    nsAutoCString host;
+    nsresult rv = aURI->GetAsciiHost(host);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint32_t flags;
+    rv = GetSubstitutionFlags(host, &flags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *aResult = flags & nsISubstitutingProtocolHandler::ALLOW_CONTENT_ACCESS;
+    return NS_OK;
+}
+
 nsresult
-nsResProtocolHandler::GetSubstitutionInternal(const nsACString& root, nsIURI **result)
+nsResProtocolHandler::GetSubstitutionInternal(const nsACString& aRoot,
+                                              nsIURI** aResult,
+                                              uint32_t* aFlags)
 {
     nsAutoCString uri;
 
-    if (!ResolveSpecialCases(root, NS_LITERAL_CSTRING("/"), NS_LITERAL_CSTRING("/"), uri)) {
+    if (!ResolveSpecialCases(aRoot, NS_LITERAL_CSTRING("/"), NS_LITERAL_CSTRING("/"), uri)) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
-    return NS_NewURI(result, uri);
+    *aFlags = 0; // No content access.
+    return NS_NewURI(aResult, uri);
 }
 
 bool
 nsResProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
                                           const nsACString& aPath,
                                           const nsACString& aPathname,
                                           nsACString& aResult)
 {
@@ -93,8 +113,19 @@ nsResProtocolHandler::ResolveSpecialCase
 nsresult
 nsResProtocolHandler::SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI)
 {
     MOZ_ASSERT(!aRoot.Equals(""));
     MOZ_ASSERT(!aRoot.Equals(kAPP));
     MOZ_ASSERT(!aRoot.Equals(kGRE));
     return SubstitutingProtocolHandler::SetSubstitution(aRoot, aBaseURI);
 }
+
+nsresult
+nsResProtocolHandler::SetSubstitutionWithFlags(const nsACString& aRoot,
+                                               nsIURI* aBaseURI,
+                                               uint32_t aFlags)
+{
+    MOZ_ASSERT(!aRoot.Equals(""));
+    MOZ_ASSERT(!aRoot.Equals(kAPP));
+    MOZ_ASSERT(!aRoot.Equals(kGRE));
+    return SubstitutingProtocolHandler::SetSubstitutionWithFlags(aRoot, aBaseURI, aFlags);
+}
--- a/netwerk/protocol/res/nsResProtocolHandler.h
+++ b/netwerk/protocol/res/nsResProtocolHandler.h
@@ -29,16 +29,17 @@ public:
     nsResProtocolHandler()
       : SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE,
                                     /* aEnforceFileOrJar = */ false)
     {}
 
     MOZ_MUST_USE nsresult Init();
 
     NS_IMETHOD SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) override;
+    NS_IMETHOD SetSubstitutionWithFlags(const nsACString& aRoot, nsIURI* aBaseURI, uint32_t aFlags) override;
 
     NS_IMETHOD GetSubstitution(const nsACString& aRoot, nsIURI** aResult) override
     {
         return mozilla::SubstitutingProtocolHandler::GetSubstitution(aRoot, aResult);
     }
 
     NS_IMETHOD HasSubstitution(const nsACString& aRoot, bool* aResult) override
     {
@@ -56,17 +57,17 @@ public:
     }
 
     NS_IMETHOD RemoveObserver(nsISubstitutionObserver *aObserver) override
     {
         return mozilla::SubstitutingProtocolHandler::RemoveObserver(aObserver);
     }
 
 protected:
-    MOZ_MUST_USE nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override;
+    MOZ_MUST_USE nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult, uint32_t* aFlags) override;
     virtual ~nsResProtocolHandler() {}
 
     MOZ_MUST_USE bool ResolveSpecialCases(const nsACString& aHost,
                                           const nsACString& aPath,
                                           const nsACString& aPathname,
                                           nsACString& aResult) override;
 
 private:
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -51,17 +51,17 @@ struct ManifestDirective
 
   // Some directives should only be delivered for APP or EXTENSION locations.
   bool componentonly;
 
   bool ischrome;
 
   bool allowbootstrap;
 
-  // The contentaccessible flags only apply to content directives.
+  // The contentaccessible flags only apply to content/resource directives.
   bool contentflags;
 
   // Function to handle this directive. This isn't a union because C++ still
   // hasn't learned how to initialize unions in a sane way.
   void (nsComponentManagerImpl::*mgrfunc)(
     nsComponentManagerImpl::ManifestProcessingContext& aCx,
     int aLineNo, char* const* aArgv);
   void (nsChromeRegistry::*regfunc)(
@@ -118,17 +118,17 @@ static const ManifestDirective kParsingT
   },
   {
     // NB: note that while skin manifests can use this, they are only allowed
     // to use it for chrome://../skin/ URLs
     "override",         2, false, false, true, true, false,
     nullptr, &nsChromeRegistry::ManifestOverride, nullptr
   },
   {
-    "resource",         2, false, true, true, true, false,
+    "resource",         2, false, true, true, true, true,
     nullptr, &nsChromeRegistry::ManifestResource, nullptr
   }
 };
 
 static const char kWhitespace[] = "\t ";
 
 static bool
 IsNewline(char aChar)