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