Bug 444641 part 3. Propagate the loading principal to the image channel as needed. r=joe,dveditz
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 20 Sep 2011 17:00:42 -0400
changeset 77221 c237a8550070a05b648d64a7a328e81151cd948b
parent 77220 a6a3d724bcf5946639e7352178542dc6120c15fd
child 77222 68928bdabfd745229946af70e896ac48112af3c0
push id21187
push usermak77@bonardo.net
push dateWed, 21 Sep 2011 08:36:41 +0000
treeherdermozilla-central@3178f1c42505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe, 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 3. Propagate the loading principal to the image channel as needed. r=joe,dveditz
modules/libpr0n/src/imgLoader.cpp
modules/libpr0n/src/imgLoader.h
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -87,16 +87,18 @@
 // until this point, we have an evil hack:
 #include "nsIHttpChannelInternal.h"  
 #include "nsIContentSecurityPolicy.h"
 #include "nsIChannelPolicy.h"
 
 #include "mozilla/FunctionTimer.h"
 #include "mozilla/Preferences.h"
 
+#include "nsContentUtils.h"
+
 using namespace mozilla;
 using namespace mozilla::imagelib;
 
 #if defined(DEBUG_pavlov) || defined(DEBUG_timeless)
 #include "nsISimpleEnumerator.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsXPIDLString.h"
@@ -390,23 +392,24 @@ nsProgressNotificationProxy::GetInterfac
     NS_ADDREF_THIS();
     return NS_OK;
   }
   if (mOriginalCallbacks)
     return mOriginalCallbacks->GetInterface(iid, result);
   return NS_NOINTERFACE;
 }
 
-static PRBool NewRequestAndEntry(imgRequest **request, imgCacheEntry **entry)
+static PRBool NewRequestAndEntry(bool forcePrincipalCheckForCacheEntry,
+                                 imgRequest **request, imgCacheEntry **entry)
 {
   *request = new imgRequest();
   if (!*request)
     return PR_FALSE;
 
-  *entry = new imgCacheEntry(*request);
+  *entry = new imgCacheEntry(*request, forcePrincipalCheckForCacheEntry);
   if (!*entry) {
     delete *request;
     return PR_FALSE;
   }
 
   NS_ADDREF(*request);
   NS_ADDREF(*entry);
 
@@ -454,23 +457,25 @@ static PRBool ShouldRevalidateEntry(imgC
 
   return bValidateEntry;
 }
 
 // Returns true if this request is compatible with the given CORS mode on the
 // given loading principal, and false if the request may not be reused due
 // to CORS.
 static bool
-ValidateCORS(imgRequest* request, PRInt32 corsmode, nsIPrincipal* loadingPrincipal)
+ValidateCORSAndPrincipal(imgRequest* request, bool forcePrincipalCheck,
+                         PRInt32 corsmode, nsIPrincipal* loadingPrincipal)
 {
   // If the entry's CORS mode doesn't match, or the CORS mode matches but the
   // document principal isn't the same, we can't use this request.
   if (request->GetCORSMode() != corsmode) {
     return false;
-  } else if (request->GetCORSMode() != imgIRequest::CORS_NONE) {
+  } else if (request->GetCORSMode() != imgIRequest::CORS_NONE ||
+             forcePrincipalCheck) {
     nsCOMPtr<nsIPrincipal> otherprincipal = request->GetLoadingPrincipal();
 
     // If we previously had a principal, but we don't now, we can't use this
     // request.
     if (otherprincipal && !loadingPrincipal) {
       return false;
     }
 
@@ -480,23 +485,33 @@ ValidateCORS(imgRequest* request, PRInt3
       return equals;
     }
   }
 
   return true;
 }
 
 static nsresult NewImageChannel(nsIChannel **aResult,
+                                // If aForcePrincipalCheckForCacheEntry is
+                                // true, then we will force a principal check
+                                // even when not using CORS before assuming we
+                                // have a cache hit on a cache entry that we
+                                // create for this channel.  This is an out
+                                // param that should be set to true if this
+                                // channel ends up depending on
+                                // aLoadingPrincipal and false otherwise.
+                                bool *aForcePrincipalCheckForCacheEntry,
                                 nsIURI *aURI,
                                 nsIURI *aInitialDocumentURI,
                                 nsIURI *aReferringURI,
                                 nsILoadGroup *aLoadGroup,
                                 const nsCString& aAcceptHeader,
                                 nsLoadFlags aLoadFlags,
-                                nsIChannelPolicy *aPolicy)
+                                nsIChannelPolicy *aPolicy,
+                                nsIPrincipal *aLoadingPrincipal)
 {
   nsresult rv;
   nsCOMPtr<nsIChannel> newChannel;
   nsCOMPtr<nsIHttpChannel> newHttpChannel;
  
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
 
   if (aLoadGroup) {
@@ -521,16 +536,18 @@ static nsresult NewImageChannel(nsIChann
                      nsnull,      // Cached IOService
                      nsnull,      // LoadGroup
                      callbacks,   // Notification Callbacks
                      aLoadFlags,
                      aPolicy);
   if (NS_FAILED(rv))
     return rv;
 
+  *aForcePrincipalCheckForCacheEntry = false;
+
   // Initialize HTTP-specific attributes
   newHttpChannel = do_QueryInterface(*aResult);
   if (newHttpChannel) {
     newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                      aAcceptHeader,
                                      PR_FALSE);
 
     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(newHttpChannel);
@@ -545,34 +562,39 @@ static nsresult NewImageChannel(nsIChann
     PRUint32 priority = nsISupportsPriority::PRIORITY_LOW;
 
     if (aLoadFlags & nsIRequest::LOAD_BACKGROUND)
       ++priority; // further reduce priority for background loads
 
     p->AdjustPriority(priority);
   }
 
+  PRBool setOwner = nsContentUtils::SetUpChannelOwner(aLoadingPrincipal,
+                                                      *aResult, aURI, PR_FALSE);
+  *aForcePrincipalCheckForCacheEntry = setOwner;
+
   return NS_OK;
 }
 
 static PRUint32 SecondsFromPRTime(PRTime prTime)
 {
   return PRUint32(PRInt64(prTime) / PRInt64(PR_USEC_PER_SEC));
 }
 
-imgCacheEntry::imgCacheEntry(imgRequest *request)
+imgCacheEntry::imgCacheEntry(imgRequest *request, bool forcePrincipalCheck)
  : mRequest(request),
    mDataSize(0),
    mTouchedTime(SecondsFromPRTime(PR_Now())),
    mExpiryTime(0),
    mMustValidate(PR_FALSE),
    // We start off as evicted so we don't try to update the cache. PutIntoCache
    // will set this to false.
    mEvicted(PR_TRUE),
-   mHasNoProxies(PR_TRUE)
+   mHasNoProxies(PR_TRUE),
+   mForcePrincipalCheck(forcePrincipalCheck)
 {}
 
 imgCacheEntry::~imgCacheEntry()
 {
   LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
 }
 
 void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */)
@@ -1236,24 +1258,27 @@ PRBool imgLoader::ValidateRequestWithNew
 
     return NS_SUCCEEDED(rv);
 
   } else {
     // We will rely on Necko to cache this request when it's possible, and to
     // tell imgCacheValidator::OnStartRequest whether the request came from its
     // cache.
     nsCOMPtr<nsIChannel> newChannel;
+    bool forcePrincipalCheck;
     rv = NewImageChannel(getter_AddRefs(newChannel),
+                         &forcePrincipalCheck,
                          aURI,
                          aInitialDocumentURI,
                          aReferrerURI,
                          aLoadGroup,
                          mAcceptHeader,
                          aLoadFlags,
-                         aPolicy);
+                         aPolicy,
+                         aLoadingPrincipal);
     if (NS_FAILED(rv)) {
       return PR_FALSE;
     }
 
     nsCOMPtr<imgIRequest> req;
     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
                                   aLoadFlags, aExistingRequest, getter_AddRefs(req));
     if (NS_FAILED(rv)) {
@@ -1261,17 +1286,18 @@ PRBool imgLoader::ValidateRequestWithNew
     }
 
     // Make sure that OnStatus/OnProgress calls have the right request set...
     nsRefPtr<nsProgressNotificationProxy> progressproxy =
         new nsProgressNotificationProxy(newChannel, req);
     if (!progressproxy)
       return PR_FALSE;
 
-    nsRefPtr<imgCacheValidator> hvc = new imgCacheValidator(progressproxy, request, aCX);
+    nsRefPtr<imgCacheValidator> hvc =
+      new imgCacheValidator(progressproxy, request, aCX, forcePrincipalCheck);
 
     nsCOMPtr<nsIStreamListener> listener = hvc.get();
 
     if (aCORSMode != imgIRequest::CORS_NONE) {
       PRBool withCredentials = aCORSMode == imgIRequest::CORS_USE_CREDENTIALS;
       nsCOMPtr<nsIStreamListener> corsproxy =
         new nsCORSListenerProxy(hvc, aLoadingPrincipal, newChannel, withCredentials, &rv);
       if (NS_FAILED(rv)) {
@@ -1350,17 +1376,18 @@ PRBool imgLoader::ValidateEntry(imgCache
     }
   }
 
   nsRefPtr<imgRequest> request(aEntry->GetRequest());
 
   if (!request)
     return PR_FALSE;
 
-  if (!ValidateCORS(request, aCORSMode, aLoadingPrincipal))
+  if (!ValidateCORSAndPrincipal(request, aEntry->ForcePrincipalCheck(),
+                                aCORSMode, aLoadingPrincipal))
     return PR_FALSE;
 
   PRBool validateRequest = PR_FALSE;
 
   // If the request's loadId is the same as the aCX, then it is ok to use
   // this one because it has already been validated for this context.
   //
   // XXX: nsnull seems to be a 'special' key value that indicates that NO
@@ -1651,28 +1678,32 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIUR
 
   // Keep the channel in this scope, so we can adjust its notificationCallbacks
   // later when we create the proxy.
   nsCOMPtr<nsIChannel> newChannel;
   // If we didn't get a cache hit, we need to load from the network.
   if (!request) {
     LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
 
+    bool forcePrincipalCheck;
     rv = NewImageChannel(getter_AddRefs(newChannel),
+                         &forcePrincipalCheck,
                          aURI,
                          aInitialDocumentURI,
                          aReferrerURI,
                          aLoadGroup,
                          mAcceptHeader,
                          requestFlags,
-                         aPolicy);
+                         aPolicy,
+                         aLoadingPrincipal);
     if (NS_FAILED(rv))
       return NS_ERROR_FAILURE;
 
-    if (!NewRequestAndEntry(getter_AddRefs(request), getter_AddRefs(entry)))
+    if (!NewRequestAndEntry(forcePrincipalCheck, getter_AddRefs(request),
+                            getter_AddRefs(entry)))
       return NS_ERROR_OUT_OF_MEMORY;
 
     PR_LOG(gImgLog, PR_LOG_DEBUG,
            ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get()));
 
     // Create a loadgroup for this new channel.  This way if the channel
     // is redirected, we'll have a way to cancel the resulting channel.
     nsCOMPtr<nsILoadGroup> loadGroup =
@@ -1863,17 +1894,21 @@ NS_IMETHODIMP imgLoader::LoadImageWithCh
     channel->Cancel(NS_ERROR_PARSED_DATA_CACHED); // this should fire an OnStopRequest
 
     *listener = nsnull; // give them back a null nsIStreamListener
 
     rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
                                   requestFlags, nsnull, _retval);
     static_cast<imgRequestProxy*>(*_retval)->NotifyListener();
   } else {
-    if (!NewRequestAndEntry(getter_AddRefs(request), getter_AddRefs(entry)))
+    // Default to doing a principal check because we don't know who
+    // started that load and whether their principal ended up being
+    // inherited on the channel.
+    if (!NewRequestAndEntry(PR_TRUE, getter_AddRefs(request),
+                            getter_AddRefs(entry)))
       return NS_ERROR_OUT_OF_MEMORY;
 
     // We use originalURI here to fulfil the imgIRequest contract on GetURI.
     nsCOMPtr<nsIURI> originalURI;
     channel->GetOriginalURI(getter_AddRefs(originalURI));
 
     // No principal specified here, because we're not passed one.
     request->Init(originalURI, uri, channel, channel, entry,
@@ -2079,22 +2114,24 @@ NS_IMETHODIMP ProxyListener::OnDataAvail
 
 NS_IMPL_ISUPPORTS5(imgCacheValidator, nsIStreamListener, nsIRequestObserver,
                    nsIChannelEventSink, nsIInterfaceRequestor,
                    nsIAsyncVerifyRedirectCallback)
 
 imgLoader imgCacheValidator::sImgLoader;
 
 imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress,
-                                     imgRequest *request, void *aContext)
+                                     imgRequest *request, void *aContext,
+                                     bool forcePrincipalCheckForCacheEntry)
  : mProgressProxy(progress),
    mRequest(request),
    mContext(aContext)
 {
-  NewRequestAndEntry(getter_AddRefs(mNewRequest), getter_AddRefs(mNewEntry));
+  NewRequestAndEntry(forcePrincipalCheckForCacheEntry,
+                     getter_AddRefs(mNewRequest), getter_AddRefs(mNewEntry));
 }
 
 imgCacheValidator::~imgCacheValidator()
 {
   if (mRequest) {
     mRequest->mValidator = nsnull;
   }
 }
--- a/modules/libpr0n/src/imgLoader.h
+++ b/modules/libpr0n/src/imgLoader.h
@@ -63,17 +63,17 @@ class imgRequest;
 class imgRequestProxy;
 class imgIRequest;
 class imgIDecoderObserver;
 class nsILoadGroup;
 
 class imgCacheEntry
 {
 public:
-  imgCacheEntry(imgRequest *request);
+  imgCacheEntry(imgRequest *request, bool aForcePrincipalCheck);
   ~imgCacheEntry();
 
   nsrefcnt AddRef()
   {
     NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
     NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(), "imgCacheEntry addref isn't thread-safe!");
     ++mRefCnt;
     NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this));
@@ -152,16 +152,21 @@ public:
     return &mExpirationState;
   }
 
   PRBool HasNoProxies() const
   {
     return mHasNoProxies;
   }
 
+  bool ForcePrincipalCheck() const
+  {
+    return mForcePrincipalCheck;
+  }
+
 private: // methods
   friend class imgLoader;
   friend class imgCacheQueue;
   void Touch(PRBool updateTime = PR_TRUE);
   void UpdateCache(PRInt32 diff = 0);
   void SetEvicted(PRBool evict)
   {
     mEvicted = evict;
@@ -178,16 +183,17 @@ private: // data
   nsRefPtr<imgRequest> mRequest;
   PRUint32 mDataSize;
   PRInt32 mTouchedTime;
   PRInt32 mExpiryTime;
   nsExpirationState mExpirationState;
   PRPackedBool mMustValidate : 1;
   PRPackedBool mEvicted : 1;
   PRPackedBool mHasNoProxies : 1;
+  PRPackedBool mForcePrincipalCheck : 1;
 };
 
 #include <vector>
 
 #define NS_IMGLOADER_CID \
 { /* 9f6a0d2e-1dd1-11b2-a5b8-951f13c846f7 */         \
      0x9f6a0d2e,                                     \
      0x1dd1,                                         \
@@ -415,17 +421,18 @@ class nsProgressNotificationProxy : publ
 #include "nsCOMArray.h"
 
 class imgCacheValidator : public nsIStreamListener,
                           public nsIChannelEventSink,
                           public nsIInterfaceRequestor,
                           public nsIAsyncVerifyRedirectCallback
 {
 public:
-  imgCacheValidator(nsProgressNotificationProxy* progress, imgRequest *request, void *aContext);
+  imgCacheValidator(nsProgressNotificationProxy* progress, imgRequest *request,
+                    void *aContext, bool forcePrincipalCheckForCacheEntry);
   virtual ~imgCacheValidator();
 
   void AddProxy(imgRequestProxy *aProxy);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSICHANNELEVENTSINK