blob: 09a25fb086407a73e618308473e1e18d9c1085c2 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/fetch/Request.h"
#include "bindings/core/v8/Dictionary.h"
#include "core/dom/ExecutionContext.h"
#include "core/loader/ThreadableLoader.h"
#include "modules/fetch/BodyStreamBuffer.h"
#include "modules/fetch/FetchManager.h"
#include "modules/fetch/RequestInit.h"
#include "platform/bindings/V8PrivateProperty.h"
#include "platform/loader/fetch/FetchUtils.h"
#include "platform/loader/fetch/ResourceLoaderOptions.h"
#include "platform/loader/fetch/ResourceRequest.h"
#include "platform/network/HTTPParsers.h"
#include "platform/network/http_names.h"
#include "platform/runtime_enabled_features.h"
#include "platform/weborigin/OriginAccessEntry.h"
#include "platform/weborigin/Referrer.h"
#include "public/platform/WebURLRequest.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
namespace blink {
FetchRequestData* CreateCopyOfFetchRequestDataForFetch(
ScriptState* script_state,
const FetchRequestData* original) {
FetchRequestData* request = FetchRequestData::Create();
request->SetURL(original->Url());
request->SetMethod(original->Method());
request->SetHeaderList(original->HeaderList()->Clone());
// FIXME: Set client.
DOMWrapperWorld& world = script_state->World();
if (world.IsIsolatedWorld()) {
request->SetOrigin(world.IsolatedWorldSecurityOrigin());
} else {
request->SetOrigin(
ExecutionContext::From(script_state)->GetSecurityOrigin());
}
// FIXME: Set ForceOriginHeaderFlag.
request->SetSameOriginDataURLFlag(true);
request->SetReferrer(original->GetReferrer());
request->SetMode(original->Mode());
request->SetCredentials(original->Credentials());
request->SetCacheMode(original->CacheMode());
request->SetAttachedCredential(original->AttachedCredential());
request->SetRedirect(original->Redirect());
request->SetIntegrity(original->Integrity());
return request;
}
Request* Request::CreateRequestWithRequestOrString(
ScriptState* script_state,
Request* input_request,
const String& input_string,
RequestInit& init,
ExceptionState& exception_state) {
// - "If |input| is a Request object and it is disturbed, throw a
// TypeError."
if (input_request && input_request->bodyUsed()) {
exception_state.ThrowTypeError(
"Cannot construct a Request with a Request object that has already "
"been used.");
return nullptr;
}
// - "Let |temporaryBody| be |input|'s request's body if |input| is a
// Request object, and null otherwise."
BodyStreamBuffer* temporary_body =
input_request ? input_request->BodyBuffer() : nullptr;
// "Let |request| be |input|'s request, if |input| is a Request object,
// and a new request otherwise."
scoped_refptr<SecurityOrigin> origin =
ExecutionContext::From(script_state)->GetSecurityOrigin();
// TODO(yhirano): Implement the following steps:
// - "Let |window| be client."
// - "If |request|'s window is an environment settings object and its
// origin is same origin with entry settings object's origin, set
// |window| to |request|'s window."
// - "If |init|'s window member is present and it is not null, throw a
// TypeError."
// - "If |init|'s window member is present, set |window| to no-window."
//
// "Set |request| to a new request whose url is |request|'s current url,
// method is |request|'s method, header list is a copy of |request|'s
// header list, unsafe-request flag is set, client is entry settings object,
// window is |window|, origin is "client", omit-Origin-header flag is
// |request|'s omit-Origin-header flag, same-origin data-URL flag is set,
// referrer is |request|'s referrer, referrer policy is |request|'s
// referrer policy, destination is the empty string, mode is |request|'s
// mode, credentials mode is |request|'s credentials mode, cache mode is
// |request|'s cache mode, redirect mode is |request|'s redirect mode, and
// integrity metadata is |request|'s integrity metadata."
FetchRequestData* request = CreateCopyOfFetchRequestDataForFetch(
script_state,
input_request ? input_request->GetRequest() : FetchRequestData::Create());
// We don't use fallback values. We set these flags directly in below.
// - "Let |fallbackMode| be null."
// - "Let |fallbackCredentials| be null."
// "Let |baseURL| be entry settings object's API base URL."
const KURL base_url = ExecutionContext::From(script_state)->BaseURL();
// "If |input| is a string, run these substeps:"
if (!input_request) {
// "Let |parsedURL| be the result of parsing |input| with |baseURL|."
KURL parsed_url = KURL(base_url, input_string);
// "If |parsedURL| is failure, throw a TypeError."
if (!parsed_url.IsValid()) {
exception_state.ThrowTypeError("Failed to parse URL from " +
input_string);
return nullptr;
}
// "If |parsedURL| includes credentials, throw a TypeError."
if (!parsed_url.User().IsEmpty() || !parsed_url.Pass().IsEmpty()) {
exception_state.ThrowTypeError(
"Request cannot be constructed from a URL that includes "
"credentials: " +
input_string);
return nullptr;
}
// "Set |request|'s url to |parsedURL| and replace |request|'s url list
// single URL with a copy of |parsedURL|."
request->SetURL(parsed_url);
// We don't use fallback values. We set these flags directly in below.
// - "Set |fallbackMode| to "cors"."
// - "Set |fallbackCredentials| to "omit"."
}
// "If any of |init|'s members are present, run these substeps:"
if (init.AreAnyMembersSet()) {
// "If |request|'s |mode| is "navigate", throw a TypeError."
if (request->Mode() == WebURLRequest::kFetchRequestModeNavigate) {
exception_state.ThrowTypeError(
"Cannot construct a Request with a Request whose mode is 'navigate' "
"and a non-empty RequestInit.");
return nullptr;
}
// TODO(yhirano): Implement the following substep:
// "Unset |request|'s omit-Origin-header flag."
// The substep "Set |request|'s referrer to "client"." is performed by
// the code below as follows:
// - |init.referrer.referrer| gets initialized by the RequestInit
// constructor to "about:client" when any of |options|'s members are
// present.
// - The code below does the equivalent as the step specified in the
// spec by processing the "about:client".
// The substep "Set |request|'s referrer policy to the empty string."
// is also performed by the code below similarly.
}
// The following if-clause performs the following two steps:
// - "If |init|'s referrer member is present, run these substeps:"
// "If |init|'s referrerPolicy member is present, set |request|'s
// referrer policy to it."
//
// The condition "if any of |init|'s members are present"
// (areAnyMembersSet) is used for the if-clause instead of conditions
// indicating presence of each member as specified in the spec. This is to
// perform the substeps in the previous step together here.
if (init.AreAnyMembersSet()) {
// Nothing to do for the step "Let |referrer| be |init|'s referrer
// member."
if (init.GetReferrer().referrer.IsEmpty()) {
// "If |referrer| is the empty string, set |request|'s referrer to
// "no-referrer" and terminate these substeps."
request->SetReferrerString(AtomicString(Referrer::NoReferrer()));
} else {
// "Let |parsedReferrer| be the result of parsing |referrer| with
// |baseURL|."
KURL parsed_referrer(base_url, init.GetReferrer().referrer);
if (!parsed_referrer.IsValid()) {
// "If |parsedReferrer| is failure, throw a TypeError."
exception_state.ThrowTypeError("Referrer '" +
init.GetReferrer().referrer +
"' is not a valid URL.");
return nullptr;
}
if (parsed_referrer.ProtocolIsAbout() &&
parsed_referrer.Host().IsEmpty() &&
parsed_referrer.GetPath() == "client") {
// "If |parsedReferrer|'s non-relative flag is set, scheme is
// "about", and path contains a single string "client", set
// request's referrer to "client" and terminate these
// substeps."
request->SetReferrerString(FetchRequestData::ClientReferrerString());
} else if (!origin->IsSameSchemeHostPortAndSuborigin(
SecurityOrigin::Create(parsed_referrer).get())) {
// "If |parsedReferrer|'s origin is not same origin with
// |origin|, throw a TypeError."
exception_state.ThrowTypeError(
"The origin of '" + init.GetReferrer().referrer +
"' should be same as '" + origin->ToString() + "'");
return nullptr;
} else {
// "Set |request|'s referrer to |parsedReferrer|."
request->SetReferrerString(AtomicString(parsed_referrer.GetString()));
}
}
request->SetReferrerPolicy(init.GetReferrer().referrer_policy);
}
// The following code performs the following steps:
// - "Let |mode| be |init|'s mode member if it is present, and
// |fallbackMode| otherwise."
// - "If |mode| is "navigate", throw a TypeError."
// - "If |mode| is non-null, set |request|'s mode to |mode|."
if (init.Mode() == "navigate") {
exception_state.ThrowTypeError(
"Cannot construct a Request with a RequestInit whose mode member is "
"set as 'navigate'.");
return nullptr;
}
if (init.Mode() == "same-origin") {
request->SetMode(WebURLRequest::kFetchRequestModeSameOrigin);
} else if (init.Mode() == "no-cors") {
request->SetMode(WebURLRequest::kFetchRequestModeNoCORS);
} else if (init.Mode() == "cors") {
request->SetMode(WebURLRequest::kFetchRequestModeCORS);
} else {
// |inputRequest| is directly checked here instead of setting and
// checking |fallbackMode| as specified in the spec.
if (!input_request)
request->SetMode(WebURLRequest::kFetchRequestModeCORS);
}
// "Let |credentials| be |init|'s credentials member if it is present, and
// |fallbackCredentials| otherwise."
// "If |credentials| is non-null, set |request|'s credentials mode to
// |credentials|."
if (init.Credentials() == "omit") {
request->SetCredentials(WebURLRequest::kFetchCredentialsModeOmit);
} else if (init.Credentials() == "same-origin") {
request->SetCredentials(WebURLRequest::kFetchCredentialsModeSameOrigin);
} else if (init.Credentials() == "include") {
request->SetCredentials(WebURLRequest::kFetchCredentialsModeInclude);
} else if (init.Credentials() == "password") {
if (!init.AttachedCredential().get()) {
exception_state.ThrowTypeError(
"Cannot construct a Request with a credential mode of 'password' "
"without a PasswordCredential.");
return nullptr;
}
request->SetCredentials(WebURLRequest::kFetchCredentialsModePassword);
request->SetAttachedCredential(init.AttachedCredential());
request->SetRedirect(WebURLRequest::kFetchRedirectModeManual);
} else {
if (!input_request)
request->SetCredentials(WebURLRequest::kFetchCredentialsModeOmit);
}
// "If |init|'s cache member is present, set |request|'s cache mode to it."
if (init.CacheMode() == "default") {
request->SetCacheMode(mojom::FetchCacheMode::kDefault);
} else if (init.CacheMode() == "no-store") {
request->SetCacheMode(mojom::FetchCacheMode::kNoStore);
} else if (init.CacheMode() == "reload") {
request->SetCacheMode(mojom::FetchCacheMode::kBypassCache);
} else if (init.CacheMode() == "no-cache") {
request->SetCacheMode(mojom::FetchCacheMode::kValidateCache);
} else if (init.CacheMode() == "force-cache") {
request->SetCacheMode(mojom::FetchCacheMode::kForceCache);
} else if (init.CacheMode() == "only-if-cached") {
request->SetCacheMode(mojom::FetchCacheMode::kOnlyIfCached);
}
// "If |init|'s redirect member is present, set |request|'s redirect mode
// to it."
if (init.Redirect() == "follow") {
request->SetRedirect(WebURLRequest::kFetchRedirectModeFollow);
} else if (init.Redirect() == "error") {
request->SetRedirect(WebURLRequest::kFetchRedirectModeError);
} else if (init.Redirect() == "manual") {
request->SetRedirect(WebURLRequest::kFetchRedirectModeManual);
}
// "If |init|'s integrity member is present, set |request|'s
// integrity metadata to it."
if (!init.Integrity().IsNull())
request->SetIntegrity(init.Integrity());
// "If |init|'s method member is present, let |method| be it and run these
// substeps:"
if (!init.Method().IsNull()) {
// "If |method| is not a method or method is a forbidden method, throw
// a TypeError."
if (!IsValidHTTPToken(init.Method())) {
exception_state.ThrowTypeError("'" + init.Method() +
"' is not a valid HTTP method.");
return nullptr;
}
if (FetchUtils::IsForbiddenMethod(init.Method())) {
exception_state.ThrowTypeError("'" + init.Method() +
"' HTTP method is unsupported.");
return nullptr;
}
// "Normalize |method|."
// "Set |request|'s method to |method|."
request->SetMethod(
FetchUtils::NormalizeMethod(AtomicString(init.Method())));
}
// "Let |r| be a new Request object associated with |request| and a new
// Headers object whose guard is "request"."
Request* r = Request::Create(script_state, request);
// Perform the following steps:
// - "Let |headers| be a copy of |r|'s Headers object."
// - "If |init|'s headers member is present, set |headers| to |init|'s
// headers member."
//
// We don't create a copy of r's Headers object when init's headers member
// is present.
Headers* headers = nullptr;
if (init.GetHeaders().IsNull()) {
headers = r->getHeaders()->Clone();
}
// "Empty |r|'s request's header list."
r->request_->HeaderList()->ClearList();
// "If |r|'s request's mode is "no-cors", run these substeps:
if (r->GetRequest()->Mode() == WebURLRequest::kFetchRequestModeNoCORS) {
// "If |r|'s request's method is not a CORS-safelisted method, throw a
// TypeError."
if (!FetchUtils::IsCORSSafelistedMethod(r->GetRequest()->Method())) {
exception_state.ThrowTypeError("'" + r->GetRequest()->Method() +
"' is unsupported in no-cors mode.");
return nullptr;
}
// "Set |r|'s Headers object's guard to "request-no-cors"."
r->getHeaders()->SetGuard(Headers::kRequestNoCORSGuard);
}
// "Fill |r|'s Headers object with |headers|. Rethrow any exceptions."
if (!init.GetHeaders().IsNull()) {
r->getHeaders()->FillWith(init.GetHeaders(), exception_state);
} else {
DCHECK(headers);
r->getHeaders()->FillWith(headers, exception_state);
}
if (exception_state.HadException())
return nullptr;
// "If either |init|'s body member is present or |temporaryBody| is
// non-null, and |request|'s method is `GET` or `HEAD`, throw a TypeError.
if (init.GetBody() || temporary_body ||
request->Credentials() == WebURLRequest::kFetchCredentialsModePassword) {
if (request->Method() == HTTPNames::GET ||
request->Method() == HTTPNames::HEAD) {
exception_state.ThrowTypeError(
"Request with GET/HEAD method cannot have body.");
return nullptr;
}
}
// TODO(mkwst): See the comment in RequestInit about serializing the attached
// credential prior to hitting the Service Worker machinery.
if (request->Credentials() == WebURLRequest::kFetchCredentialsModePassword) {
r->getHeaders()->append(HTTPNames::Content_Type, init.ContentType(),
exception_state);
const OriginAccessEntry access_entry =
OriginAccessEntry(r->url().Protocol(), r->url().Host(),
OriginAccessEntry::kAllowRegisterableDomains);
if (access_entry.MatchesDomain(*origin) ==
OriginAccessEntry::kDoesNotMatchOrigin) {
exception_state.ThrowTypeError(
"Credentials may only be submitted to endpoints on the same "
"registrable domain.");
return nullptr;
}
}
// "If |init|'s body member is present, run these substeps:"
if (init.GetBody()) {
// Perform the following steps:
// - "Let |stream| and |Content-Type| be the result of extracting
// |init|'s body member."
// - "Set |temporaryBody| to |stream|.
// - "If |Content-Type| is non-null and |r|'s request's header list
// contains no header named `Content-Type`, append
// `Content-Type`/|Content-Type| to |r|'s Headers object. Rethrow any
// exception."
temporary_body =
new BodyStreamBuffer(script_state, std::move(init.GetBody()));
if (!init.ContentType().IsEmpty() &&
!r->getHeaders()->has(HTTPNames::Content_Type, exception_state)) {
r->getHeaders()->append(HTTPNames::Content_Type, init.ContentType(),
exception_state);
}
if (exception_state.HadException())
return nullptr;
}
// "Set |r|'s request's body to |temporaryBody|.
if (temporary_body) {
r->request_->SetBuffer(temporary_body);
r->RefreshBody(script_state);
}
// "Set |r|'s MIME type to the result of extracting a MIME type from |r|'s
// request's header list."
r->request_->SetMIMEType(r->request_->HeaderList()->ExtractMIMEType());
// "If |input| is a Request object and |input|'s request's body is
// non-null, run these substeps:"
if (input_request && input_request->BodyBuffer()) {
// "Let |dummyStream| be an empty ReadableStream object."
auto* dummy_stream =
new BodyStreamBuffer(script_state, BytesConsumer::CreateClosed());
// "Set |input|'s request's body to a new body whose stream is
// |dummyStream|."
input_request->request_->SetBuffer(dummy_stream);
input_request->RefreshBody(script_state);
// "Let |reader| be the result of getting reader from |dummyStream|."
// "Read all bytes from |dummyStream| with |reader|."
input_request->BodyBuffer()->CloseAndLockAndDisturb();
}
// "Return |r|."
return r;
}
Request* Request::Create(ScriptState* script_state,
const RequestInfo& input,
const Dictionary& init,
ExceptionState& exception_state) {
DCHECK(!input.IsNull());
if (input.IsUSVString())
return Create(script_state, input.GetAsUSVString(), init, exception_state);
return Create(script_state, input.GetAsRequest(), init, exception_state);
}
Request* Request::Create(ScriptState* script_state,
const String& input,
ExceptionState& exception_state) {
return Create(script_state, input, Dictionary(), exception_state);
}
Request* Request::Create(ScriptState* script_state,
const String& input,
const Dictionary& init,
ExceptionState& exception_state) {
RequestInit request_init(ExecutionContext::From(script_state), init,
exception_state);
return CreateRequestWithRequestOrString(script_state, nullptr, input,
request_init, exception_state);
}
Request* Request::Create(ScriptState* script_state,
Request* input,
ExceptionState& exception_state) {
return Create(script_state, input, Dictionary(), exception_state);
}
Request* Request::Create(ScriptState* script_state,
Request* input,
const Dictionary& init,
ExceptionState& exception_state) {
RequestInit request_init(ExecutionContext::From(script_state), init,
exception_state);
return CreateRequestWithRequestOrString(script_state, input, String(),
request_init, exception_state);
}
Request* Request::Create(ScriptState* script_state, FetchRequestData* request) {
return new Request(script_state, request);
}
Request* Request::Create(ScriptState* script_state,
const WebServiceWorkerRequest& web_request) {
FetchRequestData* request =
FetchRequestData::Create(script_state, web_request);
return new Request(script_state, request);
}
Request::Request(ScriptState* script_state,
FetchRequestData* request,
Headers* headers)
: Body(ExecutionContext::From(script_state)),
request_(request),
headers_(headers) {
RefreshBody(script_state);
}
Request::Request(ScriptState* script_state, FetchRequestData* request)
: Request(script_state, request, Headers::Create(request->HeaderList())) {
headers_->SetGuard(Headers::kRequestGuard);
}
String Request::method() const {
// "The method attribute's getter must return request's method."
return request_->Method();
}
KURL Request::url() const {
return request_->Url();
}
String Request::Context() const {
// "The context attribute's getter must return request's context"
switch (request_->Context()) {
case WebURLRequest::kRequestContextUnspecified:
return "";
case WebURLRequest::kRequestContextAudio:
return "audio";
case WebURLRequest::kRequestContextBeacon:
return "beacon";
case WebURLRequest::kRequestContextCSPReport:
return "cspreport";
case WebURLRequest::kRequestContextDownload:
return "download";
case WebURLRequest::kRequestContextEmbed:
return "embed";
case WebURLRequest::kRequestContextEventSource:
return "eventsource";
case WebURLRequest::kRequestContextFavicon:
return "favicon";
case WebURLRequest::kRequestContextFetch:
return "fetch";
case WebURLRequest::kRequestContextFont:
return "font";
case WebURLRequest::kRequestContextForm:
return "form";
case WebURLRequest::kRequestContextFrame:
return "frame";
case WebURLRequest::kRequestContextHyperlink:
return "hyperlink";
case WebURLRequest::kRequestContextIframe:
return "iframe";
case WebURLRequest::kRequestContextImage:
return "image";
case WebURLRequest::kRequestContextImageSet:
return "imageset";
case WebURLRequest::kRequestContextImport:
return "import";
case WebURLRequest::kRequestContextInternal:
return "internal";
case WebURLRequest::kRequestContextLocation:
return "location";
case WebURLRequest::kRequestContextManifest:
return "manifest";
case WebURLRequest::kRequestContextObject:
return "object";
case WebURLRequest::kRequestContextPing:
return "ping";
case WebURLRequest::kRequestContextPlugin:
return "plugin";
case WebURLRequest::kRequestContextPrefetch:
return "prefetch";
case WebURLRequest::kRequestContextScript:
return "script";
case WebURLRequest::kRequestContextServiceWorker:
return "serviceworker";
case WebURLRequest::kRequestContextSharedWorker:
return "sharedworker";
case WebURLRequest::kRequestContextSubresource:
return "subresource";
case WebURLRequest::kRequestContextStyle:
return "style";
case WebURLRequest::kRequestContextTrack:
return "track";
case WebURLRequest::kRequestContextVideo:
return "video";
case WebURLRequest::kRequestContextWorker:
return "worker";
case WebURLRequest::kRequestContextXMLHttpRequest:
return "xmlhttprequest";
case WebURLRequest::kRequestContextXSLT:
return "xslt";
}
NOTREACHED();
return "";
}
String Request::referrer() const {
// "The referrer attribute's getter must return the empty string if
// request's referrer is no referrer, "about:client" if request's referrer
// is client and request's referrer, serialized, otherwise."
DCHECK_EQ(FetchRequestData::NoReferrerString(), AtomicString());
DCHECK_EQ(FetchRequestData::ClientReferrerString(),
AtomicString("about:client"));
return request_->ReferrerString();
}
String Request::getReferrerPolicy() const {
switch (request_->GetReferrerPolicy()) {
case kReferrerPolicyAlways:
return "unsafe-url";
case kReferrerPolicyDefault:
return "";
case kReferrerPolicyNoReferrerWhenDowngrade:
return "no-referrer-when-downgrade";
case kReferrerPolicyNever:
return "no-referrer";
case kReferrerPolicyOrigin:
return "origin";
case kReferrerPolicyOriginWhenCrossOrigin:
return "origin-when-cross-origin";
case kReferrerPolicySameOrigin:
return "same-origin";
case kReferrerPolicyStrictOrigin:
return "strict-origin";
case kReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin:
return "strict-origin-when-cross-origin";
}
NOTREACHED();
return String();
}
String Request::mode() const {
// "The mode attribute's getter must return the value corresponding to the
// first matching statement, switching on request's mode:"
switch (request_->Mode()) {
case WebURLRequest::kFetchRequestModeSameOrigin:
return "same-origin";
case WebURLRequest::kFetchRequestModeNoCORS:
return "no-cors";
case WebURLRequest::kFetchRequestModeCORS:
case WebURLRequest::kFetchRequestModeCORSWithForcedPreflight:
return "cors";
case WebURLRequest::kFetchRequestModeNavigate:
return "navigate";
}
NOTREACHED();
return "";
}
String Request::credentials() const {
// "The credentials attribute's getter must return the value corresponding
// to the first matching statement, switching on request's credentials
// mode:"
switch (request_->Credentials()) {
case WebURLRequest::kFetchCredentialsModeOmit:
return "omit";
case WebURLRequest::kFetchCredentialsModeSameOrigin:
return "same-origin";
case WebURLRequest::kFetchCredentialsModeInclude:
return "include";
case WebURLRequest::kFetchCredentialsModePassword:
return "password";
}
NOTREACHED();
return "";
}
String Request::cache() const {
// "The cache attribute's getter must return request's cache mode."
switch (request_->CacheMode()) {
case mojom::FetchCacheMode::kDefault:
return "default";
case mojom::FetchCacheMode::kNoStore:
return "no-store";
case mojom::FetchCacheMode::kBypassCache:
return "reload";
case mojom::FetchCacheMode::kValidateCache:
return "no-cache";
case mojom::FetchCacheMode::kForceCache:
return "force-cache";
case mojom::FetchCacheMode::kOnlyIfCached:
return "only-if-cached";
case mojom::FetchCacheMode::kUnspecifiedOnlyIfCachedStrict:
case mojom::FetchCacheMode::kUnspecifiedForceCacheMiss:
NOTREACHED();
break;
}
NOTREACHED();
return "";
}
String Request::redirect() const {
// "The redirect attribute's getter must return request's redirect mode."
switch (request_->Redirect()) {
case WebURLRequest::kFetchRedirectModeFollow:
return "follow";
case WebURLRequest::kFetchRedirectModeError:
return "error";
case WebURLRequest::kFetchRedirectModeManual:
return "manual";
}
NOTREACHED();
return "";
}
String Request::integrity() const {
return request_->Integrity();
}
Request* Request::clone(ScriptState* script_state,
ExceptionState& exception_state) {
if (IsBodyLocked() || bodyUsed()) {
exception_state.ThrowTypeError("Request body is already used");
return nullptr;
}
FetchRequestData* request = request_->Clone(script_state);
RefreshBody(script_state);
Headers* headers = Headers::Create(request->HeaderList());
headers->SetGuard(headers_->GetGuard());
return new Request(script_state, request, headers);
}
FetchRequestData* Request::PassRequestData(ScriptState* script_state) {
DCHECK(!bodyUsed());
FetchRequestData* data = request_->Pass(script_state);
RefreshBody(script_state);
// |data|'s buffer('s js wrapper) has no retainer, but it's OK because
// the only caller is the fetch function and it uses the body buffer
// immediately.
return data;
}
bool Request::HasBody() const {
return BodyBuffer();
}
void Request::PopulateWebServiceWorkerRequest(
WebServiceWorkerRequest& web_request) const {
web_request.SetMethod(method());
web_request.SetMode(request_->Mode());
web_request.SetCredentialsMode(request_->Credentials());
web_request.SetCacheMode(request_->CacheMode());
web_request.SetRedirectMode(request_->Redirect());
web_request.SetIntegrity(request_->Integrity());
web_request.SetRequestContext(request_->Context());
// Strip off the fragment part of URL. So far, all users of
// WebServiceWorkerRequest expect the fragment to be excluded.
KURL url(request_->Url());
if (request_->Url().HasFragmentIdentifier())
url.RemoveFragmentIdentifier();
web_request.SetURL(url);
const FetchHeaderList* header_list = headers_->HeaderList();
for (const auto& header : header_list->List()) {
web_request.AppendHeader(header.first, header.second);
}
web_request.SetReferrer(
request_->ReferrerString(),
static_cast<WebReferrerPolicy>(request_->GetReferrerPolicy()));
// FIXME: How can we set isReload properly? What is the correct place to load
// it in to the Request object? We should investigate the right way to plumb
// this information in to here.
}
String Request::MimeType() const {
return request_->MimeType();
}
String Request::ContentType() const {
String result;
request_->HeaderList()->Get(HTTPNames::Content_Type, result);
return result;
}
void Request::RefreshBody(ScriptState* script_state) {
v8::Local<v8::Value> request = ToV8(this, script_state);
if (request.IsEmpty()) {
// |toV8| can return an empty handle when the worker is terminating.
// We don't want the renderer to crash in such cases.
// TODO(yhirano): Delete this block after the graceful shutdown
// mechanism is introduced.
return;
}
DCHECK(request->IsObject());
v8::Local<v8::Value> body_buffer = ToV8(this->BodyBuffer(), script_state);
V8PrivateProperty::GetInternalBodyBuffer(script_state->GetIsolate())
.Set(request.As<v8::Object>(), body_buffer);
}
void Request::Trace(blink::Visitor* visitor) {
Body::Trace(visitor);
visitor->Trace(request_);
visitor->Trace(headers_);
}
} // namespace blink