Bug 1390346 - Redirects to moz-extension:-URLs fail when loaded from a xpi. r=jimm, a=lizzard
authorHaik Aftandilian <haftandilian@mozilla.com>
Fri, 18 Aug 2017 11:30:21 -0700
changeset 655638 e9c83e49ec46b763d0529edcb9580837739ba639
parent 655637 bc8f7e510cc8955cf6041dc4e0577622f3e1b382
child 655639 2968e01b6f206a949ea8f941a3f86b6738828114
push id76942
push userbmo:scwwu@mozilla.com
push dateWed, 30 Aug 2017 08:27:15 +0000
reviewersjimm, lizzard
bugs1390346
milestone56.0
Bug 1390346 - Redirects to moz-extension:-URLs fail when loaded from a xpi. r=jimm, a=lizzard MozReview-Commit-ID: 2FzUWLTiLs
modules/libpref/init/all.js
netwerk/protocol/res/ExtensionProtocolHandler.cpp
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4948,17 +4948,19 @@ pref("extensions.allow-non-mpc-extension
 pref("extensions.webextensions.keepStorageOnUninstall", false);
 pref("extensions.webextensions.keepUuidOnUninstall", false);
 // Redirect basedomain used by identity api
 pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org");
 // Whether or not webextension themes are supported.
 pref("extensions.webextensions.themes.enabled", false);
 pref("extensions.webextensions.themes.icons.enabled", false);
 pref("extensions.webextensions.remote", false);
-// Whether or not the moz-extension resource loads are remoted
+// Whether or not the moz-extension resource loads are remoted. For debugging
+// purposes only. Setting this to false will break moz-extension URI loading
+// unless other process sandboxing and extension remoting prefs are changed.
 pref("extensions.webextensions.protocol.remote", true);
 
 // Report Site Issue button
 pref("extensions.webcompat-reporter.newIssueEndpoint", "https://webcompat.com/issues/new");
 #if defined(MOZ_DEV_EDITION) || defined(NIGHTLY_BUILD)
 pref("extensions.webcompat-reporter.enabled", true);
 #else
 pref("extensions.webcompat-reporter.enabled", false);
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -107,16 +107,17 @@ class ExtensionStreamGetter : public Ref
 {
   public:
     // To use when getting a remote input stream for a resource
     // in an unpacked extension.
     ExtensionStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo)
       : mURI(aURI)
       , mLoadInfo(aLoadInfo)
       , mIsJarChannel(false)
+      , mIsCachedJar(false)
     {
       MOZ_ASSERT(aURI);
       MOZ_ASSERT(aLoadInfo);
 
       SetupEventTarget();
     }
 
     // To use when getting an FD for a packed extension JAR file
@@ -124,25 +125,44 @@ class ExtensionStreamGetter : public Ref
     ExtensionStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo,
                           already_AddRefed<nsIJARChannel>&& aJarChannel,
                           nsIFile* aJarFile)
       : mURI(aURI)
       , mLoadInfo(aLoadInfo)
       , mJarChannel(Move(aJarChannel))
       , mJarFile(aJarFile)
       , mIsJarChannel(true)
+      , mIsCachedJar(false)
     {
       MOZ_ASSERT(aURI);
       MOZ_ASSERT(aLoadInfo);
       MOZ_ASSERT(mJarChannel);
       MOZ_ASSERT(aJarFile);
 
       SetupEventTarget();
     }
 
+    // To use when the request resolves to a JAR file that is already cached.
+    // Using a SimpleChannel with an ExtensionStreamGetter here (like the
+    // non-cached JAR case) isn't needed to load the extension resource
+    // because we don't need to ask the parent for an FD for the JAR, but
+    // wrapping the JARChannel in a SimpleChannel allows HTTP forwarding to
+    // moz-extension URI's to work because HTTP forwarding requires the
+    // target channel implement nsIChildChannel.
+    explicit
+      ExtensionStreamGetter(already_AddRefed<nsIJARChannel>&& aJarChannel)
+      : mJarChannel(Move(aJarChannel))
+      , mIsJarChannel(true)
+      , mIsCachedJar(true)
+    {
+      MOZ_ASSERT(mJarChannel);
+
+      SetupEventTarget();
+    }
+
     ~ExtensionStreamGetter() {}
 
     void SetupEventTarget()
     {
       mMainThreadEventTarget =
         nsContentUtils::GetEventTargetByLoadInfo(mLoadInfo, TaskCategory::Other);
       if (!mMainThreadEventTarget) {
         mMainThreadEventTarget = GetMainThreadSerialEventTarget();
@@ -165,16 +185,20 @@ class ExtensionStreamGetter : public Ref
     nsCOMPtr<nsIURI> mURI;
     nsCOMPtr<nsILoadInfo> mLoadInfo;
     nsCOMPtr<nsIJARChannel> mJarChannel;
     nsCOMPtr<nsIFile> mJarFile;
     nsCOMPtr<nsIStreamListener> mListener;
     nsCOMPtr<nsIChannel> mChannel;
     nsCOMPtr<nsISerialEventTarget> mMainThreadEventTarget;
     bool mIsJarChannel;
+
+    // Indicates the JAR for this channel is cached
+    // and implies mIsJarChannel==true
+    bool mIsCachedJar;
 };
 
 class ExtensionJARFileOpener : public nsISupports
 {
 public:
   ExtensionJARFileOpener(nsIFile* aFile,
                          NeckoParent::GetExtensionFDResolver& aResolve) :
     mFile(aFile),
@@ -245,16 +269,24 @@ ExtensionStreamGetter::GetAsync(nsIStrea
                                 nsIChannel* aChannel)
 {
   MOZ_ASSERT(IsNeckoChild());
   MOZ_ASSERT(mMainThreadEventTarget);
 
   mListener = aListener;
   mChannel = aChannel;
 
+  // We don't have to request an FD from the
+  // parent if the JAR is cached
+  if (mIsCachedJar) {
+    MOZ_ASSERT(mIsJarChannel);
+    mJarChannel->AsyncOpen2(mListener);
+    return Ok();
+  }
+
   // Serialize the URI to send to parent
   mozilla::ipc::URIParams uri;
   SerializeURI(mURI, uri);
 
   RefPtr<ExtensionStreamGetter> self = this;
   if (mIsJarChannel) {
     // Request an FD for this moz-extension URI
     gNeckoChild->SendGetExtensionFD(uri)->Then(
@@ -366,16 +398,21 @@ ExtensionProtocolHandler::GetSingleton()
 }
 
 ExtensionProtocolHandler::ExtensionProtocolHandler()
   : SubstitutingProtocolHandler(EXTENSION_SCHEME)
 #if !defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
   , mAlreadyCheckedDevRepo(false)
 #endif
 {
+  // Note, extensions.webextensions.protocol.remote=false is for
+  // debugging purposes only. With process-level sandboxing, child
+  // processes (specifically content and extension processes), will
+  // not be able to load most moz-extension URI's when the pref is
+  // set to false.
   mUseRemoteFileChannels = IsNeckoChild() &&
     Preferences::GetBool("extensions.webextensions.protocol.remote");
 }
 
 static inline ExtensionPolicyService&
 EPS()
 {
   return ExtensionPolicyService::GetSingleton();
@@ -829,31 +866,36 @@ ExtensionProtocolHandler::SubstituteRemo
   nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(*aRetVal, &rv);
   NS_TRY(rv);
 
   bool isCached = false;
   NS_TRY(jarChannel->EnsureCached(&isCached));
   if (MOZ_LOG_TEST(gExtProtocolLog, LogLevel::Debug)) {
     Unused << LogCacheCheck(jarChannel, jarURI, isCached);
   }
+
+  RefPtr<ExtensionStreamGetter> streamGetter;
+
   if (isCached) {
-    return Ok();
-  }
-
-  nsCOMPtr<nsIURI> innerFileURI;
-  NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI)));
+    streamGetter = new ExtensionStreamGetter(jarChannel.forget());
+  } else {
+    nsCOMPtr<nsIURI> innerFileURI;
+    NS_TRY(jarURI->GetJARFile(getter_AddRefs(innerFileURI)));
 
-  nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
-  NS_TRY(rv);
+    nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
+    NS_TRY(rv);
+
+    nsCOMPtr<nsIFile> jarFile;
+    NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile)));
 
-  nsCOMPtr<nsIFile> jarFile;
-  NS_TRY(innerFileURL->GetFile(getter_AddRefs(jarFile)));
-
-  RefPtr<ExtensionStreamGetter> streamGetter =
-    new ExtensionStreamGetter(aURI, aLoadinfo, jarChannel.forget(), jarFile);
+    streamGetter = new ExtensionStreamGetter(aURI,
+                                             aLoadinfo,
+                                             jarChannel.forget(),
+                                             jarFile);
+  }
 
   NewSimpleChannel(aURI, aLoadinfo, streamGetter, aRetVal);
   return Ok();
 }
 
 #undef NS_TRY
 
 } // namespace net