Chromium Code Reviews| 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 |