Bug 1577498 - Part 3: Ensure actor and conduit cleanup r=rpl
[gecko.git] / image / ImageCacheKey.cpp
blob3efe072539a7df92e91840d40d5f2a18e640489a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ImageCacheKey.h"
8 #include "mozilla/HashFunctions.h"
9 #include "mozilla/Move.h"
10 #include "mozilla/Unused.h"
11 #include "nsContentUtils.h"
12 #include "nsICookieService.h"
13 #include "nsLayoutUtils.h"
14 #include "nsString.h"
15 #include "mozilla/AntiTrackingCommon.h"
16 #include "mozilla/HashFunctions.h"
17 #include "mozilla/StorageAccess.h"
18 #include "mozilla/dom/BlobURLProtocolHandler.h"
19 #include "mozilla/dom/File.h"
20 #include "mozilla/dom/ServiceWorkerManager.h"
21 #include "mozilla/dom/Document.h"
22 #include "nsHashKeys.h"
23 #include "nsPrintfCString.h"
25 namespace mozilla {
27 using namespace dom;
29 namespace image {
31 static Maybe<uint64_t> BlobSerial(nsIURI* aURI) {
32 nsAutoCString spec;
33 aURI->GetSpec(spec);
35 RefPtr<BlobImpl> blob;
36 if (NS_SUCCEEDED(NS_GetBlobForBlobURISpec(spec, getter_AddRefs(blob))) &&
37 blob) {
38 return Some(blob->GetSerialNumber());
41 return Nothing();
44 ImageCacheKey::ImageCacheKey(nsIURI* aURI, const OriginAttributes& aAttrs,
45 Document* aDocument)
46 : mURI(aURI),
47 mOriginAttributes(aAttrs),
48 mControlledDocument(GetSpecialCaseDocumentToken(aDocument, aURI)),
49 mTopLevelBaseDomain(GetTopLevelBaseDomain(aDocument, aURI)),
50 mIsChrome(false) {
51 if (mURI->SchemeIs("blob")) {
52 mBlobSerial = BlobSerial(mURI);
53 } else if (mURI->SchemeIs("chrome")) {
54 mIsChrome = true;
58 ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
59 : mURI(aOther.mURI),
60 mBlobSerial(aOther.mBlobSerial),
61 mBlobRef(aOther.mBlobRef),
62 mOriginAttributes(aOther.mOriginAttributes),
63 mControlledDocument(aOther.mControlledDocument),
64 mTopLevelBaseDomain(aOther.mTopLevelBaseDomain),
65 mHash(aOther.mHash),
66 mIsChrome(aOther.mIsChrome) {}
68 ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
69 : mURI(std::move(aOther.mURI)),
70 mBlobSerial(std::move(aOther.mBlobSerial)),
71 mBlobRef(std::move(aOther.mBlobRef)),
72 mOriginAttributes(aOther.mOriginAttributes),
73 mControlledDocument(aOther.mControlledDocument),
74 mTopLevelBaseDomain(aOther.mTopLevelBaseDomain),
75 mHash(aOther.mHash),
76 mIsChrome(aOther.mIsChrome) {}
78 bool ImageCacheKey::operator==(const ImageCacheKey& aOther) const {
79 // Don't share the image cache between a controlled document and anything
80 // else.
81 if (mControlledDocument != aOther.mControlledDocument) {
82 return false;
84 // Don't share the image cache between two top-level documents of different
85 // base domains.
86 if (!mTopLevelBaseDomain.Equals(aOther.mTopLevelBaseDomain,
87 nsCaseInsensitiveCStringComparator())) {
88 return false;
90 // The origin attributes always have to match.
91 if (mOriginAttributes != aOther.mOriginAttributes) {
92 return false;
94 if (mBlobSerial || aOther.mBlobSerial) {
95 if (mBlobSerial && mBlobRef.IsEmpty()) {
96 EnsureBlobRef();
98 if (aOther.mBlobSerial && aOther.mBlobRef.IsEmpty()) {
99 aOther.EnsureBlobRef();
101 // If at least one of us has a blob serial, just compare the blob serial and
102 // the ref portion of the URIs.
103 return mBlobSerial == aOther.mBlobSerial && mBlobRef == aOther.mBlobRef;
106 // For non-blob URIs, compare the URIs.
107 bool equals = false;
108 nsresult rv = mURI->Equals(aOther.mURI, &equals);
109 return NS_SUCCEEDED(rv) && equals;
112 void ImageCacheKey::EnsureBlobRef() const {
113 MOZ_ASSERT(mBlobSerial);
114 MOZ_ASSERT(mBlobRef.IsEmpty());
116 nsresult rv = mURI->GetRef(mBlobRef);
117 NS_ENSURE_SUCCESS_VOID(rv);
120 void ImageCacheKey::EnsureHash() const {
121 MOZ_ASSERT(mHash.isNothing());
122 PLDHashNumber hash = 0;
124 // Since we frequently call Hash() several times in a row on the same
125 // ImageCacheKey, as an optimization we compute our hash once and store it.
127 nsPrintfCString ptr("%p", mControlledDocument);
128 nsAutoCString suffix;
129 mOriginAttributes.CreateSuffix(suffix);
131 if (mBlobSerial) {
132 if (mBlobRef.IsEmpty()) {
133 EnsureBlobRef();
135 hash = HashGeneric(*mBlobSerial, HashString(mBlobRef));
136 } else {
137 nsAutoCString spec;
138 Unused << mURI->GetSpec(spec);
139 hash = HashString(spec);
142 hash = AddToHash(hash, HashString(suffix), HashString(mTopLevelBaseDomain),
143 HashString(ptr));
144 mHash.emplace(hash);
147 /* static */
148 void* ImageCacheKey::GetSpecialCaseDocumentToken(Document* aDocument,
149 nsIURI* aURI) {
150 // Cookie-averse documents can never have storage granted to them. Since they
151 // may not have inner windows, they would require special handling below, so
152 // just bail out early here.
153 if (!aDocument || aDocument->IsCookieAverse()) {
154 return nullptr;
157 // For controlled documents, we cast the pointer into a void* to avoid
158 // dereferencing it (since we only use it for comparisons).
159 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
160 if (swm && aDocument->GetController().isSome()) {
161 return aDocument;
164 return nullptr;
167 /* static */
168 nsCString ImageCacheKey::GetTopLevelBaseDomain(Document* aDocument,
169 nsIURI* aURI) {
170 if (!aDocument || !aDocument->GetInnerWindow()) {
171 return EmptyCString();
174 // If the window is 3rd party resource, let's see if first-party storage
175 // access is granted for this image.
176 if (nsContentUtils::IsThirdPartyTrackingResourceWindow(
177 aDocument->GetInnerWindow())) {
178 return StorageDisabledByAntiTracking(aDocument, aURI)
179 ? aDocument->GetBaseDomain()
180 : EmptyCString();
183 // Another scenario is if this image is a 3rd party resource loaded by a
184 // first party context. In this case, we should check if the nsIChannel has
185 // been marked as tracking resource, but we don't have the channel yet at
186 // this point. The best approach here is to be conservative: if we are sure
187 // that the permission is granted, let's return 0. Otherwise, let's make a
188 // unique image cache per the top-level document eTLD+1.
189 if (!AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(
190 aDocument->GetInnerWindow(), aURI)) {
191 nsPIDOMWindowOuter* top =
192 aDocument->GetInnerWindow()->GetInProcessScriptableTop();
193 nsPIDOMWindowInner* topInner = top ? top->GetCurrentInnerWindow() : nullptr;
194 if (!topInner) {
195 return aDocument
196 ->GetBaseDomain(); // because we don't have anything better!
198 return topInner->GetExtantDoc() ? topInner->GetExtantDoc()->GetBaseDomain()
199 : EmptyCString();
202 return EmptyCString();
205 } // namespace image
206 } // namespace mozilla