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/StaticPrefs_browser.h"
10 #include "mozilla/Unused.h"
11 #include "mozilla/dom/cache/AutoUtils.h"
12 #include "mozilla/dom/cache/ManagerId.h"
13 #include "mozilla/dom/cache/ReadStream.h"
14 #include "mozilla/dom/cache/SavedTypes.h"
15 #include "mozilla/ipc/FileDescriptorSetParent.h"
16 #include "mozilla/ipc/InputStreamUtils.h"
17 #include "mozilla/ipc/IPCStreamUtils.h"
19 namespace mozilla::dom::cache
{
21 using mozilla::ipc::FileDescriptorSetParent
;
22 using mozilla::ipc::PBackgroundParent
;
24 CacheOpParent::CacheOpParent(PBackgroundParent
* aIpcManager
, CacheId aCacheId
,
25 const CacheOpArgs
& aOpArgs
)
26 : mIpcManager(aIpcManager
),
28 mNamespace(INVALID_NAMESPACE
),
30 MOZ_DIAGNOSTIC_ASSERT(mIpcManager
);
33 CacheOpParent::CacheOpParent(PBackgroundParent
* aIpcManager
,
34 Namespace aNamespace
, const CacheOpArgs
& aOpArgs
)
35 : mIpcManager(aIpcManager
),
36 mCacheId(INVALID_CACHE_ID
),
37 mNamespace(aNamespace
),
39 MOZ_DIAGNOSTIC_ASSERT(mIpcManager
);
42 CacheOpParent::~CacheOpParent() { NS_ASSERT_OWNINGTHREAD(CacheOpParent
); }
44 void CacheOpParent::Execute(const SafeRefPtr
<ManagerId
>& aManagerId
) {
45 NS_ASSERT_OWNINGTHREAD(CacheOpParent
);
46 MOZ_DIAGNOSTIC_ASSERT(!mManager
);
47 MOZ_DIAGNOSTIC_ASSERT(!mVerifier
);
49 auto managerOrErr
= cache::Manager::AcquireCreateIfNonExistent(aManagerId
);
50 if (NS_WARN_IF(managerOrErr
.isErr())) {
51 ErrorResult
result(managerOrErr
.unwrapErr());
52 Unused
<< Send__delete__(this, std::move(result
), void_t());
56 Execute(managerOrErr
.unwrap());
59 void CacheOpParent::Execute(SafeRefPtr
<cache::Manager
> aManager
) {
60 NS_ASSERT_OWNINGTHREAD(CacheOpParent
);
61 MOZ_DIAGNOSTIC_ASSERT(!mManager
);
62 MOZ_DIAGNOSTIC_ASSERT(!mVerifier
);
64 mManager
= std::move(aManager
);
67 if (mOpArgs
.type() == CacheOpArgs::TCachePutAllArgs
) {
68 MOZ_DIAGNOSTIC_ASSERT(mCacheId
!= INVALID_CACHE_ID
);
70 const CachePutAllArgs
& args
= mOpArgs
.get_CachePutAllArgs();
71 const nsTArray
<CacheRequestResponse
>& list
= args
.requestResponseList();
73 AutoTArray
<nsCOMPtr
<nsIInputStream
>, 256> requestStreamList
;
74 AutoTArray
<nsCOMPtr
<nsIInputStream
>, 256> responseStreamList
;
76 for (uint32_t i
= 0; i
< list
.Length(); ++i
) {
77 requestStreamList
.AppendElement(
78 DeserializeCacheStream(list
[i
].request().body()));
79 responseStreamList
.AppendElement(
80 DeserializeCacheStream(list
[i
].response().body()));
83 mManager
->ExecutePutAll(this, mCacheId
, args
.requestResponseList(),
84 requestStreamList
, responseStreamList
);
88 // Handle all other cache ops
89 if (mCacheId
!= INVALID_CACHE_ID
) {
90 MOZ_DIAGNOSTIC_ASSERT(mNamespace
== INVALID_NAMESPACE
);
91 mManager
->ExecuteCacheOp(this, mCacheId
, mOpArgs
);
95 // Handle all storage ops
96 MOZ_DIAGNOSTIC_ASSERT(mNamespace
!= INVALID_NAMESPACE
);
97 mManager
->ExecuteStorageOp(this, mNamespace
, mOpArgs
);
100 void CacheOpParent::WaitForVerification(PrincipalVerifier
* aVerifier
) {
101 NS_ASSERT_OWNINGTHREAD(CacheOpParent
);
102 MOZ_DIAGNOSTIC_ASSERT(!mManager
);
103 MOZ_DIAGNOSTIC_ASSERT(!mVerifier
);
105 mVerifier
= aVerifier
;
106 mVerifier
->AddListener(*this);
109 void CacheOpParent::ActorDestroy(ActorDestroyReason aReason
) {
110 NS_ASSERT_OWNINGTHREAD(CacheOpParent
);
113 mVerifier
->RemoveListener(*this);
118 mManager
->RemoveListener(this);
122 mIpcManager
= nullptr;
125 void CacheOpParent::OnPrincipalVerified(
126 nsresult aRv
, const SafeRefPtr
<ManagerId
>& aManagerId
) {
127 NS_ASSERT_OWNINGTHREAD(CacheOpParent
);
129 mVerifier
->RemoveListener(*this);
132 if (NS_WARN_IF(NS_FAILED(aRv
))) {
133 ErrorResult
result(aRv
);
134 Unused
<< Send__delete__(this, std::move(result
), void_t());
141 void CacheOpParent::OnOpComplete(ErrorResult
&& aRv
,
142 const CacheOpResult
& aResult
,
143 CacheId aOpenedCacheId
,
144 const Maybe
<StreamInfo
>& aStreamInfo
) {
145 NS_ASSERT_OWNINGTHREAD(CacheOpParent
);
146 MOZ_DIAGNOSTIC_ASSERT(mIpcManager
);
147 MOZ_DIAGNOSTIC_ASSERT(mManager
);
149 // Never send an op-specific result if we have an error. Instead, send
150 // void_t() to ensure that we don't leak actors on the child side.
151 if (NS_WARN_IF(aRv
.Failed())) {
152 Unused
<< Send__delete__(this, std::move(aRv
), void_t());
156 if (aStreamInfo
.isSome()) {
157 ProcessCrossOriginResourcePolicyHeader(aRv
,
158 aStreamInfo
->mSavedResponseList
);
159 if (NS_WARN_IF(aRv
.Failed())) {
160 Unused
<< Send__delete__(this, std::move(aRv
), void_t());
165 uint32_t entryCount
=
166 std::max(1lu, aStreamInfo
? static_cast<unsigned long>(std::max(
167 aStreamInfo
->mSavedResponseList
.Length(),
168 aStreamInfo
->mSavedRequestList
.Length()))
171 // The result must contain the appropriate type at this point. It may
172 // or may not contain the additional result data yet. For types that
173 // do not need special processing, it should already be set. If the
174 // result requires actor-specific operations, then we do that below.
175 // If the type and data types don't match, then we will trigger an
176 // assertion in AutoParentOpResult::Add().
177 AutoParentOpResult
result(mIpcManager
, aResult
, entryCount
);
179 if (aOpenedCacheId
!= INVALID_CACHE_ID
) {
180 result
.Add(aOpenedCacheId
, mManager
.clonePtr());
184 const auto& streamInfo
= *aStreamInfo
;
186 for (const auto& savedResponse
: streamInfo
.mSavedResponseList
) {
187 result
.Add(savedResponse
, streamInfo
.mStreamList
);
190 for (const auto& savedRequest
: streamInfo
.mSavedRequestList
) {
191 result
.Add(savedRequest
, streamInfo
.mStreamList
);
195 Unused
<< Send__delete__(this, std::move(aRv
), result
.SendAsOpResult());
198 already_AddRefed
<nsIInputStream
> CacheOpParent::DeserializeCacheStream(
199 const Maybe
<CacheReadStream
>& aMaybeStream
) {
200 if (aMaybeStream
.isNothing()) {
204 nsCOMPtr
<nsIInputStream
> stream
;
205 const CacheReadStream
& readStream
= aMaybeStream
.ref();
207 // Option 1: One of our own ReadStreams was passed back to us with a stream
209 stream
= ReadStream::Create(readStream
);
211 return stream
.forget();
214 // Option 2: A stream was serialized using normal methods or passed
215 // as a PChildToParentStream actor. Use the standard method for
216 // extracting the resulting stream.
217 return DeserializeIPCStream(readStream
.stream());
220 void CacheOpParent::ProcessCrossOriginResourcePolicyHeader(
221 ErrorResult
& aRv
, const nsTArray
<SavedResponse
>& aResponses
) {
222 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
225 // Only checking for match/matchAll.
226 nsILoadInfo::CrossOriginEmbedderPolicy loadingCOEP
=
227 nsILoadInfo::EMBEDDER_POLICY_NULL
;
228 Maybe
<PrincipalInfo
> principalInfo
;
229 switch (mOpArgs
.type()) {
230 case CacheOpArgs::TCacheMatchArgs
: {
232 mOpArgs
.get_CacheMatchArgs().request().loadingEmbedderPolicy();
233 principalInfo
= mOpArgs
.get_CacheMatchArgs().request().principalInfo();
236 case CacheOpArgs::TCacheMatchAllArgs
: {
237 if (mOpArgs
.get_CacheMatchAllArgs().maybeRequest().isSome()) {
238 loadingCOEP
= mOpArgs
.get_CacheMatchAllArgs()
241 .loadingEmbedderPolicy();
242 principalInfo
= mOpArgs
.get_CacheMatchAllArgs()
254 // skip checking if the request has no principal for same-origin/same-site
256 if (principalInfo
.isNothing() ||
257 principalInfo
.ref().type() != PrincipalInfo::TContentPrincipalInfo
) {
260 const ContentPrincipalInfo
& contentPrincipalInfo
=
261 principalInfo
.ref().get_ContentPrincipalInfo();
263 for (auto it
= aResponses
.cbegin(); it
!= aResponses
.cend(); ++it
) {
264 if (it
->mValue
.type() != ResponseType::Opaque
&&
265 it
->mValue
.type() != ResponseType::Opaqueredirect
) {
269 const auto& headers
= it
->mValue
.headers();
270 const auto corpHeaderIt
=
271 std::find_if(headers
.cbegin(), headers
.cend(), [](const auto& header
) {
272 return header
.name().EqualsLiteral("Cross-Origin-Resource-Policy");
275 // According to https://github.com/w3c/ServiceWorker/issues/1490, the cache
276 // response is expected with CORP header, otherwise, throw the type error.
277 // Note that this is different with the CORP checking for fetch metioned in
278 // https://wicg.github.io/cross-origin-embedder-policy/#corp-check.
279 // For fetch, if the response has no CORP header, "same-origin" checking
280 // will be performed.
281 if (corpHeaderIt
== headers
.cend() &&
282 loadingCOEP
== nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP
) {
283 aRv
.ThrowTypeError("Response is expected with CORP header.");
287 // Skip the case if the response has no principal for same-origin/same-site
289 if (it
->mValue
.principalInfo().isNothing() ||
290 it
->mValue
.principalInfo().ref().type() !=
291 PrincipalInfo::TContentPrincipalInfo
) {
295 const ContentPrincipalInfo
& responseContentPrincipalInfo
=
296 it
->mValue
.principalInfo().ref().get_ContentPrincipalInfo();
299 corpHeaderIt
== headers
.cend() ? EmptyCString() : corpHeaderIt
->value();
301 if (corp
.EqualsLiteral("same-origin")) {
302 if (responseContentPrincipalInfo
== contentPrincipalInfo
) {
303 aRv
.ThrowTypeError("Response is expected from same origin.");
306 } else if (corp
.EqualsLiteral("same-site")) {
307 if (!responseContentPrincipalInfo
.baseDomain().Equals(
308 contentPrincipalInfo
.baseDomain())) {
309 aRv
.ThrowTypeError("Response is expected from same site.");
316 } // namespace mozilla::dom::cache