Files
palemoon27/dom/fetch/FetchDriver.cpp
T
roytam1 52a27516d8 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1261720 (part 1) - Separate js::ClassExtension from js::Class. r=jorendorff,bz. (6368952442)
- Bug 1261720 (part 2) - Move ClassExtension::isWrappedNative into js::Class::flags. r=jorendorff. (554fe695c2)
- Bug 1261723 (part 1) - Rename js::Class::ops as oOps. r=efaust. (e9dace574c)
- Bug 1261723 (part 2) - Separate class ops from js::Class. code=njn,h4writer. r=efaust,bz. (54bc06aec6)
- Bug 1263865 - Check the return value of GetSelfHostedFunction in ArraySpeciesCreate. r=efaust (cc6e1c0e50)
- Bug 1248948 - Don't pass non-SavedFrame objects to SavedFrame JSAPI functions; r=tromey r=evilpie (8c753c3c89)
- Bug 1260475 - Add an option to disable GC slices triggered by painting. r=terrence a=kwierso (df86be5e34)
- Bug 1263803 - Change AllocateArrayBuffer to receive byteLength instead of nelements. r=lth (751e333ff0)
- Bug 1264941 - Use byteLength of source typedArray in CloneArrayBuffer. r=lth (0095fce3aa)
- Bug 1263879 - Check the return value of AtomizeString in str_replace_string_raw. r=h4writer (31e1470bc3)
- Bug 1258453 - Compact arenas containing strings r=terrence (f6b4029d79)
- Bug 1258095 - patch 1/3 - OSFileSystem should have the root == the directory root, r=smaug (8cf74899f4)
- Bug 1258095 - patch 2/3 - Implement Directory::GetPath() correctly, r=smaug (776f503c98)
- Bug 1258095 - patch 3/3 - Directory tasks should use FallibleArray, r=smaug (b4888c92b9)
- Bug 1258221 - patch 1 - File::CreateFromFile only for main-thread, r=smaug (c106b37cb4)
- Bug 1258056 - Propagate the window opener full page zoom across the IPC layer; r=smaug (511386589f)
- Bug 1248772 - Trigger a OS window focus in ServiceWorkerClients::OpenWindow. r=ehsan (feb322b9f0)
- Bug 1259707 - Fix confusion between desktop and CSS pixels when session-restore is constraining window to the available screen space. r=emk (def9cc918b)
- Bug 1250266 - Always send a TTL in the Push mochitests. r=benbangert (43c7bde7b8)
- Bug 1244816 - Create PushService mock for mochitests backed by a mock web socket. r=kitcambridge (92270cbae5)
- Bug 1257395: Update comments for GCHashTable and GCPolicy. DONTBUILD r=terrence (fee0311ea0)
- Bug 1263772 - Use WeakCache wrapper to sweep BaseShape table; r=jonco (a0e015667c)
- Bug 1263777 - Use WeakCache to sweep the InitialShapeTable; r=jonco (84d1591585)
- Bug 1132502 (part 1) - Abort if compartmentStats is null during memory reporting. r=jandem. (bd144192b1)
- Bug 1132502 (part 2) - Don't call AddClassInfo() for BaseShapes. r=jandem. (10881fe48c)
- Bug 1259490 - Update the DtoA cache after compacting GC; r=jandem (6e3139501c)
- Bug 1257903 - Fix spurious GC hazard on a CLOSED TREE r=me (ce2935d8b2)
- Bug 1259042 - Re-introduce a version of ZoneCellIter for use under GC r=terrence (d1e4ca6e30)
- Bug 1259180 - Compact arenas containing scripts r=terrence (365d22ab00)
- Bug 1258407 - Limit GC heap growth parameters r=sfink (1c9ffb0e3f)
- Bug 1263966 - Compact arenas containing lazy scripts r=terrence (e22c571c37)
- Bug 1266107 - Update type descriptors first when compacting r=terrence (4c2ca98bb3)
- Bug 1266105 - Only purge runtime tables once per slice when compacting r=terrence (74e2977241)
- Bug 1263769 - Sweep WeakCaches in parallel; r=sfink (54b077b37b)
- Bug 1266107 - Track all existing typed object descriptor objects r=terrence (bc60425bf6)
- Bug 1260198 - Clear per-zone string cache after compacting r=terrence (c3002b8021)
- Bug 1259306 - Trace ShapeTables r=terrence (15abb48a33)
- Bug 1262203 - Skip shape table tracing where possible r=terrence (8a1ec15053)
- Bug 1164432 - Update test_try_registering_offline_disabled.html to use mock push server. r=kitcambridge (6d52de1a17)
- Bug 1263857 - Initialize the slots of the match result object before creating properties in generateRegExpMatcherStub. r=h4writer (802af47b3a)
- Bug 1263549 - Fix inlined RegExpPrototypeOptimizable and RegExpInstanceOptimizable. r=h4writer (5565bca590)
- Bug 1264998 - CodeGeneratorShared::assignBailoutId: Properly handle allocation errors. r=h4writer (258cbfc0b6)
- Bug 1257408 - Fix VS2015 C4312 warnings in js/src. r=nbp (d52a4d1fe7)
- Bug 1220466 - Don't build event region display items for pseudo stacking contexts, unless they are also an AGR. r=mstange (9f9a2c7eba)
- Bug 1220466 - Avoid doing unnecessary layer building work for inactive layers. r=mstange (372097e6df)
- Bug 1239151. Increase skia font cache size to 10mb on non-android platforms. r=lsalzman (8a2029da40)
- Bug 1255068 - Do not allow empty transaction transform changes if the scroll position has changed since the last paint. r=kats, r=mattwoodrow (f4a42e10c8)
- Bug 1255068 - Add a check for null scrollid. r=kats (3ddbb78fd3)
- Bug 1258910 - IonMonkey: MIPS: Implement float-point conditional move instructions. r=huangwenjun06 (b53499103b)
- Bug 1258910 - OdinMonkey: MIPS64: Implement AsmSelectI64. r=bbouvier (a36cfffa1d)
- Bug 1258910 - OdinMonkey: MIPS: Implement AsmSelect. r=bbouvier (e458724bc1)
- Bug 1254500 - IonMonkey: MIPS: Implement ma_ctz. r=arai (c8bfd7a6c9)
- Bug 1254500 - IonMonkey: MIPS: Implement CodeGeneratorMIPSShared::visitCtzI. r=arai (b65dad7d1b)
- Bug 1254500 - IonMonkey: MIPS: Implement CodeGeneratorMIPSShared::visitPopcntI. r=arai (58c00b603e)
- Bug 1258105 - Port object length stubs to CacheIR. r=efaust (ed0ec07678)
- Bug 1258301 - Use TraceNullableEdge for CacheIR pointers. r=jonco (3342ef24b4)
- Bug 1258327 - Part 1: Move ToAddress from CodeGeneratorMIPSShared to CodeGeneratorShared. r=hev (59e7494f02)
- Bug 1258327 - Part 2: Remove Operand variant from bailoutCmp32. r=nbp (c4609af383)
- Bug 1258327 - Part 3: Remove Operand variant from branch32 except x86-shared. r=nbp (dca4bc7577)
- Bug 1258327 - Part 4: Remove ToOperand call from arm CodeGenerator. r=jandem (395d7a004f)
- Bug 1258327 - Part 5: Remove dummy ToOperand definition from arm64 CodeGenerator. r=jandem (bc447cb6aa)
- Bug 1258327 - Part 6: Remove ToOperand call from mips-shared and mips64 CodeGenerator. r=hev (d01b028148)
- Bug 1258327 - Part 7: Move ToOperand from CodeGeneratorShared to CodeGeneratorX86Shared. r=nbp (71449d7c62)
- Bug 1248412 - Prevent immediate bailout from innermost for-of loops. r=h4writer (616897a580)
- Bug 1261326 - Fix a bogus assert. r=bhackett (ff715a7954)
- Bug 1260371 - Rearrange RelocationOverlay so that magic field does not overlay inline string chars r=terrence (f88fbb8927)
- Bug 1262203 - Do GC relocation writes in order; r=sfink (b82a25dd15)
- Bug 1247909 - Move MFunctionEnvironment after the entry resume points operands. r=h4writer (29d8d1c2df)
- Bug 1259925 - Port ModuleNamespace getprop stub to CacheIR. r=efaust (b31980bdc3)
- Bug 1264561 - Fix ClassOps::call and ClassOps::construct address calculation in visitIsCallable and visitIsConstructor. r=efaust (172037857b)
- Bug 1264823 - Add pre-barrier to the elements of mapIterationResultPair. r=jandem (b2e451c371)
- Bug 1263609: SharedStubs - Allow JSOP_POW in ion codegen ON CLOSED TREE, r=bbouvier (16bb4ca9af)
- Bug 1265159 - IonMonkey: Throw error when popping from an empty array in MArrayPopShift, r=jandem (e3a1b08614)
- Bug 1259392 - nail down isLockFree(4) for good. r=jolesen (72fa558eec)
- Bug 1244252 - Don't check object group generation when generating code to create unboxed objects off thread, r=terrence. (4191175759)
- Bug 1254578 - Fix OOM case when rematerializing frames. (r=jandem) (a213756a2b)
- Bug 1263139 - Apply ToString to non-standard flags argument of String.prototype.{match,search,replace}. r=till (91c8f6f592)
- Bug 1257810 - ReleaseAcquire should be adequate for a counter; r=sfink (e394a21b3d)
- Bug 1251833 - Part 4: Remove some unneeded qualification from GCRuntime and friends. r=terrence (e77de1a3ac)
- Bug 1260371 - Forward another pointer during TypedObject tracing to fix bustage r=me (24636e9463)
- Bug 1266107 - Simplify typed object tracing now type descriptors are traced first r=terrence (6872be4bfa)
- Bug 1260998 - Add support for HOST_CPPFLAGS for consistency. r=nalexander (9fe0ddee7e)
- Bug 1105556 - Don't call CheckLoadURIWithPrincipal() in DoCheckLoadURIChecks() for TYPE_DOCUMENT loads where we don't have a loadingPrincipal. Ensure SEC_COOKIES_SAME_ORIGIN isn't set for TYPE_DOCUMENT loads in CheckChannel(). r=ckerschb, sicking (fbebbf1017)
- Bug 1195172 - Use channel->ascynOpen2 layout/style/FontFaceSet.cpp (r=bz,cam) (3e943da95c)
- Bug 1250986 - Make Request.referrer a USVString; r=bzbarsky (6987ab9838)
- Bug 1250987 - Make RequestInit.body nullable; r=bzbarsky (2496ebea1f)
- Bug 1251448 - Add support for RequestInit.referrer; r=jdm (dcf1a668f8)
- Bug 1250985 - Part 1: Add a way to identify whether a WebIDL dictionary has any members present; r=bzbarsky (7ddd6e8043)
- Bug 1250985 - Part 2: Prevent copy constructing a Request object with navigate mode if a RequestInit member is present; r=bzbarsky (24c8c1ca56)
- Bug 1184550 - Move the check for bodyUsed before the check for a null body so subsequent fetches with the same Request fail. r=bkelly (60837c04eb)
- Bug 1251872 - Part 1: Implement Request.referrerPolicy; r=jdm (bd024c0614)
- bug 1252687 - make Migration's ctor constexpr r=bz (793d608bcd)
- Bug 1251872 - Part 2: Store the Request referrerPolicy in the DOM Cache; r=bkelly (e1511c07a6)
- Bug 1251229 P2 Add wpt test verifying FetchEvent.request.url does not include fragments. r=ehsan (1d74e2491e)
- Bug 1251229 P1 Strip fragment from request URL when creating FetchEvent. r=ehsan (5faddecc78)
- Bug 1120715 - Part 4: Add tests for Request.cache; r=bkelly (81537bd125)
- Bug 1237455 P1 Make file_CrossSiteXHR_server.sjs check headers on redirects. r=ehsan (597fdf223d)
- Bug 1237455 P2 Test headers on redirects in fetch mochitests. r=ehsan (2a19ac6a44)
- Bug 1237455 P3 Add a version of test_fetch_cors that reroutes through an empty service worker. r=ehsan (0550d5e115)
- Bug 1205288 - implement and test fetch spec changes for blob scheme with non-GET method. r=bkelly (bb519b1c71)
- Bug 1237455 P4 Create helper method to set fetch request headers. r=ehsan (20266aa708)
- Bug 1237455 P5 Set headers on fetch() redirects. r=ehsan (95dcfe7f71)
- Bug 1120715 - Part 5: Treat a default cache mode Request with a revalidation header as no-store; r=bkelly (4182fded21)
- fix! (c3097a682a)
- Bug 1204520 - Remove unused return value from FetchDriver::FailWithNetworkError. r=jdm (ddd84df514)
- Bug 1253054 - Stop warning if request has already failed. r=bkelly (52eb17afd2)
- Bug 1176824 - Intermittent browser_test_web_manifest.js. r=ckerschb (b8c1fc5757)
- Bug 1242051 - Add inter-directory test support file dependencies to ini manifests. r=gps (5d06d6b00c)
- Bug 1250048 - CSP manifest-src doesn't override default-src. r=ckerschb,bkelly,ehsan (3d77b91226)
- Bug 1262624 Move service worker wpt tests out of mozilla dir so they will be upstreamed. r=jgraham (cf1f010cd2)
- Bug 1263469 P1 Set FetchEvent.request.cache value correctly for non-fetch channels. r=mayhemer (0fac232769)
- Bug 1263469 P2 Validate FetchEvent.request.cache in refresh mochitest. r=ehsan (de0db7da84)
- Bug 1263469 P3 Test FetchEvent.request.cache value on reload in wpt test. r=ehsan (8b4fc58d08)
- Bug 1263469 P4 Update test_eventsource_intercept.html to validate FetchEvent.request.cache. r=ehsan (81843666c0)
- Bug 1263469 P5 Add a wpt test case for EventSource. r=ehsan (a32cbbaf80)
- Bug 1265941 - Rename the ReferrerPolicy "origin-only" enum value to "origin"; r=jdm (1b1dfcebbc)
2024-04-16 14:48:59 +08:00

761 lines
25 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/FetchDriver.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIDocument.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIScriptSecurityManager.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIUploadChannel2.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIPipe.h"
#include "nsContentPolicyUtils.h"
#include "nsCORSListenerProxy.h"
#include "nsDataHandler.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsStreamUtils.h"
#include "nsStringStream.h"
#include "nsHttpChannel.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/workers/Workers.h"
#include "mozilla/unused.h"
#include "Fetch.h"
#include "InternalRequest.h"
#include "InternalResponse.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(FetchDriver,
nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
nsIThreadRetargetableStreamListener)
FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
nsILoadGroup* aLoadGroup)
: mPrincipal(aPrincipal)
, mLoadGroup(aLoadGroup)
, mRequest(aRequest)
, mResponseAvailableCalled(false)
, mFetchCalled(false)
{
}
FetchDriver::~FetchDriver()
{
// We assert this since even on failures, we should call
// FailWithNetworkError().
MOZ_ASSERT(mResponseAvailableCalled);
}
nsresult
FetchDriver::Fetch(FetchDriverObserver* aObserver)
{
workers::AssertIsOnMainThread();
MOZ_ASSERT(!mFetchCalled);
mFetchCalled = true;
mObserver = aObserver;
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
mRequest->WasCreatedByFetchEvent());
// FIXME(nsm): Deal with HSTS.
MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
"Synchronous fetch not supported");
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &FetchDriver::ContinueFetch);
return NS_DispatchToCurrentThread(r);
}
nsresult
FetchDriver::ContinueFetch()
{
workers::AssertIsOnMainThread();
nsresult rv = HttpFetch();
if (NS_FAILED(rv)) {
FailWithNetworkError();
}
return rv;
}
// This function implements the "HTTP Fetch" algorithm from the Fetch spec.
// Functionality is often split between here, the CORS listener proxy and the
// Necko HTTP implementation.
nsresult
FetchDriver::HttpFetch()
{
// Step 1. "Let response be null."
mResponse = nullptr;
nsresult rv;
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString url;
mRequest->GetURL(url);
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri),
url,
nullptr,
nullptr,
ios);
NS_ENSURE_SUCCESS(rv, rv);
// Unsafe requests aren't allowed with when using no-core mode.
if (mRequest->Mode() == RequestMode::No_cors &&
mRequest->UnsafeRequest() &&
(!mRequest->HasSimpleMethod() ||
!mRequest->Headers()->HasOnlySimpleHeaders())) {
MOZ_ASSERT(false, "The API should have caught this");
return NS_ERROR_DOM_BAD_URI;
}
// non-GET requests aren't allowed for blob.
if (IsBlobURI(uri)) {
nsAutoCString method;
mRequest->GetMethod(method);
if (!method.EqualsLiteral("GET")) {
return NS_ERROR_DOM_NETWORK_ERR;
}
}
// Step 2 deals with letting ServiceWorkers intercept requests. This is
// handled by Necko after the channel is opened.
// FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
// set based on the Request's flag.
// Step 3.1 "If the CORS preflight flag is set and one of these conditions is
// true..." is handled by the CORS proxy.
//
// Step 3.2 "Set request's skip service worker flag." This isn't required
// since Necko will fall back to the network if the ServiceWorker does not
// respond with a valid Response.
//
// NS_StartCORSPreflight() will automatically kick off the original request
// if it succeeds, so we need to have everything setup for the original
// request too.
// Step 3.3 "Let credentials flag be set if one of
// - request's credentials mode is "include"
// - request's credentials mode is "same-origin" and either the CORS flag
// is unset or response tainting is "opaque"
// is true, and unset otherwise."
// Set skip serviceworker flag.
// While the spec also gates on the client being a ServiceWorker, we can't
// infer that here. Instead we rely on callers to set the flag correctly.
const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
nsSecurityFlags secFlags = nsILoadInfo::SEC_ABOUT_BLANK_INHERITS;
if (mRequest->Mode() == RequestMode::Cors) {
secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
} else if (mRequest->Mode() == RequestMode::Same_origin ||
mRequest->Mode() == RequestMode::Navigate) {
secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
} else if (mRequest->Mode() == RequestMode::No_cors) {
secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
} else {
MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
return NS_ERROR_UNEXPECTED;
}
if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
}
// This is handles the use credentials flag in "HTTP
// network or cache fetch" in the spec and decides whether to transmit
// cookies and other identifying information.
if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
} else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
} else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) {
secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
} else {
MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
return NS_ERROR_UNEXPECTED;
}
// From here on we create a channel and set its properties with the
// information from the InternalRequest. This is an implementation detail.
MOZ_ASSERT(mLoadGroup);
nsCOMPtr<nsIChannel> chan;
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
bypassFlag | nsIChannel::LOAD_CLASSIFY_URI;
if (mDocument) {
MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
rv = NS_NewChannel(getter_AddRefs(chan),
uri,
mDocument,
secFlags,
mRequest->ContentPolicyType(),
mLoadGroup,
nullptr, /* aCallbacks */
loadFlags,
ios);
} else {
rv = NS_NewChannel(getter_AddRefs(chan),
uri,
mPrincipal,
secFlags,
mRequest->ContentPolicyType(),
mLoadGroup,
nullptr, /* aCallbacks */
loadFlags,
ios);
}
NS_ENSURE_SUCCESS(rv, rv);
mLoadGroup = nullptr;
// Insert ourselves into the notification callbacks chain so we can set
// headers on redirects.
#ifdef DEBUG
{
nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
MOZ_ASSERT(!notificationCallbacks);
}
#endif
chan->SetNotificationCallbacks(this);
// Step 3.5 begins "HTTP network or cache fetch".
// HTTP network or cache fetch
// ---------------------------
// Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
if (httpChan) {
// Copy the method.
nsAutoCString method;
mRequest->GetMethod(method);
rv = httpChan->SetRequestMethod(method);
NS_ENSURE_SUCCESS(rv, rv);
// Set the same headers.
SetRequestHeaders(httpChan);
// Step 2. Set the referrer.
nsAutoString referrer;
mRequest->GetReferrer(referrer);
ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
net::ReferrerPolicy net_referrerPolicy = net::RP_Unset;
switch (referrerPolicy) {
case ReferrerPolicy::_empty:
net_referrerPolicy = net::RP_Default;
break;
case ReferrerPolicy::No_referrer:
net_referrerPolicy = net::RP_No_Referrer;
break;
case ReferrerPolicy::No_referrer_when_downgrade:
net_referrerPolicy = net::RP_No_Referrer_When_Downgrade;
break;
case ReferrerPolicy::Origin:
net_referrerPolicy = net::RP_Origin;
break;
case ReferrerPolicy::Origin_when_cross_origin:
net_referrerPolicy = net::RP_Origin_When_Crossorigin;
break;
case ReferrerPolicy::Unsafe_url:
net_referrerPolicy = net::RP_Unsafe_URL;
break;
default:
MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
break;
}
if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
mDocument,
httpChan,
net_referrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
} else if (referrer.IsEmpty()) {
rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// From "Determine request's Referrer" step 3
// "If request's referrer is a URL, let referrerSource be request's
// referrer."
nsCOMPtr<nsIURI> referrerURI;
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t documentReferrerPolicy = mDocument ? mDocument->GetReferrerPolicy() :
net::RP_Default;
rv =
httpChan->SetReferrerWithPolicy(referrerURI,
referrerPolicy == ReferrerPolicy::_empty ?
documentReferrerPolicy :
net_referrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
}
// Bug 1120722 - Authorization will be handled later.
// Auth may require prompting, we don't support it yet.
// The next patch in this same bug prevents this from aborting the request.
// Credentials checks for CORS are handled by nsCORSListenerProxy,
nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
// Conversion between enumerations is safe due to static asserts in
// dom/workers/ServiceWorkerManager.cpp
internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode()));
mRequest->MaybeSkipCacheIfPerformingRevalidation();
internalChan->SetFetchCacheMode(static_cast<uint32_t>(mRequest->GetCacheMode()));
}
// Step 5. Proxy authentication will be handled by Necko.
// Continue setting up 'HTTPRequest'. Content-Type and body data.
nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
if (uploadChan) {
nsAutoCString contentType;
ErrorResult result;
mRequest->Headers()->Get(NS_LITERAL_CSTRING("content-type"), contentType, result);
// This is an error because the Request constructor explicitly extracts and
// sets a content-type per spec.
if (result.Failed()) {
return result.StealNSResult();
}
nsCOMPtr<nsIInputStream> bodyStream;
mRequest->GetBody(getter_AddRefs(bodyStream));
if (bodyStream) {
nsAutoCString method;
mRequest->GetMethod(method);
rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
NS_ENSURE_SUCCESS(rv, rv);
}
}
// If preflight is required, start a "CORS preflight fetch"
// https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
// implementation is handled by the http channel calling into
// nsCORSListenerProxy. We just inform it which unsafe headers are included
// in the request.
if (mRequest->Mode() == RequestMode::Cors) {
AutoTArray<nsCString, 5> unsafeHeaders;
mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
}
rv = chan->AsyncOpen2(this);
NS_ENSURE_SUCCESS(rv, rv);
// Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
return NS_OK;
}
already_AddRefed<InternalResponse>
FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
nsIURI* aFinalURI,
bool aFoundOpaqueRedirect)
{
MOZ_ASSERT(aResponse);
nsAutoCString reqURL;
if (aFinalURI) {
aFinalURI->GetSpec(reqURL);
} else {
mRequest->GetURL(reqURL);
}
DebugOnly<nsresult> rv = aResponse->StripFragmentAndSetUrl(reqURL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
RefPtr<InternalResponse> filteredResponse;
if (aFoundOpaqueRedirect) {
filteredResponse = aResponse->OpaqueRedirectResponse();
} else {
switch (mRequest->GetResponseTainting()) {
case LoadTainting::Basic:
filteredResponse = aResponse->BasicResponse();
break;
case LoadTainting::CORS:
filteredResponse = aResponse->CORSResponse();
break;
case LoadTainting::Opaque:
filteredResponse = aResponse->OpaqueResponse();
break;
default:
MOZ_CRASH("Unexpected case");
}
}
MOZ_ASSERT(filteredResponse);
MOZ_ASSERT(mObserver);
mObserver->OnResponseAvailable(filteredResponse);
mResponseAvailableCalled = true;
return filteredResponse.forget();
}
void
FetchDriver::FailWithNetworkError()
{
workers::AssertIsOnMainThread();
RefPtr<InternalResponse> error = InternalResponse::NetworkError();
if (mObserver) {
mObserver->OnResponseAvailable(error);
mResponseAvailableCalled = true;
mObserver->OnResponseEnd();
mObserver = nullptr;
}
}
namespace {
class FillResponseHeaders final : public nsIHttpHeaderVisitor {
InternalResponse* mResponse;
~FillResponseHeaders()
{ }
public:
NS_DECL_ISUPPORTS
explicit FillResponseHeaders(InternalResponse* aResponse)
: mResponse(aResponse)
{
}
NS_IMETHOD
VisitHeader(const nsACString & aHeader, const nsACString & aValue) override
{
ErrorResult result;
mResponse->Headers()->Append(aHeader, aValue, result);
if (result.Failed()) {
NS_WARNING(nsPrintfCString("Fetch ignoring illegal header - '%s': '%s'",
PromiseFlatCString(aHeader).get(),
PromiseFlatCString(aValue).get()).get());
result.SuppressException();
}
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(FillResponseHeaders, nsIHttpHeaderVisitor)
} // namespace
NS_IMETHODIMP
FetchDriver::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
workers::AssertIsOnMainThread();
// Note, this can be called multiple times if we are doing an opaqueredirect.
// In that case we will get a simulated OnStartRequest() and then the real
// channel will call in with an errored OnStartRequest().
nsresult rv;
aRequest->GetStatus(&rv);
if (NS_FAILED(rv)) {
FailWithNetworkError();
return rv;
}
// We should only get to the following code once.
MOZ_ASSERT(!mPipeOutputStream);
MOZ_ASSERT(mObserver);
RefPtr<InternalResponse> response;
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
// On a successful redirect we perform the following substeps of HTTP Fetch,
// step 5, "redirect status", step 11.
// Step 11.5 "Append locationURL to request's url list." so that when we set the
// Response's URL from the Request's URL in Main Fetch, step 15, we get the
// final value. Note, we still use a single URL value instead of a list.
// Because of that we only need to do this after the request finishes.
nsCOMPtr<nsIURI> newURI;
rv = NS_GetFinalChannelURI(channel, getter_AddRefs(newURI));
if (NS_FAILED(rv)) {
FailWithNetworkError();
return rv;
}
nsAutoCString newUrl;
newURI->GetSpec(newUrl);
mRequest->SetURL(newUrl);
bool foundOpaqueRedirect = false;
if (httpChannel) {
uint32_t responseStatus;
httpChannel->GetResponseStatus(&responseStatus);
if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
FailWithNetworkError();
return NS_BINDING_FAILED;
}
if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
foundOpaqueRedirect = true;
}
}
nsAutoCString statusText;
httpChannel->GetResponseStatusText(statusText);
response = new InternalResponse(responseStatus, statusText);
RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
rv = httpChannel->VisitResponseHeaders(visitor);
if (NS_WARN_IF(NS_FAILED(rv))) {
NS_WARNING("Failed to visit all headers.");
}
} else {
response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
ErrorResult result;
nsAutoCString contentType;
rv = channel->GetContentType(contentType);
if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
nsAutoCString contentCharset;
channel->GetContentCharset(contentCharset);
if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
}
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
contentType,
result);
MOZ_ASSERT(!result.Failed());
}
int64_t contentLength;
rv = channel->GetContentLength(&contentLength);
if (NS_SUCCEEDED(rv) && contentLength) {
nsAutoCString contentLenStr;
contentLenStr.AppendInt(contentLength);
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
contentLenStr,
result);
MOZ_ASSERT(!result.Failed());
}
}
// We open a pipe so that we can immediately set the pipe's read end as the
// response's body. Setting the segment size to UINT32_MAX means that the
// pipe has infinite space. The nsIChannel will continue to buffer data in
// xpcom events even if we block on a fixed size pipe. It might be possible
// to suspend the channel and then resume when there is space available, but
// for now use an infinite pipe to avoid blocking.
nsCOMPtr<nsIInputStream> pipeInputStream;
rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
getter_AddRefs(mPipeOutputStream),
0, /* default segment size */
UINT32_MAX /* infinite pipe */,
true /* non-blocking input, otherwise you deadlock */,
false /* blocking output, since the pipe is 'in'finite */ );
if (NS_WARN_IF(NS_FAILED(rv))) {
FailWithNetworkError();
// Cancel request.
return rv;
}
response->SetBody(pipeInputStream);
response->InitChannelInfo(channel);
nsCOMPtr<nsIURI> channelURI;
rv = channel->GetURI(getter_AddRefs(channelURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
FailWithNetworkError();
// Cancel request.
return rv;
}
nsCOMPtr<nsILoadInfo> loadInfo;
rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
if (NS_WARN_IF(NS_FAILED(rv))) {
FailWithNetworkError();
return rv;
}
// Propagate any tainting from the channel back to our response here. This
// step is not reflected in the spec because the spec is written such that
// FetchEvent.respondWith() just passes the already-tainted Response back to
// the outer fetch(). In gecko, however, we serialize the Response through
// the channel and must regenerate the tainting from the channel in the
// interception case.
mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
// Resolves fetch() promise which may trigger code running in a worker. Make
// sure the Response is fully initialized before calling this.
mResponse = BeginAndGetFilteredResponse(response, channelURI,
foundOpaqueRedirect);
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
FailWithNetworkError();
// Cancel request.
return rv;
}
// Try to retarget off main thread.
if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
}
return NS_OK;
}
NS_IMETHODIMP
FetchDriver::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aInputStream,
uint64_t aOffset,
uint32_t aCount)
{
// NB: This can be called on any thread! But we're guaranteed that it is
// called between OnStartRequest and OnStopRequest, so we don't need to worry
// about races.
uint32_t aRead;
MOZ_ASSERT(mResponse);
MOZ_ASSERT(mPipeOutputStream);
nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
mPipeOutputStream,
aCount, &aRead);
return rv;
}
NS_IMETHODIMP
FetchDriver::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatusCode)
{
workers::AssertIsOnMainThread();
if (NS_FAILED(aStatusCode)) {
nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
if (outputStream) {
outputStream->CloseWithStatus(NS_BINDING_FAILED);
}
// We proceed as usual here, since we've already created a successful response
// from OnStartRequest.
} else {
MOZ_ASSERT(mResponse);
MOZ_ASSERT(!mResponse->IsError());
if (mPipeOutputStream) {
mPipeOutputStream->Close();
}
}
if (mObserver) {
mObserver->OnResponseEnd();
mObserver = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
nsIChannel* aNewChannel,
uint32_t aFlags,
nsIAsyncVerifyRedirectCallback *aCallback)
{
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
if (httpChannel) {
SetRequestHeaders(httpChannel);
}
aCallback->OnRedirectVerifyCallback(NS_OK);
return NS_OK;
}
NS_IMETHODIMP
FetchDriver::CheckListenerChain()
{
return NS_OK;
}
NS_IMETHODIMP
FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
{
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
*aResult = static_cast<nsIChannelEventSink*>(this);
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
*aResult = static_cast<nsIStreamListener*>(this);
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
*aResult = static_cast<nsIRequestObserver*>(this);
NS_ADDREF_THIS();
return NS_OK;
}
return QueryInterface(aIID, aResult);
}
void
FetchDriver::SetDocument(nsIDocument* aDocument)
{
// Cannot set document after Fetch() has been called.
MOZ_ASSERT(!mFetchCalled);
mDocument = aDocument;
}
void
FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const
{
MOZ_ASSERT(aChannel);
AutoTArray<InternalHeaders::Entry, 5> headers;
mRequest->Headers()->GetEntries(headers);
bool hasAccept = false;
for (uint32_t i = 0; i < headers.Length(); ++i) {
if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) {
hasAccept = true;
}
if (headers[i].mValue.IsEmpty()) {
aChannel->SetEmptyRequestHeader(headers[i].mName);
} else {
aChannel->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
}
}
if (!hasAccept) {
aChannel->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
NS_LITERAL_CSTRING("*/*"),
false /* merge */);
}
if (mRequest->ForceOriginHeader()) {
nsAutoString origin;
if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) {
aChannel->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
NS_ConvertUTF16toUTF8(origin),
false /* merge */);
}
}
}
} // namespace dom
} // namespace mozilla