Bug 1202085 - Part 4: Add an ID for controlled document to the image cache key; r=seth a=ritu
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 27 Oct 2015 14:12:46 -0400
changeset 305450 5289a341cf53fb6779e1ca57f8fad7e56efb5635
parent 305449 8802cf4e9ecf5090fc295efb6543974aae4714cb
child 305451 4f2fb4f1bd945a026a874a791810eb44c7624534
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersseth, ritu
bugs1202085
milestone44.0a2
Bug 1202085 - Part 4: Add an ID for controlled document to the image cache key; r=seth a=ritu This ID will be null for non-controlled documents and also for image cache entries for which a document is not available, and it will be the numerical value of the document pointer for controlled documents. This effectively makes sure that a controlled document doesn't share its image cache entries with anything else.
image/ImageCacheKey.cpp
image/ImageCacheKey.h
image/imgLoader.cpp
image/imgRequest.cpp
--- a/image/ImageCacheKey.cpp
+++ b/image/ImageCacheKey.cpp
@@ -5,16 +5,20 @@
 
 #include "ImageCacheKey.h"
 
 #include "mozilla/Move.h"
 #include "File.h"
 #include "ImageURL.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsString.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "nsIDOMDocument.h"
+#include "nsIDocument.h"
+#include "nsPrintfCString.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 namespace image {
 
 bool
@@ -37,59 +41,67 @@ BlobSerial(ImageURL* aURI)
   if (NS_SUCCEEDED(NS_GetBlobForBlobURISpec(spec, getter_AddRefs(blob))) &&
       blob) {
     return Some(blob->GetSerialNumber());
   }
 
   return Nothing();
 }
 
-ImageCacheKey::ImageCacheKey(nsIURI* aURI)
+ImageCacheKey::ImageCacheKey(nsIURI* aURI, nsIDOMDocument* aDocument)
   : mURI(new ImageURL(aURI))
+  , mControlledDocument(GetControlledDocumentToken(aDocument))
   , mIsChrome(URISchemeIs(mURI, "chrome"))
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (URISchemeIs(mURI, "blob")) {
     mBlobSerial = BlobSerial(mURI);
   }
 
-  mHash = ComputeHash(mURI, mBlobSerial);
+  mHash = ComputeHash(mURI, mBlobSerial, mControlledDocument);
 }
 
-ImageCacheKey::ImageCacheKey(ImageURL* aURI)
+ImageCacheKey::ImageCacheKey(ImageURL* aURI, nsIDOMDocument* aDocument)
   : mURI(aURI)
+  , mControlledDocument(GetControlledDocumentToken(aDocument))
   , mIsChrome(URISchemeIs(mURI, "chrome"))
 {
   MOZ_ASSERT(aURI);
 
   if (URISchemeIs(mURI, "blob")) {
     mBlobSerial = BlobSerial(mURI);
   }
 
-  mHash = ComputeHash(mURI, mBlobSerial);
+  mHash = ComputeHash(mURI, mBlobSerial, mControlledDocument);
 }
 
 ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
   : mURI(aOther.mURI)
   , mBlobSerial(aOther.mBlobSerial)
+  , mControlledDocument(aOther.mControlledDocument)
   , mHash(aOther.mHash)
   , mIsChrome(aOther.mIsChrome)
 { }
 
 ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
   : mURI(Move(aOther.mURI))
   , mBlobSerial(Move(aOther.mBlobSerial))
+  , mControlledDocument(aOther.mControlledDocument)
   , mHash(aOther.mHash)
   , mIsChrome(aOther.mIsChrome)
 { }
 
 bool
 ImageCacheKey::operator==(const ImageCacheKey& aOther) const
 {
+  // Don't share the image cache between a controlled document and anything else.
+  if (mControlledDocument != aOther.mControlledDocument) {
+    return false;
+  }
   if (mBlobSerial || aOther.mBlobSerial) {
     // If at least one of us has a blob serial, just compare the blob serial and
     // the ref portion of the URIs.
     return mBlobSerial == aOther.mBlobSerial &&
            mURI->HasSameRef(*aOther.mURI);
   }
 
   // For non-blob URIs, compare the URIs.
@@ -99,32 +111,53 @@ ImageCacheKey::operator==(const ImageCac
 const char*
 ImageCacheKey::Spec() const
 {
   return mURI->Spec();
 }
 
 /* static */ uint32_t
 ImageCacheKey::ComputeHash(ImageURL* aURI,
-                           const Maybe<uint64_t>& aBlobSerial)
+                           const Maybe<uint64_t>& aBlobSerial,
+                           void* aControlledDocument)
 {
   // Since we frequently call Hash() several times in a row on the same
   // ImageCacheKey, as an optimization we compute our hash once and store it.
 
+  nsPrintfCString ptr("%p", aControlledDocument);
   if (aBlobSerial) {
     // For blob URIs, we hash the serial number of the underlying blob, so that
     // different blob URIs which point to the same blob share a cache entry. We
     // also include the ref portion of the URI to support -moz-samplesize, which
     // requires us to create different Image objects even if the source data is
     // the same.
     nsAutoCString ref;
     aURI->GetRef(ref);
-    return HashGeneric(*aBlobSerial, HashString(ref));
+    return HashGeneric(*aBlobSerial, HashString(ref + ptr));
   }
 
   // For non-blob URIs, we hash the URI spec.
   nsAutoCString spec;
   aURI->GetSpec(spec);
-  return HashString(spec);
+  return HashString(spec + ptr);
+}
+
+/* static */ void*
+ImageCacheKey::GetControlledDocumentToken(nsIDOMDocument* aDocument)
+{
+  // For non-controlled documents, we just return null.  For controlled
+  // documents, we cast the pointer into a void* to avoid dereferencing
+  // it (since we only use it for comparisons), and return it.
+  void* pointer = nullptr;
+  using dom::workers::ServiceWorkerManager;
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
+  if (doc && swm) {
+    ErrorResult rv;
+    if (swm->IsControlled(doc, rv)) {
+      pointer = doc;
+    }
+  }
+  return pointer;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/ImageCacheKey.h
+++ b/image/ImageCacheKey.h
@@ -7,53 +7,59 @@
  * ImageCacheKey is the key type for the image cache (see imgLoader.h).
  */
 
 #ifndef mozilla_image_src_ImageCacheKey_h
 #define mozilla_image_src_ImageCacheKey_h
 
 #include "mozilla/Maybe.h"
 
+class nsIDOMDocument;
 class nsIURI;
 
 namespace mozilla {
 namespace image {
 
 class ImageURL;
 
 /**
  * An ImageLib cache entry key.
  *
  * We key the cache on the initial URI (before any redirects), with some
  * canonicalization applied. See ComputeHash() for the details.
+ * Controlled documents do not share their cache entries with
+ * non-controlled documents, or other controlled documents.
  */
 class ImageCacheKey final
 {
 public:
-  explicit ImageCacheKey(nsIURI* aURI);
-  explicit ImageCacheKey(ImageURL* aURI);
+  ImageCacheKey(nsIURI* aURI, nsIDOMDocument* aDocument);
+  ImageCacheKey(ImageURL* aURI, nsIDOMDocument* aDocument);
 
   ImageCacheKey(const ImageCacheKey& aOther);
   ImageCacheKey(ImageCacheKey&& aOther);
 
   bool operator==(const ImageCacheKey& aOther) const;
   uint32_t Hash() const { return mHash; }
 
   /// A weak pointer to the URI spec for this cache entry. For logging only.
   const char* Spec() const;
 
   /// Is this cache entry for a chrome image?
   bool IsChrome() const { return mIsChrome; }
 
 private:
   static uint32_t ComputeHash(ImageURL* aURI,
-                              const Maybe<uint64_t>& aBlobSerial);
+                              const Maybe<uint64_t>& aBlobSerial,
+                              void* aControlledDocument);
+  static void* GetControlledDocumentToken(nsIDOMDocument* aDocument);
 
   RefPtr<ImageURL> mURI;
   Maybe<uint64_t> mBlobSerial;
+  void* mControlledDocument;
   uint32_t mHash;
   bool mIsChrome;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_src_ImageCacheKey_h
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -47,16 +47,17 @@
 #include "prtime.h"
 
 // we want to explore making the document own the load group
 // so we can associate the document URI with the load group.
 // until this point, we have an evil hack:
 #include "nsIHttpChannelInternal.h"
 #include "nsILoadContext.h"
 #include "nsILoadGroupChild.h"
+#include "nsIDOMDocument.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
 using namespace mozilla::net;
 
 MOZ_DEFINE_MALLOC_SIZE_OF(ImagesMallocSizeOf)
 
 class imgMemoryReporter final : public nsIMemoryReporter
@@ -1336,17 +1337,17 @@ imgLoader::ClearCache(bool chrome)
 
 NS_IMETHODIMP
 imgLoader::FindEntryProperties(nsIURI* uri,
                                nsIDOMDocument* doc,
                                nsIProperties** _retval)
 {
   *_retval = nullptr;
 
-  ImageCacheKey key(uri);
+  ImageCacheKey key(uri, doc);
   imgCacheTable& cache = GetCache(key);
 
   RefPtr<imgCacheEntry> entry;
   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
     if (mCacheTracker && entry->HasNoProxies()) {
       mCacheTracker->MarkUsed(entry);
     }
 
@@ -2082,17 +2083,18 @@ imgLoader::LoadImage(nsIURI* aURI,
   }
 
   RefPtr<imgCacheEntry> entry;
 
   // Look in the cache for our URI, and then validate it.
   // XXX For now ignore aCacheKey. We will need it in the future
   // for correctly dealing with image load requests that are a result
   // of post data.
-  ImageCacheKey key(aURI);
+  nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aCX);
+  ImageCacheKey key(aURI, doc);
   imgCacheTable& cache = GetCache(key);
 
   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
     if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
                       aReferrerPolicy, aLoadGroup, aObserver, aCX,
                       requestFlags, aContentPolicyType, true, _retval,
                       aLoadingPrincipal, corsmode)) {
       request = entry->GetRequest();
@@ -2306,17 +2308,18 @@ imgLoader::LoadImageWithChannel(nsIChann
                "imgLoader::LoadImageWithChannel -- NULL channel pointer");
 
   MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy);
 
   RefPtr<imgRequest> request;
 
   nsCOMPtr<nsIURI> uri;
   channel->GetURI(getter_AddRefs(uri));
-  ImageCacheKey key(uri);
+  nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aCX);
+  ImageCacheKey key(uri, doc);
 
   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
   channel->GetLoadFlags(&requestFlags);
 
   RefPtr<imgCacheEntry> entry;
 
   if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
     RemoveFromCache(key);
@@ -2400,17 +2403,17 @@ imgLoader::LoadImageWithChannel(nsIChann
     // We use originalURI here to fulfil the imgIRequest contract on GetURI.
     nsCOMPtr<nsIURI> originalURI;
     channel->GetOriginalURI(getter_AddRefs(originalURI));
 
     // XXX(seth): We should be able to just use |key| here, except that |key| is
     // constructed above with the *current URI* and not the *original URI*. I'm
     // pretty sure this is a bug, and it's preventing us from ever getting a
     // cache hit in LoadImageWithChannel when redirects are involved.
-    ImageCacheKey originalURIKey(originalURI);
+    ImageCacheKey originalURIKey(originalURI, doc);
 
     // 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.
     NewRequestAndEntry(/* aForcePrincipalCheckForCacheEntry = */ true,
                        this, originalURIKey,
                        getter_AddRefs(request),
                        getter_AddRefs(entry));
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -496,17 +496,17 @@ imgRequest::RemoveFromCache()
     isInCache = mIsInCache;
   }
 
   if (isInCache && mLoader) {
     // mCacheEntry is nulled out when we have no more observers.
     if (mCacheEntry) {
       mLoader->RemoveFromCache(mCacheEntry);
     } else {
-      mLoader->RemoveFromCache(ImageCacheKey(mURI));
+      mLoader->RemoveFromCache(mCacheKey);
     }
   }
 
   mCacheEntry = nullptr;
 }
 
 bool
 imgRequest::HasConsumers() const