Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / cache / TypeUtils.cpp
blob818b1cf93ba3c660cab4d29c27b1cb6c36e2fd22
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/TypeUtils.h"
9 #include <algorithm>
10 #include "mozilla/StaticPrefs_extensions.h"
11 #include "mozilla/Unused.h"
12 #include "mozilla/dom/CacheBinding.h"
13 #include "mozilla/dom/CacheStorageBinding.h"
14 #include "mozilla/dom/FetchTypes.h"
15 #include "mozilla/dom/InternalRequest.h"
16 #include "mozilla/dom/Request.h"
17 #include "mozilla/dom/Response.h"
18 #include "mozilla/dom/RootedDictionary.h"
19 #include "mozilla/dom/cache/CacheTypes.h"
20 #include "mozilla/dom/cache/ReadStream.h"
21 #include "mozilla/ipc/BackgroundChild.h"
22 #include "mozilla/ipc/IPCStreamUtils.h"
23 #include "mozilla/ipc/PBackgroundChild.h"
24 #include "mozilla/ipc/InputStreamUtils.h"
25 #include "nsCharSeparatedTokenizer.h"
26 #include "nsCOMPtr.h"
27 #include "nsHttp.h"
28 #include "nsIIPCSerializableInputStream.h"
29 #include "nsPromiseFlatString.h"
30 #include "nsQueryObject.h"
31 #include "nsStreamUtils.h"
32 #include "nsString.h"
33 #include "nsURLParsers.h"
35 namespace mozilla::dom::cache {
37 using mozilla::ipc::BackgroundChild;
38 using mozilla::ipc::FileDescriptor;
39 using mozilla::ipc::PBackgroundChild;
41 namespace {
43 static bool HasVaryStar(mozilla::dom::InternalHeaders* aHeaders) {
44 nsCString varyHeaders;
45 ErrorResult rv;
46 aHeaders->Get("vary"_ns, varyHeaders, rv);
47 MOZ_ALWAYS_TRUE(!rv.Failed());
49 for (const nsACString& header :
50 nsCCharSeparatedTokenizer(varyHeaders, NS_HTTP_HEADER_SEP).ToRange()) {
51 if (header.EqualsLiteral("*")) {
52 return true;
55 return false;
58 nsTArray<HeadersEntry> ToHeadersEntryList(InternalHeaders* aHeaders) {
59 MOZ_DIAGNOSTIC_ASSERT(aHeaders);
61 AutoTArray<InternalHeaders::Entry, 16> entryList;
62 aHeaders->GetEntries(entryList);
64 nsTArray<HeadersEntry> result;
65 result.SetCapacity(entryList.Length());
66 std::transform(entryList.cbegin(), entryList.cend(), MakeBackInserter(result),
67 [](const auto& entry) {
68 return HeadersEntry(entry.mName, entry.mValue);
69 });
71 return result;
74 } // namespace
76 SafeRefPtr<InternalRequest> TypeUtils::ToInternalRequest(
77 JSContext* aCx, const RequestOrUTF8String& aIn, BodyAction aBodyAction,
78 ErrorResult& aRv) {
79 if (aIn.IsRequest()) {
80 Request& request = aIn.GetAsRequest();
82 // Check and set bodyUsed flag immediately because its on Request
83 // instead of InternalRequest.
84 CheckAndSetBodyUsed(aCx, request, aBodyAction, aRv);
85 if (aRv.Failed()) {
86 return nullptr;
89 return request.GetInternalRequest();
92 return ToInternalRequest(aIn.GetAsUTF8String(), aRv);
95 SafeRefPtr<InternalRequest> TypeUtils::ToInternalRequest(
96 JSContext* aCx, const OwningRequestOrUTF8String& aIn,
97 BodyAction aBodyAction, ErrorResult& aRv) {
98 if (aIn.IsRequest()) {
99 Request& request = aIn.GetAsRequest();
101 // Check and set bodyUsed flag immediately because its on Request
102 // instead of InternalRequest.
103 CheckAndSetBodyUsed(aCx, request, aBodyAction, aRv);
104 if (aRv.Failed()) {
105 return nullptr;
108 return request.GetInternalRequest();
111 return ToInternalRequest(aIn.GetAsUTF8String(), aRv);
114 void TypeUtils::ToCacheRequest(CacheRequest& aOut, const InternalRequest& aIn,
115 BodyAction aBodyAction,
116 SchemeAction aSchemeAction, ErrorResult& aRv) {
117 aIn.GetMethod(aOut.method());
118 nsCString url(aIn.GetURLWithoutFragment());
119 bool schemeValid;
120 ProcessURL(url, &schemeValid, &aOut.urlWithoutQuery(), &aOut.urlQuery(), aRv);
121 if (aRv.Failed()) {
122 return;
124 if (!schemeValid) {
125 if (aSchemeAction == TypeErrorOnInvalidScheme) {
126 aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Request", url);
127 return;
130 aOut.urlFragment() = aIn.GetFragment();
132 aIn.GetReferrer(aOut.referrer());
133 aOut.referrerPolicy() = aIn.ReferrerPolicy_();
134 RefPtr<InternalHeaders> headers = aIn.Headers();
135 MOZ_DIAGNOSTIC_ASSERT(headers);
136 aOut.headers() = ToHeadersEntryList(headers);
137 aOut.headersGuard() = headers->Guard();
138 aOut.mode() = aIn.Mode();
139 aOut.credentials() = aIn.GetCredentialsMode();
140 aOut.contentPolicyType() = aIn.ContentPolicyType();
141 aOut.requestCache() = aIn.GetCacheMode();
142 aOut.requestRedirect() = aIn.GetRedirectMode();
144 aOut.integrity() = aIn.GetIntegrity();
145 aOut.loadingEmbedderPolicy() = aIn.GetEmbedderPolicy();
146 const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
147 aIn.GetPrincipalInfo();
148 if (principalInfo) {
149 aOut.principalInfo() = Some(*(principalInfo.get()));
152 if (aBodyAction == IgnoreBody) {
153 aOut.body() = Nothing();
154 return;
157 // BodyUsed flag is checked and set previously in ToInternalRequest()
159 nsCOMPtr<nsIInputStream> stream;
160 aIn.GetBody(getter_AddRefs(stream));
161 SerializeCacheStream(stream, &aOut.body(), aRv);
162 if (NS_WARN_IF(aRv.Failed())) {
163 return;
167 void TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
168 InternalResponse& aIn,
169 ErrorResult& aRv) {
170 aOut.type() = aIn.Type();
172 aIn.GetUnfilteredURLList(aOut.urlList());
173 AutoTArray<nsCString, 4> urlList;
174 aIn.GetURLList(urlList);
176 for (uint32_t i = 0; i < aOut.urlList().Length(); i++) {
177 MOZ_DIAGNOSTIC_ASSERT(!aOut.urlList()[i].IsEmpty());
178 // Pass all Response URL schemes through... The spec only requires we take
179 // action on invalid schemes for Request objects.
180 ProcessURL(aOut.urlList()[i], nullptr, nullptr, nullptr, aRv);
183 aOut.status() = aIn.GetUnfilteredStatus();
184 aOut.statusText() = aIn.GetUnfilteredStatusText();
185 RefPtr<InternalHeaders> headers = aIn.UnfilteredHeaders();
186 MOZ_DIAGNOSTIC_ASSERT(headers);
187 if (aIn.Type() != ResponseType::Opaque && HasVaryStar(headers)) {
188 aRv.ThrowTypeError("Invalid Response object with a 'Vary: *' header.");
189 return;
191 aOut.headers() = ToHeadersEntryList(headers);
192 aOut.headersGuard() = headers->Guard();
193 aOut.securityInfo() = aIn.GetChannelInfo().SecurityInfo();
194 if (aIn.GetPrincipalInfo()) {
195 aOut.principalInfo() = Some(*aIn.GetPrincipalInfo());
196 } else {
197 aOut.principalInfo() = Nothing();
200 aOut.paddingInfo() = aIn.GetPaddingInfo();
201 aOut.paddingSize() = aIn.GetPaddingSize();
204 void TypeUtils::ToCacheResponse(JSContext* aCx, CacheResponse& aOut,
205 Response& aIn, ErrorResult& aRv) {
206 if (aIn.BodyUsed()) {
207 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
208 return;
211 SafeRefPtr<InternalResponse> ir = aIn.GetInternalResponse();
212 ToCacheResponseWithoutBody(aOut, *ir, aRv);
213 if (NS_WARN_IF(aRv.Failed())) {
214 return;
217 nsCOMPtr<nsIInputStream> stream;
218 ir->GetUnfilteredBody(getter_AddRefs(stream));
219 if (stream) {
220 aIn.SetBodyUsed(aCx, aRv);
221 if (NS_WARN_IF(aRv.Failed())) {
222 return;
226 SerializeCacheStream(stream, &aOut.body(), aRv);
227 if (NS_WARN_IF(aRv.Failed())) {
228 return;
232 // static
233 void TypeUtils::ToCacheQueryParams(CacheQueryParams& aOut,
234 const CacheQueryOptions& aIn) {
235 aOut.ignoreSearch() = aIn.mIgnoreSearch;
236 aOut.ignoreMethod() = aIn.mIgnoreMethod;
237 aOut.ignoreVary() = aIn.mIgnoreVary;
240 // static
241 void TypeUtils::ToCacheQueryParams(CacheQueryParams& aOut,
242 const MultiCacheQueryOptions& aIn) {
243 ToCacheQueryParams(aOut, static_cast<const CacheQueryOptions&>(aIn));
244 aOut.cacheNameSet() = aIn.mCacheName.WasPassed();
245 if (aOut.cacheNameSet()) {
246 aOut.cacheName() = aIn.mCacheName.Value();
247 } else {
248 aOut.cacheName() = u""_ns;
252 already_AddRefed<Response> TypeUtils::ToResponse(const CacheResponse& aIn) {
253 if (aIn.type() == ResponseType::Error) {
254 // We don't bother tracking the internal error code for cached responses...
255 RefPtr<Response> r =
256 new Response(GetGlobalObject(),
257 InternalResponse::NetworkError(NS_ERROR_FAILURE), nullptr);
258 return r.forget();
261 SafeRefPtr<InternalResponse> ir =
262 MakeSafeRefPtr<InternalResponse>(aIn.status(), aIn.statusText());
263 ir->SetURLList(aIn.urlList());
265 RefPtr<InternalHeaders> internalHeaders =
266 ToInternalHeaders(aIn.headers(), aIn.headersGuard());
267 ErrorResult result;
269 // Be careful to fill the headers before setting the guard in order to
270 // correctly re-create the original headers.
271 ir->Headers()->Fill(*internalHeaders, result);
272 MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
273 ir->Headers()->SetGuard(aIn.headersGuard(), result);
274 MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
276 ir->InitChannelInfo(aIn.securityInfo());
277 if (aIn.principalInfo().isSome()) {
278 UniquePtr<mozilla::ipc::PrincipalInfo> info(
279 new mozilla::ipc::PrincipalInfo(aIn.principalInfo().ref()));
280 ir->SetPrincipalInfo(std::move(info));
283 nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
284 ir->SetBody(stream, InternalResponse::UNKNOWN_BODY_SIZE);
286 switch (aIn.type()) {
287 case ResponseType::Basic:
288 ir = ir->BasicResponse();
289 break;
290 case ResponseType::Cors:
291 ir = ir->CORSResponse();
292 break;
293 case ResponseType::Default:
294 break;
295 case ResponseType::Opaque:
296 ir = ir->OpaqueResponse();
297 break;
298 case ResponseType::Opaqueredirect:
299 ir = ir->OpaqueRedirectResponse();
300 break;
301 default:
302 MOZ_CRASH("Unexpected ResponseType!");
304 MOZ_DIAGNOSTIC_ASSERT(ir);
306 ir->SetPaddingSize(aIn.paddingSize());
308 RefPtr<Response> ref =
309 new Response(GetGlobalObject(), std::move(ir), nullptr);
310 return ref.forget();
312 SafeRefPtr<InternalRequest> TypeUtils::ToInternalRequest(
313 const CacheRequest& aIn) {
314 nsAutoCString url(aIn.urlWithoutQuery());
315 url.Append(aIn.urlQuery());
316 auto internalRequest =
317 MakeSafeRefPtr<InternalRequest>(url, aIn.urlFragment());
318 internalRequest->SetMethod(aIn.method());
319 internalRequest->SetReferrer(aIn.referrer());
320 internalRequest->SetReferrerPolicy(aIn.referrerPolicy());
321 internalRequest->SetMode(aIn.mode());
322 internalRequest->SetCredentialsMode(aIn.credentials());
323 internalRequest->SetContentPolicyType(aIn.contentPolicyType());
324 internalRequest->SetCacheMode(aIn.requestCache());
325 internalRequest->SetRedirectMode(aIn.requestRedirect());
326 internalRequest->SetIntegrity(aIn.integrity());
328 RefPtr<InternalHeaders> internalHeaders =
329 ToInternalHeaders(aIn.headers(), aIn.headersGuard());
330 ErrorResult result;
332 // Be careful to fill the headers before setting the guard in order to
333 // correctly re-create the original headers.
334 internalRequest->Headers()->Fill(*internalHeaders, result);
335 MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
337 internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
338 MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
340 nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
342 internalRequest->SetBody(stream, -1);
344 return internalRequest;
347 SafeRefPtr<Request> TypeUtils::ToRequest(const CacheRequest& aIn) {
348 return MakeSafeRefPtr<Request>(GetGlobalObject(), ToInternalRequest(aIn),
349 nullptr);
352 // static
353 already_AddRefed<InternalHeaders> TypeUtils::ToInternalHeaders(
354 const nsTArray<HeadersEntry>& aHeadersEntryList, HeadersGuardEnum aGuard) {
355 nsTArray<InternalHeaders::Entry> entryList;
356 entryList.SetCapacity(aHeadersEntryList.Length());
357 std::transform(aHeadersEntryList.cbegin(), aHeadersEntryList.cend(),
358 MakeBackInserter(entryList), [](const auto& headersEntry) {
359 return InternalHeaders::Entry(headersEntry.name(),
360 headersEntry.value());
363 RefPtr<InternalHeaders> ref =
364 new InternalHeaders(std::move(entryList), aGuard);
365 return ref.forget();
368 // Utility function to remove the fragment from a URL, check its scheme, and
369 // optionally provide a URL without the query. We're not using nsIURL or URL to
370 // do this because they require going to the main thread. static
371 void TypeUtils::ProcessURL(nsACString& aUrl, bool* aSchemeValidOut,
372 nsACString* aUrlWithoutQueryOut,
373 nsACString* aUrlQueryOut, ErrorResult& aRv) {
374 const nsCString& flatURL = PromiseFlatCString(aUrl);
375 const char* url = flatURL.get();
377 // off the main thread URL parsing using nsStdURLParser.
378 nsCOMPtr<nsIURLParser> urlParser = new nsStdURLParser();
380 uint32_t pathPos;
381 int32_t pathLen;
382 uint32_t schemePos;
383 int32_t schemeLen;
384 aRv = urlParser->ParseURL(url, flatURL.Length(), &schemePos, &schemeLen,
385 nullptr, nullptr, // ignore authority
386 &pathPos, &pathLen);
387 if (NS_WARN_IF(aRv.Failed())) {
388 return;
391 if (aSchemeValidOut) {
392 nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
393 *aSchemeValidOut =
394 scheme.LowerCaseEqualsLiteral("http") ||
395 scheme.LowerCaseEqualsLiteral("https") ||
396 (StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup() &&
397 scheme.LowerCaseEqualsLiteral("moz-extension"));
400 uint32_t queryPos;
401 int32_t queryLen;
403 aRv = urlParser->ParsePath(url + pathPos, flatURL.Length() - pathPos, nullptr,
404 nullptr, // ignore filepath
405 &queryPos, &queryLen, nullptr, nullptr);
406 if (NS_WARN_IF(aRv.Failed())) {
407 return;
410 if (!aUrlWithoutQueryOut) {
411 return;
414 MOZ_DIAGNOSTIC_ASSERT(aUrlQueryOut);
416 if (queryLen < 0) {
417 *aUrlWithoutQueryOut = aUrl;
418 aUrlQueryOut->Truncate();
419 return;
422 // ParsePath gives us query position relative to the start of the path
423 queryPos += pathPos;
425 *aUrlWithoutQueryOut = Substring(aUrl, 0, queryPos - 1);
426 *aUrlQueryOut = Substring(aUrl, queryPos - 1, queryLen + 1);
429 void TypeUtils::CheckAndSetBodyUsed(JSContext* aCx, Request& aRequest,
430 BodyAction aBodyAction, ErrorResult& aRv) {
431 if (aBodyAction == IgnoreBody) {
432 return;
435 if (aRequest.BodyUsed()) {
436 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
437 return;
440 nsCOMPtr<nsIInputStream> stream;
441 aRequest.GetBody(getter_AddRefs(stream));
442 if (stream) {
443 aRequest.SetBodyUsed(aCx, aRv);
444 if (NS_WARN_IF(aRv.Failed())) {
445 return;
450 SafeRefPtr<InternalRequest> TypeUtils::ToInternalRequest(const nsACString& aIn,
451 ErrorResult& aRv) {
452 RequestOrUTF8String requestOrString;
453 requestOrString.SetAsUTF8String().ShareOrDependUpon(aIn);
455 // Re-create a GlobalObject stack object so we can use webidl Constructors.
456 AutoJSAPI jsapi;
457 if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
458 aRv.Throw(NS_ERROR_UNEXPECTED);
459 return nullptr;
461 JSContext* cx = jsapi.cx();
462 GlobalObject global(cx, GetGlobalObject()->GetGlobalJSObject());
463 MOZ_DIAGNOSTIC_ASSERT(!global.Failed());
465 RootedDictionary<RequestInit> requestInit(cx);
466 SafeRefPtr<Request> request =
467 Request::Constructor(global, requestOrString, requestInit, aRv);
468 if (NS_WARN_IF(aRv.Failed())) {
469 return nullptr;
472 return request->GetInternalRequest();
475 void TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
476 Maybe<CacheReadStream>* aStreamOut,
477 ErrorResult& aRv) {
478 *aStreamOut = Nothing();
479 if (!aStream) {
480 return;
483 RefPtr<ReadStream> controlled = do_QueryObject(aStream);
484 if (controlled) {
485 controlled->Serialize(aStreamOut, aRv);
486 return;
489 aStreamOut->emplace(CacheReadStream());
490 CacheReadStream& cacheStream = aStreamOut->ref();
492 cacheStream.control() = nullptr;
494 MOZ_ALWAYS_TRUE(mozilla::ipc::SerializeIPCStream(do_AddRef(aStream),
495 cacheStream.stream(),
496 /* aAllowLazy */ false));
499 } // namespace mozilla::dom::cache