Bug 1864652 - Expose settings for Global Privacy Control. r=geckoview-reviewers,ohall...
[gecko.git] / image / ImageCacheKey.cpp
blob7a5f78b70fd3e9e4332c53bebfe5dd252d836886
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/AntiTrackingUtils.h"
11 #include "mozilla/HashFunctions.h"
12 #include "mozilla/StorageAccess.h"
13 #include "mozilla/StoragePrincipalHelper.h"
14 #include "mozilla/Unused.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/File.h"
17 #include "mozilla/dom/ServiceWorkerManager.h"
18 #include "mozilla/StaticPrefs_privacy.h"
19 #include "mozilla/StorageAccess.h"
20 #include "nsContentUtils.h"
21 #include "nsHashKeys.h"
22 #include "nsLayoutUtils.h"
23 #include "nsPrintfCString.h"
24 #include "nsString.h"
26 namespace mozilla {
28 using namespace dom;
30 namespace image {
32 ImageCacheKey::ImageCacheKey(nsIURI* aURI, CORSMode aCORSMode,
33 const OriginAttributes& aAttrs,
34 Document* aDocument)
35 : mURI(aURI),
36 mOriginAttributes(aAttrs),
37 mControlledDocument(GetSpecialCaseDocumentToken(aDocument)),
38 mIsolationKey(GetIsolationKey(aDocument, aURI)),
39 mCORSMode(aCORSMode) {}
41 ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
42 : mURI(aOther.mURI),
43 mOriginAttributes(aOther.mOriginAttributes),
44 mControlledDocument(aOther.mControlledDocument),
45 mIsolationKey(aOther.mIsolationKey),
46 mHash(aOther.mHash),
47 mCORSMode(aOther.mCORSMode) {}
49 ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
50 : mURI(std::move(aOther.mURI)),
51 mOriginAttributes(aOther.mOriginAttributes),
52 mControlledDocument(aOther.mControlledDocument),
53 mIsolationKey(aOther.mIsolationKey),
54 mHash(aOther.mHash),
55 mCORSMode(aOther.mCORSMode) {}
57 bool ImageCacheKey::operator==(const ImageCacheKey& aOther) const {
58 // Don't share the image cache between a controlled document and anything
59 // else.
60 if (mControlledDocument != aOther.mControlledDocument) {
61 return false;
63 // Don't share the image cache between two top-level documents of different
64 // base domains.
65 if (!mIsolationKey.Equals(aOther.mIsolationKey,
66 nsCaseInsensitiveCStringComparator)) {
67 return false;
69 // The origin attributes always have to match.
70 if (mOriginAttributes != aOther.mOriginAttributes) {
71 return false;
74 if (mCORSMode != aOther.mCORSMode) {
75 return false;
78 // For non-blob URIs, compare the URIs.
79 bool equals = false;
80 nsresult rv = mURI->Equals(aOther.mURI, &equals);
81 return NS_SUCCEEDED(rv) && equals;
84 void ImageCacheKey::EnsureHash() const {
85 MOZ_ASSERT(mHash.isNothing());
86 PLDHashNumber hash = 0;
88 // Since we frequently call Hash() several times in a row on the same
89 // ImageCacheKey, as an optimization we compute our hash once and store it.
91 nsPrintfCString ptr("%p", mControlledDocument);
92 nsAutoCString suffix;
93 mOriginAttributes.CreateSuffix(suffix);
95 nsAutoCString spec;
96 Unused << mURI->GetSpec(spec);
97 hash = HashString(spec);
99 hash = AddToHash(hash, HashString(suffix), HashString(mIsolationKey),
100 HashString(ptr));
101 mHash.emplace(hash);
104 /* static */
105 void* ImageCacheKey::GetSpecialCaseDocumentToken(Document* aDocument) {
106 // Cookie-averse documents can never have storage granted to them. Since they
107 // may not have inner windows, they would require special handling below, so
108 // just bail out early here.
109 if (!aDocument || aDocument->IsCookieAverse()) {
110 return nullptr;
113 // For controlled documents, we cast the pointer into a void* to avoid
114 // dereferencing it (since we only use it for comparisons).
115 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
116 if (swm && aDocument->GetController().isSome()) {
117 return aDocument;
120 return nullptr;
123 /* static */
124 nsCString ImageCacheKey::GetIsolationKey(Document* aDocument, nsIURI* aURI) {
125 if (!aDocument || !aDocument->GetInnerWindow()) {
126 return ""_ns;
129 // Network-state isolation
130 if (StaticPrefs::privacy_partition_network_state()) {
131 OriginAttributes oa;
132 StoragePrincipalHelper::GetOriginAttributesForNetworkState(aDocument, oa);
134 nsAutoCString suffix;
135 oa.CreateSuffix(suffix);
137 return std::move(suffix);
140 // If the window is 3rd party resource, let's see if first-party storage
141 // access is granted for this image.
142 if (AntiTrackingUtils::IsThirdPartyWindow(aDocument->GetInnerWindow(),
143 nullptr)) {
144 uint32_t rejectedReason = 0;
145 Unused << rejectedReason;
146 return StorageDisabledByAntiTracking(aDocument, aURI, rejectedReason)
147 ? aDocument->GetBaseDomain()
148 : ""_ns;
151 // Another scenario is if this image is a 3rd party resource loaded by a
152 // first party context. In this case, we should check if the nsIChannel has
153 // been marked as tracking resource, but we don't have the channel yet at
154 // this point. The best approach here is to be conservative: if we are sure
155 // that the permission is granted, let's return 0. Otherwise, let's make a
156 // unique image cache per the top-level document eTLD+1.
157 if (!ApproximateAllowAccessForWithoutChannel(aDocument->GetInnerWindow(),
158 aURI)) {
159 // If we are here, the image is a 3rd-party resource loaded by a first-party
160 // context. We can just use the document's base domain as the key because it
161 // should be the same as the top-level document's base domain.
162 return aDocument
163 ->GetBaseDomain(); // because we don't have anything better!
166 return ""_ns;
169 } // namespace image
170 } // namespace mozilla