| 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..2cf5213a6229f4ce5fb478815fa08be08f62f6fc
|
| --- /dev/null
|
| +++ b/Source/modules/serviceworkers/Headers.cpp
|
| @@ -0,0 +1,320 @@
|
| +// 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/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* init, 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(init, exceptionState);
|
| + // "3. Return |headers|."
|
| + return headers.release();
|
| +}
|
| +
|
| +PassRefPtr<Headers> Headers::create(const Dictionary& init, 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(init, 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 |request|, 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|/`invalid` 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. Delete |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;
|
| + m_headerList->get(name, result);
|
| + return result;
|
| +}
|
| +
|
| +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::fillWith(const Headers* object, ExceptionState& exceptionState)
|
| +{
|
| + ASSERT(m_headerList->size() == 0);
|
| + // "To fill a Headers object (|this|) 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 < object->m_headerList->list().size(); ++i) {
|
| + append(object->m_headerList->list()[i]->first, object->m_headerList->list()[i]->second, exceptionState);
|
| + if (exceptionState.hadException())
|
| + return;
|
| + }
|
| +}
|
| +
|
| +void Headers::fillWith(const Dictionary& object, ExceptionState& exceptionState)
|
| +{
|
| + ASSERT(m_headerList->size() == 0);
|
| + Vector<String> keys;
|
| + object.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 (object.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 item to
|
| + // |headers|. Rethrow any exception."
|
| + for (size_t i = 0; i < keys.size(); ++i) {
|
| + // We've already got the keyValuePair for key[0].
|
| + if (i > 0) {
|
| + if (!object.get(keys[i], keyValuePair)) {
|
| + exceptionState.throwTypeError("Invalid value");
|
| + return;
|
| + }
|
| + }
|
| + if (keyValuePair.size() < 2) {
|
| + 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 (!object.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]->second, m_headerList->list()[i]->first, this);
|
| + else
|
| + callback->handleItem(m_headerList->list()[i]->second, m_headerList->list()[i]->first, this);
|
| + if (exceptionState.hadException())
|
| + break;
|
| + }
|
| +}
|
| +
|
| +} // namespace WebCore
|
|
|