OLD | NEW |
---|---|
(Empty) | |
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 "modules/serviceworkers/Headers.h" | |
7 | |
8 #include "bindings/v8/Dictionary.h" | |
9 #include "bindings/v8/ExceptionState.h" | |
10 #include "core/fetch/CrossOriginAccessControl.h" | |
11 #include "core/xml/XMLHttpRequest.h" | |
12 #include "modules/serviceworkers/HeadersForEachCallback.h" | |
13 #include "wtf/NotFound.h" | |
14 #include "wtf/PassRefPtr.h" | |
15 #include "wtf/RefPtr.h" | |
16 #include "wtf/text/WTFString.h" | |
17 | |
18 namespace WebCore { | |
19 | |
20 PassRefPtr<Headers> Headers::create() | |
21 { | |
22 return adoptRef(new Headers); | |
23 } | |
24 | |
25 PassRefPtr<Headers> Headers::create(ExceptionState&) | |
26 { | |
27 return create(); | |
28 } | |
29 | |
30 PassRefPtr<Headers> Headers::create(const Headers* input, ExceptionState& except ionState) | |
31 { | |
32 // The Headers(init) constructor, when invoked, must run these steps: | |
33 // 1. Let headers be a new Headers object. | |
34 RefPtr<Headers> headers = create(); | |
35 // 2. If init is given, fill headers with init. Rethrow any exception. | |
36 headers->fillWith(input, exceptionState); | |
37 // 3. Return headers. | |
38 return headers.release(); | |
39 } | |
40 | |
41 PassRefPtr<Headers> Headers::create(const Dictionary& input, ExceptionState& exc eptionState) | |
42 { | |
43 // The Headers(init) constructor, when invoked, must run these steps: | |
44 // 1. Let headers be a new Headers object. | |
45 RefPtr<Headers> headers = create(); | |
46 // 2. If init is given, fill headers with init. Rethrow any exception. | |
47 headers->fillWith(input, exceptionState); | |
48 // 3. Return headers. | |
49 return headers.release(); | |
50 } | |
51 | |
52 // Called when creating Request. | |
53 PassRefPtr<Headers> Headers::create(FetchHeaderList* headerList) | |
54 { | |
55 return adoptRef(new Headers(headerList)); | |
56 } | |
57 | |
58 Headers::~Headers() | |
59 { | |
60 } | |
61 | |
62 unsigned long Headers::size() const | |
63 { | |
64 return m_headerList->size(); | |
65 } | |
66 | |
67 void Headers::append(const String& name, const String& value, ExceptionState& ex ceptionState) | |
68 { | |
69 // To append a name/value (name/value) pair to a Headers object (headers), | |
70 // run these steps: | |
71 // 1. If name is not a name or value is not a value, throw a TypeError. | |
72 if (!FetchHeaderList::isValidHeaderName(name)) { | |
73 exceptionState.throwTypeError("Invalid name"); | |
74 return; | |
75 } | |
76 if (!FetchHeaderList::isValidHeaderValue(value)) { | |
77 exceptionState.throwTypeError("Invalid value"); | |
78 return; | |
79 } | |
80 // 2. If guard is immutable, throw a TypeError. | |
81 if (m_guard == ImmutableGuard) { | |
82 exceptionState.throwTypeError("Headers are immutable"); | |
83 return; | |
84 } | |
85 // 3. Otherwise, if guard is request and name is a forbidden header name, | |
86 // return. | |
87 if (m_guard == RequestGuard && FetchHeaderList::isForbiddenHeaderName(name)) | |
88 return; | |
89 // 4. Otherwise, if guard is request-no-CORS and name/value is not a simple | |
90 // header, return. | |
91 if (m_guard == RequestNoCORSGuard && !FetchHeaderList::isSimpleHeader(name, value)) | |
92 return; | |
93 // 5. Otherwise, if guard is response and name is a forbidden response | |
94 // header name, return. | |
95 if (m_guard == ResponseGuard && FetchHeaderList::isForbiddenResponseHeaderNa me(name)) | |
96 return; | |
97 // 6. Append name/value to header list. | |
98 m_headerList->append(name, value); | |
99 } | |
100 | |
101 void Headers::remove(const String& name, ExceptionState& exceptionState) | |
102 { | |
103 // The delete(name) method, when invoked, must run these steps: | |
104 // 1. If name is not a name, throw a TypeError. | |
105 if (!FetchHeaderList::isValidHeaderName(name)) { | |
106 exceptionState.throwTypeError("Invalid name"); | |
107 return; | |
108 } | |
109 // 2. If guard is immutable, throw a TypeError. | |
110 if (m_guard == ImmutableGuard) { | |
111 exceptionState.throwTypeError("Headers are immutable"); | |
112 return; | |
113 } | |
114 // 3. Otherwise, if guard is request and name is a forbidden header name, | |
115 // return. | |
116 if (m_guard == RequestGuard && FetchHeaderList::isForbiddenHeaderName(name)) | |
117 return; | |
118 // 4. Otherwise, if guard is request-no-CORS and name/value is not a simple | |
119 // header, return. | |
120 if (m_guard == RequestNoCORSGuard && !FetchHeaderList::isSimpleHeader(name, "invalid")) | |
121 return; | |
122 // 5. Otherwise, if guard is response and name is a forbidden response | |
123 // header name, return. | |
124 if (m_guard == ResponseGuard && FetchHeaderList::isForbiddenResponseHeaderNa me(name)) | |
125 return; | |
126 // 6. Remove all headers whose name is name from header list. | |
127 m_headerList->remove(name); | |
128 } | |
129 | |
130 String Headers::get(const String& name, ExceptionState& exceptionState) | |
131 { | |
132 // The get(name) method, when invoked, must run these steps: | |
133 // 1. If name is not a name, throw a TypeError. | |
134 if (!FetchHeaderList::isValidHeaderName(name)) { | |
135 exceptionState.throwTypeError("Invalid name"); | |
136 return String(); | |
137 } | |
138 // 2. Return the value of the first header in header list whose name is | |
139 // name, and null otherwise. | |
140 String result; | |
141 if (m_headerList->get(name, result)) | |
142 return result; | |
143 return String(); | |
falken
2014/06/27 02:08:06
could this just be return result like getAll below
horo
2014/06/27 04:19:10
Done.
| |
144 } | |
145 | |
146 Vector<String> Headers::getAll(const String& name, ExceptionState& exceptionStat e) | |
147 { | |
148 // The getAll(name) method, when invoked, must run these steps: | |
149 // 1. If name is not a name, throw a TypeError. | |
150 if (!FetchHeaderList::isValidHeaderName(name)) { | |
151 exceptionState.throwTypeError("Invalid name"); | |
152 return Vector<String>(); | |
153 } | |
154 // 2. Return the values of all headers in header list whose name is name, in | |
155 // list order, and the empty sequence otherwise. | |
156 Vector<String> result; | |
157 m_headerList->getAll(name, result); | |
158 return result; | |
159 } | |
160 | |
161 bool Headers::has(const String& name, ExceptionState& exceptionState) | |
162 { | |
163 // The has(name) method, when invoked, must run these steps: | |
164 // 1. If name is not a name, throw a TypeError. | |
165 if (!FetchHeaderList::isValidHeaderName(name)) { | |
166 exceptionState.throwTypeError("Invalid name"); | |
167 return false; | |
168 } | |
169 // 2. Return true if there is a header in header list whose name is name, | |
170 // and false otherwise. | |
171 return m_headerList->has(name); | |
172 } | |
173 | |
174 void Headers::set(const String& name, const String& value, ExceptionState& excep tionState) | |
175 { | |
176 // The set(name, value) method, when invoked, must run these steps: | |
177 // 1. If name is not a name or value is not a value, throw a TypeError. | |
178 if (!FetchHeaderList::isValidHeaderName(name)) { | |
179 exceptionState.throwTypeError("Invalid name"); | |
180 return; | |
181 } | |
182 if (!FetchHeaderList::isValidHeaderValue(value)) { | |
183 exceptionState.throwTypeError("Invalid value"); | |
184 return; | |
185 } | |
186 // 2. If guard is immutable, throw a TypeError. | |
187 if (m_guard == ImmutableGuard) { | |
188 exceptionState.throwTypeError("Headers are immutable"); | |
189 return; | |
190 } | |
191 // 3. Otherwise, if guard is request and name is a forbidden header name, | |
192 // return. | |
193 if (m_guard == RequestGuard && FetchHeaderList::isForbiddenHeaderName(name)) | |
194 return; | |
195 // 4. Otherwise, if guard is request-no-CORS and name/value is not a simple | |
196 // header, return. | |
197 if (m_guard == RequestNoCORSGuard && !FetchHeaderList::isSimpleHeader(name, value)) | |
198 return; | |
199 // 5. Otherwise, if guard is response and name is a forbidden response | |
200 // header name, return. | |
201 if (m_guard == ResponseGuard && FetchHeaderList::isForbiddenResponseHeaderNa me(name)) | |
202 return; | |
203 // 6. Set name/value in header list. | |
204 m_headerList->set(name, value); | |
205 } | |
206 | |
207 void Headers::forEach(PassOwnPtr<HeadersForEachCallback> callback, ScriptValue& thisArg) | |
208 { | |
209 forEachInternal(callback, &thisArg); | |
210 } | |
211 | |
212 void Headers::forEach(PassOwnPtr<HeadersForEachCallback> callback) | |
213 { | |
214 forEachInternal(callback, 0); | |
215 } | |
216 | |
217 void Headers::fillWith(const Headers* headers, ExceptionState& exceptionState) | |
218 { | |
219 ASSERT(m_headerList->size() == 0); | |
220 // To fill a Headers object (headers) with a given object (object), run | |
221 // these steps: | |
222 // 1. If object is a Headers object, copy its header list as headerListCopy | |
223 // and then for each header in headerListCopy, retaining order, append | |
224 // header's name/header's value to headers. Rethrow any exception. | |
225 for (size_t i = 0; i < headers->m_headerList->list().size(); ++i) { | |
226 append(headers->m_headerList->list()[i]->first, headers->m_headerList->l ist()[i]->second, exceptionState); | |
227 if (exceptionState.hadException()) | |
228 return; | |
229 } | |
230 } | |
231 | |
232 void Headers::fillWith(const Dictionary& dictionary, ExceptionState& exceptionSt ate) | |
233 { | |
234 ASSERT(m_headerList->size() == 0); | |
235 Vector<String> keys; | |
236 dictionary.getOwnPropertyNames(keys); | |
237 if (keys.size() == 0) | |
238 return; | |
239 | |
240 // Because of the restrictions in IDL compiler of blink we recieve | |
241 // sequence<sequence<ByteString>> as a Dictionary, which is a type of union | |
242 // type of HeadersInit defined in the spec. | |
243 // http://fetch.spec.whatwg.org/#headers-class | |
244 // FIXME: Support sequence<sequence<ByteString>>. | |
245 Vector<String> keyValuePair; | |
246 if (dictionary.get(keys[0], keyValuePair)) { | |
247 // 2. Otherwise, if object is a sequence, then for each header in | |
248 // object, run these substeps: | |
249 // 1. If header does not contain two items, throw a TypeError. | |
falken
2014/06/27 02:08:06
spec is a unclear if this means exactly two items
horo
2014/06/27 04:19:10
Thank you.
| |
250 // 2. Append header's first item/header's second time to headers. | |
falken
2014/06/27 02:08:06
spec has a typo: s/time/item. I filed a spec bug.
horo
2014/06/27 04:19:10
Done.
| |
251 // Rethrow any exception. | |
252 for (size_t i = 0; i < keys.size(); ++i) { | |
253 // We've already got the keyValuePair for key[0]. | |
254 if (i > 0) { | |
255 if (!dictionary.get(keys[i], keyValuePair)) { | |
256 exceptionState.throwTypeError("Invalid value"); | |
257 return; | |
258 } | |
259 } | |
260 if (keyValuePair.size() < 2) { | |
261 exceptionState.throwTypeError("Invalid value"); | |
262 return; | |
263 } | |
264 append(keyValuePair[0], keyValuePair[1], exceptionState); | |
265 if (exceptionState.hadException()) | |
266 return; | |
267 keyValuePair.clear(); | |
268 } | |
269 return; | |
270 } | |
271 // 3. Otherwise, if object is an open-ended dictionary, then for each header | |
272 // in object, run these substeps: | |
273 // 1. Set header's key to header's key, converted to ByteString. Rethrow | |
274 // any exception. | |
275 // 2. Append header's key/header's value to headers. Rethrow any | |
276 // exception. | |
277 // FIXME: Support OpenEndedDictionary<ByteString>. | |
278 for (size_t i = 0; i < keys.size(); ++i) { | |
279 String value; | |
280 if (!dictionary.get(keys[i], value)) { | |
281 exceptionState.throwTypeError("Invalid value"); | |
282 return; | |
283 } | |
284 append(keys[i], value, exceptionState); | |
285 if (exceptionState.hadException()) | |
286 return; | |
287 } | |
288 } | |
289 | |
290 Headers::Headers() | |
291 : m_headerList(FetchHeaderList::create()) | |
292 , m_guard(NoneGuard) | |
293 { | |
294 ScriptWrappable::init(this); | |
295 } | |
296 | |
297 // Called when creating Request or Responce. | |
298 Headers::Headers(FetchHeaderList* headerList) | |
299 : m_headerList(headerList) | |
300 , m_guard(NoneGuard) | |
301 { | |
302 ScriptWrappable::init(this); | |
303 } | |
304 | |
305 void Headers::forEachInternal(PassOwnPtr<HeadersForEachCallback> callback, Scrip tValue* thisArg) | |
306 { | |
307 TrackExceptionState exceptionState; | |
308 for (size_t i = 0; i < m_headerList->size(); ++i) { | |
309 if (thisArg) | |
310 callback->handleItem(*thisArg, m_headerList->list()[i]->second, m_he aderList->list()[i]->first, this); | |
311 else | |
312 callback->handleItem(m_headerList->list()[i]->second, m_headerList-> list()[i]->first, this); | |
313 if (exceptionState.hadException()) | |
314 break; | |
315 } | |
316 } | |
317 | |
318 } // namespace WebCore | |
OLD | NEW |