diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
commit | f5b16fed647e941aa66933178da85db2860d639b (patch) | |
tree | f00e9856c04aad3b558a140955e7674add33f051 /webkit/port/bindings/v8 | |
parent | 920c091ac3ee15079194c82ae8a7a18215f3f23c (diff) | |
download | chromium_src-f5b16fed647e941aa66933178da85db2860d639b.zip chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.gz chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.bz2 |
Add webkit to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/port/bindings/v8')
30 files changed, 11314 insertions, 0 deletions
diff --git a/webkit/port/bindings/v8/JSXPathNSResolver.cpp b/webkit/port/bindings/v8/JSXPathNSResolver.cpp new file mode 100644 index 0000000..ed6ed3c --- /dev/null +++ b/webkit/port/bindings/v8/JSXPathNSResolver.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2008, 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 "JSXPathNSResolver.h" + +#if ENABLE(XPATH) + +#include "v8_proxy.h" +#include "v8_binding.h" +#include "PlatformString.h" + +namespace WebCore { + +JSXPathNSResolver::JSXPathNSResolver(v8::Handle<v8::Object> resolver) +: m_resolver(resolver) { +} + +JSXPathNSResolver::~JSXPathNSResolver() { +} + +String JSXPathNSResolver::lookupNamespaceURI(const String& prefix) { + v8::Handle<v8::Function> lookupNamespaceURIFunc; + v8::Handle<v8::String> lookupNamespaceURIName = v8::String::New("lookupNamespaceURI"); + + // Check if the resolver has a function property named lookupNamespaceURI. + if (m_resolver->Has(lookupNamespaceURIName)) { + v8::Handle<v8::Value> lookupNamespaceURI = m_resolver->Get(lookupNamespaceURIName); + if (lookupNamespaceURI->IsFunction()) { + lookupNamespaceURIFunc = v8::Handle<v8::Function>::Cast(lookupNamespaceURI); + } + } + + if (lookupNamespaceURIFunc.IsEmpty() && !m_resolver->IsFunction()) { + Frame* frame = V8Proxy::retrieveActiveFrame(); + log_info(frame, "XPathNSResolver does not have a lookupNamespaceURI method.", String()); + return String(); + } + + // Catch exceptions from calling the namespace resolver. + v8::TryCatch try_catch; + try_catch.SetVerbose(true); // Print exceptions to console. + + const int argc = 1; + v8::Handle<v8::Value> argv[argc] = { v8String(prefix) }; + v8::Handle<v8::Function> function = lookupNamespaceURIFunc.IsEmpty() + ? v8::Handle<v8::Function>::Cast(m_resolver) + : lookupNamespaceURIFunc; + + V8Proxy* proxy = V8Proxy::retrieve(); + v8::Handle<v8::Value> retval = proxy->CallFunction(function, m_resolver, argc, argv); + + // Eat exceptions from namespace resolver and return an empty string. This + // will most likely cause NAMESPACE_ERR. + if (try_catch.HasCaught()) { + return String(); + } + + return ToWebCoreString(retval); +} + +} + +#endif // ENABLE(XPATH) diff --git a/webkit/port/bindings/v8/JSXPathNSResolver.h b/webkit/port/bindings/v8/JSXPathNSResolver.h new file mode 100644 index 0000000..b584ace --- /dev/null +++ b/webkit/port/bindings/v8/JSXPathNSResolver.h @@ -0,0 +1,58 @@ +// Copyright 2008, 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. + +#ifndef JSXPATHNSRESOLVER_H__ +#define JSXPATHNSRESOLVER_H__ + +#if ENABLE(XPATH) + +#include <v8.h> +#include <wtf/RefCounted.h> +#include "XPathNSResolver.h" + +namespace WebCore { + + class String; + + class JSXPathNSResolver : public XPathNSResolver { + public: + + JSXPathNSResolver(v8::Handle<v8::Object> resolver); + virtual ~JSXPathNSResolver(); + + virtual String lookupNamespaceURI(const String& prefix); + + private: + v8::Handle<v8::Object> m_resolver; // Handle to resolver object. + }; +} + +#endif // ENABLE(XPATH) + +#endif // JSXPATHNSRESOLVER_H__ diff --git a/webkit/port/bindings/v8/V8SVGPODTypeWrapper.h b/webkit/port/bindings/v8/V8SVGPODTypeWrapper.h new file mode 100644 index 0000000..190b982 --- /dev/null +++ b/webkit/port/bindings/v8/V8SVGPODTypeWrapper.h @@ -0,0 +1,299 @@ +// Copyright 2008, 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. + +#ifndef V8SVGPODTypeWrapper_h +#define V8SVGPODTypeWrapper_h + +#if ENABLE(SVG) + +#include "config.h" +#include "SVGElement.h" +#include "SVGList.h" +#include <wtf/Assertions.h> +#include <wtf/RefCounted.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +template<typename PODType> +class V8SVGPODTypeWrapper : public RefCounted<V8SVGPODTypeWrapper<PODType> > { +public: + V8SVGPODTypeWrapper() : RefCounted<V8SVGPODTypeWrapper<PODType> >(0) { + } + virtual ~V8SVGPODTypeWrapper() { + } + + // Getter wrapper + virtual operator PODType() = 0; + + // Setter wrapper + virtual void commitChange(PODType, SVGElement*) = 0; +}; + +template<typename PODType> +class V8SVGPODTypeWrapperCreatorForList : public V8SVGPODTypeWrapper<PODType> +{ +public: + typedef PODType (SVGPODListItem<PODType>::*GetterMethod)() const; + typedef void (SVGPODListItem<PODType>::*SetterMethod)(PODType); + + V8SVGPODTypeWrapperCreatorForList(SVGPODListItem<PODType>* creator, const QualifiedName& attributeName) + : m_creator(creator) + , m_getter(&SVGPODListItem<PODType>::value) + , m_setter(&SVGPODListItem<PODType>::setValue) + , m_associatedAttributeName(attributeName) + { + ASSERT(m_creator); + ASSERT(m_getter); + ASSERT(m_setter); + } + + virtual ~V8SVGPODTypeWrapperCreatorForList() { } + + // Getter wrapper + virtual operator PODType() { return (m_creator.get()->*m_getter)(); } + + // Setter wrapper + virtual void commitChange(PODType type, SVGElement* context) + { + if (!m_setter) + return; + + (m_creator.get()->*m_setter)(type); + + if (context) + context->svgAttributeChanged(m_associatedAttributeName); + } + +private: + // Update callbacks + RefPtr<SVGPODListItem<PODType> > m_creator; + GetterMethod m_getter; + SetterMethod m_setter; + const QualifiedName& m_associatedAttributeName; +}; + +template<typename PODType> +class V8SVGPODTypeWrapperCreatorReadOnly : public V8SVGPODTypeWrapper<PODType> +{ +public: + V8SVGPODTypeWrapperCreatorReadOnly(PODType type) + : m_podType(type) + { } + + virtual ~V8SVGPODTypeWrapperCreatorReadOnly() { } + + // Getter wrapper + virtual operator PODType() { return m_podType; } + + // Setter wrapper + virtual void commitChange(PODType type, SVGElement*) + { + m_podType = type; + } + +private: + PODType m_podType; +}; + +template<typename PODType, typename PODTypeCreator> +class V8SVGPODTypeWrapperCreatorReadWrite : public V8SVGPODTypeWrapper<PODType> +{ +public: + typedef PODType (PODTypeCreator::*GetterMethod)() const; + typedef void (PODTypeCreator::*SetterMethod)(PODType); + typedef void (*CacheRemovalCallback)(V8SVGPODTypeWrapper<PODType>*); + + V8SVGPODTypeWrapperCreatorReadWrite(PODTypeCreator* creator, GetterMethod getter, SetterMethod setter, CacheRemovalCallback cacheRemovalCallback) + : m_creator(creator) + , m_getter(getter) + , m_setter(setter) + , m_cacheRemovalCallback(cacheRemovalCallback) + { + ASSERT(creator); + ASSERT(getter); + ASSERT(setter); + ASSERT(cacheRemovalCallback); + } + + virtual ~V8SVGPODTypeWrapperCreatorReadWrite() { + ASSERT(m_cacheRemovalCallback); + + (*m_cacheRemovalCallback)(this); + } + + // Getter wrapper + virtual operator PODType() { return (m_creator.get()->*m_getter)(); } + + // Setter wrapper + virtual void commitChange(PODType type, SVGElement* context) + { + if (!m_setter) + return; + + (m_creator.get()->*m_setter)(type); + + if (context) + context->svgAttributeChanged(m_creator->associatedAttributeName()); + } + +private: + // Update callbacks + RefPtr<PODTypeCreator> m_creator; + GetterMethod m_getter; + SetterMethod m_setter; + CacheRemovalCallback m_cacheRemovalCallback; +}; + +// Caching facilities +template<typename PODType, typename PODTypeCreator> +struct PODTypeReadWriteHashInfo { + typedef PODType (PODTypeCreator::*GetterMethod)() const; + typedef void (PODTypeCreator::*SetterMethod)(PODType); + + // Empty value + PODTypeReadWriteHashInfo() + : creator(0) + , getter(0) + , setter(0) + { } + + // Deleted value + explicit PODTypeReadWriteHashInfo(bool) + : creator(reinterpret_cast<PODTypeCreator*>(-1)) + , getter(0) + , setter(0) + { } + + PODTypeReadWriteHashInfo(PODTypeCreator* _creator, GetterMethod _getter, SetterMethod _setter) + : creator(_creator) + , getter(_getter) + , setter(_setter) + { + ASSERT(creator); + ASSERT(getter); + } + + bool operator==(const PODTypeReadWriteHashInfo& other) const + { + return creator == other.creator && getter == other.getter && setter == other.setter; + } + + PODTypeCreator* creator; + GetterMethod getter; + SetterMethod setter; +}; + +template<typename PODType, typename PODTypeCreator> +struct PODTypeReadWriteHashInfoHash { + static unsigned hash(const PODTypeReadWriteHashInfo<PODType, PODTypeCreator>& info) + { + return StringImpl::computeHash((::UChar*) &info, sizeof(PODTypeReadWriteHashInfo<PODType, PODTypeCreator>) / sizeof(::UChar)); + } + + static bool equal(const PODTypeReadWriteHashInfo<PODType, PODTypeCreator>& a, const PODTypeReadWriteHashInfo<PODType, PODTypeCreator>& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +template<typename PODType, typename PODTypeCreator> +struct PODTypeReadWriteHashInfoTraits : WTF::GenericHashTraits<PODTypeReadWriteHashInfo<PODType, PODTypeCreator> > { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + + static const PODTypeReadWriteHashInfo<PODType, PODTypeCreator>& deletedValue() + { + static PODTypeReadWriteHashInfo<PODType, PODTypeCreator> key(true); + return key; + } + + static const PODTypeReadWriteHashInfo<PODType, PODTypeCreator>& emptyValue() + { + static PODTypeReadWriteHashInfo<PODType, PODTypeCreator> key; + return key; + } +}; + +template<typename PODType, typename PODTypeCreator> +class V8SVGPODTypeWrapperCache +{ +public: + typedef PODType (PODTypeCreator::*GetterMethod)() const; + typedef void (PODTypeCreator::*SetterMethod)(PODType); + + typedef HashMap<PODTypeReadWriteHashInfo<PODType, PODTypeCreator>, V8SVGPODTypeWrapperCreatorReadWrite<PODType, PODTypeCreator>*, PODTypeReadWriteHashInfoHash<PODType, PODTypeCreator>, PODTypeReadWriteHashInfoTraits<PODType, PODTypeCreator> > ReadWriteHashMap; + typedef typename ReadWriteHashMap::const_iterator ReadWriteHashMapIterator; + + static ReadWriteHashMap& readWriteHashMap() + { + static ReadWriteHashMap _readWriteHashMap; + return _readWriteHashMap; + } + + // Used for readwrite attributes only + static V8SVGPODTypeWrapper<PODType>* lookupOrCreateWrapper(PODTypeCreator* creator, GetterMethod getter, SetterMethod setter) + { + ReadWriteHashMap& map(readWriteHashMap()); + PODTypeReadWriteHashInfo<PODType, PODTypeCreator> info(creator, getter, setter); + + if (map.contains(info)) + return map.get(info); + + V8SVGPODTypeWrapperCreatorReadWrite<PODType, PODTypeCreator>* wrapper = new V8SVGPODTypeWrapperCreatorReadWrite<PODType, PODTypeCreator>( + creator, getter, setter, forgetWrapper); + map.set(info, wrapper); + return wrapper; + } + + static void forgetWrapper(V8SVGPODTypeWrapper<PODType>* wrapper) + { + ReadWriteHashMap& map(readWriteHashMap()); + + ReadWriteHashMapIterator it = map.begin(); + ReadWriteHashMapIterator end = map.end(); + + for (; it != end; ++it) { + if (it->second != wrapper) + continue; + + // It's guaruanteed that there's just one object we need to take care of. + map.remove(it->first); + break; + } + } +}; + + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // V8SVGPODTypeWrapper_h diff --git a/webkit/port/bindings/v8/dom_wrapper_map.h b/webkit/port/bindings/v8/dom_wrapper_map.h new file mode 100644 index 0000000..d5f88b3 --- /dev/null +++ b/webkit/port/bindings/v8/dom_wrapper_map.h @@ -0,0 +1,96 @@ +// Copyright 2008, 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. + +#ifndef BINDINGS_V8_DOM_WRAPPER_MAP +#define BINDINGS_V8_DOM_WRAPPER_MAP + +#include <wtf/HashMap.h> + +// A table of wrappers with weak pointers. +// This table allows us to avoid track wrapped objects for debugging +// and for ensuring that we don't double wrap the same object. +template<class KeyType, class ValueType> +class WeakReferenceMap { + public: + WeakReferenceMap(v8::WeakReferenceCallback callback) : + weak_reference_callback_(callback) { } +#ifndef NDEBUG + virtual ~WeakReferenceMap() { + if (map_.size() > 0) { + fprintf(stderr, "Leak %d JS wrappers.\n", map_.size()); + // Print out details. + } + } +#endif + + // Get the JS wrapper object of an object. + virtual v8::Persistent<ValueType> get(KeyType* obj) { + ValueType* wrapper = map_.get(obj); + return wrapper ? v8::Persistent<ValueType>(wrapper) + : v8::Persistent<ValueType>(); + } + + virtual void set(KeyType* obj, v8::Persistent<ValueType> wrapper) { + ASSERT(!map_.contains(obj)); + wrapper.MakeWeak(obj, weak_reference_callback_); + map_.set(obj, *wrapper); + } + + virtual void forget(KeyType* obj) { + ASSERT(obj); + ValueType* wrapper = map_.take(obj); + if (wrapper) { + v8::Persistent<ValueType> handle(wrapper); + handle.Dispose(); + handle.Clear(); + } + } + + bool contains(KeyType* obj) { + return map_.contains(obj); + } + + HashMap<KeyType*, ValueType*>& impl() { + return map_; + } + + protected: + HashMap<KeyType*, ValueType*> map_; + v8::WeakReferenceCallback weak_reference_callback_; +}; + + +template <class KeyType> +class DOMWrapperMap : public WeakReferenceMap<KeyType, v8::Object> { + public: + DOMWrapperMap(v8::WeakReferenceCallback callback) : + WeakReferenceMap(callback) { } +}; + +#endif // BINDINGS_V8_DOM_WRAPPER_MAP diff --git a/webkit/port/bindings/v8/np_v8object.cpp b/webkit/port/bindings/v8/np_v8object.cpp new file mode 100644 index 0000000..df04aca --- /dev/null +++ b/webkit/port/bindings/v8/np_v8object.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 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: + * 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. + */ + +#include "config.h" + +#define max max +#define min min +#include <string> +#include <sstream> +#include <v8.h> +#include "np_v8object.h" +#include "Frame.h" +#include "bindings/npruntime.h" +#include "npruntime_priv.h" +#include "PlatformString.h" +#include "v8_helpers.h" +#include "v8_np_utils.h" +#include "v8_proxy.h" +#include "V8Bridge.h" +#include "DOMWindow.h" + +using WebCore::V8ClassIndex; +using WebCore::V8Proxy; + +namespace { + +// TODO(mbelshe): comments on why use malloc and free. +static NPObject* AllocV8NPObject(NPP, NPClass*) { + return static_cast<NPObject*>(malloc(sizeof(V8NPObject))); +} + +static void FreeV8NPObject(NPObject* npobj) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); +#ifndef NDEBUG + V8Proxy::UnregisterGlobalHandle(object, object->v8_object); +#endif + object->v8_object.Dispose(); + free(object); +} + +static v8::Handle<v8::Value>* listFromVariantArgs(const NPVariant* args, + uint32_t argCount, + NPObject *owner) { + v8::Handle<v8::Value>* argv = new v8::Handle<v8::Value>[argCount]; + for (uint32_t index = 0; index < argCount; index++) { + const NPVariant *arg = &args[index]; + argv[index] = ConvertNPVariantToV8Object(arg, owner); + } + return argv; +} + +// Create an identifier (null terminated utf8 char*) from the NPIdentifier. +static void NPIdentifierToV8Identifier(NPIdentifier name, std::string &string) { + PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(name); + if (identifier->isString) { + string = static_cast<const char *>(identifier->value.string); + } else { + std::ostringstream o; + o << identifier->value.number; + string = o.str(); + } +} + +static NPClass V8NPObjectClass = { NP_CLASS_STRUCT_VERSION, + AllocV8NPObject, + FreeV8NPObject, + 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +} // namespace + +// +// NPAPI's npruntime functions +// +NPClass* NPScriptObjectClass = &V8NPObjectClass; + +NPObject* NPN_CreateScriptObject(NPP npp, v8::Handle<v8::Object> object, + WebCore::DOMWindow* root) { + // Check to see if this object is already wrapped. + if (object->InternalFieldCount() == 3 && + object->GetInternalField(1)->IsNumber() && + object->GetInternalField(1)->Uint32Value() == V8ClassIndex::NPOBJECT) { + NPObject* rv = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, + object); + NPN_RetainObject(rv); + return rv; + } + + V8NPObject* obj = + reinterpret_cast<V8NPObject*>(NPN_CreateObject(npp, &V8NPObjectClass)); + obj->v8_object = v8::Persistent<v8::Object>::New(object); +#ifndef NDEBUG + V8Proxy::RegisterGlobalHandle(WebCore::NPOBJECT, obj, obj->v8_object); +#endif + obj->root_object = root; + return reinterpret_cast<NPObject*>(obj); +} + +bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, + const NPVariant *args, uint32_t argCount, NPVariant *result) { + if (npobj == NULL) + return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + PrivateIdentifier *identifier = static_cast<PrivateIdentifier*>(methodName); + if (!identifier->isString) + return false; + + v8::HandleScope handle_scope; + // TODO: should use the plugin's owner frame as the security context + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + + v8::Context::Scope scope(context); + + // Special case the "eval" method. + if (methodName == NPN_GetStringIdentifier("eval")) { + if (argCount != 1) + return false; + if (args[0].type != NPVariantType_String) + return false; + return NPN_Evaluate(npp, npobj, + const_cast<NPString*>(&args[0].value.stringValue), result); + } + + v8::Handle<v8::Value> func_obj = + object->v8_object->Get(v8::String::New(identifier->value.string)); + if (func_obj.IsEmpty() || func_obj->IsNull()) { + NULL_TO_NPVARIANT(*result); + return false; + } + if (func_obj->IsUndefined()) { + VOID_TO_NPVARIANT(*result); + return false; + } + + WebCore::V8Proxy* proxy = GetV8Proxy(npobj); + ASSERT(proxy); // must not be null + + // TODO: fix variable naming + // Call the function object + v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_obj); + // Create list of args to pass to v8 + v8::Handle<v8::Value>* argv = listFromVariantArgs(args, argCount, npobj); + v8::Local<v8::Value> resultObj = + proxy->CallFunction(func, object->v8_object, argCount, argv); + delete[] argv; + + // If we had an error, return false. The spec is a little unclear here, but + // says "Returns true if the method was successfully invoked". If we get an + // error return value, was that successfully invoked? + if (resultObj.IsEmpty()) return false; + + // Convert the result back to an NPVariant + ConvertV8ObjectToNPVariant(resultObj, npobj, result); + return true; + } + + if (npobj->_class->invoke) + return npobj->_class->invoke(npobj, methodName, args, argCount, result); + + VOID_TO_NPVARIANT(*result); + return true; +} + + +// TODO: Fix it same as NPN_Invoke (HandleScope and such) +bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result) { + if (npobj == NULL) + return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + VOID_TO_NPVARIANT(*result); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + + v8::Context::Scope scope(context); + + // Lookup the function object + v8::Handle<v8::Object> funcObj(object->v8_object); + if (!funcObj->IsFunction()) + return false; + + // Call the function object + v8::Local<v8::Value> resultObj; + v8::Handle<v8::Function> func(v8::Function::Cast(*funcObj)); + if (!func->IsNull()) { + WebCore::V8Proxy* proxy = GetV8Proxy(npobj); + ASSERT(proxy); + + // Create list of args to pass to v8 + v8::Handle<v8::Value>* argv = listFromVariantArgs(args, argCount, npobj); + resultObj = proxy->CallFunction(func, funcObj, argCount, argv); + delete[] argv; + } + + // If we had an error, return false. The spec is a little unclear here, but + // says "Returns true if the method was successfully invoked". If we get an + // error return value, was that successfully invoked? + if (resultObj.IsEmpty()) return false; + + // Convert the result back to an NPVariant + ConvertV8ObjectToNPVariant(resultObj, npobj, result); + return true; + } + + if (npobj->_class->invokeDefault) + return npobj->_class->invokeDefault(npobj, args, argCount, result); + + VOID_TO_NPVARIANT(*result); + return true; +} + +bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *npscript, + NPVariant *result) { + VOID_TO_NPVARIANT(*result); + if (npobj == NULL) + return false; + + if (npobj->_class == NPScriptObjectClass) { + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) + return false; + + WebCore::V8Proxy* proxy = GetV8Proxy(npobj); + ASSERT(proxy); + + v8::Context::Scope scope(context); + + WebCore::String filename(L"npscript"); + // Convert UTF-8 stream to WebCore::String. + WebCore::String script = WebCore::String::fromUTF8( + npscript->UTF8Characters, npscript->UTF8Length); + v8::Local<v8::Value> v8result = + proxy->Evaluate(filename, 0, script, NULL); + + // If we had an error, return false. + if (v8result.IsEmpty()) return false; + + ConvertV8ObjectToNPVariant(v8result, npobj, result); + return true; + } + + return false; +} + +bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, + NPVariant *result) { + if (npobj == NULL) + return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + + v8::Context::Scope scope(context); + + v8::Handle<v8::Object> obj(object->v8_object); + + std::string identifier; + NPIdentifierToV8Identifier(propertyName, identifier); + v8::Local<v8::Value> v8result = + obj->Get(v8::String::New(identifier.c_str())); + + ConvertV8ObjectToNPVariant(v8result, npobj, result); + return true; + } + + if (npobj->_class->hasProperty && npobj->_class->getProperty) + if (npobj->_class->hasProperty(npobj, propertyName)) + return npobj->_class->getProperty(npobj, propertyName, result); + + VOID_TO_NPVARIANT(*result); + return false; +} + +bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, + const NPVariant *value) { + if (npobj == NULL) + return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + + v8::Context::Scope scope(context); + + v8::Handle<v8::Object> obj(object->v8_object); + std::string identifier; + NPIdentifierToV8Identifier(propertyName, identifier); + obj->Set(v8::String::New(identifier.c_str()), + ConvertNPVariantToV8Object(value, + object->root_object->frame()->windowScriptNPObject())); + return true; + } + + if (npobj->_class->setProperty) + return npobj->_class->setProperty(npobj, propertyName, value); + + return false; +} + +bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName) { + if (npobj == NULL) + return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + v8::Context::Scope scope(context); + + v8::Handle<v8::Object> obj(object->v8_object); + std::string identifier; + NPIdentifierToV8Identifier(propertyName, identifier); + // TODO(mbelshe) - verify that setting to undefined is right. + obj->Set(v8::String::New(identifier.c_str()), v8::Undefined()); + return true; + } + + return false; +} + +bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName) { + if (npobj == NULL) + return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + v8::Context::Scope scope(context); + + v8::Handle<v8::Object> obj(object->v8_object); + std::string identifier; + NPIdentifierToV8Identifier(propertyName, identifier); + return obj->Has(v8::String::New(identifier.c_str())); + } + + if (npobj->_class->hasProperty) + return npobj->_class->hasProperty(npobj, propertyName); + + return false; +} + +bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName) { + if (npobj == NULL) return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + v8::Context::Scope scope(context); + + v8::Handle<v8::Object> obj(object->v8_object); + std::string identifier; + NPIdentifierToV8Identifier(methodName, identifier); + v8::Handle<v8::Value> prop = obj->Get(v8::String::New(identifier.c_str())); + return prop->IsFunction(); + } + + if (npobj->_class->hasMethod) + return npobj->_class->hasMethod(npobj, methodName); + + return false; +} + +void NPN_SetException(NPObject *npobj, const NPUTF8 *message) { + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(NULL, npobj); + if (context.IsEmpty()) return; + + v8::Context::Scope scope(context); + + V8Proxy::ThrowError(V8Proxy::GENERAL_ERROR, message); + } +} + +bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, + uint32_t *count) { + if (npobj == NULL) return false; + + if (npobj->_class == NPScriptObjectClass) { + V8NPObject *object = reinterpret_cast<V8NPObject*>(npobj); + + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = GetV8Context(npp, npobj); + if (context.IsEmpty()) return false; + v8::Context::Scope scope(context); + + v8::Handle<v8::Object> obj(object->v8_object); + + // TODO(fqian): http://b/issue?id=1210340: Use a v8::Object::Keys() method + // when it exists, instead of evaluating javascript. + + // TODO(mpcomplete): figure out how to cache this helper function. + // Run a helper function that collects the properties on the object into + // an array. + const char kEnumeratorCode[] = + "(function (obj) {" + " var props = [];" + " for (var prop in obj) {" + " props[props.length] = prop;" + " }" + " return props;" + "});"; + v8::Handle<v8::String> source = v8::String::New(kEnumeratorCode); + v8::Handle<v8::Script> script = v8::Script::Compile(source, NULL); + v8::Handle<v8::Value> enumerator_obj = script->Run(); + v8::Handle<v8::Function> enumerator = + v8::Handle<v8::Function>::Cast(enumerator_obj); + v8::Handle<v8::Value> argv[] = { obj }; + v8::Local<v8::Value> props_obj = + enumerator->Call(v8::Handle<v8::Object>::Cast(enumerator_obj), + arraysize(argv), argv); + if (props_obj.IsEmpty()) + return false; + + // Convert the results into an array of NPIdentifiers. + v8::Handle<v8::Array> props = v8::Handle<v8::Array>::Cast(props_obj); + *count = props->Length(); + *identifier = + static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier*) * *count)); + for (uint32_t i = 0; i < *count; ++i) { + v8::Local<v8::Value> name = props->Get(v8::Integer::New(i)); + (*identifier)[i] = GetStringIdentifier(v8::Local<v8::String>::Cast(name)); + } + return true; + } + + if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) && + npobj->_class->enumerate) { + return npobj->_class->enumerate(npobj, identifier, count); + } + + return false; +} diff --git a/webkit/port/bindings/v8/np_v8object.h b/webkit/port/bindings/v8/np_v8object.h new file mode 100644 index 0000000..cc25057 --- /dev/null +++ b/webkit/port/bindings/v8/np_v8object.h @@ -0,0 +1,63 @@ +// Copyright 2008, 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. + +#ifndef NP_V8OBJECT_H__ +#define NP_V8OBJECT_H__ + +#include "bindings/npruntime.h" +#include <v8.h> + +namespace WebCore { + class DOMWindow; +} + +extern NPClass* NPScriptObjectClass; + +// A V8NPObject is a NPObject which carries additional V8-specific +// information. It is allocated and deallocated by AllocV8NPObject() +// and FreeV8NPObject() methods. +struct V8NPObject { + NPObject object; + v8::Persistent<v8::Object> v8_object; + WebCore::DOMWindow* root_object; +}; + +struct PrivateIdentifier { + union { + const NPUTF8* string; + int32_t number; + } value; + bool isString; +}; + +NPObject* NPN_CreateScriptObject(NPP npp, v8::Handle<v8::Object>, + WebCore::DOMWindow*); +NPObject* NPN_CreateNoScriptObject(void); + +#endif // NP_V8OBJECT_H__ diff --git a/webkit/port/bindings/v8/npruntime.cpp b/webkit/port/bindings/v8/npruntime.cpp new file mode 100644 index 0000000..074fa46 --- /dev/null +++ b/webkit/port/bindings/v8/npruntime.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 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: + * 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. + */ + +#include "config.h" + +#include <map> +#include <set> +#include <string> +#include <v8.h> +#include "base/stats_counters.h" +#include "bindings/npruntime.h" +#include "np_v8object.h" +#include "npruntime_priv.h" +#include "v8_npobject.h" + +#include <wtf/Assertions.h> + +using namespace v8; + + +// TODO: Consider removing locks if we're singlethreaded already. +// The static initializer here should work okay, but we want to avoid +// static initialization in general. +// +// Commenting out the locks to avoid dependencies on chrome for now. +// Need a platform abstraction which we can use. +// static Lock StringIdentifierMapLock; + +typedef std::map<std::string, PrivateIdentifier*> StringIdentifierMap; + +static StringIdentifierMap* getStringIdentifierMap() { + static StringIdentifierMap* stringIdentifierMap = 0; + if (!stringIdentifierMap) + stringIdentifierMap = new StringIdentifierMap(); + return stringIdentifierMap; +} + +// TODO: Consider removing locks if we're singlethreaded already. +// static Lock IntIdentifierMapLock; + +typedef std::map<int, PrivateIdentifier*> IntIdentifierMap; + +static IntIdentifierMap* getIntIdentifierMap() { + static IntIdentifierMap* intIdentifierMap = 0; + if (!intIdentifierMap) + intIdentifierMap = new IntIdentifierMap; + return intIdentifierMap; +} + +extern "C" { + +NPIdentifier NPN_GetStringIdentifier(const NPUTF8* name) { + ASSERT(name); + + if (name) { + // AutoLock safeLock(StringIdentifierMapLock); + + StringIdentifierMap::iterator iter = + getStringIdentifierMap()->find(std::string(name)); + if (iter != getStringIdentifierMap()->end()) + return static_cast<NPIdentifier>(iter->second); + + PrivateIdentifier* identifier = reinterpret_cast<PrivateIdentifier*>( + malloc(sizeof(PrivateIdentifier))); + // We never release identifier names, so this dictionary will grow, + // as will the memory for the identifier name strings. + identifier->isString = true; + identifier->value.string = strdup(name); + (*getStringIdentifierMap())[std::string(name)] = identifier; + return (NPIdentifier)identifier; + } + + return 0; +} + +void NPN_GetStringIdentifiers(const NPUTF8** names, int32_t nameCount, + NPIdentifier* identifiers) { + ASSERT(names); + ASSERT(identifiers); + + if (names && identifiers) + for (int i = 0; i < nameCount; i++) + identifiers[i] = NPN_GetStringIdentifier(names[i]); +} + +NPIdentifier NPN_GetIntIdentifier(int32_t intid) { + // AutoLock safeLock(IntIdentifierMapLock); + + IntIdentifierMap::iterator iter = getIntIdentifierMap()->find(intid); + if (iter != getIntIdentifierMap()->end()) + return static_cast<NPIdentifier>(iter->second); + + PrivateIdentifier* identifier = reinterpret_cast<PrivateIdentifier*>( + malloc(sizeof(PrivateIdentifier))); + // We never release identifier names, so this dictionary will grow. + identifier->isString = false; + identifier->value.number = intid; + (*getIntIdentifierMap())[intid] = identifier; + return (NPIdentifier)identifier; +} + +bool NPN_IdentifierIsString(NPIdentifier identifier) { + PrivateIdentifier* i = reinterpret_cast<PrivateIdentifier*>(identifier); + return i->isString; +} + +NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier) { + PrivateIdentifier* i = reinterpret_cast<PrivateIdentifier*>(identifier); + if (!i->isString || !i->value.string) + return NULL; + + return (NPUTF8 *)strdup(i->value.string); +} + +int32_t NPN_IntFromIdentifier(NPIdentifier identifier) { + PrivateIdentifier* i = reinterpret_cast<PrivateIdentifier*>(identifier); + if (i->isString) + return 0; + return i->value.number; +} + +void NPN_ReleaseVariantValue(NPVariant* variant) { + ASSERT(variant); + + if (variant->type == NPVariantType_Object) { + NPN_ReleaseObject(variant->value.objectValue); + variant->value.objectValue = 0; + } else if (variant->type == NPVariantType_String) { + free((void*)variant->value.stringValue.UTF8Characters); + variant->value.stringValue.UTF8Characters = 0; + variant->value.stringValue.UTF8Length = 0; + } + + variant->type = NPVariantType_Void; +} + +static StatsCounter global_npobjects(L"NPObjects"); + +NPObject *NPN_CreateObject(NPP npp, NPClass* aClass) { + ASSERT(aClass); + + if (aClass) { + NPObject* obj; + if (aClass->allocate != NULL) + obj = aClass->allocate(npp, aClass); + else + obj = reinterpret_cast<NPObject*>(malloc(sizeof(NPObject))); + + obj->_class = aClass; + obj->referenceCount = 1; + + global_npobjects.Increment(); + return obj; + } + + return 0; +} + +NPObject* NPN_RetainObject(NPObject* obj) { + ASSERT(obj); + ASSERT(obj->referenceCount > 0); + + if (obj) + obj->referenceCount++; + + return obj; +} + +// _NPN_DeallocateObject actually deletes the object. Technically, +// callers should use NPN_ReleaseObject. Webkit exposes this function +// to kill objects which plugins may not have properly released. +void _NPN_DeallocateObject(NPObject *obj) { + ASSERT(obj); + ASSERT(obj->referenceCount >= 0); + + if (obj) { + global_npobjects.Decrement(); + + // NPObjects that remain in pure C++ may never have wrappers. + // Hence, if it's not already alive, don't unregister it. + // If it is alive, unregister it as the *last* thing we do + // so that it can do as much cleanup as possible on its own. + if (_NPN_IsAlive(obj)) + _NPN_UnregisterObject(obj); + + obj->referenceCount = -1; + if (obj->_class->deallocate) + obj->_class->deallocate(obj); + else + free(obj); + } +} + +void NPN_ReleaseObject(NPObject* obj) { + ASSERT(obj); + ASSERT(obj->referenceCount >= 1); + + if (obj && obj->referenceCount >= 1) { + if (--obj->referenceCount == 0) + _NPN_DeallocateObject(obj); + } +} + +void _NPN_InitializeVariantWithStringCopy(NPVariant* variant, + const NPString* value) { + variant->type = NPVariantType_String; + variant->value.stringValue.UTF8Length = value->UTF8Length; + variant->value.stringValue.UTF8Characters = + reinterpret_cast<NPUTF8*>(malloc(sizeof(NPUTF8) * value->UTF8Length)); + memcpy((void*)variant->value.stringValue.UTF8Characters, + value->UTF8Characters, + sizeof(NPUTF8) * value->UTF8Length); +} + + +// NPN_Registry +// +// The registry is designed for quick lookup of NPObjects. +// JS needs to be able to quickly lookup a given NPObject to determine +// if it is alive or not. +// The browser needs to be able to quickly lookup all NPObjects which are +// "owned" by an object. +// +// The g_live_objects is a hash table of all live objects to their owner +// objects. Presence in this table is used primarily to determine if +// objects are live or not. +// +// The g_root_objects is a hash table of root objects to a set of +// objects that should be deactivated in sync with the root. A +// root is defined as a top-level owner object. This is used on +// Frame teardown to deactivate all objects associated +// with a particular plugin. + +typedef std::set<NPObject*> NPObjectSet; +typedef std::map<NPObject*, NPObject*> NPObjectMap; +typedef std::map<NPObject*, NPObjectSet*> NPRootObjectMap; + +// A map of live NPObjects with pointers to their Roots. +NPObjectMap g_live_objects; + +// A map of the root objects and the list of NPObjects +// associated with that object. +NPRootObjectMap g_root_objects; + +void _NPN_RegisterObject(NPObject* obj, NPObject* owner) { + ASSERT(obj); + + // Check if already registered. + if (g_live_objects.find(obj) != g_live_objects.end()) { + return; + } + + if (!owner) { + // Registering a new owner object. + ASSERT(g_root_objects.find(obj) == g_root_objects.end()); + g_root_objects[obj] = new NPObjectSet(); + } else { + // Always associate this object with it's top-most parent. + // Since we always flatten, we only have to look up one level. + NPObjectMap::iterator owner_entry = g_live_objects.find(owner); + NPObject* parent = NULL; + if (g_live_objects.end() != owner_entry) + parent = owner_entry->second; + + if (parent) { + owner = parent; + } + ASSERT(g_root_objects.find(obj) == g_root_objects.end()); + if (g_root_objects.find(owner) != g_root_objects.end()) + (g_root_objects[owner])->insert(obj); + } + + ASSERT(g_live_objects.find(obj) == g_live_objects.end()); + g_live_objects[obj] = owner; +} + +void _NPN_UnregisterObject(NPObject* obj) { + ASSERT(obj); + ASSERT(g_live_objects.find(obj) != g_live_objects.end()); + + NPObject* owner = NULL; + if (g_live_objects.find(obj) != g_live_objects.end()) + owner = g_live_objects.find(obj)->second; + + if (owner == NULL) { + // Unregistering a owner object; also unregister it's descendants. + ASSERT(g_root_objects.find(obj) != g_root_objects.end()); + NPObjectSet* set = g_root_objects[obj]; + while (set->size() > 0) { + size_t size = set->size(); + NPObject* sub_object = *(set->begin()); + // The sub-object should not be a owner! + ASSERT(g_root_objects.find(sub_object) == g_root_objects.end()); + + // First, unregister the object. + set->erase(sub_object); + g_live_objects.erase(sub_object); + + // Remove the JS references to the object. + ForgetV8ObjectForNPObject(sub_object); + + ASSERT(set->size() < size); + } + delete set; + g_root_objects.erase(obj); + } else { + NPRootObjectMap::iterator owner_entry = g_root_objects.find(owner); + if (owner_entry != g_root_objects.end()) { + NPObjectSet* list = owner_entry->second; + ASSERT(list->find(obj) != list->end()); + list->erase(obj); + } + } + ForgetV8ObjectForNPObject(obj); + + g_live_objects.erase(obj); +} + +bool _NPN_IsAlive(NPObject* obj) { + return g_live_objects.find(obj) != g_live_objects.end(); +} + +} // extern "C" diff --git a/webkit/port/bindings/v8/npruntime_impl.h b/webkit/port/bindings/v8/npruntime_impl.h new file mode 100644 index 0000000..7dae168 --- /dev/null +++ b/webkit/port/bindings/v8/npruntime_impl.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2004 Apple Computer, 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. + */ + +#ifndef _NP_RUNTIME_IMPL_H_ +#define _NP_RUNTIME_IMPL_H_ + +#include "bindings/npruntime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void _NPN_ReleaseVariantValue(NPVariant *variant); +extern NPIdentifier _NPN_GetStringIdentifier(const NPUTF8 *name); +extern void _NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers); +extern NPIdentifier _NPN_GetIntIdentifier(int32_t intid); +extern bool _NPN_IdentifierIsString(NPIdentifier identifier); +extern NPUTF8 *_NPN_UTF8FromIdentifier(NPIdentifier identifier); +extern int32_t _NPN_IntFromIdentifier(NPIdentifier identifier); +extern NPObject *_NPN_CreateObject(NPP npp, NPClass *aClass); +extern NPObject *_NPN_RetainObject(NPObject *obj); +extern void _NPN_ReleaseObject(NPObject *obj); +extern void _NPN_DeallocateObject(NPObject *obj); +extern bool _NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result); +extern bool _NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +extern bool _NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, NPVariant *result); +extern bool _NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, NPVariant *result); +extern bool _NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value); +extern bool _NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +extern bool _NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +extern bool _NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName); +extern void _NPN_SetException(NPObject *obj, const NPUTF8 *message); +extern bool _NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif diff --git a/webkit/port/bindings/v8/npruntime_internal.h b/webkit/port/bindings/v8/npruntime_internal.h new file mode 100644 index 0000000..f5357cd --- /dev/null +++ b/webkit/port/bindings/v8/npruntime_internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007 Collabora, Ltd. 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.1 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * This is a internal include header for npapi.h + * + * Some of the #defines which are in X11 headers conflict with type and enum + * names in JavaScriptCore and WebCore + * This header #undefs those defines to fix the conflicts + * If you need to include npapi.h or npruntime.h when building on X11, + * include this file instead of the actual npapi.h or npruntime.h + */ + +#include "npruntime.h" + +#ifdef XP_UNIX + #include <X11/Xresource.h> + + #undef None + #undef Above + #undef Below + #undef Auto + #undef Complex + #undef Status +#endif diff --git a/webkit/port/bindings/v8/npruntime_priv.h b/webkit/port/bindings/v8/npruntime_priv.h new file mode 100644 index 0000000..543c7d1 --- /dev/null +++ b/webkit/port/bindings/v8/npruntime_priv.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, 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. + */ + +#ifndef NP_RUNTIME_PRIV_H_ +#define NP_RUNTIME_PRIV_H_ + + +#include "bindings/npruntime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + _NPN_InitializeVariantWithStringCopy() will copy string data. The string data + will be deallocated by calls to NPReleaseVariantValue(). +*/ +void _NPN_InitializeVariantWithStringCopy(NPVariant*, const NPString*); +void _NPN_DeallocateObject(NPObject *obj); + +// The following routines allow the browser to aggressively cleanup NPObjects +// on a per plugin basis. All NPObjects used through the NPRuntime API should +// be "registered" while they are alive. After an object has been +// deleted, it is possible for Javascript to have a reference to that object +// which has not yet been garbage collected. Javascript access to NPObjects +// will reference this registry to determine if the object is accessible or +// not. + +// Windows introduces an additional complication for objects created by the +// plugin. Plugins load inside of a DLL. Each DLL has it's own heap. If +// the browser unloads the plugin DLL, all objects created within the DLL's +// heap instantly become invalid. Normally, when WebKit drops the reference +// on the top-level plugin object, it tells the plugin manager that the +// plugin can be destroyed, which can unload the DLL. So, we must eliminate +// all pointers to any object ever created by the plugin. + +// We generally associate NPObjects with an owner. The owner of an NPObject +// is an NPObject which, when destroyed, also destroys all objects it owns. +// For example, if an NPAPI plugin creates 10 sub-NPObjects, all 11 objects +// (the NPAPI plugin + its 10 sub-objects) should become inaccessible +// simultaneously. + +// The ownership hierarchy is flat, and not a tree. Imagine the following +// object creation: +// PluginObject +// | +// +-- Creates -----> Object1 +// | +// +-- Creates -----> Object2 +// +// PluginObject will be the "owner" for both Object1 and Object2. + +// Register an NPObject with the runtime. If the owner is NULL, the +// object is treated as an owning object. If owner is not NULL, +// this object will be registered as owned by owner's top-level owner. +void _NPN_RegisterObject(NPObject* obj, NPObject* owner); + +// Unregister an NPObject with the runtime. If obj is an owning +// object, this call will also unregister all of the owned objects. +void _NPN_UnregisterObject(NPObject* obj); + +// Check to see if an object is registered with the runtime. +// Return true if registered, false otherwise. +bool _NPN_IsAlive(NPObject* obj); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif diff --git a/webkit/port/bindings/v8/v8_binding.h b/webkit/port/bindings/v8/v8_binding.h new file mode 100644 index 0000000..66237e8 --- /dev/null +++ b/webkit/port/bindings/v8/v8_binding.h @@ -0,0 +1,195 @@ +// Copyright 2008, 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. + +#ifndef V8_BINDING_H__ +#define V8_BINDING_H__ + +#include <v8.h> +#include "PlatformString.h" +#include "MathExtras.h" +#include "StringBuffer.h" + +// Suppress warnings in CString of converting size_t to unsigned int. +// TODO(fqian): fix CString.h. +#pragma warning(push, 0) +#include "CString.h" +#pragma warning(pop) +#include "windows.h" + +#include "platform.h" + +namespace WebCore { + +// The string returned by this function is still owned by the argument +// and will be deallocated when the argument is deallocated. +inline const uint16_t* FromWebCoreString(const String& str) { + return reinterpret_cast<const uint16_t*>(str.characters()); +} + +// WebCoreStringResource is a helper class for v8ExternalString. It is used +// to manage the life-cycle of the underlying buffer of the external string. +class WebCoreStringResource: public v8::String::ExternalStringResource { + public: + explicit WebCoreStringResource(const String& str) + : external_string_(str) { + } + + ~WebCoreStringResource() {} + + const uint16_t* data() const { return FromWebCoreString(external_string_); } + + size_t length() const { return external_string_.length(); } + + String webcore_string() { return external_string_; } + + private: + // A shallow copy of the string. + // Keeps the string buffer alive until the V8 engine garbage collects it. + String external_string_; +}; + +// TODO: converting between WebCore::String and V8 string is expensive. +// Optimize it !!! +inline String ToWebCoreString(v8::Handle<v8::Value> obj) { + v8::TryCatch block; + v8::Local<v8::String> v8_str = obj->ToString(); + if (v8_str.IsEmpty()) + return ""; + + if (v8_str->IsExternal()) { + WebCoreStringResource* str_resource = static_cast<WebCoreStringResource*>( + v8_str->GetExternalStringResource()); + return str_resource->webcore_string(); + } + + int length = v8_str->Length(); + StringBuffer buf(length); + v8_str->Write(reinterpret_cast<uint16_t*>(buf.characters()), 0, length); + return String::adopt(buf); +} + +inline String valueToStringWithNullCheck(v8::Handle<v8::Value> value) { + if (value->IsNull()) return String(); + return ToWebCoreString(value); +} + +inline String valueToStringWithNullOrUndefinedCheck( + v8::Handle<v8::Value> value) { + if (value->IsNull() || value->IsUndefined()) return String(); + return ToWebCoreString(value); +} + +// Convert a value to a 32-bit integer. The conversion fails if the +// value cannot be converted to an integer or converts to nan or to an +// infinity. +inline int ToInt32(v8::Handle<v8::Value> value, bool& ok) { + ok = true; + + // Fast case. The value is already a 32-bit integer. + if (value->IsInt32()) { + return value->Int32Value(); + } + + // Can the value be converted to a number? + v8::Local<v8::Number> number_object = value->ToNumber(); + if (number_object.IsEmpty()) { + ok = false; + return 0; + } + + // Does the value convert to nan or to an infinity? + double number_value = number_object->Value(); + if (isnan(number_value) || isinf(number_value)) { + ok = false; + return 0; + } + + // Can the value be converted to a 32-bit integer? + v8::Local<v8::Int32> int_value = value->ToInt32(); + if (int_value.IsEmpty()) { + ok = false; + return 0; + } + + // Return the result of the int32 conversion. + return int_value->Value(); +} + +// Convert a value to a 32-bit integer assuming the conversion cannot fail. +inline int ToInt32(v8::Handle<v8::Value> value) { + bool ok; + return ToInt32(value, ok); +} + +// If a WebCore string length is greater than the threshold, +// v8String creates an external string to avoid allocating +// the string in the large object space (which has a high memory overhead). +static const int kV8ExternalStringThreshold = 2048; + +// Convert a string to a V8 string. +inline v8::Handle<v8::String> v8String(const String& str) { + if (str.length() <= kV8ExternalStringThreshold) { + return v8::String::New(FromWebCoreString(str), str.length()); + } else { + return v8::String::NewExternal(new WebCoreStringResource(str)); + } +} + +inline v8::Handle<v8::String> v8UndetectableString(const String& str) { + return v8::String::NewUndetectable(FromWebCoreString(str), str.length()); +} + +// Return a V8 external string that shares the underlying buffer with the given +// WebCore string. The reference counting mechanism is used to keep the +// underlying buffer alive while the string is still live in the V8 engine. +inline v8::Local<v8::String> v8ExternalString(const String& str) { + return v8::String::NewExternal(new WebCoreStringResource(str)); +} + +inline v8::Handle<v8::Value> v8StringOrNull(const String& str) { + return str.isNull() + ? v8::Handle<v8::Value>(v8::Null()) + : v8::Handle<v8::Value>(v8String(str)); +} + +inline v8::Handle<v8::Value> v8StringOrUndefined(const String& str) { + return str.isNull() + ? v8::Handle<v8::Value>(v8::Undefined()) + : v8::Handle<v8::Value>(v8String(str)); +} + +inline v8::Handle<v8::Value> v8StringOrFalse(const String& str) { + return str.isNull() + ? v8::Handle<v8::Value>(v8::False()) + : v8::Handle<v8::Value>(v8String(str)); +} + +} // namespace WebCore + +#endif // V8_BINDING_H__ diff --git a/webkit/port/bindings/v8/v8_custom.cpp b/webkit/port/bindings/v8/v8_custom.cpp new file mode 100644 index 0000000..63b9d24 --- /dev/null +++ b/webkit/port/bindings/v8/v8_custom.cpp @@ -0,0 +1,3414 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2000 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004-2006 Apple Computer, Inc. + * Copyright (C) 2006 James G. Speth (speth@end.com) + * Copyright (C) 2006 Samuel Weinig (sam@webkit.org) + * Copyright 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <Assertions.h> + +#include "v8_proxy.h" +#include "v8_events.h" +#include "v8_binding.h" +#include "v8_npobject.h" +#include "v8_vectornodelist.h" +#include "v8_custom.h" + +#include "V8Attr.h" +#include "V8CanvasGradient.h" +#include "V8CanvasPattern.h" +#include "V8Document.h" +#include "V8DOMWindow.h" +#include "V8HTMLCanvasElement.h" +#include "V8HTMLDocument.h" +#include "V8HTMLImageElement.h" +#include "V8HTMLOptionElement.h" +#include "V8Node.h" +#include "V8XPathNSResolver.h" +#include "V8XPathResult.h" + +#include "CanvasGradient.h" +#include "CanvasPattern.h" +#include "CanvasStyle.h" +#include "CanvasRenderingContext2D.h" + +#include "Clipboard.h" +#include "ClipboardEvent.h" + +#include "Base64.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameTree.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "DOMParser.h" +#include "DOMWindow.h" +#include "Location.h" +#include "History.h" +#include "ScheduledAction.h" +#include "Event.h" +#include "EventListener.h" +#include "EventTargetNode.h" +#include "EventTarget.h" +#include "ExceptionCode.h" +#include "xmlhttprequest.h" +#include "XMLSerializer.h" +#include "KURL.h" +#include "DeprecatedString.h" +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "HTMLBodyElement.h" +#include "HTMLEmbedElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLFrameElement.h" +#include "HTMLFrameElementBase.h" +#include "HTMLFrameSetElement.h" +#include "HTMLImageElement.h" +#include "HTMLCanvasElement.h" +#include "HTMLFormElement.h" +#include "HTMLOptionElement.h" +#include "HTMLOptionsCollection.h" +#include "HTMLSelectElement.h" +#include "MouseEvent.h" +#include "NodeIterator.h" +#include "Page.h" +#include "PlatformScreen.h" +#include "RenderWidget.h" +#include "RenderPartObject.h" +#include "RGBColor.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "StyleSheetList.h" +#include "TreeWalker.h" +#include "WindowFeatures.h" +#include "XPathEvaluator.h" +#include "JSXPathNSResolver.h" +#include "XPathResult.h" +#include "XSLTProcessor.h" +#include "V8Bridge.h" + +#if ENABLE(SVG) +#include "V8SVGPODTypeWrapper.h" +#include "SVGException.h" +#include "SVGPathSeg.h" +#endif + +#undef LOG + +#include "webkit/glue/webplugin_impl.h" + +// Horizontal and vertical offset, from the parent content area, around newly +// opened popups that don't specify a location. +static const int kPopupTilePixels = 10; + +namespace WebCore { + +#define CALLBACK_FUNC_DECL(NAME) \ +v8::Handle<v8::Value> V8Custom::v8##NAME##Callback(const v8::Arguments& args) + +#define ACCESSOR_GETTER(NAME) \ +v8::Handle<v8::Value> V8Custom::v8##NAME##AccessorGetter(\ + v8::Local<v8::String> name, const v8::AccessorInfo& info) + +#define ACCESSOR_SETTER(NAME) \ +void V8Custom::v8##NAME##AccessorSetter(v8::Local<v8::String> name, \ + v8::Local<v8::Value> value, \ + const v8::AccessorInfo& info) + +#define NAMED_PROPERTY_GETTER(NAME) \ +v8::Handle<v8::Value> V8Custom::v8##NAME##NamedPropertyGetter(\ + v8::Local<v8::String> name, const v8::AccessorInfo& info) + +#define NAMED_PROPERTY_SETTER(NAME) \ +v8::Handle<v8::Value> V8Custom::v8##NAME##NamedPropertySetter(\ + v8::Local<v8::String> name, v8::Local<v8::Value> value, \ + const v8::AccessorInfo& info) + +#define NAMED_PROPERTY_DELETER(NAME) \ +v8::Handle<v8::Boolean> V8Custom::v8##NAME##NamedPropertyDeleter(\ + v8::Local<v8::String> name, const v8::AccessorInfo& info) + +#define INDEXED_PROPERTY_GETTER(NAME) \ +v8::Handle<v8::Value> V8Custom::v8##NAME##IndexedPropertyGetter(\ + uint32_t index, const v8::AccessorInfo& info) + +#define INDEXED_PROPERTY_SETTER(NAME) \ +v8::Handle<v8::Value> V8Custom::v8##NAME##IndexedPropertySetter(\ + uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info) + +#define INDEXED_PROPERTY_DELETER(NAME) \ +v8::Handle<v8::Boolean> V8Custom::v8##NAME##IndexedPropertyDeleter(\ + uint32_t index, const v8::AccessorInfo& info) + +#define NAMED_ACCESS_CHECK(NAME) \ +bool V8Custom::v8##NAME##NamedSecurityCheck(v8::Local<v8::Object> host, \ + v8::Local<v8::Value> key, \ + v8::AccessType type, \ + v8::Local<v8::Value> data) + +#define INDEXED_ACCESS_CHECK(NAME) \ +bool V8Custom::v8##NAME##IndexedSecurityCheck(v8::Local<v8::Object> host, \ + uint32_t index, \ + v8::AccessType type, \ + v8::Local<v8::Value> data) + +class V8ScheduledAction : public ScheduledAction { + public: + V8ScheduledAction(v8::Handle<v8::Function> func, int argc, + v8::Handle<v8::Value> argv[]); + explicit V8ScheduledAction(const WebCore::String& code) : m_code(code), + m_argc(0), m_argv(NULL) { } + virtual ~V8ScheduledAction(); + virtual void execute(DOMWindow* window); + + private: + v8::Persistent<v8::Function> m_func; + int m_argc; + v8::Persistent<v8::Value>* m_argv; + + String m_code; +}; + +V8ScheduledAction::V8ScheduledAction(v8::Handle<v8::Function> func, int argc, + v8::Handle<v8::Value> argv[]) { + m_func = v8::Persistent<v8::Function>::New(func); + +#ifndef NDEBUG + V8Proxy::RegisterGlobalHandle(SCHEDULED_ACTION, this, m_func); +#endif + + m_argc = argc; + if (argc > 0) { + m_argv = new v8::Persistent<v8::Value>[argc]; + for (int i = 0; i < argc; i++) { + m_argv[i] = v8::Persistent<v8::Value>::New(argv[i]); + +#ifndef NDEBUG + V8Proxy::RegisterGlobalHandle(SCHEDULED_ACTION, this, m_argv[i]); +#endif + } + } else { + m_argv = NULL; + } +} + + +V8ScheduledAction::~V8ScheduledAction() { + if (!m_func.IsEmpty()) { +#ifndef NDEBUG + V8Proxy::UnregisterGlobalHandle(this, m_func); +#endif + m_func.Dispose(); + + for (int i = 0; i < m_argc; i++) { +#ifndef NDEBUG + V8Proxy::UnregisterGlobalHandle(this, m_argv[i]); +#endif + m_argv[i].Dispose(); + } + if (m_argc > 0) { + delete[] m_argv; + } + } +} + + +void V8ScheduledAction::execute(DOMWindow* window) { + // TODO(ager): Timeouts for running the javascript code are not set. + Frame* frame = window->frame(); + if (!frame) return; + + v8::HandleScope handle_scope; + v8::Local<v8::Context> context = V8Proxy::GetContext(frame); + if (context.IsEmpty()) return; // JS may not be enabled. + + v8::Context::Scope scope(context); + + V8Proxy* proxy = V8Proxy::retrieve(frame); + proxy->setTimerCallback(true); + + if (!m_func.IsEmpty() && m_func->IsFunction()) { + proxy->CallFunction(v8::Persistent<v8::Function>::Cast(m_func), + context->Global(), m_argc, m_argv); + } else { + // TODO: why cannot just compile and run m_code? + // check what's in V8Proxy::Evaluate + frame->loader()->executeScript(m_code); + } + + if (Document* doc = frame->document()) + doc->updateRendering(); + + proxy->setTimerCallback(false); +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestConstructor) { + INC_STATS(L"DOM.XMLHttpRequest.Constructor"); + + if (!args.IsConstructCall()) { + V8Proxy::ThrowError(V8Proxy::TYPE_ERROR, + "DOM object constructor cannot be called as a function."); + return v8::Undefined(); + } + // Expect no parameters. + // Allocate a XMLHttpRequest object as its internal field. + Document* doc = V8Proxy::retrieveFrame()->document(); + XMLHttpRequest* xhr = new XMLHttpRequest(doc); + V8Proxy::SetDOMWrapper(args.Holder(), + V8ClassIndex::ToInt(V8ClassIndex::XMLHTTPREQUEST), xhr); + // Set object as the peer. + V8Proxy::SetJSWrapperForDOMObject(xhr, + v8::Persistent<v8::Object>::New(args.Holder())); + return args.Holder(); +} + + +CALLBACK_FUNC_DECL(DOMParserConstructor) { + INC_STATS(L"DOM.DOMParser.Contructor"); + return V8Proxy::ConstructDOMObject<V8ClassIndex::DOMPARSER, + DOMParser>(args); +} + + +CALLBACK_FUNC_DECL(XMLSerializerConstructor) { + INC_STATS(L"DOM.XMLSerializer.Constructor"); + return V8Proxy::ConstructDOMObject<V8ClassIndex::XMLSERIALIZER, + XMLSerializer>(args); +} + + +CALLBACK_FUNC_DECL(XPathEvaluatorConstructor) { + INC_STATS(L"DOM.XPathEvaluator.Constructor"); + return V8Proxy::ConstructDOMObject<V8ClassIndex::XPATHEVALUATOR, + XPathEvaluator>(args); +} + + +CALLBACK_FUNC_DECL(XSLTProcessorConstructor) { + INC_STATS(L"DOM.XSLTProcessor.Constructor"); + return V8Proxy::ConstructDOMObject<V8ClassIndex::XSLTPROCESSOR, + XSLTProcessor>(args); +} + + +CALLBACK_FUNC_DECL(XSLTProcessorImportStylesheet) { + INC_STATS(L"DOM.XSLTProcessor.importStylesheet"); + // Return undefined if argument does not have the correct type. + if (!V8Node::HasInstance(args[0])) + return v8::Undefined(); + + XSLTProcessor* imp = V8Proxy::FastToNativeObject<XSLTProcessor>( + V8ClassIndex::XSLTPROCESSOR, args.Holder()); + + Node* node = V8Proxy::FastToNativeObject<Node>(V8ClassIndex::NODE, args[0]); + imp->importStylesheet(node); + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(XSLTProcessorTransformToFragment) { + INC_STATS(L"DOM.XSLTProcessor.transformToFragment"); + // Return undefined if arguments do not have correct types. + if (!V8Node::HasInstance(args[0]) || !V8Document::HasInstance(args[1])) + return v8::Undefined(); + + XSLTProcessor* imp = V8Proxy::FastToNativeObject<XSLTProcessor>( + V8ClassIndex::XSLTPROCESSOR, args.Holder()); + + Node* source = V8Proxy::FastToNativeObject<Node>(V8ClassIndex::NODE, args[0]); + Document* owner = + V8Proxy::FastToNativeObject<Document>(V8ClassIndex::NODE, args[1]); + RefPtr<DocumentFragment> result = imp->transformToFragment(source, owner); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + + +CALLBACK_FUNC_DECL(XSLTProcessorTransformToDocument) { + INC_STATS(L"DOM.XSLTProcessor.transformToDocument"); + // Return undefined if argument does not have the correct type. + if (!V8Node::HasInstance(args[0])) + return v8::Undefined(); + + XSLTProcessor* imp = V8Proxy::FastToNativeObject<XSLTProcessor>( + V8ClassIndex::XSLTPROCESSOR, args.Holder()); + + Node* source = V8Proxy::FastToNativeObject<Node>(V8ClassIndex::NODE, args[0]); + if (!source) return v8::Undefined(); + RefPtr<Document> result = imp->transformToDocument(source); + // Return undefined if no result was found. + if (!result) return v8::Undefined(); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + + +CALLBACK_FUNC_DECL(XSLTProcessorSetParameter) { + INC_STATS(L"DOM.XSLTProcessor.setParameter"); + // Bail out if localName or value is null or undefined. + if (args[1]->IsNull() || args[1]->IsUndefined() || + args[2]->IsNull() || args[2]->IsUndefined()) { + return v8::Undefined(); + } + + XSLTProcessor* imp = V8Proxy::FastToNativeObject<XSLTProcessor>( + V8ClassIndex::XSLTPROCESSOR, args.Holder()); + + String namespaceURI = ToWebCoreString(args[0]); + String localName = ToWebCoreString(args[1]); + String value = ToWebCoreString(args[2]); + imp->setParameter(namespaceURI, localName, value); + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(XSLTProcessorGetParameter) { + INC_STATS(L"DOM.XSLTProcessor.getParameter"); + // Bail out if localName is null or undefined. + if (args[1]->IsNull() || args[1]->IsUndefined()) { + return v8::Undefined(); + } + + XSLTProcessor* imp = V8Proxy::FastToNativeObject<XSLTProcessor>( + V8ClassIndex::XSLTPROCESSOR, args.Holder()); + + String namespaceURI = ToWebCoreString(args[0]); + String localName = ToWebCoreString(args[1]); + String result = imp->getParameter(namespaceURI, localName); + // Return undefined if the string is null. + if (result.isNull()) return v8::Undefined(); + return v8String(result); +} + + +CALLBACK_FUNC_DECL(XSLTProcessorRemoveParameter) { + INC_STATS(L"DOM.XSLTProcessor.removeParameter"); + // Bail out if localName is null or undefined. + if (args[1]->IsNull() || args[1]->IsUndefined()) { + return v8::Undefined(); + } + + XSLTProcessor* imp = V8Proxy::FastToNativeObject<XSLTProcessor>( + V8ClassIndex::XSLTPROCESSOR, args.Holder()); + + String namespaceURI = ToWebCoreString(args[0]); + String localName = ToWebCoreString(args[1]); + imp->removeParameter(namespaceURI, localName); + return v8::Undefined(); +} + + +// ---- Canvas support ---- +static v8::Handle<v8::Value> CanvasStyleToV8Object(CanvasStyle* style) { + if (style->gradient()) { + return V8Proxy::ToV8Object(V8ClassIndex::CANVASGRADIENT, + static_cast<Peerable*>(style->gradient())); + } + if (style->pattern()) { + return V8Proxy::ToV8Object(V8ClassIndex::CANVASPATTERN, + static_cast<Peerable*>(style->pattern())); + } + return v8String(style->color()); +} + + +static PassRefPtr<CanvasStyle> V8ObjectToCanvasStyle( + v8::Handle<v8::Value> value) { + if (value->IsString()) return new CanvasStyle(ToWebCoreString(value)); + + if (V8CanvasGradient::HasInstance(value)) { + CanvasGradient* gradient = + V8Proxy::FastDOMWrapperToNative<CanvasGradient>(value); + return new CanvasStyle(gradient); + } + + if (V8CanvasPattern::HasInstance(value)) { + CanvasPattern* pattern = + V8Proxy::FastDOMWrapperToNative<CanvasPattern>(value); + return new CanvasStyle(pattern); + } + + return 0; +} + + +ACCESSOR_GETTER(CanvasRenderingContext2DStrokeStyle) { + CanvasRenderingContext2D* impl = + V8Proxy::FastDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder()); + CanvasStyle* strokeStyle = impl->strokeStyle(); + return CanvasStyleToV8Object(strokeStyle); +} + + +ACCESSOR_SETTER(CanvasRenderingContext2DStrokeStyle) { + CanvasRenderingContext2D* impl = + V8Proxy::FastDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder()); + impl->setStrokeStyle(V8ObjectToCanvasStyle(value)); +} + +ACCESSOR_GETTER(CanvasRenderingContext2DFillStyle) { + CanvasRenderingContext2D* impl = + V8Proxy::FastDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder()); + CanvasStyle* fillStyle = impl->fillStyle(); + return CanvasStyleToV8Object(fillStyle); +} + + +ACCESSOR_SETTER(CanvasRenderingContext2DFillStyle) { + CanvasRenderingContext2D* impl = + V8Proxy::FastDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder()); + impl->setFillStyle(V8ObjectToCanvasStyle(value)); +} + + +// DOMImplementation is a singleton in WebCore. If we use our normal +// mapping from DOM objects to V8 wrappers, the same wrapper will be +// shared for all frames in the same process. This is a major +// security problem. Therefore, we generate a DOMImplementation +// wrapper per document and store it in an internal field of the +// document. Since the DOMImplementation object is a singleton, we do +// not have to do anything to keep the DOMImplementation object alive +// for the lifetime of the wrapper. +ACCESSOR_GETTER(DocumentImplementation) { + ASSERT(info.Holder()->InternalFieldCount() >= + kDocumentMinimumInternalFieldCount); + // Check if the internal field already contains a wrapper. + v8::Local<v8::Value> implementation = + info.Holder()->GetInternalField(kDocumentImplementationIndex); + if (!implementation->IsUndefined()) { + return implementation; + } + // Generate a wrapper. + Document* doc = V8Proxy::FastDOMWrapperToNative<Document>(info.Holder()); + v8::Handle<v8::Value> wrapper = + V8Proxy::DOMImplementationToV8Object(doc->implementation()); + // Store the wrapper in the internal field. + info.Holder()->SetInternalField(kDocumentImplementationIndex, wrapper); + + return wrapper; +} + + +ACCESSOR_GETTER(DocumentLocation) { + Document* imp = V8Proxy::FastDOMWrapperToNative<Document>(info.Holder()); + if (!imp->frame()) + return v8::Null(); + + DOMWindow* window = imp->frame()->domWindow(); + return V8Proxy::ToV8Object(V8ClassIndex::LOCATION, + static_cast<Peerable*>(window->location())); +} + + +ACCESSOR_SETTER(DocumentLocation) { + Document* imp = V8Proxy::FastDOMWrapperToNative<Document>(info.Holder()); + if (!imp->frame()) + return; + + DOMWindow* window = imp->frame()->domWindow(); + // DOMWindow::setLocation does security checks. + window->setLocation(ToWebCoreString(value)); +} + + +ACCESSOR_GETTER(DOMWindowLocation) { + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper( + V8ClassIndex::DOMWINDOW, info.This()); + if (holder.IsEmpty()) + return v8::Undefined(); + + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, holder); + + // Give subframes precedence. + if (imp->frame()) { + Frame* child = imp->frame()->tree()->child("location"); + if (child) + return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, child->domWindow()); + } + + // Normal getter or real 'location' property. + Location* v = imp->location(); + return V8Proxy::ToV8Object(V8ClassIndex::LOCATION, static_cast<Peerable*>(v)); +} + + +ACCESSOR_SETTER(DOMWindowLocation) { + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper( + V8ClassIndex::DOMWINDOW, info.This()); + if (holder.IsEmpty()) + return; + + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, holder); + imp->setLocation(ToWebCoreString(value)); +} + + +ACCESSOR_SETTER(DOMWindowOpener) { + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, info.Holder()); + + if (!V8Proxy::IsFromSameOrigin(imp->frame(), true)) + return; + + // Opener can be shadowed if it is in the same domain. + // Have a special handling of null value to behave + // like Firefox. See bug 1224887 & 791706. + if (value->IsNull()) { + // imp->frame() cannot be null, + // otherwise, SameOrigin check would have failed. + ASSERT(imp->frame()); + imp->frame()->loader()->setOpener(0); + } + + // Delete the accessor from this object. + info.Holder()->Delete(name); + + // Put property on the front (this) object. + info.This()->Set(name, value); +} + + +ACCESSOR_GETTER(EventSrcElement) { + Event* event = V8Proxy::FastDOMWrapperToNative<Event>(info.Holder()); + EventTarget* target = event->target(); + return V8Proxy::EventTargetToV8Object(target); +} + + +ACCESSOR_GETTER(EventReturnValue) { + Event* event = V8Proxy::FastDOMWrapperToNative<Event>(info.Holder()); + return event->defaultPrevented() ? v8::False() : v8::True(); +} + + +ACCESSOR_SETTER(EventReturnValue) { + Event* event = V8Proxy::FastDOMWrapperToNative<Event>(info.Holder()); + bool v = value->BooleanValue(); + event->setDefaultPrevented(!v); +} + + +ACCESSOR_GETTER(EventDataTransfer) { + Event* event = V8Proxy::FastDOMWrapperToNative<Event>(info.Holder()); + + if (event->isDragEvent()) { + MouseEvent* impl = static_cast<MouseEvent*>(event); + Clipboard* clipboard = impl->clipboard(); + return V8Proxy::ToV8Object(V8ClassIndex::CLIPBOARD, clipboard); + } + + return v8::Undefined(); +} + + +ACCESSOR_GETTER(EventClipboardData) { + Event* event = V8Proxy::FastDOMWrapperToNative<Event>(info.Holder()); + + if (event->isClipboardEvent()) { + ClipboardEvent* impl = static_cast<ClipboardEvent*>(event); + Clipboard* clipboard = impl->clipboard(); + return V8Proxy::ToV8Object(V8ClassIndex::CLIPBOARD, clipboard); + } + + return v8::Undefined(); +} + + +static v8::Handle<v8::Value> HTMLCollectionGetNamedItems( + HTMLCollection* collection, String name) { + Vector<RefPtr<Node> > namedItems; + collection->namedItems(name, namedItems); + switch (namedItems.size()) { + case 0: + return v8::Handle<v8::Value>(); + case 1: + return V8Proxy::ToV8Object(V8ClassIndex::NODE, namedItems.at(0).get()); + default: + NodeList* list = new V8VectorNodeList(namedItems); + return V8Proxy::ToV8Object(V8ClassIndex::NODELIST, + static_cast<Peerable*>(list)); + } +} + + +static v8::Handle<v8::Value> HTMLCollectionGetItem( + HTMLCollection* collection, v8::Handle<v8::Value> argument) { + v8::Local<v8::Uint32> index = argument->ToArrayIndex(); + if (index.IsEmpty()) { + v8::Handle<v8::String> str = argument->ToString(); + v8::Handle<v8::Value> result = + HTMLCollectionGetNamedItems(collection, ToWebCoreString(str)); + if (result.IsEmpty()) + return v8::Undefined(); + else + return result; + } + unsigned i = index->Uint32Value(); + RefPtr<Node> result = collection->item(i); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + + +NAMED_PROPERTY_GETTER(HTMLCollection) { + INC_STATS(L"DOM.HTMLCollection.NamedPropertyGetter"); + // Search the prototype chain first. + v8::Handle<v8::Value> value = + info.Holder()->GetRealNamedPropertyInPrototypeChain(name); + + if (!value.IsEmpty()) { + return value; + } + + // Search local callback properties next to find IDL defined + // properties. + if (info.Holder()->HasRealNamedCallbackProperty(name)) { + return v8::Handle<v8::Value>(); + } + + // Finally, search the DOM structure. + HTMLCollection* imp = V8Proxy::FastToNativeObject<HTMLCollection>( + V8ClassIndex::HTMLCOLLECTION, info.Holder()); + String key = ToWebCoreString(name); + return HTMLCollectionGetNamedItems(imp, key); +} + + +CALLBACK_FUNC_DECL(HTMLCollectionItem) { + INC_STATS(L"DOM.HTMLCollection.item()"); + HTMLCollection* imp = V8Proxy::FastToNativeObject<HTMLCollection>( + V8ClassIndex::HTMLCOLLECTION, args.Holder()); + return HTMLCollectionGetItem(imp, args[0]); +} + + +CALLBACK_FUNC_DECL(HTMLCollectionNamedItem) { + INC_STATS(L"DOM.HTMLCollection.namedItem()"); + HTMLCollection* imp = V8Proxy::FastToNativeObject<HTMLCollection>( + V8ClassIndex::HTMLCOLLECTION, args.Holder()); + String name = ToWebCoreString(args[0]); + v8::Handle<v8::Value> result = + HTMLCollectionGetNamedItems(imp, name); + if (result.IsEmpty()) + return v8::Undefined(); + else + return result; +} + + +CALLBACK_FUNC_DECL(HTMLCollectionCallAsFunction) { + INC_STATS(L"DOM.HTMLCollection.callAsFunction()"); + if (args.Length() < 1) return v8::Undefined(); + + HTMLCollection* imp = V8Proxy::FastToNativeObject<HTMLCollection>( + V8ClassIndex::HTMLCOLLECTION, args.Holder()); + + if (args.Length() == 1) { + return HTMLCollectionGetItem(imp, args[0]); + } + + // If there is a second argument it is the index of the item we + // want. + String name = ToWebCoreString(args[0]); + v8::Local<v8::Uint32> index = args[1]->ToArrayIndex(); + if (index.IsEmpty()) return v8::Undefined(); + unsigned i = index->Uint32Value(); + Node* node = imp->namedItem(name); + while (node) { + if (i == 0) return V8Proxy::ToV8Object(V8ClassIndex::NODE, node); + node = imp->nextNamedItem(name); + i--; + } + + return v8::Undefined(); +} + + +static v8::Handle<v8::Value> V8HTMLSelectElementRemoveHelper( + HTMLSelectElement* imp, const v8::Arguments& args) { + if (V8HTMLOptionElement::HasInstance(args[0])) { + HTMLOptionElement* element = V8Proxy::ToNativeObject<HTMLOptionElement>( + V8ClassIndex::HTMLOPTIONELEMENT, args[0]); + imp->remove(element->index()); + return v8::Undefined(); + } + + imp->remove(ToInt32(args[0])); + return v8::Undefined(); +} + +CALLBACK_FUNC_DECL(HTMLSelectElementRemove) { + INC_STATS(L"DOM.HTMLSelectElement.remove"); + HTMLSelectElement* imp = V8Proxy::FastToNativeObject<HTMLSelectElement>( + V8ClassIndex::HTMLSELECTELEMENT, args.Holder()); + return V8HTMLSelectElementRemoveHelper(imp, args); +} + +CALLBACK_FUNC_DECL(HTMLOptionsCollectionRemove) { + INC_STATS(L"DOM.HTMLOptionsCollection.remove()"); + HTMLOptionsCollection* imp = + V8Proxy::FastToNativeObject<HTMLOptionsCollection>( + V8ClassIndex::HTMLOPTIONSCOLLECTION, args.Holder()); + HTMLSelectElement* base = static_cast<HTMLSelectElement*>(imp->base()); + return V8HTMLSelectElementRemoveHelper(base, args); +} + + +CALLBACK_FUNC_DECL(HTMLOptionsCollectionAdd) { + INC_STATS(L"DOM.HTMLOptionsCollection.add()"); + if (!V8HTMLOptionElement::HasInstance(args[0])) { + V8Proxy::SetDOMException(TYPE_MISMATCH_ERR); + return v8::Undefined(); + } + HTMLOptionsCollection* imp = + V8Proxy::FastToNativeObject<HTMLOptionsCollection>( + V8ClassIndex::HTMLOPTIONSCOLLECTION, args.Holder()); + HTMLOptionElement* option = V8Proxy::ToNativeObject<HTMLOptionElement>( + V8ClassIndex::NODE, args[0]); + + ExceptionCode ec = 0; + if (args.Length() < 2) { + imp->add(option, ec); + } else { + bool ok; + v8::TryCatch try_catch; + int index = ToInt32(args[1], ok); + if (try_catch.HasCaught()) { + return v8::Undefined(); + } + if (!ok) { + ec = TYPE_MISMATCH_ERR; + } else { + imp->add(option, index, ec); + } + } + if (ec != 0) { + V8Proxy::SetDOMException(ec); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(DOMWindowAddEventListener) { + INC_STATS(L"DOM.DOMWindow.addEventListener()"); + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + + // Fast check This argument with the global object of security context. + if ((args.This() != v8::Context::GetCurrentSecurityContext()->Global()) && + !V8Proxy::IsFromSameOrigin(imp->frame(), true)) { + return v8::Undefined(); + } + + if (!imp->frame()) + return v8::Undefined(); // DOMWindow could be disconnected from the frame + + Document* doc = imp->frame()->document(); + if (!doc) + return v8::Undefined(); + + // TODO: Check if there is not enough arguments + V8Proxy* proxy = V8Proxy::retrieve(imp->frame()); + if (!proxy) + return v8::Undefined(); + + EventListener* listener = + proxy->FindOrCreateV8EventListener(args[1], false); + + if (listener) { + String event_type = ToWebCoreString(args[0]); + bool useCapture = args[2]->BooleanValue(); + doc->addWindowEventListener(event_type, listener, useCapture); + } + + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(DOMWindowRemoveEventListener) { + INC_STATS(L"DOM.DOMWindow.removeEventListener()"); + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + + // Fast check This argument with the global object of security context. + if ((args.This() != v8::Context::GetCurrentSecurityContext()->Global()) && + !V8Proxy::IsFromSameOrigin(imp->frame(), true)) { + return v8::Undefined(); + } + + if (!imp->frame()) + return v8::Undefined(); + + Document* doc = imp->frame()->document(); + if (!doc) + return v8::Undefined(); + + V8Proxy* proxy = V8Proxy::retrieve(imp->frame()); + if (!proxy) + return v8::Undefined(); + + EventListener* listener = + proxy->FindV8EventListener(args[1], false); + + if (listener) { + String event_type = ToWebCoreString(args[0]); + bool useCapture = args[2]->BooleanValue(); + doc->removeWindowEventListener(event_type, listener, useCapture); + } + + return v8::Undefined(); +} + +CALLBACK_FUNC_DECL(DOMWindowPostMessage) { + INC_STATS(L"DOM.DOMWindow.postMessage()"); + DOMWindow* window = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + + DOMWindow* source = V8Proxy::retrieveActiveFrame()->domWindow(); + ASSERT(source->frame()); + + String domain = source->frame()->loader()->url().host(); + String uri = source->frame()->loader()->url().string(); + + v8::TryCatch try_catch; + + String message = ToWebCoreString(args[0]); + + if (try_catch.HasCaught()) return v8::Undefined(); + + window->postMessage(message, domain, uri, source); + + return v8::Undefined(); +} + + +static bool canShowModalDialogNow(const Frame* frame) { + // A frame can out live its page. See bug 1219613. + if (!frame || !frame->page()) + return false; + return frame->page()->chrome()->canRunModalNow(); +} + +static bool allowPopUp() { + Frame* frame = V8Proxy::retrieveActiveFrame(); + + ASSERT(frame); + if (frame->scriptBridge()->wasRunByUserGesture()) return true; + Settings* settings = frame->settings(); + return settings && settings->JavaScriptCanOpenWindowsAutomatically(); +} + +static HashMap<String, String> parseModalDialogFeatures( + const String& features_arg) { + HashMap<String, String> map; + + Vector<String> features = features_arg.split(';'); + Vector<String>::const_iterator end = features.end(); + for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) { + String s = *it; + int pos = s.find('='); + int colonPos = s.find(':'); + if (pos >= 0 && colonPos >= 0) + continue; // ignore any strings that have both = and : + if (pos < 0) + pos = colonPos; + if (pos < 0) { + // null string for value means key without value + map.set(s.stripWhiteSpace().lower(), String()); + } else { + String key = s.left(pos).stripWhiteSpace().lower(); + String val = s.substring(pos + 1).stripWhiteSpace().lower(); + int spacePos = val.find(' '); + if (spacePos != -1) + val = val.left(spacePos); + map.set(key, val); + } + } + + return map; +} + + +static Frame* createWindow(Frame* opener_frame, + const String& url, + const String& frame_name, + const WindowFeatures& window_features, + v8::Local<v8::Value> dialog_args) { + Frame* active_frame = V8Proxy::retrieveActiveFrame(); + + ResourceRequest request; + if (active_frame) + request.setHTTPReferrer(active_frame->loader()->outgoingReferrer()); + FrameLoadRequest frame_request(request, frame_name); + + // FIXME: It's much better for client API if a new window starts with a URL, + // here where we know what URL we are going to open. Unfortunately, this code + // passes the empty string for the URL, but there's a reason for that. + // Before loading we have to set up the opener, openedByDOM, + // and dialogArguments values. Also, to decide whether to use the URL + // we currently do an allowsAccessFrom call using the window we create, + // which can't be done before creating it. We'd have to resolve all those + // issues to pass the URL instead of "". + + bool created; + // We pass in the opener frame here so it can be used for looking up the frame + // name, in case the active frame is different from the opener frame, and + // the name references a frame relative to the opener frame, for example + // "_self" or "_parent". + Frame* new_frame = active_frame->loader()->createWindow( + opener_frame->loader(), frame_request, window_features, created); + if (!new_frame) { + return 0; + } + + new_frame->loader()->setOpener(opener_frame); + new_frame->loader()->setOpenedByDOM(); + + // Set dialog arguments on the global object of the new frame. + if (!dialog_args.IsEmpty()) { + v8::Local<v8::Context> context = V8Proxy::GetContext(new_frame); + if (!context.IsEmpty()) { + v8::Context::Scope scope(context); + context->Global()->Set(v8::String::New("dialogArguments"), dialog_args); + } + } + + if (!parseURL(url).startsWith("javascript:", false) || + JSBridge::isSafeScript(new_frame)) { + String completed_url = + url.isEmpty() ? url : active_frame->document()->completeURL(url); + bool user_gesture = active_frame->scriptBridge()->wasRunByUserGesture(); + + if (created) { + new_frame->loader()->changeLocation( + KURL(completed_url.deprecatedString()), + active_frame->loader()->outgoingReferrer(), + false, + user_gesture); + if (Document* old_doc = opener_frame->document()) { + new_frame->document()->setBaseURL(old_doc->baseURL()); + } + } else if (!url.isEmpty()) { + new_frame->loader()->scheduleLocationChange( + completed_url, + active_frame->loader()->outgoingReferrer(), + false, + user_gesture); + } + } + + return new_frame; +} + + +CALLBACK_FUNC_DECL(DOMWindowShowModalDialog) { + INC_STATS(L"DOM.DOMWindow.showModalDialog()"); + DOMWindow* window = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + Frame* frame = window->frame(); + + if (!frame) + return v8::Undefined(); + + if (!canShowModalDialogNow(frame) || !allowPopUp()) + return v8::Undefined(); + + String url = valueToStringWithNullOrUndefinedCheck(args[0]); + v8::Local<v8::Value> dialog_args = args[1]; + String feature_args = valueToStringWithNullOrUndefinedCheck(args[2]); + + const HashMap<String, String> features = + parseModalDialogFeatures(feature_args); + + const bool trusted = false; + + FloatRect screenRect = screenAvailableRect(frame->view()); + + WindowFeatures wargs; + // default here came from frame size of dialog in MacIE. + wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, + screenRect.width(), 620); + wargs.widthSet = true; + // default here came from frame size of dialog in MacIE. + wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, + screenRect.height(), 450); + wargs.heightSet = true; + + wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), + screenRect.right() - wargs.width, + -1); + wargs.xSet = wargs.x > 0; + wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), + screenRect.bottom() - wargs.height, + -1); + wargs.ySet = wargs.y > 0; + + if (WindowFeatures::boolFeature(features, "center", true)) { + if (!wargs.xSet) { + wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2; + wargs.xSet = true; + } + if (!wargs.ySet) { + wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2; + wargs.ySet = true; + } + } + + wargs.dialog = true; + wargs.resizable = WindowFeatures::boolFeature(features, "resizable"); + wargs.scrollbarsVisible = + WindowFeatures::boolFeature(features, "scroll", true); + wargs.statusBarVisible = + WindowFeatures::boolFeature(features, "status", !trusted); + wargs.menuBarVisible = false; + wargs.toolBarVisible = false; + wargs.locationBarVisible = false; + wargs.fullscreen = false; + + Frame* dialog_frame = createWindow(frame, url, "", wargs, dialog_args); + if (!dialog_frame) + return v8::Undefined(); + + // Hold on to the context of the dialog window long enough to retrieve the + // value of the return value property. + v8::Local<v8::Context> context = V8Proxy::GetContext(dialog_frame); + + // Run the dialog. + dialog_frame->page()->chrome()->runModal(); + + // Extract the return value property from the dialog window. + v8::Local<v8::Value> return_value; + if (!context.IsEmpty()) { + v8::Context::Scope scope(context); + return_value = context->Global()->Get(v8::String::New("returnValue")); + } + + if (!return_value.IsEmpty()) { + return return_value; + } + + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(DOMWindowOpen) { + INC_STATS(L"DOM.DOMWindow.open()"); + DOMWindow* parent = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + Frame* frame = parent->frame(); + if (!frame) + return v8::Undefined(); + + // Fast check This argument with the global object of security context. + if ((args.This() != v8::Context::GetCurrentSecurityContext()->Global()) && + !V8Proxy::IsFromSameOrigin(frame, true)) { + return v8::Undefined(); + } + + Frame* active_frame = V8Proxy::retrieveActiveFrame(); + if (!active_frame) + return v8::Undefined(); + + Page* page = frame->page(); + if (!page) + return v8::Undefined(); + + String url_string = valueToStringWithNullOrUndefinedCheck(args[0]); + AtomicString frame_name = (args[1]->IsUndefined() || args[1]->IsNull()) ? + "_blank" : AtomicString(ToWebCoreString(args[1])); + + // Because FrameTree::find() returns true for empty strings, we must check + // for empty framenames. Otherwise, illegitimate window.open() calls with + // no name will pass right through the popup blocker. + if (!allowPopUp() && + (frame_name.isEmpty() || + !frame->tree()->find(frame_name))) { + return v8::Undefined(); + } + + // Get the target frame for the special cases of _top and _parent. In those + // cases, we can schedule a location change right now and return early. + bool top_or_parent = false; + if (frame_name == "_top") { + frame = frame->tree()->top(); + top_or_parent = true; + } else if (frame_name == "_parent") { + if (Frame* parent = frame->tree()->parent()) { + frame = parent; + } + top_or_parent = true; + } + if (top_or_parent) { + if (!active_frame->loader()->shouldAllowNavigation(frame)) { + return v8::Undefined(); + } + + String completed_url; + if (!url_string.isEmpty()) { + completed_url = active_frame->document()->completeURL(url_string); + } + + if (!completed_url.isEmpty() && + (!parseURL(url_string).startsWith("javascript:", false) || + JSBridge::isSafeScript(frame))) { + bool user_gesture = active_frame->scriptBridge()->wasRunByUserGesture(); + frame->loader()->scheduleLocationChange( + completed_url, + active_frame->loader()->outgoingReferrer(), + false, + user_gesture); + } + return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, frame->domWindow()); + } + + // In the case of a named frame or a new window, we'll use the createWindow() + // helper. + WindowFeatures window_features( + valueToStringWithNullOrUndefinedCheck(args[2])); + FloatRect screen_rect = screenAvailableRect(page->mainFrame()->view()); + + // Set default size and location near parent window if none were specified. + // These may be further modified by adjustWindowRect, below. + if (!window_features.xSet) { + window_features.x = parent->screenX() - screen_rect.x() + kPopupTilePixels; + window_features.xSet = true; + } + if (!window_features.ySet) { + window_features.y = parent->screenY() - screen_rect.y() + kPopupTilePixels; + window_features.ySet = true; + } + if (!window_features.widthSet) { + window_features.width = parent->innerWidth(); + window_features.widthSet = true; + } + if (!window_features.heightSet) { + window_features.height = parent->innerHeight(); + window_features.heightSet = true; + } + + FloatRect window_rect(window_features.x, window_features.y, + window_features.width, window_features.height); + + // The new window's location is relative to its current screen, so shift + // it in case it's on a secondary monitor. See http://b/viewIssue?id=967905. + window_rect.move(screen_rect.x(), screen_rect.y()); + WebCore::DOMWindow::adjustWindowRect(screen_rect, window_rect, window_rect); + + // createWindow expects the location to be specified relative to the parent. + window_features.x = window_rect.x() - parent->screenX(); + window_features.y = window_rect.y() - parent->screenY(); + window_features.height = window_rect.height(); + window_features.width = window_rect.width(); + + frame = createWindow(frame, url_string, frame_name, + window_features, v8::Local<v8::Value>()); + + if (!frame) return v8::Undefined(); + + return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, frame->domWindow()); +} + + +INDEXED_PROPERTY_GETTER(DOMWindow) { + INC_STATS(L"DOM.DOMWindow.IndexedPropertyGetter"); + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper( + V8ClassIndex::DOMWINDOW, info.This()); + if (holder.IsEmpty()) + return v8::Handle<v8::Value>(); + + DOMWindow* window = V8Proxy::ToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, holder); + if (!window) + return v8::Handle<v8::Value>(); + + Frame* frame = window->frame(); + if (!frame) + return v8::Handle<v8::Value>(); + + Frame* child = frame->tree()->child(index); + if (child) { + return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, child->domWindow()); + } + + return v8::Handle<v8::Value>(); +} + + +NAMED_PROPERTY_GETTER(DOMWindow) { + INC_STATS(L"DOM.DOMWindow.NamedPropertyGetter"); + // The key must be a string. + if (!name->IsString()) + return v8::Handle<v8::Value>(); + + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper( + V8ClassIndex::DOMWINDOW, info.This()); + if (holder.IsEmpty()) + return v8::Handle<v8::Value>(); + + DOMWindow* window = V8Proxy::ToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, holder); + if (!window) + return v8::Handle<v8::Value>(); + + String prop_name = ToWebCoreString(name); + + Frame* frame = window->frame(); + // window is detached from a frame. + if (!frame) + return v8::Handle<v8::Value>(); + + // Search sub-frames. + Frame* child = frame->tree()->child(prop_name); + if (child) + return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, child->domWindow()); + + // Search IDL functions defined in the prototype + v8::Handle<v8::Value> result = + holder->GetRealNamedPropertyInPrototypeChain(name); + if (!result.IsEmpty()) + return result; + + // Search named items in the document. + Document* doc = frame->document(); + if (doc) { + RefPtr<HTMLCollection> items = doc->windowNamedItems(prop_name); + if (items->length() >= 1) { + if (items->length() == 1) { + return V8Proxy::ToV8Object(V8ClassIndex::NODE, items->firstItem()); + } else { + return V8Proxy::ToV8Object(V8ClassIndex::HTMLCOLLECTION, + static_cast<Peerable*>(items.get())); + } + } + } + + // Lazy initialization map keeps global properties that can be lazily + // initialized. The value is the code to instantiate the property. + // It must return the value of property after initialization. + static HashMap<String, String> kLazyInitMap; + if (kLazyInitMap.isEmpty()) { + kLazyInitMap.set("Image", + "function Image() { \ + return document.createElement('image'); \ + }; \ + Image"); + kLazyInitMap.set("Option", + "function Option(text, value, defaultSelected, selected) { \ + var option = document.createElement('option'); \ + if (text == null) return option; \ + option.text = text; \ + if (value == null) return option; \ + option.value = value; \ + if (defaultSelected == null) return option; \ + option.defaultSelected = defaultSelected; \ + if (selected == null) return option; \ + option.selected = selected; \ + return option; \ + }; \ + Option"); + } + + String code = kLazyInitMap.get(prop_name); + if (!code.isEmpty()) { + v8::Local<v8::Context> context = V8Proxy::GetContext(window->frame()); + // Bail out if we cannot get the context for the frame. + if (context.IsEmpty()) return v8::Handle<v8::Value>(); + + // switch to the target object's environment. + v8::Context::Scope scope(context); + + // Set the property name to undefined to make sure that the + // property exists. This is necessary because this getter + // might be called when evaluating 'var RangeException = value' + // to figure out if we have a property named 'RangeException' before + // we set RangeException to the new value. In that case, we will + // evaluate 'var RangeException = {}' and enter an infinite loop. + // Setting the property name to undefined on the global object + // ensures that we do not have to ask this getter to figure out + // that we have the property. + // + // TODO(ager): We probably should implement the Has method + // for the interceptor instead of using the default Has method + // that calls Get. + context->Global()->Set(v8String(prop_name), v8::Undefined()); + V8Proxy* proxy = V8Proxy::retrieve(window->frame()); + ASSERT(proxy); + + v8::Local<v8::Value> result = proxy->Evaluate(prop_name, 0, code, 0); + if (result.IsEmpty()) return result; + + return result; + } + + return v8::Handle<v8::Value>(); +} + + +NAMED_PROPERTY_DELETER(HTMLDocument) { + // Only handle document.all. Insert the marker object into the + // shadow internal field to signal that document.all is no longer + // shadowed. + String key = ToWebCoreString(name); + if (key == "all") { + ASSERT(info.Holder()->InternalFieldCount() == + kHTMLDocumentInternalFieldCount); + v8::Local<v8::Value> marker = + info.Holder()->GetInternalField(kHTMLDocumentMarkerIndex); + info.Holder()->SetInternalField(kHTMLDocumentShadowIndex, marker); + return v8::True(); + } + return v8::Handle<v8::Boolean>(); +} + + +NAMED_PROPERTY_SETTER(HTMLDocument) { + INC_STATS(L"DOM.HTMLDocument.NamedPropertySetter"); + // Only handle document.all. We insert the value into the shadow + // internal field from which the getter will retrieve it. + String key = ToWebCoreString(name); + if (key == "all") { + ASSERT(info.Holder()->InternalFieldCount() == + kHTMLDocumentInternalFieldCount); + info.Holder()->SetInternalField(kHTMLDocumentShadowIndex, value); + } + return v8::Handle<v8::Value>(); +} + + +NAMED_PROPERTY_GETTER(HTMLDocument) { + INC_STATS(L"DOM.HTMLDocument.NamedPropertyGetter"); + String key = ToWebCoreString(name); + + // Special case for document.all. If the value in the shadow + // internal field is not the marker object, then document.all has + // been temporarily shadowed and we return the value. + if (key == "all") { + ASSERT(info.Holder()->InternalFieldCount() == + kHTMLDocumentInternalFieldCount); + v8::Local<v8::Value> marker = + info.Holder()->GetInternalField(kHTMLDocumentMarkerIndex); + v8::Local<v8::Value> value = + info.Holder()->GetInternalField(kHTMLDocumentShadowIndex); + if (marker != value) { + return value; + } + } + + HTMLDocument* imp = V8Proxy::FastToNativeObject<HTMLDocument>( + V8ClassIndex::HTMLDOCUMENT, info.Holder()); + + // Fast case for named elements that are not there. + if (!imp->hasNamedItem(key) && !imp->hasDocExtraNamedItem(key)) { + return v8::Handle<v8::Value>(); + } + RefPtr<HTMLCollection> items = imp->documentNamedItems(key); + if (items->length() == 0) return v8::Handle<v8::Value>(); + if (items->length() == 1) { + Node* node = items->firstItem(); + Frame* frame = 0; + if (node->hasTagName(HTMLNames::iframeTag) && + (frame = static_cast<HTMLIFrameElement*>(node)->contentFrame())) { + return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, frame->domWindow()); + } + return V8Proxy::ToV8Object(V8ClassIndex::NODE, node); + } + return V8Proxy::ToV8Object(V8ClassIndex::HTMLCOLLECTION, + static_cast<Peerable*>(items.get())); +} + + +NAMED_PROPERTY_GETTER(HTMLFrameSetElement) { + INC_STATS(L"DOM.HTMLFrameSetElement.NamedPropertyGetter"); + HTMLFrameSetElement* imp = V8Proxy::FastToNativeObject<HTMLFrameSetElement>( + V8ClassIndex::HTMLFRAMESETELEMENT, info.Holder()); + String key = ToWebCoreString(name); + Node* frame = imp->children()->namedItem(key); + if (frame && frame->hasTagName(HTMLNames::frameTag)) { + Document* doc = static_cast<HTMLFrameElement*>(frame)->contentDocument(); + if (doc) { + Frame* content_frame = doc->frame(); + if (content_frame) { + return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, + content_frame->domWindow()); + } + } + return v8::Undefined(); + } + return v8::Handle<v8::Value>(); +} + + +NAMED_PROPERTY_GETTER(HTMLFormElement) { + INC_STATS(L"DOM.HTMLFormElement.NamedPropertyGetter"); + HTMLFormElement* imp = V8Proxy::FastToNativeObject<HTMLFormElement>( + V8ClassIndex::HTMLFORMELEMENT, info.Holder()); + String v = ToWebCoreString(name); + + // Call getNamedElements twice, first time check if it has a value + // and let HTMLFormElement update its cache. + // See issue: 867404 + { + Vector<RefPtr<Node> > elements; + imp->getNamedElements(v, elements); + if (elements.size() == 0) + return v8::Handle<v8::Value>(); + } + // Second call may return different results from the first call, + // but if the first the size cannot be zero. + Vector<RefPtr<Node>> elements; + imp->getNamedElements(v, elements); + ASSERT(elements.size() != 0); + if (elements.size() == 1) { + return V8Proxy::ToV8Object(V8ClassIndex::NODE, elements.at(0).get()); + } else { + NodeList* collection = new V8VectorNodeList(elements); + return V8Proxy::ToV8Object(V8ClassIndex::NODELIST, + static_cast<Peerable*>(collection)); + } +} + + +INDEXED_PROPERTY_GETTER(NamedNodeMap) { + INC_STATS(L"DOM.NamedNodeMap.IndexedPropertyGetter"); + NamedNodeMap* imp = V8Proxy::FastToNativeObject<NamedNodeMap>( + V8ClassIndex::NAMEDNODEMAP, info.Holder()); + RefPtr<Node> result = imp->item(index); + if (!result) return v8::Handle<v8::Value>(); + + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + +NAMED_PROPERTY_GETTER(NamedNodeMap) { + INC_STATS(L"DOM.NamedNodeMap.NamedPropertyGetter"); + // Search the prototype chain first. + v8::Handle<v8::Value> value = + info.Holder()->GetRealNamedPropertyInPrototypeChain(name); + if (!value.IsEmpty()) + return value; + + // Then look for IDL defined properties on the object itself. + if (info.Holder()->HasRealNamedCallbackProperty(name)) + return v8::Handle<v8::Value>(); + + // Finally, search the DOM. + NamedNodeMap* imp = V8Proxy::FastToNativeObject<NamedNodeMap>( + V8ClassIndex::NAMEDNODEMAP, info.Holder()); + String prop_name = ToWebCoreString(name); + RefPtr<Node> result = imp->getNamedItem(prop_name); + if (!result) return v8::Handle<v8::Value>(); + + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + + +NAMED_PROPERTY_GETTER(NodeList) { + INC_STATS(L"DOM.NodeList.NamedPropertyGetter"); + NodeList* list = V8Proxy::FastToNativeObject<NodeList>( + V8ClassIndex::NODELIST, info.Holder()); + String prop_name = ToWebCoreString(name); + + // Length property cannot be overridden. + if (prop_name == "length") + return v8::Number::New(list->length()); + + RefPtr<Node> result = list->itemWithName(prop_name); + if (result) + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); + + return v8::Handle<v8::Value>(); +} + + +INDEXED_PROPERTY_GETTER(HTMLFormElement) { + INC_STATS(L"DOM.HTMLFormElement.IndexedPropertyGetter"); + HTMLFormElement* form = V8Proxy::FastToNativeObject<HTMLFormElement>( + V8ClassIndex::HTMLFORMELEMENT, info.Holder()); + + RefPtr<Node> result = form->elements()->item(index); + if (!result) return v8::Handle<v8::Value>(); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + + +INDEXED_PROPERTY_GETTER(HTMLOptionsCollection) { + INC_STATS(L"DOM.HTMLOptionsCollection.IndexedPropertyGetter"); + HTMLOptionsCollection* collection = + V8Proxy::FastToNativeObject<HTMLOptionsCollection>( + V8ClassIndex::HTMLOPTIONSCOLLECTION, info.Holder()); + + RefPtr<Node> result = collection->item(index); + if (!result) return v8::Handle<v8::Value>(); + + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + +static v8::Handle<v8::Value> OptionsCollectionSetter(uint32_t index, + v8::Handle<v8::Value> value, HTMLSelectElement* base) { + if (value->IsNull() || value->IsUndefined()) { + base->remove(index); + return value; + } + + ExceptionCode ec = 0; + + // Check that the value is an HTMLOptionElement. If not, throw a + // TYPE_MISMATCH_ERR DOMException. + if (!V8HTMLOptionElement::HasInstance(value)) { + V8Proxy::SetDOMException(TYPE_MISMATCH_ERR); + return value; + } + + HTMLOptionElement* element = V8Proxy::FastToNativeObject<HTMLOptionElement>( + V8ClassIndex::HTMLOPTIONELEMENT, v8::Handle<v8::Object>::Cast(value)); + base->setOption(index, element, ec); + + V8Proxy::SetDOMException(ec); + return value; +} + + +INDEXED_PROPERTY_SETTER(HTMLOptionsCollection) { + INC_STATS(L"DOM.HTMLOptionsCollection.IndexedPropertySetter"); + HTMLOptionsCollection* collection = + V8Proxy::FastToNativeObject<HTMLOptionsCollection>( + V8ClassIndex::HTMLOPTIONSCOLLECTION, info.Holder()); + HTMLSelectElement* base = static_cast<HTMLSelectElement*>(collection->base()); + return OptionsCollectionSetter(index, value, base); +} + + +INDEXED_PROPERTY_SETTER(HTMLSelectElementCollection) { + INC_STATS(L"DOM.HTMLSelectElementCollection.IndexedPropertySetter"); + HTMLSelectElement* select = + V8Proxy::FastToNativeObject<HTMLSelectElement>( + V8ClassIndex::HTMLSELECTELEMENT, info.Holder()); + return OptionsCollectionSetter(index, value, select); +} + + +// When getting properties on CSSStyleDeclarations, the name used from +// Javascript and the actual name of the property are not the same, so +// we have to do the following translation. The translation turns upper +// case characters into lower case characters and inserts dashes to +// separate words. +// +// Example: 'backgroundPositionY' -> 'background-position-y' +// +// Also, certain prefixes such as 'pos', 'css-' and 'pixel-' are stripped +// and the pixel_or_pos_prefix out parameter is used to indicate whether or +// not the property name was prefixed with 'pos-' or 'pixel-'. +static String CSSPropertyName(const String &p, bool *pixel_or_pos_prefix = 0) { + DeprecatedString prop = p.deprecatedString(); + + int i = prop.length(); + while (--i > 0) { + ::UChar c = prop[i].unicode(); + if (c >= 'A' && c <= 'Z') + prop.insert(i, '-'); + } + + prop = prop.lower(); + + if (pixel_or_pos_prefix) + *pixel_or_pos_prefix = false; + + if (prop.startsWith("css-")) { + prop = prop.mid(4); + } else if (prop.startsWith("pixel-")) { + prop = prop.mid(6); + if (pixel_or_pos_prefix) + *pixel_or_pos_prefix = true; + } else if (prop.startsWith("pos-")) { + prop = prop.mid(4); + if (pixel_or_pos_prefix) + *pixel_or_pos_prefix = true; + } else if (prop.startsWith("khtml-") || + prop.startsWith("apple-") || + prop.startsWith("webkit-")) { + prop.insert(0, '-'); + } + + return prop; +} + + +NAMED_PROPERTY_GETTER(CSSStyleDeclaration) { + INC_STATS(L"DOM.CSSStyleDeclaration.NamedPropertyGetter"); + // First look for API defined attributes on the style declaration + // object. + if (info.Holder()->HasRealNamedCallbackProperty(name)) { + return v8::Handle<v8::Value>(); + } + + // Search the style declaration. + CSSStyleDeclaration* imp = V8Proxy::FastToNativeObject<CSSStyleDeclaration>( + V8ClassIndex::CSSSTYLEDECLARATION, info.Holder()); + + bool pixel_or_pos; + String p = ToWebCoreString(name); + String prop = CSSPropertyName(p, &pixel_or_pos); + + // Do not handle non-property names. + if (!CSSStyleDeclaration::isPropertyName(prop)) { + return v8::Handle<v8::Value>(); + } + + RefPtr<CSSValue> v = imp->getPropertyCSSValue(prop); + if (v) { + if (pixel_or_pos && v->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) { + RefPtr<CSSPrimitiveValue> primitive_value = + static_pointer_cast<CSSPrimitiveValue>(v); + return v8::Number::New( + primitive_value->getFloatValue(CSSPrimitiveValue::CSS_PX)); + } + return v8StringOrNull(v->cssText()); + } + + String result = imp->getPropertyValue(prop); + if (result.isNull()) + result = ""; // convert null to empty string. + + // The 'filter' attribute is made undetectable in KJS/WebKit + // to avoid confusion with IE's filter extension. + if (prop == "filter") { + return v8UndetectableString(result); + } + return v8String(result); +} + + +NAMED_PROPERTY_SETTER(CSSStyleDeclaration) { + INC_STATS(L"DOM.CSSStyleDeclaration.NamedPropertySetter"); + CSSStyleDeclaration* imp = V8Proxy::FastToNativeObject<CSSStyleDeclaration>( + V8ClassIndex::CSSSTYLEDECLARATION, info.Holder()); + String property_name = ToWebCoreString(name); + int ec = 0; + + bool pixel_or_pos; + String prop = CSSPropertyName(property_name, &pixel_or_pos); + if (!CSSStyleDeclaration::isPropertyName(prop)) { + return v8::Handle<v8::Value>(); // do not block the call + } + + String prop_value = valueToStringWithNullCheck(value); + if (pixel_or_pos) prop_value += "px"; + imp->setProperty(prop, prop_value, ec); + + V8Proxy::SetDOMException(ec); + return value; +} + + +NAMED_PROPERTY_GETTER(HTMLPlugInElement) { + INC_STATS(L"DOM.HTMLPlugInElement.NamedPropertyGetter"); + HTMLPlugInElement* imp = V8Proxy::FastToNativeObject<HTMLPlugInElement>( + V8ClassIndex::NODE, info.Holder()); + v8::Local<v8::Object> instance = + v8::Local<v8::Object>::New(imp->getInstance()); + if (instance.IsEmpty()) return v8::Handle<v8::Object>(); + return NPObjectGetNamedProperty(instance, name); +} + + +NAMED_PROPERTY_SETTER(HTMLPlugInElement) { + INC_STATS(L"DOM.HTMLPlugInElement.NamedPropertySetter"); + HTMLPlugInElement* imp = V8Proxy::FastToNativeObject<HTMLPlugInElement>( + V8ClassIndex::NODE, info.Holder()); + v8::Local<v8::Object> instance = + v8::Local<v8::Object>::New(imp->getInstance()); + if (instance.IsEmpty()) { + return v8::Handle<v8::Value>(); // do not block the call + } + + return NPObjectSetNamedProperty(instance, name, value); +} + + +CALLBACK_FUNC_DECL(HTMLPlugInElement) { + INC_STATS(L"DOM.HTMLPluginElement()"); + return NPObjectInvokeDefaultHandler(args); +} + + +INDEXED_PROPERTY_GETTER(HTMLPlugInElement) { + INC_STATS(L"DOM.HTMLPlugInElement.IndexedPropertyGetter"); + HTMLPlugInElement* imp = V8Proxy::FastToNativeObject<HTMLPlugInElement>( + V8ClassIndex::NODE, info.Holder()); + v8::Local<v8::Object> instance = + v8::Local<v8::Object>::New(imp->getInstance()); + if (instance.IsEmpty()) return v8::Handle<v8::Object>(); + return NPObjectGetIndexedProperty(instance, index); +} + + +INDEXED_PROPERTY_SETTER(HTMLPlugInElement) { + INC_STATS(L"DOM.HTMLPlugInElement.IndexedPropertySetter"); + HTMLPlugInElement* imp = V8Proxy::FastToNativeObject<HTMLPlugInElement>( + V8ClassIndex::NODE, info.Holder()); + v8::Local<v8::Object> instance = + v8::Local<v8::Object>::New(imp->getInstance()); + if (instance.IsEmpty()) { + return v8::Handle<v8::Value>(); // do not block the call + } + + return NPObjectSetIndexedProperty(instance, index, value); +} + +NAMED_PROPERTY_GETTER(StyleSheetList) { + INC_STATS(L"DOM.StyleSheetList.NamedPropertyGetter"); + // Look for local properties first. + if (info.Holder()->HasRealNamedProperty(name)) { + return v8::Handle<v8::Value>(); + } + + // Search style sheet. + StyleSheetList* imp = V8Proxy::FastToNativeObject<StyleSheetList>( + V8ClassIndex::STYLESHEETLIST, info.Holder()); + String key = ToWebCoreString(name); + HTMLStyleElement* item = imp->getNamedItem(key); + if (item) { + return V8Proxy::ToV8Object(V8ClassIndex::HTMLSTYLEELEMENT, item); + } + return v8::Handle<v8::Value>(); +} + + +CALLBACK_FUNC_DECL(CSSPrimitiveValueGetRGBColorValue) { + INC_STATS(L"DOM.CSSPrimitiveValue.getRGBColorValue()"); + CSSPrimitiveValue* value = V8Proxy::FastToNativeObject<CSSPrimitiveValue>( + V8ClassIndex::CSSPRIMITIVEVALUE, args.Holder()); + ExceptionCode ec = 0; + unsigned int rgbcolor = value->getRGBColorValue(ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return V8Proxy::ToV8Object(V8ClassIndex::RGBCOLOR, + static_cast<Peerable*>(new RGBColor(rgbcolor))); +} + +// Helper macro for converting v8 values into floats (expected by many of the +// canvas functions). +#define TO_FLOAT(a) static_cast<float>((a)->NumberValue()) + +// TODO: SetStrokeColor and SetFillColor are similar except function names, +// consolidate them into one. +CALLBACK_FUNC_DECL(CanvasRenderingContext2DSetStrokeColor) { + INC_STATS(L"DOM.CanvasRenderingContext2D.setStrokeColor()"); + CanvasRenderingContext2D* context = + V8Proxy::FastToNativeObject<CanvasRenderingContext2D>( + V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder()); + switch (args.Length()) { + case 1: + if (args[0]->IsString()) { + context->setStrokeColor(ToWebCoreString(args[0])); + } else { + context->setStrokeColor(TO_FLOAT(args[0])); + } + break; + case 2: + if (args[0]->IsString()) { + context->setStrokeColor(ToWebCoreString(args[0]), + TO_FLOAT(args[1])); + } else { + context->setStrokeColor(TO_FLOAT(args[0]), + TO_FLOAT(args[1])); + } + break; + case 4: + context->setStrokeColor(TO_FLOAT(args[0]), + TO_FLOAT(args[1]), + TO_FLOAT(args[2]), + TO_FLOAT(args[3])); + break; + case 5: + context->setStrokeColor(TO_FLOAT(args[0]), + TO_FLOAT(args[1]), + TO_FLOAT(args[2]), + TO_FLOAT(args[3]), + TO_FLOAT(args[4])); + break; + default: + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, + "setStrokeColor: Invalid number of arguments"); + break; + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(CanvasRenderingContext2DSetFillColor) { + INC_STATS(L"DOM.CanvasRenderingContext2D.steFillColor()"); + CanvasRenderingContext2D* context = + V8Proxy::FastToNativeObject<CanvasRenderingContext2D>( + V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder()); + switch (args.Length()) { + case 1: + if (args[0]->IsString()) { + context->setFillColor(ToWebCoreString(args[0])); + } else { + context->setFillColor(TO_FLOAT(args[0])); + } + break; + case 2: + if (args[0]->IsString()) { + context->setFillColor(ToWebCoreString(args[0]), TO_FLOAT(args[1])); + } else { + context->setFillColor(TO_FLOAT(args[0]), TO_FLOAT(args[1])); + } + break; + case 4: + context->setFillColor(TO_FLOAT(args[0]), + TO_FLOAT(args[1]), + TO_FLOAT(args[2]), + TO_FLOAT(args[3])); + break; + case 5: + context->setFillColor(TO_FLOAT(args[0]), + TO_FLOAT(args[1]), + TO_FLOAT(args[2]), + TO_FLOAT(args[3]), + TO_FLOAT(args[4])); + break; + default: + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, + "setFillColor: Invalid number of arguments"); + break; + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(CanvasRenderingContext2DStrokeRect) { + INC_STATS(L"DOM.CanvasRenderingContext2D.strokeRect()"); + CanvasRenderingContext2D* context = + V8Proxy::FastToNativeObject<CanvasRenderingContext2D>( + V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder()); + ExceptionCode ec = 0; + double x = 0, y = 0, width = 0, height = 0, line_width = 0; + if (args.Length() == 5) { + context->strokeRect(TO_FLOAT(args[0]), + TO_FLOAT(args[1]), + TO_FLOAT(args[2]), + TO_FLOAT(args[3]), + TO_FLOAT(args[4]), + ec); + } else if (args.Length() == 4) { + context->strokeRect(TO_FLOAT(args[0]), + TO_FLOAT(args[1]), + TO_FLOAT(args[2]), + TO_FLOAT(args[3]), + ec); + } else { + // Should throw index error + ec = INDEX_SIZE_ERR; + } + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(CanvasRenderingContext2DSetShadow) { + INC_STATS(L"DOM.CanvasRenderingContext2D.setShadow()"); + CanvasRenderingContext2D* context = + V8Proxy::FastToNativeObject<CanvasRenderingContext2D>( + V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder()); + + switch (args.Length()) { + case 3: + context->setShadow(TO_FLOAT(args[0]), TO_FLOAT(args[1]), + TO_FLOAT(args[2])); + break; + case 4: + if (args[3]->IsString()) + context->setShadow(TO_FLOAT(args[0]), TO_FLOAT(args[1]), + TO_FLOAT(args[2]), ToWebCoreString(args[3])); + else + context->setShadow(TO_FLOAT(args[0]), TO_FLOAT(args[1]), + TO_FLOAT(args[2]), TO_FLOAT(args[3])); + break; + case 5: + if (args[3]->IsString()) + context->setShadow(TO_FLOAT(args[0]), TO_FLOAT(args[1]), + TO_FLOAT(args[2]), ToWebCoreString(args[3]), + TO_FLOAT(args[4])); + else + context->setShadow(TO_FLOAT(args[0]), TO_FLOAT(args[1]), + TO_FLOAT(args[2]), TO_FLOAT(args[3]), + TO_FLOAT(args[4])); + break; + case 7: + context->setShadow(TO_FLOAT(args[0]), TO_FLOAT(args[1]), + TO_FLOAT(args[2]), TO_FLOAT(args[3]), + TO_FLOAT(args[4]), TO_FLOAT(args[5]), + TO_FLOAT(args[6])); + break; + case 8: + context->setShadow(TO_FLOAT(args[0]), TO_FLOAT(args[1]), + TO_FLOAT(args[2]), TO_FLOAT(args[3]), + TO_FLOAT(args[4]), TO_FLOAT(args[5]), + TO_FLOAT(args[6]), TO_FLOAT(args[7])); + break; + default: + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, + "setShadow: Invalid number of arguments"); + break; + } + + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(CanvasRenderingContext2DDrawImage) { + INC_STATS(L"DOM.CanvasRenderingContext2D.drawImage()"); + CanvasRenderingContext2D* context = + V8Proxy::FastToNativeObject<CanvasRenderingContext2D>( + V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder()); + + v8::Handle<v8::Value> arg = args[0]; + + if (V8HTMLImageElement::HasInstance(arg)) { + ExceptionCode ec = 0; + HTMLImageElement* image_element = + V8Proxy::FastToNativeObject<HTMLImageElement>( + V8ClassIndex::HTMLIMAGEELEMENT, arg); + switch (args.Length()) { + case 3: + context->drawImage(image_element, TO_FLOAT(args[1]), TO_FLOAT(args[2])); + break; + case 5: + context->drawImage(image_element, TO_FLOAT(args[1]), TO_FLOAT(args[2]), + TO_FLOAT(args[3]), TO_FLOAT(args[4]), ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + break; + case 9: + context->drawImage(image_element, + FloatRect(TO_FLOAT(args[1]), TO_FLOAT(args[2]), + TO_FLOAT(args[3]), TO_FLOAT(args[4])), + FloatRect(TO_FLOAT(args[5]), TO_FLOAT(args[6]), + TO_FLOAT(args[7]), TO_FLOAT(args[8])), + ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + break; + default: + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, + "drawImage: Invalid number of arguments"); + return v8::Undefined(); + } + return v8::Undefined(); + } + + // HTMLCanvasElement + if (V8HTMLCanvasElement::HasInstance(arg)) { + ExceptionCode ec = 0; + HTMLCanvasElement* canvas_element = + V8Proxy::FastToNativeObject<HTMLCanvasElement>( + V8ClassIndex::HTMLCANVASELEMENT, arg); + switch (args.Length()) { + case 3: + context->drawImage(canvas_element, TO_FLOAT(args[1]), TO_FLOAT(args[2])); + break; + case 5: + context->drawImage(canvas_element, TO_FLOAT(args[1]), TO_FLOAT(args[2]), + TO_FLOAT(args[3]), TO_FLOAT(args[4]), ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + break; + case 9: + context->drawImage(canvas_element, + FloatRect(TO_FLOAT(args[1]), TO_FLOAT(args[2]), + TO_FLOAT(args[3]), TO_FLOAT(args[4])), + FloatRect(TO_FLOAT(args[5]), TO_FLOAT(args[6]), + TO_FLOAT(args[7]), TO_FLOAT(args[8])), + ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + break; + default: + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, + "drawImage: Invalid number of arguments"); + return v8::Undefined(); + } + return v8::Undefined(); + } + + V8Proxy::SetDOMException(TYPE_MISMATCH_ERR); + return v8::Handle<v8::Value>(); +} + + +CALLBACK_FUNC_DECL(CanvasRenderingContext2DDrawImageFromRect) { + INC_STATS(L"DOM.CanvasRenderingContext2D.drawImageFromRect()"); + CanvasRenderingContext2D* context = + V8Proxy::FastToNativeObject<CanvasRenderingContext2D>( + V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder()); + + v8::Handle<v8::Value> arg = args[0]; + + if (!V8Proxy::MaybeDOMWrapper(arg)) { + V8Proxy::ThrowError(V8Proxy::TYPE_ERROR, + "drawImageFromRect: Invalid type of arguments"); + return v8::Undefined(); + } + + if (V8HTMLImageElement::HasInstance(arg)) { + HTMLImageElement* image_element = + V8Proxy::FastToNativeObject<HTMLImageElement>( + V8ClassIndex::HTMLIMAGEELEMENT, arg); + context->drawImageFromRect(image_element, + TO_FLOAT(args[1]), TO_FLOAT(args[2]), + TO_FLOAT(args[3]), TO_FLOAT(args[4]), + TO_FLOAT(args[5]), TO_FLOAT(args[6]), + TO_FLOAT(args[7]), TO_FLOAT(args[8]), + ToWebCoreString(args[9])); + } else { + V8Proxy::ThrowError(V8Proxy::TYPE_ERROR, + "drawImageFromRect: Invalid type of arguments"); + } + + return v8::Undefined(); +} + +// Remove the macro since we don't need it anymore. +#undef TO_FLOAT + +CALLBACK_FUNC_DECL(CanvasRenderingContext2DCreatePattern) { + INC_STATS(L"DOM.CanvasRenderingContext2D.createPattern()"); + CanvasRenderingContext2D* context = + V8Proxy::FastToNativeObject<CanvasRenderingContext2D>( + V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder()); + + v8::Handle<v8::Value> arg = args[0]; + + if (V8HTMLImageElement::HasInstance(arg)) { + HTMLImageElement* image_element = + V8Proxy::FastToNativeObject<HTMLImageElement>( + V8ClassIndex::HTMLIMAGEELEMENT, arg); + ExceptionCode ec = 0; + RefPtr<CanvasPattern> pattern = + context->createPattern(image_element, ToWebCoreString(args[1]), ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return V8Proxy::ToV8Object(V8ClassIndex::CANVASPATTERN, + static_cast<Peerable*>(pattern.get())); + } + + if (V8HTMLCanvasElement::HasInstance(arg)) { + HTMLCanvasElement* canvas_element = + V8Proxy::FastToNativeObject<HTMLCanvasElement>( + V8ClassIndex::HTMLCANVASELEMENT, arg); + ExceptionCode ec = 0; + RefPtr<CanvasPattern> pattern = + context->createPattern(canvas_element, ToWebCoreString(args[1]), ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return V8Proxy::ToV8Object(V8ClassIndex::CANVASPATTERN, + static_cast<Peerable*>(pattern.get())); + } + + V8Proxy::SetDOMException(TYPE_MISMATCH_ERR); + return v8::Handle<v8::Value>(); +} + + +CALLBACK_FUNC_DECL(ClipboardClearData) { + INC_STATS(L"DOM.Clipboard.clearData()"); + Clipboard* imp = V8Proxy::FastToNativeObject<Clipboard>( + V8ClassIndex::CLIPBOARD, args.Holder()); + if (args.Length() == 0) { + imp->clearAllData(); + } else { + String v = ToWebCoreString(args[0]); + imp->clearData(v); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(ClipboardGetData) { + INC_STATS(L"DOM.Clipboard.getData()"); + Clipboard* imp = V8Proxy::FastToNativeObject<Clipboard>( + V8ClassIndex::CLIPBOARD, args.Holder()); + if (args.Length() == 1) { + bool success; + String v = ToWebCoreString(args[0]); + String result = imp->getData(v, success); + if (success) return v8String(result); + return v8::Undefined(); + } + + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, + "getData: Invalid number of arguments"); + + return v8::Undefined(); +} + +CALLBACK_FUNC_DECL(ClipboardSetData) { + INC_STATS(L"DOM.Clipboard.setData()"); + Clipboard* imp = V8Proxy::FastToNativeObject<Clipboard>( + V8ClassIndex::CLIPBOARD, args.Holder()); + if (args.Length() == 2) { + String type = ToWebCoreString(args[0]); + String data = ToWebCoreString(args[1]); + bool result = imp->setData(type, data); + return result?v8::True():v8::False(); + } + + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, + "setData: Invalid number of arguments"); + + return v8::Undefined(); +} + + +static bool AllowSettingSrcToJavascriptURL(Element* element, String name, + String value) { + // Need to parse value as URL first in order to check its protocol. + // " javascript:", "java\0script:", "javascript\t:", "javascript\1:" + // are all parsed as "javascript:" url. + // When changing location in HTMLFrameElement, value is parsed as url. + // We must match the behavior there. + // See issue 804099. + // + // parseURL is defined in CSSHelper.cpp. + if ((element->hasTagName(HTMLNames::iframeTag) || + element->hasTagName(HTMLNames::frameTag)) && + equalIgnoringCase(name, "src") && + parseURL(value).startsWith("javascript:", false)) { + HTMLFrameElementBase* frame = static_cast<HTMLFrameElementBase*>(element); + Node* contentDoc = frame->contentDocument(); + if (contentDoc && !V8Proxy::CheckNodeSecurity(contentDoc)) + return false; + } + return true; +} + + +static bool AllowSettingFrameSrcToJavascriptUrl(HTMLFrameElementBase* frame, + String value) { + // See same issue in AllowSettingSrcToJavascriptURL. + if (parseURL(value).startsWith("javascript:", false)) { + Node* contentDoc = frame->contentDocument(); + if (contentDoc && !V8Proxy::CheckNodeSecurity(contentDoc)) + return false; + } + return true; +} + + +CALLBACK_FUNC_DECL(ElementSetAttribute) { + INC_STATS(L"DOM.Element.setAttribute()"); + Element* imp = V8Proxy::FastToNativeObject<Element>( + V8ClassIndex::ELEMENT, args.Holder()); + ExceptionCode ec = 0; + String name = ToWebCoreString(args[0]); + String value = ToWebCoreString(args[1]); + + if (!AllowSettingSrcToJavascriptURL(imp, name, value)) { + return v8::Undefined(); + } + + imp->setAttribute(name, value, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(ElementSetAttributeNode) { + INC_STATS(L"DOM.Element.setAttributeNode()"); + if (!V8Attr::HasInstance(args[0])) { + V8Proxy::SetDOMException(TYPE_MISMATCH_ERR); + return v8::Handle<v8::Value>(); + } + + Attr* newAttr = + V8Proxy::FastToNativeObject<Attr>(V8ClassIndex::ATTR, args[0]); + + ExceptionCode ec = 0; + Element* imp = V8Proxy::FastToNativeObject<Element>( + V8ClassIndex::ELEMENT, args.Holder()); + + if (!AllowSettingSrcToJavascriptURL(imp, newAttr->name(), newAttr->value())) { + return v8::Undefined(); + } + + RefPtr<Attr> result = imp->setAttributeNode(newAttr, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + + +CALLBACK_FUNC_DECL(ElementSetAttributeNS) { + INC_STATS(L"DOM.Element.setAttributeNS()"); + Element* imp = V8Proxy::FastToNativeObject<Element>( + V8ClassIndex::ELEMENT, args.Holder()); + ExceptionCode ec = 0; + String namespaceURI = valueToStringWithNullCheck(args[0]); + String qualifiedName = ToWebCoreString(args[1]); + String value = ToWebCoreString(args[2]); + + if (!AllowSettingSrcToJavascriptURL(imp, qualifiedName, value)) { + return v8::Undefined(); + } + + imp->setAttributeNS(namespaceURI, qualifiedName, value, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(ElementSetAttributeNodeNS) { + INC_STATS(L"DOM.Element.setAttributeNodeNS()"); + if (!V8Attr::HasInstance(args[0])) { + V8Proxy::SetDOMException(TYPE_MISMATCH_ERR); + return v8::Handle<v8::Value>(); + } + + Attr* newAttr = + V8Proxy::FastToNativeObject<Attr>(V8ClassIndex::ATTR, args[0]); + + Element* imp = V8Proxy::FastToNativeObject<Element>( + V8ClassIndex::ELEMENT, args.Holder()); + ExceptionCode ec = 0; + + if (!AllowSettingSrcToJavascriptURL(imp, newAttr->name(), newAttr->value())) { + return v8::Undefined(); + } + + RefPtr<Attr> result = imp->setAttributeNodeNS(newAttr, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return V8Proxy::ToV8Object(V8ClassIndex::NODE, result.get()); +} + + +ACCESSOR_SETTER(AttrValue) { + Attr* imp = + V8Proxy::FastToNativeObject<Attr>(V8ClassIndex::ATTR, info.Holder()); + String v = valueToStringWithNullCheck(value); + Element* ownerElement = imp->ownerElement(); + + if (ownerElement && + !AllowSettingSrcToJavascriptURL(ownerElement, imp->name(), v)) + return; + + ExceptionCode ec = 0; + imp->setValue(v, ec); + V8Proxy::SetDOMException(ec); +} + + +ACCESSOR_SETTER(HTMLFrameElementSrc) { + HTMLFrameElement* imp = V8Proxy::FastToNativeObject<HTMLFrameElement>( + V8ClassIndex::HTMLFRAMEELEMENT, info.Holder()); + String v = valueToStringWithNullCheck(value); + + if (!AllowSettingFrameSrcToJavascriptUrl(imp, v)) return; + + imp->setSrc(v); +} + + +ACCESSOR_SETTER(HTMLFrameElementLocation) { + HTMLFrameElement* imp = V8Proxy::FastToNativeObject<HTMLFrameElement>( + V8ClassIndex::HTMLFRAMEELEMENT, info.Holder()); + String v = valueToStringWithNullCheck(value); + + if (!AllowSettingFrameSrcToJavascriptUrl(imp, v)) return; + + imp->setLocation(v); +} + + +ACCESSOR_SETTER(HTMLIFrameElementSrc) { + HTMLIFrameElement* imp = V8Proxy::FastToNativeObject<HTMLIFrameElement>( + V8ClassIndex::HTMLIFRAMEELEMENT, info.Holder()); + String v = valueToStringWithNullCheck(value); + + if (!AllowSettingFrameSrcToJavascriptUrl(imp, v)) return; + + imp->setSrc(v); +} + + +v8::Handle<v8::Value> V8Custom::WindowSetTimeoutImpl(const v8::Arguments& args, + bool single_shot) { + int num_arguments = args.Length(); + + if (num_arguments < 1) return v8::Undefined(); + + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + + // Fast check This argument with the global object of security context. + if ((args.This() != v8::Context::GetCurrentSecurityContext()->Global()) && + !V8Proxy::IsFromSameOrigin(imp->frame(), true)) { + return v8::Undefined(); + } + + v8::Handle<v8::Value> function = args[0]; + + int32_t timeout = 0; + if (num_arguments >= 2) timeout = args[1]->Int32Value(); + + int id; + if (function->IsString()) { + // Don't allow setting timeouts to run empty functions! + // (Bug 1009597) + WebCore::String string_function = ToWebCoreString(function); + if (string_function.length() == 0) + return v8::Undefined(); + + id = imp->installTimeout(new V8ScheduledAction(string_function), + timeout, single_shot); + } else if (function->IsFunction()) { + int param_count = num_arguments >= 2 ? num_arguments - 2 : 0; + v8::Local<v8::Value>* params = 0; + if (param_count > 0) { + params = new v8::Local<v8::Value>[param_count]; + for (int i = 0; i < param_count; i++) + // parameters must be globalized + params[i] = args[i+2]; + } + + // params is passed to action, and released in action's destructor + ScheduledAction* action = new V8ScheduledAction( + v8::Handle<v8::Function>::Cast(function), param_count, params); + + delete[] params; + + id = imp->installTimeout(action, timeout, single_shot); + } else { + // TODO(fqian): what's the right return value if failed. + return v8::Undefined(); + } + return v8::Integer::New(id); +} + + +CALLBACK_FUNC_DECL(DOMWindowSetTimeout) { + INC_STATS(L"DOM.DOMWindow.setTimeout()"); + return WindowSetTimeoutImpl(args, true); +} + + +CALLBACK_FUNC_DECL(DOMWindowSetInterval) { + INC_STATS(L"DOM.DOMWindow.setInterval()"); + return WindowSetTimeoutImpl(args, false); +} + +// Concatenates "args" to a string. If args is empty, returns empty string. +// Firefox/Safari/IE support non-standard arguments to document.write, ex: +// document.write("a", "b", "c") --> document.write("abc") +// document.write() --> document.write("") +static String WriteHelper_GetString(const v8::Arguments& args) { + String str = ""; + for (int i = 0; i < args.Length(); ++i) { + str += ToWebCoreString(args[i]); + } + return str; +} + + +CALLBACK_FUNC_DECL(HTMLDocumentWrite) { + INC_STATS(L"DOM.HTMLDocument.write()"); + HTMLDocument* imp = V8Proxy::FastToNativeObject<HTMLDocument>( + V8ClassIndex::HTMLDOCUMENT, args.Holder()); + imp->write(WriteHelper_GetString(args)); + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(HTMLDocumentWriteln) { + INC_STATS(L"DOM.HTMLDocument.writeln()"); + HTMLDocument* imp = V8Proxy::FastToNativeObject<HTMLDocument>( + V8ClassIndex::HTMLDOCUMENT, args.Holder()); + imp->writeln(WriteHelper_GetString(args)); + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(HTMLDocumentOpen) { + INC_STATS(L"DOM.HTMLDocument.open()"); + HTMLDocument* imp = V8Proxy::FastToNativeObject<HTMLDocument>( + V8ClassIndex::HTMLDOCUMENT, args.Holder()); + + if (args.Length() > 2) { + if (Frame* frame = imp->frame()) { + // Fetch the global object for the frame. + v8::Local<v8::Context> context = V8Proxy::GetContext(frame); + // Bail out if we cannot get the context. + if (context.IsEmpty()) return v8::Undefined(); + v8::Local<v8::Object> global = context->Global(); + // Get the open property of the global object. + v8::Local<v8::Value> function = global->Get(v8::String::New("open")); + // If the open property is not a function throw a type error. + if (!function->IsFunction()) { + V8Proxy::ThrowError(V8Proxy::TYPE_ERROR, "open is not a function"); + return v8::Undefined(); + } + // Wrap up the arguments and call the function. + v8::Local<v8::Value>* params = new v8::Local<v8::Value>[args.Length()]; + for (int i = 0; i < args.Length(); i++) { + params[i] = args[i]; + } + + V8Proxy* proxy = V8Proxy::retrieve(frame); + ASSERT(proxy); + + v8::Local<v8::Value> result = + proxy->CallFunction(v8::Local<v8::Function>::Cast(function), + global, args.Length(), params); + delete[] params; + return result; + } + } + + imp->open(); + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(HTMLDocumentClear) { + INC_STATS(L"DOM.HTMLDocument.clear()"); + // Do nothing (unimplemented) + + // KJS's bindings do do the same thing in JSHTMLDocument::clear(). + // Invoking HTMLDocument::clear() can cause a crash, since it + // deletes the document's tokenizer while it may still be in use. + // See b:1055485 for details. + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(DocumentEvaluate) { + INC_STATS(L"DOM.Document.evaluate()"); + + Document* imp = V8Proxy::FastToNativeObject<Document>( + V8ClassIndex::DOCUMENT, args.Holder()); + ExceptionCode ec = 0; + String expression = ToWebCoreString(args[0]); + Node* contextNode = NULL; + if (V8Node::HasInstance(args[1])) { + contextNode = + V8Proxy::FastToNativeObject<Node>(V8ClassIndex::NODE, args[1]); + } + // Find the XPath + XPathNSResolver* resolver = NULL; + if (V8XPathNSResolver::HasInstance(args[2])) { + resolver = V8Proxy::FastToNativeObject<XPathNSResolver>( + V8ClassIndex::XPATHNSRESOLVER, args[2]); + } else if (args[2]->IsObject()) { + v8::Handle<v8::Object> obj = args[2]->ToObject(); + resolver = new JSXPathNSResolver(obj); + } else if (!args[2]->IsNull() && !args[2]->IsUndefined()) { + V8Proxy::SetDOMException(TYPE_MISMATCH_ERR); + return v8::Handle<v8::Value>(); + } + int type = ToInt32(args[3]); + XPathResult* inResult = NULL; + if (V8XPathResult::HasInstance(args[4])) { + inResult = V8Proxy::FastToNativeObject<XPathResult>( + V8ClassIndex::XPATHRESULT, args[4]); + } + RefPtr<XPathResult> result = + imp->evaluate(expression, contextNode, resolver, type, inResult, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return V8Proxy::ToV8Object(V8ClassIndex::XPATHRESULT, + static_cast<Peerable*>(result.get())); +} + + +static bool IsAscii(const String& str) { + for (size_t i = 0; i < str.length(); i++) { + if (str[i] > 0xFF) + return false; + } + return true; +} + + +static v8::Handle<v8::Value> Base64Convert(const String& str, bool encode) { + if (!IsAscii(str)) { + V8Proxy::SetDOMException(INVALID_CHARACTER_ERR); + return v8::Handle<v8::Value>(); + } + + Vector<char> in(str.length()); + for (size_t i = 0; i < str.length(); i++) { + in[i] = static_cast<char>(str[i]); + } + Vector<char> out; + + if (encode) { + base64Encode(in, out); + } else { + if (!base64Decode(in, out)) { + V8Proxy::ThrowError(V8Proxy::GENERAL_ERROR, "Cannot decode base64"); + return v8::Undefined(); + } + } + + return v8String(String(out.data(), out.size())); +} + + +CALLBACK_FUNC_DECL(DOMWindowAtob) { + INC_STATS(L"DOM.DOMWindow.atob()"); + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + + // Fast check This argument with the global object of security context. + if ((args.This() != v8::Context::GetCurrentSecurityContext()->Global()) && + !V8Proxy::IsFromSameOrigin(imp->frame(), true)) { + return v8::Undefined(); + } + + if (args.Length() < 1) { + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, "Not enough arguments"); + return v8::Undefined(); + } + + if (args[0]->IsNull()) return v8String(""); + + String str = ToWebCoreString(args[0]); + return Base64Convert(str, false); +} + + +CALLBACK_FUNC_DECL(DOMWindowBtoa) { + INC_STATS(L"DOM.DOMWindow.btoa()"); + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, args.Holder()); + + // Fast check This argument with the global object of security context. + if ((args.This() != v8::Context::GetCurrentSecurityContext()->Global()) && + !V8Proxy::IsFromSameOrigin(imp->frame(), true)) { + return v8::Undefined(); + } + + if (args.Length() < 1) { + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, "Not enough arguments"); + return v8::Undefined(); + } + + if (args[0]->IsNull()) return v8String(""); + + String str = ToWebCoreString(args[0]); + return Base64Convert(str, true); +} + + +// TODO(fqian): returning string is cheating, and we should +// fix this by calling toString function on the receiver. +// However, V8 implements toString in JavaScript, which requires +// switching context of receiver. I consider it is dangerous. +CALLBACK_FUNC_DECL(DOMWindowToString) { + INC_STATS(L"DOM.DOMWindow.toString()"); + return args.This()->ObjectProtoToString(); +} + + +CALLBACK_FUNC_DECL(DOMWindowNOP) { + INC_STATS(L"DOM.DOMWindow.nop()"); + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(EventTargetNodeAddEventListener) { + INC_STATS(L"DOM.EventTargetNode.addEventListener()"); + EventTargetNode* node = V8Proxy::FastToNativeObject<EventTargetNode>( + V8ClassIndex::EVENTTARGETNODE, args.Holder()); + + V8Proxy* proxy = V8Proxy::retrieve(node->document()->frame()); + if (!proxy) + return v8::Undefined(); + + EventListener* listener = + proxy->FindOrCreateV8EventListener(args[1], false); + if (listener) { + String type = ToWebCoreString(args[0]); + bool useCapture = args[2]->BooleanValue(); + node->addEventListener(type, listener, useCapture); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(EventTargetNodeRemoveEventListener) { + INC_STATS(L"DOM.EventTargetNode.removeEventListener()"); + EventTargetNode* node = V8Proxy::FastToNativeObject<EventTargetNode>( + V8ClassIndex::EVENTTARGETNODE, args.Holder()); + + V8Proxy* proxy = V8Proxy::retrieve(node->document()->frame()); + // It is possbile that the owner document of the node is detached + // from the frame, return immediately in this case. + // See issue 878909 + if (!proxy) + return v8::Undefined(); + + EventListener* listener = + proxy->FindV8EventListener(args[1], false); + if (listener) { + String type = ToWebCoreString(args[0]); + bool useCapture = args[2]->BooleanValue(); + node->removeEventListener(type, listener, useCapture); + } + + return v8::Undefined(); +} + + +// ------------------------------------------------------------------ +// Customized XMLHttpRequest binding implementation + +// Use an array to hold dependents. It works like a ref-counted scheme. +// A value can be added more than once to the xhr object. +static void CreateHiddenXHRDependency(v8::Local<v8::Object> xhr, + v8::Local<v8::Value> value) { + ASSERT(V8Proxy::GetDOMWrapperType(xhr) == V8ClassIndex::XMLHTTPREQUEST); + int last_internal_field_index = xhr->InternalFieldCount() - 1; + v8::Local<v8::Value> cache = xhr->GetInternalField(last_internal_field_index); + if (cache->IsNull() || cache->IsUndefined()) { + cache = v8::Array::New(); + xhr->SetInternalField(last_internal_field_index, cache); + } + + v8::Local<v8::Array> cache_array = v8::Local<v8::Array>::Cast(cache); + cache_array->Set(v8::Integer::New(cache_array->Length()), value); +} + + +static void RemoveHiddenXHRDependency(v8::Local<v8::Object> xhr, + v8::Local<v8::Value> value) { + ASSERT(V8Proxy::GetDOMWrapperType(xhr) == V8ClassIndex::XMLHTTPREQUEST); + int last_internal_field_index = xhr->InternalFieldCount() - 1; + v8::Local<v8::Value> cache = xhr->GetInternalField(last_internal_field_index); + ASSERT(cache->IsArray()); + v8::Local<v8::Array> cache_array = v8::Local<v8::Array>::Cast(cache); + for (int i = cache_array->Length() - 1; i >= 0; i--) { + v8::Local<v8::Value> cached = cache_array->Get(v8::Integer::New(i)); + if (cached->StrictEquals(value)) { + cache_array->Delete(i); + return; + } + } + + // Should not reach here. + ASSERT(false); +} + + +ACCESSOR_SETTER(XMLHttpRequestOnreadystatechange) { + XMLHttpRequest* imp = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, info.Holder()); + if (value->IsNull()) { + if (imp->onreadystatechange()) { + V8XHREventListener* listener = + static_cast<V8XHREventListener*>(imp->onreadystatechange()); + v8::Local<v8::Object> v8_listener = listener->GetListenerObject(); + RemoveHiddenXHRDependency(info.Holder(), v8_listener); + } + + // Clear the listener + imp->setOnreadystatechange(0); + + } else { + V8Proxy* proxy = V8Proxy::retrieve(imp->document()->frame()); + if (!proxy) + return; + + EventListener* listener = + proxy->FindOrCreateXHREventListener(value, false); + if (listener) { + imp->setOnreadystatechange(listener); + CreateHiddenXHRDependency(info.Holder(), value); + } + } +} + + +ACCESSOR_SETTER(XMLHttpRequestOnload) { + XMLHttpRequest* imp = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, info.Holder()); + if (value->IsNull()) { + if (imp->onload()) { + V8XHREventListener* listener = + static_cast<V8XHREventListener*>(imp->onload()); + v8::Local<v8::Object> v8_listener = listener->GetListenerObject(); + RemoveHiddenXHRDependency(info.Holder(), v8_listener); + } + + imp->setOnload(0); + + } else { + V8Proxy* proxy = V8Proxy::retrieve(imp->document()->frame()); + if (!proxy) + return; + + EventListener* listener = + proxy->FindOrCreateXHREventListener(value, false); + if (listener) { + imp->setOnload(listener); + CreateHiddenXHRDependency(info.Holder(), value); + } + } +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestAddEventListener) { + INC_STATS(L"DOM.XMLHttpRequest.addEventListener()"); + XMLHttpRequest* imp = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, args.Holder()); + + V8Proxy* proxy = V8Proxy::retrieve(imp->document()->frame()); + if (!proxy) + return v8::Undefined(); + + EventListener* listener = + proxy->FindOrCreateXHREventListener(args[1], false); + if (listener) { + String type = ToWebCoreString(args[0]); + bool useCapture = args[2]->BooleanValue(); + imp->addEventListener(type, listener, useCapture); + + CreateHiddenXHRDependency(args.Holder(), args[1]); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestRemoveEventListener) { + INC_STATS(L"DOM.XMLHttpRequest.removeEventListener()"); + XMLHttpRequest* imp = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, args.Holder()); + + V8Proxy* proxy = V8Proxy::retrieve(imp->document()->frame()); + if (!proxy) + return v8::Undefined(); // probably leaked + + EventListener* listener = + proxy->FindXHREventListener(args[1], false); + + if (listener) { + String type = ToWebCoreString(args[0]); + bool useCapture = args[2]->BooleanValue(); + imp->removeEventListener(type, listener, useCapture); + + RemoveHiddenXHRDependency(args.Holder(), args[1]); + } + + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestOpen) { + INC_STATS(L"DOM.XMLHttpRequest.open()"); + // Four cases: + // open(method, url) + // open(method, url, async) + // open(method, url, async, user) + // open(method, url, async, user, passwd) + + if (args.Length() < 2) { + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, "Not enough arguments"); + return v8::Undefined(); + } + + // get the implementation + XMLHttpRequest* xhr = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, args.Holder()); + + // retrieve parameters + String method = ToWebCoreString(args[0]); + String urlstring = ToWebCoreString(args[1]); + V8Proxy* proxy = V8Proxy::retrieve(); + KURL url = + proxy->frame()->document()->completeURL(urlstring).deprecatedString(); + + bool async = (args.Length() < 3) ? true : args[2]->BooleanValue(); + + ExceptionCode ec = 0; + String user, passwd; + if (args.Length() >= 4 && !args[3]->IsUndefined()) { + user = valueToStringWithNullCheck(args[3]); + + if (args.Length() >= 5 && !args[4]->IsUndefined()) { + passwd = valueToStringWithNullCheck(args[4]); + xhr->open(method, url, async, user, passwd, ec); + } else { + xhr->open(method, url, async, user, ec); + } + } else { + xhr->open(method, url, async, ec); + } + + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + + return v8::Undefined(); +} + + +static bool IsDocumentType(v8::Handle<v8::Value> value) { + // TODO(fqian): add other document types. + return V8Document::HasInstance(value) || V8HTMLDocument::HasInstance(value); +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestSend) { + INC_STATS(L"DOM.XMLHttpRequest.send()"); + // Two cases: + // send(document) + // send(string) + + // get implementation + XMLHttpRequest* xhr = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, args.Holder()); + + String body; + if (args.Length() >= 1) { + v8::Handle<v8::Value> arg = args[0]; + if (IsDocumentType(arg)) { + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(arg); + Document* doc = + V8Proxy::ToNativeObject<Document>(V8ClassIndex::DOCUMENT, obj); + ASSERT(doc); + body = doc->toString(); + } else { + body = valueToStringWithNullCheck(arg); + } + } + + ExceptionCode ec = 0; + xhr->send(body, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestSetRequestHeader) { + INC_STATS(L"DOM.XMLHttpRequest.setRequestHeader()"); + if (args.Length() < 2) { + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, "Not enough arguments"); + return v8::Undefined(); + } + + XMLHttpRequest* imp = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, args.Holder()); + ExceptionCode ec = 0; + String header = ToWebCoreString(args[0]); + String value = ToWebCoreString(args[1]); + imp->setRequestHeader(header, value, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return v8::Undefined(); +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestGetResponseHeader) { + INC_STATS(L"DOM.XMLHttpRequest.getResponseHeader()"); + if (args.Length() < 1) { + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, "Not enough arguments"); + return v8::Undefined(); + } + + XMLHttpRequest* imp = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, args.Holder()); + ExceptionCode ec = 0; + String header = ToWebCoreString(args[0]); + String result = imp->getResponseHeader(header, ec); + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + return v8StringOrNull(result); +} + + +CALLBACK_FUNC_DECL(XMLHttpRequestOverrideMimeType) { + INC_STATS(L"DOM.XMLHttpRequest.overrideMimeType()"); + if (args.Length() < 1) { + V8Proxy::ThrowError(V8Proxy::SYNTAX_ERROR, "Not enough arguments"); + return v8::Undefined(); + } + + XMLHttpRequest* imp = V8Proxy::FastToNativeObject<XMLHttpRequest>( + V8ClassIndex::XMLHTTPREQUEST, args.Holder()); + String value = ToWebCoreString(args[0]); + imp->overrideMIMEType(value); + return v8::Undefined(); +} + +CALLBACK_FUNC_DECL(TreeWalkerParentNode) { + INC_STATS(L"DOM.TreeWalker.parentNode()"); + TreeWalker* imp = V8Proxy::FastToNativeObject<TreeWalker>( + V8ClassIndex::TREEWALKER, args.Holder()); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->parentNode()); +} + +CALLBACK_FUNC_DECL(TreeWalkerFirstChild) { + INC_STATS(L"DOM.TreeWalker.firstChild()"); + TreeWalker* imp = V8Proxy::FastToNativeObject<TreeWalker>( + V8ClassIndex::TREEWALKER, args.Holder()); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->firstChild()); +} + +CALLBACK_FUNC_DECL(TreeWalkerLastChild) { + INC_STATS(L"DOM.TreeWalker.lastChild()"); + TreeWalker* imp = V8Proxy::FastToNativeObject<TreeWalker>( + V8ClassIndex::TREEWALKER, args.Holder()); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->lastChild()); +} + +CALLBACK_FUNC_DECL(TreeWalkerNextNode) { + INC_STATS(L"DOM.TreeWalker.nextNode()"); + TreeWalker* imp = V8Proxy::FastToNativeObject<TreeWalker>( + V8ClassIndex::TREEWALKER, args.Holder()); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->nextNode()); +} + +CALLBACK_FUNC_DECL(TreeWalkerPreviousNode) { + INC_STATS(L"DOM.TreeWalker.previousNode()"); + TreeWalker* imp = V8Proxy::FastToNativeObject<TreeWalker>( + V8ClassIndex::TREEWALKER, args.Holder()); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->previousNode()); +} + +CALLBACK_FUNC_DECL(TreeWalkerNextSibling) { + INC_STATS(L"DOM.TreeWalker.nextSibling()"); + TreeWalker* imp = V8Proxy::FastToNativeObject<TreeWalker>( + V8ClassIndex::TREEWALKER, args.Holder()); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->nextSibling()); +} + +CALLBACK_FUNC_DECL(TreeWalkerPreviousSibling) { + INC_STATS(L"DOM.TreeWalker.previousSibling()"); + TreeWalker* imp = V8Proxy::FastToNativeObject<TreeWalker>( + V8ClassIndex::TREEWALKER, args.Holder()); + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->previousSibling()); +} + +CALLBACK_FUNC_DECL(NodeIteratorNextNode) { + INC_STATS(L"DOM.NodeIterator.nextNode()"); + NodeIterator* imp = V8Proxy::FastToNativeObject<NodeIterator>( + V8ClassIndex::NODEITERATOR, args.Holder()); + ExceptionCode ec = 0; + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->nextNode(ec)); +} + +CALLBACK_FUNC_DECL(NodeIteratorPreviousNode) { + INC_STATS(L"DOM.NodeIterator.previousNode()"); + NodeIterator* imp = V8Proxy::FastToNativeObject<NodeIterator>( + V8ClassIndex::NODEITERATOR, args.Holder()); + ExceptionCode ec = 0; + return V8Proxy::ToV8Object(V8ClassIndex::NODE, imp->previousNode(ec)); +} + +CALLBACK_FUNC_DECL(NodeFilterAcceptNode) { + INC_STATS(L"DOM.NodeFilter.acceptNode()"); + NodeFilter* imp = V8Proxy::FastToNativeObject<NodeFilter>( + V8ClassIndex::NODEFILTER, args.Holder()); + Node* node = V8Proxy::FastToNativeObject<Node>(V8ClassIndex::NODE, args[0]); + return v8::Local<v8::Integer>(v8::Integer::New(imp->acceptNode(node))); +} + +ACCESSOR_SETTER(DOMWindowEventHandler) { + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper( + V8ClassIndex::DOMWINDOW, info.This()); + if (holder.IsEmpty()) + return; + + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, holder); + if (!imp->frame()) + return; + + Document* doc = imp->frame()->document(); + if (!doc) + return; + + // Name starts with 'on', remove them. + String key = ToWebCoreString(name); + ASSERT(key.startsWith("on")); + String event_type = key.substring(2); + + if (value->IsNull()) { + // Clear the event listener + doc->removeHTMLWindowEventListener(event_type); + } else { + V8Proxy* proxy = V8Proxy::retrieve(imp->frame()); + if (!proxy) + return; + + EventListener* listener = + proxy->FindOrCreateV8EventListener(value, true); + if (listener) { + doc->setHTMLWindowEventListener(event_type, listener); + } + } +} + + +ACCESSOR_GETTER(DOMWindowEventHandler) { + v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper( + V8ClassIndex::DOMWINDOW, info.This()); + if (holder.IsEmpty()) + return v8::Undefined(); + + DOMWindow* imp = V8Proxy::FastToNativeObject<DOMWindow>( + V8ClassIndex::DOMWINDOW, holder); + if (!imp->frame()) + return v8::Undefined(); + + Document* doc = imp->frame()->document(); + if (!doc) + return v8::Undefined(); + + // Name starts with 'on', remove them. + String key = ToWebCoreString(name); + ASSERT(key.startsWith("on")); + String event_type = key.substring(2); + + EventListener* listener = doc->getHTMLWindowEventListener(event_type); + return V8Proxy::EventListenerToV8Object(listener); +} + + +ACCESSOR_SETTER(ElementEventHandler) { + EventTargetNode* node = V8Proxy::FastToNativeObject<EventTargetNode>( + V8ClassIndex::EVENTTARGETNODE, info.Holder()); + + // Name starts with 'on', remove them. + String key = ToWebCoreString(name); + ASSERT(key.startsWith("on")); + String event_type = key.substring(2); + + // Set handler if the value is a function. Otherwise, clear the + // event handler. + if (value->IsFunction()) { + V8Proxy* proxy = V8Proxy::retrieve(node->document()->frame()); + // the document might be created using createDocument, + // which does not have a frame, use the active frame + if (!proxy) + proxy = V8Proxy::retrieve(V8Proxy::retrieveActiveFrame()); + if (!proxy) + return; + + EventListener* listener = + proxy->FindOrCreateV8EventListener(value, true); + if (listener) { + node->setHTMLEventListener(event_type, listener); + } + } else { + node->removeHTMLEventListener(event_type); + } +} + + +ACCESSOR_GETTER(ElementEventHandler) { + EventTargetNode* node = V8Proxy::FastToNativeObject<EventTargetNode>( + V8ClassIndex::EVENTTARGETNODE, info.Holder()); + + // Name starts with 'on', remove them. + String key = ToWebCoreString(name); + ASSERT(key.startsWith("on")); + String event_type = key.substring(2); + + EventListener* listener = node->getHTMLEventListener(event_type); + return V8Proxy::EventListenerToV8Object(listener); +} + + +ACCESSOR_SETTER(HTMLOptionsCollectionLength) { + HTMLOptionsCollection* imp = + V8Proxy::FastToNativeObject<HTMLOptionsCollection>( + V8ClassIndex::HTMLOPTIONSCOLLECTION, info.Holder()); + double v = value->NumberValue(); + unsigned newLength = 0; + ExceptionCode ec = 0; + if (!isnan(v) && !isinf(v)) { + if (v < 0.0) { + ec = INDEX_SIZE_ERR; + } else if (v > static_cast<double>(UINT_MAX)) { + newLength = UINT_MAX; + } else { + newLength = static_cast<unsigned>(v); + } + } + if (!ec) imp->setLength(value->Uint32Value(), ec); + V8Proxy::SetDOMException(ec); +} + +#if ENABLE(SVG) +CALLBACK_FUNC_DECL(SVGMatrixInverse) { + INC_STATS(L"DOM.SVGMatrix.inverse()"); + AffineTransform imp = + *V8Proxy::FastToNativeObject<V8SVGPODTypeWrapper<AffineTransform> >( + V8ClassIndex::SVGMATRIX, args.Holder()); + ExceptionCode ec = 0; + AffineTransform result = imp.inverse(); + if (!imp.isInvertible()) { + ec = SVGException::SVG_MATRIX_NOT_INVERTABLE; + } + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + + Peerable* peer = static_cast<Peerable*>( + new V8SVGPODTypeWrapperCreatorReadOnly<AffineTransform>(result)); + return V8Proxy::ToV8Object(V8ClassIndex::SVGMATRIX, peer); +} + +CALLBACK_FUNC_DECL(SVGMatrixRotateFromVector) { + INC_STATS(L"DOM.SVGMatrix.rotateFromVector()"); + AffineTransform imp = + *V8Proxy::FastToNativeObject<V8SVGPODTypeWrapper<AffineTransform> >( + V8ClassIndex::SVGMATRIX, args.Holder()); + ExceptionCode ec = 0; + float x = static_cast<float>(args[0]->NumberValue()); + float y = static_cast<float>(args[1]->NumberValue()); + AffineTransform result = imp; + result.rotateFromVector(x, y); + if (x == 0.0 || y == 0.0) { + ec = SVGException::SVG_INVALID_VALUE_ERR; + } + if (ec != 0) { + V8Proxy::SetDOMException(ec); + return v8::Handle<v8::Value>(); + } + + Peerable* peer = static_cast<Peerable*>( + new V8SVGPODTypeWrapperCreatorReadOnly<AffineTransform>(result)); + return V8Proxy::ToV8Object(V8ClassIndex::SVGMATRIX, peer); +} + +#endif // ENABLE(SVG) + +// --------------- Security Checks ------------------------- +NAMED_ACCESS_CHECK(DOMWindow) { + ASSERT(V8ClassIndex::ToWrapperType(data) == V8ClassIndex::DOMWINDOW); + v8::Handle<v8::Value> window = host->GetPrototype(); + DOMWindow* target_win = + V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window); + + ASSERT(target_win); + + Frame* target = target_win->frame(); + if (!target) + return false; + + if (key->IsString()) { + String name = ToWebCoreString(key); + + // Allow access of GET and HAS if index is a subframe. + if ((type == v8::ACCESS_GET || type == v8::ACCESS_HAS) && + target->tree()->child(name)) { + return true; + } + } + + return V8Proxy::CanAccess(target); +} + + +INDEXED_ACCESS_CHECK(DOMWindow) { + ASSERT(V8ClassIndex::ToWrapperType(data) == V8ClassIndex::DOMWINDOW); + v8::Handle<v8::Value> window = host->GetPrototype(); + DOMWindow* target_win = + V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window); + + ASSERT(target_win); + + Frame* target = target_win->frame(); + if (!target) + return false; + + // Allow access of GET and HAS if index is a subframe. + if ((type == v8::ACCESS_GET || type == v8::ACCESS_HAS) && + target->tree()->child(index)) { + return true; + } + + return V8Proxy::CanAccess(target); +} + + +INDEXED_ACCESS_CHECK(History) { + ASSERT(V8ClassIndex::ToWrapperType(data) == V8ClassIndex::HISTORY); + // Only allow same origin access + History* imp = + V8Proxy::FastToNativeObject<History>(V8ClassIndex::HISTORY, host); + return V8Proxy::IsFromSameOrigin(imp->frame(), false); +} + + +NAMED_ACCESS_CHECK(History) { + ASSERT(V8ClassIndex::ToWrapperType(data) == V8ClassIndex::HISTORY); + // Only allow same origin access + History* imp = + V8Proxy::FastToNativeObject<History>(V8ClassIndex::HISTORY, host); + return V8Proxy::IsFromSameOrigin(imp->frame(), false); +} + + +INDEXED_ACCESS_CHECK(Location) { + ASSERT(V8ClassIndex::ToWrapperType(data) == V8ClassIndex::LOCATION); + // Only allow same origin access + Location* imp = + V8Proxy::FastToNativeObject<Location>(V8ClassIndex::LOCATION, host); + return V8Proxy::IsFromSameOrigin(imp->frame(), false); +} + + +NAMED_ACCESS_CHECK(Location) { + ASSERT(V8ClassIndex::ToWrapperType(data) == V8ClassIndex::LOCATION); + // Only allow same origin access + Location* imp = + V8Proxy::FastToNativeObject<Location>(V8ClassIndex::LOCATION, host); + return V8Proxy::IsFromSameOrigin(imp->frame(), false); +} + + +#undef INDEXED_ACCESS_CHECK +#undef NAMED_ACCESS_CHECK +#undef ACCESSOR_GETTER +#undef ACCESSOR_SETTER +#undef CALLBACK_FUNC_DECL +#undef NAMED_PROPERTY_GETTER +#undef NAMED_PROPERTY_SETTER +#undef INDEXED_PROPERTY_GETTER +#undef INDEXED_PROPERTY_SETTER + + +// static +Frame* V8Custom::GetTargetFrame(v8::Local<v8::Object> host, + v8::Local<v8::Value> data) { + Frame* target = 0; + switch (V8ClassIndex::ToWrapperType(data)) { + case V8ClassIndex::DOMWINDOW: { + v8::Handle<v8::Value> window = host->GetPrototype(); + DOMWindow* target_win = + V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window); + target = target_win->frame(); + break; + } + case V8ClassIndex::LOCATION: { + History* imp = + V8Proxy::FastToNativeObject<History>(V8ClassIndex::HISTORY, host); + target = imp->frame(); + break; + } + case V8ClassIndex::HISTORY: { + Location* imp = + V8Proxy::FastToNativeObject<Location>(V8ClassIndex::LOCATION, host); + target = imp->frame(); + break; + } + } + return target; +} + +#if ENABLE(SVG) +V8ClassIndex::V8WrapperType V8Custom::DowncastSVGPathSeg(void* path_seg) { + WebCore::SVGPathSeg *real_path_seg = + reinterpret_cast<WebCore::SVGPathSeg*>(path_seg); + + switch (real_path_seg->pathSegType()) { +#define MAKE_CASE(svg_val, v8_val) \ + case WebCore::SVGPathSeg::svg_val: \ + return V8ClassIndex::v8_val; + +MAKE_CASE(PATHSEG_CLOSEPATH, SVGPATHSEGCLOSEPATH) +MAKE_CASE(PATHSEG_MOVETO_ABS, SVGPATHSEGMOVETOABS) +MAKE_CASE(PATHSEG_MOVETO_REL, SVGPATHSEGMOVETOREL) +MAKE_CASE(PATHSEG_LINETO_ABS, SVGPATHSEGLINETOABS) +MAKE_CASE(PATHSEG_LINETO_REL, SVGPATHSEGLINETOREL) +MAKE_CASE(PATHSEG_CURVETO_CUBIC_ABS, SVGPATHSEGCURVETOCUBICABS) +MAKE_CASE(PATHSEG_CURVETO_CUBIC_REL, SVGPATHSEGCURVETOCUBICREL) +MAKE_CASE(PATHSEG_CURVETO_QUADRATIC_ABS, SVGPATHSEGCURVETOQUADRATICABS) +MAKE_CASE(PATHSEG_CURVETO_QUADRATIC_REL, SVGPATHSEGCURVETOQUADRATICREL) +MAKE_CASE(PATHSEG_ARC_ABS, SVGPATHSEGARCABS) +MAKE_CASE(PATHSEG_ARC_REL, SVGPATHSEGARCREL) +MAKE_CASE(PATHSEG_LINETO_HORIZONTAL_ABS, SVGPATHSEGLINETOHORIZONTALABS) +MAKE_CASE(PATHSEG_LINETO_HORIZONTAL_REL, SVGPATHSEGLINETOHORIZONTALREL) +MAKE_CASE(PATHSEG_LINETO_VERTICAL_ABS, SVGPATHSEGLINETOVERTICALABS) +MAKE_CASE(PATHSEG_LINETO_VERTICAL_REL, SVGPATHSEGLINETOVERTICALREL) +MAKE_CASE(PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, SVGPATHSEGCURVETOCUBICSMOOTHABS) +MAKE_CASE(PATHSEG_CURVETO_CUBIC_SMOOTH_REL, SVGPATHSEGCURVETOCUBICSMOOTHREL) +MAKE_CASE(PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, \ + SVGPATHSEGCURVETOQUADRATICSMOOTHABS) +MAKE_CASE(PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, \ + SVGPATHSEGCURVETOQUADRATICSMOOTHREL) + +#undef MAKE_CASE + + default: + return V8ClassIndex::INVALID_CLASS_INDEX; + } +} + +#endif // ENABLE(SVG) + +} // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_custom.h b/webkit/port/bindings/v8/v8_custom.h new file mode 100644 index 0000000..6b94f81 --- /dev/null +++ b/webkit/port/bindings/v8/v8_custom.h @@ -0,0 +1,459 @@ +// Copyright 2008, 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. + +#ifndef V8_CUSTOM_H__ +#define V8_CUSTOM_H__ + +#include <v8.h> + +struct NPObject; + +namespace WebCore { + +class Frame; +class V8Proxy; +class String; +class HTMLCollection; + +class V8Custom { + public: + + // Constants. + static const int kDefaultWrapperInternalFieldCount = 2; + static const int kDocumentMinimumInternalFieldCount = 3; + static const int kDocumentImplementationIndex = 2; + static const int kHTMLDocumentInternalFieldCount = 5; + static const int kHTMLDocumentMarkerIndex = 3; + static const int kHTMLDocumentShadowIndex = 4; + +#define DECLARE_PROPERTY_ACCESSOR_GETTER(NAME) \ +static v8::Handle<v8::Value> v8##NAME##AccessorGetter(\ + v8::Local<v8::String> name, const v8::AccessorInfo& info); + +#define DECLARE_PROPERTY_ACCESSOR_SETTER(NAME) \ +static void v8##NAME##AccessorSetter(v8::Local<v8::String> name, \ + v8::Local<v8::Value> value, \ + const v8::AccessorInfo& info); + +#define DECLARE_PROPERTY_ACCESSOR(NAME) \ + DECLARE_PROPERTY_ACCESSOR_GETTER(NAME) \ + DECLARE_PROPERTY_ACCESSOR_SETTER(NAME) + + +#define DECLARE_NAMED_PROPERTY_GETTER(NAME) \ +static v8::Handle<v8::Value> v8##NAME##NamedPropertyGetter(\ + v8::Local<v8::String> name, const v8::AccessorInfo& info); + +#define DECLARE_NAMED_PROPERTY_SETTER(NAME) \ +static v8::Handle<v8::Value> v8##NAME##NamedPropertySetter(\ + v8::Local<v8::String> name, \ + v8::Local<v8::Value> value, \ + const v8::AccessorInfo& info); + +#define DECLARE_NAMED_PROPERTY_DELETER(NAME) \ +static v8::Handle<v8::Boolean> v8##NAME##NamedPropertyDeleter(\ + v8::Local<v8::String> name, const v8::AccessorInfo& info); + +#define USE_NAMED_PROPERTY_GETTER(NAME) \ + V8Custom::v8##NAME##NamedPropertyGetter + +#define USE_NAMED_PROPERTY_SETTER(NAME) \ + V8Custom::v8##NAME##NamedPropertySetter + +#define USE_NAMED_PROPERTY_DELETER(NAME) \ + V8Custom::v8##NAME##NamedPropertyDeleter + +#define DECLARE_INDEXED_PROPERTY_GETTER(NAME) \ +static v8::Handle<v8::Value> v8##NAME##IndexedPropertyGetter(\ + uint32_t index, const v8::AccessorInfo& info); + +#define DECLARE_INDEXED_PROPERTY_SETTER(NAME) \ +static v8::Handle<v8::Value> v8##NAME##IndexedPropertySetter(\ + uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info); + +#define DECLARE_INDEXED_PROPERTY_DELETER(NAME) \ +static v8::Handle<v8::Boolean> v8##NAME##IndexedPropertyDeleter(\ + uint32_t index, const v8::AccessorInfo& info); + +#define USE_INDEXED_PROPERTY_GETTER(NAME) \ + V8Custom::v8##NAME##IndexedPropertyGetter + +#define USE_INDEXED_PROPERTY_SETTER(NAME) \ + V8Custom::v8##NAME##IndexedPropertySetter + +#define USE_INDEXED_PROPERTY_DELETER(NAME) \ + V8Custom::v8##NAME##IndexedPropertyDeleter + +#define DECLARE_CALLBACK(NAME) \ +static v8::Handle<v8::Value> v8##NAME##Callback(const v8::Arguments& args); + +#define USE_CALLBACK(NAME) \ + V8Custom::v8##NAME##Callback + +#define DECLARE_NAMED_ACCESS_CHECK(NAME) \ +static bool v8##NAME##NamedSecurityCheck(v8::Local<v8::Object> host, \ + v8::Local<v8::Value> key, \ + v8::AccessType type, \ + v8::Local<v8::Value> data); + +#define DECLARE_INDEXED_ACCESS_CHECK(NAME) \ +static bool v8##NAME##IndexedSecurityCheck(v8::Local<v8::Object> host, \ + uint32_t index, \ + v8::AccessType type, \ + v8::Local<v8::Value> data); + +DECLARE_PROPERTY_ACCESSOR(CanvasRenderingContext2DStrokeStyle) +DECLARE_PROPERTY_ACCESSOR(CanvasRenderingContext2DFillStyle) +// Customized getter&setter of DOMWindow.location +DECLARE_PROPERTY_ACCESSOR(DOMWindowLocation) +// Customized setter of DOMWindow.opener +DECLARE_PROPERTY_ACCESSOR_SETTER(DOMWindowOpener) + +DECLARE_PROPERTY_ACCESSOR(DocumentLocation) +DECLARE_PROPERTY_ACCESSOR(DocumentImplementation) +DECLARE_PROPERTY_ACCESSOR_GETTER(EventSrcElement) +DECLARE_PROPERTY_ACCESSOR(EventReturnValue) +DECLARE_PROPERTY_ACCESSOR_GETTER(EventDataTransfer) +DECLARE_PROPERTY_ACCESSOR_GETTER(EventClipboardData) + +// Getter/Setter for window event handlers +DECLARE_PROPERTY_ACCESSOR(DOMWindowEventHandler) +// Getter/Setter for Element event handlers +DECLARE_PROPERTY_ACCESSOR(ElementEventHandler) + +// Customized setter of src and location on HTMLFrameElement +DECLARE_PROPERTY_ACCESSOR_SETTER(HTMLFrameElementSrc) +DECLARE_PROPERTY_ACCESSOR_SETTER(HTMLFrameElementLocation) +// Customized setter of src on HTMLIFrameElement +DECLARE_PROPERTY_ACCESSOR_SETTER(HTMLIFrameElementSrc) +// Customized setter of Attr.value +DECLARE_PROPERTY_ACCESSOR_SETTER(AttrValue) + +// Customized setter of HTMLOptionsCollection length +DECLARE_PROPERTY_ACCESSOR_SETTER(HTMLOptionsCollectionLength) + +DECLARE_NAMED_ACCESS_CHECK(Location) +DECLARE_INDEXED_ACCESS_CHECK(History) + +DECLARE_NAMED_ACCESS_CHECK(History) +DECLARE_INDEXED_ACCESS_CHECK(Location) + +// HTMLCollection customized functions. +DECLARE_CALLBACK(HTMLCollectionItem) +DECLARE_CALLBACK(HTMLCollectionNamedItem) +// HTMLCollections are callable as functions. +DECLARE_CALLBACK(HTMLCollectionCallAsFunction) + +// HTMLSelectElement customized functions. +DECLARE_CALLBACK(HTMLSelectElementRemove) + +// HTMLOptionsCollection customized functions. +DECLARE_CALLBACK(HTMLOptionsCollectionRemove) +DECLARE_CALLBACK(HTMLOptionsCollectionAdd) + +// HTMLDocument customized functions +DECLARE_CALLBACK(HTMLDocumentWrite) +DECLARE_CALLBACK(HTMLDocumentWriteln) +DECLARE_CALLBACK(HTMLDocumentOpen) +DECLARE_CALLBACK(HTMLDocumentClear) + +// Document customized functions +DECLARE_CALLBACK(DocumentEvaluate) + +// Window customized functions +DECLARE_CALLBACK(DOMWindowAddEventListener) +DECLARE_CALLBACK(DOMWindowRemoveEventListener) +DECLARE_CALLBACK(DOMWindowPostMessage) +DECLARE_CALLBACK(DOMWindowSetTimeout) +DECLARE_CALLBACK(DOMWindowSetInterval) +DECLARE_CALLBACK(DOMWindowAtob) +DECLARE_CALLBACK(DOMWindowBtoa) +DECLARE_CALLBACK(DOMWindowNOP) +DECLARE_CALLBACK(DOMWindowToString) +DECLARE_CALLBACK(DOMWindowShowModalDialog) +DECLARE_CALLBACK(DOMWindowOpen) + +DECLARE_CALLBACK(DOMParserConstructor) +DECLARE_CALLBACK(XMLHttpRequestConstructor) +DECLARE_CALLBACK(XMLSerializerConstructor) +DECLARE_CALLBACK(XPathEvaluatorConstructor) +DECLARE_CALLBACK(XSLTProcessorConstructor) + +// Implementation of custom XSLTProcessor methods. +DECLARE_CALLBACK(XSLTProcessorImportStylesheet) +DECLARE_CALLBACK(XSLTProcessorTransformToFragment) +DECLARE_CALLBACK(XSLTProcessorTransformToDocument) +DECLARE_CALLBACK(XSLTProcessorSetParameter) +DECLARE_CALLBACK(XSLTProcessorGetParameter) +DECLARE_CALLBACK(XSLTProcessorRemoveParameter) + +// CSSPrimitiveValue customized functions +DECLARE_CALLBACK(CSSPrimitiveValueGetRGBColorValue) + +// Canvas 2D customized functions +DECLARE_CALLBACK(CanvasRenderingContext2DSetStrokeColor) +DECLARE_CALLBACK(CanvasRenderingContext2DSetFillColor) +DECLARE_CALLBACK(CanvasRenderingContext2DStrokeRect) +DECLARE_CALLBACK(CanvasRenderingContext2DSetShadow) +DECLARE_CALLBACK(CanvasRenderingContext2DDrawImage) +DECLARE_CALLBACK(CanvasRenderingContext2DDrawImageFromRect) +DECLARE_CALLBACK(CanvasRenderingContext2DCreatePattern) + +// Implementation of Clipboard methods. +DECLARE_CALLBACK(ClipboardClearData) +DECLARE_CALLBACK(ClipboardGetData) +DECLARE_CALLBACK(ClipboardSetData) + +// Implementation of Element methods. +DECLARE_CALLBACK(ElementSetAttribute) +DECLARE_CALLBACK(ElementSetAttributeNode) +DECLARE_CALLBACK(ElementSetAttributeNS) +DECLARE_CALLBACK(ElementSetAttributeNodeNS) + +// Implementation of EventTarget::addEventListener +// and EventTarget::removeEventListener +DECLARE_CALLBACK(EventTargetNodeAddEventListener) +DECLARE_CALLBACK(EventTargetNodeRemoveEventListener) + +// Custom implementation of XMLHttpRequest properties +DECLARE_PROPERTY_ACCESSOR_SETTER(XMLHttpRequestOnreadystatechange) +DECLARE_PROPERTY_ACCESSOR_SETTER(XMLHttpRequestOnload) +DECLARE_CALLBACK(XMLHttpRequestAddEventListener) +DECLARE_CALLBACK(XMLHttpRequestRemoveEventListener) +DECLARE_CALLBACK(XMLHttpRequestOpen) +DECLARE_CALLBACK(XMLHttpRequestSend) +DECLARE_CALLBACK(XMLHttpRequestSetRequestHeader) +DECLARE_CALLBACK(XMLHttpRequestGetResponseHeader) +DECLARE_CALLBACK(XMLHttpRequestOverrideMimeType) + +// Custom implementation of TreeWalker functions +DECLARE_CALLBACK(TreeWalkerParentNode) +DECLARE_CALLBACK(TreeWalkerFirstChild) +DECLARE_CALLBACK(TreeWalkerLastChild) +DECLARE_CALLBACK(TreeWalkerNextNode) +DECLARE_CALLBACK(TreeWalkerPreviousNode) +DECLARE_CALLBACK(TreeWalkerNextSibling) +DECLARE_CALLBACK(TreeWalkerPreviousSibling) + +// Custom implementation of NodeIterator functions +DECLARE_CALLBACK(NodeIteratorNextNode) +DECLARE_CALLBACK(NodeIteratorPreviousNode) + +// Custom implementation of NodeFilter function +DECLARE_CALLBACK(NodeFilterAcceptNode) + +DECLARE_NAMED_PROPERTY_GETTER(DOMWindow) +DECLARE_INDEXED_PROPERTY_GETTER(DOMWindow) +DECLARE_NAMED_ACCESS_CHECK(DOMWindow) +DECLARE_INDEXED_ACCESS_CHECK(DOMWindow) + +DECLARE_NAMED_PROPERTY_GETTER(HTMLFrameSetElement) +DECLARE_NAMED_PROPERTY_GETTER(HTMLFormElement) +DECLARE_NAMED_PROPERTY_GETTER(HTMLDocument) +DECLARE_NAMED_PROPERTY_SETTER(HTMLDocument) +DECLARE_NAMED_PROPERTY_DELETER(HTMLDocument) +DECLARE_NAMED_PROPERTY_GETTER(NodeList) +DECLARE_NAMED_PROPERTY_GETTER(NamedNodeMap) +DECLARE_NAMED_PROPERTY_GETTER(CSSStyleDeclaration) +DECLARE_NAMED_PROPERTY_SETTER(CSSStyleDeclaration) +DECLARE_NAMED_PROPERTY_GETTER(HTMLPlugInElement) +DECLARE_NAMED_PROPERTY_SETTER(HTMLPlugInElement) +DECLARE_INDEXED_PROPERTY_GETTER(HTMLPlugInElement) +DECLARE_INDEXED_PROPERTY_SETTER(HTMLPlugInElement) + +// Plugin object can be called as function. +DECLARE_CALLBACK(HTMLPlugInElement) + +DECLARE_NAMED_PROPERTY_GETTER(StyleSheetList) +DECLARE_INDEXED_PROPERTY_GETTER(NamedNodeMap) +DECLARE_INDEXED_PROPERTY_GETTER(HTMLFormElement) +DECLARE_INDEXED_PROPERTY_GETTER(HTMLOptionsCollection) +DECLARE_INDEXED_PROPERTY_SETTER(HTMLOptionsCollection) +DECLARE_INDEXED_PROPERTY_SETTER(HTMLSelectElementCollection) +DECLARE_NAMED_PROPERTY_GETTER(HTMLCollection) + +// SVG custom properties and callbacks +#if ENABLE(SVG) +DECLARE_CALLBACK(SVGMatrixInverse) +DECLARE_CALLBACK(SVGMatrixRotateFromVector) +#endif + +#undef DECLARE_INDEXED_ACCESS_CHECK +#undef DECLARE_NAMED_ACCESS_CHECK + +#undef DECLARE_PROPERTY_ACCESSOR_SETTER +#undef DECLARE_PROPERTY_ACCESSOR_GETTER +#undef DECLARE_PROPERTY_ACCESSOR + +#undef DECLARE_NAMED_PROPERTY_GETTER +#undef DECLARE_NAMED_PROPERTY_SETTER +#undef DECLARE_NAMED_PROPERTY_DELETER + +#undef DECLARE_INDEXED_PROPERTY_GETTER +#undef DECLARE_INDEXED_PROPERTY_SETTER +#undef DECLARE_INDEXED_PROPERTY_DELETER + +#undef DECLARE_CALLBACK + + // Returns the NPObject corresponding to an HTMLElement object. + static NPObject* GetHTMLPlugInElementNPObject(v8::Handle<v8::Object> object); + + // Returns the owner frame pointer of a DOM wrapper object. It only works for + // these DOM objects requiring cross-domain access check. + static Frame* GetTargetFrame(v8::Local<v8::Object> host, + v8::Local<v8::Value> data); + + // Special case for downcasting SVG path segments +#if ENABLE(SVG) + static V8ClassIndex::V8WrapperType DowncastSVGPathSeg(void* path_seg); +#endif + + private: + static v8::Handle<v8::Value> WindowSetTimeoutImpl(const v8::Arguments& args, + bool single_shot); +}; + + +// A template of named property accessor of collections. +template <class C> +static v8::Handle<v8::Value> CollectionNamedPropertyGetter( + v8::Local<v8::String> name, const v8::AccessorInfo& info) { + return GetNamedPropertyOfCollection<C>(name, info.Holder(), info.Data()); +} + + +// Returns named property of a collection. +template <class C> +static v8::Handle<v8::Value> GetNamedPropertyOfCollection( + v8::Local<v8::String> name, + v8::Local<v8::Object> object, + v8::Local<v8::Value> data) { + // TODO: assert object is a collection type + ASSERT(V8Proxy::MaybeDOMWrapper(object)); + + V8ClassIndex::V8WrapperType t = V8Proxy::GetDOMWrapperType(object); + C* collection = V8Proxy::FastToNativeObject<C>(t, object); + String prop_name = ToWebCoreString(name); + void* result = collection->namedItem(prop_name); + if (!result) return v8::Handle<v8::Value>(); + V8ClassIndex::V8WrapperType type = V8ClassIndex::ToWrapperType(data); + return V8Proxy::ToV8Object(type, result); +} + + +// A template returns whether a collection has a named property. +// This function does not cause JS heap allocation. +template <class C> +static bool HasNamedPropertyOfCollection(v8::Local<v8::String> name, + v8::Local<v8::Object> object, + v8::Local<v8::Value> data) { + // TODO: assert object is a collection type + ASSERT(V8Proxy::MaybeDOMWrapper(object)); + + V8ClassIndex::V8WrapperType t = V8Proxy::GetDOMWrapperType(object); + C* collection = V8Proxy::FastToNativeObject<C>(t, object); + String prop_name = ToWebCoreString(name); + void* result = collection->namedItem(prop_name); + return result != NULL; +} + + +// A template of index interceptor of collections. +template <class C> +static v8::Handle<v8::Value> CollectionIndexedPropertyGetter( + uint32_t index, const v8::AccessorInfo& info) { + return GetIndexedPropertyOfCollection<C>(index, info.Holder(), info.Data()); +} + + +// Returns the property at the index of a collection. +template <class C> +static v8::Handle<v8::Value> GetIndexedPropertyOfCollection( + uint32_t index, v8::Local<v8::Object> object, v8::Local<v8::Value> data) { + // TODO, assert that object must be a collection type + ASSERT(V8Proxy::MaybeDOMWrapper(object)); + V8ClassIndex::V8WrapperType t = V8Proxy::GetDOMWrapperType(object); + C* collection = V8Proxy::FastToNativeObject<C>(t, object); + void* result = collection->item(index); + if (!result) return v8::Handle<v8::Value>(); + V8ClassIndex::V8WrapperType type = V8ClassIndex::ToWrapperType(data); + return V8Proxy::ToV8Object(type, result); +} + + +// Get an array containing the names of indexed properties in a collection. +template <class C> +static v8::Handle<v8::Array> CollectionIndexedPropertyEnumerator( + const v8::AccessorInfo& info) { + ASSERT(V8Proxy::MaybeDOMWrapper(info.Holder())); + V8ClassIndex::V8WrapperType t = V8Proxy::GetDOMWrapperType(info.Holder()); + C* collection = V8Proxy::FastToNativeObject<C>(t, info.Holder()); + int length = collection->length(); + v8::Handle<v8::Array> properties = v8::Array::New(length); + for (int i = 0; i < length; i++) { + // TODO(ager): Do we need to check that the item function returns + // a non-null value for this index? + v8::Handle<v8::Integer> integer = v8::Integer::New(i); + properties->Set(integer, integer); + } + return properties; +} + + +// Returns whether a collection has a property at a given index. +// This function does not cause JS heap allocation. +template <class C> +static bool HasIndexedPropertyOfCollection(uint32_t index, + v8::Local<v8::Object> object, + v8::Local<v8::Value> data) { + // TODO, assert that object must be a collection type + ASSERT(V8Proxy::MaybeDOMWrapper(object)); + V8ClassIndex::V8WrapperType t = V8Proxy::GetDOMWrapperType(object); + C* collection = V8Proxy::FastToNativeObject<C>(t, object); + void* result = collection->item(index); + return result != NULL; +} + + +// A template for indexed getters on collections of strings that should return +// null if the resulting string is a null string. +template <class C> +static v8::Handle<v8::Value> CollectionStringOrNullIndexedPropertyGetter( + uint32_t index, const v8::AccessorInfo& info) { + // TODO, assert that object must be a collection type + ASSERT(V8Proxy::MaybeDOMWrapper(info.Holder())); + V8ClassIndex::V8WrapperType t = V8Proxy::GetDOMWrapperType(info.Holder()); + C* collection = V8Proxy::FastToNativeObject<C>(t, info.Holder()); + String result = collection->item(index); + return v8StringOrNull(result); +} + +} // namespace WebCore + +#endif // V8_CUSTOM_H__ diff --git a/webkit/port/bindings/v8/v8_events.cpp b/webkit/port/bindings/v8/v8_events.cpp new file mode 100644 index 0000000..38e3802 --- /dev/null +++ b/webkit/port/bindings/v8/v8_events.cpp @@ -0,0 +1,472 @@ +// Copyright (c) 2008, 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 "v8_events.h" +#include "v8_proxy.h" +#include "v8_binding.h" +#include "Frame.h" +#include "DOMWindow.h" +#include "V8Event.h" +#include "Event.h" +#include "Document.h" +#include "Tokenizer.h" +#include "Node.h" +#include "XMLHttpRequest.h" +#include "CString.h" + +namespace WebCore { + + +V8AbstractEventListener::V8AbstractEventListener(Frame* frame, bool html) + : m_frame(frame), m_html(html) { + ASSERT(m_frame); + if (!m_frame) return; + + // Get the position in the source if any. + m_lineNumber = 0; + m_columnNumber = 0; + if (m_html && m_frame->document()->tokenizer()) { + m_lineNumber = m_frame->document()->tokenizer()->lineNumber(); + m_columnNumber = m_frame->document()->tokenizer()->columnNumber(); + } +} + +void V8AbstractEventListener::handleEvent(Event* event, bool isWindowEvent) { + // EventListener could be disconnected from the frame. + ASSERT(m_frame); + if (!m_frame) return; + + // The callback function on XMLHttpRequest can clear the event listener + // and destroys 'this' object. Keep a local reference of it. + // See issue 889829 + RefPtr<V8AbstractEventListener> self(this); + + v8::HandleScope handle_scope; + + v8::Handle<v8::Context> context = V8Proxy::GetContext(m_frame); + if (context.IsEmpty()) return; + v8::Context::Scope scope(context); + + // m_frame can removed by the callback function, + // protect it until the callback function returns. + RefPtr<Frame> protector(m_frame); + + IF_DEVEL(log_info(frame, "Handling DOM event", m_frame->document()->URL())); + + v8::Handle<v8::Value> jsevent = V8Proxy::EventToV8Object(event); + + v8::Local<v8::String> event_symbol = v8::String::NewSymbol("event"); + + // Save the old 'event' property. + v8::Local<v8::Value> saved_evt = context->Global()->Get(event_symbol); + + // Make the event available in the window object. + // + // TODO: This does not work as in safari if the window.event + // property is already set. We need to make sure that property + // access is intercepted correctly. + context->Global()->Set(event_symbol, jsevent); + + v8::Local<v8::Value> ret; + { + // Catch exceptions thrown in the event handler so they do not + // propagate to javascript code that caused the event to fire. + v8::TryCatch try_catch; + try_catch.SetVerbose(true); + + // Call the event handler. + ret = CallListenerFunction(jsevent, event, isWindowEvent); + } + + if (V8Proxy::HandleOutOfMemory()) + ASSERT(ret.IsEmpty()); + + if (ret.IsEmpty()) { + return; + } + + if (!ret.IsEmpty()) { + if (!ret->IsNull() && !ret->IsUndefined() && + event->storesResultAsString()) { + event->storeResult(ToWebCoreString(ret)); + } + // Prevent default action if the return value is false; + // TODO(fqian): example, and reference to buganizer entry + if (m_html) { + if (ret->IsBoolean() && !ret->BooleanValue()) { + event->preventDefault(); + } + } + } + + // Restore the old event. + context->Global()->Set(event_symbol, saved_evt); + + Document::updateDocumentsRendering(); +} + + +void V8AbstractEventListener::DisposeListenerObject() { + if (!m_listener.IsEmpty()) { +#ifndef NDEBUG + V8Proxy::UnregisterGlobalHandle(this, m_listener); +#endif + m_listener.Dispose(); + m_listener.Clear(); + } +} + + +V8EventListener::V8EventListener(Frame* frame, v8::Local<v8::Object> listener, + bool html) + : V8AbstractEventListener(frame, html) { + m_listener = v8::Persistent<v8::Object>::New(listener); +#ifndef NDEBUG + V8Proxy::RegisterGlobalHandle(EVENT_LISTENER, this, m_listener); +#endif +} + + +V8EventListener::~V8EventListener() { + if (m_frame) { + V8Proxy* proxy = V8Proxy::retrieve(m_frame); + if (proxy) + proxy->RemoveV8EventListener(this); + } + + DisposeListenerObject(); +} + + +v8::Local<v8::Function> V8EventListener::GetListenerFunction() { + // It could be disposed already. + if (m_listener.IsEmpty()) return v8::Local<v8::Function>(); + + if (m_listener->IsFunction()) { + return v8::Local<v8::Function>::New( + v8::Persistent<v8::Function>::Cast(m_listener)); + + } else if (m_listener->IsObject()) { + v8::Local<v8::Value> prop = + m_listener->Get(v8::String::NewSymbol("handleEvent")); + if (prop->IsFunction()) { + return v8::Local<v8::Function>::Cast(prop); + } + } + + return v8::Local<v8::Function>(); +} + + +v8::Local<v8::Value> +V8EventListener::CallListenerFunction(v8::Handle<v8::Value> jsevent, + Event* event, bool isWindowEvent) { + v8::Local<v8::Function> handler_func = GetListenerFunction(); + if (handler_func.IsEmpty()) return v8::Local<v8::Value>(); + + v8::Local<v8::Object> this_obj = GetThisObject(event, isWindowEvent); + v8::Handle<v8::Value> parameters[1] = {jsevent}; + + V8Proxy* proxy = V8Proxy::retrieve(m_frame); + ASSERT(proxy); + + return proxy->CallFunction(handler_func, this_obj, 1, parameters); +} + + +v8::Local<v8::Object> V8EventListener::GetThisObject(Event* event, + bool isWindowEvent) { + if (!m_listener.IsEmpty() && !m_listener->IsFunction()) { + return v8::Local<v8::Object>::New(m_listener); + } + + if (isWindowEvent) { + return v8::Context::GetCurrent()->Global(); + } + + EventTarget* target = event->currentTarget(); + if (target->toNode()) { + v8::Handle<v8::Value> value = + V8Proxy::ToV8Object(V8ClassIndex::NODE, target->toNode()); + return v8::Local<v8::Object>::New(v8::Handle<v8::Object>::Cast(value)); + + } else if (target->toXMLHttpRequest()) { + v8::Handle<v8::Value> value = V8Proxy::ToV8Object( + V8ClassIndex::XMLHTTPREQUEST, target->toXMLHttpRequest()); + return v8::Local<v8::Object>::New(v8::Handle<v8::Object>::Cast(value)); + + } else { + ASSERT(false); + return v8::Local<v8::Object>(); + } +} + + +// ------- V 8 X H R E v e n t L i s t e n e r ----------------- + +static void WeakXHRListenerCallback(v8::Persistent<v8::Object> obj, + void* para) { + V8XHREventListener* listener = static_cast<V8XHREventListener*>(para); + + // Remove the wrapper + Frame* frame = listener->frame(); + if (frame) { + V8Proxy* proxy = V8Proxy::retrieve(frame); + if (proxy) + proxy->RemoveXHREventListener(listener); + + // Because the listener is no longer in the list, it must + // be disconnected from the frame to avoid dangling frame pointer + // in the destructor. + listener->disconnectFrame(); + } + + // Dispose the listener object. + listener->DisposeListenerObject(); +} + + +V8XHREventListener::V8XHREventListener(Frame* frame, + v8::Local<v8::Object> listener, + bool html) + : V8EventListener(frame, listener, html) { + // make m_listener weak. + m_listener.MakeWeak(this, WeakXHRListenerCallback); +} + + +V8XHREventListener::~V8XHREventListener() { + if (m_frame) { + ASSERT(!m_listener.IsEmpty()); + V8Proxy* proxy = V8Proxy::retrieve(m_frame); + if (proxy) + proxy->RemoveXHREventListener(this); + } + + DisposeListenerObject(); +} + + +// ------- L a z y E v e n t L i s t e n e r --------------- + +V8LazyEventListener::V8LazyEventListener(Frame *frame, const String& code, + const String& func_name) + : V8AbstractEventListener(frame, true), m_code(code), + m_func_name(func_name), m_compiled(false), + m_wrapped_function_compiled(false) { +} + + +V8LazyEventListener::~V8LazyEventListener() { + DisposeListenerObject(); + + // Dispose wrapped function + if (!m_wrapped_function.IsEmpty()) { +#ifndef NDEBUG + V8Proxy::UnregisterGlobalHandle(this, m_wrapped_function); +#endif + m_wrapped_function.Dispose(); + m_wrapped_function.Clear(); + } +} + + +v8::Local<v8::Object> V8LazyEventListener::GetThisObject(Event* event, + bool isWindowEvent) { + if (isWindowEvent) { + return v8::Context::GetCurrent()->Global(); + } + + v8::Handle<v8::Value> value = + V8Proxy::ToV8Object(V8ClassIndex::NODE, event->currentTarget()->toNode()); + ASSERT(!value.IsEmpty()); + + return v8::Local<v8::Object>::New(v8::Handle<v8::Object>::Cast(value)); +} + + +v8::Local<v8::Function> V8LazyEventListener::GetListenerFunction() { + if (m_compiled) { + ASSERT(m_listener.IsEmpty() || m_listener->IsFunction()); + return m_listener.IsEmpty() ? + v8::Local<v8::Function>() : + v8::Local<v8::Function>::New( + v8::Persistent<v8::Function>::Cast(m_listener)); + } + + m_compiled = true; + + ASSERT(m_frame); + + { + // Switch to the contex of m_frame + v8::HandleScope handle_scope; + + // Use the outer scope to hold context. + v8::Handle<v8::Context> context = V8Proxy::GetContext(m_frame); + // Bail out if we could not get the context. + if (context.IsEmpty()) return v8::Local<v8::Function>(); + + v8::Context::Scope scope(context); + + // Wrap function around the event code. The parenthesis around + // the function are needed so that evaluating the code yields + // the function value. Without the parenthesis the function + // value is thrown away. + + // Make it an anonymous function to avoid name conflict for cases like + // <body onload='onload()'> + // <script> function onload() { alert('hi'); } </script>. + // Set function name to function object instead. + // See issue 944690. + // + // The ECMAScript spec says (very obliquely) that the parameter to + // an event handler is named "evt". + String code = "(function (evt) {\n"; + code.append(m_code); + code.append("})"); + + IF_DEVEL(log_info(frame, code, "<getListener>")); + + v8::Handle<v8::String> codeExternalString = v8ExternalString(code); + v8::Handle<v8::Script> script = V8Proxy::CompileScript(codeExternalString, + m_frame->document()->url(), m_lineNumber - 1); + if (!script.IsEmpty()) { + V8Proxy* proxy = V8Proxy::retrieve(m_frame); + ASSERT(proxy); // must be valid at this point + v8::Local<v8::Value> value = proxy->RunScript(script, false); + if (!value.IsEmpty()) { + ASSERT(value->IsFunction()); + v8::Local<v8::Function> listener_func = + v8::Local<v8::Function>::Cast(value); + // Set the function name. + listener_func->SetName(v8::String::New(FromWebCoreString(m_func_name), + m_func_name.length())); + + m_listener = v8::Persistent<v8::Function>::New(listener_func); +#ifndef NDEBUG + V8Proxy::RegisterGlobalHandle(EVENT_LISTENER, this, m_listener); +#endif + } + } + } // end of HandleScope + + ASSERT(m_listener.IsEmpty() || m_listener->IsFunction()); + return m_listener.IsEmpty() ? + v8::Local<v8::Function>() : + v8::Local<v8::Function>::New( + v8::Persistent<v8::Function>::Cast(m_listener)); +} + + +v8::Local<v8::Value> +V8LazyEventListener::CallListenerFunction(v8::Handle<v8::Value> jsevent, + Event* event, bool isWindowEvent) { + v8::Local<v8::Object> this_obj = GetThisObject(event, isWindowEvent); + v8::Local<v8::Function> handler_func = GetWrappedListenerFunction(); + if (handler_func.IsEmpty()) return v8::Local<v8::Value>(); + + v8::Handle<v8::Value> parameters[1] = {jsevent}; + + V8Proxy* proxy = V8Proxy::retrieve(m_frame); + return proxy->CallFunction(handler_func, this_obj, 1, parameters); +} + + +v8::Local<v8::Function> V8LazyEventListener::GetWrappedListenerFunction() { + if (m_wrapped_function_compiled) { + ASSERT(m_wrapped_function.IsEmpty() || m_wrapped_function->IsFunction()); + return m_wrapped_function.IsEmpty() ? v8::Local<v8::Function>() : + v8::Local<v8::Function>::New(m_wrapped_function); + } + + m_wrapped_function_compiled = true; + + { + // Switch to the contex of m_frame + v8::HandleScope handle_scope; + + // Use the outer scope to hold context. + v8::Handle<v8::Context> context = V8Proxy::GetContext(m_frame); + // Bail out if we cannot get the context. + if (context.IsEmpty()) return v8::Local<v8::Function>(); + + v8::Context::Scope scope(context); + + // TODO(fqian): cache the wrapper function. + String code = "(function (evt) {\n"; + + // This variable records how many lines the code has been offset within the + // source code to be evaluated + int codeOffset = 2; + + // Nodes other than the document object, when executing inline event + // handlers push document, form, and the target node on the scope chain. + // We do this by using 'with' statement. + // See chrome/fast/forms/form-action.html + // chrome/fast/forms/selected-index-value.html + // base/fast/overflow/onscroll-layer-self-destruct.html + code.append(" with (this.ownerDocument ? this.ownerDocument : {}) {\n"); + code.append(" with (this.form ? this.form : {}) {\n"); + code.append(" with (this) {\n"); + code.append(" "); + code.append(m_code); + code.append("\n"); + code.append(" }\n"); + code.append(" }\n"); + code.append(" }\n"); + code.append("})"); + v8::Handle<v8::String> codeExternalString = v8ExternalString(code); + v8::Handle<v8::Script> script = V8Proxy::CompileScript(codeExternalString, + m_frame->document()->url(), m_lineNumber - 4); + if (!script.IsEmpty()) { + V8Proxy* proxy = V8Proxy::retrieve(m_frame); + ASSERT(proxy); + v8::Local<v8::Value> value = proxy->RunScript(script, false); + if (!value.IsEmpty()) { + ASSERT(value->IsFunction()); + + m_wrapped_function = v8::Persistent<v8::Function>::New( + v8::Local<v8::Function>::Cast(value)); +#ifndef NDEBUG + V8Proxy::RegisterGlobalHandle(EVENT_LISTENER, this, m_wrapped_function); +#endif + // Set the function name. + m_wrapped_function->SetName(v8::String::New( + FromWebCoreString(m_func_name), m_func_name.length())); + } + } + } // end of local scope + + return v8::Local<v8::Function>::New(m_wrapped_function); +} + +} // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_events.h b/webkit/port/bindings/v8/v8_events.h new file mode 100644 index 0000000..e2dc524 --- /dev/null +++ b/webkit/port/bindings/v8/v8_events.h @@ -0,0 +1,171 @@ +// Copyright 2008, 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. + +#ifndef V8_EVENTS_H__ +#define V8_EVENTS_H__ + +#include "config.h" +#include <v8.h> +#include "Frame.h" +#include "Event.h" +#include "EventListener.h" +#include "PlatformString.h" + +// #define IF_DEVEL(stmt) stmt +#define IF_DEVEL(stmt) ((void) 0) + +namespace WebCore { + +// There are two kinds of event listeners: HTML or non-HMTL. +// onload, onfocus, etc (attributes) are always HTML event handler type; +// Event listeners added by Window.addEventListener +// or EventTargetNode::addEventListener are non-HTML type. +// +// Why does this matter? +// WebKit does not allow duplicated HTML event handlers of the same type, +// but ALLOWs duplicated non-HTML event handlers. + +class V8AbstractEventListener : public EventListener { + public: + virtual ~V8AbstractEventListener() { } + + // Returns the owner frame of the listener. + Frame* frame() { return m_frame; } + + // Handle event. + void handleEvent(Event*, bool isWindowEvent); + + // Returns the listener object, either a function or an object. + virtual v8::Local<v8::Object> GetListenerObject() { + return v8::Local<v8::Object>::New(m_listener); + } + + // Dispose listener object and clear the handle + void DisposeListenerObject(); + + private: + V8AbstractEventListener(Frame* frame, bool html); + + + // Call listener function. + virtual v8::Local<v8::Value> + CallListenerFunction(v8::Handle<v8::Value> jsevent, + Event* event, bool isWindowEvent) = 0; + + // Frame to which the event listener is attached to. + // An event listener must be destroyed before its owner + // frame is deleted. + // See fast/dom/replaceChild.html + // TODO(fqian): this could hold m_frame live until + // the event listener is deleted. Fix this! + Frame* m_frame; + + // Listener object, avoid using peer because it can keep this object alive. + v8::Persistent<v8::Object> m_listener; + + // Flags this is a HTML type listener. + bool m_html; + + // Position in the HTML source for HTML event listeners. + int m_lineNumber; + int m_columnNumber; + + friend class V8EventListener; + friend class V8XHREventListener; + friend class V8LazyEventListener; +}; + +// V8EventListener is a wrapper of a JS object implements EventListener +// interface (has handleEvent(event) method), or a JS function +// that can handle the event. +class V8EventListener : public V8AbstractEventListener { + public: + V8EventListener(Frame* frame, v8::Local<v8::Object> listener, bool html); + virtual ~V8EventListener(); + virtual bool isHTMLEventListener() const { return m_html; } + + // Detach the listener from its owner frame. + void disconnectFrame() { m_frame = 0; } + + private: + // Call listener function. + virtual v8::Local<v8::Value> CallListenerFunction( + v8::Handle<v8::Value> jsevent, Event* event, bool isWindowEvent); + v8::Local<v8::Object> GetThisObject(Event* event, bool isWindowEvent); + v8::Local<v8::Function> GetListenerFunction(); +}; + + +// V8XHREventListener is a special listener wrapper for XMLHttpRequest object. +// It keeps JS listener week. +class V8XHREventListener : public V8EventListener { + public: + V8XHREventListener(Frame* frame, v8::Local<v8::Object> listener, bool html); + virtual ~V8XHREventListener(); +}; + + +// V8LazyEventListener is a wrapper for a JavaScript code string that is +// compiled and evaluated when an event is fired. +// A V8LazyEventListener is always a HTML event handler. +class V8LazyEventListener : public V8AbstractEventListener { + public: + V8LazyEventListener(Frame *frame, const String& code, + const String& func_name); + virtual ~V8LazyEventListener(); + virtual bool isHTMLEventListener() const { return true; } + + // For lazy event listener, the listener object is the same as its listener + // function without additional scope chains. + virtual v8::Local<v8::Object> GetListenerObject() { + return GetWrappedListenerFunction(); + } + + private: + String m_code; + String m_func_name; // function name + bool m_compiled; + + // If the event listener is on a non-document dom node, + // we compile the function with some implicit scope chains before it. + bool m_wrapped_function_compiled; + v8::Persistent<v8::Function> m_wrapped_function; + + v8::Local<v8::Function> GetWrappedListenerFunction(); + + virtual v8::Local<v8::Value> CallListenerFunction( + v8::Handle<v8::Value> jsevent, Event* event, bool isWindowEvent); + + v8::Local<v8::Object> GetThisObject(Event* event, bool isWindowEvent); + v8::Local<v8::Function> GetListenerFunction(); +}; + +} // namespace WebCore + +#endif // V8_EVENTS_H__ diff --git a/webkit/port/bindings/v8/v8_helpers.cpp b/webkit/port/bindings/v8/v8_helpers.cpp new file mode 100644 index 0000000..466f810 --- /dev/null +++ b/webkit/port/bindings/v8/v8_helpers.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2008, 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" + +#define max max +#define min min +#include "v8_helpers.h" +#include "v8_proxy.h" +#include "v8_index.h" +#include "np_v8object.h" + +#include "DOMWindow.h" + +void WrapNPObject(v8::Handle<v8::Object> obj, NPObject* npobj) { + ASSERT(obj->InternalFieldCount() >= 3); + + WebCore::V8Proxy::SetDOMWrapper(obj, WebCore::V8ClassIndex::NPOBJECT, npobj); + + // Create a JS object as a hash map for functions + obj->SetInternalField(2, v8::Object::New()); +} + +v8::Local<v8::Context> GetV8Context(NPP npp, NPObject* npobj) { + V8NPObject* object = reinterpret_cast<V8NPObject*>(npobj); + return WebCore::V8Proxy::GetContext(object->root_object->frame()); +} + +WebCore::V8Proxy* GetV8Proxy(NPObject* npobj) { + V8NPObject* object = reinterpret_cast<V8NPObject*>(npobj); + WebCore::Frame* frame = object->root_object->frame(); + return WebCore::V8Proxy::retrieve(frame); +} diff --git a/webkit/port/bindings/v8/v8_helpers.h b/webkit/port/bindings/v8/v8_helpers.h new file mode 100644 index 0000000..39b53f3 --- /dev/null +++ b/webkit/port/bindings/v8/v8_helpers.h @@ -0,0 +1,49 @@ +// Copyright 2008, 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. + +#ifndef V8_HELPERS_H__ +#define V8_HELPERS_H__ + +#include "bindings/npruntime.h" +#include <v8.h> + +namespace WebCore { + class V8Proxy; +} + +// Associates an NPObject with a V8 object. +void WrapNPObject(v8::Handle<v8::Object> obj, NPObject *npobj); + +// Retrieves the V8 Context from the NP context pr obj (at most 1 may be NULL). +v8::Local<v8::Context> GetV8Context(NPP npp, NPObject* npobj); + +// Get V8Proxy object from an NPObject. +WebCore::V8Proxy* GetV8Proxy(NPObject* npobj); + +#endif // V8_HELPERS_H__ diff --git a/webkit/port/bindings/v8/v8_index.cpp b/webkit/port/bindings/v8/v8_index.cpp new file mode 100644 index 0000000..4cbe7d9 --- /dev/null +++ b/webkit/port/bindings/v8/v8_index.cpp @@ -0,0 +1,361 @@ +// Copyright (c) 2008, 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 "v8_index.h" + +// TODO: Can we use a macro to include necessary headers by using +// WRAPPER_TYPES? +#include "V8Attr.h" +#include "V8BarInfo.h" +#include "V8CanvasRenderingContext2D.h" +#include "V8CanvasGradient.h" +#include "V8CanvasPattern.h" +#include "V8CDATASection.h" +#include "V8CharacterData.h" +#include "V8Clipboard.h" +#include "V8Comment.h" +#include "V8Console.h" +#include "V8Counter.h" +#include "V8CSSSTyleDeclaration.h" +#include "V8CSSRule.h" +#include "V8CSSStyleRule.h" +#include "V8CSSCharsetRule.h" +#include "V8CSSImportRule.h" +#include "V8CSSMediaRule.h" +#include "V8CSSFontFaceRule.h" +#include "V8CSSPageRule.h" +#include "V8CSSRuleList.h" +#include "V8CSSPrimitiveValue.h" +#include "V8CSSValue.h" +#include "V8CSSValueList.h" +#include "V8CSSStyleSheet.h" +#include "V8Document.h" +#include "V8DocumentFragment.h" +#include "V8DocumentType.h" +#include "V8Element.h" +#include "V8Entity.h" +#include "V8EntityReference.h" +#include "V8History.h" +#include "V8HTMLCanvasElement.h" +#include "V8UndetectableHTMLCollection.h" +#include "V8HTMLCollection.h" +#include "V8HTMLDocument.h" +#include "V8HTMLElement.h" +#include "V8HTMLOptionsCollection.h" +#include "V8HTMLAnchorElement.h" +#include "V8HTMLAppletElement.h" +#include "V8HTMLAreaElement.h" +#include "V8HTMLBaseElement.h" +#include "V8HTMLBaseFontElement.h" +#include "V8HTMLBlockquoteElement.h" +#include "V8HTMLBodyElement.h" +#include "V8HTMLBRElement.h" +#include "V8HTMLButtonElement.h" +#include "V8HTMLCanvasElement.h" +#include "V8HTMLModElement.h" +#include "V8HTMLDirectoryElement.h" +#include "V8HTMLDivElement.h" +#include "V8HTMLDListElement.h" +#include "V8HTMLEmbedElement.h" +#include "V8HTMLFieldSetElement.h" +#include "V8HTMLFormElement.h" +#include "V8HTMLFontElement.h" +#include "V8HTMLFrameElement.h" +#include "V8HTMLFrameSetElement.h" +#include "V8HTMLHeadingElement.h" +#include "V8HTMLHeadElement.h" +#include "V8HTMLHRElement.h" +#include "V8HTMLHtmlElement.h" +#include "V8HTMLIFrameElement.h" +#include "V8HTMLImageElement.h" +#include "V8HTMLInputElement.h" +#include "V8HTMLSelectionInputElement.h" +#include "V8HTMLIsIndexElement.h" +#include "V8HTMLLabelElement.h" +#include "V8HTMLLegendElement.h" +#include "V8HTMLLIElement.h" +#include "V8HTMLLinkElement.h" +#include "V8HTMLMapElement.h" +#include "V8HTMLMarqueeElement.h" +#include "V8HTMLMenuElement.h" +#include "V8HTMLMetaElement.h" +#include "V8HTMLObjectElement.h" +#include "V8HTMLOListElement.h" +#include "V8HTMLOptGroupElement.h" +#include "V8HTMLOptionElement.h" +#include "V8HTMLParagraphElement.h" +#include "V8HTMLParamElement.h" +#include "V8HTMLPreElement.h" +#include "V8HTMLQuoteElement.h" +#include "V8HTMLScriptElement.h" +#include "V8HTMLSelectElement.h" +#include "V8HTMLStyleElement.h" +#include "V8HTMLTableCaptionElement.h" +#include "V8HTMLTableColElement.h" +#include "V8HTMLTableElement.h" +#include "V8HTMLTableSectionElement.h" +#include "V8HTMLTableCellElement.h" +#include "V8HTMLTableRowElement.h" +#include "V8HTMLTextAreaElement.h" +#include "V8HTMLTitleElement.h" +#include "V8HTMLUListElement.h" +#include "V8InspectorController.h" +#include "V8MediaList.h" +#include "V8MessageEvent.h" +#include "V8NamedNodeMap.h" +#include "V8Node.h" +#include "V8NodeList.h" +#include "V8NodeFilter.h" +#include "V8Notation.h" +#include "V8ProcessingInstruction.h" +#include "V8ProgressEvent.h" +#include "V8StyleSheet.h" +#include "V8Text.h" +#include "V8TextEvent.h" +#include "V8DOMCoreException.h" +#include "V8DOMParser.h" +#include "V8DOMWindow.h" +#include "V8Event.h" +#include "V8EventException.h" +#include "V8EventTargetNode.h" +#include "V8KeyboardEvent.h" +#include "V8MouseEvent.h" +#include "V8WheelEvent.h" +#include "V8UIEvent.h" +#include "V8MutationEvent.h" +#include "V8OverflowEvent.h" +#include "V8Location.h" +#include "V8Screen.h" +#include "V8DOMSelection.h" +#include "V8Navigator.h" +#include "V8MimeType.h" +#include "V8MimeTypeArray.h" +#include "V8Plugin.h" +#include "V8PluginArray.h" +#include "V8Range.h" +#include "V8RangeException.h" +#include "V8Rect.h" +#include "V8NodeIterator.h" +#include "V8TreeWalker.h" +#include "V8StyleSheetList.h" +#include "V8DOMImplementation.h" +#include "V8XPathResult.h" +#include "V8XPathException.h" +#include "V8XPathExpression.h" +#include "V8XPathNSResolver.h" +#include "V8XMLHttpRequest.h" +#include "V8XMLHttpRequestException.h" +#include "V8XMLSerializer.h" +#include "V8XPathEvaluator.h" +#include "V8XSLTProcessor.h" +#include "V8RGBColor.h" + +#if ENABLE(SVG_ANIMATION) +#include "V8SVGAnimateColorElement.h" +#include "V8SVGAnimateElement.h" +#include "V8SVGAnimateTransformElement.h" +#include "V8SVGAnimationElement.h" +#include "V8SVGSetElement.h" +#endif + +#if ENABLE(SVG_FILTERS) +#include "V8SVGComponentTransferFunctionElement.h" +#include "V8SVGFEBlendElement.h" +#include "V8SVGFEColorMatrixElement.h" +#include "V8SVGFEComponentTransferElement.h" +#include "V8SVGFECompositeElement.h" +#include "V8SVGFEDiffuseLightingElement.h" +#include "V8SVGFEDisplacementMapElement.h" +#include "V8SVGFEDistantLightElement.h" +#include "V8SVGFEFloodElement.h" +#include "V8SVGFEFuncAElement.h" +#include "V8SVGFEFuncBElement.h" +#include "V8SVGFEFuncGElement.h" +#include "V8SVGFEFuncRElement.h" +#include "V8SVGFEGaussianBlurElement.h" +#include "V8SVGFEImageElement.h" +#include "V8SVGFEMergeElement.h" +#include "V8SVGFEMergeNodeElement.h" +#include "V8SVGFEOffsetElement.h" +#include "V8SVGFEPointLightElement.h" +#include "V8SVGFESpecularLightingElement.h" +#include "V8SVGFESpotLightElement.h" +#include "V8SVGFETileElement.h" +#include "V8SVGFETurbulenceElement.h" +#include "V8SVGFilterElement.h" +#endif + +#if ENABLE(SVG_FONTS) +#include "V8SVGDefinitionSrcElement.h" +#include "V8SVGFontFaceElement.h" +#include "V8SVGFontFaceFormatElement.h" +#include "V8SVGFontFaceNameElement.h" +#include "V8SVGFontFaceSrcElement.h" +#include "V8SVGFontFaceUriElement.h" +#endif + +#if ENABLE(SVG_FOREIGN_OBJECT) +#include "V8SVGForeignObjectElement.h" +#endif + +#if ENABLE(SVG_USE) +#include "V8SVGUseElement.h" +#endif + +#if ENABLE(SVG) +#include "V8SVGAElement.h" +#include "V8SVGCircleElement.h" +#include "V8SVGClipPathElement.h" +#include "V8SVGCursorElement.h" +#include "V8SVGDefsElement.h" +#include "V8SVGDescElement.h" +#include "V8SVGElement.h" +#include "V8SVGEllipseElement.h" +#include "V8SVGException.h" +#include "V8SVGGElement.h" +#include "V8SVGGradientElement.h" +#include "V8SVGImageElement.h" +#include "V8SVGLinearGradientElement.h" +#include "V8SVGLineElement.h" +#include "V8SVGMarkerElement.h" +#include "V8SVGMaskElement.h" +#include "V8SVGMetadataElement.h" +#include "V8SVGPathElement.h" +#include "V8SVGPatternElement.h" +#include "V8SVGPolygonElement.h" +#include "V8SVGPolylineElement.h" +#include "V8SVGRadialGradientElement.h" +#include "V8SVGRectElement.h" +#include "V8SVGScriptElement.h" +#include "V8SVGStopElement.h" +#include "V8SVGStyleElement.h" +#include "V8SVGSVGElement.h" +#include "V8SVGSwitchElement.h" +#include "V8SVGSymbolElement.h" +#include "V8SVGTextContentElement.h" +#include "V8SVGTextElement.h" +#include "V8SVGTextPathElement.h" +#include "V8SVGTextPositioningElement.h" +#include "V8SVGTitleElement.h" +#include "V8SVGTRefElement.h" +#include "V8SVGTSpanElement.h" +#include "V8SVGViewElement.h" +#include "V8SVGAngle.h" +#include "V8SVGAnimatedAngle.h" +#include "V8SVGAnimatedBoolean.h" +#include "V8SVGAnimatedEnumeration.h" +#include "V8SVGAnimatedInteger.h" +#include "V8SVGAnimatedLength.h" +#include "V8SVGAnimatedLengthList.h" +#include "V8SVGAnimatedNumber.h" +#include "V8SVGAnimatedNumberList.h" +#include "V8SVGAnimatedPoints.h" +#include "V8SVGAnimatedPreserveAspectRatio.h" +#include "V8SVGAnimatedRect.h" +#include "V8SVGAnimatedString.h" +#include "V8SVGAnimatedTransformList.h" +#include "V8SVGColor.h" +#include "V8SVGDocument.h" +#include "V8SVGElementInstance.h" +#include "V8SVGElementInstanceList.h" +#include "V8SVGLength.h" +#include "V8SVGLengthList.h" +#include "V8SVGMatrix.h" +#include "V8SVGNumber.h" +#include "V8SVGNumberList.h" +#include "V8SVGPaint.h" +#include "V8SVGPathSeg.h" +#include "V8SVGPathSegArcAbs.h" +#include "V8SVGPathSegArcRel.h" +#include "V8SVGPathSegClosePath.h" +#include "V8SVGPathSegCurvetoCubicAbs.h" +#include "V8SVGPathSegCurvetoCubicRel.h" +#include "V8SVGPathSegCurvetoCubicSmoothAbs.h" +#include "V8SVGPathSegCurvetoCubicSmoothRel.h" +#include "V8SVGPathSegCurvetoQuadraticAbs.h" +#include "V8SVGPathSegCurvetoQuadraticRel.h" +#include "V8SVGPathSegCurvetoQuadraticSmoothAbs.h" +#include "V8SVGPathSegCurvetoQuadraticSmoothRel.h" +#include "V8SVGPathSegLinetoAbs.h" +#include "V8SVGPathSegLinetoHorizontalAbs.h" +#include "V8SVGPathSegLinetoHorizontalRel.h" +#include "V8SVGPathSegLinetoRel.h" +#include "V8SVGPathSegLinetoVerticalAbs.h" +#include "V8SVGPathSegLinetoVerticalRel.h" +#include "V8SVGPathSegList.h" +#include "V8SVGPathSegMovetoAbs.h" +#include "V8SVGPathSegMovetoRel.h" +#include "V8SVGPoint.h" +#include "V8SVGPointList.h" +#include "V8SVGPreserveAspectRatio.h" +#include "V8SVGRect.h" +#include "V8SVGRenderingIntent.h" +#include "V8SVGStringList.h" +#include "V8SVGTransform.h" +#include "V8SVGTransformList.h" +#include "V8SVGUnitTypes.h" +#include "V8SVGURIReference.h" +#include "V8SVGZoomEvent.h" +#endif + +namespace WebCore { + +FunctionTemplateFactory V8ClassIndex::GetFactory(V8WrapperType type) { + switch (type) { +#define MAKE_CASE(type, name)\ + case V8ClassIndex::type: return V8##name::GetTemplate; + WRAPPER_TYPES(MAKE_CASE) +#undef MAKE_CASE + default: return NULL; + } +} + + +#define MAKE_CACHE(type, name)\ + static v8::Persistent<v8::FunctionTemplate> name##_cache_; + ALL_WRAPPER_TYPES(MAKE_CACHE) +#undef MAKE_CACHE + + +v8::Persistent<v8::FunctionTemplate>* V8ClassIndex::GetCache( + V8WrapperType type) { + switch (type) { +#define MAKE_CASE(type, name)\ + case V8ClassIndex::type: return &name##_cache_; + ALL_WRAPPER_TYPES(MAKE_CASE) +#undef MAKE_CASE + default: + ASSERT(false); + return NULL; + } +} + +} // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_index.h b/webkit/port/bindings/v8/v8_index.h new file mode 100644 index 0000000..7fc6041 --- /dev/null +++ b/webkit/port/bindings/v8/v8_index.h @@ -0,0 +1,422 @@ +// Copyright 2008, 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. + +#ifndef V8_INDEX_H__ +#define V8_INDEX_H__ + +#include <v8.h> +#include "PlatformString.h" // for WebCore::String + +namespace WebCore { + +class Node; +class XMLHttpRequest; + +typedef v8::Persistent<v8::FunctionTemplate> (*FunctionTemplateFactory)(); + + +#define NODE_WRAPPER_TYPES(V) \ + V(ATTR, Attr) \ + V(CHARACTERDATA, CharacterData) \ + V(CDATASECTION, CDATASection) \ + V(COMMENT, Comment) \ + V(DOCUMENT, Document) \ + V(DOCUMENTFRAGMENT, DocumentFragment) \ + V(DOCUMENTTYPE, DocumentType) \ + V(ELEMENT, Element) \ + V(ENTITY, Entity) \ + V(ENTITYREFERENCE, EntityReference) \ + V(EVENTTARGETNODE, EventTargetNode) \ + V(HTMLDOCUMENT, HTMLDocument) \ + V(NODE, Node) \ + V(NOTATION, Notation) \ + V(PROCESSINGINSTRUCTION, ProcessingInstruction) \ + V(TEXT, Text) + +#define HTMLELEMENT_TYPES(V) \ + V(HTMLANCHORELEMENT, HTMLAnchorElement) \ + V(HTMLAPPLETELEMENT, HTMLAppletElement) \ + V(HTMLAREAELEMENT, HTMLAreaElement) \ + V(HTMLBASEELEMENT, HTMLBaseElement) \ + V(HTMLBASEFONTELEMENT, HTMLBaseFontElement) \ + V(HTMLBLOCKQUOTEELEMENT, HTMLBlockquoteElement) \ + V(HTMLBODYELEMENT, HTMLBodyElement) \ + V(HTMLBRELEMENT, HTMLBRElement) \ + V(HTMLBUTTONELEMENT, HTMLButtonElement) \ + V(HTMLCANVASELEMENT, HTMLCanvasElement) \ + V(HTMLDIRECTORYELEMENT, HTMLDirectoryElement) \ + V(HTMLDIVELEMENT, HTMLDivElement) \ + V(HTMLDLISTELEMENT, HTMLDListElement) \ + V(HTMLEMBEDELEMENT, HTMLEmbedElement) \ + V(HTMLFIELDSETELEMENT, HTMLFieldSetElement) \ + V(HTMLFONTELEMENT, HTMLFontElement) \ + V(HTMLFORMELEMENT, HTMLFormElement) \ + V(HTMLFRAMEELEMENT, HTMLFrameElement) \ + V(HTMLFRAMESETELEMENT, HTMLFrameSetElement) \ + V(HTMLHEADINGELEMENT, HTMLHeadingElement) \ + V(HTMLHEADELEMENT, HTMLHeadElement) \ + V(HTMLHRELEMENT, HTMLHRElement) \ + V(HTMLHTMLELEMENT, HTMLHtmlElement) \ + V(HTMLIFRAMEELEMENT, HTMLIFrameElement) \ + V(HTMLIMAGEELEMENT, HTMLImageElement) \ + V(HTMLINPUTELEMENT, HTMLInputElement) \ + V(HTMLSELECTIONINPUTELEMENT, HTMLSelectionInputElement) \ + V(HTMLISINDEXELEMENT, HTMLIsIndexElement) \ + V(HTMLLABELELEMENT, HTMLLabelElement) \ + V(HTMLLEGENDELEMENT, HTMLLegendElement) \ + V(HTMLLIELEMENT, HTMLLIElement) \ + V(HTMLLINKELEMENT, HTMLLinkElement) \ + V(HTMLMAPELEMENT, HTMLMapElement) \ + V(HTMLMARQUEEELEMENT, HTMLMarqueeElement) \ + V(HTMLMENUELEMENT, HTMLMenuElement) \ + V(HTMLMETAELEMENT, HTMLMetaElement) \ + V(HTMLMODELEMENT, HTMLModElement) \ + V(HTMLOBJECTELEMENT, HTMLObjectElement) \ + V(HTMLOLISTELEMENT, HTMLOListElement) \ + V(HTMLOPTGROUPELEMENT, HTMLOptGroupElement) \ + V(HTMLOPTIONELEMENT, HTMLOptionElement) \ + V(HTMLPARAGRAPHELEMENT, HTMLParagraphElement) \ + V(HTMLPARAMELEMENT, HTMLParamElement) \ + V(HTMLPREELEMENT, HTMLPreElement) \ + V(HTMLQUOTEELEMENT, HTMLQuoteElement) \ + V(HTMLSCRIPTELEMENT, HTMLScriptElement) \ + V(HTMLSELECTELEMENT, HTMLSelectElement) \ + V(HTMLSTYLEELEMENT, HTMLStyleElement) \ + V(HTMLTABLECAPTIONELEMENT, HTMLTableCaptionElement) \ + V(HTMLTABLECOLELEMENT, HTMLTableColElement) \ + V(HTMLTABLEELEMENT, HTMLTableElement) \ + V(HTMLTABLESECTIONELEMENT, HTMLTableSectionElement) \ + V(HTMLTABLECELLELEMENT, HTMLTableCellElement) \ + V(HTMLTABLEROWELEMENT, HTMLTableRowElement) \ + V(HTMLTEXTAREAELEMENT, HTMLTextAreaElement) \ + V(HTMLTITLEELEMENT, HTMLTitleElement) \ + V(HTMLULISTELEMENT, HTMLUListElement) \ + V(HTMLELEMENT, HTMLElement) + +#if ENABLE(SVG_ANIMATION) +#define SVG_ANIMATION_ELEMENT_TYPES(V) \ + V(SVGANIMATECOLORELEMENT, SVGAnimateColorElement) \ + V(SVGANIMATEELEMENT, SVGAnimateElement) \ + V(SVGANIMATETRANSFORMELEMENT, SVGAnimateTransformElement) \ + V(SVGANIMATIONELEMENT, SVGAnimationElement) \ + V(SVGSETELEMENT, SVGSetElement) +#else +#define SVG_ANIMATION_ELEMENT_TYPES(V) +#endif + +#if ENABLE(SVG_FILTERS) +#define SVG_FILTERS_ELEMENT_TYPES(V) \ + V(SVGCOMPONENTTRANSFERFUNCTIONELEMENT, SVGComponentTransferFunctionElement)\ + V(SVGFEBLENDELEMENT, SVGFEBlendElement) \ + V(SVGFECOLORMATRIXELEMENT, SVGFEColorMatrixElement) \ + V(SVGFECOMPONENTTRANSFERELEMENT, SVGFEComponentTransferElement) \ + V(SVGFECOMPOSITEELEMENT, SVGFECompositeElement) \ + V(SVGFEDIFFUSELIGHTINGELEMENT, SVGFEDiffuseLightingElement) \ + V(SVGFEDISPLACEMENTMAPELEMENT, SVGFEDisplacementMapElement) \ + V(SVGFEDISTANTLIGHTELEMENT, SVGFEDistantLightElement) \ + V(SVGFEFLOODELEMENT, SVGFEFloodElement) \ + V(SVGFEFUNCAELEMENT, SVGFEFuncAElement) \ + V(SVGFEFUNCBELEMENT, SVGFEFuncBElement) \ + V(SVGFEFUNCGELEMENT, SVGFEFuncGElement) \ + V(SVGFEFUNCRELEMENT, SVGFEFuncRElement) \ + V(SVGFEGAUSSIANBLURELEMENT, SVGFEGaussianBlurElement) \ + V(SVGFEIMAGEELEMENT, SVGFEImageElement) \ + V(SVGFEMERGEELEMENT, SVGFEMergeElement) \ + V(SVGFEMERGENODEELEMENT, SVGFEMergeNodeElement) \ + V(SVGFEOFFSETELEMENT, SVGFEOffsetElement) \ + V(SVGFEPOINTLIGHTELEMENT, SVGFEPointLightElement) \ + V(SVGFESPECULARLIGHTINGELEMENT, SVGFESpecularLightingElement) \ + V(SVGFESPOTLIGHTELEMENT, SVGFESpotLightElement) \ + V(SVGFETILEELEMENT, SVGFETileElement) \ + V(SVGFETURBULENCEELEMENT, SVGFETurbulenceElement) \ + V(SVGFILTERELEMENT, SVGFilterElement) +#else +#define SVG_FILTERS_ELEMENT_TYPES(V) +#endif + +#if ENABLE(SVG_FONTS) +#define SVG_FONTS_ELEMENT_TYPES(V) \ + V(SVGDEFINITIONSRCELEMENT, SVGDefinitionSrcElement) \ + V(SVGFONTFACEELEMENT, SVGFontFaceElement) \ + V(SVGFONTFACEFORMATELEMENT, SVGFontFaceFormatElement) \ + V(SVGFONTFACENAMEELEMENT, SVGFontFaceNameElement) \ + V(SVGFONTFACESRCELEMENT, SVGFontFaceSrcElement) \ + V(SVGFONTFACEURIELEMENT, SVGFontFaceUriElement) +#else +#define SVG_FONTS_ELEMENT_TYPES(V) +#endif + +#if ENABLE(SVG_FOREIGN_OBJECT) +#define SVG_FOREIGN_OBJECT_ELEMENT_TYPES(V) \ + V(SVGFOREIGNOBJECTELEMENT, SVGForeignObjectElement) +#else +#define SVG_FOREIGN_OBJECT_ELEMENT_TYPES(V) +#endif + +#if ENABLE(SVG_USE) +#define SVG_USE_ELEMENT_TYPES(V) \ + V(SVGUSEELEMENT, SVGUseElement) +#else +#define SVG_USE_ELEMENT_TYPES(V) +#endif + +#if ENABLE(SVG) +#define SVGELEMENT_TYPES(V) \ + SVG_ANIMATION_ELEMENT_TYPES(V) \ + SVG_FILTERS_ELEMENT_TYPES(V) \ + SVG_FONTS_ELEMENT_TYPES(V) \ + SVG_FOREIGN_OBJECT_ELEMENT_TYPES(V) \ + SVG_USE_ELEMENT_TYPES(V) \ + V(SVGAELEMENT, SVGAElement) \ + V(SVGCIRCLEELEMENT, SVGCircleElement) \ + V(SVGCLIPPATHELEMENT, SVGClipPathElement) \ + V(SVGCURSORELEMENT, SVGCursorElement) \ + V(SVGDEFSELEMENT, SVGDefsElement) \ + V(SVGDESCELEMENT, SVGDescElement) \ + V(SVGELLIPSEELEMENT, SVGEllipseElement) \ + V(SVGGELEMENT, SVGGElement) \ + V(SVGGRADIENTELEMENT, SVGGradientElement) \ + V(SVGIMAGEELEMENT, SVGImageElement) \ + V(SVGLINEARGRADIENTELEMENT, SVGLinearGradientElement) \ + V(SVGLINEELEMENT, SVGLineElement) \ + V(SVGMARKERELEMENT, SVGMarkerElement) \ + V(SVGMASKELEMENT, SVGMaskElement) \ + V(SVGMETADATAELEMENT, SVGMetadataElement) \ + V(SVGPATHELEMENT, SVGPathElement) \ + V(SVGPATTERNELEMENT, SVGPatternElement) \ + V(SVGPOLYGONELEMENT, SVGPolygonElement) \ + V(SVGPOLYLINEELEMENT, SVGPolylineElement) \ + V(SVGRADIALGRADIENTELEMENT, SVGRadialGradientElement) \ + V(SVGRECTELEMENT, SVGRectElement) \ + V(SVGSCRIPTELEMENT, SVGScriptElement) \ + V(SVGSTOPELEMENT, SVGStopElement) \ + V(SVGSTYLEELEMENT, SVGStyleElement) \ + V(SVGSVGELEMENT, SVGSVGElement) \ + V(SVGSWITCHELEMENT, SVGSwitchElement) \ + V(SVGSYMBOLELEMENT, SVGSymbolElement) \ + V(SVGTEXTCONTENTELEMENT, SVGTextContentElement) \ + V(SVGTEXTELEMENT, SVGTextElement) \ + V(SVGTEXTPATHELEMENT, SVGTextPathElement) \ + V(SVGTEXTPOSITIONINGELEMENT, SVGTextPositioningElement) \ + V(SVGTITLEELEMENT, SVGTitleElement) \ + V(SVGTREFELEMENT, SVGTRefElement) \ + V(SVGTSPANELEMENT, SVGTSpanElement) \ + V(SVGVIEWELEMENT, SVGViewElement) \ + V(SVGELEMENT, SVGElement) +#endif + + +#define NONNODE_WRAPPER_TYPES(V) \ + V(BARINFO, BarInfo) \ + V(CANVASGRADIENT, CanvasGradient) \ + V(CANVASPATTERN, CanvasPattern) \ + V(CANVASRENDERINGCONTEXT2D, CanvasRenderingContext2D) \ + V(CLIPBOARD, Clipboard) \ + V(CONSOLE, Console) \ + V(COUNTER, Counter) \ + V(CSSCHARSETRULE, CSSCharsetRule) \ + V(CSSFONTFACERULE, CSSFontFaceRule) \ + V(CSSIMPORTRULE, CSSImportRule) \ + V(CSSMEDIARULE, CSSMediaRule) \ + V(CSSPAGERULE, CSSPageRule) \ + V(CSSPRIMITIVEVALUE, CSSPrimitiveValue) \ + V(CSSRULE, CSSRule) \ + V(CSSRULELIST, CSSRuleList) \ + V(CSSSTYLEDECLARATION, CSSStyleDeclaration) \ + V(CSSSTYLERULE, CSSStyleRule) \ + V(CSSSTYLESHEET, CSSStyleSheet) \ + V(CSSVALUE, CSSValue) \ + V(CSSVALUELIST, CSSValueList) \ + V(DOMCOREEXCEPTION, DOMCoreException) \ + V(DOMIMPLEMENTATION, DOMImplementation) \ + V(DOMPARSER, DOMParser) \ + V(DOMSELECTION, DOMSelection) \ + V(DOMWINDOW, DOMWindow) \ + V(EVENT, Event) \ + V(EVENTEXCEPTION, EventException) \ + V(HISTORY, History) \ + V(UNDETECTABLEHTMLCOLLECTION, UndetectableHTMLCollection) \ + V(HTMLCOLLECTION, HTMLCollection) \ + V(HTMLOPTIONSCOLLECTION, HTMLOptionsCollection) \ + V(INSPECTORCONTROLLER, InspectorController) \ + V(KEYBOARDEVENT, KeyboardEvent) \ + V(LOCATION, Location) \ + V(MEDIALIST, MediaList) \ + V(MESSAGEEVENT, MessageEvent) \ + V(MIMETYPE, MimeType) \ + V(MIMETYPEARRAY, MimeTypeArray) \ + V(MOUSEEVENT, MouseEvent) \ + V(MUTATIONEVENT, MutationEvent) \ + V(NAMEDNODEMAP, NamedNodeMap) \ + V(NAVIGATOR, Navigator) \ + V(NODEFILTER, NodeFilter) \ + V(NODEITERATOR, NodeIterator) \ + V(NODELIST, NodeList) \ + V(OVERFLOWEVENT, OverflowEvent) \ + V(PLUGIN, Plugin) \ + V(PLUGINARRAY, PluginArray) \ + V(PROGRESSEVENT, ProgressEvent) \ + V(RANGE, Range) \ + V(RANGEEXCEPTION, RangeException) \ + V(RECT, Rect) \ + V(RGBCOLOR, RGBColor) \ + V(SCREEN, Screen) \ + V(STYLESHEET, StyleSheet) \ + V(STYLESHEETLIST, StyleSheetList) \ + V(TEXTEVENT, TextEvent) \ + V(TREEWALKER, TreeWalker) \ + V(UIEVENT, UIEvent) \ + V(WHEELEVENT, WheelEvent) \ + V(XMLHTTPREQUEST, XMLHttpRequest) \ + V(XMLHTTPREQUESTEXCEPTION, XMLHttpRequestException) \ + V(XMLSERIALIZER, XMLSerializer) \ + V(XPATHEVALUATOR, XPathEvaluator) \ + V(XPATHEXCEPTION, XPathException) \ + V(XPATHEXPRESSION, XPathExpression) \ + V(XPATHNSRESOLVER, XPathNSResolver) \ + V(XPATHRESULT, XPathResult) \ + V(XSLTPROCESSOR, XSLTProcessor) + +#if ENABLE(SVG) +#define SVGNODE_WRAPPER_TYPES(V) \ + V(SVGDOCUMENT, SVGDocument) + +#define SVGNONNODE_WRAPPER_TYPES(V) \ + V(SVGANGLE, SVGAngle) \ + V(SVGANIMATEDANGLE, SVGAnimatedAngle) \ + V(SVGANIMATEDBOOLEAN, SVGAnimatedBoolean) \ + V(SVGANIMATEDENUMERATION, SVGAnimatedEnumeration) \ + V(SVGANIMATEDINTEGER, SVGAnimatedInteger) \ + V(SVGANIMATEDLENGTH, SVGAnimatedLength) \ + V(SVGANIMATEDLENGTHLIST, SVGAnimatedLengthList) \ + V(SVGANIMATEDNUMBER, SVGAnimatedNumber) \ + V(SVGANIMATEDNUMBERLIST, SVGAnimatedNumberList) \ + V(SVGANIMATEDPOINTS, SVGAnimatedPoints) \ + V(SVGANIMATEDPRESERVEASPECTRATIO, SVGAnimatedPreserveAspectRatio) \ + V(SVGANIMATEDRECT, SVGAnimatedRect) \ + V(SVGANIMATEDSTRING, SVGAnimatedString) \ + V(SVGANIMATEDTRANSFORMLIST, SVGAnimatedTransformList) \ + V(SVGCOLOR, SVGColor) \ + V(SVGELEMENTINSTANCE, SVGElementInstance) \ + V(SVGELEMENTINSTANCELIST, SVGElementInstanceList) \ + V(SVGEXCEPTION, SVGException) \ + V(SVGLENGTH, SVGLength) \ + V(SVGLENGTHLIST, SVGLengthList) \ + V(SVGMATRIX, SVGMatrix) \ + V(SVGNUMBER, SVGNumber) \ + V(SVGNUMBERLIST, SVGNumberList) \ + V(SVGPAINT, SVGPaint) \ + V(SVGPATHSEG, SVGPathSeg) \ + V(SVGPATHSEGARCABS, SVGPathSegArcAbs) \ + V(SVGPATHSEGARCREL, SVGPathSegArcRel) \ + V(SVGPATHSEGCLOSEPATH, SVGPathSegClosePath) \ + V(SVGPATHSEGCURVETOCUBICABS, SVGPathSegCurvetoCubicAbs) \ + V(SVGPATHSEGCURVETOCUBICREL, SVGPathSegCurvetoCubicRel) \ + V(SVGPATHSEGCURVETOCUBICSMOOTHABS, SVGPathSegCurvetoCubicSmoothAbs) \ + V(SVGPATHSEGCURVETOCUBICSMOOTHREL, SVGPathSegCurvetoCubicSmoothRel) \ + V(SVGPATHSEGCURVETOQUADRATICABS, SVGPathSegCurvetoQuadraticAbs) \ + V(SVGPATHSEGCURVETOQUADRATICREL, SVGPathSegCurvetoQuadraticRel) \ + V(SVGPATHSEGCURVETOQUADRATICSMOOTHABS, SVGPathSegCurvetoQuadraticSmoothAbs)\ + V(SVGPATHSEGCURVETOQUADRATICSMOOTHREL, SVGPathSegCurvetoQuadraticSmoothRel)\ + V(SVGPATHSEGLINETOABS, SVGPathSegLinetoAbs) \ + V(SVGPATHSEGLINETOHORIZONTALABS, SVGPathSegLinetoHorizontalAbs) \ + V(SVGPATHSEGLINETOHORIZONTALREL, SVGPathSegLinetoHorizontalRel) \ + V(SVGPATHSEGLINETOREL, SVGPathSegLinetoRel) \ + V(SVGPATHSEGLINETOVERTICALABS, SVGPathSegLinetoVerticalAbs) \ + V(SVGPATHSEGLINETOVERTICALREL, SVGPathSegLinetoVerticalRel) \ + V(SVGPATHSEGLIST, SVGPathSegList) \ + V(SVGPATHSEGMOVETOABS, SVGPathSegMovetoAbs) \ + V(SVGPATHSEGMOVETOREL, SVGPathSegMovetoRel) \ + V(SVGPOINT, SVGPoint) \ + V(SVGPOINTLIST, SVGPointList) \ + V(SVGPRESERVEASPECTRATIO, SVGPreserveAspectRatio) \ + V(SVGRECT, SVGRect) \ + V(SVGRENDERINGINTENT, SVGRenderingIntent) \ + V(SVGSTRINGLIST, SVGStringList) \ + V(SVGTRANSFORM, SVGTransform) \ + V(SVGTRANSFORMLIST, SVGTransformList) \ + V(SVGUNITTYPES, SVGUnitTypes) \ + V(SVGURIREFERENCE, SVGURIReference) \ + V(SVGZOOMEVENT, SVGZoomEvent) +#endif + +// EVENTTARGET, EVENTLISTENER, and NPOBJECT do not have V8 wrappers. +#define NO_WRAPPER_TYPES(V) \ + V(EVENTTARGET, EventTarget) \ + V(EVENTLISTENER, EventListener) \ + V(NPOBJECT, NPObject) + +#if ENABLE(SVG) +#define WRAPPER_TYPES(V) \ + NODE_WRAPPER_TYPES(V) \ + HTMLELEMENT_TYPES(V) \ + NONNODE_WRAPPER_TYPES(V) \ + SVGNODE_WRAPPER_TYPES(V) \ + SVGELEMENT_TYPES(V) \ + SVGNONNODE_WRAPPER_TYPES(V) +#else +#define WRAPPER_TYPES(V) \ + NODE_WRAPPER_TYPES(V) \ + HTMLELEMENT_TYPES(V) \ + NONNODE_WRAPPER_TYPES(V) +#endif + +#define ALL_WRAPPER_TYPES(V) \ + WRAPPER_TYPES(V) \ + NO_WRAPPER_TYPES(V) + +class V8ClassIndex { + public: + // Type must start at non-negative numbers. See ToInt, FromInt. + enum V8WrapperType { + INVALID_CLASS_INDEX = 0, +#define DEFINE_ENUM(name, type) name, +ALL_WRAPPER_TYPES(DEFINE_ENUM) +#undef DEFINE_ENUM + CLASSINDEX_END, + }; + static inline V8WrapperType ToWrapperType(v8::Handle<v8::Value> obj) { + return static_cast<V8WrapperType>( + reinterpret_cast<int>(v8::Handle<v8::External>::Cast(obj)->Value())); + } + + static int ToInt(V8WrapperType type) { return static_cast<int>(type); } + + static V8WrapperType FromInt(int v) { + ASSERT(INVALID_CLASS_INDEX <= v && v < CLASSINDEX_END); + return static_cast<V8WrapperType>(v); + } + + static FunctionTemplateFactory GetFactory(V8WrapperType type); + // Returns a field to be used as cache for the template for the given type + static v8::Persistent<v8::FunctionTemplate>* GetCache(V8WrapperType type); +}; +} + +#endif // V8_INDEX_H__ diff --git a/webkit/port/bindings/v8/v8_nodefilter.cpp b/webkit/port/bindings/v8/v8_nodefilter.cpp new file mode 100644 index 0000000..c8846ab --- /dev/null +++ b/webkit/port/bindings/v8/v8_nodefilter.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2008, 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 "v8_nodefilter.h" +#include "v8_proxy.h" +#include "NodeFilter.h" +#include "Node.h" + +namespace WebCore { + +V8NodeFilterCondition::V8NodeFilterCondition(v8::Handle<v8::Value> filter) { + m_filter = v8::Persistent<v8::Value>::New(filter); +#ifndef NDEBUG + V8Proxy::RegisterGlobalHandle(NODE_FILTER, this, m_filter); +#endif +} + +V8NodeFilterCondition::~V8NodeFilterCondition() { +#ifndef NDEBUG + V8Proxy::UnregisterGlobalHandle(this, m_filter); +#endif + m_filter.Dispose(); + m_filter.Clear(); +} + +short V8NodeFilterCondition::acceptNode(Node* node) const { + ASSERT(v8::Context::InContext()); + + if (!m_filter->IsFunction()) return NodeFilter::FILTER_ACCEPT; + + v8::TryCatch exception_catcher; + + v8::Handle<v8::Object> this_obj = v8::Context::GetCurrent()->Global(); + v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(m_filter); + v8::Handle<v8::Value>* args = new v8::Handle<v8::Value>[1]; + args[0] = V8Proxy::ToV8Object(V8ClassIndex::NODE, node); + + V8Proxy* proxy = V8Proxy::retrieve(); + ASSERT(proxy); + + v8::Handle<v8::Value> result = + proxy->CallFunction(callback, this_obj, 1, args); + delete[] args; + + // TODO(fqian): this code can be removed when issue 1042294 is fixed. + // See also 1068243. + if (exception_catcher.HasCaught()) { + return NodeFilter::FILTER_REJECT; + } + + ASSERT(!result.IsEmpty()); + + return result->Int32Value(); +} + +} // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_nodefilter.h b/webkit/port/bindings/v8/v8_nodefilter.h new file mode 100644 index 0000000..01f2023 --- /dev/null +++ b/webkit/port/bindings/v8/v8_nodefilter.h @@ -0,0 +1,54 @@ +// Copyright 2008, 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. + +#ifndef V8_NODEFILTER_H__ +#define V8_NODEFILTER_H__ + +#include <v8.h> +#include "NodeFilterCondition.h" + +// NodeFilter is a JavaScript function that takes a Node as parameter +// and returns a short (ACCEPT, SKIP, REJECT) as the result. +namespace WebCore { +class Node; + +// NodeFilterCondition is a wrapper around a NodeFilter JS function. +class V8NodeFilterCondition : public NodeFilterCondition { + public: + explicit V8NodeFilterCondition(v8::Handle<v8::Value> filter); + virtual ~V8NodeFilterCondition(); + + virtual short acceptNode(Node* node) const; + + private: + mutable v8::Persistent<v8::Value> m_filter; +}; + +} // namesapce WebCore +#endif // V8_NODEFILTER_H__ diff --git a/webkit/port/bindings/v8/v8_np_utils.cpp b/webkit/port/bindings/v8/v8_np_utils.cpp new file mode 100644 index 0000000..06e02b8 --- /dev/null +++ b/webkit/port/bindings/v8/v8_np_utils.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2008, 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 "v8_np_utils.h" + +#include "base/string_util.h" +#include "DOMWindow.h" +#include "Frame.h" +#include "npruntime_priv.h" +#include "np_v8object.h" +#include "v8_npobject.h" +#include "v8_proxy.h" + +void ConvertV8ObjectToNPVariant(v8::Local<v8::Value> object, NPObject *owner, + NPVariant* result) { + VOID_TO_NPVARIANT(*result); + + // It is really the caller's responsibility to deal with the empty handle + // case because there could be different actions to take in different + // contexts. + ASSERT(!object.IsEmpty()); + + if (object.IsEmpty()) return; + + if (object->IsNumber()) { + DOUBLE_TO_NPVARIANT(object->NumberValue(), *result); + + } else if (object->IsBoolean()) { + BOOLEAN_TO_NPVARIANT(object->BooleanValue(), *result); + + } else if (object->IsNull()) { + NULL_TO_NPVARIANT(*result); + + } else if (object->IsUndefined()) { + VOID_TO_NPVARIANT(*result); + + } else if (object->IsString()) { + v8::Handle<v8::String> str = object->ToString(); + uint16_t *buf = new uint16_t[str->Length()+1]; + str->Write(buf); + std::string utf8 = WideToUTF8(reinterpret_cast<wchar_t*>(buf)); + char* utf8_chars = strdup(utf8.c_str()); + STRINGN_TO_NPVARIANT(utf8_chars, utf8.length(), *result); + delete[] buf; + + } else if (object->IsObject()) { + WebCore::DOMWindow* window = WebCore::V8Proxy::retrieveWindow(); + NPObject* npobject = NPN_CreateScriptObject( + 0, v8::Handle<v8::Object>::Cast(object), window); + if (npobject) { + _NPN_RegisterObject(npobject, owner); + } + OBJECT_TO_NPVARIANT(npobject, *result); + } +} + + +v8::Handle<v8::Value> ConvertNPVariantToV8Object(const NPVariant* variant, + NPObject* npobject) { + NPVariantType type = variant->type; + + if (type == NPVariantType_Int32) { + return v8::Integer::New(NPVARIANT_TO_INT32(*variant)); + } + if (type == NPVariantType_Double) { + return v8::Number::New(NPVARIANT_TO_DOUBLE(*variant)); + } + if (type == NPVariantType_Bool) { + return NPVARIANT_TO_BOOLEAN(*variant) ? v8::True() : v8::False(); + } + if (type == NPVariantType_Null) { + return v8::Null(); + } + if (type == NPVariantType_Void) { + return v8::Undefined(); + } + if (type == NPVariantType_String) { + NPString src = NPVARIANT_TO_STRING(*variant); + return v8::String::New(src.UTF8Characters, src.UTF8Length); + } + if (type == NPVariantType_Object) { + NPObject* obj = NPVARIANT_TO_OBJECT(*variant); + if (obj->_class == NPScriptObjectClass) { + return reinterpret_cast<V8NPObject*>(obj)->v8_object; + } + return CreateV8ObjectForNPObject(obj, npobject); + } + return v8::Undefined(); +} + +// Helper function to create an NPN String Identifier from a v8 string. +NPIdentifier GetStringIdentifier(v8::Handle<v8::String> str) { + char *buf = new char[str->Length() + 1]; + str->WriteAscii(buf); + NPIdentifier ident = NPN_GetStringIdentifier(buf); + delete[] buf; + return ident; +} diff --git a/webkit/port/bindings/v8/v8_np_utils.h b/webkit/port/bindings/v8/v8_np_utils.h new file mode 100644 index 0000000..5124571 --- /dev/null +++ b/webkit/port/bindings/v8/v8_np_utils.h @@ -0,0 +1,54 @@ +// Copyright 2008, 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. + +#ifndef V8_NP_UTILS_H__ +#define V8_NP_UTILS_H__ + +#include <v8.h> +#include "bindings/npruntime.h" + +namespace WebCore { + class Frame; +} + +// Convert a V8 Value of any type (string, bool, object, etc) to a NPVariant. +void ConvertV8ObjectToNPVariant(v8::Local<v8::Value> object, NPObject *owner, + NPVariant* result); + +// Convert a NPVariant (string, bool, object, etc) back to a V8 Value. +// The owner object is the NPObject which relates to the object, if the object +// is an Object. The created NPObject will be tied to the lifetime of the +// owner. +v8::Handle<v8::Value> ConvertNPVariantToV8Object(const NPVariant* value, + NPObject* owner); + +// Helper function to create an NPN String Identifier from a v8 string. +NPIdentifier GetStringIdentifier(v8::Handle<v8::String> str); + +#endif // V8_NP_UTILS_H__ diff --git a/webkit/port/bindings/v8/v8_npobject.cpp b/webkit/port/bindings/v8/v8_npobject.cpp new file mode 100644 index 0000000..eecc753 --- /dev/null +++ b/webkit/port/bindings/v8/v8_npobject.cpp @@ -0,0 +1,364 @@ +// Copyright (c) 2008, 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 "v8_helpers.h" +#include "v8_npobject.h" +#include "v8_np_utils.h" +#include "np_v8object.h" +#include "npruntime_priv.h" +#include "v8_proxy.h" +#include "dom_wrapper_map.h" +#include "HTMLPlugInElement.h" +#include "V8HTMLAppletElement.h" +#include "V8HTMLEmbedElement.h" +#include "V8HTMLObjectElement.h" + +using namespace WebCore; + +enum InvokeFunctionType { + INVOKE_METHOD = 1, + INVOKE_DEFAULT = 2 +}; + +// TODO(mbelshe): need comments. +// Params: holder could be HTMLEmbedElement or NPObject +static v8::Handle<v8::Value> NPObjectInvokeImpl( + const v8::Arguments& args, InvokeFunctionType func_id) { + NPObject* npobject; + + // These three types are subtypes of HTMLPlugInElement. + if (V8HTMLAppletElement::HasInstance(args.Holder()) || + V8HTMLEmbedElement::HasInstance(args.Holder()) || + V8HTMLObjectElement::HasInstance(args.Holder())) { + // The holder object is a subtype of HTMLPlugInElement. + HTMLPlugInElement* imp = V8Proxy::FastToNativeObject<HTMLPlugInElement>( + V8ClassIndex::NODE, args.Holder()); + v8::Handle<v8::Object> instance = imp->getInstance(); + npobject = V8Proxy::ToNativeObject<NPObject>( + V8ClassIndex::NPOBJECT, instance); + + } else { + // The holder object is not a subtype of HTMLPlugInElement, it + // must be an NPObject which has three internal fields. + ASSERT(args.Holder()->InternalFieldCount() == 3); + npobject = V8Proxy::ToNativeObject<NPObject>( + V8ClassIndex::NPOBJECT, args.Holder()); + } + + // Verify that our wrapper wasn't using a NPObject which + // has already been deleted. + if (!npobject || !_NPN_IsAlive(npobject)) { + V8Proxy::ThrowError(V8Proxy::REFERENCE_ERROR, "NPObject deleted"); + return v8::Undefined(); + } + + // wrap up parameters + int argc = args.Length(); + NPVariant* np_args = new NPVariant[argc]; + + for (int i = 0; i < argc; i++) { + ConvertV8ObjectToNPVariant(args[i], npobject, &np_args[i]); + } + + NPVariant result; + VOID_TO_NPVARIANT(result); + + switch (func_id) { + case INVOKE_METHOD: + if (npobject->_class->invoke) { + v8::Handle<v8::String> function_name(v8::String::Cast(*args.Data())); + NPIdentifier ident = GetStringIdentifier(function_name); + npobject->_class->invoke(npobject, ident, np_args, argc, &result); + } + break; + case INVOKE_DEFAULT: + if (npobject->_class->invokeDefault) { + npobject->_class->invokeDefault(npobject, np_args, argc, &result); + } + break; + default: + break; + } + + for (int i=0; i < argc; i++) { + NPN_ReleaseVariantValue(&np_args[i]); + } + delete[] np_args; + + // unwrap return values + v8::Handle<v8::Value> rv = ConvertNPVariantToV8Object(&result, npobject); + NPN_ReleaseVariantValue(&result); + + return rv; +} + + +v8::Handle<v8::Value> NPObjectMethodHandler(const v8::Arguments& args) { + return NPObjectInvokeImpl(args, INVOKE_METHOD); +} + + +v8::Handle<v8::Value> NPObjectInvokeDefaultHandler(const v8::Arguments& args) { + return NPObjectInvokeImpl(args, INVOKE_DEFAULT); +} + + +static void WeakTemplateCallback(v8::Persistent<v8::Object> obj, void* param); + +// NPIdentifier is PrivateIdentifier*. +static WeakReferenceMap<PrivateIdentifier, v8::FunctionTemplate> \ + static_template_map(&WeakTemplateCallback); + +static void WeakTemplateCallback(v8::Persistent<v8::Object> obj, + void* param) { + PrivateIdentifier* iden = static_cast<PrivateIdentifier*>(param); + ASSERT(iden != NULL); + ASSERT(static_template_map.contains(iden)); + + static_template_map.forget(iden); +} + + +static v8::Handle<v8::Value> NPObjectGetProperty(v8::Local<v8::Object> self, + NPIdentifier ident, + v8::Local<v8::Value> key) { + NPObject* npobject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, + self); + + // Verify that our wrapper wasn't using a NPObject which + // has already been deleted. + if (!npobject || !_NPN_IsAlive(npobject)) { + V8Proxy::ThrowError(V8Proxy::REFERENCE_ERROR, "NPObject deleted"); + return v8::Handle<v8::Value>(); + } + + if (npobject->_class->hasProperty && + npobject->_class->hasProperty(npobject, ident) && + npobject->_class->getProperty) { + NPVariant result; + VOID_TO_NPVARIANT(result); + if (!npobject->_class->getProperty(npobject, ident, &result)) { + return v8::Handle<v8::Value>(); + } + + v8::Handle<v8::Value> rv = ConvertNPVariantToV8Object(&result, npobject); + NPN_ReleaseVariantValue(&result); + return rv; + + } else if (key->IsString() && + npobject->_class->hasMethod && + npobject->_class->hasMethod(npobject, ident)) { + PrivateIdentifier* id = static_cast<PrivateIdentifier*>(ident); + v8::Persistent<v8::FunctionTemplate> desc = static_template_map.get(id); + // Cache templates using identifier as the key. + if (desc.IsEmpty()) { + // Create a new template + v8::Local<v8::FunctionTemplate> temp = v8::FunctionTemplate::New(); + temp->SetCallHandler(NPObjectMethodHandler, key); + desc = v8::Persistent<v8::FunctionTemplate>::New(temp); + static_template_map.set(id, desc); + } + + // FunctionTemplate caches function for each context. + v8::Local<v8::Function> func = desc->GetFunction(); + func->SetName(v8::Handle<v8::String>::Cast(key)); + return func; + } + + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> NPObjectNamedPropertyGetter(v8::Local<v8::String> name, + const v8::AccessorInfo& info) { + NPIdentifier ident = GetStringIdentifier(name); + return NPObjectGetProperty(info.Holder(), ident, name); +} + +v8::Handle<v8::Value> NPObjectIndexedPropertyGetter(uint32_t index, + const v8::AccessorInfo& info) { + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectGetProperty(info.Holder(), ident, v8::Number::New(index)); +} + +v8::Handle<v8::Value> NPObjectGetNamedProperty(v8::Local<v8::Object> self, + v8::Local<v8::String> name) { + NPIdentifier ident = GetStringIdentifier(name); + return NPObjectGetProperty(self, ident, name); +} + +v8::Handle<v8::Value> NPObjectGetIndexedProperty(v8::Local<v8::Object> self, + uint32_t index) { + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectGetProperty(self, ident, v8::Number::New(index)); +} + +static v8::Handle<v8::Value> NPObjectSetProperty(v8::Local<v8::Object> self, + NPIdentifier ident, + v8::Local<v8::Value> value) { + NPObject* npobject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, + self); + + // Verify that our wrapper wasn't using a NPObject which + // has already been deleted. + if (!npobject || !_NPN_IsAlive(npobject)) { + V8Proxy::ThrowError(V8Proxy::REFERENCE_ERROR, "NPObject deleted"); + return value; // intercepted, but an exception was thrown + } + + if (npobject->_class->hasProperty && + npobject->_class->hasProperty(npobject, ident) && + npobject->_class->setProperty) { + NPVariant npvalue; + VOID_TO_NPVARIANT(npvalue); + ConvertV8ObjectToNPVariant(value, npobject, &npvalue); + bool succ = npobject->_class->setProperty(npobject, ident, &npvalue); + NPN_ReleaseVariantValue(&npvalue); + if (succ) return value; // intercept the call + } + return v8::Local<v8::Value>(); // do not intercept the call +} + + +v8::Handle<v8::Value> NPObjectNamedPropertySetter(v8::Local<v8::String> name, + v8::Local<v8::Value> value, + const v8::AccessorInfo& info) { + NPIdentifier ident = GetStringIdentifier(name); + return NPObjectSetProperty(info.Holder(), ident, value); +} + + +v8::Handle<v8::Value> NPObjectIndexedPropertySetter(uint32_t index, + v8::Local<v8::Value> value, + const v8::AccessorInfo& info) { + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectSetProperty(info.Holder(), ident, value); +} + +v8::Handle<v8::Value> NPObjectSetNamedProperty(v8::Local<v8::Object> self, + v8::Local<v8::String> name, + v8::Local<v8::Value> value) { + NPIdentifier ident = GetStringIdentifier(name); + return NPObjectSetProperty(self, ident, value); +} + +v8::Handle<v8::Value> NPObjectSetIndexedProperty(v8::Local<v8::Object> self, + uint32_t index, + v8::Local<v8::Value> value) { + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectSetProperty(self, ident, value); +} + + +static void WeakNPObjectCallback(v8::Persistent<v8::Object> obj, void* param); + +static DOMWrapperMap<NPObject> static_npobject_map(&WeakNPObjectCallback); + +static void WeakNPObjectCallback(v8::Persistent<v8::Object> obj, + void* param) { + NPObject* npobject = static_cast<NPObject*>(param); + ASSERT(static_npobject_map.contains(npobject)); + ASSERT(npobject != NULL); + + // Must remove from our map before calling NPN_ReleaseObject(). + // NPN_ReleaseObject can call ForgetV8ObjectForNPObject, which + // uses the table as well. + static_npobject_map.forget(npobject); + + if (_NPN_IsAlive(npobject)) + NPN_ReleaseObject(npobject); +} + + +v8::Local<v8::Object> CreateV8ObjectForNPObject(NPObject* object, + NPObject* root) { + static v8::Persistent<v8::FunctionTemplate> np_object_desc; + + ASSERT(v8::Context::InContext()); + + // If this is a v8 object, just return it. + if (object->_class == NPScriptObjectClass) { + V8NPObject* v8npobject = reinterpret_cast<V8NPObject*>(object); + return v8::Local<v8::Object>::New(v8npobject->v8_object); + } + + // If we've already wrapped this object, just return it. + if (static_npobject_map.contains(object)) + return v8::Local<v8::Object>::New(static_npobject_map.get(object)); + + // TODO: we should create a Wrapper type as a subclass of JSObject. + // It has two internal fields, field 0 is the wrapped pointer, + // and field 1 is the type. There should be an api function that + // returns unused type id. + // The same Wrapper type can be used by DOM bindings. + if (np_object_desc.IsEmpty()) { + np_object_desc = + v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); + np_object_desc->InstanceTemplate()->SetInternalFieldCount(3); + np_object_desc->InstanceTemplate()->SetNamedPropertyHandler( + NPObjectNamedPropertyGetter, NPObjectNamedPropertySetter); + np_object_desc->InstanceTemplate()->SetIndexedPropertyHandler( + NPObjectIndexedPropertyGetter, NPObjectIndexedPropertySetter); + np_object_desc->InstanceTemplate()->SetCallAsFunctionHandler( + NPObjectInvokeDefaultHandler); + } + + v8::Handle<v8::Function> func = np_object_desc->GetFunction(); + v8::Local<v8::Object> value = SafeAllocation::NewInstance(func); + + // If we were unable to allocate the instance we avoid wrapping + // and registering the NP object. + if (value.IsEmpty()) + return value; + + WrapNPObject(value, object); + + // KJS retains the object as part of its wrapper (see Bindings::CInstance) + NPN_RetainObject(object); + + _NPN_RegisterObject(object, root); + + // Maintain a weak pointer for v8 so we can cleanup the object. + v8::Persistent<v8::Object> weak_ref = v8::Persistent<v8::Object>::New(value); + static_npobject_map.set(object, weak_ref); + + return value; +} + +void ForgetV8ObjectForNPObject(NPObject* object) { + if (static_npobject_map.contains(object)) { + v8::HandleScope scope; + v8::Persistent<v8::Object> handle(static_npobject_map.get(object)); + WebCore::V8Proxy::SetDOMWrapper(handle, + WebCore::V8ClassIndex::NPOBJECT, NULL); + static_npobject_map.forget(object); + NPN_ReleaseObject(object); + } +} diff --git a/webkit/port/bindings/v8/v8_npobject.h b/webkit/port/bindings/v8/v8_npobject.h new file mode 100644 index 0000000..3ff7d31 --- /dev/null +++ b/webkit/port/bindings/v8/v8_npobject.h @@ -0,0 +1,79 @@ +// Copyright 2008, 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. + +#ifndef V8_NPOBJECT_H__ +#define V8_NPOBJECT_H__ + +#include <v8.h> +#include "bindings/npruntime.h" + +// These functions can be replaced by normal JS operation. +// Getters +v8::Handle<v8::Value> NPObjectNamedPropertyGetter(v8::Local<v8::String> name, + const v8::AccessorInfo& info); +v8::Handle<v8::Value> NPObjectIndexedPropertyGetter(uint32_t index, + const v8::AccessorInfo& info); +v8::Handle<v8::Value> NPObjectGetNamedProperty(v8::Local<v8::Object> self, + v8::Local<v8::String> name); +v8::Handle<v8::Value> NPObjectGetIndexedProperty(v8::Local<v8::Object> self, + uint32_t index); + +// Setters +v8::Handle<v8::Value> NPObjectNamedPropertySetter(v8::Local<v8::String> name, + v8::Local<v8::Value> value, + const v8::AccessorInfo& info); +v8::Handle<v8::Value> NPObjectIndexedPropertySetter(uint32_t index, + const v8::AccessorInfo& info); +v8::Handle<v8::Value> NPObjectSetNamedProperty(v8::Local<v8::Object> self, + v8::Local<v8::String> name, + v8::Local<v8::Value> value); +v8::Handle<v8::Value> NPObjectSetIndexedProperty(v8::Local<v8::Object> self, + uint32_t index, + v8::Local<v8::Value> value); + +v8::Handle<v8::Value> NPObjectInvokeDefaultHandler(const v8::Arguments& args); + +// Get a wrapper for a NPObject. +// If the object is already wrapped, the pre-existing wrapper +// will be returned. +// If the object is not wrapped, wrap it, and give V8 a weak +// reference to the wrapper which will cleanup when there are +// no more JS references to the object. +v8::Local<v8::Object> CreateV8ObjectForNPObject(NPObject* object, + NPObject *root); + +// Tell V8 to forcibly remove an object. +// This is used at plugin teardown so that the caller can +// aggressively unload the plugin library. After calling this +// function, the persistent handle to the wrapper will be +// gone, and the wrapped NPObject will be removed so that +// it cannot be referred to. +void ForgetV8ObjectForNPObject(NPObject*object); + +#endif // V8_NPOBJECT_H__ diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp new file mode 100644 index 0000000..be8c7ff --- /dev/null +++ b/webkit/port/bindings/v8/v8_proxy.cpp @@ -0,0 +1,2557 @@ +// Copyright (c) 2008, 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 <v8.h> + +#include "v8_proxy.h" +#include "dom_wrapper_map.h" +#include "v8_index.h" +#include "v8_events.h" +#include "v8_binding.h" +#include "v8_custom.h" +#include "v8_nodefilter.h" +#include "V8Bridge.h" + +#include "RefCounted.h" // for Peerable + +#include "DOMCoreException.h" +#include "EventException.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "HTMLDocument.h" +#include "HTMLElement.h" +#include "HTMLImageElement.h" +#include "HTMLSelectElement.h" +#include "HTMLOptionsCollection.h" +#include "Page.h" +#include "DOMWindow.h" +#include "Navigator.h" // for MimeTypeArray +#include "V8DOMWindow.h" +#include "V8HTMLElement.h" +#include "Entity.h" +#include "MediaList.h" +#include "NodeList.h" +#include "Notation.h" +#include "Text.h" +#include "ProcessingInstruction.h" +#include "CharacterData.h" +#include "DocumentType.h" +#include "DocumentFragment.h" +#include "EventListener.h" +#include "EventTargetNode.h" +#include "EventTarget.h" +#include "Event.h" +#include "HTMLInputElement.h" +#include "xmlhttprequest.h" +#include "StyleSheet.h" +#include "StyleSheetList.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSValueList.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "RangeException.h" +#include "NodeFilter.h" +#include "SecurityOrigin.h" +#include "XMLHttpRequestException.h" +#include "XPathException.h" + +#if ENABLE(SVG) +#include "SVGElement.h" +#include "SVGElementInstance.h" +#include "SVGException.h" +#endif + +#if ENABLE(XPATH) +#include "XPathEvaluator.h" +#endif + +#include "base/stats_table.h" +#include "webkit/glue/glue_util.h" + +namespace WebCore { + +// DOM binding algorithm: +// +// There are two kinds of DOM objects: +// 1. DOM tree nodes, such as Document, HTMLElement, ... +// there classes implements TreeShared<T> interface; +// 2. Non-node DOM objects, such as CSSRule, Location, etc. +// these classes implements RefCounted<T> interface. +// +// A DOM object may have a JS wrapper object. If a tree node +// is alive, its JS wrapper must be kept alive even it is not +// reachable from JS roots. +// However, JS wrappers of non-node objects can go away if +// not reachable from other JS objects. It works like a cache. +// +// DOM objects are ref-counted, and JS objects are traced from +// a set of root objects. They can create a cycle. To break +// cycles, we do following: +// Peer from DOM objects to JS wrappers are always weak, +// so JS wrappers of non-node object cannot create a cycle. +// Before starting a global GC, we create a virtual connection +// between nodes in the same tree in the JS heap. If the wrapper +// of one node in a tree is alive, wrappers of all nodes in +// the same tree are considered alive. This is done by creating +// object groups in GC prologue callbacks. The mark-compact +// collector will remove these groups after each GC. + + +#ifndef NDEBUG +// Keeps track of global handles created (not JS wrappers +// of DOM objects). Often these global handles are source +// of leaks. +// +// If you want to let a C++ object hold a persistent handle +// to a JS object, you should register the handle here to +// keep track of leaks. +// +// When creating a persistent handle, call: +// +// #ifndef NDEBUG +// V8Proxy::RegisterGlobalHandle(type, host, handle); +// #endif +// +// When releasing the handle, call: +// +// #ifndef NDEBUG +// V8Proxy::UnregisterGlobalHandle(type, host, handle); +// #endif +// +typedef HashMap<v8::Value*, GlobalHandleInfo*> GlobalHandleMap; + +static GlobalHandleMap& global_handle_map() { + static GlobalHandleMap static_global_handle_map; + return static_global_handle_map; +} + + +// The function is the place to set the break point to inspect +// live global handles. Leaks are often come from leaked global handles. +static void EnumerateGlobalHandles() { + for (GlobalHandleMap::iterator it = global_handle_map().begin(), + end = global_handle_map().end(); it != end; ++it) { + GlobalHandleInfo* info = it->second; + v8::Value* handle = it->first; + } +} + + +void V8Proxy::RegisterGlobalHandle(GlobalHandleType type, void* host, + v8::Persistent<v8::Value> handle) { + ASSERT(!global_handle_map().contains(*handle)); + global_handle_map().set(*handle, new GlobalHandleInfo(host, type)); +} + + +void V8Proxy::UnregisterGlobalHandle(void* host, + v8::Persistent<v8::Value> handle) { + ASSERT(global_handle_map().contains(*handle)); + GlobalHandleInfo* info = global_handle_map().take(*handle); + ASSERT(info->host_ == host); + delete info; +} +#endif + + +typedef HashMap<Node*, v8::Object*> NodeMap; +typedef HashMap<Peerable*, v8::Object*> PeerableMap; + +// Type T must implement Peerable interface. +template<class T> +class DOMPeerableWrapperMap : public DOMWrapperMap<T> { + public: + explicit DOMPeerableWrapperMap(v8::WeakReferenceCallback callback) : + DOMWrapperMap(callback) { } + + // Get the JS wrapper object of an object. + v8::Persistent<v8::Object> get(T* obj) { + v8::Object* peer = static_cast<v8::Object*>(obj->peer()); + ASSERT(peer == map_.get(obj)); + return peer ? v8::Persistent<v8::Object>(peer) + : v8::Persistent<v8::Object>(); + } + + void set(T* obj, v8::Persistent<v8::Object> peer_handle) { + ASSERT(obj->peer() == 0); + obj->setPeer(*peer_handle); + DOMWrapperMap<T>::set(obj, peer_handle); + } + + void forget(T* obj) { + v8::Object* peer = static_cast<v8::Object*>(obj->peer()); + ASSERT(peer == map_.get(obj)); + if (peer) + obj->setPeer(0); + DOMWrapperMap<T>::forget(obj); + } +}; + + +static void WeakPeerableCallback(v8::Persistent<v8::Object> obj, void* para); +static void WeakNodeCallback(v8::Persistent<v8::Object> obj, void* para); +// A map from DOM node to its JS wrapper. +static DOMWrapperMap<Node>& dom_node_map() { + static DOMPeerableWrapperMap<Node> static_dom_node_map(&WeakNodeCallback); + return static_dom_node_map; +} + + +// A map from a non-DOM node (peerable) to its JS wrapper. +static DOMWrapperMap<Peerable>& dom_object_map() { + static DOMPeerableWrapperMap<Peerable> + static_dom_object_map(&WeakPeerableCallback); + return static_dom_object_map; +} + +#if ENABLE(SVG) +static void WeakSVGElementInstanceCallback(v8::Persistent<v8::Object> obj, + void* param); + +// A map for SVGElementInstances, which are not peerable +static DOMWrapperMap<SVGElementInstance>& dom_svg_element_instance_map() { + static DOMWrapperMap<SVGElementInstance> + static_dom_svg_element_instance_map(&WeakSVGElementInstanceCallback); + return static_dom_svg_element_instance_map; +} + +static void WeakSVGElementInstanceCallback(v8::Persistent<v8::Object> obj, + void* param) { + SVGElementInstance* instance = static_cast<SVGElementInstance*>(param); + ASSERT(dom_svg_element_instance_map().contains(instance)); + + instance->deref(); + dom_svg_element_instance_map().forget(instance); +} + +v8::Handle<v8::Object> V8Proxy::SVGElementInstanceToV8Object( + SVGElementInstance* instance) { + if (!instance) return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> existing_instance = + dom_svg_element_instance_map().get(instance); + if (!existing_instance.IsEmpty()) { + return existing_instance; + } + + instance->ref(); + + // Instantiate the V8 object and remember it + v8::Handle<v8::Object> result = + InstantiateV8Object(V8ClassIndex::SVGELEMENTINSTANCE, instance); + if (!result.IsEmpty()) { + // Only update the DOM SVG element map if the result is non-empty. + dom_svg_element_instance_map().set(instance, + v8::Persistent<v8::Object>::New(result)); + } + return result; +} + +// SVG non-node elements may have a reference to a context node which +// should be notified when the element is changed +static void WeakSVGObjectWithContext(v8::Persistent<v8::Object> obj, + void* param); + +// Map of SVG objects with contexts to V8 objects +static DOMWrapperMap<Peerable>& dom_svg_object_with_context_map() { + static DOMPeerableWrapperMap<Peerable> + static_dom_svg_object_with_context_map(&WeakSVGObjectWithContext); + return static_dom_svg_object_with_context_map; +} + +// Map of SVG objects with contexts to their contexts +static HashMap<void*, SVGElement*>& svg_object_to_context_map() { + static HashMap<void*, SVGElement*> static_svg_object_to_context_map; + return static_svg_object_to_context_map; +} + +v8::Handle<v8::Object> V8Proxy::SVGObjectWithContextToV8Object( + Peerable* object, V8ClassIndex::V8WrapperType type) { + if (!object) return v8::Handle<v8::Object>(); + + // Special case: SVGPathSegs need to be downcast to their real type + if (type == V8ClassIndex::SVGPATHSEG) { + type = V8Custom::DowncastSVGPathSeg(object); + } + + v8::Persistent<v8::Object> result = + dom_svg_object_with_context_map().get(object); + if (result.IsEmpty()) { + v8::Local<v8::Object> v8obj = InstantiateV8Object(type, object); + if (!v8obj.IsEmpty()) { + result = v8::Persistent<v8::Object>::New(v8obj); + dom_svg_object_with_context_map().set(object, result); + } + } + + return result; +} + +static void WeakSVGObjectWithContext(v8::Persistent<v8::Object> obj, + void* param) { + Peerable* dom_obj = static_cast<Peerable*>(param); + ASSERT(dom_svg_object_with_context_map().contains(dom_obj)); + + // Release the reference to the context if it exists + if (svg_object_to_context_map().contains(dom_obj)) { + svg_object_to_context_map().get(dom_obj)->deref(); + svg_object_to_context_map().remove(dom_obj); + } + + // forget function removes object from the map, + // disposes the wrapper and clears the peer. + dom_svg_object_with_context_map().forget(dom_obj); +} + +void V8Proxy::SetSVGContext(void* obj, SVGElement* context) { + SVGElement* old_context = svg_object_to_context_map().get(obj); + + if (old_context == context) { + return; + } + + if (old_context) { + old_context->deref(); + } + + if (context) { + context->ref(); + } + + svg_object_to_context_map().set(obj, context); +} + +SVGElement* V8Proxy::GetSVGContext(void* obj) { + return svg_object_to_context_map().get(obj); +} + +#endif + + +// Called when obj is near death (not reachable from JS roots) +// It is time to remove the entry from the table and dispose +// the handle. +static void WeakPeerableCallback(v8::Persistent<v8::Object> obj, void* para) { + Peerable* dom_obj = static_cast<Peerable*>(para); + ASSERT(dom_object_map().contains(dom_obj)); + + // forget function removes object from the map, + // disposes the wrapper and clears the peer. + dom_object_map().forget(dom_obj); +} + + +static void WeakNodeCallback(v8::Persistent<v8::Object> obj, void* param) { + Node* node = static_cast<Node*>(param); + ASSERT(dom_node_map().contains(node)); + + dom_node_map().forget(node); +} + + +// Create object groups for DOM tree nodes. +static void GCPrologue() { +#ifndef NDEBUG + // Check that all references in the map are weak. + PeerableMap peer_map = dom_object_map().impl(); + for (PeerableMap::iterator it = peer_map.begin(), end = peer_map.end(); + it != end; ++it) { + Peerable* obj = it->first; + ASSERT(v8::Persistent<v8::Object>(it->second).IsWeak()); + } +#endif + + // Create object groups. + NodeMap node_map = dom_node_map().impl(); + for (NodeMap::iterator it = node_map.begin(), end = node_map.end(); + it != end; ++it) { + Node* node = it->first; + + // If the node is in document, put it in the ownerDocument's + // object group. + // + // If an image element was created by JavaScript "new Image", + // it is not in a document. However, if the load event has not + // been fired (still onloading), it is treated as in the document. + // + if (node->inDocument() || + (node->hasTagName(HTMLNames::imgTag) && + !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent()) ) { + Document* doc = node->document(); + v8::Persistent<v8::Object> wrapper = dom_node_map().get(node); + if (!wrapper.IsEmpty()) { + v8::V8::AddObjectToGroup(doc, wrapper); + } + } + } +} + + +static void GCEpilogue() { +#ifndef NDEBUG + // Check all survivals are weak. + PeerableMap peer_map = dom_object_map().impl(); + for (PeerableMap::iterator it = peer_map.begin(), end = peer_map.end(); + it != end; ++it) { + Peerable* obj = it->first; + ASSERT(v8::Persistent<v8::Object>(it->second).IsWeak()); + } + + NodeMap node_map = dom_node_map().impl(); + for (NodeMap::iterator it = node_map.begin(), end = node_map.end(); + it != end; ++it) { + Node* node = it->first; + ASSERT(v8::Persistent<v8::Object>(it->second).IsWeak()); + } + + EnumerateGlobalHandles(); +#endif +} + + +// A map from a peerable node to its JS wrapper, the wrapper +// is kept as a strong reference to survive GCs. +static PeerableMap& gc_protected_map() { + static PeerableMap static_gc_protected_map; + return static_gc_protected_map; +} + + +// static +void V8Proxy::GCProtect(Peerable* dom_object) { + if (!dom_object) return; + if (gc_protected_map().contains(dom_object)) return; + if (!dom_object->peer()) return; + + // Create a new (strong) persistent handle for the peer. + v8::Persistent<v8::Object> + wrapper(static_cast<v8::Object*>(dom_object->peer())); + + gc_protected_map().set(dom_object, *v8::Persistent<v8::Object>::New(wrapper)); +} + + +// static +void V8Proxy::GCUnprotect(Peerable* dom_object) { + if (!dom_object) return; + if (!gc_protected_map().contains(dom_object)) return; + + // Dispose the strong reference. + v8::Persistent<v8::Object> wrapper(gc_protected_map().take(dom_object)); + wrapper.Dispose(); +} + + +typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap; + +bool AllowAllocation::m_current = false; + + +// JavaScriptConsoleMessages encapsulate everything needed to +// log messages originating from JavaScript to the Chrome console. +class JavaScriptConsoleMessage { + public: + JavaScriptConsoleMessage(const String& str, + const String& sourceID, + unsigned lineNumber) + : m_string(str) + , m_sourceID(sourceID) + , m_lineNumber(lineNumber) { } + + void AddToPage(Page* page) const; + + private: + const String m_string; + const String m_sourceID; + const unsigned m_lineNumber; +}; + + +void JavaScriptConsoleMessage::AddToPage(Page* page) const { + ASSERT(page); + Chrome* chrome = page->chrome(); + // Only messages with ErrorMessageLevel are logged when + // calling Chrome::addMessageToConsole(). + chrome->addMessageToConsole(JSMessageSource, ErrorMessageLevel, + m_string, m_lineNumber, m_sourceID); +} + + +// The ConsoleMessageManager handles all console messages that stem +// from JavaScript. It keeps a list of messages that have been delayed but +// it makes sure to add all messages to the console in the right order. +class ConsoleMessageManager { + public: + // Add a message to the console. May end up calling JavaScript code + // indirectly through the inspector so only call this function when + // it is safe to do allocations. + static void AddMessage(Page* page, const JavaScriptConsoleMessage& message); + + // Add a message to the console but delay the reporting until it + // is safe to do so: Either when we leave JavaScript execution or + // when adding other console messages. The primary purpose of this + // method is to avoid calling into V8 to handle console messages + // when the VM is in a state that does not support GCs or allocations. + // Delayed messages are always reported in the page corresponding + // to the active context. + static void AddDelayedMessage(const JavaScriptConsoleMessage& message); + + // Process any delayed messages. May end up calling JavaScript code + // indirectly through the inspector so only call this function when + // it is safe to do allocations. + static void ProcessDelayedMessages(); + + private: + // All delayed messages are stored in this vector. If the vector + // is NULL, there are no delayed messages. + static Vector<JavaScriptConsoleMessage>* m_delayed; +}; + + +Vector<JavaScriptConsoleMessage>* ConsoleMessageManager::m_delayed = NULL; + + +void ConsoleMessageManager::AddMessage( + Page* page, + const JavaScriptConsoleMessage& message) { + // Process any delayed messages to make sure that messages + // appear in the right order in the console. + ProcessDelayedMessages(); + message.AddToPage(page); +} + + +void ConsoleMessageManager::AddDelayedMessage( + const JavaScriptConsoleMessage& message) { + if (!m_delayed) { + // Allocate a vector for the delayed messages. Will be + // deallocated when the delayed messages are processed + // in ProcessDelayedMessages(). + m_delayed = new Vector<JavaScriptConsoleMessage>(); + } + m_delayed->append(message); +} + + +void ConsoleMessageManager::ProcessDelayedMessages() { + // If we have a delayed vector it cannot be empty. + if (!m_delayed) return; + ASSERT(!m_delayed->isEmpty()); + + // Add the delayed messages to the page of the active + // context. If that for some bizarre reason does not + // exist, we clear the list of delayed messages to avoid + // posting messages. We still deallocate the vector. + Frame* frame = V8Proxy::retrieveActiveFrame(); + Page* page = NULL; + if (frame) page = frame->page(); + if (!page) m_delayed->clear(); + + // Iterate through all the delayed messages and add them + // to the console. + const int size = m_delayed->size(); + for (int i = 0; i < size; i++) { + m_delayed->at(i).AddToPage(page); + } + + // Deallocate the delayed vector. + delete m_delayed; + m_delayed = NULL; +} + + +// Convenience class for ensuring that delayed messages in the +// ConsoleMessageManager are processed quickly. +class ConsoleMessageScope { + public: + ConsoleMessageScope() { ConsoleMessageManager::ProcessDelayedMessages(); } + ~ConsoleMessageScope() { ConsoleMessageManager::ProcessDelayedMessages(); } +}; + + +void log_info(Frame* frame, const String& msg, const String& url) { + Page* page = frame->page(); + if (!page) return; + JavaScriptConsoleMessage message(msg, url, 0); + ConsoleMessageManager::AddMessage(page, message); +} + + +static void HandleConsoleMessage(v8::Handle<v8::Message> message, + v8::Handle<v8::Value> data) { + // Use the frame where JavaScript is called from. + Frame* frame = V8Proxy::retrieveActiveFrame(); + if (!frame) return; + + Page* page = frame->page(); + if (!page) return; + + v8::Handle<v8::String> errorMessageString = message->Get(); + ASSERT(!errorMessageString.IsEmpty()); + String errorMessage = ToWebCoreString(errorMessageString); + + v8::Handle<v8::String> resourceNameString = message->GetScriptResourceName(); + String resourceName = (resourceNameString.IsEmpty()) + ? frame->document()->url() + : ToWebCoreString(resourceNameString); + JavaScriptConsoleMessage consoleMessage(errorMessage, + resourceName, + message->GetLineNumber()); + ConsoleMessageManager::AddMessage(page, consoleMessage); +} + + +enum DelayReporting { + REPORT_LATER, + REPORT_NOW +}; + + +static void ReportUnsafeAccessTo(Frame* target, DelayReporting delay) { + ASSERT(target); + Document* targetDocument = target->document(); + if (!targetDocument) return; + + Frame* source = V8Proxy::retrieveActiveFrame(); + Document* sourceDocument = source->document(); + ASSERT(sourceDocument); + + // FIXME: This error message should contain more specifics of why the same + // origin check has failed. + String str = String::format("Unsafe JavaScript attempt to access frame " + "with URL %s from frame with URL %s. Domains, protocols and ports must " + "match.\n", + targetDocument->url().utf8().data(), + sourceDocument->url().utf8().data()); + + // Build a console message with fake source ID and line number. + const String kSourceID = ""; + const int kLineNumber = 1; + JavaScriptConsoleMessage message(str, kSourceID, kLineNumber); + + if (delay == REPORT_NOW) { + // NOTE(tc): Apple prints the message in the target page, but it seems like + // it should be in the source page. Even for delayed messages, we put it in + // the source page; see ConsoleMessageManager::ProcessDelayedMessages(). + ConsoleMessageManager::AddMessage(source->page(), message); + + } else { + ASSERT(delay == REPORT_LATER); + // We cannot safely report the message eagerly, because this may cause + // allocations and GCs internally in V8 and we cannot handle that at this + // point. Therefore we delay the reporting. + ConsoleMessageManager::AddDelayedMessage(message); + } +} + + +static void ReportUnsafeJavaScriptAccess(v8::Local<v8::Object> host, + v8::AccessType type, + v8::Local<v8::Value> data) { + // Do not report error if the access type is HAS. + if (type == v8::ACCESS_HAS) return; + + Frame* target = V8Custom::GetTargetFrame(host, data); + if (target) + ReportUnsafeAccessTo(target, REPORT_LATER); +} + + +static void ReportFatalErrorInV8(const char* location, const char* message) { + // V8 is shutdown, we cannot use V8 api. + // The only thing we can do is to disable JavaScript. + // TODO: clean up V8Proxy and disable JavaScript. + printf("V8 error: %s (%s)\n", message, location); +} + + +static void HandleFatalErrorInV8() { + // TODO: We temporarily deal with V8 internal error situations + // such as out-of-memory by crashing the renderer. + CRASH(); +} + + +V8Proxy::~V8Proxy() { + clear(); + DestroyGlobal(); +} + + +void V8Proxy::DestroyGlobal() { + if (!m_global.IsEmpty()) { +#ifndef NDEBUG + UnregisterGlobalHandle(this, m_global); +#endif + m_global.Dispose(); + m_global.Clear(); + } +} + + +void V8Proxy::SetJSWrapperForDOMObject(Peerable* obj, + v8::Persistent<v8::Object> wrapper) { + dom_object_map().set(obj, wrapper); +} + + +void V8Proxy::SetJSWrapperForDOMNode(Node* node, + v8::Persistent<v8::Object> wrapper) { + dom_node_map().set(node, wrapper); +} + + +EventListener* V8Proxy::createHTMLEventHandler(const String& functionName, + const String& code, Node* node) { + return new V8LazyEventListener(m_frame, code, functionName); +} + +#if ENABLE(SVG) +EventListener* V8Proxy::createSVGEventHandler(const String& functionName, + const String& code, Node* node) { + return new V8LazyEventListener(m_frame, code, functionName); +} +#endif + + +// Event listeners + +static V8EventListener* FindEventListenerInList(V8EventListenerList& list, + v8::Local<v8::Value> listener, + bool html) { + ASSERT(v8::Context::InContext()); + + if (!listener->IsObject()) return 0; + + V8EventListenerList::iterator p = list.begin(); + while (p != list.end()) { + V8EventListener* el = *p; + v8::Local<v8::Object> wrapper = el->GetListenerObject(); + ASSERT(!wrapper.IsEmpty()); + // Since the listener is an object, it is safe to compare for + // strict equality (in the JS sense) by doing a simple equality + // check using the == operator on the handles. This is much, + // much faster than calling StrictEquals through the API in + // the negative case. + if (el->isHTMLEventListener() == html && listener == wrapper) { + return el; + } + ++p; + } + return 0; +} + + +// Find an existing wrapper for a JS event listener in the map. +V8EventListener* V8Proxy::FindV8EventListener(v8::Local<v8::Value> listener, + bool html) { + return FindEventListenerInList(m_event_listeners, listener, html); +} + + +V8EventListener* V8Proxy::FindOrCreateV8EventListener(v8::Local<v8::Value> obj, + bool html) { + ASSERT(v8::Context::InContext()); + + if (!obj->IsObject()) return 0; + + V8EventListener* wrapper = + FindEventListenerInList(m_event_listeners, obj, html); + if (wrapper) return wrapper; + + // Create a new one, and add to cache. + V8EventListener* new_listener = + new V8EventListener(m_frame, v8::Local<v8::Object>::Cast(obj), html); + m_event_listeners.push_back(new_listener); + + return new_listener; +} + + +// XMLHttpRequest(XHR) event listeners are different from listeners +// on DOM nodes. A XHR event listener wrapper only hold a weak reference +// to the JS function. A strong reference can create a cycle. +// +// The lifetime of a XHR object is bounded by the life time of its JS_XHR +// object. So we can create a hidden reference from JS_XHR to JS function. +// +// (peer) +// XHR <---------- JS_XHR +// | (hidden) : ^ +// V V : (may reachable by closure) +// V8_listener --------> JS_function +// (weak) <-- may create a cycle if it is strong +// +// The persistent reference is made weak in the constructor +// of V8XHREventListener. + +V8EventListener* V8Proxy::FindXHREventListener(v8::Local<v8::Value> listener, + bool html) { + return FindEventListenerInList(m_xhr_listeners, listener, html); +} + + +V8EventListener* +V8Proxy::FindOrCreateXHREventListener(v8::Local<v8::Value> obj, + bool html) { + ASSERT(v8::Context::InContext()); + + if (!obj->IsObject()) return 0; + + V8EventListener* wrapper = + FindEventListenerInList(m_xhr_listeners, obj, html); + if (wrapper) return wrapper; + + // Create a new one, and add to cache. + V8EventListener* new_listener = + new V8XHREventListener(m_frame, v8::Local<v8::Object>::Cast(obj), html); + m_xhr_listeners.push_back(new_listener); + + return new_listener; +} + + +static void RemoveEventListenerFromList(V8EventListenerList& list, + V8EventListener* listener) { + V8EventListenerList::iterator p = list.begin(); + while (p != list.end()) { + if (*p == listener) { + list.erase(p); + return; + } + ++p; + } +} + + +void V8Proxy::RemoveV8EventListener(V8EventListener* listener) { + RemoveEventListenerFromList(m_event_listeners, listener); +} + + +void V8Proxy::RemoveXHREventListener(V8XHREventListener* listener) { + RemoveEventListenerFromList(m_xhr_listeners, listener); +} + + +static void DisconnectEventListenersInList(V8EventListenerList& list) { + V8EventListenerList::iterator p = list.begin(); + while (p != list.end()) { + (*p)->disconnectFrame(); + ++p; + } + list.clear(); +} + + +void V8Proxy::DisconnectEventListeners() { + DisconnectEventListenersInList(m_event_listeners); + DisconnectEventListenersInList(m_xhr_listeners); +} + + +v8::Handle<v8::Script> V8Proxy::CompileScript(v8::Handle<v8::String> code, + const String& fileName, + int baseLine) { + const uint16_t* fileNameString = FromWebCoreString(fileName); + v8::Handle<v8::String> name = + v8::String::New(fileNameString, fileName.length()); + v8::Handle<v8::Integer> line = v8::Integer::New(baseLine); + v8::ScriptOrigin origin(name, line); + v8::Handle<v8::Script> script = v8::Script::Compile(code, &origin); + return script; +} + + +bool V8Proxy::HandleOutOfMemory() { + v8::Local<v8::Context> context = v8::Context::GetCurrent(); + + if (!context->HasOutOfMemoryException()) + return false; + + // Warning, error, disable JS for this frame? + Frame* frame = V8Proxy::retrieveFrame(context); + + V8Proxy* proxy = V8Proxy::retrieve(frame); + // Clean m_context, m_document, and event handlers. + proxy->clear(); + // Destroy the global object. + proxy->DestroyGlobal(); + + webkit_glue::NotifyJSOutOfMemory(frame); + + // Disable JS. + Settings* settings = frame->settings(); + ASSERT(settings); + settings->setJavaScriptEnabled(false); + + return true; +} + + +v8::Local<v8::Value> V8Proxy::Evaluate(const String& fileName, int baseLine, + const String& str, Node* n) { + ASSERT(v8::Context::InContext()); + + // Compile the script. + v8::Local<v8::String> code = v8ExternalString(str); + v8::Handle<v8::Script> script = CompileScript(code, fileName, baseLine); + + // Set inlineCode to true for <a href="javascript:doSomething()"> + // and false for <script>doSomething</script>. For some reason, fileName + // gives us this information. + return RunScript(script, fileName.isNull()); +} + + +v8::Local<v8::Value> V8Proxy::RunScript(v8::Handle<v8::Script> script, + bool inline_code) { + if (script.IsEmpty()) + return v8::Local<v8::Value>(); + + // Compute the source string and prevent against infinite recursion. + if (m_recursion >= 20) { + v8::Local<v8::String> code = + v8ExternalString("throw RangeError('Recursion too deep')"); + // TODO(kasperl): Ideally, we should be able to re-use the origin of the + // script passed to us as the argument instead of using an empty string + // and 0 baseLine. + script = CompileScript(code, "", 0); + } + + if (HandleOutOfMemory()) + ASSERT(script.IsEmpty()); + + if (script.IsEmpty()) + return v8::Local<v8::Value>(); + + // Save the previous value of the inlineCode flag and update the flag for + // the duration of the script invocation. + bool previous_inline_code = inlineCode(); + setInlineCode(inline_code); + + // Run the script and keep track of the current recursion depth. + v8::Local<v8::Value> result; + { ConsoleMessageScope scope; + m_recursion++; + + // Evaluating the JavaScript could cause the frame to be deallocated, + // so we start the keep alive timer here. + // Frame::keepAlive method adds the ref count of the frame and sets a + // timer to decrease the ref count. It assumes that the current JavaScript + // execution finishs before firing the timer. + // See issue 1218756 and 914430. + m_frame->keepAlive(); + + result = script->Run(); + m_recursion--; + } + + if (HandleOutOfMemory()) + ASSERT(result.IsEmpty()); + + // Handle V8 internal error situation (Out-of-memory). + if (result.IsEmpty()) + return v8::Local<v8::Value>(); + + // Restore inlineCode flag. + setInlineCode(previous_inline_code); + + if (v8::V8::IsDead()) + HandleFatalErrorInV8(); + + return result; +} + + +v8::Local<v8::Value> V8Proxy::CallFunction(v8::Handle<v8::Function> function, + v8::Handle<v8::Object> receiver, + int argc, + v8::Handle<v8::Value> args[]) { + // For now, we don't put any artificial limitations on the depth + // of recursion that stems from calling functions. This is in + // contrast to the script evaluations. + v8::Local<v8::Value> result; + { ConsoleMessageScope scope; + + // Evaluating the JavaScript could cause the frame to be deallocated, + // so we start the keep alive timer here. + // Frame::keepAlive method adds the ref count of the frame and sets a + // timer to decrease the ref count. It assumes that the current JavaScript + // execution finishs before firing the timer. + // See issue 1218756 and 914430. + m_frame->keepAlive(); + + result = function->Call(receiver, argc, args); + } + + if (v8::V8::IsDead()) + HandleFatalErrorInV8(); + + return result; +} + + +v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate( + V8ClassIndex::V8WrapperType type) { + v8::Persistent<v8::FunctionTemplate>* cache_cell = + V8ClassIndex::GetCache(type); + if (!(*cache_cell).IsEmpty()) return *cache_cell; + + // not found + FunctionTemplateFactory factory = V8ClassIndex::GetFactory(type); + v8::Persistent<v8::FunctionTemplate> desc = factory(); + 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. + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(CSSStyleDeclaration), + USE_NAMED_PROPERTY_SETTER(CSSStyleDeclaration)); + SetCollectionStringOrNullIndexedGetter<CSSStyleDeclaration>(desc); + break; + case V8ClassIndex::CSSRULELIST: + SetCollectionIndexedGetter<CSSRuleList>(desc, V8ClassIndex::CSSRULE); + break; + case V8ClassIndex::CSSVALUELIST: + SetCollectionIndexedGetter<CSSValueList>(desc, V8ClassIndex::CSSVALUE); + break; + case V8ClassIndex::UNDETECTABLEHTMLCOLLECTION: + desc->InstanceTemplate()->MarkAsUndetectable(); // fall through + case V8ClassIndex::HTMLCOLLECTION: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLCollection)); + desc->InstanceTemplate()->SetCallAsFunctionHandler( + USE_CALLBACK(HTMLCollectionCallAsFunction)); + SetCollectionIndexedGetter<HTMLCollection>(desc, V8ClassIndex::NODE); + break; + case V8ClassIndex::HTMLOPTIONSCOLLECTION: + SetCollectionNamedGetter<HTMLOptionsCollection>(desc, V8ClassIndex::NODE); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(HTMLOptionsCollection), + USE_INDEXED_PROPERTY_SETTER(HTMLOptionsCollection)); + desc->InstanceTemplate()->SetCallAsFunctionHandler( + USE_CALLBACK(HTMLCollectionCallAsFunction)); + break; + case V8ClassIndex::HTMLSELECTELEMENT: + desc->InstanceTemplate()->SetNamedPropertyHandler( + CollectionNamedPropertyGetter<HTMLSelectElement>, + 0, + 0, + 0, + 0, + v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE))); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + CollectionIndexedPropertyGetter<HTMLSelectElement>, + USE_INDEXED_PROPERTY_SETTER(HTMLSelectElementCollection), + 0, + 0, + CollectionIndexedPropertyEnumerator<HTMLSelectElement>, + v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE))); + break; + case V8ClassIndex::HTMLDOCUMENT: { + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLDocument), + USE_NAMED_PROPERTY_SETTER(HTMLDocument), + 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> instance_template = + desc->InstanceTemplate(); + ASSERT(instance_template->InternalFieldCount() == + V8Custom::kDefaultWrapperInternalFieldCount); + instance_template->SetInternalFieldCount( + V8Custom::kHTMLDocumentInternalFieldCount); + break; + } + case V8ClassIndex::DOCUMENT: + case V8ClassIndex::SVGDOCUMENT: { + // We add an extra internal field to all Document wrappers for + // storing a per document DOMImplementation wrapper. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + ASSERT(instance_template->InternalFieldCount() == + V8Custom::kDefaultWrapperInternalFieldCount); + instance_template->SetInternalFieldCount( + V8Custom::kDocumentMinimumInternalFieldCount); + break; + } + case V8ClassIndex::HTMLEMBEDELEMENT: + // fall through + case V8ClassIndex::HTMLOBJECTELEMENT: + // Follow through. Both HTMLEmbedElement and HTMLObjectElement are + // inherited from HTMLPlugInElement, and they share the same property + // handling code. + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLPlugInElement), + USE_NAMED_PROPERTY_SETTER(HTMLPlugInElement)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(HTMLPlugInElement), + USE_INDEXED_PROPERTY_SETTER(HTMLPlugInElement)); + desc->InstanceTemplate()->SetCallAsFunctionHandler( + USE_CALLBACK(HTMLPlugInElement)); + break; + case V8ClassIndex::HTMLFRAMESETELEMENT: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLFrameSetElement)); + break; + case V8ClassIndex::HTMLFORMELEMENT: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(HTMLFormElement)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(HTMLFormElement), + 0, + 0, + 0, + CollectionIndexedPropertyEnumerator<HTMLFormElement>, + v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE))); + break; + case V8ClassIndex::MEDIALIST: + SetCollectionStringOrNullIndexedGetter<MediaList>(desc); + break; + case V8ClassIndex::MIMETYPEARRAY: + SetCollectionIndexedAndNamedGetters<MimeTypeArray>( + desc, + V8ClassIndex::MIMETYPE); + break; + case V8ClassIndex::NAMEDNODEMAP: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(NamedNodeMap)); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(NamedNodeMap), + 0, + 0, + 0, + CollectionIndexedPropertyEnumerator<NamedNodeMap>, + v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE))); + break; + case V8ClassIndex::NODELIST: + SetCollectionIndexedGetter<NodeList>(desc, V8ClassIndex::NODE); + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(NodeList)); + break; + case V8ClassIndex::PLUGIN: + SetCollectionIndexedAndNamedGetters<Plugin>(desc, V8ClassIndex::MIMETYPE); + break; + case V8ClassIndex::PLUGINARRAY: + SetCollectionIndexedAndNamedGetters<PluginArray>(desc, + V8ClassIndex::PLUGIN); + break; + case V8ClassIndex::STYLESHEETLIST: + desc->InstanceTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(StyleSheetList)); + SetCollectionIndexedGetter<StyleSheetList>(desc, + V8ClassIndex::STYLESHEET); + break; + case V8ClassIndex::DOMWINDOW: { + v8::Local<v8::Signature> default_signature = v8::Signature::New(desc); + + desc->PrototypeTemplate()->SetNamedPropertyHandler( + USE_NAMED_PROPERTY_GETTER(DOMWindow)); + desc->PrototypeTemplate()->SetIndexedPropertyHandler( + USE_INDEXED_PROPERTY_GETTER(DOMWindow)); + + desc->PrototypeTemplate()->Set( + v8::String::New("addEventListener"), + v8::FunctionTemplate::New(USE_CALLBACK(DOMWindowAddEventListener), + v8::Handle<v8::Value>(), + default_signature), + v8::None); + desc->PrototypeTemplate()->Set( + v8::String::New("removeEventListener"), + v8::FunctionTemplate::New(USE_CALLBACK(DOMWindowRemoveEventListener), + v8::Handle<v8::Value>(), + default_signature), + v8::None); + desc->SetHiddenPrototype(true); + break; + } + case V8ClassIndex::LOCATION: { + break; + } + case V8ClassIndex::HISTORY: { + break; + } + + // DOMParser, XMLSerializer, and XMLHttpRequest objects are created from + // JS world, but we setup the constructor function lazily in + // WindowNamedPropertyHandler::get. + case V8ClassIndex::DOMPARSER: + desc->SetCallHandler(USE_CALLBACK(DOMParserConstructor)); + break; + case V8ClassIndex::XMLSERIALIZER: + desc->SetCallHandler(USE_CALLBACK(XMLSerializerConstructor)); + break; + case V8ClassIndex::XMLHTTPREQUEST: { + // Reserve one more internal field for keeping event listeners. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + int internal_field_count = instance_template->InternalFieldCount() + 1; + instance_template->SetInternalFieldCount(internal_field_count); + desc->SetCallHandler(USE_CALLBACK(XMLHttpRequestConstructor)); + break; + } + case V8ClassIndex::XPATHEVALUATOR: + desc->SetCallHandler(USE_CALLBACK(XPathEvaluatorConstructor)); + break; + case V8ClassIndex::XSLTPROCESSOR: + desc->SetCallHandler(USE_CALLBACK(XSLTProcessorConstructor)); + break; + default: + break; + } + + *cache_cell = desc; + return desc; +} + + +bool V8Proxy::ContextInitialized() { + return !m_context.IsEmpty(); +} + + +DOMWindow* V8Proxy::retrieveWindow() { + // TODO: This seems very fragile. How do we know that the global object + // from the current context is something sensible? Do we need to use the + // last entered here? Who calls this? + v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global(); + if (global.IsEmpty()) return 0; + v8::Handle<v8::Value> window = global->GetPrototype(); + return ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window); +} + + +Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context) { + v8::Handle<v8::Object> global = context->Global(); + v8::Handle<v8::Value> window_peer = global->GetPrototype(); + DOMWindow* window = + ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window_peer); + return window->frame(); +} + + +Frame* V8Proxy::retrieveActiveFrame() { + v8::Handle<v8::Context> context = v8::Context::GetEntered(); + if (context.IsEmpty()) + return 0; + return retrieveFrame(context); +} + + +Frame* V8Proxy::retrieveFrame() { + DOMWindow* window = retrieveWindow(); + return window ? window->frame() : 0; +} + + +V8Proxy* V8Proxy::retrieve() { + DOMWindow* window = retrieveWindow(); + ASSERT(window); + return retrieve(window->frame()); +} + + +V8Proxy* V8Proxy::retrieve(Frame* frame) { + if (!frame) return 0; + V8Bridge* bridge = static_cast<V8Bridge*>(frame->scriptBridge()); + return bridge->isEnabled() ? bridge->proxy() : 0; +} + + +void V8Proxy::disconnectFrame() { + // disconnect all event listeners + DisconnectEventListeners(); + + // clear all timeouts. + if (m_frame->domWindow()) + m_frame->domWindow()->clearAllTimeouts(); +} + +bool V8Proxy::isEnabled() { + Settings* settings = m_frame->settings(); + if (!settings) + return false; + + // In the common case, JavaScript is enabled and we're done. + if (settings->isJavaScriptEnabled()) + return true; + + // If JavaScript has been disabled, we need to look at the frame to tell + // whether this script came from the web or the embedder. Scripts from the + // embedder are safe to run, but scripts from the other sources are + // disallowed. + Document* document = m_frame->document(); + if (!document) + return false; + + SecurityOrigin* origin = document->securityOrigin(); + if (origin->protocol().isEmpty()) + return false; // Uninitialized document + + if (origin->protocol() == "http" || origin->protocol() == "https") + return false; // Web site + + if (origin->protocol() == + webkit_glue::StdStringToString(webkit_glue::GetUIResourceProtocol())) + return true; // Embedder's scripts are ok to run + + // If the scheme is ftp: or file:, an empty file name indicates a directory + // listing, which requires JavaScript to function properly. + const char* kDirProtocols[] = { "ftp", "file" }; + GURL url(document->url().utf8().data()); + for (size_t i = 0; i < arraysize(kDirProtocols); ++i) { + if (origin->protocol() == kDirProtocols[i]) { + ASSERT(url.SchemeIs(kDirProtocols[i])); + return url.ExtractFileName().empty(); + } + } + + return false; // Other protocols fall through to here +} + + +void V8Proxy::clearDocumentWrapper() { + v8::HandleScope handle_scope; + v8::Local<v8::Context> context = GetContext(); + if (context.IsEmpty()) return; // not initialize yet + + if (!m_document.IsEmpty()) { +#ifndef NDEBUG + UnregisterGlobalHandle(this, m_document); +#endif + m_document.Dispose(); + m_document.Clear(); + } +} + + +// static +void V8Proxy::DomainChanged(Frame* frame) { + V8Proxy* proxy = retrieve(frame); + proxy->ClearSecurityToken(); +} + + +void V8Proxy::ClearSecurityToken() { + m_context->SetSecurityToken(m_global); +} + + +void V8Proxy::clear() { + if (!m_context.IsEmpty()) { + ClearSecurityToken(); + + if (m_frame->domWindow()) + m_frame->domWindow()->clearAllTimeouts(); + + clearDocumentWrapper(); + + // Corresponds to the context creation in initContextIfNeeded(). + m_context.Dispose(); + m_context.Clear(); + } +} + + +// Check if two frames are from the same origin. +// This function is equivalent to +// KJS::Window::allowsAccessFrom(const JSGlobalObject*, +// SecurityOrigin::Reason&, String& message) const. +static bool SameOrigin(Frame* source, Frame* target, + SecurityOrigin::Reason& reason, String& message) { + if (!source) { + reason = SecurityOrigin::GenericMismatch; + return false; + } + + if (!target) { + reason = SecurityOrigin::GenericMismatch; + return false; + } + + // Allow access if the frames the windows represent are the same. + if (source == target) + return true; + + Document* target_document = target->document(); + + // JS may be attempting to access the "window" object, which should be valid, + // even if the document hasn't been constructed yet. If the document doesn't + // exist yet allow JS to access the window object. + if (!target_document) + return true; + + Document* act_document = source->document(); + + const SecurityOrigin* active_security_origin = act_document->securityOrigin(); + const SecurityOrigin* target_security_origin = + target_document->securityOrigin(); + + String ui_resource_protocol = + webkit_glue::StdStringToString(webkit_glue::GetUIResourceProtocol()); + if (active_security_origin->protocol() == ui_resource_protocol) { + KURL inspector_url = + webkit_glue::GURLToKURL(webkit_glue::GetInspectorURL()); + ASSERT(inspector_url.protocol() == ui_resource_protocol); + ASSERT(inspector_url.protocol().endsWith("-resource")); + + // The Inspector can access anything. + if (active_security_origin->host() == inspector_url.host()) + return true; + + // To mitigate XSS vulnerabilities on the browser itself, UI resources + // besides the Inspector can't access other documents. + return false; + } + + if (active_security_origin->canAccess(target_security_origin, reason)) + return true; + + return false; +} + + +// Check if the current execution context can access a target frame. +// First it checks same domain policy using the security context +// (where the script is invoked), if domain check failed due to +// setting document.domain, using the lexical context of the function +// to check domain policy. +// +// This is equivalent to KJS::Window::allowsAccessFrom(ExecState*, String&). +bool V8Proxy::CanAccess(Frame* target) { + SecurityOrigin::Reason reason; + String message; + + // Check dynamic (security) context first. + Frame* source = + V8Proxy::retrieveFrame(v8::Context::GetCurrentSecurityContext()); + if (SameOrigin(source, target, reason, message)) { + return true; + } + + // Check lexical orgin if the reason is DomainSetInDOM mismatch. + if (reason == SecurityOrigin::DomainSetInDOMMismatch) { + source = V8Proxy::retrieveFrame(v8::Context::GetCurrent()); + if (SameOrigin(source, target, reason, message)) { + return true; + } + } + + return false; +} + + +bool V8Proxy::IsFromSameOrigin(Frame* target, bool report_error) { + // The subject is detached from a frame, deny accesses. + if (!target) return false; + + if (!CanAccess(target)) { + if (report_error) ReportUnsafeAccessTo(target, REPORT_NOW); + return false; + } + return true; +} + + +bool V8Proxy::CheckNodeSecurity(Node* node) { + if (!node) + return false; + + Frame* target = node->document()->frame(); + + if (!target) + return false; + + return IsFromSameOrigin(target, true); +} + + +// Create a new environment and setup the global object. +// +// The global object corresponds to a DOMWindow instance. However, to +// allow properties of the JS DOMWindow instance to be shadowed, we +// use a shadow object as the global object and use the JS DOMWindow +// instance as the prototype for that shadow object. The JS DOMWindow +// instance is undetectable from javascript code because the __proto__ +// accessors skip that object. +// +// The shadow object and the DOMWindow instance are seen as one object +// from javascript. The javascript object that corresponds to a +// DOMWindow instance is the shadow object. When mapping a DOMWindow +// instance to a V8 object, we return the shadow object. +void V8Proxy::initContextIfNeeded() { + // Bail out if the context has already been initialized. + if (!m_context.IsEmpty()) return; + + // Install counters handler with V8. + static bool v8_counters_initialized = false; + if (!v8_counters_initialized) { + v8::V8::SetCounterFunction(StatsTable::FindLocation); + v8_counters_initialized = true; + } + + // Setup the security handlers and message listener. This only has + // to be done once. + static bool v8_initialized = false; + if (!v8_initialized) { + v8_initialized = true; + + // Tells V8 not to call the default OOM handler, binding code + // will handle it. + v8::V8::IgnoreOutOfMemoryException(); + v8::V8::SetFatalErrorHandler(ReportFatalErrorInV8); + + v8::V8::SetGlobalGCPrologueCallback(&GCPrologue); + v8::V8::SetGlobalGCEpilogueCallback(&GCEpilogue); + + v8::V8::AddMessageListener(HandleConsoleMessage); + + v8::V8::SetFailedAccessCheckCallbackFunction(ReportUnsafeJavaScriptAccess); + } + + // Create a new environment using an empty template for the shadow + // object. Reuse the global object if one has been created earlier. + v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); + if (global_template.IsEmpty()) + return; + + // Install a security handler with V8. + { v8::Local<v8::External> external = + v8::External::New(reinterpret_cast<void*>(V8ClassIndex::DOMWINDOW)); + if (external.IsEmpty()) + return; + + global_template->SetAccessCheckCallbacks( + V8Custom::v8DOMWindowNamedSecurityCheck, + V8Custom::v8DOMWindowIndexedSecurityCheck, + external); + } + + m_context = v8::Context::New(NULL, global_template, m_global); + if (m_context.IsEmpty()) + return; + + // Starting from now, use local context only. + v8::Local<v8::Context> context = GetContext(); + v8::Context::Scope scope(context); + + // Store the first global object created so we can reuse it. + if (m_global.IsEmpty()) { + m_global = v8::Persistent<v8::Object>::New(context->Global()); +#ifndef NDEBUG + RegisterGlobalHandle(PROXY, this, m_global); +#endif + } + + // Create a new JS window object and use it as the prototype for the + // shadow global object. + v8::Persistent<v8::FunctionTemplate> window_descriptor = + GetTemplate(V8ClassIndex::DOMWINDOW); + v8::Local<v8::Object> window_peer = + SafeAllocation::NewInstance(window_descriptor->GetFunction()); + if (window_peer.IsEmpty()) + return; + + DOMWindow* window = m_frame->domWindow(); + + // Get rid of the old window peer object if one exists. + dom_object_map().forget(window); + + // Setup the peer object for the DOM window. + dom_object_map().set(window, v8::Persistent<v8::Object>::New(window_peer)); + // Wrap the window. + SetDOMWrapper(window_peer, + V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), + window); + // Insert the window instance as the prototype of the shadow object. + v8::Handle<v8::Object> v8_global = context->Global(); + v8_global->Set(v8::String::New("__proto__"), window_peer); + + context->SetSecurityToken(GenerateSecurityToken(context)); + + V8Proxy::retrieveFrame(context)->loader()->dispatchWindowObjectAvailable(); + + if (JSBridge::RecordPlaybackMode()) { + // Inject code which overrides a few common JS functions for implementing + // randomness. In order to implement effective record & playback of + // websites, it is important that the URLs not change. Many popular web + // based apps use randomness in URLs to unique-ify urls for proxies. + // Unfortunately, this breaks playback. + // To work around this, we take the two most common client-side randomness + // generators and make them constant. They really need to be constant + // (rather than a constant seed followed by constant change) + // because the playback mode wants flexibility in how it plays them back + // and cannot always guarantee that requests for randomness are played back + // in exactly the same order in which they were recorded. + String script( + "Math.random = function() { return 0.5; };" + "__ORIGDATE__ = Date;" + "Date.__proto__.now = function() { " + " return new __ORIGDATE__(1204251968254); };" + "Date = function() { return Date.now(); };"); + this->Evaluate(String(), 0, script, 0); + } +} + + +v8::Handle<v8::Value> V8Proxy::GenerateSecurityToken( + v8::Local<v8::Context> context) { + Document* document = V8Proxy::retrieveFrame(context)->document(); + if (!document) + return context->Global(); + + // Ask the document's SecurityOrigin to generate a security token. + // If two tokens are equal, then the SecurityOrigins canAccess each other. + // If two tokens are not equal, then we have to call canAccess. + String token = document->securityOrigin()->securityToken(); + + // An empty token means we always have to call canAccess. In this case, we + // use the global object as the security token to avoid calling canAccess + // when a script accesses its own objects. + if (token.isEmpty()) + return context->Global(); + + CString utf8_token = token.utf8(); + // NOTE: V8 does identity comparison in fast path, must use a symbol + // as the security token. + return v8::String::NewSymbol(utf8_token.data(), utf8_token.length()); +} + + +void V8Proxy::SetDOMException(int exception_code) { + if (exception_code <= 0) return; + + if (exception_code == XMLHttpRequestException::PERMISSION_DENIED) { + ThrowError(GENERAL_ERROR, "Permission denied"); + return; + } + + ExceptionCodeDescription description; + getExceptionCodeDescription(exception_code, description); + + v8::Handle<v8::Value> exception; + switch (description.type) { + case DOMExceptionType: + exception = ToV8Object(V8ClassIndex::DOMCOREEXCEPTION, + new DOMCoreException(description)); + break; + case RangeExceptionType: + exception = ToV8Object(V8ClassIndex::RANGEEXCEPTION, + new RangeException(description)); + break; + case EventExceptionType: + exception = ToV8Object(V8ClassIndex::EVENTEXCEPTION, + new EventException(description)); + break; + case XMLHttpRequestExceptionType: + exception = ToV8Object(V8ClassIndex::XMLHTTPREQUESTEXCEPTION, + new XMLHttpRequestException(description)); + break; +#if ENABLE(SVG) + case SVGExceptionType: + exception = ToV8Object(V8ClassIndex::SVGEXCEPTION, + new SVGException(description)); + break; +#endif +#if ENABLE(XPATH) + case XPathExceptionType: + exception = ToV8Object(V8ClassIndex::XPATHEXCEPTION, + new XPathException(description)); + break; +#endif + } + + ASSERT(!exception.IsEmpty()); + v8::ThrowException(exception); +} + + +v8::Handle<v8::Value> V8Proxy::ThrowError(ErrorType type, const char* message) { + switch (type) { + case RANGE_ERROR: + return v8::ThrowException(v8::Exception::RangeError(v8String(message))); + case REFERENCE_ERROR: + return v8::ThrowException( + v8::Exception::ReferenceError(v8String(message))); + case SYNTAX_ERROR: + return v8::ThrowException(v8::Exception::SyntaxError(v8String(message))); + case TYPE_ERROR: + return v8::ThrowException(v8::Exception::TypeError(v8String(message))); + case GENERAL_ERROR: + return v8::ThrowException(v8::Exception::Error(v8String(message))); + default: + ASSERT(false); + return v8::Handle<v8::Value>(); + } +} + + +v8::Local<v8::Context> V8Proxy::GetContext(Frame* frame) { + V8Proxy* proxy = retrieve(frame); + if (!proxy) + return v8::Local<v8::Context>(); + + proxy->initContextIfNeeded(); + return proxy->GetContext(); +} + + +v8::Local<v8::Context> V8Proxy::GetCurrentContext() { + return v8::Context::GetCurrent(); +} + + +v8::Handle<v8::Value> V8Proxy::ToV8Object(V8ClassIndex::V8WrapperType type, + void* imp) { + ASSERT(type != V8ClassIndex::EVENTLISTENER); + ASSERT(type != V8ClassIndex::EVENTTARGET); + ASSERT(type != V8ClassIndex::EVENT); + + if (!imp) return v8::Null(); + +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: + + switch (type) { + NODE_WRAPPER_TYPES(MAKE_CASE) + HTMLELEMENT_TYPES(MAKE_CASE) +#if ENABLE(SVG) + SVGNODE_WRAPPER_TYPES(MAKE_CASE) + SVGELEMENT_TYPES(MAKE_CASE) +#endif + return NodeToV8Object(static_cast<Node*>(imp)); + case V8ClassIndex::CSSVALUE: + return CSSValueToV8Object(static_cast<CSSValue*>(imp)); + case V8ClassIndex::CSSRULE: + return CSSRuleToV8Object(static_cast<CSSRule*>(imp)); + case V8ClassIndex::STYLESHEET: + return StyleSheetToV8Object(static_cast<StyleSheet*>(imp)); + case V8ClassIndex::DOMWINDOW: + return WindowToV8Object(static_cast<DOMWindow*>(imp)); +#if ENABLE(SVG) + SVGNONNODE_WRAPPER_TYPES(MAKE_CASE) + if (type == V8ClassIndex::SVGELEMENTINSTANCE) { + return SVGElementInstanceToV8Object( + static_cast<SVGElementInstance*>(imp)); + } else { + return SVGObjectWithContextToV8Object(static_cast<Peerable*>(imp), + type); + } +#endif + default: + break; + } + +#undef MAKE_CASE + + // Non DOM node + Peerable* obj = static_cast<Peerable*>(imp); + v8::Persistent<v8::Object> result = dom_object_map().get(obj); + if (result.IsEmpty()) { + v8::Local<v8::Object> v8obj = InstantiateV8Object(type, imp); + if (!v8obj.IsEmpty()) { + result = v8::Persistent<v8::Object>::New(v8obj); + dom_object_map().set(obj, result); + } + } + return result; +} + + +V8ClassIndex::V8WrapperType V8Proxy::GetDOMWrapperType( + v8::Handle<v8::Object> object) { + if (!MaybeDOMWrapper(object)) { + return V8ClassIndex::INVALID_CLASS_INDEX; + } + + v8::Handle<v8::Value> type = object->GetInternalField(1); + return V8ClassIndex::FromInt(type->Int32Value()); +} + + +void* V8Proxy::FastToNativeObjectImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object) { + // Native event listener is per frame, it cannot be handled + // by this generic function. + ASSERT(type != V8ClassIndex::EVENTLISTENER); + ASSERT(type != V8ClassIndex::EVENTTARGET); + + ASSERT(MaybeDOMWrapper(object)); + +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: + switch (type) { + NODE_WRAPPER_TYPES(MAKE_CASE) + HTMLELEMENT_TYPES(MAKE_CASE) +#if ENABLE(SVG) + SVGNODE_WRAPPER_TYPES(MAKE_CASE) + SVGELEMENT_TYPES(MAKE_CASE) +#endif + return FastDOMWrapperToNative<Node>(object); + case V8ClassIndex::XMLHTTPREQUEST: + return FastDOMWrapperToNative<XMLHttpRequest>(object); + case V8ClassIndex::EVENT: + return FastDOMWrapperToNative<Event>(object); + case V8ClassIndex::CSSRULE: + return FastDOMWrapperToNative<CSSRule>(object); + default: + break; + } +#undef MAKE_CASE + + return FastDOMWrapperToNative<Peerable>(object); +} + + +v8::Handle<v8::Object> V8Proxy::LookupDOMWrapper( + V8ClassIndex::V8WrapperType type, v8::Handle<v8::Value> value) { + if (value.IsEmpty()) return v8::Handle<v8::Object>(); + + v8::Handle<v8::FunctionTemplate> desc = V8Proxy::GetTemplate(type); + while (value->IsObject()) { + v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value); + if (desc->HasInstance(object)) + return object; + + value = object->GetPrototype(); + } + return v8::Handle<v8::Object>(); +} + + +void* V8Proxy::ToNativeObjectImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object) { + // Native event listener is per frame, it cannot be handled + // by this generic function. + ASSERT(type != V8ClassIndex::EVENTLISTENER); + ASSERT(type != V8ClassIndex::EVENTTARGET); + + // It could be null, undefined, etc. + if (!MaybeDOMWrapper(object)) + return 0; + +#define MAKE_CASE(TYPE, NAME) case V8ClassIndex::TYPE: + switch (type) { + NODE_WRAPPER_TYPES(MAKE_CASE) + HTMLELEMENT_TYPES(MAKE_CASE) +#if ENABLE(SVG) + SVGNODE_WRAPPER_TYPES(MAKE_CASE) + SVGELEMENT_TYPES(MAKE_CASE) +#endif + return DOMWrapperToNative<Node>(object); + case V8ClassIndex::XMLHTTPREQUEST: + return DOMWrapperToNative<XMLHttpRequest>(object); + case V8ClassIndex::EVENT: + return DOMWrapperToNative<Event>(object); + case V8ClassIndex::CSSRULE: + return DOMWrapperToNative<CSSRule>(object); + default: + break; + } +#undef MAKE_CASE + + return DOMWrapperToNative<Peerable>(object); +} + + +NodeFilter* V8Proxy::ToNativeNodeFilter(v8::Handle<v8::Value> filter) { + // A NodeFilter is used when walking through a DOM tree or iterating tree + // nodes. + // TODO: 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. No need to use peer field in this case. + if (!filter->IsFunction()) return 0; + + NodeFilterCondition* cond = new V8NodeFilterCondition(filter); + return new NodeFilter(cond); +} + + +v8::Local<v8::Object> V8Proxy::InstantiateV8Object( + V8ClassIndex::V8WrapperType type, void* imp) { + V8ClassIndex::V8WrapperType wrapper_type = type; + // Make a special case for document.all + if (type == V8ClassIndex::HTMLCOLLECTION && + static_cast<HTMLCollection*>(imp)->type() == HTMLCollection::DocAll) { + wrapper_type = V8ClassIndex::UNDETECTABLEHTMLCOLLECTION; + } + + // Special case for HTMLInputElements that support selection. + if (type == V8ClassIndex::HTMLINPUTELEMENT) { + HTMLInputElement* element = static_cast<HTMLInputElement*>(imp); + if (element->canHaveSelection()) { + wrapper_type = V8ClassIndex::HTMLSELECTIONINPUTELEMENT; + } + } + + v8::Persistent<v8::FunctionTemplate> desc = GetTemplate(wrapper_type); + v8::Local<v8::Function> function = desc->GetFunction(); + v8::Local<v8::Object> instance = SafeAllocation::NewInstance(function); + if (!instance.IsEmpty()) { + // Avoid setting the DOM wrapper for failed allocations. + SetDOMWrapper(instance, V8ClassIndex::ToInt(type), imp); + } + return instance; +} + +v8::Handle<v8::Value> V8Proxy::CheckNewLegal(const v8::Arguments& args) { + if (!AllowAllocation::m_current) { + return ThrowError(TYPE_ERROR, "Illegal constructor"); + } else { + return args.This(); + } +} + + +v8::Handle<v8::Value> V8Proxy::WrapCPointer(void* cptr) { + // Represent void* as int + int addr = reinterpret_cast<int>(cptr); + if ((addr & 0x01) == 0) { + return v8::Number::New(addr >> 1); + } else { + return v8::External::New(cptr); + } +} + + +void* V8Proxy::ExtractCPointerImpl(v8::Handle<v8::Value> obj) { + if (obj->IsNumber()) { + int addr = obj->Int32Value(); + return reinterpret_cast<void*>(addr << 1); + } else if (obj->IsExternal()) { + return v8::Handle<v8::External>::Cast(obj)->Value(); + } + ASSERT(false); + return 0; +} + + +bool V8Proxy::SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* cptr) { + ASSERT(obj->InternalFieldCount() >= 2); + obj->SetInternalField(0, WrapCPointer(cptr)); + obj->SetInternalField(1, v8::Integer::New(type)); + return true; +} + + +bool V8Proxy::MaybeDOMWrapper(v8::Handle<v8::Value> value) { + if (value.IsEmpty() || !value->IsObject()) return false; + + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(value); + if (obj->InternalFieldCount() < 2) return false; + + v8::Handle<v8::Value> wrapper = obj->GetInternalField(0); + if (!wrapper->IsNumber() && !wrapper->IsExternal()) return false; + + v8::Handle<v8::Value> type = obj->GetInternalField(1); + if (!type->IsNumber()) return false; + + return true; +} + + +#define FOR_EACH_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 V8Proxy::GetHTMLElementType(HTMLElement* element) { + static HashMap<String, V8ClassIndex::V8WrapperType> map; + if (map.isEmpty()) { +#define ADD_TO_HASH_MAP(tag, name) \ + map.set(#tag, V8ClassIndex::HTML##name##ELEMENT); +FOR_EACH_TAG(ADD_TO_HASH_MAP) +#undef ADD_TO_HASH_MAP + } + + V8ClassIndex::V8WrapperType t = map.get(element->localName().impl()); + if (t == 0) return V8ClassIndex::HTMLELEMENT; + return t; +} +#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(circle, CIRCLE) \ + macro(clipPath, CLIPPATH) \ + macro(cursor, CURSOR) \ + macro(defs, DEFS) \ + macro(desc, DESC) \ + macro(ellipse, ELLIPSE) \ + macro(g, G) \ + 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 V8Proxy::GetSVGElementType(SVGElement* element) { + static HashMap<String, V8ClassIndex::V8WrapperType> map; + if (map.isEmpty()) { +#define ADD_TO_HASH_MAP(tag, name) \ + map.set(#tag, V8ClassIndex::SVG##name##ELEMENT); +FOR_EACH_TAG(ADD_TO_HASH_MAP) +#undef ADD_TO_HASH_MAP + } + + V8ClassIndex::V8WrapperType t = map.get(element->localName().impl()); + if (t == 0) return V8ClassIndex::SVGELEMENT; + return t; +} +#undef FOR_EACH_TAG + +#endif // ENABLE(SVG) + + +v8::Handle<v8::Value> V8Proxy::EventToV8Object(Event* event) { + if (!event) return v8::Null(); + + v8::Handle<v8::Object> peer = dom_object_map().get(event); + if (!peer.IsEmpty()) { + return peer; + } + + V8ClassIndex::V8WrapperType type = V8ClassIndex::EVENT; + + if (event->isKeyboardEvent()) + type = V8ClassIndex::KEYBOARDEVENT; + else if (event->isMouseEvent()) + type = V8ClassIndex::MOUSEEVENT; + else if (event->isMessageEvent()) + type = V8ClassIndex::MESSAGEEVENT; + else if (event->isWheelEvent()) + type = V8ClassIndex::WHEELEVENT; + else if (event->isTextEvent()) + type = V8ClassIndex::TEXTEVENT; + else if (event->isUIEvent()) + type = V8ClassIndex::UIEVENT; + else if (event->isMutationEvent()) + type = V8ClassIndex::MUTATIONEVENT; + else if (event->isOverflowEvent()) + type = V8ClassIndex::OVERFLOWEVENT; + else if (event->isProgressEvent()) + type = V8ClassIndex::PROGRESSEVENT; + + // Set the peer object for future access. + v8::Handle<v8::Object> result = InstantiateV8Object(type, 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(); + } + + dom_object_map().set(event, v8::Persistent<v8::Object>::New(result)); + + return result; +} + + +v8::Handle<v8::Object> V8Proxy::NodeToV8Object(Node* node) { + if (!node) return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> peer = dom_node_map().get(node); + if (!peer.IsEmpty()) { + return peer; + } + + bool is_document = false; // document type node has special handling + V8ClassIndex::V8WrapperType type; + + switch (node->nodeType()) { + case Node::ELEMENT_NODE: + if (node->isHTMLElement()) { + type = GetHTMLElementType(static_cast<HTMLElement*>(node)); +#if ENABLE(SVG) + } else if (node->isSVGElement()) { + type = GetSVGElementType(static_cast<SVGElement*>(node)); +#endif + } else { + type = V8ClassIndex::ELEMENT; + } + break; + case Node::ATTRIBUTE_NODE: + type = V8ClassIndex::ATTR; + break; + case Node::TEXT_NODE: + type = V8ClassIndex::TEXT; + break; + case Node::CDATA_SECTION_NODE: + type = V8ClassIndex::CDATASECTION; + break; + case Node::ENTITY_NODE: + type = V8ClassIndex::ENTITY; + break; + case Node::PROCESSING_INSTRUCTION_NODE: + type = V8ClassIndex::PROCESSINGINSTRUCTION; + break; + case Node::COMMENT_NODE: + type = V8ClassIndex::COMMENT; + break; + case Node::DOCUMENT_NODE: { + is_document = true; + Document* doc = static_cast<Document*>(node); + if (doc->isHTMLDocument()) { + type = V8ClassIndex::HTMLDOCUMENT; +#if ENABLE(SVG) + } else if (doc->isSVGDocument()) { + type = V8ClassIndex::SVGDOCUMENT; +#endif + } else { + type = V8ClassIndex::DOCUMENT; + } + break; + } + case Node::DOCUMENT_TYPE_NODE: + type = V8ClassIndex::DOCUMENTTYPE; + break; + case Node::NOTATION_NODE: + type = V8ClassIndex::NOTATION; + break; + case Node::DOCUMENT_FRAGMENT_NODE: + type = V8ClassIndex::DOCUMENTFRAGMENT; + break; + case Node::ENTITY_REFERENCE_NODE: + type = V8ClassIndex::ENTITYREFERENCE; + break; + default: + type = V8ClassIndex::NODE; + } + + // Set the peer object for future access. + // InstantiateV8Object automatically casts node to Peerable*. + v8::Local<v8::Object> result = InstantiateV8Object(type, node); + 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; + } + + dom_node_map().set(node, v8::Persistent<v8::Object>::New(result)); + + if (is_document) { + Document* doc = static_cast<Document*>(node); + V8Proxy* proxy = V8Proxy::retrieve(doc->frame()); + if (proxy) { + proxy->UpdateDocumentHandle(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; +} + + +void V8Proxy::UpdateDocumentHandle(v8::Local<v8::Object> handle) { + // If the old handle is not empty, release it. + if (!m_document.IsEmpty()) { +#ifndef NDEBUG + UnregisterGlobalHandle(this, m_document); +#endif + m_document.Dispose(); + m_document.Clear(); + } + + m_document = v8::Persistent<v8::Object>::New(handle); +#ifndef NDEBUG + RegisterGlobalHandle(PROXY, this, m_document); +#endif +} + + +// A JS object of type EventTarget can only be two possible types: +// 1) EventTargetNode; 2) XMLHttpRequest; +v8::Handle<v8::Value> V8Proxy::EventTargetToV8Object(EventTarget* target) { + if (!target) return v8::Null(); + +#if ENABLE(SVG) + SVGElementInstance* instance = target->toSVGElementInstance(); + if (instance) return ToV8Object(V8ClassIndex::SVGELEMENTINSTANCE, instance); +#endif + + Node* node = target->toNode(); + if (node) return NodeToV8Object(node); + + // XMLHttpRequest is created within its JS counterpart. + XMLHttpRequest* xhr = target->toXMLHttpRequest(); + if (xhr) { + v8::Handle<v8::Object> peer = dom_object_map().get(xhr); + ASSERT(!peer.IsEmpty()); + return peer; + } + + ASSERT(0); + return v8::Handle<v8::Value>(); +} + + +v8::Handle<v8::Value> V8Proxy::EventListenerToV8Object( + EventListener* listener) { + if (listener == 0) return v8::Null(); + + // TODO(fqian): 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> V8Proxy::DOMImplementationToV8Object( + DOMImplementation* impl) { + v8::Handle<v8::Object> result = + InstantiateV8Object(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::Object> V8Proxy::StyleSheetToV8Object(StyleSheet* sheet) { + if (!sheet) return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> peer = dom_object_map().get(sheet); + if (!peer.IsEmpty()) { + return peer; + } + + V8ClassIndex::V8WrapperType type = V8ClassIndex::STYLESHEET; + if (sheet->isCSSStyleSheet()) type = V8ClassIndex::CSSSTYLESHEET; + + v8::Handle<v8::Object> result = InstantiateV8Object(type, sheet); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + dom_object_map().set(sheet, v8::Persistent<v8::Object>::New(result)); + } + return result; +} + + +v8::Handle<v8::Object> V8Proxy::CSSValueToV8Object(CSSValue* value) { + if (!value) return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> peer = dom_object_map().get(value); + if (!peer.IsEmpty()) { + return peer; + } + + V8ClassIndex::V8WrapperType type; + + 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, value); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + dom_object_map().set(value, v8::Persistent<v8::Object>::New(result)); + } + return result; +} + + +v8::Handle<v8::Object> V8Proxy::CSSRuleToV8Object(CSSRule* rule) { + if (!rule) return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> peer = dom_object_map().get(rule); + if (!peer.IsEmpty()) { + return peer; + } + + 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; + default: // CSSRule::UNKNOWN_RULE + type = V8ClassIndex::CSSRULE; + } + + // Set the peer object for future access. + v8::Handle<v8::Object> result = InstantiateV8Object(type, rule); + if (!result.IsEmpty()) { + // Only update the DOM object map if the result is non-empty. + dom_object_map().set(rule, v8::Persistent<v8::Object>::New(result)); + } + return result; +} + +v8::Handle<v8::Object> V8Proxy::WindowToV8Object(DOMWindow* window) { + // Initializes environment of a frame, and return the global object + // of the frame. + Frame* frame = window->frame(); + if (!frame) return v8::Handle<v8::Object>(); + + v8::Handle<v8::Context> context = GetContext(frame); + if (context.IsEmpty()) return v8::Handle<v8::Object>(); + + v8::Handle<v8::Object> global = context->Global(); + ASSERT(!global.IsEmpty()); + return global; +} + + +void V8Proxy::BindJSObjectToWindow(Frame* frame, + const char* name, + int type, + v8::Handle<v8::FunctionTemplate> desc, + void* imp) { + // Get environment. + v8::Handle<v8::Context> context = V8Proxy::GetContext(frame); + if (context.IsEmpty()) return; // JS not enabled. + + v8::Context::Scope scope(context); + v8::Handle<v8::Object> instance = desc->GetFunction(); + SetDOMWrapper(instance, type, imp); + + v8::Handle<v8::Object> global = context->Global(); + global->Set(v8::String::New(name), instance); +} + + +void V8Proxy::ProcessConsoleMessages() { + ConsoleMessageManager::ProcessDelayedMessages(); +} + + +} // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_proxy.h b/webkit/port/bindings/v8/v8_proxy.h new file mode 100644 index 0000000..7bee90e --- /dev/null +++ b/webkit/port/bindings/v8/v8_proxy.h @@ -0,0 +1,571 @@ +// Copyright 2008, 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. + +#ifndef V8_PROXY_H__ +#define V8_PROXY_H__ + +#include <v8.h> +#include "v8_index.h" +#include "v8_custom.h" +#include "v8_utility.h" +#include "Node.h" +#include "NodeFilter.h" +#include "PlatformString.h" // for WebCore::String +#include <wtf/HashMap.h> // for HashMap + +#include <iterator> +#include <list> + +#ifdef ENABLE_DOM_STATS_COUNTERS +#include "base/stats_counters.h" +#define INC_STATS(name) StatsCounter(name).Increment() +#else +#define INC_STATS(name) +#endif + +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 EventTargetNode; +class StyleSheetList; +class CSSValue; +class CSSRule; +class CSSRuleList; +class CSSValueList; +class NodeFilter; + +#if ENABLE(SVG) +class SVGElementInstance; +#endif + +class V8EventListener; +class V8XHREventListener; +typedef std::list<V8EventListener*> V8EventListenerList; + +// 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(JSINSTANCE) \ + + +// 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 + +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_inlineCode(false), + m_timerCallback(false), m_recursion(0) { } + + ~V8Proxy(); + + // Clear security token by setting the security token + // for the context to the global object. + void ClearSecurityToken(); + + // Clear page-specific data, exception keep the global object identify. + void clear(); + + // Destroy the global object. + void DestroyGlobal(); + + Frame* frame() { return m_frame; } + + // 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(); + + // Remove 'document' property from the global object. + void clearDocumentWrapper(); + + // Find/Create/Remove event listener wrappers. + V8EventListener* FindV8EventListener(v8::Local<v8::Value> listener, + bool html); + V8EventListener* FindOrCreateV8EventListener(v8::Local<v8::Value> listener, + bool html); + + V8EventListener* FindXHREventListener(v8::Local<v8::Value> listener, + bool html); + V8EventListener* FindOrCreateXHREventListener(v8::Local<v8::Value> listener, + bool html); + + void RemoveV8EventListener(V8EventListener* listener); + void RemoveXHREventListener(V8XHREventListener* listener); + + // Protect/Unprotect JS wrappers of a DOM object. + static void GCProtect(Peerable* dom_object); + static void GCUnprotect(Peerable* dom_object); + + // Create a lazy event listener. + EventListener* createHTMLEventHandler(const String& functionName, + const String& code, Node* node); +#if ENABLE(SVG) + 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 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 String& filename, int baseLine, + const String& code, 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 window object of the currently executing context. + static DOMWindow* retrieveWindow(); + // Returns V8Proxy object of the currently executing context. + static V8Proxy* retrieve(); + // Returns V8Proxy object associated with a frame. + static V8Proxy* retrieve(Frame* frame); + // 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 + // an 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(); + + // Generate the security token for a context. + static v8::Handle<v8::Value> GenerateSecurityToken( + v8::Local<v8::Context> context); + + // Check if the active execution context is from the same origin + // as the target frame. + static bool IsFromSameOrigin(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); + + // Return true if the current security context can access the target frame. + static bool CanAccess(Frame* target); + + // Create a V8 wrapper for a C pointer + static v8::Handle<v8::Value> WrapCPointer(void* cptr); + + static v8::Handle<v8::Value> CheckNewLegal(const v8::Arguments& args); + + // 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); + + // Checks if a v8 value can be a DOM wrapper + static bool MaybeDOMWrapper(v8::Handle<v8::Value> obj); + + // Sets contents of a DOM wrapper, returns false if + // obj is not a DOM wrapper type + static bool 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) { + if (!MaybeDOMWrapper(object)) + return 0; + return ExtractCPointer<C>( + v8::Handle<v8::Object>::Cast(object)->GetInternalField(0)); + } + + // A helper function extract native object pointer from a DOM wrapper + // and cast to the specified type. + template <class C> + static C* FastDOMWrapperToNative(v8::Handle<v8::Value> object) { + ASSERT(MaybeDOMWrapper(object)); + return ExtractCPointer<C>( + v8::Handle<v8::Object>::Cast(object)->GetInternalField(0)); + } + + // 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> object) { + if (!MaybeDOMWrapper(object)) + return 0; + v8::Handle<v8::Value> wrapper = + v8::Handle<v8::Object>::Cast(object)->GetInternalField(0); + return static_cast<C*>(ExtractCPointer<Node>(wrapper)); + } + + static v8::Handle<v8::Value> ToV8Object(V8ClassIndex::V8WrapperType type, + void* imp); + + template <class C> + static C* FastToNativeObject(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object) { + return static_cast<C*>(FastToNativeObjectImpl(type, object)); + } + + 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 V8Proxy::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) { + 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. When wrapping the + // DOMImplementation object, the peer field is not set. + static v8::Handle<v8::Value> DOMImplementationToV8Object( + DOMImplementation* impl); + + // Wrap JS node filter in C++ + static 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); + + // Set JS wrapper of a DOM object + static void SetJSWrapperForDOMObject(Peerable* obj, + v8::Persistent<v8::Object> wrapper); + static void SetJSWrapperForDOMNode(Node* node, + v8::Persistent<v8::Object> wrapper); + + // Domain of a frame is changed, invalidate its security token. + static void DomainChanged(Frame* frame); + + // 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 + + private: + void initContextIfNeeded(); + void DisconnectEventListeners(); + + static void* ToNativeObjectImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object); + static void* FastToNativeObjectImpl(V8ClassIndex::V8WrapperType type, + v8::Handle<v8::Value> object); + + // Take C pointer out of a v8 wrapper + static void* ExtractCPointerImpl(v8::Handle<v8::Value> obj); + + static v8::Handle<v8::Object> NodeToV8Object(Node* node); + static v8::Handle<v8::Object> StyleSheetToV8Object(StyleSheet* sheet); + static v8::Handle<v8::Object> CSSValueToV8Object(CSSValue* value); + static v8::Handle<v8::Object> CSSRuleToV8Object(CSSRule* rule); + // Returns the JS wrapper of a window object, initializes the environment + // of the window frame if needed. + static v8::Handle<v8::Object> V8Proxy::WindowToV8Object(DOMWindow* window); + +#if ENABLE(SVG) + static v8::Handle<v8::Object> SVGElementInstanceToV8Object( + SVGElementInstance* instance); + static v8::Handle<v8::Object> SVGObjectWithContextToV8Object( + Peerable* object, V8ClassIndex::V8WrapperType type); +#endif + + static V8ClassIndex::V8WrapperType GetHTMLElementType(HTMLElement* elm); + + static v8::Local<v8::Object> InstantiateV8Object( + V8ClassIndex::V8WrapperType 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 + + // Update m_document field, dispose old one and create a string reference + // to the new one. + void UpdateDocumentHandle(v8::Local<v8::Object> handle); + + // Returns a local handle of the context. + v8::Local<v8::Context> GetContext() { + return v8::Local<v8::Context>::New(m_context); + } + + Frame* m_frame; + v8::Persistent<v8::Context> m_context; + v8::Persistent<v8::Object> m_global; + + // Special handling of document wrapper; + v8::Persistent<v8::Object> m_document; + + 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; +}; + + +// Add indexed getter to the function template for a collection. +template <class T> +static void SetCollectionIndexedGetter(v8::Handle<v8::FunctionTemplate> desc, + V8ClassIndex::V8WrapperType type) { + desc->InstanceTemplate()->SetIndexedPropertyHandler( + CollectionIndexedPropertyGetter<T>, + 0, + 0, + 0, + CollectionIndexedPropertyEnumerator<T>, + v8::External::New(reinterpret_cast<void*>(type))); +} + +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(); + } + T* obj = new T(); + V8Proxy::SetDOMWrapper(args.Holder(), tag, obj); + V8Proxy::SetJSWrapperForDOMObject( + obj, v8::Persistent<v8::Object>::New(args.Holder())); + return args.Holder(); +} + +// Add named getter to the function template for a collection. +template <class T> +static void SetCollectionNamedGetter(v8::Handle<v8::FunctionTemplate> desc, + V8ClassIndex::V8WrapperType type) { + desc->InstanceTemplate()->SetNamedPropertyHandler( + CollectionNamedPropertyGetter<T>, + 0, + 0, + 0, + 0, + v8::External::New(reinterpret_cast<void*>(type))); +} + + +// Add named and indexed getters to the function template for a collection. +template <class T> +static void SetCollectionIndexedAndNamedGetters( + v8::Handle<v8::FunctionTemplate> desc, V8ClassIndex::V8WrapperType type) { + // If we interceptor before object, accessing 'length' can trigger + // a webkit assertion error. + // (see fast/dom/HTMLDocument/document-special-properties.html + desc->InstanceTemplate()->SetNamedPropertyHandler( + CollectionNamedPropertyGetter<T>, + 0, + 0, + 0, + 0, + v8::External::New(reinterpret_cast<void*>(type))); + desc->InstanceTemplate()->SetIndexedPropertyHandler( + CollectionIndexedPropertyGetter<T>, + 0, + 0, + 0, + CollectionIndexedPropertyEnumerator<T>, + v8::External::New(reinterpret_cast<void*>(type))); +} + + +// Add indexed getter returning a string or null to a function template +// for a collection. +template <class T> +static void SetCollectionStringOrNullIndexedGetter( + v8::Handle<v8::FunctionTemplate> desc) { + desc->InstanceTemplate()->SetIndexedPropertyHandler( + CollectionStringOrNullIndexedPropertyGetter<T>, + 0, + 0, + 0, + CollectionIndexedPropertyEnumerator<T>); +} + + +} // namespace WebCore + +#endif // V8_PROXY_H__ diff --git a/webkit/port/bindings/v8/v8_utility.h b/webkit/port/bindings/v8/v8_utility.h new file mode 100644 index 0000000..91322b5 --- /dev/null +++ b/webkit/port/bindings/v8/v8_utility.h @@ -0,0 +1,82 @@ +// Copyright 2008, 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. + +#ifndef V8_UTILITY_H__ +#define V8_UTILITY_H__ + +namespace WebCore { + +class AllowAllocation { + public: + inline AllowAllocation() { + m_prev = m_current; + m_current = true; + } + inline ~AllowAllocation() { + m_current = m_prev; + } + static bool m_current; + private: + bool m_prev; +}; + +class SafeAllocation { + public: + static inline v8::Local<v8::Object> NewInstance( + v8::Handle<v8::Function> fun); + static inline v8::Local<v8::Object> NewInstance( + v8::Handle<v8::ObjectTemplate> templ); + static inline v8::Local<v8::Object> NewInstance( + v8::Handle<v8::Function> fun, int argc, v8::Handle<v8::Value> argv[]); +}; + +v8::Local<v8::Object> SafeAllocation::NewInstance( + v8::Handle<v8::Function> fun) { + if (fun.IsEmpty()) return v8::Local<v8::Object>(); + AllowAllocation allow; + return fun->NewInstance(); +} + +v8::Local<v8::Object> SafeAllocation::NewInstance( + v8::Handle<v8::ObjectTemplate> templ) { + if (templ.IsEmpty()) return v8::Local<v8::Object>(); + AllowAllocation allow; + return templ->NewInstance(); +} + +v8::Local<v8::Object> SafeAllocation::NewInstance( + v8::Handle<v8::Function> fun, int argc, v8::Handle<v8::Value> argv[]) { + if (fun.IsEmpty()) return v8::Local<v8::Object>(); + AllowAllocation allow; + return fun->NewInstance(argc, argv); +} + +} // namespace WebCore + +#endif // V8_UTILITY_H__ diff --git a/webkit/port/bindings/v8/v8_vectornodelist.cpp b/webkit/port/bindings/v8/v8_vectornodelist.cpp new file mode 100644 index 0000000..7e19cf8 --- /dev/null +++ b/webkit/port/bindings/v8/v8_vectornodelist.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2008, 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 "v8_vectornodelist.h" +#include "NamedAttrMap.h" // Node::attributes + +namespace WebCore { + +unsigned V8VectorNodeList::length() const { + return m_nodes.size(); +} + +Node* V8VectorNodeList::item(unsigned index) const { + if (index < m_nodes.size()) + return m_nodes[index].get(); + return 0; +} + +Node* V8VectorNodeList::itemWithName(const AtomicString& id) const { + for (unsigned i = 0; i < m_nodes.size(); i++) { + Node* node = m_nodes[i].get(); + if (node->hasAttributes() && node->attributes()->id() == id) { + return node; + } + } + return 0; +} + +} // namespace WebCore diff --git a/webkit/port/bindings/v8/v8_vectornodelist.h b/webkit/port/bindings/v8/v8_vectornodelist.h new file mode 100644 index 0000000..e4cd61c --- /dev/null +++ b/webkit/port/bindings/v8/v8_vectornodelist.h @@ -0,0 +1,58 @@ +// Copyright 2008, 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. + +#ifndef V8_VECTORNODELIST_H__ +#define V8_VECTORNODELIST_H__ + +#include "config.h" +#include <v8.h> +#include "Node.h" +#include "NodeList.h" +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// A NodeList backed by a Vector of Nodes. +class V8VectorNodeList : public NodeList { + public: + explicit V8VectorNodeList(const Vector<RefPtr<Node> >& nodes) + : m_nodes(nodes) { } + virtual unsigned length() const; + virtual Node* item(unsigned index) const; + virtual Node* itemWithName(const AtomicString& name) const; + + private: + Vector<RefPtr<Node> > m_nodes; +}; + + +} // namespace WebCore + +#endif // V8_VECTORNODELIST_H__ |