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