Bug 1577199 test speaker-selection permissions policy on selectAudioOutput() r=jib
[gecko.git] / image / ImageCacheKey.cpp
blob7ded0b771b9c6c9179a0ec191588a6e6fe465ad5
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/ContentBlocking.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 "nsContentUtils.h"
20 #include "nsHashKeys.h"
21 #include "nsLayoutUtils.h"
22 #include "nsPrintfCString.h"
23 #include "nsString.h"
25 namespace mozilla {
27 using namespace dom;
29 namespace image {
31 ImageCacheKey::ImageCacheKey(nsIURI* aURI, const OriginAttributes& aAttrs,
32 Document* aDocument)
33 : mURI(aURI),
34 mOriginAttributes(aAttrs),
35 mControlledDocument(GetSpecialCaseDocumentToken(aDocument)),
36 mIsolationKey(GetIsolationKey(aDocument, aURI)),
37 mIsChrome(false) {
38 if (mURI->SchemeIs("chrome")) {
39 mIsChrome = true;
43 ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
44 : mURI(aOther.mURI),
45 mOriginAttributes(aOther.mOriginAttributes),
46 mControlledDocument(aOther.mControlledDocument),
47 mIsolationKey(aOther.mIsolationKey),
48 mHash(aOther.mHash),
49 mIsChrome(aOther.mIsChrome) {}
51 ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
52 : mURI(std::move(aOther.mURI)),
53 mOriginAttributes(aOther.mOriginAttributes),
54 mControlledDocument(aOther.mControlledDocument),
55 mIsolationKey(aOther.mIsolationKey),
56 mHash(aOther.mHash),
57 mIsChrome(aOther.mIsChrome) {}
59 bool ImageCacheKey::operator==(const ImageCacheKey& aOther) const {
60 // Don't share the image cache between a controlled document and anything
61 // else.
62 if (mControlledDocument != aOther.mControlledDocument) {
63 return false;
65 // Don't share the image cache between two top-level documents of different
66 // base domains.
67 if (!mIsolationKey.Equals(aOther.mIsolationKey,
68 nsCaseInsensitiveCStringComparator)) {
69 return false;
71 // The origin attributes always have to match.
72 if (mOriginAttributes != aOther.mOriginAttributes) {
73 return false;
76 // For non-blob URIs, compare the URIs.
77 bool equals = false;
78 nsresult rv = mURI->Equals(aOther.mURI, &equals);
79 return NS_SUCCEEDED(rv) && equals;
82 void ImageCacheKey::EnsureHash() const {
83 MOZ_ASSERT(mHash.isNothing());
84 PLDHashNumber hash = 0;
86 // Since we frequently call Hash() several times in a row on the same
87 // ImageCacheKey, as an optimization we compute our hash once and store it.
89 nsPrintfCString ptr("%p", mControlledDocument);
90 nsAutoCString suffix;
91 mOriginAttributes.CreateSuffix(suffix);
93 nsAutoCString spec;
94 Unused << mURI->GetSpec(spec);
95 hash = HashString(spec);
97 hash = AddToHash(hash, HashString(suffix), HashString(mIsolationKey),
98 HashString(ptr));
99 mHash.emplace(hash);
102 /* static */
103 void* ImageCacheKey::GetSpecialCaseDocumentToken(Document* aDocument) {
104 // Cookie-averse documents can never have storage granted to them. Since they
105 // may not have inner windows, they would require special handling below, so
106 // just bail out early here.
107 if (!aDocument || aDocument->IsCookieAverse()) {
108 return nullptr;
111 // For controlled documents, we cast the pointer into a void* to avoid
112 // dereferencing it (since we only use it for comparisons).
113 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
114 if (swm && aDocument->GetController().isSome()) {
115 return aDocument;
118 return nullptr;
121 /* static */
122 nsCString ImageCacheKey::GetIsolationKey(Document* aDocument, nsIURI* aURI) {
123 if (!aDocument || !aDocument->GetInnerWindow()) {
124 return ""_ns;
127 // Network-state isolation
128 if (StaticPrefs::privacy_partition_network_state()) {
129 OriginAttributes oa;
130 StoragePrincipalHelper::GetOriginAttributesForNetworkState(aDocument, oa);
132 nsAutoCString suffix;
133 oa.CreateSuffix(suffix);
135 return std::move(suffix);
138 // If the window is 3rd party resource, let's see if first-party storage
139 // access is granted for this image.
140 if (nsContentUtils::IsThirdPartyWindowOrChannel(aDocument->GetInnerWindow(),
141 nullptr, nullptr)) {
142 uint32_t rejectedReason = 0;
143 Unused << rejectedReason;
144 return StorageDisabledByAntiTracking(aDocument, aURI, rejectedReason)
145 ? aDocument->GetBaseDomain()
146 : ""_ns;
149 // Another scenario is if this image is a 3rd party resource loaded by a
150 // first party context. In this case, we should check if the nsIChannel has
151 // been marked as tracking resource, but we don't have the channel yet at
152 // this point. The best approach here is to be conservative: if we are sure
153 // that the permission is granted, let's return 0. Otherwise, let's make a
154 // unique image cache per the top-level document eTLD+1.
155 if (!ContentBlocking::ApproximateAllowAccessForWithoutChannel(
156 aDocument->GetInnerWindow(), aURI)) {
157 // If we are here, the image is a 3rd-party resource loaded by a first-party
158 // context. We can just use the document's base domain as the key because it
159 // should be the same as the top-level document's base domain.
160 return aDocument
161 ->GetBaseDomain(); // because we don't have anything better!
164 return ""_ns;
167 } // namespace image
168 } // namespace mozilla