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