Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / serviceworkers / Request.cpp
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.
4
5 #include "config.h"
6 #include "Request.h"
7
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"
22
23 namespace blink {
24
25 namespace {
26
27 class FillWebRequestHeaders : public HeadersForEachCallback {
28 public:
29     FillWebRequestHeaders(WebServiceWorkerRequest* webRequest) : m_webRequest(webRequest) { }
30
31     virtual bool handleItem(ScriptValue, const String&, const String&, Headers*)
32     {
33         ASSERT_NOT_REACHED();
34         return false;
35     }
36
37     virtual bool handleItem(const String& value, const String& key, Headers*)
38     {
39         m_webRequest->appendHeader(key, value);
40         return true;
41     }
42
43 private:
44     WebServiceWorkerRequest* m_webRequest;
45 };
46
47 } // namespace
48
49 Request* Request::createRequestWithRequestData(ExecutionContext* context, FetchRequestData* request, const RequestInit& init, FetchRequestData::Mode mode, FetchRequestData::Credentials credentials, ExceptionState& exceptionState)
50 {
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);
60     } else {
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);
64     }
65
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
69     // |credentials|.
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);
76     } else {
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);
80     }
81
82     // "11. If |init|'s method member is present, let |method| be it and run
83     // these substeps:"
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.");
88             return 0;
89         }
90         if (!isValidHTTPToken(init.method)) {
91             exceptionState.throwTypeError("'" + init.method + "' is not a valid HTTP method.");
92             return 0;
93         }
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)));
97     }
98     // "12. Let |r| be a new Request object associated with |request|, Headers
99     // object."
100     Request* r = Request::create(context, request);
101
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
104     // headers member."
105     // We don't create a copy of r's Headers object when init's headers member
106     // is present.
107     Headers* headers = 0;
108     if (!init.headers && init.headersDictionary.isUndefinedOrNull()) {
109         headers = r->headers()->createCopy();
110     }
111     // "15. Empty |r|'s request's header list."
112     r->clearHeaderList();
113
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
117         // TypeError."
118         if (!FetchUtils::isSimpleMethod(r->request()->method())) {
119             exceptionState.throwTypeError("'" + r->request()->method() + "' is unsupported in no-cors mode.");
120             return 0;
121         }
122         // "Set |r|'s Headers object's guard to |request-no-CORS|.
123         r->headers()->setGuard(Headers::RequestNoCORSGuard);
124     }
125
126     // "17. Fill |r|'s Headers object with |headers|. Rethrow any exceptions."
127     if (init.headers) {
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);
132     } else {
133         ASSERT(headers);
134         r->headers()->fillWith(headers, exceptionState);
135     }
136     if (exceptionState.hadException())
137         return 0;
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
146         // exception."
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);
150         }
151         if (exceptionState.hadException())
152             return 0;
153     }
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.
157
158     // "20. Return |r|."
159     return r;
160 }
161
162 Request* Request::create(ExecutionContext* context, const String& input, ExceptionState& exceptionState)
163 {
164     return create(context, input, Dictionary(), exceptionState);
165 }
166
167 Request* Request::create(ExecutionContext* context, const String& input, const Dictionary& init, ExceptionState& exceptionState)
168 {
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");
181         return 0;
182     }
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);
188 }
189
190 Request* Request::create(ExecutionContext* context, Request* input, ExceptionState& exceptionState)
191 {
192     return create(context, input, Dictionary(), exceptionState);
193 }
194
195 Request* Request::create(ExecutionContext* context, Request* input, const Dictionary& init, ExceptionState& exceptionState)
196 {
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.");
203         return 0;
204     }
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);
217 }
218
219 Request* Request::create(ExecutionContext* context, FetchRequestData* request)
220 {
221     Request* r = new Request(context, request);
222     r->suspendIfNeeded();
223     return r;
224 }
225
226 Request::Request(ExecutionContext* context, FetchRequestData* request)
227     : Body(context)
228     , m_request(request)
229     , m_headers(Headers::create(m_request->headerList()))
230 {
231     m_headers->setGuard(Headers::RequestGuard);
232 }
233
234 Request* Request::create(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
235 {
236     Request* r = new Request(context, webRequest);
237     r->suspendIfNeeded();
238     return r;
239 }
240
241 Request* Request::create(const Request& copyFrom)
242 {
243     Request* r = new Request(copyFrom);
244     r->suspendIfNeeded();
245     return r;
246 }
247
248 Request::Request(ExecutionContext* context, const WebServiceWorkerRequest& webRequest)
249     : Body(context)
250     , m_request(FetchRequestData::create(webRequest))
251     , m_headers(Headers::create(m_request->headerList()))
252 {
253     m_headers->setGuard(Headers::RequestGuard);
254 }
255
256 Request::Request(const Request& copy_from)
257     : Body(copy_from)
258     , m_request(copy_from.m_request)
259     , m_headers(copy_from.m_headers->createCopy())
260 {
261 }
262
263
264 String Request::method() const
265 {
266     // "The method attribute's getter must return request's method."
267     return m_request->method();
268 }
269
270 String Request::url() const
271 {
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();
277     return url;
278 }
279
280 String Request::referrer() const
281 {
282     // "The referrer attribute's getter must return the empty string if
283     // request's referrer is none, and request's referrer, serialized,
284     // otherwise."
285     return m_request->referrer().referrer().referrer;
286 }
287
288 String Request::mode() const
289 {
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:
296         return "no-cors";
297     case FetchRequestData::CORSMode:
298     case FetchRequestData::CORSWithForcedPreflight:
299         return "cors";
300     }
301     ASSERT_NOT_REACHED();
302     return "";
303 }
304
305 String Request::credentials() const
306 {
307     // "The credentials attribute's getter must return the value corresponding
308     // to the first matching statement, switching on request's credentials
309     // mode:"
310     switch (m_request->credentials()) {
311     case FetchRequestData::OmitCredentials:
312         return "omit";
313     case FetchRequestData::SameOriginCredentials:
314         return "same-origin";
315     case FetchRequestData::IncludeCredentials:
316         return "include";
317     }
318     ASSERT_NOT_REACHED();
319     return "";
320 }
321
322 Request* Request::clone() const
323 {
324     return Request::create(*this);
325 }
326
327 void Request::populateWebServiceWorkerRequest(WebServiceWorkerRequest& webRequest)
328 {
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.
335 }
336
337 void Request::setBodyBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle)
338 {
339     m_request->setBlobDataHandle(blobDataHandle);
340 }
341
342 void Request::clearHeaderList()
343 {
344     m_request->headerList()->clearList();
345 }
346
347 PassRefPtr<BlobDataHandle> Request::blobDataHandle()
348 {
349     return m_request->blobDataHandle();
350 }
351
352 void Request::trace(Visitor* visitor)
353 {
354     Body::trace(visitor);
355     visitor->trace(m_request);
356     visitor->trace(m_headers);
357 }
358
359 } // namespace blink