/*
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
 *  Copyright (C) 2007 Samuel Weinig <sam@webkit.org>
 *  Copyright (C) 2009 Google, Inc. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifndef JSDOMBinding_h
#define JSDOMBinding_h

#include "BindingState.h"
#include "BufferSource.h"
#include "CSSImportRule.h"
#include "CSSStyleDeclaration.h"
#include "CSSStyleSheet.h"
#include "JSDOMGlobalObject.h"
#include "JSDOMWrapper.h"
#include "DOMWrapperWorld.h"
#include "Document.h"
#include "Element.h"
#include "MediaKeySessionType.h"
#include "MediaKeysRequirement.h"
#include "MediaKeySystemConfiguration.h"
#include "MediaKeySystemMediaCapability.h"
#include "MediaList.h"
#include "ScriptWrappable.h"
#include "ShadowRoot.h"
#include "StylePropertySet.h"
#include "StyledElement.h"
#include "WebCoreTypedArrayController.h"
#include <heap/SlotVisitor.h>
#include <heap/Weak.h>
#include <runtime/Error.h>
#include <runtime/IteratorOperations.h>
#include <runtime/ExceptionScope.h>
#include <runtime/FunctionPrototype.h>
#include <runtime/JSArray.h>
#include <runtime/JSArrayBuffer.h>
#include <runtime/JSDataView.h>
#include <runtime/JSTypedArrays.h>
#include <runtime/Lookup.h>
#include <runtime/ObjectPrototype.h>
#include <runtime/Operations.h>
#include <runtime/TypedArrays.h>
#include <runtime/WriteBarrier.h>
#include <wtf/Forward.h>
#include <wtf/GetPtr.h>
#include <wtf/Noncopyable.h>
#include <wtf/NullPtr.h>
#include <wtf/Vector.h>

// FIXME: We could make this file a lot easier to read by putting all function declarations at the top,
// and function definitions below, even for template and inline functions.

namespace JSC {
class JSFunction;
}

namespace WebCore {

class CachedScript;
class DOMStringList;
class HTMLFrameElementBase;
class Frame;
class JSDocument;
class JSElement;
class JSNode;
class KURL;

typedef int ExceptionCode;

void callFunctionWithCurrentArguments(JSC::ExecState&, JSC::JSObject& thisObject, JSC::JSFunction&);

JSC::EncodedJSValue reportDeprecatedGetterError(JSC::ExecState&, const char* interfaceName, const char* attributeName);
void reportDeprecatedSetterError(JSC::ExecState&, const char* interfaceName, const char* attributeName);

void throwArrayElementTypeError(JSC::ExecState&);
void throwAttributeTypeError(JSC::ExecState&, const char* interfaceName, const char* attributeName, const char* expectedType);
void throwSequenceTypeError(JSC::ExecState&);
JSC::EncodedJSValue throwGetterTypeError(JSC::ExecState&, const char* interfaceName, const char* attributeName);
bool throwSetterTypeError(JSC::ExecState&, const char* interfaceName, const char* attributeName);
void throwNonFiniteTypeError(JSC::ExecState&);
void throwSecurityError(JSC::ExecState&, const String& message);

JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::ExecState&, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedValues);
JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::ExecState&, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName);
JSC::EncodedJSValue throwArgumentTypeError(JSC::ExecState&, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedType);
JSC::EncodedJSValue throwConstructorDocumentUnavailableError(JSC::ExecState&, const char* interfaceName);

String makeGetterTypeErrorMessage(const char* interfaceName, const char* attributeName);
String makeThisTypeErrorMessage(const char* interfaceName, const char* attributeName);
JSC::EncodedJSValue throwGetterTypeError(JSC::ExecState&, const char* interfaceName, const char* attributeName);
JSC::EncodedJSValue throwThisTypeError(JSC::ExecState&, const char* interfaceName, const char* functionName);

// Base class for all constructor objects in the JSC bindings.
class DOMConstructorObject : public JSDOMObject {
	typedef JSDOMObject Base;
public:
	static const unsigned StructureFlags = Base::StructureFlags | JSC::ImplementsHasInstance | JSC::ImplementsDefaultHasInstance | JSC::OverridesGetCallData;
	static ALWAYS_INLINE JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
	{
		return JSC::Structure::create(globalData, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
	}

protected:
	ALWAYS_INLINE DOMConstructorObject(JSC::Structure* structure, JSDOMGlobalObject* globalObject)
		: JSDOMObject(structure, globalObject)
	{
	}

    static String className(const JSObject*) { return ASCIILiteral("Function"); }
    static JSC::CallType getCallData(JSCell*, JSC::CallData&);
};

// Constructors using this base class depend on being in a Document and
// can never be used from a WorkerGlobalScope.
class DOMConstructorWithDocument : public DOMConstructorObject {
	typedef DOMConstructorObject Base;
public:
	ALWAYS_INLINE Document* document() const
	{
		return static_cast<Document*>(scriptExecutionContext());
	}

protected:
	ALWAYS_INLINE DOMConstructorWithDocument(JSC::Structure* structure, JSDOMGlobalObject* globalObject)
		: DOMConstructorObject(structure, globalObject)
	{
	}

	ALWAYS_INLINE void finishCreation(JSC::JSGlobalData& vm)
	{
		Base::finishCreation(vm);
		ASSERT(globalObject()->scriptExecutionContext()->isDocument());
	}
};

class DOMConstructorJSBuiltinObject : public DOMConstructorObject {
public:
    typedef DOMConstructorObject Base;

protected:
    DOMConstructorJSBuiltinObject(JSC::Structure* structure, JSDOMGlobalObject* globalObject)
        : DOMConstructorObject(structure, globalObject) { }

    static void visitChildren(JSC::JSCell*, JSC::SlotVisitor&);

    JSC::JSFunction* initializeFunction() { return m_initializeFunction.get(); }
    void setInitializeFunction(JSC::VM& vm, JSC::JSFunction& function) { m_initializeFunction.set(vm, this, &function); }

private:
    JSC::WriteBarrier<JSC::JSFunction> m_initializeFunction;
};

JSC::Structure* getCachedDOMStructure(JSDOMGlobalObject*, const JSC::ClassInfo*);
JSC::Structure* cacheDOMStructure(JSDOMGlobalObject*, JSC::Structure*, const JSC::ClassInfo*);

enum ShouldAllowNonFinite { ShouldAllowNonFiniteNo, ShouldAllowNonFiniteYes };

enum IntegerConversionConfiguration { NormalConversion, EnforceRange, Clamp };

template<typename T> struct Converter;

template<typename T> T convert(JSC::ExecState&, JSC::JSValue);
template<typename T> T convert(JSC::ExecState&, JSC::JSValue, IntegerConversionConfiguration);
template<typename T> T convert(JSC::ExecState&, JSC::JSValue, ShouldAllowNonFinite);

template<typename T> Optional<T> convertDictionary(JSC::ExecState&, JSC::JSValue);

ALWAYS_INLINE JSDOMGlobalObject* deprecatedGlobalObjectForPrototype(JSC::ExecState* exec)
{
	// FIXME: Callers to this function should be using the global object
	// from which the object is being created, instead of assuming the lexical one.
	// e.g. subframe.document.body should use the subframe's global object, not the lexical one.
	return JSC::jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
}

template<typename WrapperClass> inline JSC::Structure* getDOMStructure(JSC::JSGlobalData& vm, JSDOMGlobalObject* globalObject)
{
	if (JSC::Structure* structure = getCachedDOMStructure(globalObject, WrapperClass::info()))
		return structure;
	return cacheDOMStructure(globalObject, WrapperClass::createStructure(vm, globalObject, WrapperClass::createPrototype(vm, globalObject)), WrapperClass::info());
}

template<typename WrapperClass> inline JSC::Structure* deprecatedGetDOMStructure(JSC::ExecState* exec)
{
    // FIXME: This function is wrong. It uses the wrong global object for creating the prototype structure.
    return getDOMStructure<WrapperClass>(exec->vm(), deprecatedGlobalObjectForPrototype(exec));
}

template<typename WrapperClass> ALWAYS_INLINE JSC::JSObject* getDOMPrototype(JSC::JSGlobalData& vm, JSC::JSGlobalObject* globalObject)
{
	return JSC::jsCast<JSC::JSObject*>(asObject(getDOMStructure<WrapperClass>(vm, JSC::jsCast<JSDOMGlobalObject*>(globalObject))->storedPrototype()));
}

ALWAYS_INLINE JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld* world, JSC::ArrayBuffer*)
{
    return static_cast<WebCoreTypedArrayController*>(world->vm()->m_typedArrayController.get())->wrapperOwner();
}

ALWAYS_INLINE void* wrapperContext(DOMWrapperWorld* world, JSC::ArrayBuffer*)
{
    return world;
}

template<typename DOMClass> void* wrapperKey(DOMClass* domObject)
{
	return domObject;
}

template<>
ALWAYS_INLINE void* wrapperKey(JSC::ArrayBuffer* domObject)
{
    return domObject;
}

inline JSDOMObject* getInlineCachedWrapper(DOMWrapperWorld*, void*) { return 0; }
inline bool setInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMObject*, JSC::WeakHandleOwner*, void*) { return false; }
inline bool clearInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMObject*) { return false; }

inline JSDOMObject* getInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject)
{
    if (!world->isNormal())
        return 0;
    return domObject->wrapper();
}

inline bool setInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject, JSDOMObject* wrapper, JSC::WeakHandleOwner* wrapperOwner, void* context)
{
    if (!world->isNormal())
        return false;
    domObject->setWrapper(*world->globalData(), wrapper, wrapperOwner, context);
    return true;
}

inline bool clearInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject, JSDOMObject* wrapper)
{
    if (!world->isNormal())
        return false;
    domObject->clearWrapper(wrapper);
    return true;
}

ALWAYS_INLINE JSC::JSArrayBuffer* getInlineCachedWrapper(DOMWrapperWorld* world, JSC::ArrayBuffer* buffer)
{
    if (LIKELY(world->isNormal()))
    	return buffer->m_wrapper.get();
    return 0;
}

ALWAYS_INLINE bool setInlineCachedWrapper(DOMWrapperWorld* world, JSC::ArrayBuffer* domObject, JSC::JSArrayBuffer* wrapper, JSC::WeakHandleOwner*, void*)
{
    if (LIKELY(world->isNormal())){
		domObject->m_wrapper = JSC::PassWeak<JSC::JSArrayBuffer>(wrapper, wrapperOwner(world, domObject), wrapperContext(world, domObject));
		return true;
    }
    return false;
}

ALWAYS_INLINE bool clearInlineCachedWrapper(DOMWrapperWorld* world, JSC::ArrayBuffer* domObject, JSC::JSArrayBuffer* wrapper)
{
    if (LIKELY(world->isNormal())){
    	weakClear(domObject->m_wrapper, wrapper);
    	return true;
    }
    return false;
}

template <typename DOMClass> inline JSC::JSObject* getCachedWrapper(DOMWrapperWorld* world, DOMClass* domObject)
{
    if (JSC::JSObject* wrapper = getInlineCachedWrapper(world, domObject))
        return wrapper;
    return world->m_wrappers.get(wrapperKey(domObject));
}

template <typename DOMClass> inline JSC::JSObject* getCachedWrapper(DOMWrapperWorld* world, DOMClass& domObject)
{
    if (JSC::JSObject* wrapper = getInlineCachedWrapper(world, domObject))
        return wrapper;
    return world->m_wrappers.get(wrapperKey(domObject));
}

template <typename DOMClass, typename WrapperClass> inline void cacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, WrapperClass* wrapper)
{
    JSC::WeakHandleOwner* owner = wrapperOwner(world, domObject);
    void* context = wrapperContext(world, domObject);
    if (setInlineCachedWrapper(world, domObject, wrapper, owner, context))
        return;
    JSC::PassWeak<JSC::JSObject> passWeak(wrapper, owner, context);
	weakAdd(world->m_wrappers, wrapperKey(domObject), passWeak);
}

template <typename DOMClass, typename WrapperClass> inline void uncacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, WrapperClass* wrapper)
{
	if (clearInlineCachedWrapper(world, domObject, wrapper))
		return;
	weakRemove(world->m_wrappers, wrapperKey(domObject), wrapper);
}

#define CREATE_DOM_WRAPPER(globalObject, className, object) createWrapper<JS##className>(globalObject, static_cast<className*>(object))
template<typename WrapperClass, typename DOMClass> inline JSDOMObject* createWrapper(JSDOMGlobalObject* globalObject, DOMClass* node)
{
	ASSERT(node);
    ASSERT(!getCachedWrapper(globalObject->world(), node));
	WrapperClass* wrapper = WrapperClass::create(getDOMStructure<WrapperClass>(globalObject->vm(), globalObject), globalObject, node);
	cacheWrapper(globalObject->world(), node, wrapper);
	return wrapper;
}

template<typename WrapperClass, typename DOMClass> inline JSC::JSValue wrap(JSDOMGlobalObject* globalObject, DOMClass* domObject)
{
	if (!domObject)
		return JSC::jsNull();
	if (JSC::JSObject* wrapper = getCachedWrapper(globalObject->world(), domObject))
		return wrapper;
	return createWrapper<WrapperClass>(globalObject, domObject);
}

template<typename WrapperClass, typename DOMClass> inline JSC::JSValue wrap(JSDOMGlobalObject* globalObject, DOMClass& domObject)
{
	if (JSC::JSObject* wrapper = getCachedWrapper(globalObject->world(), domObject))
		return wrapper;
	return createWrapper<WrapperClass>(globalObject, domObject);
}

template<typename WrapperClass, typename DOMClass> ALWAYS_INLINE JSC::JSValue getExistingWrapper(JSDOMGlobalObject* globalObject, DOMClass* domObject)
{
    ASSERT(domObject);
    return getCachedWrapper(globalObject->world(), domObject);
}

template<class WrapperClass, class DOMClass> inline JSC::JSValue createNewWrapper(JSDOMGlobalObject* globalObject, DOMClass* domObject)
{
    ASSERT(domObject);
    ASSERT(!getCachedWrapper(globalObject->world(), domObject));
    return createWrapper<WrapperClass>(globalObject, domObject);
}

inline void* root(Node* node)
{
	if (node->inDocument())
		return node->document();

	while (auto* nextNode = node->parentOrShadowHostNode())
		node = nextNode;
	return node;
}

ALWAYS_INLINE void* root(Node& node)
{
    return root(&node);
}

inline void* root(StyleSheet*);

inline void* root(CSSRule* rule)
{
	if (rule->parentRule())
		return root(rule->parentRule());
	if (rule->parentStyleSheet())
		return root(rule->parentStyleSheet());
	return rule;
}

inline void* root(CSSRule& rule)
{
	return root(&rule);
}

inline void* root(StyleSheet* styleSheet)
{
	if (styleSheet->ownerRule())
		return root(styleSheet->ownerRule());
	if (styleSheet->ownerNode())
		return root(styleSheet->ownerNode());
	return styleSheet;
}

inline void* root(StyleSheet& styleSheet)
{
	return root(&styleSheet);
}

inline void* root(CSSStyleDeclaration* style)
{
	if (CSSRule* parentRule = style->parentRule())
		return root(parentRule);
	if (CSSStyleSheet* styleSheet = style->parentStyleSheet())
		return root(styleSheet);
	return style;
}

inline void* root(CSSStyleDeclaration& style)
{
	return root(&style);
}

inline void* root(MediaList* mediaList)
{
	if (CSSRule* parentRule = mediaList->parentRule())
		return root(parentRule);
	if (CSSStyleSheet* parentStyleSheet = mediaList->parentStyleSheet())
		return root(parentStyleSheet);
	return mediaList;
}

inline void* root(MediaList& mediaList)
{
	return root(&mediaList);
}

template<typename JSClass> JSClass& castThisValue(JSC::ExecState&);

void addImpureProperty(const AtomicString&);

const JSC::HashTable& getHashTableForGlobalData(JSC::JSGlobalData&, const JSC::HashTable& staticTable);

String makeGetterTypeErrorMessage(const char* interfaceName, const char* attributeName);
String makeThisTypeErrorMessage(const char* interfaceName, const char* attributeName);

JSC::EncodedJSValue throwThisTypeError(JSC::ExecState&, const char* interfaceName, const char* functionName);

void reportException(JSC::ExecState*, JSC::JSValue exception, CachedScript* = 0);
void reportException(JSC::ExecState*, JSC::Exception*, CachedScript* cachedScript = 0);
void reportCurrentException(JSC::ExecState*);

JSC::JSValue createDOMException(JSC::ExecState*, ExceptionCode);
JSC::JSValue createDOMException(JSC::ExecState*, ExceptionCode, const String& message);
// Convert a DOM implementation exception code into a JavaScript exception in the execution state.
void setDOMExceptionSlow(JSC::ExecState* exec, ExceptionCode ec);
inline void setDOMException(JSC::ExecState* exec, ExceptionCode ec)
{
	if(UNLIKELY(ec))
		setDOMExceptionSlow(exec, ec);
}

JSC::JSValue jsString(JSC::ExecState*, const KURL&); // empty if the URL is null

JSC::JSValue jsStringOrNull(JSC::ExecState*, const String&); // null if the string is null
JSC::JSValue jsStringOrNull(JSC::ExecState*, const KURL&); // null if the URL is null

JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const String&); // undefined if the string is null
JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const KURL&); // undefined if the URL is null

// See JavaScriptCore for explanation: Should be used for any string that is already owned by another
// object, to let the engine know that collecting the JSString wrapper is unlikely to save memory.
JSC::JSValue jsOwnedStringOrNull(JSC::ExecState*, const String&);

String propertyNameToString(JSC::PropertyName);
AtomicString propertyNameToAtomicString(JSC::PropertyName);

template<JSC::NativeFunction, int length> JSC::EncodedJSValue nonCachingStaticFunctionGetter(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);

// FIXME: This is only used by legacy code and should go away. Use valueToStringTreatingNullAsEmptyString() instead.
String valueToStringWithNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null

String valueToStringTreatingNullAsEmptyString(JSC::ExecState*, JSC::JSValue);
String valueToStringWithUndefinedOrNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null or undefined

String valueToUSVString(JSC::ExecState*, JSC::JSValue);
String valueToUSVStringTreatingNullAsEmptyString(JSC::ExecState*, JSC::JSValue);
String valueToUSVStringWithUndefinedOrNullCheck(JSC::ExecState*, JSC::JSValue);

template<typename T> JSC::JSValue toNullableJSNumber(Optional<T>); // null if the optional is null

template<typename JSClass> inline JSClass& castThisValue(JSC::ExecState& state)
{
	JSC::JSValue thisValue = state.thisValue();
	JSClass* castedThis = JSC::jsDynamicCast<JSClass*>(thisValue);
    ASSERT(castedThis);
    return *castedThis;
}

inline int32_t finiteInt32Value(JSC::JSValue value, JSC::ExecState* exec, bool& okay)
{
	double number = value.toNumber(exec);
	okay = isfinite(number);
	return JSC::toInt32(number);
}

int8_t toInt8EnforceRange(JSC::ExecState&, JSC::JSValue);
uint8_t toUInt8EnforceRange(JSC::ExecState&, JSC::JSValue);
int16_t toInt16EnforceRange(JSC::ExecState&, JSC::JSValue);
uint16_t toUInt16EnforceRange(JSC::ExecState&, JSC::JSValue);
int32_t toInt32EnforceRange(JSC::ExecState&, JSC::JSValue);
uint32_t toUInt32EnforceRange(JSC::ExecState&, JSC::JSValue);
int64_t toInt64EnforceRange(JSC::ExecState&, JSC::JSValue);
uint64_t toUInt64EnforceRange(JSC::ExecState&, JSC::JSValue);

int8_t toInt8Clamp(JSC::ExecState&, JSC::JSValue);
uint8_t toUInt8Clamp(JSC::ExecState&, JSC::JSValue);
int16_t toInt16Clamp(JSC::ExecState&, JSC::JSValue);
uint16_t toUInt16Clamp(JSC::ExecState&, JSC::JSValue);
int32_t toInt32Clamp(JSC::ExecState&, JSC::JSValue);
uint32_t toUInt32Clamp(JSC::ExecState&, JSC::JSValue);
int64_t toInt64Clamp(JSC::ExecState&, JSC::JSValue);
uint64_t toUInt64Clamp(JSC::ExecState&, JSC::JSValue);

int8_t toInt8(JSC::ExecState&, JSC::JSValue);
uint8_t toUInt8(JSC::ExecState&, JSC::JSValue);
int16_t toInt16(JSC::ExecState&, JSC::JSValue);
uint16_t toUInt16(JSC::ExecState&, JSC::JSValue);
int64_t toInt64(JSC::ExecState&, JSC::JSValue);
uint64_t toUInt64(JSC::ExecState&, JSC::JSValue);

// Returns a Date instance for the specified value, or null if the value is NaN or infinity.
JSC::JSValue jsDateOrNull(JSC::ExecState*, double);

// NaN if the value can't be converted to a date.
double valueToDate(JSC::ExecState*, JSC::JSValue);

// Validates that the passed object is a sequence type per section 4.1.13 of the WebIDL spec.
inline JSC::JSObject* toJSSequence(JSC::ExecState* exec, JSC::JSValue value, unsigned& length)
{
	JSC::JSObject* object = value.getObject();
	if (!object) {
        throwVMError(exec, createTypeError(exec, "Value is not a sequence"));
		return 0;
	}

	JSC::VM& vm = exec->vm();
	JSC::JSValue lengthValue = object->get(exec, vm.propertyNames->length);
    RETURN_IF_EXCEPTION(vm, static_cast<JSC::JSObject*>(0));

	if (lengthValue.isUndefinedOrNull()) {
        throwVMError(exec, createTypeError(exec, "Value is not a sequence"));
		return 0;
	}

	length = lengthValue.toUInt32(exec);
    RETURN_IF_EXCEPTION(vm, static_cast<JSC::JSObject*>(0));

	return object;
}

ALWAYS_INLINE JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, JSC::ArrayBuffer* buffer)
{
    if (!buffer)
        return JSC::jsNull();
    if (JSC::JSValue result = getExistingWrapper<JSC::JSArrayBuffer>(globalObject, buffer))
        return result;
    // The JSArrayBuffer::create function will register the wrapper in finishCreation.
    JSC::JSArrayBuffer* wrapper = JSC::JSArrayBuffer::create(exec->vm(), globalObject->arrayBufferStructure(), buffer);
    return wrapper;
}

ALWAYS_INLINE JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, JSC::ArrayBufferView* view)
{
    if (!view)
        return JSC::jsNull();
    return view->wrap(exec, globalObject);
}

inline JSC::JSValue toJS(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, JSC::ArrayBufferView* view)
{
    if (!view)
        return JSC::jsNull();
    return view->wrap(exec, globalObject);
}

inline JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const BufferSource& bufferSource)
{
	return toJS(exec, globalObject, bufferSource.buffer().get());
}

template <typename T>
ALWAYS_INLINE JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<T> ptr)
{
	return toJS(exec, globalObject, ptr.get());
}

ALWAYS_INLINE JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject* globalObject, const String& s)
{
	return JSC::jsStringWithCache(globalObject->vm(), s);
}

inline JSC::JSValue toJSIterator(JSC::ExecState& state, JSDOMGlobalObject&, JSC::JSValue value)
{
    return JSC::createIteratorResultObject(&state, value, false);
}

template<typename T> inline JSC::JSValue toJSIterator(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const T& value)
{
    return JSC::createIteratorResultObject(&state, toJS(&state, &globalObject, value), false);
}

inline JSC::JSValue toJSIteratorEnd(JSC::ExecState& state)
{
    return JSC::createIteratorResultObject(&state, JSC::jsUndefined(), true);
}

template <typename T>
struct JSValueTraits {
	static ALWAYS_INLINE JSC::JSValue arrayJSValue(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const T& value)
	{
		return toJS(exec, globalObject, WTF::getPtr(value));
	}
};

template<>
struct JSValueTraits<String> {
	static ALWAYS_INLINE JSC::JSValue arrayJSValue(JSC::ExecState*, JSDOMGlobalObject* globalObject, const String& value)
	{
		return JSC::jsStringWithCache(globalObject->vm(), value);
	}
};

template<>
struct JSValueTraits<float> {
	static ALWAYS_INLINE JSC::JSValue arrayJSValue(JSC::ExecState*, JSDOMGlobalObject*, const float& value)
	{
		return JSC::jsNumber(value);
	}
};

template<>
struct JSValueTraits<unsigned long> {
	static ALWAYS_INLINE JSC::JSValue arrayJSValue(JSC::ExecState*, JSDOMGlobalObject*, const unsigned long& value)
	{
		return JSC::jsNumber(value);
	}
};

#if ENABLE(ENCRYPTED_MEDIA)
JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, const MediaKeySystemMediaCapability&);
template<>
struct JSValueTraits<MediaKeySystemMediaCapability> {
	static ALWAYS_INLINE JSC::JSValue arrayJSValue(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const MediaKeySystemMediaCapability& value)
	{
		return toJS(exec, globalObject, value);
	}
};

JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, MediaKeySessionType);
template<>
struct JSValueTraits<MediaKeySessionType> {
	static ALWAYS_INLINE JSC::JSValue arrayJSValue(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, MediaKeySessionType value)
	{
		return toJS(exec, globalObject, value);
	}
};
#endif

template <typename T, size_t inlineCapacity>
JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Vector<T, inlineCapacity>& vector)
{
	JSC::MarkedArgumentBuffer list;
	typename Vector<T, inlineCapacity>::const_iterator end = vector.end();
	typedef JSValueTraits<T> TraitsType;

	for (typename Vector<T, inlineCapacity>::const_iterator iter = vector.begin(); iter != end; ++iter)
		list.append(TraitsType::arrayJSValue(exec, globalObject, *iter));

	return JSC::constructArray(exec, 0, globalObject, list);
}

template <typename T, size_t inlineCapacity>
JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Vector<T, inlineCapacity>* vector)
{
    if (!vector)
        return JSC::constructEmptyArray(exec, 0, globalObject, 0);
    return jsArray(exec, globalObject, *vector);
}

JSC::JSValue jsArray(JSC::ExecState*, JSDOMGlobalObject*, PassRefPtr<DOMStringList>);

template <typename T, size_t inlineCapacity>
ALWAYS_INLINE JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Vector<T, inlineCapacity>& iterator)
{
	return jsArray(exec, globalObject, iterator);
}

template<typename Value1, typename Value2> inline JSC::JSValue jsPair(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Value1& value1, const Value2& value2)
{
    JSC::MarkedArgumentBuffer args;
    args.append(toJS(exec, globalObject, value1));
    args.append(toJS(exec, globalObject, value2));
    return constructArray(exec, 0, globalObject, args);
}

ALWAYS_INLINE RefPtr<JSC::ArrayBufferView> toArrayBufferView(JSC::JSValue value)
{
    JSC::JSArrayBufferView* wrapper = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(value);
    if (!wrapper)
        return nullptr;
    return wrapper->impl();
}

ALWAYS_INLINE PassRefPtr<JSC::Int8Array> toInt8Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Int8Adaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Int16Array> toInt16Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Int16Adaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Int32Array> toInt32Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Int32Adaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Uint8Array> toUint8Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Uint8Adaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Uint8ClampedArray> toUint8ClampedArray(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Uint8ClampedAdaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Uint16Array> toUint16Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Uint16Adaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Uint32Array> toUint32Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Uint32Adaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Float32Array> toFloat32Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Float32Adaptor>(value); }
ALWAYS_INLINE PassRefPtr<JSC::Float64Array> toFloat64Array(JSC::JSValue value) { return JSC::toNativeTypedView<JSC::Float64Adaptor>(value); }

inline BufferSource toBufferSource(JSC::ExecState* exec, JSC::JSValue value)
{
    JSC::VM& vm = exec->vm();

    if (JSC::ArrayBuffer* buffer = JSC::toArrayBuffer(value))
        return BufferSource(buffer);
    if (RefPtr<JSC::ArrayBufferView> bufferView = toArrayBufferView(value))
        return BufferSource(ArrayBuffer::create(static_cast<uint8_t*>(bufferView->baseAddress()), bufferView->byteLength()));

    throwTypeError(exec, ASCIILiteral("Only ArrayBuffer and ArrayBufferView objects can be passed as BufferSource arguments"));
    return BufferSource();
}

inline PassRefPtr<JSC::DataView> toDataView(JSC::JSValue value)
{
    JSC::JSDataView* wrapper = JSC::jsDynamicCast<JSC::JSDataView*>(value);
    if (!wrapper)
        return nullptr;
    return wrapper->typedImpl();
}

template<typename T> struct NativeValueTraits;

template<>
struct NativeValueTraits<String> {
	static ALWAYS_INLINE bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, String& indexedValue)
	{
		indexedValue = jsValue.toWTFString(exec);
		return true;
	}
};

template<>
struct NativeValueTraits<CString> {
	static ALWAYS_INLINE bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, CString& indexedValue)
	{
		indexedValue = jsValue.toWTFString(exec).utf8();
		return true;
	}
};

template<>
struct NativeValueTraits<unsigned short> {
	static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, unsigned short& indexedValue)
	{
		if (!jsValue.isNumber())
			return false;

		indexedValue = jsValue.toUInt32(exec);
		if (exec->hadException())
			return false;

		return true;
	}
};

template<>
struct NativeValueTraits<unsigned long> {
	static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, unsigned long& indexedValue)
	{
		if (!jsValue.isNumber())
			return false;

		indexedValue = jsValue.toUInt32(exec);
		if (exec->hadException())
			return false;

		return true;
	}
};

template<>
struct NativeValueTraits<float> {
    static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, float& indexedValue)
    {
        indexedValue = jsValue.toFloat(exec);
        return !exec->hadException();
    }
};

#if ENABLE(ENCRYPTED_MEDIA)

template<> Optional<MediaKeySessionType> convert(JSC::ExecState& state, JSC::JSValue value);
template<> Optional<MediaKeysRequirement> convert(JSC::ExecState& state, JSC::JSValue value);

template<> Optional<MediaKeySystemConfiguration> convertDictionary<MediaKeySystemConfiguration>(JSC::ExecState&, JSC::JSValue);
template<> Optional<MediaKeySystemMediaCapability> convertDictionary<MediaKeySystemMediaCapability>(JSC::ExecState&, JSC::JSValue);

template<>
struct NativeValueTraits<MediaKeySessionType> {
    static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, MediaKeySessionType& indexedValue)
    {
    	if(Optional<MediaKeySessionType> convertedValue = convert<Optional<MediaKeySessionType> >(*exec, jsValue)) {
    		indexedValue = convertedValue.value();
    		return true;
    	}
    	return false;
    }
};

template<>
struct NativeValueTraits<MediaKeySystemConfiguration> {
    static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, MediaKeySystemConfiguration& indexedValue)
    {
    	if(Optional<MediaKeySystemConfiguration> nativeValue = convertDictionary<MediaKeySystemConfiguration>(*exec, jsValue)) {
            indexedValue = nativeValue.value();
    		return true;
    	}
    	return false;
    }
};

template<>
struct NativeValueTraits<MediaKeySystemMediaCapability> {
    static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, MediaKeySystemMediaCapability& indexedValue)
    {
    	if(Optional<MediaKeySystemMediaCapability> nativeValue = convertDictionary<MediaKeySystemMediaCapability>(*exec, jsValue)) {
            indexedValue = nativeValue.value();
    		return true;
    	}
    	return false;
    }
};

#endif

template <typename T, typename JST>
Vector<RefPtr<T> > toRefPtrNativeArray(JSC::ExecState* exec, JSC::JSValue value, T* (*toT)(JSC::JSValue value))
{
    if (!isJSArray(value))
        return Vector<RefPtr<T> >();

    Vector<RefPtr<T> > result;
    JSC::JSArray* array = asArray(value);
    for (size_t i = 0; i < array->length(); ++i) {
        JSC::JSValue element = array->getIndex(exec, i);
        if (element.inherits(JST::info()))
            result.append((*toT)(element));
        else {
            throwArrayElementTypeError(*exec);
            return Vector<RefPtr<T> >();
        }
    }
    return result;
}

template <typename T, typename JST, typename VectorType>
VectorType toRefPtrNativeArray(JSC::ExecState& exec, JSC::JSValue value, T* (*toT)(JSC::JSValue value))
{
    if (!isJSArray(value))
        return VectorType();

    VectorType result;
    JSC::JSArray* array = asArray(value);
    for (size_t i = 0; i < array->length(); ++i) {
        JSC::JSValue element = array->getIndex(&exec, i);
        if (element.inherits(JST::info()))
            result.append((*toT)(element));
        else {
            throwArrayElementTypeError(exec);
            return VectorType();
        }
    }
    return result;
}

template <typename T>
Vector<T> toNativeArray(JSC::ExecState* exec, JSC::JSValue value)
{
    JSC::JSObject* object = value.getObject();
    if (!object)
        return Vector<T>();

	unsigned length = 0;
	if (isJSArray(value)) {
		JSC::JSArray* array = asArray(value);
		length = array->length();
	} else
		toJSSequence(exec, value, length);

	Vector<T> result;
    result.reserveInitialCapacity(length);
	typedef NativeValueTraits<T> TraitsType;

	for (unsigned i = 0; i < length; ++i) {
		T indexValue;
		if (!TraitsType::nativeValue(exec, object->get(exec, i), indexValue))
			return Vector<T>();
		result.append(indexValue);
	}
	return result;
}

template <typename T>
Vector<T> toNativeArguments(JSC::ExecState* exec, size_t startIndex)
{
    size_t length = exec->argumentCount();
    ASSERT(startIndex <= length);

    Vector<T> result;
    result.reserveInitialCapacity(length - startIndex);
    typedef NativeValueTraits<T> TraitsType;

    for (size_t i = startIndex; i < length; ++i) {
        T indexValue;
        if (!TraitsType::nativeValue(exec, exec->argument(i), indexValue))
            return Vector<T>();
        result.append(indexValue);
    }
    return result;
}

bool shouldAllowAccessToNode(JSC::ExecState*, Node*);
bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*);
bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*, String& message);
bool shouldAllowAccessToDOMWindow(BindingState*, DOMWindow*, String& message);
bool shouldAllowAccessToDOMWindow(BindingState*, DOMWindow&, String& message);

void printErrorMessageForFrame(Frame*, const String& message);

ALWAYS_INLINE String propertyNameToString(JSC::PropertyName propertyName)
{
    ASSERT(!propertyName.isSymbol());
    return propertyName.uid() ? propertyName.uid() : propertyName.publicName();
}

ALWAYS_INLINE AtomicString propertyNameToAtomicString(JSC::PropertyName propertyName)
{
    return AtomicString(propertyName.uid() ? propertyName.uid() : propertyName.publicName());
}

template<JSC::NativeFunction nativeFunction, int length>
JSC::EncodedJSValue nonCachingStaticFunctionGetter(JSC::ExecState* exec, JSC::EncodedJSValue, JSC::PropertyName propertyName)
{
    return JSC::JSValue::encode(JSC::JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), length, propertyName.publicName(), nativeFunction));
}

enum SecurityReportingOption {
    DoNotReportSecurityError,
    ReportSecurityError,
};

class BindingSecurity {
public:
    static bool shouldAllowAccessToNode(BindingState*, Node*);
    static bool shouldAllowAccessToNode(BindingState*, Node&);
    static bool shouldAllowAccessToDOMWindow(BindingState*, DOMWindow&, SecurityReportingOption = ReportSecurityError);
    static bool shouldAllowAccessToDOMWindow(BindingState*, DOMWindow*, SecurityReportingOption = ReportSecurityError);
    static bool shouldAllowAccessToFrame(BindingState*, Frame*, SecurityReportingOption = ReportSecurityError);
    static bool allowSettingFrameSrcToJavascriptUrl(BindingState*, HTMLFrameElementBase*, const String& value);
};

template<typename T> inline JSC::JSValue toNullableJSNumber(Optional<T> optionalNumber)
{
    return optionalNumber ? JSC::jsNumber(optionalNumber.value()) : JSC::jsNull();
}

JSElement* jsElementCast(JSC::JSValue value);
JSNode* jsNodeCast(JSC::JSValue value);
JSDocument* jsDocumentCast(JSC::JSValue value);

inline JSC::JSValue getPropertyFromObject(JSC::VM& vm, JSC::ExecState& exec, JSC::JSObject& object, const char* identifier)
{
    return object.get(&exec, JSC::Identifier::fromString(vm, identifier));
}

inline void setPropertyToObject(JSC::VM& vm, JSC::ExecState& exec, JSC::JSObject& object, const char* name, JSC::JSValue value)
{
    JSC::PutPropertySlot propertySlot(&object);
    JSC::JSObject::put(&object, &exec, JSC::Identifier::fromString(vm, name), value, propertySlot);
}

} // namespace WebCore

#include "JSMediaKeySessionType.h"
#include "JSMediaKeysRequirement.h"
#include "JSMediaKeySystemConfiguration.h"
#include "JSMediaKeySystemMediaCapability.h"

#endif // JSDOMBinding_h
