Bug 1610307 [wpt PR 21266] - Update wpt metadata, a=testonly
[gecko.git] / image / ImageCacheKey.cpp
blobd5c69e3de67fb483f550ed45945c52453b73d1f4
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 <utility>
10 #include "mozilla/AntiTrackingCommon.h"
11 #include "mozilla/HashFunctions.h"
12 #include "mozilla/StorageAccess.h"
13 #include "mozilla/Unused.h"
14 #include "mozilla/dom/BlobURLProtocolHandler.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/File.h"
17 #include "mozilla/dom/ServiceWorkerManager.h"
18 #include "nsContentUtils.h"
19 #include "nsHashKeys.h"
20 #include "nsLayoutUtils.h"
21 #include "nsPrintfCString.h"
22 #include "nsString.h"
24 namespace mozilla {
26 using namespace dom;
28 namespace image {
30 static Maybe<uint64_t> BlobSerial(nsIURI* aURI) {
31 nsAutoCString spec;
32 aURI->GetSpec(spec);
34 RefPtr<BlobImpl> blob;
35 if (NS_SUCCEEDED(NS_GetBlobForBlobURISpec(spec, getter_AddRefs(blob))) &&
36 blob) {
37 return Some(blob->GetSerialNumber());
40 return Nothing();
43 ImageCacheKey::ImageCacheKey(nsIURI* aURI, const OriginAttributes& aAttrs,
44 Document* aDocument)
45 : mURI(aURI),
46 mOriginAttributes(aAttrs),
47 mControlledDocument(GetSpecialCaseDocumentToken(aDocument, aURI)),
48 mTopLevelBaseDomain(GetTopLevelBaseDomain(aDocument, aURI)),
49 mIsChrome(false) {
50 if (mURI->SchemeIs("blob")) {
51 mBlobSerial = BlobSerial(mURI);
52 } else if (mURI->SchemeIs("chrome")) {
53 mIsChrome = true;
57 ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
58 : mURI(aOther.mURI),
59 mBlobSerial(aOther.mBlobSerial),
60 mBlobRef(aOther.mBlobRef),
61 mOriginAttributes(aOther.mOriginAttributes),
62 mControlledDocument(aOther.mControlledDocument),
63 mTopLevelBaseDomain(aOther.mTopLevelBaseDomain),
64 mHash(aOther.mHash),
65 mIsChrome(aOther.mIsChrome) {}
67 ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
68 : mURI(std::move(aOther.mURI)),
69 mBlobSerial(std::move(aOther.mBlobSerial)),
70 mBlobRef(std::move(aOther.mBlobRef)),
71 mOriginAttributes(aOther.mOriginAttributes),
72 mControlledDocument(aOther.mControlledDocument),
73 mTopLevelBaseDomain(aOther.mTopLevelBaseDomain),
74 mHash(aOther.mHash),
75 mIsChrome(aOther.mIsChrome) {}
77 bool ImageCacheKey::operator==(const ImageCacheKey& aOther) const {
78 // Don't share the image cache between a controlled document and anything
79 // else.
80 if (mControlledDocument != aOther.mControlledDocument) {
81 return false;
83 // Don't share the image cache between two top-level documents of different
84 // base domains.
85 if (!mTopLevelBaseDomain.Equals(aOther.mTopLevelBaseDomain,
86 nsCaseInsensitiveCStringComparator())) {
87 return false;
89 // The origin attributes always have to match.
90 if (mOriginAttributes != aOther.mOriginAttributes) {
91 return false;
93 if (mBlobSerial || aOther.mBlobSerial) {
94 if (mBlobSerial && mBlobRef.IsEmpty()) {
95 EnsureBlobRef();
97 if (aOther.mBlobSerial && aOther.mBlobRef.IsEmpty()) {
98 aOther.EnsureBlobRef();
100 // If at least one of us has a blob serial, just compare the blob serial and
101 // the ref portion of the URIs.
102 return mBlobSerial == aOther.mBlobSerial && mBlobRef == aOther.mBlobRef;
105 // For non-blob URIs, compare the URIs.
106 bool equals = false;
107 nsresult rv = mURI->Equals(aOther.mURI, &equals);
108 return NS_SUCCEEDED(rv) && equals;
111 void ImageCacheKey::EnsureBlobRef() const {
112 MOZ_ASSERT(mBlobSerial);
113 MOZ_ASSERT(mBlobRef.IsEmpty());
115 nsresult rv = mURI->GetRef(mBlobRef);
116 NS_ENSURE_SUCCESS_VOID(rv);
119 void ImageCacheKey::EnsureHash() const {
120 MOZ_ASSERT(mHash.isNothing());
121 PLDHashNumber hash = 0;
123 // Since we frequently call Hash() several times in a row on the same
124 // ImageCacheKey, as an optimization we compute our hash once and store it.
126 nsPrintfCString ptr("%p", mControlledDocument);
127 nsAutoCString suffix;
128 mOriginAttributes.CreateSuffix(suffix);
130 if (mBlobSerial) {
131 if (mBlobRef.IsEmpty()) {
132 EnsureBlobRef();
134 hash = HashGeneric(*mBlobSerial, HashString(mBlobRef));
135 } else {
136 nsAutoCString spec;
137 Unused << mURI->GetSpec(spec);
138 hash = HashString(spec);
141 hash = AddToHash(hash, HashString(suffix), HashString(mTopLevelBaseDomain),
142 HashString(ptr));
143 mHash.emplace(hash);
146 /* static */
147 void* ImageCacheKey::GetSpecialCaseDocumentToken(Document* aDocument,
148 nsIURI* aURI) {
149 // Cookie-averse documents can never have storage granted to them. Since they
150 // may not have inner windows, they would require special handling below, so
151 // just bail out early here.
152 if (!aDocument || aDocument->IsCookieAverse()) {
153 return nullptr;
156 // For controlled documents, we cast the pointer into a void* to avoid
157 // dereferencing it (since we only use it for comparisons).
158 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
159 if (swm && aDocument->GetController().isSome()) {
160 return aDocument;
163 return nullptr;
166 /* static */
167 nsCString ImageCacheKey::GetTopLevelBaseDomain(Document* aDocument,
168 nsIURI* aURI) {
169 if (!aDocument || !aDocument->GetInnerWindow()) {
170 return EmptyCString();
173 // If the window is 3rd party resource, let's see if first-party storage
174 // access is granted for this image.
175 if (nsContentUtils::IsThirdPartyTrackingResourceWindow(
176 aDocument->GetInnerWindow())) {
177 return StorageDisabledByAntiTracking(aDocument, aURI)
178 ? aDocument->GetBaseDomain()
179 : EmptyCString();
182 // Another scenario is if this image is a 3rd party resource loaded by a
183 // first party context. In this case, we should check if the nsIChannel has
184 // been marked as tracking resource, but we don't have the channel yet at
185 // this point. The best approach here is to be conservative: if we are sure
186 // that the permission is granted, let's return 0. Otherwise, let's make a
187 // unique image cache per the top-level document eTLD+1.
188 if (!AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(
189 aDocument->GetInnerWindow(), aURI)) {
190 nsPIDOMWindowOuter* top =
191 aDocument->GetInnerWindow()->GetInProcessScriptableTop();
192 nsPIDOMWindowInner* topInner = top ? top->GetCurrentInnerWindow() : nullptr;
193 if (!topInner) {
194 return aDocument
195 ->GetBaseDomain(); // because we don't have anything better!
197 return topInner->GetExtantDoc() ? topInner->GetExtantDoc()->GetBaseDomain()
198 : EmptyCString();
201 return EmptyCString();
204 } // namespace image
205 } // namespace mozilla