/*
 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "PlatformExportMacros.h"
#include "WebCoreJSBuiltins.h"
#include "WebCoreJSBuiltinInternals.h"
#include <runtime/JSGlobalObject.h>
#include <runtime/Operations.h>

namespace WebCore {

class DOMGuardedObject;
class Document;
class Event;
class DOMWrapperWorld;
class ScriptExecutionContext;

typedef HashMap<const JSC::ClassInfo*, JSC::WriteBarrier<JSC::Structure> > JSDOMStructureMap;
typedef HashMap<const JSC::ClassInfo*, JSC::WriteBarrier<JSC::JSObject> > JSDOMConstructorMap;
typedef HashSet<DOMGuardedObject*> DOMGuardedObjectSet;

class JSDOMGlobalObject : public JSC::JSGlobalObject {
	typedef JSC::JSGlobalObject Base;
protected:
	struct JSDOMGlobalObjectData;

	JSDOMGlobalObject(JSC::JSGlobalData&, JSC::Structure*, PassRefPtr<DOMWrapperWorld>, const JSC::GlobalObjectMethodTable* = 0);
	static void destroy(JSC::JSCell*);
	void finishCreation(JSC::JSGlobalData&);
	void finishCreation(JSC::JSGlobalData&, JSC::JSObject*);

public:
	JSDOMStructureMap& structures() { return m_structures; }
	JSDOMConstructorMap& constructors() { return m_constructors; }

    DOMGuardedObjectSet& guardedObjects() { return m_guardedObjects; }

	ScriptExecutionContext* scriptExecutionContext() const;

	// Make binding code generation easier.
	JSDOMGlobalObject* globalObject() { return this; }

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

	ALWAYS_INLINE DOMWrapperWorld* world() { return m_world.get(); }
	bool worldIsNormal() const { return m_worldIsNormal; }

	JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; }
	JSBuiltinFunctions& builtinFunctions() { return m_builtinFunctions; }

	DECLARE_EXPORT_INFO;

public:
	~JSDOMGlobalObject();

	static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSValue prototype)
	{
		return JSC::Structure::create(globalData, 0, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags), info());
	}

protected:
    static void promiseRejectionTracker(JSC::JSGlobalObject*, JSC::ExecState*, JSC::JSPromise*, JSC::JSPromiseRejectionOperation);

	JSDOMStructureMap m_structures;
	JSDOMConstructorMap m_constructors;
    DOMGuardedObjectSet m_guardedObjects;

	const RefPtr<DOMWrapperWorld> m_world;
	bool m_worldIsNormal;

private:
	void addBuiltinGlobals(JSC::VM&);
	friend void JSBuiltinInternalFunctions::initialize(JSDOMGlobalObject&, JSC::JSGlobalData&);

	JSBuiltinInternalFunctions m_builtinInternalFunctions;
	JSBuiltinFunctions m_builtinFunctions;
};

ScriptExecutionContext* scriptExecutionContext(JSC::JSGlobalObject*);

template<class ConstructorClass>
inline JSC::JSObject* getDOMConstructor(JSC::JSGlobalData& vm, const JSDOMGlobalObject* globalObject)
{
	if (JSC::JSObject* constructor = const_cast<JSDOMGlobalObject*>(globalObject)->constructors().get(ConstructorClass::info()).get())
		return constructor;
	JSC::Structure* structure = ConstructorClass::createStructure(vm, const_cast<JSDOMGlobalObject*>(globalObject), ConstructorClass::prototypeForStructure(vm, globalObject));
	JSC::JSObject* constructor = ConstructorClass::create(vm, structure, const_cast<JSDOMGlobalObject*>(globalObject));
	ASSERT(!const_cast<JSDOMGlobalObject*>(globalObject)->constructors().contains(ConstructorClass::info()));
	JSC::WriteBarrier<JSC::JSObject> temp;
	const_cast<JSDOMGlobalObject*>(globalObject)->constructors().add(ConstructorClass::info(), temp).iterator->second.set(vm, globalObject, constructor);
	return constructor;
}

JSDOMGlobalObject* toJSDOMGlobalObject(Document*, JSC::ExecState*);
JSDOMGlobalObject* toJSDOMGlobalObject(ScriptExecutionContext*, JSC::ExecState*);

JSDOMGlobalObject* toJSDOMGlobalObject(Document*, DOMWrapperWorld*);
JSDOMGlobalObject* toJSDOMGlobalObject(ScriptExecutionContext*, DOMWrapperWorld*);

WEBCORE_EXPORT JSDOMGlobalObject& callerGlobalObject(JSC::ExecState&);

} // namespace WebCore
