Bug 444641 part 2. Factor out the channel owner setting from docshell so other consumers can use it too. r=jst,dveditz
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 20 Sep 2011 17:00:42 -0400
changeset 78531 a6a3d724bcf5946639e7352178542dc6120c15fd
parent 78530 2ca75ce160b0bffda896de07b8f30d5bd1336fd2
child 78532 c237a8550070a05b648d64a7a328e81151cd948b
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, dveditz
bugs444641
milestone9.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 444641 part 2. Factor out the channel owner setting from docshell so other consumers can use it too. r=jst,dveditz
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsObjectLoadingContent.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1796,16 +1796,36 @@ public:
                                   nsIDocument* aDocument);
 
   /**
    * Calling this adds support for
    * ontouch* event handler DOM attributes.
    */
   static void InitializeTouchEventTable();
 
+  /**
+   * Test whether the given URI always inherits a security context
+   * from the document it comes from.
+   */
+  static nsresult URIInheritsSecurityContext(nsIURI *aURI, PRBool *aResult);
+
+  /**
+   * Set the given principal as the owner of the given channel, if
+   * needed.  aURI must be the URI of aChannel.  aPrincipal may be
+   * null.  If aSetUpForAboutBlank is true, then about:blank will get
+   * the principal set up on it.
+   *
+   * The return value is whether the principal was set up as the owner
+   * of the channel.
+   */
+  static bool SetUpChannelOwner(nsIPrincipal* aLoadingPrincipal,
+                                nsIChannel* aChannel,
+                                nsIURI* aURI,
+                                PRBool aSetUpForAboutBlank);
+
   static nsresult Btoa(const nsAString& aBinaryData,
                        nsAString& aAsciiBase64String);
 
   static nsresult Atob(const nsAString& aAsciiString,
                        nsAString& aBinaryData);
   
 private:
   static PRBool InitializeEventTable();
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4669,16 +4669,17 @@ nsContentUtils::FilterDropEffect(PRUint3
 
 /* static */
 PRBool
 nsContentUtils::URIIsLocalFile(nsIURI *aURI)
 {
   PRBool isFile;
   nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
 
+  // Important: we do NOT test the entire URI chain here!
   return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
                                 nsIProtocolHandler::URI_IS_LOCAL_FILE,
                                 &isFile)) &&
          isFile;
 }
 
 nsresult
 nsContentUtils::SplitURIAtHash(nsIURI *aURI,
@@ -5716,16 +5717,81 @@ nsContentUtils::IsPatternMatching(nsAStr
 
   res = JS_ExecuteRegExpNoStatics(ctx, re, reinterpret_cast<jschar*>
                                     (aValue.BeginWriting()),
                                   aValue.Length(), &idx, JS_TRUE, &rval);
 
   return res == JS_FALSE || rval != JSVAL_NULL;
 }
 
+// static
+nsresult
+nsContentUtils::URIInheritsSecurityContext(nsIURI *aURI, PRBool *aResult)
+{
+  // Note: about:blank URIs do NOT inherit the security context from the
+  // current document, which is what this function tests for...
+  return NS_URIChainHasFlags(aURI,
+                             nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+                             aResult);
+}
+
+// static
+bool
+nsContentUtils::SetUpChannelOwner(nsIPrincipal* aLoadingPrincipal,
+                                  nsIChannel* aChannel,
+                                  nsIURI* aURI,
+                                  PRBool aSetUpForAboutBlank)
+{
+  //
+  // Set the owner of the channel, but only for channels that can't
+  // provide their own security context.
+  //
+  // XXX: It seems wrong that the owner is ignored - even if one is
+  //      supplied) unless the URI is javascript or data or about:blank.
+  // XXX: If this is ever changed, check all callers for what owners
+  //      they're passing in.  In particular, see the code and
+  //      comments in nsDocShell::LoadURI where we fall back on
+  //      inheriting the owner if called from chrome.  That would be
+  //      very wrong if this code changed anything but channels that
+  //      can't provide their own security context!
+  //
+  //      (Currently chrome URIs set the owner when they are created!
+  //      So setting a NULL owner would be bad!)
+  //
+  PRBool inherit;
+  // We expect URIInheritsSecurityContext to return success for an
+  // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
+  // This condition needs to match the one in nsDocShell::InternalLoad where
+  // we're checking for things that will use the owner.
+  if (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherit)) &&
+      (inherit || (aSetUpForAboutBlank && NS_IsAboutBlank(aURI)))) {
+    aChannel->SetOwner(aLoadingPrincipal);
+    return true;
+  }
+
+  //
+  // file: uri special-casing
+  //
+  // If this is a file: load opened from another file: then it may need
+  // to inherit the owner from the referrer so they can script each other.
+  // If we don't set the owner explicitly then each file: gets an owner
+  // based on its own codebase later.
+  //
+  if (URIIsLocalFile(aURI) && aLoadingPrincipal &&
+      NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, PR_FALSE)) &&
+      // One more check here.  CheckMayLoad will always return true for the
+      // system principal, but we do NOT want to inherit in that case.
+      !IsSystemPrincipal(aLoadingPrincipal)) {
+    aChannel->SetOwner(aLoadingPrincipal);
+    return true;
+  }
+
+  return false;
+}
+
 PRBool
 nsContentUtils::IsFullScreenApiEnabled()
 {
   return sIsFullScreenApiEnabled;
 }
 
 PRBool nsContentUtils::IsRequestFullScreenAllowed()
 {
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -1438,27 +1438,18 @@ nsObjectLoadingContent::LoadObject(nsIUR
     nsCAutoString typeHint, dummy;
     NS_ParseContentType(aTypeHint, typeHint, dummy);
     if (!typeHint.IsEmpty()) {
       chan->SetContentType(typeHint);
     }
   }
 
   // Set up the channel's principal and such, like nsDocShell::DoURILoad does
-  PRBool inheritPrincipal;
-  rv = NS_URIChainHasFlags(aURI,
-                           nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
-                           &inheritPrincipal);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (inheritPrincipal || NS_IsAboutBlank(aURI) ||
-      (nsContentUtils::URIIsLocalFile(aURI) &&
-       NS_SUCCEEDED(thisContent->NodePrincipal()->CheckMayLoad(aURI,
-                                                               PR_FALSE)))) {
-    chan->SetOwner(thisContent->NodePrincipal());
-  }
+  nsContentUtils::SetUpChannelOwner(thisContent->NodePrincipal(),
+                                    chan, aURI, PR_TRUE);
 
   nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
   if (scriptChannel) {
     // Allow execution against our context if the principals match
     scriptChannel->
       SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
   }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8166,31 +8166,34 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     // callers want something interesting to happen with the about:blank
     // principal in this case, they should pass an owner in.
     //
     {
         PRBool inherits;
         // One more twist: Don't inherit the owner for external loads.
         if (aLoadType != LOAD_NORMAL_EXTERNAL && !owner &&
             (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
-            NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherits)) &&
+            NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
+                                                                    &inherits)) &&
             inherits) {
 
             owner = GetInheritedPrincipal(PR_TRUE);
         }
     }
 
     // Don't allow loads that would inherit our security context
     // if this document came from an unsafe channel.
     {
         PRBool willInherit;
-        // This condition needs to match the one in DoChannelLoad.
+        // This condition needs to match the one in
+        // nsContentUtils::SetUpChannelOwner.
         // Except we reverse the rv check to be safe in case
-        // URIInheritsSecurityContext fails here and succeeds there.
-        rv = URIInheritsSecurityContext(aURI, &willInherit);
+        // nsContentUtils::URIInheritsSecurityContext fails here and
+        // succeeds there.
+        rv = nsContentUtils::URIInheritsSecurityContext(aURI, &willInherit);
         if (NS_FAILED(rv) || willInherit || NS_IsAboutBlank(aURI)) {
             nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
             do {
                 nsCOMPtr<nsIDocShell> itemDocShell =
                     do_QueryInterface(treeItem);
                 PRBool isUnsafe;
                 if (itemDocShell &&
                     NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
@@ -8996,66 +8999,19 @@ nsDocShell::DoURILoad(nsIURI * aURI,
             rv = AddHeadersToChannel(aHeadersData, httpChannel);
         }
         // Set the referrer explicitly
         if (aReferrerURI && aSendReferrer) {
             // Referrer is currenly only set for link clicks here.
             httpChannel->SetReferrer(aReferrerURI);
         }
     }
-    //
-    // Set the owner of the channel, but only for channels that can't
-    // provide their own security context.
-    //
-    // XXX: Is seems wrong that the owner is ignored - even if one is
-    //      supplied) unless the URI is javascript or data or about:blank.
-    // XXX: If this is ever changed, check all callers for what owners they're
-    //      passing in.  In particular, see the code and comments in LoadURI
-    //      where we fall back on inheriting the owner if called
-    //      from chrome.  That would be very wrong if this code changed
-    //      anything but channels that can't provide their own security context!
-    //
-    //      (Currently chrome URIs set the owner when they are created!
-    //      So setting a NULL owner would be bad!)
-    //
-    // If this code ever changes, change nsObjectLoadingContent::LoadObject
-    // accordingly.
-    PRBool inherit;
-    // We expect URIInheritsSecurityContext to return success for an
-    // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
-    // This condition needs to match the one in InternalLoad where
-    // we're checking for things that will use the owner.
-    rv = URIInheritsSecurityContext(aURI, &inherit);
-    if (NS_SUCCEEDED(rv) && (inherit || NS_IsAboutBlank(aURI))) {
-        channel->SetOwner(aOwner);
-    }
-
-    //
-    // file: uri special-casing
-    //
-    // If this is a file: load opened from another file: then it may need
-    // to inherit the owner from the referrer so they can script each other.
-    // If we don't set the owner explicitly then each file: gets an owner
-    // based on its own codebase later.
-    //
+
     nsCOMPtr<nsIPrincipal> ownerPrincipal(do_QueryInterface(aOwner));
-    if (URIIsLocalFile(aURI) && ownerPrincipal &&
-        NS_SUCCEEDED(ownerPrincipal->CheckMayLoad(aURI, PR_FALSE))) {
-        // One more check here.  CheckMayLoad will always return true for the
-        // system principal, but we do NOT want to inherit in that case.
-        PRBool isSystem;
-        nsCOMPtr<nsIScriptSecurityManager> secMan =
-            do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
-        if (secMan &&
-            NS_SUCCEEDED(secMan->IsSystemPrincipal(ownerPrincipal,
-                                                   &isSystem)) &&
-            !isSystem) {
-            channel->SetOwner(aOwner);
-        }
-    }
+    nsContentUtils::SetUpChannelOwner(ownerPrincipal, channel, aURI, PR_TRUE);
 
     nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
     if (scriptChannel) {
         // Allow execution against our context if the principals match
         scriptChannel->
             SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
     }
 
@@ -9738,17 +9694,17 @@ nsDocShell::AddState(nsIVariant *aData, 
                        charset.get(), docBaseURI);
 
         // 2b: If 2a fails, raise a SECURITY_ERR
         if (NS_FAILED(rv)) {
             return NS_ERROR_DOM_SECURITY_ERR;
         }
 
         // 2c: Same-origin check.
-        if (!URIIsLocalFile(newURI)) {
+        if (!nsContentUtils::URIIsLocalFile(newURI)) {
             // In addition to checking that the security manager says that
             // the new URI has the same origin as our current URI, we also
             // check that the two URIs have the same userpass. (The
             // security manager says that |http://foo.com| and
             // |http://me@foo.com| have the same origin.)  mCurrentURI
             // won't contain the password part of the userpass, so this
             // means that it's never valid to specify a password in a
             // pushState or replaceState URI.
@@ -11246,40 +11202,16 @@ nsDocShell::IsAppOfType(PRUint32 aAppTyp
 
 NS_IMETHODIMP
 nsDocShell::GetIsContent(PRBool *aIsContent)
 {
     *aIsContent = (mItemType == typeContent);
     return NS_OK;
 }
 
-/* static */
-nsresult
-nsDocShell::URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult)
-{
-    // Note: about:blank URIs do NOT inherit the security context from the
-    // current document, which is what this function tests for...
-    return NS_URIChainHasFlags(aURI,
-                               nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
-                               aResult);
-}
-
-/* static */
-PRBool
-nsDocShell::URIIsLocalFile(nsIURI *aURI)
-{
-    PRBool isFile;
-    nsCOMPtr<nsINetUtil> util = do_GetNetUtil();
-
-    return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
-                                    nsIProtocolHandler::URI_IS_LOCAL_FILE,
-                                    &isFile)) &&
-           isFile;
-}
-
 PRBool
 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
 {
     NS_PRECONDITION(aURI, "Must have a URI!");
     
     if (!mFiredUnloadEvent) {
         return PR_TRUE;
     }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -645,22 +645,16 @@ protected:
 
     // Call BeginRestore(nsnull, PR_FALSE) for each child of this shell.
     nsresult BeginRestoreChildren();
 
     // Method to get our current position and size without flushing
     void DoGetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
                               PRInt32 * cy);
     
-    // Check whether aURI should inherit our security context
-    static nsresult URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult);
-
-    // Check whether aURI is a URI_IS_LOCAL_FILE or not
-    static PRBool URIIsLocalFile(nsIURI *aURI);
-
     // Call this when a URI load is handed to us (via OnLinkClick or
     // InternalLoad).  This makes sure that we're not inside unload, or that if
     // we are it's still OK to load this URI.
     PRBool IsOKToLoadURI(nsIURI* aURI);
     
     void ReattachEditorToWindow(nsISHEntry *aSHEntry);
 
     nsresult GetSessionStorageForURI(nsIURI* aURI,