Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "V8DOMWrapper.h"
     33 
     34 #include "CSSMutableStyleDeclaration.h"
     35 #include "DOMDataStore.h"
     36 #include "DocumentLoader.h"
     37 #include "FrameLoaderClient.h"
     38 #include "Notification.h"
     39 #include "ScriptController.h"
     40 #include "V8AbstractEventListener.h"
     41 #include "V8Binding.h"
     42 #include "V8Collection.h"
     43 #include "V8DedicatedWorkerContext.h"
     44 #include "V8DOMApplicationCache.h"
     45 #include "V8DOMMap.h"
     46 #include "V8DOMWindow.h"
     47 #include "V8EventListener.h"
     48 #include "V8EventListenerList.h"
     49 #include "V8EventSource.h"
     50 #include "V8FileReader.h"
     51 #include "V8FileWriter.h"
     52 #include "V8HTMLCollection.h"
     53 #include "V8HTMLDocument.h"
     54 #include "V8IDBDatabase.h"
     55 #include "V8IDBRequest.h"
     56 #include "V8IDBTransaction.h"
     57 #include "V8IsolatedContext.h"
     58 #include "V8Location.h"
     59 #include "V8MessageChannel.h"
     60 #include "V8NamedNodeMap.h"
     61 #include "V8Node.h"
     62 #include "V8NodeFilterCondition.h"
     63 #include "V8NodeList.h"
     64 #include "V8Notification.h"
     65 #include "V8Proxy.h"
     66 #include "V8SharedWorker.h"
     67 #include "V8SharedWorkerContext.h"
     68 #include "V8StyleSheet.h"
     69 #include "V8WebSocket.h"
     70 #include "V8Worker.h"
     71 #include "V8WorkerContext.h"
     72 #include "V8WorkerContextEventListener.h"
     73 #include "V8XMLHttpRequest.h"
     74 #include "ArrayBufferView.h"
     75 #include "WebGLContextAttributes.h"
     76 #include "WebGLUniformLocation.h"
     77 #include "WorkerContextExecutionProxy.h"
     78 #include "WrapperTypeInfo.h"
     79 
     80 #if ENABLE(SVG)
     81 #include "SVGElementInstance.h"
     82 #include "SVGPathSeg.h"
     83 #include "V8SVGElementInstance.h"
     84 #endif
     85 
     86 #if ENABLE(WEB_AUDIO)
     87 #include "V8AudioContext.h"
     88 #include "V8JavaScriptAudioNode.h"
     89 #endif
     90 
     91 #include <algorithm>
     92 #include <utility>
     93 #include <v8-debug.h>
     94 #include <wtf/Assertions.h>
     95 #include <wtf/OwnArrayPtr.h>
     96 #include <wtf/StdLibExtras.h>
     97 #include <wtf/UnusedParam.h>
     98 
     99 namespace WebCore {
    100 
    101 typedef HashMap<Node*, v8::Object*> DOMNodeMap;
    102 typedef HashMap<void*, v8::Object*> DOMObjectMap;
    103 
    104 // The caller must have increased obj's ref count.
    105 void V8DOMWrapper::setJSWrapperForDOMObject(void* object, v8::Persistent<v8::Object> wrapper)
    106 {
    107     ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper));
    108     ASSERT(!domWrapperType(wrapper)->toActiveDOMObjectFunction);
    109     getDOMObjectMap().set(object, wrapper);
    110 }
    111 
    112 // The caller must have increased obj's ref count.
    113 void V8DOMWrapper::setJSWrapperForActiveDOMObject(void* object, v8::Persistent<v8::Object> wrapper)
    114 {
    115     ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper));
    116     ASSERT(domWrapperType(wrapper)->toActiveDOMObjectFunction);
    117     getActiveDOMObjectMap().set(object, wrapper);
    118 }
    119 
    120 // The caller must have increased node's ref count.
    121 void V8DOMWrapper::setJSWrapperForDOMNode(Node* node, v8::Persistent<v8::Object> wrapper)
    122 {
    123     ASSERT(V8DOMWrapper::maybeDOMWrapper(wrapper));
    124     getDOMNodeMap().set(node, wrapper);
    125 }
    126 
    127 v8::Local<v8::Function> V8DOMWrapper::getConstructor(WrapperTypeInfo* type, v8::Handle<v8::Value> objectPrototype)
    128 {
    129     // A DOM constructor is a function instance created from a DOM constructor
    130     // template. There is one instance per context. A DOM constructor is
    131     // different from a normal function in two ways:
    132     //   1) it cannot be called as constructor (aka, used to create a DOM object)
    133     //   2) its __proto__ points to Object.prototype rather than
    134     //      Function.prototype.
    135     // The reason for 2) is that, in Safari, a DOM constructor is a normal JS
    136     // object, but not a function. Hotmail relies on the fact that, in Safari,
    137     // HTMLElement.__proto__ == Object.prototype.
    138     v8::Handle<v8::FunctionTemplate> functionTemplate = type->getTemplate();
    139     // Getting the function might fail if we're running out of
    140     // stack or memory.
    141     v8::TryCatch tryCatch;
    142     v8::Local<v8::Function> value = functionTemplate->GetFunction();
    143     if (value.IsEmpty())
    144         return v8::Local<v8::Function>();
    145     // Hotmail fix, see comments above.
    146     if (!objectPrototype.IsEmpty())
    147         value->SetPrototype(objectPrototype);
    148     return value;
    149 }
    150 
    151 v8::Local<v8::Function> V8DOMWrapper::getConstructorForContext(WrapperTypeInfo* type, v8::Handle<v8::Context> context)
    152 {
    153     // Enter the scope for this context to get the correct constructor.
    154     v8::Context::Scope scope(context);
    155 
    156     return getConstructor(type, V8DOMWindowShell::getHiddenObjectPrototype(context));
    157 }
    158 
    159 v8::Local<v8::Function> V8DOMWrapper::getConstructor(WrapperTypeInfo* type, DOMWindow* window)
    160 {
    161     Frame* frame = window->frame();
    162     if (!frame)
    163         return v8::Local<v8::Function>();
    164 
    165     v8::Handle<v8::Context> context = V8Proxy::context(frame);
    166     if (context.IsEmpty())
    167         return v8::Local<v8::Function>();
    168 
    169     return getConstructorForContext(type, context);
    170 }
    171 
    172 #if ENABLE(WORKERS)
    173 v8::Local<v8::Function> V8DOMWrapper::getConstructor(WrapperTypeInfo* type, WorkerContext*)
    174 {
    175     WorkerScriptController* controller = WorkerScriptController::controllerForContext();
    176     WorkerContextExecutionProxy* proxy = controller ? controller->proxy() : 0;
    177     if (!proxy)
    178         return v8::Local<v8::Function>();
    179 
    180     v8::Handle<v8::Context> context = proxy->context();
    181     if (context.IsEmpty())
    182         return v8::Local<v8::Function>();
    183 
    184     return getConstructorForContext(type, context);
    185 }
    186 #endif
    187 
    188 void V8DOMWrapper::setHiddenReference(v8::Handle<v8::Object> parent, v8::Handle<v8::Value> child)
    189 {
    190     v8::Local<v8::Value> hiddenReferenceObject = parent->GetInternalField(v8DOMHiddenReferenceArrayIndex);
    191     if (hiddenReferenceObject->IsNull() || hiddenReferenceObject->IsUndefined()) {
    192         hiddenReferenceObject = v8::Array::New();
    193         parent->SetInternalField(v8DOMHiddenReferenceArrayIndex, hiddenReferenceObject);
    194     }
    195     v8::Local<v8::Array> hiddenReferenceArray = v8::Local<v8::Array>::Cast(hiddenReferenceObject);
    196     hiddenReferenceArray->Set(v8::Integer::New(hiddenReferenceArray->Length()), child);
    197 }
    198 
    199 void V8DOMWrapper::setHiddenWindowReference(Frame* frame, v8::Handle<v8::Value> jsObject)
    200 {
    201     // Get DOMWindow
    202     if (!frame)
    203         return; // Object might be detached from window
    204     v8::Handle<v8::Context> context = V8Proxy::context(frame);
    205     if (context.IsEmpty())
    206         return;
    207 
    208     v8::Handle<v8::Object> global = context->Global();
    209     // Look for real DOM wrapper.
    210     global = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), global);
    211     ASSERT(!global.IsEmpty());
    212 
    213     setHiddenReference(global, jsObject);
    214 }
    215 
    216 WrapperTypeInfo* V8DOMWrapper::domWrapperType(v8::Handle<v8::Object> object)
    217 {
    218     ASSERT(V8DOMWrapper::maybeDOMWrapper(object));
    219     return static_cast<WrapperTypeInfo*>(object->GetPointerFromInternalField(v8DOMWrapperTypeIndex));
    220 }
    221 
    222 PassRefPtr<NodeFilter> V8DOMWrapper::wrapNativeNodeFilter(v8::Handle<v8::Value> filter)
    223 {
    224     // A NodeFilter is used when walking through a DOM tree or iterating tree
    225     // nodes.
    226     // FIXME: we may want to cache NodeFilterCondition and NodeFilter
    227     // object, but it is minor.
    228     // NodeFilter is passed to NodeIterator that has a ref counted pointer
    229     // to NodeFilter. NodeFilter has a ref counted pointer to NodeFilterCondition.
    230     // In NodeFilterCondition, filter object is persisted in its constructor,
    231     // and disposed in its destructor.
    232     return NodeFilter::create(V8NodeFilterCondition::create(filter));
    233 }
    234 
    235 static bool globalObjectPrototypeIsDOMWindow(v8::Handle<v8::Object> objectPrototype)
    236 {
    237     // We can identify what type of context the global object is wrapping by looking at the
    238     // internal field count of its prototype. This assumes WorkerContexts and DOMWindows have different numbers
    239     // of internal fields, so a COMPILE_ASSERT is included to warn if this ever changes.
    240 #if ENABLE(WORKERS)
    241     COMPILE_ASSERT(V8DOMWindow::internalFieldCount != V8WorkerContext::internalFieldCount,
    242         DOMWindowAndWorkerContextHaveUnequalFieldCounts);
    243     COMPILE_ASSERT(V8DOMWindow::internalFieldCount != V8DedicatedWorkerContext::internalFieldCount,
    244         DOMWindowAndDedicatedWorkerContextHaveUnequalFieldCounts);
    245 #endif
    246 #if ENABLE(SHARED_WORKERS)
    247     COMPILE_ASSERT(V8DOMWindow::internalFieldCount != V8SharedWorkerContext::internalFieldCount,
    248         DOMWindowAndSharedWorkerContextHaveUnequalFieldCounts);
    249 #endif
    250     return objectPrototype->InternalFieldCount() == V8DOMWindow::internalFieldCount;
    251 }
    252 
    253 v8::Local<v8::Object> V8DOMWrapper::instantiateV8Object(V8Proxy* proxy, WrapperTypeInfo* type, void* impl)
    254 {
    255 #if ENABLE(WORKERS)
    256     WorkerContext* workerContext = 0;
    257 #endif
    258     if (V8IsolatedContext::getEntered()) {
    259         // This effectively disables the wrapper cache for isolated worlds.
    260         proxy = 0;
    261         // FIXME: Do we need a wrapper cache for the isolated world?  We should
    262         //        see if the performance gains are worth while.
    263         // We'll get one once we give the isolated context a proper window shell.
    264     } else if (!proxy) {
    265         v8::Handle<v8::Context> context = v8::Context::GetCurrent();
    266         if (!context.IsEmpty()) {
    267             v8::Handle<v8::Object> globalPrototype = v8::Handle<v8::Object>::Cast(context->Global()->GetPrototype());
    268             if (globalObjectPrototypeIsDOMWindow(globalPrototype))
    269                 proxy = V8Proxy::retrieve(V8DOMWindow::toNative(globalPrototype)->frame());
    270 #if ENABLE(WORKERS)
    271             else
    272                 workerContext = V8WorkerContext::toNative(lookupDOMWrapper(V8WorkerContext::GetTemplate(), context->Global()));
    273 #endif
    274         }
    275     }
    276 
    277     v8::Local<v8::Object> instance;
    278     if (proxy)
    279         // FIXME: Fix this to work properly with isolated worlds (see above).
    280         instance = proxy->windowShell()->createWrapperFromCache(type);
    281     else {
    282         v8::Local<v8::Function> function;
    283 #if ENABLE(WORKERS)
    284         if (workerContext)
    285             function = getConstructor(type, workerContext);
    286         else
    287 #endif
    288             function = type->getTemplate()->GetFunction();
    289         instance = SafeAllocation::newInstance(function);
    290     }
    291     if (!instance.IsEmpty()) {
    292         // Avoid setting the DOM wrapper for failed allocations.
    293         setDOMWrapper(instance, type, impl);
    294         if (type == &V8HTMLDocument::info)
    295             instance = V8HTMLDocument::WrapInShadowObject(instance, static_cast<Node*>(impl));
    296     }
    297     return instance;
    298 }
    299 
    300 #ifndef NDEBUG
    301 bool V8DOMWrapper::maybeDOMWrapper(v8::Handle<v8::Value> value)
    302 {
    303     if (value.IsEmpty() || !value->IsObject())
    304         return false;
    305 
    306     v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
    307     if (!object->InternalFieldCount())
    308         return false;
    309 
    310     ASSERT(object->InternalFieldCount() >= v8DefaultWrapperInternalFieldCount);
    311 
    312     v8::Handle<v8::Value> wrapper = object->GetInternalField(v8DOMWrapperObjectIndex);
    313     ASSERT(wrapper->IsNumber() || wrapper->IsExternal());
    314 
    315     return true;
    316 }
    317 #endif
    318 
    319 bool V8DOMWrapper::isValidDOMObject(v8::Handle<v8::Value> value)
    320 {
    321     if (value.IsEmpty() || !value->IsObject())
    322         return false;
    323     return v8::Handle<v8::Object>::Cast(value)->InternalFieldCount();
    324 }
    325 
    326 bool V8DOMWrapper::isWrapperOfType(v8::Handle<v8::Value> value, WrapperTypeInfo* type)
    327 {
    328     if (!isValidDOMObject(value))
    329         return false;
    330 
    331     v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
    332     ASSERT(object->InternalFieldCount() >= v8DefaultWrapperInternalFieldCount);
    333 
    334     v8::Handle<v8::Value> wrapper = object->GetInternalField(v8DOMWrapperObjectIndex);
    335     ASSERT(wrapper->IsNumber() || wrapper->IsExternal());
    336 
    337     WrapperTypeInfo* typeInfo = static_cast<WrapperTypeInfo*>(object->GetPointerFromInternalField(v8DOMWrapperTypeIndex));
    338     return typeInfo == type;
    339 }
    340 
    341 v8::Handle<v8::Object> V8DOMWrapper::getWrapperSlow(Node* node)
    342 {
    343     V8IsolatedContext* context = V8IsolatedContext::getEntered();
    344     if (LIKELY(!context)) {
    345         v8::Persistent<v8::Object>* wrapper = node->wrapper();
    346         if (!wrapper)
    347             return v8::Handle<v8::Object>();
    348         return *wrapper;
    349     }
    350     DOMNodeMapping& domNodeMap = context->world()->domDataStore()->domNodeMap();
    351     return domNodeMap.get(node);
    352 }
    353 
    354 // A JS object of type EventTarget is limited to a small number of possible classes.
    355 // Check EventTarget.h for new type conversion methods
    356 v8::Handle<v8::Value> V8DOMWrapper::convertEventTargetToV8Object(EventTarget* target)
    357 {
    358     if (!target)
    359         return v8::Null();
    360 
    361 #if ENABLE(SVG)
    362     if (SVGElementInstance* instance = target->toSVGElementInstance())
    363         return toV8(instance);
    364 #endif
    365 
    366 #if ENABLE(WORKERS)
    367     if (Worker* worker = target->toWorker())
    368         return toV8(worker);
    369 
    370     if (DedicatedWorkerContext* workerContext = target->toDedicatedWorkerContext())
    371         return toV8(workerContext);
    372 #endif // WORKERS
    373 
    374 #if ENABLE(SHARED_WORKERS)
    375     if (SharedWorker* sharedWorker = target->toSharedWorker())
    376         return toV8(sharedWorker);
    377 
    378     if (SharedWorkerContext* sharedWorkerContext = target->toSharedWorkerContext())
    379         return toV8(sharedWorkerContext);
    380 #endif // SHARED_WORKERS
    381 
    382 #if ENABLE(NOTIFICATIONS)
    383     if (Notification* notification = target->toNotification())
    384         return toV8(notification);
    385 #endif
    386 
    387 #if ENABLE(INDEXED_DATABASE)
    388     if (IDBDatabase* idbDatabase = target->toIDBDatabase())
    389         return toV8(idbDatabase);
    390     if (IDBRequest* idbRequest = target->toIDBRequest())
    391         return toV8(idbRequest);
    392     if (IDBTransaction* idbTransaction = target->toIDBTransaction())
    393         return toV8(idbTransaction);
    394 #endif
    395 
    396 #if ENABLE(WEB_SOCKETS)
    397     if (WebSocket* webSocket = target->toWebSocket())
    398         return toV8(webSocket);
    399 #endif
    400 
    401     if (Node* node = target->toNode())
    402         return toV8(node);
    403 
    404     if (DOMWindow* domWindow = target->toDOMWindow())
    405         return toV8(domWindow);
    406 
    407     // XMLHttpRequest is created within its JS counterpart.
    408     if (XMLHttpRequest* xmlHttpRequest = target->toXMLHttpRequest()) {
    409         v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(xmlHttpRequest);
    410         ASSERT(!wrapper.IsEmpty());
    411         return wrapper;
    412     }
    413 
    414     // MessagePort is created within its JS counterpart
    415     if (MessagePort* port = target->toMessagePort()) {
    416         v8::Handle<v8::Object> wrapper = getActiveDOMObjectMap().get(port);
    417         ASSERT(!wrapper.IsEmpty());
    418         return wrapper;
    419     }
    420 
    421     if (XMLHttpRequestUpload* upload = target->toXMLHttpRequestUpload()) {
    422         v8::Handle<v8::Object> wrapper = getDOMObjectMap().get(upload);
    423         ASSERT(!wrapper.IsEmpty());
    424         return wrapper;
    425     }
    426 
    427 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
    428     if (DOMApplicationCache* domAppCache = target->toDOMApplicationCache())
    429         return toV8(domAppCache);
    430 #endif
    431 
    432 #if ENABLE(EVENTSOURCE)
    433     if (EventSource* eventSource = target->toEventSource())
    434         return toV8(eventSource);
    435 #endif
    436 
    437 #if ENABLE(BLOB)
    438     if (FileReader* fileReader = target->toFileReader())
    439         return toV8(fileReader);
    440 #endif
    441 
    442 #if ENABLE(FILE_SYSTEM)
    443     if (FileWriter* fileWriter = target->toFileWriter())
    444         return toV8(fileWriter);
    445 #endif
    446 
    447 #if ENABLE(WEB_AUDIO)
    448     if (JavaScriptAudioNode* jsAudioNode = target->toJavaScriptAudioNode())
    449         return toV8(jsAudioNode);
    450     if (AudioContext* audioContext = target->toAudioContext())
    451         return toV8(audioContext);
    452 #endif
    453 
    454     ASSERT(0);
    455     return notHandledByInterceptor();
    456 }
    457 
    458 PassRefPtr<EventListener> V8DOMWrapper::getEventListener(v8::Local<v8::Value> value, bool isAttribute, ListenerLookupType lookup)
    459 {
    460     v8::Handle<v8::Context> context = v8::Context::GetCurrent();
    461     if (context.IsEmpty())
    462         return 0;
    463     if (lookup == ListenerFindOnly)
    464         return V8EventListenerList::findWrapper(value, isAttribute);
    465     v8::Handle<v8::Object> globalPrototype = v8::Handle<v8::Object>::Cast(context->Global()->GetPrototype());
    466     if (globalObjectPrototypeIsDOMWindow(globalPrototype))
    467         return V8EventListenerList::findOrCreateWrapper<V8EventListener>(value, isAttribute);
    468 #if ENABLE(WORKERS)
    469     return V8EventListenerList::findOrCreateWrapper<V8WorkerContextEventListener>(value, isAttribute);
    470 #else
    471     return 0;
    472 #endif
    473 }
    474 
    475 #if ENABLE(XPATH)
    476 // XPath-related utilities
    477 RefPtr<XPathNSResolver> V8DOMWrapper::getXPathNSResolver(v8::Handle<v8::Value> value, V8Proxy* proxy)
    478 {
    479     RefPtr<XPathNSResolver> resolver;
    480     if (V8XPathNSResolver::HasInstance(value))
    481         resolver = V8XPathNSResolver::toNative(v8::Handle<v8::Object>::Cast(value));
    482     else if (value->IsObject())
    483         resolver = V8CustomXPathNSResolver::create(value->ToObject());
    484     return resolver;
    485 }
    486 #endif
    487 
    488 }  // namespace WebCore
    489