diff -r 000000000000 -r 4f2f89ce4247 WebCore/bindings/v8/V8DOMWrapper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebCore/bindings/v8/V8DOMWrapper.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "V8DOMWrapper.h" + +#include "CSSMutableStyleDeclaration.h" +#include "DOMDataStore.h" +#include "DocumentLoader.h" +#include "FrameLoaderClient.h" +#include "Notification.h" +#include "ScriptController.h" +#include "V8AbstractEventListener.h" +#include "V8Binding.h" +#include "V8Collection.h" +#include "V8CustomEventListener.h" +#include "V8DedicatedWorkerContext.h" +#include "V8DOMApplicationCache.h" +#include "V8DOMMap.h" +#include "V8DOMWindow.h" +#include "V8EventListenerList.h" +#include "V8EventSource.h" +#include "V8FileReader.h" +#include "V8HTMLCollection.h" +#include "V8HTMLDocument.h" +#include "V8IDBRequest.h" +#include "V8IsolatedContext.h" +#include "V8Location.h" +#include "V8MessageChannel.h" +#include "V8NamedNodeMap.h" +#include "V8Node.h" +#include "V8NodeFilterCondition.h" +#include "V8NodeList.h" +#include "V8Notification.h" +#include "V8Proxy.h" +#include "V8SharedWorker.h" +#include "V8SharedWorkerContext.h" +#include "V8StyleSheet.h" +#include "V8WebSocket.h" +#include "V8Worker.h" +#include "V8WorkerContext.h" +#include "V8WorkerContextEventListener.h" +#include "V8XMLHttpRequest.h" +#include "ArrayBufferView.h" +#include "WebGLContextAttributes.h" +#include "WebGLUniformLocation.h" +#include "WorkerContextExecutionProxy.h" +#include "WrapperTypeInfo.h" + +#if ENABLE(SVG) +#include "SVGElementInstance.h" +#include "SVGPathSeg.h" +#include "V8SVGElementInstance.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { + +typedef HashMap DOMNodeMap; +typedef HashMap DOMObjectMap; + +// The caller must have increased obj's ref count. +void V8DOMWrapper::setJSWrapperForDOMObject(void* object, v8::Persistent wrapper) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper)); + ASSERT(!domWrapperType(wrapper)->toActiveDOMObjectFunction); + getDOMObjectMap().set(object, wrapper); +} + +// The caller must have increased obj's ref count. +void V8DOMWrapper::setJSWrapperForActiveDOMObject(void* object, v8::Persistent wrapper) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper)); + ASSERT(domWrapperType(wrapper)->toActiveDOMObjectFunction); + getActiveDOMObjectMap().set(object, wrapper); +} + +// The caller must have increased node's ref count. +void V8DOMWrapper::setJSWrapperForDOMNode(Node* node, v8::Persistent wrapper) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper)); + getDOMNodeMap().set(node, wrapper); +} + +v8::Local V8DOMWrapper::getConstructor(WrapperTypeInfo* type, v8::Handle objectPrototype) +{ + // A DOM constructor is a function instance created from a DOM constructor + // template. There is one instance per context. A DOM constructor is + // different from a normal function in two ways: + // 1) it cannot be called as constructor (aka, used to create a DOM object) + // 2) its __proto__ points to Object.prototype rather than + // Function.prototype. + // The reason for 2) is that, in Safari, a DOM constructor is a normal JS + // object, but not a function. Hotmail relies on the fact that, in Safari, + // HTMLElement.__proto__ == Object.prototype. + v8::Handle functionTemplate = type->getTemplate(); + // Getting the function might fail if we're running out of + // stack or memory. + v8::TryCatch tryCatch; + v8::Local value = functionTemplate->GetFunction(); + if (value.IsEmpty()) + return v8::Local(); + // Hotmail fix, see comments above. + if (!objectPrototype.IsEmpty()) + value->SetPrototype(objectPrototype); + return value; +} + +v8::Local V8DOMWrapper::getConstructorForContext(WrapperTypeInfo* type, v8::Handle context) +{ + // Enter the scope for this context to get the correct constructor. + v8::Context::Scope scope(context); + + return getConstructor(type, V8DOMWindowShell::getHiddenObjectPrototype(context)); +} + +v8::Local V8DOMWrapper::getConstructor(WrapperTypeInfo* type, DOMWindow* window) +{ + Frame* frame = window->frame(); + if (!frame) + return v8::Local(); + + v8::Handle context = V8Proxy::context(frame); + if (context.IsEmpty()) + return v8::Local(); + + return getConstructorForContext(type, context); +} + +#if ENABLE(WORKERS) +v8::Local V8DOMWrapper::getConstructor(WrapperTypeInfo* type, WorkerContext*) +{ + WorkerScriptController* controller = WorkerScriptController::controllerForContext(); + WorkerContextExecutionProxy* proxy = controller ? controller->proxy() : 0; + if (!proxy) + return v8::Local(); + + v8::Handle context = proxy->context(); + if (context.IsEmpty()) + return v8::Local(); + + return getConstructorForContext(type, context); +} +#endif + +void V8DOMWrapper::setHiddenReference(v8::Handle parent, v8::Handle child) +{ + v8::Local hiddenReferenceObject = parent->GetInternalField(v8DOMHiddenReferenceArrayIndex); + if (hiddenReferenceObject->IsNull() || hiddenReferenceObject->IsUndefined()) { + hiddenReferenceObject = v8::Array::New(); + parent->SetInternalField(v8DOMHiddenReferenceArrayIndex, hiddenReferenceObject); + } + v8::Local hiddenReferenceArray = v8::Local::Cast(hiddenReferenceObject); + hiddenReferenceArray->Set(v8::Integer::New(hiddenReferenceArray->Length()), child); +} + +void V8DOMWrapper::setHiddenWindowReference(Frame* frame, v8::Handle jsObject) +{ + // Get DOMWindow + if (!frame) + return; // Object might be detached from window + v8::Handle context = V8Proxy::context(frame); + if (context.IsEmpty()) + return; + + v8::Handle global = context->Global(); + // Look for real DOM wrapper. + global = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), global); + ASSERT(!global.IsEmpty()); + + setHiddenReference(global, jsObject); +} + +WrapperTypeInfo* V8DOMWrapper::domWrapperType(v8::Handle object) +{ + ASSERT(V8DOMWrapper::maybeDOMWrapper(object)); + return static_cast(object->GetPointerFromInternalField(v8DOMWrapperTypeIndex)); +} + +PassRefPtr V8DOMWrapper::wrapNativeNodeFilter(v8::Handle filter) +{ + // A NodeFilter is used when walking through a DOM tree or iterating tree + // nodes. + // FIXME: we may want to cache NodeFilterCondition and NodeFilter + // object, but it is minor. + // NodeFilter is passed to NodeIterator that has a ref counted pointer + // to NodeFilter. NodeFilter has a ref counted pointer to NodeFilterCondition. + // In NodeFilterCondition, filter object is persisted in its constructor, + // and disposed in its destructor. + if (!filter->IsFunction()) + return 0; + + return NodeFilter::create(V8NodeFilterCondition::create(filter)); +} + +static bool globalObjectPrototypeIsDOMWindow(v8::Handle objectPrototype) +{ + // We can identify what type of context the global object is wrapping by looking at the + // internal field count of its prototype. This assumes WorkerContexts and DOMWindows have different numbers + // of internal fields, so a COMPILE_ASSERT is included to warn if this ever changes. +#if ENABLE(WORKERS) + COMPILE_ASSERT(V8DOMWindow::internalFieldCount != V8WorkerContext::internalFieldCount, + DOMWindowAndWorkerContextHaveUnequalFieldCounts); + COMPILE_ASSERT(V8DOMWindow::internalFieldCount != V8DedicatedWorkerContext::internalFieldCount, + DOMWindowAndDedicatedWorkerContextHaveUnequalFieldCounts); +#endif +#if ENABLE(SHARED_WORKERS) + COMPILE_ASSERT(V8DOMWindow::internalFieldCount != V8SharedWorkerContext::internalFieldCount, + DOMWindowAndSharedWorkerContextHaveUnequalFieldCounts); +#endif + return objectPrototype->InternalFieldCount() == V8DOMWindow::internalFieldCount; +} + +v8::Local V8DOMWrapper::instantiateV8Object(V8Proxy* proxy, WrapperTypeInfo* type, void* impl) +{ + WorkerContext* workerContext = 0; + if (V8IsolatedContext::getEntered()) { + // This effectively disables the wrapper cache for isolated worlds. + proxy = 0; + // FIXME: Do we need a wrapper cache for the isolated world? We should + // see if the performance gains are worth while. + // We'll get one once we give the isolated context a proper window shell. + } else if (!proxy) { + v8::Handle context = v8::Context::GetCurrent(); + if (!context.IsEmpty()) { + v8::Handle globalPrototype = v8::Handle::Cast(context->Global()->GetPrototype()); + if (globalObjectPrototypeIsDOMWindow(globalPrototype)) + proxy = V8Proxy::retrieve(V8DOMWindow::toNative(globalPrototype)->frame()); +#if ENABLE(WORKERS) + else + workerContext = V8WorkerContext::toNative(lookupDOMWrapper(V8WorkerContext::GetTemplate(), context->Global())); +#endif + } + } + + v8::Local instance; + if (proxy) + // FIXME: Fix this to work properly with isolated worlds (see above). + instance = proxy->windowShell()->createWrapperFromCache(type); + else { + v8::Local function; +#if ENABLE(WORKERS) + if (workerContext) + function = getConstructor(type, workerContext); + else +#endif + function = type->getTemplate()->GetFunction(); + instance = SafeAllocation::newInstance(function); + } + if (!instance.IsEmpty()) { + // Avoid setting the DOM wrapper for failed allocations. + setDOMWrapper(instance, type, impl); + } + return instance; +} + +#ifndef NDEBUG +bool V8DOMWrapper::maybeDOMWrapper(v8::Handle value) +{ + if (value.IsEmpty() || !value->IsObject()) + return false; + + v8::Handle object = v8::Handle::Cast(value); + if (!object->InternalFieldCount()) + return false; + + ASSERT(object->InternalFieldCount() >= v8DefaultWrapperInternalFieldCount); + + v8::Handle wrapper = object->GetInternalField(v8DOMWrapperObjectIndex); + ASSERT(wrapper->IsNumber() || wrapper->IsExternal()); + + return true; +} +#endif + +bool V8DOMWrapper::isValidDOMObject(v8::Handle value) +{ + if (value.IsEmpty() || !value->IsObject()) + return false; + return v8::Handle::Cast(value)->InternalFieldCount(); +} + +bool V8DOMWrapper::isWrapperOfType(v8::Handle value, WrapperTypeInfo* type) +{ + if (!isValidDOMObject(value)) + return false; + + v8::Handle object = v8::Handle::Cast(value); + ASSERT(object->InternalFieldCount() >= v8DefaultWrapperInternalFieldCount); + + v8::Handle wrapper = object->GetInternalField(v8DOMWrapperObjectIndex); + ASSERT(wrapper->IsNumber() || wrapper->IsExternal()); + + WrapperTypeInfo* typeInfo = static_cast(object->GetPointerFromInternalField(v8DOMWrapperTypeIndex)); + return typeInfo == type; +} + +v8::Handle V8DOMWrapper::getWrapper(Node* node) +{ + ASSERT(WTF::isMainThread()); + V8IsolatedContext* context = V8IsolatedContext::getEntered(); + if (LIKELY(!context)) { + v8::Persistent* wrapper = node->wrapper(); + if (!wrapper) + return v8::Handle(); + return *wrapper; + } + + DOMNodeMapping& domNodeMap = context->world()->domDataStore()->domNodeMap(); + return domNodeMap.get(node); +} + +// A JS object of type EventTarget is limited to a small number of possible classes. +// Check EventTarget.h for new type conversion methods +v8::Handle V8DOMWrapper::convertEventTargetToV8Object(EventTarget* target) +{ + if (!target) + return v8::Null(); + +#if ENABLE(SVG) + if (SVGElementInstance* instance = target->toSVGElementInstance()) + return toV8(instance); +#endif + +#if ENABLE(WORKERS) + if (Worker* worker = target->toWorker()) + return toV8(worker); + + if (DedicatedWorkerContext* workerContext = target->toDedicatedWorkerContext()) + return toV8(workerContext); +#endif // WORKERS + +#if ENABLE(SHARED_WORKERS) + if (SharedWorker* sharedWorker = target->toSharedWorker()) + return toV8(sharedWorker); + + if (SharedWorkerContext* sharedWorkerContext = target->toSharedWorkerContext()) + return toV8(sharedWorkerContext); +#endif // SHARED_WORKERS + +#if ENABLE(NOTIFICATIONS) + if (Notification* notification = target->toNotification()) + return toV8(notification); +#endif + +#if ENABLE(INDEXED_DATABASE) + if (IDBRequest* idbRequest = target->toIDBRequest()) + return toV8(idbRequest); +#endif + +#if ENABLE(WEB_SOCKETS) + if (WebSocket* webSocket = target->toWebSocket()) + return toV8(webSocket); +#endif + + if (Node* node = target->toNode()) + return toV8(node); + + if (DOMWindow* domWindow = target->toDOMWindow()) + return toV8(domWindow); + + // XMLHttpRequest is created within its JS counterpart. + if (XMLHttpRequest* xmlHttpRequest = target->toXMLHttpRequest()) { + v8::Handle wrapper = getActiveDOMObjectMap().get(xmlHttpRequest); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + + // MessagePort is created within its JS counterpart + if (MessagePort* port = target->toMessagePort()) { + v8::Handle wrapper = getActiveDOMObjectMap().get(port); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + + if (XMLHttpRequestUpload* upload = target->toXMLHttpRequestUpload()) { + v8::Handle wrapper = getDOMObjectMap().get(upload); + ASSERT(!wrapper.IsEmpty()); + return wrapper; + } + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (DOMApplicationCache* domAppCache = target->toDOMApplicationCache()) + return toV8(domAppCache); +#endif + +#if ENABLE(EVENTSOURCE) + if (EventSource* eventSource = target->toEventSource()) + return toV8(eventSource); +#endif + +#if ENABLE(FILE_READER) + if (FileReader* fileReader = target->toFileReader()) + return toV8(fileReader); +#endif + + ASSERT(0); + return notHandledByInterceptor(); +} + +PassRefPtr V8DOMWrapper::getEventListener(v8::Local value, bool isAttribute, ListenerLookupType lookup) +{ + v8::Handle context = v8::Context::GetCurrent(); + if (context.IsEmpty()) + return 0; + if (lookup == ListenerFindOnly) + return V8EventListenerList::findWrapper(value, isAttribute); + v8::Handle globalPrototype = v8::Handle::Cast(context->Global()->GetPrototype()); + if (globalObjectPrototypeIsDOMWindow(globalPrototype)) + return V8EventListenerList::findOrCreateWrapper(value, isAttribute); +#if ENABLE(WORKERS) + return V8EventListenerList::findOrCreateWrapper(value, isAttribute); +#else + return 0; +#endif +} + +} // namespace WebCore