1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "bindings/core/v8/Dictionary.h"
9 #include "core/dom/ExecutionContext.h"
10 #include "core/fetch/FetchUtils.h"
11 #include "core/fetch/ResourceLoaderOptions.h"
12 #include "core/loader/ThreadableLoader.h"
13 #include "core/xml/XMLHttpRequest.h"
14 #include "modules/serviceworkers/FetchManager.h"
15 #include "modules/serviceworkers/HeadersForEachCallback.h"
16 #include "modules/serviceworkers/RequestInit.h"
17 #include "platform/NotImplemented.h"
18 #include "platform/network/HTTPParsers.h"
19 #include "platform/network/ResourceRequest.h"
20 #include "platform/weborigin/Referrer.h"
21 #include "public/platform/WebServiceWorkerRequest.h"
27 class FillWebRequestHeaders : public HeadersForEachCallback {
29 FillWebRequestHeaders(WebServiceWorkerRequest* webRequest) : m_webRequest(webRequest) { }
31 virtual bool handleItem(ScriptValue, const String&, const String&, Headers*)
37 virtual bool handleItem(const String& value, const String& key, Headers*)
39 m_webRequest->appendHeader(key, value);
44 WebServiceWorkerRequest* m_webRequest;
49 Request* Request::createRequestWithRequestData(ExecutionContext* context, FetchRequestData* request, const RequestInit& init, FetchRequestData::Mode mode, FetchRequestData::Credentials credentials, ExceptionState& exceptionState)
51 // "7. Let |mode| be |init|'s mode member if it is present, and
52 // |fallbackMode| otherwise."
53 // "8. If |mode| is non-null, set |request|'s mode to |mode|."
54 if (init.mode == "same-origin") {
55 request->setMode(FetchRequestData::SameOriginMode);
56 } else if (init.mode == "no-cors") {
57 request->setMode(mode = FetchRequestData::NoCORSMode);
58 } else if (init.mode == "cors") {
59 request->setMode(FetchRequestData::CORSMode);
61 // Instead of using null as a special fallback value, we pass the
62 // current mode in Request::create(). So we just set here.
63 request->setMode(mode);
66 // "9. Let |credentials| be |init|'s credentials member if it is present,
67 // and |fallbackCredentials| otherwise."
68 // "10. If |credentials| is non-null, set |request|'s credentials mode to
70 if (init.credentials == "omit") {
71 request->setCredentials(FetchRequestData::OmitCredentials);
72 } else if (init.credentials == "same-origin") {
73 request->setCredentials(FetchRequestData::SameOriginCredentials);
74 } else if (init.credentials == "include") {
75 request->setCredentials(FetchRequestData::IncludeCredentials);
77 // Instead of using null as a special fallback value, we pass the
78 // current credentials in Request::create(). So we just set here.
79 request->setCredentials(credentials);
82 // "11. If |init|'s method member is present, let |method| be it and run
84 if (!init.method.isEmpty()) {
85 // "1. If |method| is not a useful method, throw a TypeError."
86 if (!FetchUtils::isUsefulMethod(init.method)) {
87 exceptionState.throwTypeError("'" + init.method + "' HTTP method is unsupported.");
90 if (!isValidHTTPToken(init.method)) {
91 exceptionState.throwTypeError("'" + init.method + "' is not a valid HTTP method.");
94 // FIXME: "2. Add case correction as in XMLHttpRequest?"
95 // "3. Set |request|'s method to |method|."
96 request->setMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(AtomicString(init.method)));
98 // "12. Let |r| be a new Request object associated with |request|, Headers
100 Request* r = Request::create(context, request);
102 // "13. Let |headers| be a copy of |r|'s Headers object."
103 // "14. If |init|'s headers member is present, set |headers| to |init|'s
105 // We don't create a copy of r's Headers object when init's headers member
107 Headers* headers = 0;
108 if (!init.headers && init.headersDictionary.isUndefinedOrNull()) {
109 headers = r->headers()->createCopy();
111 // "15. Empty |r|'s request's header list."
112 r->clearHeaderList();
114 // "16. If |r|'s request's mode is no CORS, run these substeps:
115 if (r->request()->mode() == FetchRequestData::NoCORSMode) {
116 // "1. If |r|'s request's method is not a simple method, throw a
118 if (!FetchUtils::isSimpleMethod(r->request()->method())) {
119 exceptionState.throwTypeError("'" + r->request()->method() + "' is unsupported in no-cors mode.");
122 // "Set |r|'s Headers object's guard to |request-no-CORS|.
123 r->headers()->setGuard(Headers::RequestNoCORSGuard);
126 // "17. Fill |r|'s Headers object with |headers|. Rethrow any exceptions."
128 ASSERT(init.headersDictionary.isUndefinedOrNull());
129 r->headers()->fillWith(init.headers.get(), exceptionState);
130 } else if (!init.headersDictionary.isUndefinedOrNull()) {
131 r->headers()->fillWith(init.headersDictionary, exceptionState);
134 r->headers()->fillWith(headers, exceptionState);
136 if (exceptionState.hadException())
138 // "18. If |init|'s body member is present, run these substeps:"
139 if (init.bodyBlobHandle) {
140 // "1. Let |stream| and |Content-Type| be the result of extracting
141 // |init|'s body member."
142 // "2. Set |r|'s request's body to |stream|."
143 // "3.If |Content-Type| is non-null and |r|'s request's header list
144 // contains no header named `Content-Type`, append
145 // `Content-Type`/|Content-Type| to |r|'s Headers object. Rethrow any
147 r->setBodyBlobHandle(init.bodyBlobHandle);
148 if (!init.bodyBlobHandle->type().isEmpty() && !r->headers()->has("Content-Type", exceptionState)) {
149 r->headers()->append("Content-Type", init.bodyBlobHandle->type(), exceptionState);
151 if (exceptionState.hadException())
154 // "19. Set |r|'s MIME type to the result of extracting a MIME type from
155 // |r|'s request's header list."
156 // FIXME: We don't have MIME type in Request object yet.
162 Request* Request::create(ExecutionContext* context, const String& input, ExceptionState& exceptionState)
164 return create(context, input, Dictionary(), exceptionState);
167 Request* Request::create(ExecutionContext* context, const String& input, const Dictionary& init, ExceptionState& exceptionState)
169 // "2. Let |request| be |input|'s associated request, if |input| is a
170 // Request object, and a new request otherwise."
171 FetchRequestData* request(FetchRequestData::create(context));
172 // "3. Set |request| to a restricted copy of itself."
173 request = request->createRestrictedCopy(context, SecurityOrigin::create(context->url()));
174 // "6. If |input| is a string, run these substeps:"
175 // "1. Let |parsedURL| be the result of parsing |input| with entry settings
176 // object's API base URL."
177 KURL parsedURL = context->completeURL(input);
178 // "2. If |parsedURL| is failure, throw a TypeError."
179 if (!parsedURL.isValid()) {
180 exceptionState.throwTypeError("Invalid URL");
183 // "3. Set |request|'s url to |parsedURL|."
184 request->setURL(parsedURL);
185 // "4. Set |fallbackMode| to CORS."
186 // "5. Set |fallbackCredentials| to omit."
187 return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), FetchRequestData::CORSMode, FetchRequestData::OmitCredentials, exceptionState);
190 Request* Request::create(ExecutionContext* context, Request* input, ExceptionState& exceptionState)
192 return create(context, input, Dictionary(), exceptionState);
195 Request* Request::create(ExecutionContext* context, Request* input, const Dictionary& init, ExceptionState& exceptionState)
197 // "1. If input is a Request object, run these substeps:"
198 // " 1. If input's used flag is set, throw a TypeError."
199 // " 2. Set input's used flag."
200 if (input->bodyUsed()) {
201 exceptionState.throwTypeError(
202 "Cannot construct a Request with a Request object that has already been used.");
205 input->setBodyUsed();
206 // "2. Let |request| be |input|'s associated request, if |input| is a
207 // Request object, and a new request otherwise."
208 // "3. Set |request| to a restricted copy of itself."
209 FetchRequestData* request(input->request()->createRestrictedCopy(context, SecurityOrigin::create(context->url())));
210 // "4. Let |fallbackMode| be null."
211 // "5. Let |fallbackCredentials| be null."
212 // Instead of using null as a special fallback value, just pass the current
213 // mode and credentials; it has the same effect.
214 const FetchRequestData::Mode currentMode = request->mode();
215 const FetchRequestData::Credentials currentCredentials = request->credentials();
216 return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), currentMode, currentCredentials, exceptionState);
219 Request* Request::create(ExecutionContext* context, FetchRequestData* request)
221 Request* r = new Request(context, request);
222 r->suspendIfNeeded();
226 Request::Request(ExecutionContext* context, FetchRequestData* request)
229 , m_headers(Headers::create(m_request->headerList()))
231 m_headers->setGuard(Headers::RequestGuard);
234 Request* Request::create(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
236 Request* r = new Request(context, webRequest);
237 r->suspendIfNeeded();
241 Request* Request::create(const Request& copyFrom)
243 Request* r = new Request(copyFrom);
244 r->suspendIfNeeded();
248 Request::Request(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
250 , m_request(FetchRequestData::create(webRequest))
251 , m_headers(Headers::create(m_request->headerList()))
253 m_headers->setGuard(Headers::RequestGuard);
256 Request::Request(const Request& copy_from)
258 , m_request(copy_from.m_request)
259 , m_headers(copy_from.m_headers->createCopy())
264 String Request::method() const
266 // "The method attribute's getter must return request's method."
267 return m_request->method();
270 String Request::url() const
272 // The url attribute's getter must return request's url, serialized with the exclude fragment flag set.
273 if (!m_request->url().hasFragmentIdentifier())
274 return m_request->url();
275 KURL url(m_request->url());
276 url.removeFragmentIdentifier();
280 String Request::referrer() const
282 // "The referrer attribute's getter must return the empty string if
283 // request's referrer is none, and request's referrer, serialized,
285 return m_request->referrer().referrer().referrer;
288 String Request::mode() const
290 // "The mode attribute's getter must return the value corresponding to the
291 // first matching statement, switching on request's mode:"
292 switch (m_request->mode()) {
293 case FetchRequestData::SameOriginMode:
294 return "same-origin";
295 case FetchRequestData::NoCORSMode:
297 case FetchRequestData::CORSMode:
298 case FetchRequestData::CORSWithForcedPreflight:
301 ASSERT_NOT_REACHED();
305 String Request::credentials() const
307 // "The credentials attribute's getter must return the value corresponding
308 // to the first matching statement, switching on request's credentials
310 switch (m_request->credentials()) {
311 case FetchRequestData::OmitCredentials:
313 case FetchRequestData::SameOriginCredentials:
314 return "same-origin";
315 case FetchRequestData::IncludeCredentials:
318 ASSERT_NOT_REACHED();
322 Request* Request::clone() const
324 return Request::create(*this);
327 void Request::populateWebServiceWorkerRequest(WebServiceWorkerRequest& webRequest)
329 webRequest.setMethod(method());
330 webRequest.setURL(m_request->url());
331 m_headers->forEach(new FillWebRequestHeaders(&webRequest));
332 webRequest.setReferrer(m_request->referrer().referrer().referrer, static_cast<WebReferrerPolicy>(m_request->referrer().referrer().referrerPolicy));
333 // 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
334 // to plumb this information in to here.
337 void Request::setBodyBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle)
339 m_request->setBlobDataHandle(blobDataHandle);
342 void Request::clearHeaderList()
344 m_request->headerList()->clearList();
347 PassRefPtr<BlobDataHandle> Request::blobDataHandle()
349 return m_request->blobDataHandle();
352 void Request::trace(Visitor* visitor)
354 Body::trace(visitor);
355 visitor->trace(m_request);
356 visitor->trace(m_headers);