diff options
Diffstat (limited to 'WebCore/bindings/v8/V8DOMWrapper.cpp')
-rw-r--r-- | WebCore/bindings/v8/V8DOMWrapper.cpp | 1465 |
1 files changed, 1465 insertions, 0 deletions
diff --git a/WebCore/bindings/v8/V8DOMWrapper.cpp b/WebCore/bindings/v8/V8DOMWrapper.cpp new file mode 100644 index 0000000..33d1147 --- /dev/null +++ b/WebCore/bindings/v8/V8DOMWrapper.cpp @@ -0,0 +1,1465 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +#include "config.h" +#include "V8DOMWrapper.h" + +#include "ChromiumBridge.h" +#include "CSSMutableStyleDeclaration.h" +#include "DOMObjectsInclude.h" +#include "DocumentLoader.h" +#include "FrameLoaderClient.h" +#include "ScriptController.h" +#include "V8Binding.h" +#include "V8Collection.h" +#include "V8CustomBinding.h" +#include "V8DOMMap.h" +#include "V8DOMWindow.h" +#include "V8Index.h" +#include "V8IsolatedWorld.h" +#include "WorkerContextExecutionProxy.h" + +#include <algorithm> +#include <utility> +#include <v8.h> +#include <v8-debug.h> +#include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/UnusedParam.h> + +namespace WebCore { + +typedef HashMap<Node*, v8::Object*> DOMNodeMap; +typedef HashMap<void*, v8::Object*> DOMObjectMap; + +// Get the string 'toString'. +static v8::Persistent<v8::String> GetToStringName() +{ + DEFINE_STATIC_LOCAL(v8::Persistent<v8::String>, value, ()); + if (value.IsEmpty()) + value = v8::Persistent<v8::String>::New(v8::String::New("toString")); + return value; +} + +static v8::Handle<v8::Value> ConstructorToString(const v8::Arguments& args) +{ + // The DOM constructors' toString functions grab the current toString + // for Functions by taking the toString function of itself and then + // calling it with the constructor as its receiver. This means that + // changes to the Function prototype chain or toString function are + // reflected when printing DOM constructors. The only wart is that + // changes to a DOM constructor's toString's toString will cause the + // toString of the DOM constructor itself to change. This is extremely + // obscure and unlikely to be a problem. + v8::Handle<v8::Value> value = args.Callee()->Get(GetToStringName()); + if (!value->IsFunction()) + return v8::String::New(""); + return v8::Handle<v8::Function>::Cast(value)->Call(args.This(), 0, 0); +} + +#if ENABLE(SVG) +v8::Handle<v8::Value> V8DOMWrapper::convertSVGElementInstanceToV8Object(SVGElementInstance* instance) +{ + if (!instance) + return v8::Null(); + + v8::Handle<v8::Object> existingInstance = getDOMSVGElementInstanceMap().get(instance); + if (!existingInstance.IsEmpty()) + return existingInstance; + + instance->ref(); + + // Instantiate the V8 object and remember it + v8::Handle<v8::Object> result = instantiateV8Object(V8ClassIndex::SVGELEMENTINSTANCE, V8ClassIndex::SVGELEMENTINSTANCE, instance); + if (!result.IsEmpty()) { + // Only update the DOM SVG element map if the result is non-empty. + getDOMSVGElementInstanceMap().set(instance, v8::Persistent<v8::Object>::New(result)); + } + return result; +} + +v8::Handle<v8::Value> V8DOMWrapper::convertSVGObjectWithContextToV8Object(V8ClassIndex::V8WrapperType type, void* object) +{ + if (!object) + return v8::Null(); + + v8::Persistent<v8::Object> result = getDOMSVGObjectWithContextMap().get(object); + if (!result.IsEmpty()) + return result; + + // Special case: SVGPathSegs need to be downcast to their real type + if (type == V8ClassIndex::SVGPATHSEG) + type = V8Custom::DowncastSVGPathSeg(object); + + v8::Local<v8::Object> v8Object = instantiateV8Object(type, type, object); + if (!v8Object.IsEmpty()) { + result = v8::Persistent<v8::Object>::New(v8Object); + switch (type) { +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: static_cast<NAME*>(object)->ref(); break; + SVG_OBJECT_TYPES(MAKE_CASE) +#undef MAKE_CASE +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: \ + static_cast<V8SVGPODTypeWrapper<NAME>*>(object)->ref(); break; + SVG_POD_NATIVE_TYPES(MAKE_CASE) +#undef MAKE_CASE + default: + ASSERT_NOT_REACHED(); + } + getDOMSVGObjectWithContextMap().set(object, result); + } + + return result; +} + +#endif + +bool V8DOMWrapper::domObjectHasJSWrapper(void* object) +{ + return getDOMObjectMap().contains(object) || getActiveDOMObjectMap().contains(object); +} + +// The caller must have increased obj's ref count. +void V8DOMWrapper::setJSWrapperForDOMObject(void* object, v8::Persistent<v8::Object> wrapper) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper)); +#ifndef NDEBUG + V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper); + switch (type) { +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: + ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + ASSERT_NOT_REACHED(); +#undef MAKE_CASE + default: + break; + } +#endif + getDOMObjectMap().set(object, wrapper); +} + +// The caller must have increased obj's ref count. +void V8DOMWrapper::setJSWrapperForActiveDOMObject(void* object, v8::Persistent<v8::Object> wrapper) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper)); +#ifndef NDEBUG + V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper); + switch (type) { +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: break; + ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + default: + ASSERT_NOT_REACHED(); +#undef MAKE_CASE + } +#endif + getActiveDOMObjectMap().set(object, wrapper); +} + +// The caller must have increased node's ref count. +void V8DOMWrapper::setJSWrapperForDOMNode(Node* node, v8::Persistent<v8::Object> wrapper) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper)); + getDOMNodeMap().set(node, wrapper); +} + +v8::Persistent<v8::FunctionTemplate> V8DOMWrapper::getTemplate(V8ClassIndex::V8WrapperType type) +{ + v8::Persistent<v8::FunctionTemplate>* cacheCell = V8ClassIndex::GetCache(type); + if (!cacheCell->IsEmpty()) + return *cacheCell; + + // Not in the cache. + FunctionTemplateFactory factory = V8ClassIndex::GetFactory(type); + v8::Persistent<v8::FunctionTemplate> descriptor = factory(); + // DOM constructors are functions and should print themselves as such. + // However, we will later replace their prototypes with Object + // prototypes so we need to explicitly override toString on the + // instance itself. If we later make DOM constructors full objects + // we can give them class names instead and Object.prototype.toString + // will work so we can remove this code. + DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, toStringTemplate, ()); + if (toStringTemplate.IsEmpty()) + toStringTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(ConstructorToString)); + descriptor->Set(GetToStringName(), toStringTemplate); + switch (type) { + case V8ClassIndex::CSSSTYLEDECLARATION: + // The named property handler for style declarations has a + // setter. Therefore, the interceptor has to be on the object + // itself and not on the prototype object. + descriptor->InstanceTemplate()->SetNamedPropertyHandler( USE_NAMED_PROPERTY_GETTER(CSSStyleDeclaration), USE_NAMED_PROPERTY_SETTER(CSSStyleDeclaration)); + setCollectionStringOrNullIndexedGetter<CSSStyleDeclaration>(descriptor); + break; + case V8ClassIndex::CSSRULELIST: + setCollectionIndexedGetter<CSSRuleList, CSSRule>(descriptor, V8ClassIndex::CSSRULE); + break; + case V8ClassIndex::CSSVALUELIST: + setCollectionIndexedGetter<CSSValueList, CSSValue>(descriptor, V8ClassIndex::CSSVALUE); + break; + case V8ClassIndex::CSSVARIABLESDECLARATION: + setCollectionStringOrNullIndexedGetter<CSSVariablesDeclaration>(descriptor); + break; + case V8ClassIndex::WEBKITCSSTRANSFORMVALUE: + setCollectionIndexedGetter<WebKitCSSTransformValue, CSSValue>(descriptor, V8ClassIndex::CSSVALUE); + break; + case V8ClassIndex::HTMLALLCOLLECTION: + descriptor->InstanceTemplate()->MarkAsUndetectable(); // fall through + case V8ClassIndex::HTMLCOLLECTION: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(HTMLCollection)); + descriptor->InstanceTemplate()->SetCallAsFunctionHandler(USE_CALLBACK(HTMLCollectionCallAsFunction)); + setCollectionIndexedGetter<HTMLCollection, Node>(descriptor, V8ClassIndex::NODE); + break; + case V8ClassIndex::HTMLOPTIONSCOLLECTION: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(HTMLCollection)); + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(HTMLOptionsCollection), USE_INDEXED_PROPERTY_SETTER(HTMLOptionsCollection)); + descriptor->InstanceTemplate()->SetCallAsFunctionHandler(USE_CALLBACK(HTMLCollectionCallAsFunction)); + break; + case V8ClassIndex::HTMLSELECTELEMENT: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(HTMLSelectElementCollection)); + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(nodeCollectionIndexedPropertyGetter<HTMLSelectElement>, USE_INDEXED_PROPERTY_SETTER(HTMLSelectElementCollection), + 0, 0, nodeCollectionIndexedPropertyEnumerator<HTMLSelectElement>, v8::Integer::New(V8ClassIndex::NODE)); + break; + case V8ClassIndex::HTMLDOCUMENT: { + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(HTMLDocument), 0, 0, USE_NAMED_PROPERTY_DELETER(HTMLDocument)); + + // We add an extra internal field to all Document wrappers for + // storing a per document DOMImplementation wrapper. + // + // Additionally, we add two extra internal fields for + // HTMLDocuments to implement temporary shadowing of + // document.all. One field holds an object that is used as a + // marker. The other field holds the marker object if + // document.all is not shadowed and some other value if + // document.all is shadowed. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + ASSERT(instanceTemplate->InternalFieldCount() == V8Custom::kNodeMinimumInternalFieldCount); + instanceTemplate->SetInternalFieldCount(V8Custom::kHTMLDocumentInternalFieldCount); + break; + } +#if ENABLE(SVG) + case V8ClassIndex::SVGDOCUMENT: // fall through +#endif + case V8ClassIndex::DOCUMENT: { + // We add an extra internal field to all Document wrappers for + // storing a per document DOMImplementation wrapper. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + ASSERT(instanceTemplate->InternalFieldCount() == V8Custom::kNodeMinimumInternalFieldCount); + instanceTemplate->SetInternalFieldCount( V8Custom::kDocumentMinimumInternalFieldCount); + break; + } + case V8ClassIndex::HTMLAPPLETELEMENT: // fall through + case V8ClassIndex::HTMLEMBEDELEMENT: // fall through + case V8ClassIndex::HTMLOBJECTELEMENT: + // HTMLAppletElement, HTMLEmbedElement and HTMLObjectElement are + // inherited from HTMLPlugInElement, and they share the same property + // handling code. + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(HTMLPlugInElement), USE_NAMED_PROPERTY_SETTER(HTMLPlugInElement)); + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(HTMLPlugInElement), USE_INDEXED_PROPERTY_SETTER(HTMLPlugInElement)); + descriptor->InstanceTemplate()->SetCallAsFunctionHandler(USE_CALLBACK(HTMLPlugInElement)); + break; + case V8ClassIndex::HTMLFRAMESETELEMENT: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(HTMLFrameSetElement)); + break; + case V8ClassIndex::HTMLFORMELEMENT: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(HTMLFormElement)); + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(HTMLFormElement), 0, 0, 0, nodeCollectionIndexedPropertyEnumerator<HTMLFormElement>, v8::Integer::New(V8ClassIndex::NODE)); + break; + case V8ClassIndex::STYLESHEET: // fall through + case V8ClassIndex::CSSSTYLESHEET: { + // We add an extra internal field to hold a reference to + // the owner node. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + ASSERT(instanceTemplate->InternalFieldCount() == V8Custom::kDefaultWrapperInternalFieldCount); + instanceTemplate->SetInternalFieldCount(V8Custom::kStyleSheetInternalFieldCount); + break; + } + case V8ClassIndex::MEDIALIST: + setCollectionStringOrNullIndexedGetter<MediaList>(descriptor); + break; + case V8ClassIndex::MIMETYPEARRAY: + setCollectionIndexedAndNamedGetters<MimeTypeArray, MimeType>(descriptor, V8ClassIndex::MIMETYPE); + break; + case V8ClassIndex::NAMEDNODEMAP: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(NamedNodeMap)); + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(NamedNodeMap), 0, 0, 0, collectionIndexedPropertyEnumerator<NamedNodeMap>, v8::Integer::New(V8ClassIndex::NODE)); + break; +#if ENABLE(DOM_STORAGE) + case V8ClassIndex::STORAGE: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(Storage), USE_NAMED_PROPERTY_SETTER(Storage), 0, USE_NAMED_PROPERTY_DELETER(Storage), V8Custom::v8StorageNamedPropertyEnumerator); + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(Storage), USE_INDEXED_PROPERTY_SETTER(Storage), 0, USE_INDEXED_PROPERTY_DELETER(Storage)); + break; +#endif + case V8ClassIndex::NODELIST: + setCollectionIndexedGetter<NodeList, Node>(descriptor, V8ClassIndex::NODE); + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(NodeList)); + break; + case V8ClassIndex::PLUGIN: + setCollectionIndexedAndNamedGetters<Plugin, MimeType>(descriptor, V8ClassIndex::MIMETYPE); + break; + case V8ClassIndex::PLUGINARRAY: + setCollectionIndexedAndNamedGetters<PluginArray, Plugin>(descriptor, V8ClassIndex::PLUGIN); + break; + case V8ClassIndex::STYLESHEETLIST: + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(StyleSheetList)); + setCollectionIndexedGetter<StyleSheetList, StyleSheet>(descriptor, V8ClassIndex::STYLESHEET); + break; + case V8ClassIndex::DOMWINDOW: { + v8::Local<v8::Signature> defaultSignature = v8::Signature::New(descriptor); + + descriptor->PrototypeTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(DOMWindow)); + descriptor->PrototypeTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(DOMWindow)); + + descriptor->SetHiddenPrototype(true); + + // Reserve spaces for references to location, history and + // navigator objects. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kDOMWindowInternalFieldCount); + + // Set access check callbacks, but turned off initially. + // When a context is detached from a frame, turn on the access check. + // Turning on checks also invalidates inline caches of the object. + instanceTemplate->SetAccessCheckCallbacks(V8Custom::v8DOMWindowNamedSecurityCheck, V8Custom::v8DOMWindowIndexedSecurityCheck, v8::Integer::New(V8ClassIndex::DOMWINDOW), false); + break; + } + case V8ClassIndex::LOCATION: { + // For security reasons, these functions are on the instance + // instead of on the prototype object to insure that they cannot + // be overwritten. + v8::Local<v8::ObjectTemplate> instance = descriptor->InstanceTemplate(); + instance->SetAccessor(v8::String::New("reload"), V8Custom::v8LocationReloadAccessorGetter, 0, v8::Handle<v8::Value>(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly)); + instance->SetAccessor(v8::String::New("replace"), V8Custom::v8LocationReplaceAccessorGetter, 0, v8::Handle<v8::Value>(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly)); + instance->SetAccessor(v8::String::New("assign"), V8Custom::v8LocationAssignAccessorGetter, 0, v8::Handle<v8::Value>(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly)); + break; + } + case V8ClassIndex::HISTORY: + break; + + case V8ClassIndex::MESSAGECHANNEL: { + // Reserve two more internal fields for referencing the port1 + // and port2 wrappers. This ensures that the port wrappers are + // kept alive when the channel wrapper is. + descriptor->SetCallHandler(USE_CALLBACK(MessageChannelConstructor)); + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kMessageChannelInternalFieldCount); + break; + } + + case V8ClassIndex::MESSAGEPORT: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kMessagePortInternalFieldCount); + break; + } + +#if ENABLE(WORKERS) + case V8ClassIndex::ABSTRACTWORKER: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kAbstractWorkerInternalFieldCount); + break; + } + + case V8ClassIndex::DEDICATEDWORKERCONTEXT: { + // Reserve internal fields for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + ASSERT(instanceTemplate->InternalFieldCount() == V8Custom::kDefaultWrapperInternalFieldCount); + instanceTemplate->SetInternalFieldCount(V8Custom::kDedicatedWorkerContextInternalFieldCount); + break; + } + + case V8ClassIndex::WORKER: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kWorkerInternalFieldCount); + descriptor->SetCallHandler(USE_CALLBACK(WorkerConstructor)); + break; + } + + case V8ClassIndex::WORKERCONTEXT: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + ASSERT(instanceTemplate->InternalFieldCount() == V8Custom::kDefaultWrapperInternalFieldCount); + instanceTemplate->SetInternalFieldCount(V8Custom::kWorkerContextMinimumInternalFieldCount); + break; + } + +#endif // WORKERS + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + case V8ClassIndex::DOMAPPLICATIONCACHE: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kDOMApplicationCacheFieldCount); + break; + } +#endif + + // The following objects are created from JavaScript. + case V8ClassIndex::DOMPARSER: + descriptor->SetCallHandler(USE_CALLBACK(DOMParserConstructor)); + break; +#if ENABLE(VIDEO) + case V8ClassIndex::HTMLAUDIOELEMENT: + descriptor->SetCallHandler(USE_CALLBACK(HTMLAudioElementConstructor)); + break; +#endif + case V8ClassIndex::HTMLIMAGEELEMENT: + descriptor->SetCallHandler(USE_CALLBACK(HTMLImageElementConstructor)); + break; + case V8ClassIndex::HTMLOPTIONELEMENT: + descriptor->SetCallHandler(USE_CALLBACK(HTMLOptionElementConstructor)); + break; + case V8ClassIndex::WEBKITCSSMATRIX: + descriptor->SetCallHandler(USE_CALLBACK(WebKitCSSMatrixConstructor)); + break; + case V8ClassIndex::WEBKITPOINT: + descriptor->SetCallHandler(USE_CALLBACK(WebKitPointConstructor)); + break; + case V8ClassIndex::XMLSERIALIZER: + descriptor->SetCallHandler(USE_CALLBACK(XMLSerializerConstructor)); + break; + case V8ClassIndex::XMLHTTPREQUEST: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kXMLHttpRequestInternalFieldCount); + descriptor->SetCallHandler(USE_CALLBACK(XMLHttpRequestConstructor)); + break; + } + case V8ClassIndex::XMLHTTPREQUESTUPLOAD: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instanceTemplate = descriptor->InstanceTemplate(); + instanceTemplate->SetInternalFieldCount(V8Custom::kXMLHttpRequestInternalFieldCount); + break; + } + case V8ClassIndex::XPATHEVALUATOR: + descriptor->SetCallHandler(USE_CALLBACK(XPathEvaluatorConstructor)); + break; + case V8ClassIndex::XSLTPROCESSOR: + descriptor->SetCallHandler(USE_CALLBACK(XSLTProcessorConstructor)); + break; + case V8ClassIndex::CLIENTRECTLIST: + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(ClientRectList)); + break; +#if ENABLE(DATAGRID) + case V8ClassIndex::DATAGRIDCOLUMNLIST: + descriptor->InstanceTemplate()->SetIndexedPropertyHandler(USE_INDEXED_PROPERTY_GETTER(DataGridColumnList)); + descriptor->InstanceTemplate()->SetNamedPropertyHandler(USE_NAMED_PROPERTY_GETTER(DataGridColumnList)); + break; +#endif + default: + break; + } + + *cacheCell = descriptor; + return descriptor; +} + +v8::Local<v8::Function> V8DOMWrapper::getConstructor(V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> objectPrototype) +{ + // A DOM constructor is a function instance created from a DOM constructor + // template. There is one instance per context. A DOM constructor is + // different from a normal function in two ways: + // 1) it cannot be called as constructor (aka, used to create a DOM object) + // 2) its __proto__ points to Object.prototype rather than + // Function.prototype. + // The reason for 2) is that, in Safari, a DOM constructor is a normal JS + // object, but not a function. Hotmail relies on the fact that, in Safari, + // HTMLElement.__proto__ == Object.prototype. + v8::Handle<v8::FunctionTemplate> functionTemplate = getTemplate(type); + // Getting the function might fail if we're running out of + // stack or memory. + v8::TryCatch tryCatch; + v8::Local<v8::Function> value = functionTemplate->GetFunction(); + if (value.IsEmpty()) + return v8::Local<v8::Function>(); + // Hotmail fix, see comments above. + if (!objectPrototype.IsEmpty()) + value->Set(v8::String::New("__proto__"), objectPrototype); + return value; +} + +v8::Local<v8::Function> V8DOMWrapper::getConstructorForContext(V8ClassIndex::V8WrapperType type, v8::Handle<v8::Context> context) +{ + // Enter the scope for this context to get the correct constructor. + v8::Context::Scope scope(context); + + return getConstructor(type, V8Proxy::getHiddenObjectPrototype(context)); +} + +v8::Local<v8::Function> V8DOMWrapper::getConstructor(V8ClassIndex::V8WrapperType type, DOMWindow* window) +{ + Frame* frame = window->frame(); + if (!frame) + return v8::Local<v8::Function>(); + + v8::Handle<v8::Context> context = V8Proxy::context(frame); + if (context.IsEmpty()) + return v8::Local<v8::Function>(); + + return getConstructorForContext(type, context); +} + +v8::Local<v8::Function> V8DOMWrapper::getConstructor(V8ClassIndex::V8WrapperType type, WorkerContext*) +{ + WorkerContextExecutionProxy* proxy = WorkerContextExecutionProxy::retrieve(); + if (!proxy) + return v8::Local<v8::Function>(); + + v8::Handle<v8::Context> context = proxy->context(); + if (context.IsEmpty()) + return v8::Local<v8::Function>(); + + return getConstructorForContext(type, context); +} + +v8::Handle<v8::Value> V8DOMWrapper::convertToV8Object(V8ClassIndex::V8WrapperType type, void* impl) +{ + ASSERT(type != V8ClassIndex::EVENTLISTENER); + ASSERT(type != V8ClassIndex::EVENTTARGET); + ASSERT(type != V8ClassIndex::EVENT); + + // These objects can be constructed under WorkerContextExecutionProxy. They need special + // handling, since if we proceed below V8Proxy::retrieve() will get called and will crash. + if ((type == V8ClassIndex::DOMCOREEXCEPTION + || type == V8ClassIndex::RANGEEXCEPTION + || type == V8ClassIndex::EVENTEXCEPTION + || type == V8ClassIndex::XMLHTTPREQUESTEXCEPTION + || type == V8ClassIndex::MESSAGEPORT) + && WorkerContextExecutionProxy::retrieve()) { + return WorkerContextExecutionProxy::convertToV8Object(type, impl); + } + + bool isActiveDomObject = false; + switch (type) { +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: + DOM_NODE_TYPES(MAKE_CASE) +#if ENABLE(SVG) + SVG_NODE_TYPES(MAKE_CASE) +#endif + return convertNodeToV8Object(static_cast<Node*>(impl)); + case V8ClassIndex::CSSVALUE: + return convertCSSValueToV8Object(static_cast<CSSValue*>(impl)); + case V8ClassIndex::CSSRULE: + return convertCSSRuleToV8Object(static_cast<CSSRule*>(impl)); + case V8ClassIndex::STYLESHEET: + return convertStyleSheetToV8Object(static_cast<StyleSheet*>(impl)); + case V8ClassIndex::DOMWINDOW: + return convertWindowToV8Object(static_cast<DOMWindow*>(impl)); +#if ENABLE(SVG) + SVG_NONNODE_TYPES(MAKE_CASE) + if (type == V8ClassIndex::SVGELEMENTINSTANCE) + return convertSVGElementInstanceToV8Object(static_cast<SVGElementInstance*>(impl)); + return convertSVGObjectWithContextToV8Object(type, impl); +#endif + + ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE) + isActiveDomObject = true; + break; + default: + break; + } + +#undef MAKE_CASE + + if (!impl) + return v8::Null(); + + // Non DOM node + v8::Persistent<v8::Object> result = isActiveDomObject ? getActiveDOMObjectMap().get(impl) : getDOMObjectMap().get(impl); + if (result.IsEmpty()) { + v8::Local<v8::Object> v8Object = instantiateV8Object(type, type, impl); + if (!v8Object.IsEmpty()) { + // Go through big switch statement, it has some duplications + // that were handled by code above (such as CSSVALUE, CSSRULE, etc). + switch (type) { +#define MAKE_CASE(TYPE, NAME) \ + case V8ClassIndex::TYPE: static_cast<NAME*>(impl)->ref(); break; + DOM_OBJECT_TYPES(MAKE_CASE) +#undef MAKE_CASE + default: + ASSERT_NOT_REACHED(); + } + result = v8::Persistent<v8::Object>::New(v8Object); + if (isActiveDomObject) + setJSWrapperForActiveDOMObject(impl, result); + else + setJSWrapperForDOMObject(impl, result); + + if (type == V8ClassIndex::CANVASPIXELARRAY) { + CanvasPixelArray* pixels = reinterpret_cast<CanvasPixelArray*>(impl); + result->SetIndexedPropertiesToPixelData(pixels->data()->data(), pixels->length()); + } + + // Special case for non-node objects associated with a + // DOMWindow. Both Safari and FF let the JS wrappers for these + // objects survive GC. To mimic their behavior, V8 creates + // hidden references from the DOMWindow to these wrapper + // objects. These references get cleared when the DOMWindow is + // reused by a new page. + switch (type) { + case V8ClassIndex::CONSOLE: + setHiddenWindowReference(static_cast<Console*>(impl)->frame(), V8Custom::kDOMWindowConsoleIndex, result); + break; + case V8ClassIndex::HISTORY: + setHiddenWindowReference(static_cast<History*>(impl)->frame(), V8Custom::kDOMWindowHistoryIndex, result); + break; + case V8ClassIndex::NAVIGATOR: + setHiddenWindowReference(static_cast<Navigator*>(impl)->frame(), V8Custom::kDOMWindowNavigatorIndex, result); + break; + case V8ClassIndex::SCREEN: + setHiddenWindowReference(static_cast<Screen*>(impl)->frame(), V8Custom::kDOMWindowScreenIndex, result); + break; + case V8ClassIndex::LOCATION: + setHiddenWindowReference(static_cast<Location*>(impl)->frame(), V8Custom::kDOMWindowLocationIndex, result); + break; + case V8ClassIndex::DOMSELECTION: + setHiddenWindowReference(static_cast<DOMSelection*>(impl)->frame(), V8Custom::kDOMWindowDOMSelectionIndex, result); + break; + case V8ClassIndex::BARINFO: { + BarInfo* barInfo = static_cast<BarInfo*>(impl); + Frame* frame = barInfo->frame(); + switch (barInfo->type()) { + case BarInfo::Locationbar: + setHiddenWindowReference(frame, V8Custom::kDOMWindowLocationbarIndex, result); + break; + case BarInfo::Menubar: + setHiddenWindowReference(frame, V8Custom::kDOMWindowMenubarIndex, result); + break; + case BarInfo::Personalbar: + setHiddenWindowReference(frame, V8Custom::kDOMWindowPersonalbarIndex, result); + break; + case BarInfo::Scrollbars: + setHiddenWindowReference(frame, V8Custom::kDOMWindowScrollbarsIndex, result); + break; + case BarInfo::Statusbar: + setHiddenWindowReference(frame, V8Custom::kDOMWindowStatusbarIndex, result); + break; + case BarInfo::Toolbar: + setHiddenWindowReference(frame, V8Custom::kDOMWindowToolbarIndex, result); + break; + } + break; + } + default: + break; + } + } + } + return result; +} + +void V8DOMWrapper::setHiddenWindowReference(Frame* frame, const int internalIndex, v8::Handle<v8::Object> jsObject) +{ + // Get DOMWindow + if (!frame) + return; // Object might be detached from window + v8::Handle<v8::Context> context = V8Proxy::context(frame); + if (context.IsEmpty()) + return; + + ASSERT(internalIndex < V8Custom::kDOMWindowInternalFieldCount); + + v8::Handle<v8::Object> global = context->Global(); + // Look for real DOM wrapper. + global = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, global); + ASSERT(!global.IsEmpty()); + ASSERT(global->GetInternalField(internalIndex)->IsUndefined()); + global->SetInternalField(internalIndex, jsObject); +} + +V8ClassIndex::V8WrapperType V8DOMWrapper::domWrapperType(v8::Handle<v8::Object> object) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(object)); + v8::Handle<v8::Value> type = object->GetInternalField(V8Custom::kDOMWrapperTypeIndex); + return V8ClassIndex::FromInt(type->Int32Value()); +} + +void* V8DOMWrapper::convertToSVGPODTypeImpl(V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> object) +{ + return isWrapperOfType(object, type) ? convertDOMWrapperToNative<void>(v8::Handle<v8::Object>::Cast(object)) : 0; +} + +PassRefPtr<NodeFilter> V8DOMWrapper::wrapNativeNodeFilter(v8::Handle<v8::Value> filter) +{ + // A NodeFilter is used when walking through a DOM tree or iterating tree + // nodes. + // FIXME: we may want to cache NodeFilterCondition and NodeFilter + // object, but it is minor. + // NodeFilter is passed to NodeIterator that has a ref counted pointer + // to NodeFilter. NodeFilter has a ref counted pointer to NodeFilterCondition. + // In NodeFilterCondition, filter object is persisted in its constructor, + // and disposed in its destructor. + if (!filter->IsFunction()) + return 0; + + NodeFilterCondition* condition = new V8NodeFilterCondition(filter); + return NodeFilter::create(condition); +} + +v8::Local<v8::Object> V8DOMWrapper::instantiateV8Object(V8Proxy* proxy, V8ClassIndex::V8WrapperType descriptorType, V8ClassIndex::V8WrapperType cptrType, void* impl) +{ + // Make a special case for document.all + if (descriptorType == V8ClassIndex::HTMLCOLLECTION && static_cast<HTMLCollection*>(impl)->type() == DocAll) + descriptorType = V8ClassIndex::HTMLALLCOLLECTION; + + if (V8IsolatedWorld::getEntered()) { + // This effectively disables the wrapper cache for isolated worlds. + proxy = 0; + // FIXME: Do we need a wrapper cache for the isolated world? We should + // see if the performance gains are worth while. + } else if (!proxy) + proxy = V8Proxy::retrieve(); + + v8::Local<v8::Object> instance; + if (proxy) + instance = proxy->createWrapperFromCache(descriptorType); + else { + v8::Local<v8::Function> function = getTemplate(descriptorType)->GetFunction(); + instance = SafeAllocation::newInstance(function); + } + if (!instance.IsEmpty()) { + // Avoid setting the DOM wrapper for failed allocations. + setDOMWrapper(instance, V8ClassIndex::ToInt(cptrType), impl); + } + return instance; +} + +void V8DOMWrapper::setDOMWrapper(v8::Handle<v8::Object> object, int type, void* cptr) +{ + ASSERT(object->InternalFieldCount() >= 2); + object->SetPointerInInternalField(V8Custom::kDOMWrapperObjectIndex, cptr); + object->SetInternalField(V8Custom::kDOMWrapperTypeIndex, v8::Integer::New(type)); +} + +#ifndef NDEBUG +bool V8DOMWrapper::maybeDOMWrapper(v8::Handle<v8::Value> value) +{ + if (value.IsEmpty() || !value->IsObject()) + return false; + + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value); + if (!object->InternalFieldCount()) + return false; + + ASSERT(object->InternalFieldCount() >= V8Custom::kDefaultWrapperInternalFieldCount); + + v8::Handle<v8::Value> type = object->GetInternalField(V8Custom::kDOMWrapperTypeIndex); + ASSERT(type->IsInt32()); + ASSERT(V8ClassIndex::INVALID_CLASS_INDEX < type->Int32Value() && type->Int32Value() < V8ClassIndex::CLASSINDEX_END); + + v8::Handle<v8::Value> wrapper = object->GetInternalField(V8Custom::kDOMWrapperObjectIndex); + ASSERT(wrapper->IsNumber() || wrapper->IsExternal()); + + return true; +} +#endif + +bool V8DOMWrapper::isDOMEventWrapper(v8::Handle<v8::Value> value) +{ + // All kinds of events use EVENT as dom type in JS wrappers. + // See EventToV8Object + return isWrapperOfType(value, V8ClassIndex::EVENT); +} + +bool V8DOMWrapper::isWrapperOfType(v8::Handle<v8::Value> value, V8ClassIndex::V8WrapperType classType) +{ + if (value.IsEmpty() || !value->IsObject()) + return false; + + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value); + if (!object->InternalFieldCount()) + return false; + + ASSERT(object->InternalFieldCount() >= V8Custom::kDefaultWrapperInternalFieldCount); + + v8::Handle<v8::Value> wrapper = object->GetInternalField(V8Custom::kDOMWrapperObjectIndex); + ASSERT(wrapper->IsNumber() || wrapper->IsExternal()); + + v8::Handle<v8::Value> type = object->GetInternalField(V8Custom::kDOMWrapperTypeIndex); + ASSERT(type->IsInt32()); + ASSERT(V8ClassIndex::INVALID_CLASS_INDEX < type->Int32Value() && type->Int32Value() < V8ClassIndex::CLASSINDEX_END); + + return V8ClassIndex::FromInt(type->Int32Value()) == classType; +} + +#if ENABLE(VIDEO) +#define FOR_EACH_VIDEO_TAG(macro) \ + macro(audio, AUDIO) \ + macro(source, SOURCE) \ + macro(video, VIDEO) +#else +#define FOR_EACH_VIDEO_TAG(macro) +#endif + +#if ENABLE(DATAGRID) +#define FOR_EACH_DATAGRID_TAG(macro) \ + macro(datagrid, DATAGRID) \ + macro(dcell, DATAGRIDCELL) \ + macro(dcol, DATAGRIDCOL) \ + macro(drow, DATAGRIDROW) +#else +#define FOR_EACH_DATAGRID_TAG(macro) +#endif + +#define FOR_EACH_TAG(macro) \ + FOR_EACH_DATAGRID_TAG(macro) \ + macro(a, ANCHOR) \ + macro(applet, APPLET) \ + macro(area, AREA) \ + macro(base, BASE) \ + macro(basefont, BASEFONT) \ + macro(blockquote, BLOCKQUOTE) \ + macro(body, BODY) \ + macro(br, BR) \ + macro(button, BUTTON) \ + macro(caption, TABLECAPTION) \ + macro(col, TABLECOL) \ + macro(colgroup, TABLECOL) \ + macro(del, MOD) \ + macro(canvas, CANVAS) \ + macro(dir, DIRECTORY) \ + macro(div, DIV) \ + macro(dl, DLIST) \ + macro(embed, EMBED) \ + macro(fieldset, FIELDSET) \ + macro(font, FONT) \ + macro(form, FORM) \ + macro(frame, FRAME) \ + macro(frameset, FRAMESET) \ + macro(h1, HEADING) \ + macro(h2, HEADING) \ + macro(h3, HEADING) \ + macro(h4, HEADING) \ + macro(h5, HEADING) \ + macro(h6, HEADING) \ + macro(head, HEAD) \ + macro(hr, HR) \ + macro(html, HTML) \ + macro(img, IMAGE) \ + macro(iframe, IFRAME) \ + macro(image, IMAGE) \ + macro(input, INPUT) \ + macro(ins, MOD) \ + macro(isindex, ISINDEX) \ + macro(keygen, SELECT) \ + macro(label, LABEL) \ + macro(legend, LEGEND) \ + macro(li, LI) \ + macro(link, LINK) \ + macro(listing, PRE) \ + macro(map, MAP) \ + macro(marquee, MARQUEE) \ + macro(menu, MENU) \ + macro(meta, META) \ + macro(object, OBJECT) \ + macro(ol, OLIST) \ + macro(optgroup, OPTGROUP) \ + macro(option, OPTION) \ + macro(p, PARAGRAPH) \ + macro(param, PARAM) \ + macro(pre, PRE) \ + macro(q, QUOTE) \ + macro(script, SCRIPT) \ + macro(select, SELECT) \ + macro(style, STYLE) \ + macro(table, TABLE) \ + macro(thead, TABLESECTION) \ + macro(tbody, TABLESECTION) \ + macro(tfoot, TABLESECTION) \ + macro(td, TABLECELL) \ + macro(th, TABLECELL) \ + macro(tr, TABLEROW) \ + macro(textarea, TEXTAREA) \ + macro(title, TITLE) \ + macro(ul, ULIST) \ + macro(xmp, PRE) + +V8ClassIndex::V8WrapperType V8DOMWrapper::htmlElementType(HTMLElement* element) +{ + typedef HashMap<String, V8ClassIndex::V8WrapperType> WrapperTypeMap; + DEFINE_STATIC_LOCAL(WrapperTypeMap, wrapperTypeMap, ()); + if (wrapperTypeMap.isEmpty()) { +#define ADD_TO_HASH_MAP(tag, name) \ + wrapperTypeMap.set(#tag, V8ClassIndex::HTML##name##ELEMENT); + FOR_EACH_TAG(ADD_TO_HASH_MAP) +#if ENABLE(VIDEO) + if (MediaPlayer::isAvailable()) { + FOR_EACH_VIDEO_TAG(ADD_TO_HASH_MAP) + } +#endif +#undef ADD_TO_HASH_MAP + } + + V8ClassIndex::V8WrapperType type = wrapperTypeMap.get(element->localName().impl()); + if (!type) + return V8ClassIndex::HTMLELEMENT; + return type; +} +#undef FOR_EACH_TAG + +#if ENABLE(SVG) + +#if ENABLE(SVG_ANIMATION) +#define FOR_EACH_ANIMATION_TAG(macro) \ + macro(animateColor, ANIMATECOLOR) \ + macro(animate, ANIMATE) \ + macro(animateTransform, ANIMATETRANSFORM) \ + macro(set, SET) +#else +#define FOR_EACH_ANIMATION_TAG(macro) +#endif + +#if ENABLE(SVG_FILTERS) +#define FOR_EACH_FILTERS_TAG(macro) \ + macro(feBlend, FEBLEND) \ + macro(feColorMatrix, FECOLORMATRIX) \ + macro(feComponentTransfer, FECOMPONENTTRANSFER) \ + macro(feComposite, FECOMPOSITE) \ + macro(feDiffuseLighting, FEDIFFUSELIGHTING) \ + macro(feDisplacementMap, FEDISPLACEMENTMAP) \ + macro(feDistantLight, FEDISTANTLIGHT) \ + macro(feFlood, FEFLOOD) \ + macro(feFuncA, FEFUNCA) \ + macro(feFuncB, FEFUNCB) \ + macro(feFuncG, FEFUNCG) \ + macro(feFuncR, FEFUNCR) \ + macro(feGaussianBlur, FEGAUSSIANBLUR) \ + macro(feImage, FEIMAGE) \ + macro(feMerge, FEMERGE) \ + macro(feMergeNode, FEMERGENODE) \ + macro(feOffset, FEOFFSET) \ + macro(fePointLight, FEPOINTLIGHT) \ + macro(feSpecularLighting, FESPECULARLIGHTING) \ + macro(feSpotLight, FESPOTLIGHT) \ + macro(feTile, FETILE) \ + macro(feTurbulence, FETURBULENCE) \ + macro(filter, FILTER) +#else +#define FOR_EACH_FILTERS_TAG(macro) +#endif + +#if ENABLE(SVG_FONTS) +#define FOR_EACH_FONTS_TAG(macro) \ + macro(definition-src, DEFINITIONSRC) \ + macro(font-face, FONTFACE) \ + macro(font-face-format, FONTFACEFORMAT) \ + macro(font-face-name, FONTFACENAME) \ + macro(font-face-src, FONTFACESRC) \ + macro(font-face-uri, FONTFACEURI) +#else +#define FOR_EACH_FONTS_TAG(marco) +#endif + +#if ENABLE(SVG_FOREIGN_OBJECT) +#define FOR_EACH_FOREIGN_OBJECT_TAG(macro) \ + macro(foreignObject, FOREIGNOBJECT) +#else +#define FOR_EACH_FOREIGN_OBJECT_TAG(macro) +#endif + +#if ENABLE(SVG_USE) +#define FOR_EACH_USE_TAG(macro) \ + macro(use, USE) +#else +#define FOR_EACH_USE_TAG(macro) +#endif + +#define FOR_EACH_TAG(macro) \ + FOR_EACH_ANIMATION_TAG(macro) \ + FOR_EACH_FILTERS_TAG(macro) \ + FOR_EACH_FONTS_TAG(macro) \ + FOR_EACH_FOREIGN_OBJECT_TAG(macro) \ + FOR_EACH_USE_TAG(macro) \ + macro(a, A) \ + macro(altGlyph, ALTGLYPH) \ + macro(circle, CIRCLE) \ + macro(clipPath, CLIPPATH) \ + macro(cursor, CURSOR) \ + macro(defs, DEFS) \ + macro(desc, DESC) \ + macro(ellipse, ELLIPSE) \ + macro(g, G) \ + macro(glyph, GLYPH) \ + macro(image, IMAGE) \ + macro(linearGradient, LINEARGRADIENT) \ + macro(line, LINE) \ + macro(marker, MARKER) \ + macro(mask, MASK) \ + macro(metadata, METADATA) \ + macro(path, PATH) \ + macro(pattern, PATTERN) \ + macro(polyline, POLYLINE) \ + macro(polygon, POLYGON) \ + macro(radialGradient, RADIALGRADIENT) \ + macro(rect, RECT) \ + macro(script, SCRIPT) \ + macro(stop, STOP) \ + macro(style, STYLE) \ + macro(svg, SVG) \ + macro(switch, SWITCH) \ + macro(symbol, SYMBOL) \ + macro(text, TEXT) \ + macro(textPath, TEXTPATH) \ + macro(title, TITLE) \ + macro(tref, TREF) \ + macro(tspan, TSPAN) \ + macro(view, VIEW) \ + // end of macro + +V8ClassIndex::V8WrapperType V8DOMWrapper::svgElementType(SVGElement* element) +{ + typedef HashMap<String, V8ClassIndex::V8WrapperType> WrapperTypeMap; + DEFINE_STATIC_LOCAL(WrapperTypeMap, wrapperTypeMap, ()); + if (wrapperTypeMap.isEmpty()) { +#define ADD_TO_HASH_MAP(tag, name) \ + wrapperTypeMap.set(#tag, V8ClassIndex::SVG##name##ELEMENT); + FOR_EACH_TAG(ADD_TO_HASH_MAP) +#undef ADD_TO_HASH_MAP + } + + V8ClassIndex::V8WrapperType type = wrapperTypeMap.get(element->localName().impl()); + if (!type) + return V8ClassIndex::SVGELEMENT; + return type; +} +#undef FOR_EACH_TAG + +#endif // ENABLE(SVG) + +v8::Handle<v8::Value> V8DOMWrapper::convertEventToV8Object(Event* event) +{ + if (!event) + return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(event); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type = V8ClassIndex::EVENT; + + if (event->isUIEvent()) { + if (event->isKeyboardEvent()) + type = V8ClassIndex::KEYBOARDEVENT; + else if (event->isTextEvent()) + type = V8ClassIndex::TEXTEVENT; + else if (event->isMouseEvent()) + type = V8ClassIndex::MOUSEEVENT; + else if (event->isWheelEvent()) + type = V8ClassIndex::WHEELEVENT; +#if ENABLE(SVG) + else if (event->isSVGZoomEvent()) + type = V8ClassIndex::SVGZOOMEVENT; +#endif + else + type = V8ClassIndex::UIEVENT; + } else if (event->isMutationEvent()) + type = V8ClassIndex::MUTATIONEVENT; + else if (event->isOverflowEvent()) + type = V8ClassIndex::OVERFLOWEVENT; + else if (event->isMessageEvent()) + type = V8ClassIndex::MESSAGEEVENT; + else if (event->isProgressEvent()) { + if (event->isXMLHttpRequestProgressEvent()) + type = V8ClassIndex::XMLHTTPREQUESTPROGRESSEVENT; + else + type = V8ClassIndex::PROGRESSEVENT; + } else if (event->isWebKitAnimationEvent()) + type = V8ClassIndex::WEBKITANIMATIONEVENT; + else if (event->isWebKitTransitionEvent()) + type = V8ClassIndex::WEBKITTRANSITIONEVENT; +#if ENABLE(WORKERS) + else if (event->isErrorEvent()) + type = V8ClassIndex::ERROREVENT; +#endif + + + v8::Handle<v8::Object> result = instantiateV8Object(type, V8ClassIndex::EVENT, event); + if (result.IsEmpty()) { + // Instantiation failed. Avoid updating the DOM object map and + // return null which is already handled by callers of this function + // in case the event is NULL. + return v8::Null(); + } + + event->ref(); // fast ref + setJSWrapperForDOMObject(event, v8::Persistent<v8::Object>::New(result)); + + return result; +} + +static const V8ClassIndex::V8WrapperType mapping[] = { + V8ClassIndex::INVALID_CLASS_INDEX, // NONE + V8ClassIndex::INVALID_CLASS_INDEX, // ELEMENT_NODE needs special treatment + V8ClassIndex::ATTR, // ATTRIBUTE_NODE + V8ClassIndex::TEXT, // TEXT_NODE + V8ClassIndex::CDATASECTION, // CDATA_SECTION_NODE + V8ClassIndex::ENTITYREFERENCE, // ENTITY_REFERENCE_NODE + V8ClassIndex::ENTITY, // ENTITY_NODE + V8ClassIndex::PROCESSINGINSTRUCTION, // PROCESSING_INSTRUCTION_NODE + V8ClassIndex::COMMENT, // COMMENT_NODE + V8ClassIndex::INVALID_CLASS_INDEX, // DOCUMENT_NODE needs special treatment + V8ClassIndex::DOCUMENTTYPE, // DOCUMENT_TYPE_NODE + V8ClassIndex::DOCUMENTFRAGMENT, // DOCUMENT_FRAGMENT_NODE + V8ClassIndex::NOTATION, // NOTATION_NODE + V8ClassIndex::NODE, // XPATH_NAMESPACE_NODE +}; + +// Caller checks node is not null. +v8::Handle<v8::Value> V8DOMWrapper::convertNodeToV8Object(Node* node) +{ + if (!node) + return v8::Null(); + + // Find a proxy for this node. + // + // Note that if proxy is found, we might initialize the context which can + // instantiate a document wrapper. Therefore, we get the proxy before + // checking if the node already has a wrapper. + V8Proxy* proxy = 0; + Document* document = node->document(); + if (document) { + Frame* frame = document->frame(); + proxy = V8Proxy::retrieve(frame); + if (proxy) + proxy->initContextIfNeeded(); + } + + DOMWrapperMap<Node>& domNodeMap = getDOMNodeMap(); + v8::Handle<v8::Object> wrapper = domNodeMap.get(node); + if (!wrapper.IsEmpty()) + return wrapper; + + bool isDocument = false; // document type node has special handling + V8ClassIndex::V8WrapperType type; + + Node::NodeType nodeType = node->nodeType(); + if (nodeType == Node::ELEMENT_NODE) { + if (node->isHTMLElement()) + type = htmlElementType(static_cast<HTMLElement*>(node)); +#if ENABLE(SVG) + else if (node->isSVGElement()) + type = svgElementType(static_cast<SVGElement*>(node)); +#endif + else + type = V8ClassIndex::ELEMENT; + } else if (nodeType == Node::DOCUMENT_NODE) { + isDocument = true; + Document* document = static_cast<Document*>(node); + if (document->isHTMLDocument()) + type = V8ClassIndex::HTMLDOCUMENT; +#if ENABLE(SVG) + else if (document->isSVGDocument()) + type = V8ClassIndex::SVGDOCUMENT; +#endif + else + type = V8ClassIndex::DOCUMENT; + } else { + ASSERT(nodeType < sizeof(mapping)/sizeof(mapping[0])); + type = mapping[nodeType]; + ASSERT(type != V8ClassIndex::INVALID_CLASS_INDEX); + } + + v8::Handle<v8::Context> context; + if (proxy) + context = V8Proxy::context(proxy->frame()); + + // Enter the node's context and create the wrapper in that context. + if (!context.IsEmpty()) + context->Enter(); + + v8::Local<v8::Object> result = instantiateV8Object(proxy, type, V8ClassIndex::NODE, node); + + // Exit the node's context if it was entered. + if (!context.IsEmpty()) + context->Exit(); + + if (result.IsEmpty()) { + // If instantiation failed it's important not to add the result + // to the DOM node map. Instead we return an empty handle, which + // should already be handled by callers of this function in case + // the node is NULL. + return result; + } + + node->ref(); + domNodeMap.set(node, v8::Persistent<v8::Object>::New(result)); + + if (isDocument) { + if (proxy) + proxy->updateDocumentWrapper(result); + + if (type == V8ClassIndex::HTMLDOCUMENT) { + // Create marker object and insert it in two internal fields. + // This is used to implement temporary shadowing of + // document.all. + ASSERT(result->InternalFieldCount() == V8Custom::kHTMLDocumentInternalFieldCount); + v8::Local<v8::Object> marker = v8::Object::New(); + result->SetInternalField(V8Custom::kHTMLDocumentMarkerIndex, marker); + result->SetInternalField(V8Custom::kHTMLDocumentShadowIndex, marker); + } + } + + return result; +} + +// A JS object of type EventTarget is limited to a small number of possible classes. +// Check EventTarget.h for new type conversion methods +v8::Handle<v8::Value> V8DOMWrapper::convertEventTargetToV8Object(EventTarget* target) +{ + if (!target) + return v8::Null(); + +#if ENABLE(SVG) + SVGElementInstance* instance = target->toSVGElementInstance(); + if (instance) + return convertToV8Object(V8ClassIndex::SVGELEMENTINSTANCE, instance); +#endif + +#if ENABLE(WORKERS) + Worker* worker = target->toWorker(); + if (worker) + return convertToV8Object(V8ClassIndex::WORKER, worker); +#endif // WORKERS + + Node* node = target->toNode(); + if (node) + return convertNodeToV8Object(node); + + if (DOMWindow* domWindow = target->toDOMWindow()) + return convertToV8Object(V8ClassIndex::DOMWINDOW, domWindow); + + // XMLHttpRequest is created within its JS counterpart. + XMLHttpRequest* xmlHttpRequest = target->toXMLHttpRequest(); + if (xmlHttpRequest) { + v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(xmlHttpRequest); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + + // MessagePort is created within its JS counterpart + MessagePort* port = target->toMessagePort(); + if (port) { + v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(port); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + + XMLHttpRequestUpload* upload = target->toXMLHttpRequestUpload(); + if (upload) { + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(upload); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + DOMApplicationCache* domAppCache = target->toDOMApplicationCache(); + if (domAppCache) + return convertToV8Object(V8ClassIndex::DOMAPPLICATIONCACHE, domAppCache); +#endif + + ASSERT(0); + return notHandledByInterceptor(); +} + +v8::Handle<v8::Value> V8DOMWrapper::convertEventListenerToV8Object(EventListener* listener) +{ + if (!listener) + return v8::Null(); + + // FIXME: can a user take a lazy event listener and set to other places? + V8AbstractEventListener* v8listener = static_cast<V8AbstractEventListener*>(listener); + return v8listener->getListenerObject(); +} + +v8::Handle<v8::Value> V8DOMWrapper::convertDOMImplementationToV8Object(DOMImplementation* impl) +{ + v8::Handle<v8::Object> result = instantiateV8Object(V8ClassIndex::DOMIMPLEMENTATION, V8ClassIndex::DOMIMPLEMENTATION, impl); + if (result.IsEmpty()) { + // If the instantiation failed, we ignore it and return null instead + // of returning an empty handle. + return v8::Null(); + } + return result; +} + +v8::Handle<v8::Value> V8DOMWrapper::convertStyleSheetToV8Object(StyleSheet* sheet) +{ + if (!sheet) + return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(sheet); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type = V8ClassIndex::STYLESHEET; + if (sheet->isCSSStyleSheet()) + type = V8ClassIndex::CSSSTYLESHEET; + + v8::Handle<v8::Object> result = instantiateV8Object(type, V8ClassIndex::STYLESHEET, sheet); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + sheet->ref(); + setJSWrapperForDOMObject(sheet, v8::Persistent<v8::Object>::New(result)); + } + + // Add a hidden reference from stylesheet object to its owner node. + Node* ownerNode = sheet->ownerNode(); + if (ownerNode) { + v8::Handle<v8::Object> owner = v8::Handle<v8::Object>::Cast(convertNodeToV8Object(ownerNode)); + result->SetInternalField(V8Custom::kStyleSheetOwnerNodeIndex, owner); + } + + return result; +} + +v8::Handle<v8::Value> V8DOMWrapper::convertCSSValueToV8Object(CSSValue* value) +{ + if (!value) + return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(value); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type; + + if (value->isWebKitCSSTransformValue()) + type = V8ClassIndex::WEBKITCSSTRANSFORMVALUE; + else if (value->isValueList()) + type = V8ClassIndex::CSSVALUELIST; + else if (value->isPrimitiveValue()) + type = V8ClassIndex::CSSPRIMITIVEVALUE; +#if ENABLE(SVG) + else if (value->isSVGPaint()) + type = V8ClassIndex::SVGPAINT; + else if (value->isSVGColor()) + type = V8ClassIndex::SVGCOLOR; +#endif + else + type = V8ClassIndex::CSSVALUE; + + v8::Handle<v8::Object> result = instantiateV8Object(type, V8ClassIndex::CSSVALUE, value); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + value->ref(); + setJSWrapperForDOMObject(value, v8::Persistent<v8::Object>::New(result)); + } + + return result; +} + +v8::Handle<v8::Value> V8DOMWrapper::convertCSSRuleToV8Object(CSSRule* rule) +{ + if (!rule) + return v8::Null(); + + v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(rule); + if (!wrapper.IsEmpty()) + return wrapper; + + V8ClassIndex::V8WrapperType type; + + switch (rule->type()) { + case CSSRule::STYLE_RULE: + type = V8ClassIndex::CSSSTYLERULE; + break; + case CSSRule::CHARSET_RULE: + type = V8ClassIndex::CSSCHARSETRULE; + break; + case CSSRule::IMPORT_RULE: + type = V8ClassIndex::CSSIMPORTRULE; + break; + case CSSRule::MEDIA_RULE: + type = V8ClassIndex::CSSMEDIARULE; + break; + case CSSRule::FONT_FACE_RULE: + type = V8ClassIndex::CSSFONTFACERULE; + break; + case CSSRule::PAGE_RULE: + type = V8ClassIndex::CSSPAGERULE; + break; + case CSSRule::VARIABLES_RULE: + type = V8ClassIndex::CSSVARIABLESRULE; + break; + case CSSRule::WEBKIT_KEYFRAME_RULE: + type = V8ClassIndex::WEBKITCSSKEYFRAMERULE; + break; + case CSSRule::WEBKIT_KEYFRAMES_RULE: + type = V8ClassIndex::WEBKITCSSKEYFRAMESRULE; + break; + default: // CSSRule::UNKNOWN_RULE + type = V8ClassIndex::CSSRULE; + break; + } + + v8::Handle<v8::Object> result = instantiateV8Object(type, V8ClassIndex::CSSRULE, rule); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + rule->ref(); + setJSWrapperForDOMObject(rule, v8::Persistent<v8::Object>::New(result)); + } + return result; +} + +v8::Handle<v8::Value> V8DOMWrapper::convertWindowToV8Object(DOMWindow* window) +{ + if (!window) + return v8::Null(); + // Initializes environment of a frame, and return the global object + // of the frame. + Frame* frame = window->frame(); + if (!frame) + return v8::Handle<v8::Object>(); + + // Special case: Because of evaluateInNewContext() one DOMWindow can have + // multiple contexts and multiple global objects associated with it. When + // code running in one of those contexts accesses the window object, we + // want to return the global object associated with that context, not + // necessarily the first global object associated with that DOMWindow. + v8::Handle<v8::Context> currentContext = v8::Context::GetCurrent(); + v8::Handle<v8::Object> currentGlobal = currentContext->Global(); + v8::Handle<v8::Object> windowWrapper = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, currentGlobal); + if (!windowWrapper.IsEmpty()) { + if (convertDOMWrapperToNative<DOMWindow>(windowWrapper) == window) + return currentGlobal; + } + + // Otherwise, return the global object associated with this frame. + v8::Handle<v8::Context> context = V8Proxy::context(frame); + if (context.IsEmpty()) + return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> global = context->Global(); + ASSERT(!global.IsEmpty()); + return global; +} + +} // namespace WebCore |