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 "Response.h"
7
8#include "bindings/core/v8/Dictionary.h"
9#include "bindings/core/v8/ExceptionState.h"
10#include "core/fileapi/Blob.h"
11#include "modules/serviceworkers/ResponseInit.h"
12#include "public/platform/WebServiceWorkerResponse.h"
13#include "wtf/ArrayBuffer.h"
14#include "wtf/ArrayBufferView.h"
15#include "wtf/RefPtr.h"
16
17namespace blink {
18
19namespace {
20
21FetchResponseData* createFetchResponseDataFromWebResponse(const WebServiceWorkerResponse& webResponse)
22{
23    FetchResponseData* response = 0;
24    if (200 <= webResponse.status() && webResponse.status() < 300)
25        response = FetchResponseData::create();
26    else
27        response = FetchResponseData::createNetworkErrorResponse();
28
29    response->setURL(webResponse.url());
30    response->setStatus(webResponse.status());
31    response->setStatusMessage(webResponse.statusText());
32    return response;
33}
34
35Headers* createHeadersFromWebResponse(const WebServiceWorkerResponse& webResponse)
36{
37    Headers* headers = Headers::create();
38    TrackExceptionState exceptionState;
39    for (HTTPHeaderMap::const_iterator i = webResponse.headers().begin(), end = webResponse.headers().end(); i != end; ++i) {
40        headers->append(i->key, i->value, exceptionState);
41        if (exceptionState.hadException())
42            return 0;
43    }
44    return headers;
45}
46
47}
48
49Response* Response::create(ExecutionContext* context, Blob* body, const Dictionary& responseInit, ExceptionState& exceptionState)
50{
51    return create(context, body, ResponseInit(responseInit), exceptionState);
52}
53
54Response* Response::create(ExecutionContext* context, const String& body, const Dictionary& responseInit, ExceptionState& exceptionState)
55{
56    OwnPtr<BlobData> blobData = BlobData::create();
57    blobData->appendText(body, false);
58    // "Set |Content-Type| to `text/plain;charset=UTF-8`."
59    blobData->setContentType("text/plain;charset=UTF-8");
60    const long long length = blobData->length();
61    RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), length));
62    return create(context, blob.get(), ResponseInit(responseInit), exceptionState);
63}
64
65Response* Response::create(ExecutionContext* context, const ArrayBuffer* body, const Dictionary& responseInit, ExceptionState& exceptionState)
66{
67    OwnPtr<BlobData> blobData = BlobData::create();
68    blobData->appendArrayBuffer(body);
69    const long long length = blobData->length();
70    RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), length));
71    return create(context, blob.get(), ResponseInit(responseInit), exceptionState);
72}
73
74Response* Response::create(ExecutionContext* context, const ArrayBufferView* body, const Dictionary& responseInit, ExceptionState& exceptionState)
75{
76    OwnPtr<BlobData> blobData = BlobData::create();
77    blobData->appendArrayBufferView(body);
78    const long long length = blobData->length();
79    RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), length));
80    return create(context, blob.get(), ResponseInit(responseInit), exceptionState);
81}
82
83Response* Response::create(ExecutionContext* context, Blob* body, const ResponseInit& responseInit, ExceptionState& exceptionState)
84{
85    // "1. If |init|'s status member is not in the range 200 to 599, throw a
86    // RangeError."
87    if (responseInit.status < 200 || 599 < responseInit.status) {
88        exceptionState.throwRangeError("Invalid status");
89        return 0;
90    }
91
92    // FIXME: "2. If |init|'s statusText member does not match the Reason-Phrase
93    //        token production, throw a TypeError."
94
95    // "3. Let |r| be a new Response object, associated with a new response,
96    // Headers object, and Body object."
97    Response* r = new Response(context);
98    r->suspendIfNeeded();
99
100    // "4. Set |r|'s response's status to |init|'s status member."
101    r->m_response->setStatus(responseInit.status);
102
103    // "5. Set |r|'s response's status message to |init|'s statusText member."
104    r->m_response->setStatusMessage(AtomicString(responseInit.statusText));
105
106    // "6. If |init|'s headers member is present, run these substeps:"
107    if (responseInit.headers) {
108        // "1. Empty |r|'s response's header list."
109        r->m_response->headerList()->clearList();
110        // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow
111        // any exceptions."
112        r->m_headers->fillWith(responseInit.headers.get(), exceptionState);
113        if (exceptionState.hadException())
114            return 0;
115    } else if (!responseInit.headersDictionary.isUndefinedOrNull()) {
116        // "1. Empty |r|'s response's header list."
117        r->m_response->headerList()->clearList();
118        // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow
119        // any exceptions."
120        r->m_headers->fillWith(responseInit.headersDictionary, exceptionState);
121        if (exceptionState.hadException())
122            return 0;
123    }
124    // "7. If body is given, run these substeps:"
125    if (body) {
126        // "1. Let |stream| and |Content-Type| be the result of extracting body."
127        // "2. Set |r|'s response's body to |stream|."
128        // "3. If |Content-Type| is non-null and |r|'s response's header list
129        // contains no header named `Content-Type`, append `Content-Type`/
130        // |Content-Type| to |r|'s response's header list."
131        r->m_response->setBlobDataHandle(body->blobDataHandle());
132        if (!body->type().isNull() && !r->m_response->headerList()->has("Content-Type"))
133            r->m_response->headerList()->append("Content-Type", body->type());
134    }
135
136    // FIXME: "8. Set |r|'s MIME type to the result of extracting a MIME type
137    //        from |r|'s response's header list."
138
139    // "9. Return |r|."
140    return r;
141}
142
143Response* Response::create(ExecutionContext* context, FetchResponseData* response)
144{
145    Response* r = new Response(context, response);
146    r->suspendIfNeeded();
147    return r;
148}
149
150Response* Response::create(ExecutionContext* context, const WebServiceWorkerResponse& webResponse)
151{
152    Response* r = new Response(context, webResponse);
153    r->suspendIfNeeded();
154    return r;
155}
156
157Response* Response::create(const Response& copyFrom)
158{
159    Response* r = new Response(copyFrom);
160    r->suspendIfNeeded();
161    return r;
162}
163
164String Response::type() const
165{
166    // "The type attribute's getter must return response's type."
167    switch (m_response->type()) {
168    case FetchResponseData::BasicType:
169        return "basic";
170    case FetchResponseData::CORSType:
171        return "cors";
172    case FetchResponseData::DefaultType:
173        return "default";
174    case FetchResponseData::ErrorType:
175        return "error";
176    case FetchResponseData::OpaqueType:
177        return "opaque";
178    }
179    ASSERT_NOT_REACHED();
180    return "";
181}
182
183String Response::url() const
184{
185    // "The url attribute's getter must return the empty string if response's
186    // url is null and response's url, serialized with the exclude fragment
187    // flag set, otherwise."
188    if (!m_response->url().hasFragmentIdentifier())
189        return m_response->url();
190    KURL url(m_response->url());
191    url.removeFragmentIdentifier();
192    return url;
193}
194
195unsigned short Response::status() const
196{
197    // "The status attribute's getter must return response's status."
198    return m_response->status();
199}
200
201String Response::statusText() const
202{
203    // "The statusText attribute's getter must return response's status message."
204    return m_response->statusMessage();
205}
206
207Headers* Response::headers() const
208{
209    // "The headers attribute's getter must return the associated Headers object."
210    return m_headers;
211}
212
213Response* Response::clone() const
214{
215    return Response::create(*this);
216}
217
218void Response::populateWebServiceWorkerResponse(WebServiceWorkerResponse& response)
219{
220    m_response->populateWebServiceWorkerResponse(response);
221}
222
223Response::Response(ExecutionContext* context)
224    : Body(context)
225    , m_response(FetchResponseData::create())
226    , m_headers(Headers::create(m_response->headerList()))
227{
228    m_headers->setGuard(Headers::ResponseGuard);
229}
230
231Response::Response(const Response& copy_from)
232    : Body(copy_from)
233    , m_response(copy_from.m_response)
234    , m_headers(copy_from.m_headers->createCopy())
235{
236}
237
238Response::Response(ExecutionContext* context, FetchResponseData* response)
239    : Body(context)
240    , m_response(response)
241    , m_headers(Headers::create(m_response->headerList()))
242{
243    m_headers->setGuard(Headers::ResponseGuard);
244}
245
246// FIXME: Handle response body data.
247Response::Response(ExecutionContext* context, const WebServiceWorkerResponse& webResponse)
248    : Body(context)
249    , m_response(createFetchResponseDataFromWebResponse(webResponse))
250    , m_headers(createHeadersFromWebResponse(webResponse))
251{
252    m_headers->setGuard(Headers::ResponseGuard);
253}
254
255PassRefPtr<BlobDataHandle> Response::blobDataHandle()
256{
257    return m_response->blobDataHandle();
258}
259
260void Response::trace(Visitor* visitor)
261{
262    Body::trace(visitor);
263    visitor->trace(m_response);
264    visitor->trace(m_headers);
265}
266
267} // namespace blink
268