Bug 1186152 - Implement nsIProtocolHandlerWithDynamicFlags and use it for moz-extension. r=bz
authorBobby Holley <bobbyholley@gmail.com>
Mon, 27 Jul 2015 13:27:38 -0700
changeset 286694 b72d4867ae69cae831eeca9ba92db9bbb096cb1b
parent 286693 41ea066f291fb0d433d95a4e06e9aa4803a460d4
child 286695 2f97906c22f7a7482e7e3439cbb5a4d9add8f3ef
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1186152
milestone42.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 1186152 - Implement nsIProtocolHandlerWithDynamicFlags and use it for moz-extension. r=bz
caps/nsScriptSecurityManager.cpp
caps/tests/mochitest/test_extensionURL.html
netwerk/base/nsIOService.cpp
netwerk/base/nsIProtocolHandler.idl
netwerk/base/nsProtocolProxyService.cpp
netwerk/protocol/res/ExtensionProtocolHandler.cpp
netwerk/protocol/res/ExtensionProtocolHandler.h
netwerk/protocol/res/SubstitutingProtocolHandler.cpp
netwerk/protocol/res/SubstitutingProtocolHandler.h
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -756,26 +756,16 @@ nsScriptSecurityManager::CheckLoadURIWit
 
     // If the schemes don't match, the policy is specified by the protocol
     // flags on the target URI.  Note that the order of policy checks here is
     // very important!  We start from most restrictive and work our way down.
     // Note that since we're working with the innermost URI, we can just use
     // the methods that work on chains of nested URIs and they will only look
     // at the flags for our one URI.
 
-    // Special case: moz-extension has a whitelist of URIs that are loadable by
-    // anyone.
-    if (targetScheme.EqualsLiteral("moz-extension") && GetAddonPolicyService()) {
-      bool loadable = false;
-      rv = GetAddonPolicyService()->ExtensionURILoadableByAnyone(targetBaseURI, &loadable);
-      if (NS_SUCCEEDED(rv) && loadable) {
-        return NS_OK;
-      }
-    }
-
     // Check for system target URI
     rv = DenyAccessIfURIHasFlags(targetBaseURI,
                                  nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
     if (NS_FAILED(rv)) {
         // Deny access, since the origin principal is not system
         if (reportErrors) {
             ReportError(nullptr, errorTag, sourceURI, aTargetURI);
         }
--- a/caps/tests/mochitest/test_extensionURL.html
+++ b/caps/tests/mochitest/test_extensionURL.html
@@ -99,17 +99,17 @@ https://bugzilla.mozilla.org/show_bug.cg
         document.body.appendChild(ifr);
 
         var threw = false;
         try {
           navigate(ifr, url);
         } catch (e) {
           ifr.remove();
           threw = true;
-          ok(/denied|insecure/.test(e), "exceiton correct: " + e);
+          ok(/denied|insecure/.test(e), "exception correct: " + e);
         }
         is(threw, !!shouldThrow, "Correct throwing behavior for: " + url);
         !threw || resolve();
       });
 
       return p;
     }
 
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -552,16 +552,19 @@ nsIOService::ExtractScheme(const nsACStr
 
 NS_IMETHODIMP 
 nsIOService::GetProtocolFlags(const char* scheme, uint32_t *flags)
 {
     nsCOMPtr<nsIProtocolHandler> handler;
     nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
     if (NS_FAILED(rv)) return rv;
 
+    // We can't call DoGetProtocolFlags here because we don't have a URI. This
+    // API is used by (and only used by) extensions, which is why it's still
+    // around. Calling this on a scheme with dynamic flags will throw.
     rv = handler->GetProtocolFlags(flags);
     return rv;
 }
 
 class AutoIncrement
 {
     public:
         explicit AutoIncrement(uint32_t *var) : mVar(var)
@@ -717,17 +720,17 @@ nsIOService::NewChannelFromURIWithProxyF
     }
 
     nsCOMPtr<nsIProtocolHandler> handler;
     rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
     if (NS_FAILED(rv))
         return rv;
 
     uint32_t protoFlags;
-    rv = handler->GetProtocolFlags(&protoFlags);
+    rv = handler->DoGetProtocolFlags(aURI, &protoFlags);
     if (NS_FAILED(rv))
         return rv;
 
     // Ideally we are creating new channels by calling NewChannel2 (NewProxiedChannel2).
     // Keep in mind that Addons can implement their own Protocolhandlers, hence
     // NewChannel2() might *not* be implemented.
     // We do not want to break those addons, therefore we first try to create a channel
     // calling NewChannel2(); if that fails:
@@ -1476,25 +1479,27 @@ nsIOService::ProtocolHasFlags(nsIURI   *
                               bool     *result)
 {
     NS_ENSURE_ARG(uri);
 
     *result = false;
     nsAutoCString scheme;
     nsresult rv = uri->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
-  
-    uint32_t protocolFlags;
-    rv = GetProtocolFlags(scheme.get(), &protocolFlags);
 
-    if (NS_SUCCEEDED(rv)) {
-        *result = (protocolFlags & flags) == flags;
-    }
-  
-    return rv;
+    // Grab the protocol flags from the URI.
+    uint32_t protocolFlags;
+    nsCOMPtr<nsIProtocolHandler> handler;
+    rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = handler->DoGetProtocolFlags(uri, &protocolFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *result = (protocolFlags & flags) == flags;
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsIOService::URIChainHasFlags(nsIURI   *uri,
                               uint32_t  flags,
                               bool     *result)
 {
     nsresult rv = ProtocolHasFlags(uri, flags, result);
--- a/netwerk/base/nsIProtocolHandler.idl
+++ b/netwerk/base/nsIProtocolHandler.idl
@@ -1,23 +1,43 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+%{C++
+#include "nsCOMPtr.h"
+%}
+
 interface nsIURI;
 interface nsIChannel;
 interface nsILoadInfo;
 
 /**
+ * nsIProtocolHandlerWithDynamicFlags
+ *
+ * Protocols that wish to return different flags depending on the URI should
+ * implement this interface.
+ */
+[scriptable, builtinclass, uuid(65a8e823-0591-4fc0-a56a-03265e0a4ce8)]
+interface nsIProtocolHandlerWithDynamicFlags : nsISupports
+{
+    /*
+     * Returns protocol flags for the given URI, which may be different from the
+     * flags for another URI of the same scheme.
+     */
+    unsigned long getFlagsForURI(in nsIURI aURI);
+};
+
+/**
  * nsIProtocolHandler
  */
-[scriptable, uuid(a7aad716-e72c-435d-82f1-7582dffae661)]
+[scriptable, uuid(3393c327-ce70-47f1-9be3-cc312e21c012)]
 interface nsIProtocolHandler : nsISupports
 {
     /**
      * The scheme of this protocol (e.g., "file").
      */
     readonly attribute ACString scheme;
 
     /** 
@@ -27,16 +47,25 @@ interface nsIProtocolHandler : nsISuppor
      */
     readonly attribute long defaultPort;
 
     /**
      * Returns the protocol specific flags (see flag definitions below).  
      */
     readonly attribute unsigned long protocolFlags;
 
+%{C++
+  // Helper method to get the protocol flags in the right way.
+  nsresult DoGetProtocolFlags(nsIURI* aURI, uint32_t* aFlags)
+  {
+      nsCOMPtr<nsIProtocolHandlerWithDynamicFlags> dh = do_QueryInterface(this);
+      return dh ? dh->GetFlagsForURI(aURI, aFlags) : GetProtocolFlags(aFlags);
+  }
+%}
+
     /**
      * Makes a URI object that is suitable for loading by this protocol,
      * where the URI string is given as an UTF-8 string.  The caller may
      * provide the charset from which the URI string originated, so that
      * the URI string can be translated back to that charset (if necessary)
      * before communicating with, for example, the origin server of the URI
      * string.  (Many servers do not support UTF-8 IRIs at the present time,
      * so we must be careful about tracking the native charset of the origin
@@ -253,18 +282,16 @@ interface nsIProtocolHandler : nsISuppor
      */
     const unsigned long URI_SYNC_LOAD_IS_OK = (1<<17);
 
     /**
      * URI is secure to load in an https page and should not be blocked
      * by nsMixedContentBlocker
      */
     const unsigned long URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT = (1<<18);
-
-
 };
 
 %{C++
 /**
  * Protocol handlers are registered with XPCOM under the following CONTRACTID prefix:
  */
 #define NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "@mozilla.org/network/protocol;1?name="
 /**
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -1658,17 +1658,17 @@ nsProtocolProxyService::GetProtocolInfo(
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIProtocolHandler> handler;
     rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
     if (NS_FAILED(rv))
         return rv;
 
-    rv = handler->GetProtocolFlags(&info->flags);
+    rv = handler->DoGetProtocolFlags(uri, &info->flags);
     if (NS_FAILED(rv))
         return rv;
 
     rv = handler->GetDefaultPort(&info->defaultPort);
     return rv;
 }
 
 nsresult
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -1,16 +1,36 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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 "ExtensionProtocolHandler.h"
 
+#include "nsIAddonPolicyService.h"
+#include "nsServiceManagerUtils.h"
+
 namespace mozilla {
 
 NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler, nsISubstitutingProtocolHandler,
-                        nsIProtocolHandler, nsISupportsWeakReference)
+                        nsIProtocolHandler, nsIProtocolHandlerWithDynamicFlags,
+                        nsISupportsWeakReference)
 NS_IMPL_ADDREF_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
 NS_IMPL_RELEASE_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
 
+nsresult
+ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags)
+{
+  // In general a moz-extension URI is only loadable by chrome, but a whitelisted
+  // subset are web-accessible. Check that whitelist.
+  nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
+  bool loadableByAnyone = false;
+  if (aps) {
+    nsresult rv = aps->ExtensionURILoadableByAnyone(aURI, &loadableByAnyone);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | (loadableByAnyone ? URI_LOADABLE_BY_ANYONE : URI_DANGEROUS_TO_LOAD);
+  return NS_OK;
+}
+
 } // namespace mozilla
--- a/netwerk/protocol/res/ExtensionProtocolHandler.h
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.h
@@ -7,29 +7,27 @@
 #define ExtensionProtocolHandler_h___
 
 #include "SubstitutingProtocolHandler.h"
 #include "nsWeakReference.h"
 
 namespace mozilla {
 
 class ExtensionProtocolHandler final : public nsISubstitutingProtocolHandler,
+                                       public nsIProtocolHandlerWithDynamicFlags,
                                        public mozilla::SubstitutingProtocolHandler,
                                        public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIPROTOCOLHANDLERWITHDYNAMICFLAGS
   NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
   NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
 
-  // In general a moz-extension URI is only loadable by chrome, but a whitelisted
-  // subset are web-accessible (see nsIAddonPolicyService).
-  ExtensionProtocolHandler()
-    : SubstitutingProtocolHandler("moz-extension", URI_STD | URI_DANGEROUS_TO_LOAD | URI_IS_LOCAL_RESOURCE)
-  {}
+  ExtensionProtocolHandler() : SubstitutingProtocolHandler("moz-extension") {}
 
 protected:
   ~ExtensionProtocolHandler() {}
 };
 
 } // namespace mozilla
 
 #endif /* ExtensionProtocolHandler_h___ */
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -79,20 +79,34 @@ SubstitutingURL::GetClassIDNoAlloc(nsCID
 {
   *aClassIDNoAlloc = kSubstitutingURLCID;
   return NS_OK;
 }
 
 SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags,
                                                          bool aEnforceFileOrJar)
   : mScheme(aScheme)
-  , mFlags(aFlags)
   , mSubstitutions(16)
   , mEnforceFileOrJar(aEnforceFileOrJar)
 {
+  mFlags.emplace(aFlags);
+  ConstructInternal();
+}
+
+SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme)
+  : mScheme(aScheme)
+  , mSubstitutions(16)
+  , mEnforceFileOrJar(true)
+{
+  ConstructInternal();
+}
+
+void
+SubstitutingProtocolHandler::ConstructInternal()
+{
   nsresult rv;
   mIOService = do_GetIOService(&rv);
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService);
 
   if (!gResLog) {
     gResLog = PR_NewLogModule("nsResProtocol");
   }
 }
@@ -175,17 +189,22 @@ SubstitutingProtocolHandler::GetDefaultP
 {
   *result = -1;
   return NS_OK;
 }
 
 nsresult
 SubstitutingProtocolHandler::GetProtocolFlags(uint32_t *result)
 {
-  *result = mFlags;
+  if (mFlags.isNothing()) {
+    NS_WARNING("Trying to get protocol flags the wrong way - use nsIProtocolHandlerWithDynamicFlags instead");
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  *result = mFlags.ref();
   return NS_OK;
 }
 
 nsresult
 SubstitutingProtocolHandler::NewURI(const nsACString &aSpec,
                                     const char *aCharset,
                                     nsIURI *aBaseURI,
                                     nsIURI **result)
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.h
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.h
@@ -8,55 +8,58 @@
 #define SubstitutingProtocolHandler_h___
 
 #include "nsISubstitutingProtocolHandler.h"
 
 #include "nsInterfaceHashtable.h"
 #include "nsIOService.h"
 #include "nsStandardURL.h"
 #include "mozilla/chrome/RegistryMessageUtils.h"
+#include "mozilla/Maybe.h"
 
 class nsIIOService;
 
 namespace mozilla {
 
 //
 // Base class for resource://-like substitution protocols.
 //
 // If you add a new protocol, make sure to change nsChromeRegistryChrome
 // to properly invoke CollectSubstitutions at the right time.
 class SubstitutingProtocolHandler
 {
 public:
   SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags, bool aEnforceFileOrJar = true);
+  explicit SubstitutingProtocolHandler(const char* aScheme);
 
   NS_INLINE_DECL_REFCOUNTING(SubstitutingProtocolHandler);
   NS_DECL_NON_VIRTUAL_NSIPROTOCOLHANDLER;
   NS_DECL_NON_VIRTUAL_NSISUBSTITUTINGPROTOCOLHANDLER;
 
   void CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aResources);
 
 protected:
   virtual ~SubstitutingProtocolHandler() {}
+  void ConstructInternal();
 
   void SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI);
 
   // Override this in the subclass to try additional lookups after checking
   // mSubstitutions.
   virtual nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult)
   {
     *aResult = nullptr;
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsIIOService* IOService() { return mIOService; }
 
 private:
   nsCString mScheme;
-  uint32_t mFlags;
+  Maybe<uint32_t> mFlags;
   nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
   nsCOMPtr<nsIIOService> mIOService;
 
   // 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 principal for whatever is underneath). However, this only works if
   // the protocol handler for the underlying URI doesn't set an explicit
   // owner (which chrome:// does, for example). So we want to require that