Index: Source/modules/serviceworkers/Headers.cpp |
diff --git a/Source/modules/serviceworkers/Headers.cpp b/Source/modules/serviceworkers/Headers.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..52e4a88c4796a9358435dd64edee3cdfb6e2e25a |
--- /dev/null |
+++ b/Source/modules/serviceworkers/Headers.cpp |
@@ -0,0 +1,319 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "config.h" |
+#include "modules/serviceworkers/Headers.h" |
+ |
+#include "bindings/v8/Dictionary.h" |
+#include "bindings/v8/ExceptionState.h" |
+#include "core/fetch/CrossOriginAccessControl.h" |
+#include "core/xml/XMLHttpRequest.h" |
+#include "modules/serviceworkers/FetchManager.h" |
+#include "modules/serviceworkers/HeadersForEachCallback.h" |
+#include "wtf/NotFound.h" |
+#include "wtf/PassRefPtr.h" |
+#include "wtf/RefPtr.h" |
+#include "wtf/text/WTFString.h" |
+ |
+namespace WebCore { |
+ |
+PassRefPtr<Headers> Headers::create() |
+{ |
+ return adoptRef(new Headers); |
+} |
+ |
+PassRefPtr<Headers> Headers::create(ExceptionState&) |
+{ |
+ return create(); |
+} |
+ |
+PassRefPtr<Headers> Headers::create(const Headers* input, ExceptionState& exceptionState) |
+{ |
+ // The Headers(init) constructor, when invoked, must run these steps: |
+ // 1. Let headers be a new Headers object. |
+ RefPtr<Headers> headers = create(); |
+ // 2. If init is given, fill headers with init. Rethrow any exception. |
+ headers->fillWith(input, exceptionState); |
+ // 3. Return headers. |
+ return headers.release(); |
+} |
+ |
+PassRefPtr<Headers> Headers::create(const Dictionary& input, ExceptionState& exceptionState) |
+{ |
+ // The Headers(init) constructor, when invoked, must run these steps: |
+ // 1. Let headers be a new Headers object. |
+ RefPtr<Headers> headers = create(); |
+ // 2. If init is given, fill headers with init. Rethrow any exception. |
+ headers->fillWith(input, exceptionState); |
+ // 3. Return headers. |
+ return headers.release(); |
+} |
+ |
+// Called when creating Request. |
+PassRefPtr<Headers> Headers::create(FetchHeaderList* headerList) |
+{ |
+ return adoptRef(new Headers(headerList)); |
+} |
+ |
+Headers::~Headers() |
+{ |
+} |
+ |
+unsigned long Headers::size() const |
+{ |
+ return m_headerList->size(); |
+} |
+ |
+void Headers::append(const String& name, const String& value, ExceptionState& exceptionState) |
+{ |
+ // To append a name/value (name/value) pair to a Headers object (headers), |
+ // run these steps: |
+ // 1. If name is not a name or value is not a value, throw a TypeError. |
+ if (!FetchHeaderList::isValidHeaderName(name)) { |
+ exceptionState.throwTypeError("Invalid name"); |
+ return; |
+ } |
+ if (!FetchHeaderList::isValidHeaderValue(value)) { |
+ exceptionState.throwTypeError("Invalid value"); |
+ return; |
+ } |
+ // 2. If guard is immutable, throw a TypeError. |
+ if (m_guard == ImmutableGuard) { |
+ exceptionState.throwTypeError("Headers are immutable"); |
+ return; |
+ } |
+ // 3. Otherwise, if guard is request and name is a forbidden header name, |
+ // return. |
+ if (m_guard == RequestGuard && FetchHeaderList::isForbiddenHeaderName(name)) |
+ return; |
+ // 4. Otherwise, if guard is request-no-CORS and name/value is not a simple |
+ // header, return. |
+ if (m_guard == RequestNoCORSGuard && !FetchHeaderList::isSimpleHeader(name, value)) |
+ return; |
+ // 5. Otherwise, if guard is response and name is a forbidden response |
+ // header name, return. |
+ if (m_guard == ResponseGuard && FetchHeaderList::isForbiddenResponseHeaderName(name)) |
+ return; |
+ // 6. Append name/value to header list. |
+ m_headerList->append(name, value); |
+} |
+ |
+void Headers::remove(const String& name, ExceptionState& exceptionState) |
+{ |
+ // The delete(name) method, when invoked, must run these steps: |
+ // 1. If name is not a name, throw a TypeError. |
+ if (!FetchHeaderList::isValidHeaderName(name)) { |
+ exceptionState.throwTypeError("Invalid name"); |
+ return; |
+ } |
+ // 2. If guard is immutable, throw a TypeError. |
+ if (m_guard == ImmutableGuard) { |
+ exceptionState.throwTypeError("Headers are immutable"); |
+ return; |
+ } |
+ // 3. Otherwise, if guard is request and name is a forbidden header name, |
+ // return. |
+ if (m_guard == RequestGuard && FetchHeaderList::isForbiddenHeaderName(name)) |
+ return; |
+ // 4. Otherwise, if guard is request-no-CORS and name/value is not a simple |
+ // header, return. |
+ if (m_guard == RequestNoCORSGuard && !FetchHeaderList::isSimpleHeader(name, "invalid")) |
+ return; |
+ // 5. Otherwise, if guard is response and name is a forbidden response |
+ // header name, return. |
+ if (m_guard == ResponseGuard && FetchHeaderList::isForbiddenResponseHeaderName(name)) |
+ return; |
+ // 6. Remove all headers whose name is name from header list. |
+ m_headerList->remove(name); |
+} |
+ |
+String Headers::get(const String& name, ExceptionState& exceptionState) |
+{ |
+ // The get(name) method, when invoked, must run these steps: |
+ // 1. If name is not a name, throw a TypeError. |
+ if (!FetchHeaderList::isValidHeaderName(name)) { |
+ exceptionState.throwTypeError("Invalid name"); |
+ return String(); |
+ } |
+ // 2. Return the value of the first header in header list whose name is |
+ // name, and null otherwise. |
+ String result; |
+ if (m_headerList->get(name, result)) |
+ return result; |
+ return String(); |
+} |
+ |
+Vector<String> Headers::getAll(const String& name, ExceptionState& exceptionState) |
+{ |
+ // The getAll(name) method, when invoked, must run these steps: |
+ // 1. If name is not a name, throw a TypeError. |
+ if (!FetchHeaderList::isValidHeaderName(name)) { |
+ exceptionState.throwTypeError("Invalid name"); |
+ return Vector<String>(); |
+ } |
+ // 2. Return the values of all headers in header list whose name is name, in |
+ // list order, and the empty sequence otherwise. |
+ Vector<String> result; |
+ m_headerList->getAll(name, result); |
+ return result; |
+} |
+ |
+bool Headers::has(const String& name, ExceptionState& exceptionState) |
+{ |
+ // The has(name) method, when invoked, must run these steps: |
+ // 1. If name is not a name, throw a TypeError. |
+ if (!FetchHeaderList::isValidHeaderName(name)) { |
+ exceptionState.throwTypeError("Invalid name"); |
+ return false; |
+ } |
+ // 2. Return true if there is a header in header list whose name is name, |
+ // and false otherwise. |
+ return m_headerList->has(name); |
+} |
+ |
+void Headers::set(const String& name, const String& value, ExceptionState& exceptionState) |
+{ |
+ // The set(name, value) method, when invoked, must run these steps: |
+ // 1. If name is not a name or value is not a value, throw a TypeError. |
+ if (!FetchHeaderList::isValidHeaderName(name)) { |
+ exceptionState.throwTypeError("Invalid name"); |
+ return; |
+ } |
+ if (!FetchHeaderList::isValidHeaderValue(value)) { |
+ exceptionState.throwTypeError("Invalid value"); |
+ return; |
+ } |
+ // 2. If guard is immutable, throw a TypeError. |
+ if (m_guard == ImmutableGuard) { |
+ exceptionState.throwTypeError("Headers are immutable"); |
+ return; |
+ } |
+ // 3. Otherwise, if guard is request and name is a forbidden header name, |
+ // return. |
+ if (m_guard == RequestGuard && FetchHeaderList::isForbiddenHeaderName(name)) |
+ return; |
+ // 4. Otherwise, if guard is request-no-CORS and name/value is not a simple |
+ // header, return. |
+ if (m_guard == RequestNoCORSGuard && !FetchHeaderList::isSimpleHeader(name, value)) |
+ return; |
+ // 5. Otherwise, if guard is response and name is a forbidden response |
+ // header name, return. |
+ if (m_guard == ResponseGuard && FetchHeaderList::isForbiddenResponseHeaderName(name)) |
+ return; |
+ // 6. Set name/value in header list. |
+ m_headerList->set(name, value); |
+} |
+ |
+void Headers::forEach(PassOwnPtr<HeadersForEachCallback> callback, ScriptValue& thisArg) |
+{ |
+ forEachInternal(callback, &thisArg); |
+} |
+ |
+void Headers::forEach(PassOwnPtr<HeadersForEachCallback> callback) |
+{ |
+ forEachInternal(callback, 0); |
+} |
+ |
+void Headers::clearHeaderList() |
+{ |
+ m_headerList->clearList(); |
+} |
+ |
+void Headers::fillWith(const Headers* headers, ExceptionState& exceptionState) |
+{ |
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);
|
+ // To fill a Headers object (headers) with a given object (object), run |
+ // these steps: |
+ // 1. If object is a Headers object, copy its header list as headerListCopy |
+ // and then for each header in headerListCopy, retaining order, append |
+ // header's name/header's value to headers. Rethrow any exception. |
+ for (size_t i = 0; i < headers->list()->list().size(); ++i) { |
+ append(headers->list()->list()[i]->first, headers->list()->list()[i]->second, exceptionState); |
+ if (exceptionState.hadException()) |
+ return; |
+ } |
+} |
+ |
+void Headers::fillWith(const Dictionary& dictionary, ExceptionState& exceptionState) |
+{ |
yhirano
2014/06/26 07:22:13
ditto
horo
2014/06/26 08:30:08
Done.
|
+ Vector<String> keys; |
+ dictionary.getOwnPropertyNames(keys); |
+ if (keys.size() == 0) |
+ return; |
+ |
+ // Because of the restrictions in IDL compiler of blink we recieve |
+ // sequence<sequence<ByteString>> as a Dictionary, which is a type of union |
+ // type of HeadersInit defined in the spec. |
+ // http://fetch.spec.whatwg.org/#headers-class |
+ // FIXME: Support sequence<sequence<ByteString>>. |
+ Vector<String> keyValuePair; |
+ if (dictionary.get(keys[0], keyValuePair)) { |
+ // 2. Otherwise, if object is a sequence, then for each header in |
+ // object, run these substeps: |
+ // 1. If header does not contain two items, throw a TypeError. |
+ // 2. Append header's first item/header's second time to headers. |
+ // Rethrow any exception. |
+ for (size_t i = 0; i < keys.size(); ++i) { |
+ 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.
|
+ exceptionState.throwTypeError("Invalid value"); |
+ return; |
+ } |
+ 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.
|
+ exceptionState.throwTypeError("Invalid value"); |
+ return; |
+ } |
+ append(keyValuePair[0], keyValuePair[1], exceptionState); |
+ if (exceptionState.hadException()) |
+ return; |
+ keyValuePair.clear(); |
+ } |
+ return; |
+ } |
+ // 3. Otherwise, if object is an open-ended dictionary, then for each header |
+ // in object, run these substeps: |
+ // 1. Set header's key to header's key, converted to ByteString. Rethrow |
+ // any exception. |
+ // 2. Append header's key/header's value to headers. Rethrow any |
+ // exception. |
+ // FIXME: Support OpenEndedDictionary<ByteString>. |
+ for (size_t i = 0; i < keys.size(); ++i) { |
+ String value; |
+ if (!dictionary.get(keys[i], value)) { |
+ exceptionState.throwTypeError("Invalid value"); |
+ return; |
+ } |
+ append(keys[i], value, exceptionState); |
+ if (exceptionState.hadException()) |
+ return; |
+ } |
+} |
+ |
+Headers::Headers() |
+ : m_headerList(FetchHeaderList::create()) |
+ , m_guard(NoneGuard) |
+{ |
+ ScriptWrappable::init(this); |
+} |
+ |
+// Called when creating Request or Responce. |
+Headers::Headers(FetchHeaderList* headerList) |
+ : m_headerList(headerList) |
+ , m_guard(NoneGuard) |
+{ |
+ ScriptWrappable::init(this); |
+} |
+ |
+void Headers::forEachInternal(PassOwnPtr<HeadersForEachCallback> callback, ScriptValue* thisArg) |
+{ |
+ TrackExceptionState exceptionState; |
+ for (size_t i = 0; i < m_headerList->size(); ++i) { |
+ if (thisArg) |
+ callback->handleItem(*thisArg, m_headerList->list()[i]->first, m_headerList->list()[i]->second, this); |
+ else |
+ callback->handleItem(m_headerList->list()[i]->first, m_headerList->list()[i]->second, this); |
+ if (exceptionState.hadException()) |
+ break; |
+ } |
+} |
+ |
+} // namespace WebCore |