no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / cache / CacheOpParent.cpp
blob6c88864c94b8cbe8e98d5e5f814ad43506766cee
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/cache/CacheOpParent.h"
9 #include "mozilla/ErrorResult.h"
10 #include "mozilla/StaticPrefs_browser.h"
11 #include "mozilla/Unused.h"
12 #include "mozilla/dom/cache/AutoUtils.h"
13 #include "mozilla/dom/cache/ManagerId.h"
14 #include "mozilla/dom/cache/ReadStream.h"
15 #include "mozilla/dom/cache/SavedTypes.h"
16 #include "mozilla/ipc/InputStreamUtils.h"
17 #include "mozilla/ipc/IPCStreamUtils.h"
19 namespace mozilla::dom::cache {
21 using mozilla::ipc::PBackgroundParent;
23 CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager, CacheId aCacheId,
24 const CacheOpArgs& aOpArgs)
25 : mIpcManager(aIpcManager),
26 mCacheId(aCacheId),
27 mNamespace(INVALID_NAMESPACE),
28 mOpArgs(aOpArgs) {
29 MOZ_DIAGNOSTIC_ASSERT(mIpcManager);
32 CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager,
33 Namespace aNamespace, const CacheOpArgs& aOpArgs)
34 : mIpcManager(aIpcManager),
35 mCacheId(INVALID_CACHE_ID),
36 mNamespace(aNamespace),
37 mOpArgs(aOpArgs) {
38 MOZ_DIAGNOSTIC_ASSERT(mIpcManager);
41 CacheOpParent::~CacheOpParent() { NS_ASSERT_OWNINGTHREAD(CacheOpParent); }
43 void CacheOpParent::Execute(const SafeRefPtr<ManagerId>& aManagerId) {
44 NS_ASSERT_OWNINGTHREAD(CacheOpParent);
45 MOZ_DIAGNOSTIC_ASSERT(!mManager);
46 MOZ_DIAGNOSTIC_ASSERT(!mVerifier);
48 auto managerOrErr = cache::Manager::AcquireCreateIfNonExistent(aManagerId);
49 if (NS_WARN_IF(managerOrErr.isErr())) {
50 (void)Send__delete__(this, CopyableErrorResult(managerOrErr.unwrapErr()),
51 void_t());
52 return;
55 Execute(managerOrErr.unwrap());
58 void CacheOpParent::Execute(SafeRefPtr<cache::Manager> aManager) {
59 NS_ASSERT_OWNINGTHREAD(CacheOpParent);
60 MOZ_DIAGNOSTIC_ASSERT(!mManager);
61 MOZ_DIAGNOSTIC_ASSERT(!mVerifier);
63 mManager = std::move(aManager);
65 // Handle put op
66 if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
67 MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
69 const CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
70 const nsTArray<CacheRequestResponse>& list = args.requestResponseList();
72 AutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
73 AutoTArray<nsCOMPtr<nsIInputStream>, 256> responseStreamList;
75 for (uint32_t i = 0; i < list.Length(); ++i) {
76 requestStreamList.AppendElement(
77 DeserializeCacheStream(list[i].request().body()));
78 responseStreamList.AppendElement(
79 DeserializeCacheStream(list[i].response().body()));
82 mManager->ExecutePutAll(this, mCacheId, args.requestResponseList(),
83 requestStreamList, responseStreamList);
84 return;
87 // Handle all other cache ops
88 if (mCacheId != INVALID_CACHE_ID) {
89 MOZ_DIAGNOSTIC_ASSERT(mNamespace == INVALID_NAMESPACE);
90 mManager->ExecuteCacheOp(this, mCacheId, mOpArgs);
91 return;
94 // Handle all storage ops
95 MOZ_DIAGNOSTIC_ASSERT(mNamespace != INVALID_NAMESPACE);
96 mManager->ExecuteStorageOp(this, mNamespace, mOpArgs);
99 void CacheOpParent::WaitForVerification(PrincipalVerifier* aVerifier) {
100 NS_ASSERT_OWNINGTHREAD(CacheOpParent);
101 MOZ_DIAGNOSTIC_ASSERT(!mManager);
102 MOZ_DIAGNOSTIC_ASSERT(!mVerifier);
104 mVerifier = aVerifier;
105 mVerifier->AddListener(*this);
108 void CacheOpParent::ActorDestroy(ActorDestroyReason aReason) {
109 NS_ASSERT_OWNINGTHREAD(CacheOpParent);
111 if (mVerifier) {
112 mVerifier->RemoveListener(*this);
113 mVerifier = nullptr;
116 if (mManager) {
117 mManager->RemoveListener(this);
118 mManager = nullptr;
121 mIpcManager = nullptr;
124 void CacheOpParent::OnPrincipalVerified(
125 nsresult aRv, const SafeRefPtr<ManagerId>& aManagerId) {
126 NS_ASSERT_OWNINGTHREAD(CacheOpParent);
128 mVerifier->RemoveListener(*this);
129 mVerifier = nullptr;
131 if (NS_WARN_IF(NS_FAILED(aRv))) {
132 (void)Send__delete__(this, CopyableErrorResult(aRv), void_t());
133 return;
136 Execute(aManagerId);
139 void CacheOpParent::OnOpComplete(ErrorResult&& aRv,
140 const CacheOpResult& aResult,
141 CacheId aOpenedCacheId,
142 const Maybe<StreamInfo>& aStreamInfo) {
143 NS_ASSERT_OWNINGTHREAD(CacheOpParent);
144 MOZ_DIAGNOSTIC_ASSERT(mIpcManager);
145 MOZ_DIAGNOSTIC_ASSERT(mManager);
147 // Never send an op-specific result if we have an error. Instead, send
148 // void_t() to ensure that we don't leak actors on the child side.
149 if (NS_WARN_IF(aRv.Failed())) {
150 (void)Send__delete__(this, CopyableErrorResult(std::move(aRv)), void_t());
151 return;
154 if (aStreamInfo.isSome()) {
155 ProcessCrossOriginResourcePolicyHeader(aRv,
156 aStreamInfo->mSavedResponseList);
157 if (NS_WARN_IF(aRv.Failed())) {
158 (void)Send__delete__(this, CopyableErrorResult(std::move(aRv)), void_t());
159 return;
163 uint32_t entryCount =
164 std::max(1lu, aStreamInfo ? static_cast<unsigned long>(std::max(
165 aStreamInfo->mSavedResponseList.Length(),
166 aStreamInfo->mSavedRequestList.Length()))
167 : 0lu);
169 // The result must contain the appropriate type at this point. It may
170 // or may not contain the additional result data yet. For types that
171 // do not need special processing, it should already be set. If the
172 // result requires actor-specific operations, then we do that below.
173 // If the type and data types don't match, then we will trigger an
174 // assertion in AutoParentOpResult::Add().
175 AutoParentOpResult result(mIpcManager, aResult, entryCount);
177 if (aOpenedCacheId != INVALID_CACHE_ID) {
178 result.Add(aOpenedCacheId, mManager.clonePtr());
181 if (aStreamInfo) {
182 const auto& streamInfo = *aStreamInfo;
184 for (const auto& savedResponse : streamInfo.mSavedResponseList) {
185 result.Add(savedResponse, streamInfo.mStreamList);
188 for (const auto& savedRequest : streamInfo.mSavedRequestList) {
189 result.Add(savedRequest, streamInfo.mStreamList);
193 (void)Send__delete__(this, CopyableErrorResult(std::move(aRv)),
194 result.SendAsOpResult());
197 already_AddRefed<nsIInputStream> CacheOpParent::DeserializeCacheStream(
198 const Maybe<CacheReadStream>& aMaybeStream) {
199 if (aMaybeStream.isNothing()) {
200 return nullptr;
203 nsCOMPtr<nsIInputStream> stream;
204 const CacheReadStream& readStream = aMaybeStream.ref();
206 // Option 1: One of our own ReadStreams was passed back to us with a stream
207 // control actor.
208 stream = ReadStream::Create(readStream);
209 if (stream) {
210 return stream.forget();
213 // Option 2: A stream was serialized using normal methods or passed
214 // as a DataPipe. Use the standard method for extracting the
215 // resulting stream.
216 return DeserializeIPCStream(readStream.stream());
219 void CacheOpParent::ProcessCrossOriginResourcePolicyHeader(
220 ErrorResult& aRv, const nsTArray<SavedResponse>& aResponses) {
221 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
222 return;
224 // Only checking for match/matchAll.
225 nsILoadInfo::CrossOriginEmbedderPolicy loadingCOEP =
226 nsILoadInfo::EMBEDDER_POLICY_NULL;
227 Maybe<mozilla::ipc::PrincipalInfo> principalInfo;
228 switch (mOpArgs.type()) {
229 case CacheOpArgs::TCacheMatchArgs: {
230 const auto& request = mOpArgs.get_CacheMatchArgs().request();
231 loadingCOEP = request.loadingEmbedderPolicy();
232 principalInfo = request.principalInfo();
233 break;
235 case CacheOpArgs::TCacheMatchAllArgs: {
236 if (mOpArgs.get_CacheMatchAllArgs().maybeRequest().isSome()) {
237 const auto& request =
238 mOpArgs.get_CacheMatchAllArgs().maybeRequest().ref();
239 loadingCOEP = request.loadingEmbedderPolicy();
240 principalInfo = request.principalInfo();
242 break;
244 default: {
245 return;
249 // skip checking if the request has no principal for same-origin/same-site
250 // checking.
251 if (principalInfo.isNothing() ||
252 principalInfo.ref().type() !=
253 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
254 return;
256 const mozilla::ipc::ContentPrincipalInfo& contentPrincipalInfo =
257 principalInfo.ref().get_ContentPrincipalInfo();
259 for (auto it = aResponses.cbegin(); it != aResponses.cend(); ++it) {
260 if (it->mValue.type() != ResponseType::Opaque &&
261 it->mValue.type() != ResponseType::Opaqueredirect) {
262 continue;
265 const auto& headers = it->mValue.headers();
266 const RequestCredentials credentials = it->mValue.credentials();
267 const auto corpHeaderIt =
268 std::find_if(headers.cbegin(), headers.cend(), [](const auto& header) {
269 return header.name().EqualsLiteral("Cross-Origin-Resource-Policy");
272 // According to https://github.com/w3c/ServiceWorker/issues/1490, the cache
273 // response is expected with CORP header, otherwise, throw the type error.
274 // Note that this is different with the CORP checking for fetch metioned in
275 // https://wicg.github.io/cross-origin-embedder-policy/#corp-check.
276 // For fetch, if the response has no CORP header, "same-origin" checking
277 // will be performed.
278 if (corpHeaderIt == headers.cend() &&
279 loadingCOEP == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
280 aRv.ThrowTypeError("Response is expected with CORP header.");
281 return;
284 // Skip the case if the response has no principal for same-origin/same-site
285 // checking.
286 if (it->mValue.principalInfo().isNothing() ||
287 it->mValue.principalInfo().ref().type() !=
288 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
289 continue;
292 const mozilla::ipc::ContentPrincipalInfo& responseContentPrincipalInfo =
293 it->mValue.principalInfo().ref().get_ContentPrincipalInfo();
295 nsCString corp =
296 corpHeaderIt == headers.cend() ? EmptyCString() : corpHeaderIt->value();
298 if (corp.IsEmpty()) {
299 if (loadingCOEP == nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) {
300 // This means the request of this request doesn't have
301 // credentials, so it's safe for us to return.
302 if (credentials == RequestCredentials::Omit) {
303 return;
305 corp = "same-origin";
309 if (corp.EqualsLiteral("same-origin")) {
310 if (responseContentPrincipalInfo != contentPrincipalInfo) {
311 aRv.ThrowTypeError("Response is expected from same origin.");
312 return;
314 } else if (corp.EqualsLiteral("same-site")) {
315 if (!responseContentPrincipalInfo.baseDomain().Equals(
316 contentPrincipalInfo.baseDomain())) {
317 aRv.ThrowTypeError("Response is expected from same site.");
318 return;
324 } // namespace mozilla::dom::cache