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