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/core/v8/Dictionary.h" | |
9 #include "bindings/core/v8/ExceptionState.h" | |
10 #include "bindings/core/v8/V8IteratorResultValue.h" | |
11 #include "core/dom/Iterator.h" | |
12 #include "core/fetch/FetchUtils.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 blink { | |
19 | |
20 namespace { | |
21 | |
22 class HeadersIterator final : public Iterator { | |
23 public: | |
24 // Only KeyValue is currently used; the other types are to support | |
25 // Map-like iteration with entries(), keys() and values(), but this has | |
26 // not yet been added to any spec. | |
27 enum IterationType { KeyValue, Key, Value }; | |
28 | |
29 HeadersIterator(FetchHeaderList* headers, IterationType type) : m_headers(he
aders), m_type(type), m_current(0) { } | |
30 | |
31 virtual ScriptValue next(ScriptState* scriptState, ExceptionState& exception
) override | |
32 { | |
33 // FIXME: This simply advances an index and returns the next value if | |
34 // any, so if the iterated object is mutated values may be skipped. | |
35 if (m_current >= m_headers->size()) | |
36 return v8IteratorResultDone(scriptState); | |
37 | |
38 const FetchHeaderList::Header& header = m_headers->entry(m_current++); | |
39 switch (m_type) { | |
40 case KeyValue: { | |
41 Vector<String> pair; | |
42 pair.append(header.first); | |
43 pair.append(header.second); | |
44 return v8IteratorResult(scriptState, pair); | |
45 } | |
46 case Key: | |
47 return v8IteratorResult(scriptState, header.first); | |
48 case Value: | |
49 return v8IteratorResult(scriptState, header.second); | |
50 } | |
51 ASSERT_NOT_REACHED(); | |
52 return ScriptValue(); | |
53 } | |
54 | |
55 virtual ScriptValue next(ScriptState* scriptState, ScriptValue, ExceptionSta
te& exceptionState) override | |
56 { | |
57 return next(scriptState, exceptionState); | |
58 } | |
59 | |
60 virtual void trace(Visitor* visitor) | |
61 { | |
62 Iterator::trace(visitor); | |
63 visitor->trace(m_headers); | |
64 } | |
65 | |
66 private: | |
67 const Member<FetchHeaderList> m_headers; | |
68 const IterationType m_type; | |
69 size_t m_current; | |
70 }; | |
71 | |
72 } // namespace | |
73 | |
74 Headers* Headers::create() | |
75 { | |
76 return new Headers; | |
77 } | |
78 | |
79 Headers* Headers::create(ExceptionState&) | |
80 { | |
81 return create(); | |
82 } | |
83 | |
84 Headers* Headers::create(const Headers* init, ExceptionState& exceptionState) | |
85 { | |
86 // "The Headers(|init|) constructor, when invoked, must run these steps:" | |
87 // "1. Let |headers| be a new Headers object." | |
88 Headers* headers = create(); | |
89 // "2. If |init| is given, fill headers with |init|. Rethrow any exception." | |
90 headers->fillWith(init, exceptionState); | |
91 // "3. Return |headers|." | |
92 return headers; | |
93 } | |
94 | |
95 Headers* Headers::create(const Vector<Vector<String> >& init, ExceptionState& ex
ceptionState) | |
96 { | |
97 // The same steps as above. | |
98 Headers* headers = create(); | |
99 headers->fillWith(init, exceptionState); | |
100 return headers; | |
101 } | |
102 | |
103 Headers* Headers::create(const Dictionary& init, ExceptionState& exceptionState) | |
104 { | |
105 // "The Headers(|init|) constructor, when invoked, must run these steps:" | |
106 // "1. Let |headers| be a new Headers object." | |
107 Headers* headers = create(); | |
108 // "2. If |init| is given, fill headers with |init|. Rethrow any exception." | |
109 headers->fillWith(init, exceptionState); | |
110 // "3. Return |headers|." | |
111 return headers; | |
112 } | |
113 | |
114 Headers* Headers::create(FetchHeaderList* headerList) | |
115 { | |
116 return new Headers(headerList); | |
117 } | |
118 | |
119 Headers* Headers::createCopy() const | |
120 { | |
121 FetchHeaderList* headerList = m_headerList->createCopy(); | |
122 Headers* headers = create(headerList); | |
123 headers->m_guard = m_guard; | |
124 return headers; | |
125 } | |
126 | |
127 void Headers::append(const String& name, const String& value, ExceptionState& ex
ceptionState) | |
128 { | |
129 // "To append a name/value (|name|/|value|) pair to a Headers object | |
130 // (|headers|), run these steps:" | |
131 // "1. If |name| is not a name or |value| is not a value, throw a | |
132 // TypeError." | |
133 if (!FetchHeaderList::isValidHeaderName(name)) { | |
134 exceptionState.throwTypeError("Invalid name"); | |
135 return; | |
136 } | |
137 if (!FetchHeaderList::isValidHeaderValue(value)) { | |
138 exceptionState.throwTypeError("Invalid value"); | |
139 return; | |
140 } | |
141 // "2. If guard is |request|, throw a TypeError." | |
142 if (m_guard == ImmutableGuard) { | |
143 exceptionState.throwTypeError("Headers are immutable"); | |
144 return; | |
145 } | |
146 // "3. Otherwise, if guard is |request| and |name| is a forbidden header | |
147 // name, return." | |
148 if (m_guard == RequestGuard && FetchUtils::isForbiddenHeaderName(name)) | |
149 return; | |
150 // "4. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a | |
151 // simple header, return." | |
152 if (m_guard == RequestNoCORSGuard && !FetchUtils::isSimpleHeader(AtomicStrin
g(name), AtomicString(value))) | |
153 return; | |
154 // "5. Otherwise, if guard is |response| and |name| is a forbidden response | |
155 // header name, return." | |
156 if (m_guard == ResponseGuard && FetchUtils::isForbiddenResponseHeaderName(na
me)) | |
157 return; | |
158 // "6. Append |name|/|value| to header list." | |
159 m_headerList->append(name, value); | |
160 } | |
161 | |
162 void Headers::remove(const String& name, ExceptionState& exceptionState) | |
163 { | |
164 // "The delete(|name|) method, when invoked, must run these steps:" | |
165 // "1. If name is not a name, throw a TypeError." | |
166 if (!FetchHeaderList::isValidHeaderName(name)) { | |
167 exceptionState.throwTypeError("Invalid name"); | |
168 return; | |
169 } | |
170 // "2. If guard is |immutable|, throw a TypeError." | |
171 if (m_guard == ImmutableGuard) { | |
172 exceptionState.throwTypeError("Headers are immutable"); | |
173 return; | |
174 } | |
175 // "3. Otherwise, if guard is |request| and |name| is a forbidden header | |
176 // name, return." | |
177 if (m_guard == RequestGuard && FetchUtils::isForbiddenHeaderName(name)) | |
178 return; | |
179 // "4. Otherwise, if guard is |request-no-CORS| and |name|/`invalid` is not | |
180 // a simple header, return." | |
181 if (m_guard == RequestNoCORSGuard && !FetchUtils::isSimpleHeader(AtomicStrin
g(name), "invalid")) | |
182 return; | |
183 // "5. Otherwise, if guard is |response| and |name| is a forbidden response | |
184 // header name, return." | |
185 if (m_guard == ResponseGuard && FetchUtils::isForbiddenResponseHeaderName(na
me)) | |
186 return; | |
187 // "6. Delete |name| from header list." | |
188 m_headerList->remove(name); | |
189 } | |
190 | |
191 String Headers::get(const String& name, ExceptionState& exceptionState) | |
192 { | |
193 // "The get(|name|) method, when invoked, must run these steps:" | |
194 // "1. If |name| is not a name, throw a TypeError." | |
195 if (!FetchHeaderList::isValidHeaderName(name)) { | |
196 exceptionState.throwTypeError("Invalid name"); | |
197 return String(); | |
198 } | |
199 // "2. Return the value of the first header in header list whose name is | |
200 // |name|, and null otherwise." | |
201 String result; | |
202 m_headerList->get(name, result); | |
203 return result; | |
204 } | |
205 | |
206 Vector<String> Headers::getAll(const String& name, ExceptionState& exceptionStat
e) | |
207 { | |
208 // "The getAll(|name|) method, when invoked, must run these steps:" | |
209 // "1. If |name| is not a name, throw a TypeError." | |
210 if (!FetchHeaderList::isValidHeaderName(name)) { | |
211 exceptionState.throwTypeError("Invalid name"); | |
212 return Vector<String>(); | |
213 } | |
214 // "2. Return the values of all headers in header list whose name is |name|, | |
215 // in list order, and the empty sequence otherwise." | |
216 Vector<String> result; | |
217 m_headerList->getAll(name, result); | |
218 return result; | |
219 } | |
220 | |
221 bool Headers::has(const String& name, ExceptionState& exceptionState) | |
222 { | |
223 // "The has(|name|) method, when invoked, must run these steps:" | |
224 // "1. If |name| is not a name, throw a TypeError." | |
225 if (!FetchHeaderList::isValidHeaderName(name)) { | |
226 exceptionState.throwTypeError("Invalid name"); | |
227 return false; | |
228 } | |
229 // "2. Return true if there is a header in header list whose name is |name|, | |
230 // and false otherwise." | |
231 return m_headerList->has(name); | |
232 } | |
233 | |
234 void Headers::set(const String& name, const String& value, ExceptionState& excep
tionState) | |
235 { | |
236 // "The set(|name|, |value|) method, when invoked, must run these steps:" | |
237 // "1. If |name| is not a name or |value| is not a value, throw a | |
238 // TypeError." | |
239 if (!FetchHeaderList::isValidHeaderName(name)) { | |
240 exceptionState.throwTypeError("Invalid name"); | |
241 return; | |
242 } | |
243 if (!FetchHeaderList::isValidHeaderValue(value)) { | |
244 exceptionState.throwTypeError("Invalid value"); | |
245 return; | |
246 } | |
247 // "2. If guard is |immutable|, throw a TypeError." | |
248 if (m_guard == ImmutableGuard) { | |
249 exceptionState.throwTypeError("Headers are immutable"); | |
250 return; | |
251 } | |
252 // "3. Otherwise, if guard is |request| and |name| is a forbidden header | |
253 // name, return." | |
254 if (m_guard == RequestGuard && FetchUtils::isForbiddenHeaderName(name)) | |
255 return; | |
256 // "4. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a | |
257 // simple header, return." | |
258 if (m_guard == RequestNoCORSGuard && !FetchUtils::isSimpleHeader(AtomicStrin
g(name), AtomicString(value))) | |
259 return; | |
260 // "5. Otherwise, if guard is |response| and |name| is a forbidden response | |
261 // header name, return." | |
262 if (m_guard == ResponseGuard && FetchUtils::isForbiddenResponseHeaderName(na
me)) | |
263 return; | |
264 // "6. Set |name|/|value| in header list." | |
265 m_headerList->set(name, value); | |
266 } | |
267 | |
268 void Headers::fillWith(const Headers* object, ExceptionState& exceptionState) | |
269 { | |
270 ASSERT(m_headerList->size() == 0); | |
271 // "To fill a Headers object (|this|) with a given object (|object|), run | |
272 // these steps:" | |
273 // "1. If |object| is a Headers object, copy its header list as | |
274 // |headerListCopy| and then for each |header| in |headerListCopy|, | |
275 // retaining order, append header's |name|/|header|'s value to | |
276 // |headers|. Rethrow any exception." | |
277 for (size_t i = 0; i < object->m_headerList->list().size(); ++i) { | |
278 append(object->m_headerList->list()[i]->first, object->m_headerList->lis
t()[i]->second, exceptionState); | |
279 if (exceptionState.hadException()) | |
280 return; | |
281 } | |
282 } | |
283 | |
284 void Headers::fillWith(const Vector<Vector<String> >& object, ExceptionState& ex
ceptionState) | |
285 { | |
286 ASSERT(!m_headerList->size()); | |
287 // "2. Otherwise, if |object| is a sequence, then for each |header| in | |
288 // |object|, run these substeps: | |
289 // 1. If |header| does not contain exactly two items, throw a | |
290 // TypeError. | |
291 // 2. Append |header|'s first item/|header|'s second item to | |
292 // |headers|. Rethrow any exception." | |
293 for (size_t i = 0; i < object.size(); ++i) { | |
294 if (object[i].size() != 2) { | |
295 exceptionState.throwTypeError("Invalid value"); | |
296 return; | |
297 } | |
298 append(object[i][0], object[i][1], exceptionState); | |
299 if (exceptionState.hadException()) | |
300 return; | |
301 } | |
302 } | |
303 | |
304 void Headers::fillWith(const Dictionary& object, ExceptionState& exceptionState) | |
305 { | |
306 ASSERT(!m_headerList->size()); | |
307 Vector<String> keys; | |
308 object.getPropertyNames(keys); | |
309 if (!keys.size()) | |
310 return; | |
311 | |
312 // "3. Otherwise, if |object| is an open-ended dictionary, then for each | |
313 // |header| in object, run these substeps: | |
314 // 1. Set |header|'s key to |header|'s key, converted to ByteString. | |
315 // Rethrow any exception. | |
316 // 2. Append |header|'s key/|header|'s value to |headers|. Rethrow any | |
317 // exception." | |
318 // FIXME: Support OpenEndedDictionary<ByteString>. | |
319 for (size_t i = 0; i < keys.size(); ++i) { | |
320 String value; | |
321 if (!DictionaryHelper::get(object, keys[i], value)) { | |
322 exceptionState.throwTypeError("Invalid value"); | |
323 return; | |
324 } | |
325 append(keys[i], value, exceptionState); | |
326 if (exceptionState.hadException()) | |
327 return; | |
328 } | |
329 } | |
330 | |
331 Headers::Headers() | |
332 : m_headerList(FetchHeaderList::create()) | |
333 , m_guard(NoneGuard) | |
334 { | |
335 } | |
336 | |
337 Headers::Headers(FetchHeaderList* headerList) | |
338 : m_headerList(headerList) | |
339 , m_guard(NoneGuard) | |
340 { | |
341 } | |
342 | |
343 Iterator* Headers::iterator(ScriptState*, ExceptionState&) | |
344 { | |
345 return new HeadersIterator(m_headerList, HeadersIterator::KeyValue); | |
346 } | |
347 | |
348 void Headers::trace(Visitor* visitor) | |
349 { | |
350 visitor->trace(m_headerList); | |
351 } | |
352 | |
353 } // namespace blink | |
OLD | NEW |