blob: 17a2d1b53c3c6f7f8578b2a030edcd64ab1f7613 [file] [log] [blame]
// Copyright (c) 2006-2008 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.
#ifndef V8_PROXY_H__
#define V8_PROXY_H__
#include <v8.h>
#include "v8_index.h"
#include "v8_custom.h"
#include "V8Utilities.h"
#include "Node.h"
#include "NodeFilter.h"
#include "PlatformString.h" // for WebCore::String
#include "ScriptSourceCode.h" // for WebCore::ScriptSourceCode
#include "SecurityOrigin.h" // for WebCore::SecurityOrigin
#include "V8DOMMap.h"
#include <wtf/Assertions.h>
#include <wtf/PassRefPtr.h> // so generated bindings don't have to
#include <wtf/Vector.h>
#if defined(ENABLE_DOM_STATS_COUNTERS) && PLATFORM(CHROMIUM)
#include "ChromiumBridge.h"
#define INC_STATS(name) ChromiumBridge::incrementStatsCounter(name)
#else
#define INC_STATS(name)
#endif
// FIXME: Remove the following hack when we replace all references to GetDOMObjectMap.
#define GetDOMObjectMap getDOMObjectMap
namespace WebCore {
class CSSStyleDeclaration;
class DOMImplementation;
class Element;
class Event;
class EventListener;
class Frame;
class HTMLCollection;
class HTMLOptionsCollection;
class HTMLElement;
class HTMLDocument;
class MediaList;
class NamedNodeMap;
class Node;
class NodeList;
class Screen;
class String;
class StyleSheet;
class SVGElement;
class DOMWindow;
class Document;
class EventTarget;
class Event;
class EventListener;
class Navigator;
class MimeType;
class MimeTypeArray;
class Plugin;
class PluginArray;
class StyleSheetList;
class CSSValue;
class CSSRule;
class CSSRuleList;
class CSSValueList;
class NodeFilter;
class ScriptExecutionContext;
#if ENABLE(SVG)
class SVGElementInstance;
#endif
class V8EventListener;
class V8ObjectEventListener;
// This is a container for V8EventListener objects that also does some
// caching to speed up finding entries by v8::Object.
class V8EventListenerList {
public:
static const size_t kMaxKeyNameLength = 254;
// The name should be distinct from any other V8EventListenerList
// within the same process, and <= kMaxKeyNameLength characters.
explicit V8EventListenerList(const char* name);
~V8EventListenerList();
typedef Vector<V8EventListener*>::iterator iterator;
V8EventListenerList::iterator begin();
iterator end();
// In addition to adding the listener to this list, this also caches the
// V8EventListener as a hidden property on its wrapped v8 listener object,
// so we can quickly look it up later.
void add(V8EventListener*);
void remove(V8EventListener*);
V8EventListener* find(v8::Local<v8::Object>, bool isInline);
void clear();
private:
v8::Handle<v8::String> getKey(bool isInline);
v8::Persistent<v8::String> m_inlineKey;
v8::Persistent<v8::String> m_nonInlineKey;
Vector<V8EventListener*> m_list;
};
// TODO(fqian): use standard logging facilities in WebCore.
void log_info(Frame* frame, const String& msg, const String& url);
#ifndef NDEBUG
#define GlobalHandleTypeList(V) \
V(PROXY) \
V(NPOBJECT) \
V(SCHEDULED_ACTION) \
V(EVENT_LISTENER) \
V(NODE_FILTER) \
V(SCRIPTINSTANCE) \
V(SCRIPTVALUE)
// Host information of persistent handles.
enum GlobalHandleType {
#define ENUM(name) name,
GlobalHandleTypeList(ENUM)
#undef ENUM
};
class GlobalHandleInfo {
public:
GlobalHandleInfo(void* host, GlobalHandleType type)
: host_(host), type_(type) { }
void* host_;
GlobalHandleType type_;
};
#endif // NDEBUG
// The following Batch structs and methods are used for setting multiple
// properties on an ObjectTemplate, used from the generated bindings
// initialization (ConfigureXXXTemplate). This greatly reduces the binary
// size by moving from code driven setup to data table driven setup.
// BatchedAttribute translates into calls to SetAccessor() on either the
// instance or the prototype ObjectTemplate, based on |on_proto|.
struct BatchedAttribute {
const char* const name;
v8::AccessorGetter getter;
v8::AccessorSetter setter;
V8ClassIndex::V8WrapperType data;
v8::AccessControl settings;
v8::PropertyAttribute attribute;
bool on_proto;
};
void BatchConfigureAttributes(v8::Handle<v8::ObjectTemplate> inst,
v8::Handle<v8::ObjectTemplate> proto,
const BatchedAttribute* attrs,
size_t num_attrs);
// BatchedConstant translates into calls to Set() for setting up an object's
// constants. It sets the constant on both the FunctionTemplate |desc| and the
// ObjectTemplate |proto|. PropertyAttributes is always ReadOnly.
struct BatchedConstant {
const char* const name;
int value;
};
void BatchConfigureConstants(v8::Handle<v8::FunctionTemplate> desc,
v8::Handle<v8::ObjectTemplate> proto,
const BatchedConstant* consts,
size_t num_consts);
const int kMaxRecursionDepth = 20;
// Information about an extension that is registered for use with V8. If scheme
// is non-empty, it contains the URL scheme the extension should be used with.
// Otherwise, the extension is used with all schemes.
struct V8ExtensionInfo {
String scheme;
v8::Extension* extension;
};
typedef WTF::Vector<V8ExtensionInfo> V8ExtensionList;
class V8Proxy {
public:
// The types of javascript errors that can be thrown.
enum ErrorType {
RANGE_ERROR,
REFERENCE_ERROR,
SYNTAX_ERROR,
TYPE_ERROR,
GENERAL_ERROR
};
explicit V8Proxy(Frame* frame)
: m_frame(frame), m_event_listeners("m_event_listeners"),
m_xhr_listeners("m_xhr_listeners"), m_inlineCode(false),
m_timerCallback(false), m_recursion(0) { }
~V8Proxy();
Frame* frame() { return m_frame; }
// Clear page-specific data, but keep the global object identify.
void clearForNavigation();
// Clear page-specific data before shutting down the proxy object.
void clearForClose();
// Update document object of the frame.
void updateDocument();
// Update the security origin of a document
// (e.g., after setting docoument.domain).
void updateSecurityOrigin();
// Destroy the global object.
void DestroyGlobal();
// TODO(mpcomplete): Need comment. User Gesture related.
bool inlineCode() const { return m_inlineCode; }
void setInlineCode(bool value) { m_inlineCode = value; }
bool timerCallback() const { return m_timerCallback; }
void setTimerCallback(bool value) { m_timerCallback = value; }
// Has the context for this proxy been initialized?
bool ContextInitialized();
// Disconnects the proxy from its owner frame,
// and clears all timeouts on the DOM window.
void disconnectFrame();
bool isEnabled();
// Find/Create/Remove event listener wrappers.
PassRefPtr<V8EventListener> FindV8EventListener(v8::Local<v8::Value> listener,
bool html);
PassRefPtr<V8EventListener> FindOrCreateV8EventListener(v8::Local<v8::Value> listener,
bool html);
PassRefPtr<V8EventListener> FindObjectEventListener(v8::Local<v8::Value> listener,
bool html);
PassRefPtr<V8EventListener> FindOrCreateObjectEventListener(v8::Local<v8::Value> listener,
bool html);
void RemoveV8EventListener(V8EventListener* listener);
void RemoveObjectEventListener(V8ObjectEventListener* listener);
// Protect/Unprotect JS wrappers of a DOM object.
static void GCProtect(void* dom_object);
static void GCUnprotect(void* dom_object);
// Create a lazy event listener.
PassRefPtr<EventListener> createInlineEventListener(
const String& functionName, const String& code, Node* node);
#if ENABLE(SVG)
PassRefPtr<EventListener> createSVGEventHandler(
const String& functionName, const String& code, Node* node);
static void SetSVGContext(void* object, SVGElement* context);
static SVGElement* GetSVGContext(void* object);
#endif
void setEventHandlerLineno(int lineno) { m_handlerLineno = lineno; }
void finishedWithEvent(Event* event) { }
// Evaluate JavaScript in a new context. The script gets its own global scope
// and its own prototypes for intrinsic JavaScript objects (String, Array,
// and so-on). It shares the wrappers for all DOM nodes and DOM constructors.
void evaluateInNewContext(const Vector<ScriptSourceCode>& sources);
// Evaluate a script file in the current execution environment.
// The caller must hold an execution context.
// If cannot evalute the script, it returns an error.
v8::Local<v8::Value> evaluate(const ScriptSourceCode& source,
Node* node);
// Run an already compiled script.
v8::Local<v8::Value> RunScript(v8::Handle<v8::Script> script,
bool inline_code);
// Call the function with the given receiver and arguments.
v8::Local<v8::Value> CallFunction(v8::Handle<v8::Function> function,
v8::Handle<v8::Object> receiver,
int argc,
v8::Handle<v8::Value> argv[]);
// Returns the dom constructor function for the given node type.
v8::Local<v8::Function> GetConstructor(V8ClassIndex::V8WrapperType type);
// To create JS Wrapper objects, we create a cache of a 'boiler plate'
// object, and then simply Clone that object each time we need a new one.
// This is faster than going through the full object creation process.
v8::Local<v8::Object> CreateWrapperFromCache(V8ClassIndex::V8WrapperType type);
// Returns the window object of the currently executing context.
static DOMWindow* retrieveWindow();
// Returns the window object associated with a context.
static DOMWindow* retrieveWindow(v8::Handle<v8::Context> context);
// Returns V8Proxy object of the currently executing context.
static V8Proxy* retrieve();
// Returns V8Proxy object associated with a frame.
static V8Proxy* retrieve(Frame* frame);
// Returns V8Proxy object associated with a script execution context.
static V8Proxy* retrieve(ScriptExecutionContext* context);
// Returns the frame object of the window object associated
// with the currently executing context.
static Frame* retrieveFrame();
// Returns the frame object of the window object associated with
// a context.
static Frame* retrieveFrame(v8::Handle<v8::Context> context);
// Returns the frame that started JS execution.
// NOTE: cannot declare retrieveActiveFrame as inline function,
// VS complains at linking time.
static Frame* retrieveActiveFrame();
// Returns V8 Context of a frame. If none exists, creates
// a new context. It is potentially slow and consumes memory.
static v8::Local<v8::Context> GetContext(Frame* frame);
static v8::Local<v8::Context> GetCurrentContext();
// If the current context causes out of memory, JavaScript setting
// is disabled and it returns true.
static bool HandleOutOfMemory();
// Check if the active execution context can access the target frame.
static bool CanAccessFrame(Frame* target, bool report_error);
// Check if it is safe to access the given node from the
// current security context.
static bool CheckNodeSecurity(Node* node);
static v8::Handle<v8::Value> CheckNewLegal(const v8::Arguments& args);
// Create a V8 wrapper for a C pointer
static v8::Handle<v8::Value> WrapCPointer(void* cptr) {
// Represent void* as int
int addr = reinterpret_cast<int>(cptr);
ASSERT((addr & 0x01) == 0); // the address must be aligned.
return v8::Integer::New(addr >> 1);
}
// Take C pointer out of a v8 wrapper
template <class C>
static C* ExtractCPointer(v8::Handle<v8::Value> obj) {
return static_cast<C*>(ExtractCPointerImpl(obj));
}
static v8::Handle<v8::Script> CompileScript(v8::Handle<v8::String> code,
const String& fileName,
int baseLine);
#ifndef NDEBUG
// Checks if a v8 value can be a DOM wrapper
static bool MaybeDOMWrapper(v8::Handle<v8::Value> value);
#endif
// Sets contents of a DOM wrapper.
static void SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* ptr);
static v8::Handle<v8::Object> LookupDOMWrapper(
V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> value);
// A helper function extract native object pointer from a DOM wrapper
// and cast to the specified type.
template <class C>
static C* DOMWrapperToNative(v8::Handle<v8::Value> object) {
ASSERT(MaybeDOMWrapper(object));
v8::Handle<v8::Value> ptr =
v8::Handle<v8::Object>::Cast(object)->GetInternalField(
V8Custom::kDOMWrapperObjectIndex);
return ExtractCPointer<C>(ptr);
}
// A help function extract a node type pointer from a DOM wrapper.
// Wrapped pointer must be cast to Node* first.
template <class C>
static C* DOMWrapperToNode(v8::Handle<v8::Value> value) {
ASSERT(MaybeDOMWrapper(value));
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
ASSERT(GetDOMWrapperType(object) == V8ClassIndex::NODE);
v8::Handle<v8::Value> wrapper =
object->GetInternalField(V8Custom::kDOMWrapperObjectIndex);
return static_cast<C*>(ExtractCPointer<Node>(wrapper));
}
template<typename T>
static v8::Handle<v8::Value> ToV8Object(V8ClassIndex::V8WrapperType type, PassRefPtr<T> imp)
{
return ToV8Object(type, imp.get());
}
static v8::Handle<v8::Value> ToV8Object(V8ClassIndex::V8WrapperType type,
void* imp);
// Fast-path for Node objects.
static v8::Handle<v8::Value> NodeToV8Object(Node* node);
template <class C>
static C* ToNativeObject(V8ClassIndex::V8WrapperType type,
v8::Handle<v8::Value> object) {
return static_cast<C*>(ToNativeObjectImpl(type, object));
}
static V8ClassIndex::V8WrapperType GetDOMWrapperType(
v8::Handle<v8::Object> object);
// If the exception code is different from zero, a DOM exception is
// schedule to be thrown.
static void SetDOMException(int exception_code);
// Schedule an error object to be thrown.
static v8::Handle<v8::Value> ThrowError(ErrorType type, const char* message);
// Create an instance of a function descriptor and set to the global object
// as a named property. Used by v8_test_shell.
static void BindJSObjectToWindow(Frame* frame,
const char* name,
int type,
v8::Handle<v8::FunctionTemplate> desc,
void* imp);
static v8::Handle<v8::Value> EventToV8Object(Event* event);
static Event* ToNativeEvent(v8::Handle<v8::Value> jsevent) {
if (!IsDOMEventWrapper(jsevent)) return 0;
return DOMWrapperToNative<Event>(jsevent);
}
static v8::Handle<v8::Value> EventTargetToV8Object(EventTarget* target);
// Wrap and unwrap JS event listeners
static v8::Handle<v8::Value> EventListenerToV8Object(EventListener* target);
// DOMImplementation is a singleton and it is handled in a special
// way. A wrapper is generated per document and stored in an
// internal field of the document.
static v8::Handle<v8::Value> DOMImplementationToV8Object(
DOMImplementation* impl);
// Wrap JS node filter in C++
static PassRefPtr<NodeFilter> ToNativeNodeFilter(v8::Handle<v8::Value> filter);
static v8::Persistent<v8::FunctionTemplate> GetTemplate(
V8ClassIndex::V8WrapperType type);
template <int tag, typename T>
static v8::Handle<v8::Value> ConstructDOMObject(const v8::Arguments& args);
// Checks whether a DOM object has a JS wrapper.
static bool DOMObjectHasJSWrapper(void* obj);
// Set JS wrapper of a DOM object, the caller in charge of increase ref.
static void SetJSWrapperForDOMObject(void* obj,
v8::Persistent<v8::Object> wrapper);
static void SetJSWrapperForActiveDOMObject(void* obj,
v8::Persistent<v8::Object> wrapper);
static void SetJSWrapperForDOMNode(Node* node,
v8::Persistent<v8::Object> wrapper);
// Process any pending JavaScript console messages.
static void ProcessConsoleMessages();
#ifndef NDEBUG
// For debugging and leak detection purpose
static void RegisterGlobalHandle(GlobalHandleType type,
void* host,
v8::Persistent<v8::Value> handle);
static void UnregisterGlobalHandle(void* host,
v8::Persistent<v8::Value> handle);
#endif
// Check whether a V8 value is a wrapper of type |classType|.
static bool IsWrapperOfType(v8::Handle<v8::Value> obj,
V8ClassIndex::V8WrapperType classType);
// Function for retrieving the line number and source name for the top
// JavaScript stack frame.
static int GetSourceLineNumber();
static String GetSourceName();
// Returns a local handle of the context.
v8::Local<v8::Context> GetContext() {
return v8::Local<v8::Context>::New(m_context);
}
// Registers an extension to be available on webpages with a particular scheme
// If the scheme argument is empty, the extension is available on all pages.
// Will only affect v8 contexts initialized after this call. Takes ownership
// of the v8::Extension object passed.
static void RegisterExtension(v8::Extension* extension,
const String& schemeRestriction);
private:
v8::Persistent<v8::Context> createNewContext(v8::Handle<v8::Object> global);
void InitContextIfNeeded();
void DisconnectEventListeners();
void SetSecurityToken();
void ClearDocumentWrapper();
void UpdateDocumentWrapper(v8::Handle<v8::Value> wrapper);
// Dispose global handles of m_contexts and friends.
void DisposeContextHandles();
static bool CanAccessPrivate(DOMWindow* target);
// Check whether a V8 value is a DOM Event wrapper
static bool IsDOMEventWrapper(v8::Handle<v8::Value> obj);
static void* ToNativeObjectImpl(V8ClassIndex::V8WrapperType type,
v8::Handle<v8::Value> object);
// Take C pointer out of a v8 wrapper
static void* ExtractCPointerImpl(v8::Handle<v8::Value> obj) {
ASSERT(obj->IsNumber());
int addr = obj->Int32Value();
return reinterpret_cast<void*>(addr << 1);
}
static v8::Handle<v8::Value> StyleSheetToV8Object(StyleSheet* sheet);
static v8::Handle<v8::Value> CSSValueToV8Object(CSSValue* value);
static v8::Handle<v8::Value> CSSRuleToV8Object(CSSRule* rule);
// Returns the JS wrapper of a window object, initializes the environment
// of the window frame if needed.
static v8::Handle<v8::Value> WindowToV8Object(DOMWindow* window);
#if ENABLE(SVG)
static v8::Handle<v8::Value> SVGElementInstanceToV8Object(
SVGElementInstance* instance);
static v8::Handle<v8::Value> SVGObjectWithContextToV8Object(
V8ClassIndex::V8WrapperType type, void* object);
#endif
// Set hidden references in a DOMWindow object of a frame.
static void SetHiddenWindowReference(Frame* frame,
const int internal_index,
v8::Handle<v8::Object> jsobj);
static V8ClassIndex::V8WrapperType GetHTMLElementType(HTMLElement* elm);
// The first parameter, desc_type, specifies the function descriptor
// used to create JS object. The second parameter, cptr_type, specifies
// the type of third parameter, impl, for type casting.
// For example, a HTML element has HTMLELEMENT desc_type, but always
// use NODE as cptr_type. JS wrapper stores cptr_type and impl as
// internal fields.
static v8::Local<v8::Object> InstantiateV8Object(
V8ClassIndex::V8WrapperType desc_type,
V8ClassIndex::V8WrapperType cptr_type,
void* impl);
static const char* GetRangeExceptionName(int exception_code);
static const char* GetEventExceptionName(int exception_code);
static const char* GetXMLHttpRequestExceptionName(int exception_code);
static const char* GetDOMExceptionName(int exception_code);
#if ENABLE(XPATH)
static const char* GetXPathExceptionName(int exception_code);
#endif
#if ENABLE(SVG)
static V8ClassIndex::V8WrapperType GetSVGElementType(SVGElement* elm);
static const char* GetSVGExceptionName(int exception_code);
#endif
// Create and populate the utility context.
static void CreateUtilityContext();
// Returns a local handle of the utility context.
static v8::Local<v8::Context> GetUtilityContext() {
if (m_utilityContext.IsEmpty()) {
CreateUtilityContext();
}
return v8::Local<v8::Context>::New(m_utilityContext);
}
Frame* m_frame;
v8::Persistent<v8::Context> m_context;
// For each possible type of wrapper, we keep a boilerplate object.
// The boilerplate is used to create additional wrappers of the same type.
// We keep a single persistent handle to an array of the activated
// boilerplates.
v8::Persistent<v8::Array> m_wrapper_boilerplates;
v8::Persistent<v8::Value> m_object_prototype;
v8::Persistent<v8::Object> m_global;
v8::Persistent<v8::Value> m_document;
// Utility context holding JavaScript functions used internally.
static v8::Persistent<v8::Context> m_utilityContext;
int m_handlerLineno;
// A list of event listeners created for this frame,
// the list gets cleared when removing all timeouts.
V8EventListenerList m_event_listeners;
// A list of event listeners create for XMLHttpRequest object for this frame,
// the list gets cleared when removing all timeouts.
V8EventListenerList m_xhr_listeners;
// True for <a href="javascript:foo()"> and false for <script>foo()</script>.
// Only valid during execution.
bool m_inlineCode;
// True when executing from within a timer callback. Only valid during
// execution.
bool m_timerCallback;
// Track the recursion depth to be able to avoid too deep recursion. The V8
// engine allows much more recursion than KJS does so we need to guard against
// excessive recursion in the binding layer.
int m_recursion;
// List of extensions registered with the context.
static V8ExtensionList m_extensions;
};
template <int tag, typename T>
v8::Handle<v8::Value> V8Proxy::ConstructDOMObject(const v8::Arguments& args) {
if (!args.IsConstructCall()) {
V8Proxy::ThrowError(V8Proxy::TYPE_ERROR,
"DOM object constructor cannot be called as a function.");
return v8::Undefined();
}
// Note: it's OK to let this RefPtr go out of scope because we also call
// SetDOMWrapper(), which effectively holds a reference to obj.
RefPtr<T> obj = T::create();
V8Proxy::SetDOMWrapper(args.Holder(), tag, obj.get());
obj->ref();
V8Proxy::SetJSWrapperForDOMObject(
obj.get(), v8::Persistent<v8::Object>::New(args.Holder()));
return args.Holder();
}
} // namespace WebCore
#endif // V8_PROXY_H__