1 /* 2 * Copyright (C) 1999-2001 Harri Porten (porten (at) kde.org) 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 4 * Copyright (C) 2007 Samuel Weinig <sam (at) webkit.org> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 #include "config.h" 22 #include "JSDOMBinding.h" 23 24 #include "debugger/DebuggerCallFrame.h" 25 26 #include "ActiveDOMObject.h" 27 #include "DOMCoreException.h" 28 #include "Document.h" 29 #include "EventException.h" 30 #include "ExceptionBase.h" 31 #include "ExceptionCode.h" 32 #include "Frame.h" 33 #include "HTMLAudioElement.h" 34 #include "HTMLCanvasElement.h" 35 #include "HTMLImageElement.h" 36 #include "HTMLScriptElement.h" 37 #include "HTMLNames.h" 38 #include "JSDOMCoreException.h" 39 #include "JSDOMWindowCustom.h" 40 #include "JSEventException.h" 41 #include "JSExceptionBase.h" 42 #include "JSNode.h" 43 #include "JSRangeException.h" 44 #include "JSXMLHttpRequestException.h" 45 #include "KURL.h" 46 #include "MessagePort.h" 47 #include "RangeException.h" 48 #include "ScriptCachedFrameData.h" 49 #include "ScriptController.h" 50 #include "Settings.h" 51 #include "XMLHttpRequestException.h" 52 #include <runtime/DateInstance.h> 53 #include <runtime/Error.h> 54 #include <runtime/JSFunction.h> 55 #include <runtime/PrototypeFunction.h> 56 #include <wtf/MathExtras.h> 57 #include <wtf/StdLibExtras.h> 58 59 #if ENABLE(SVG) 60 #include "JSSVGException.h" 61 #include "SVGException.h" 62 #endif 63 64 #if ENABLE(XPATH) 65 #include "JSXPathException.h" 66 #include "XPathException.h" 67 #endif 68 69 #if ENABLE(WORKERS) 70 #include <wtf/ThreadSpecific.h> 71 using namespace WTF; 72 #endif 73 74 using namespace JSC; 75 76 namespace WebCore { 77 78 using namespace HTMLNames; 79 80 typedef Document::JSWrapperCache JSWrapperCache; 81 typedef Document::JSWrapperCacheMap JSWrapperCacheMap; 82 83 inline JSWrapperCache* Document::getWrapperCache(DOMWrapperWorld* world) 84 { 85 if (world->isNormal()) { 86 if (JSWrapperCache* wrapperCache = m_normalWorldWrapperCache) 87 return wrapperCache; 88 ASSERT(!m_wrapperCacheMap.contains(world)); 89 } else if (JSWrapperCache* wrapperCache = m_wrapperCacheMap.get(world)) 90 return wrapperCache; 91 return createWrapperCache(world); 92 } 93 94 // For debugging, keep a set of wrappers currently cached, and check that 95 // all are uncached before they are destroyed. This helps us catch bugs like: 96 // - wrappers being deleted without being removed from the cache 97 // - wrappers being cached twice 98 99 static void willCacheWrapper(DOMObject* wrapper); 100 static void didUncacheWrapper(DOMObject* wrapper); 101 102 #ifdef NDEBUG 103 104 static inline void willCacheWrapper(DOMObject*) 105 { 106 } 107 108 static inline void didUncacheWrapper(DOMObject*) 109 { 110 } 111 112 #else 113 114 static HashSet<DOMObject*>& wrapperSet() 115 { 116 #if ENABLE(WORKERS) 117 DEFINE_STATIC_LOCAL(ThreadSpecific<HashSet<DOMObject*> >, staticWrapperSet, ()); 118 return *staticWrapperSet; 119 #else 120 DEFINE_STATIC_LOCAL(HashSet<DOMObject*>, staticWrapperSet, ()); 121 return staticWrapperSet; 122 #endif 123 } 124 125 static void willCacheWrapper(DOMObject* wrapper) 126 { 127 ASSERT(!wrapperSet().contains(wrapper)); 128 wrapperSet().add(wrapper); 129 } 130 131 static void didUncacheWrapper(DOMObject* wrapper) 132 { 133 if (!wrapper) 134 return; 135 ASSERT(wrapperSet().contains(wrapper)); 136 wrapperSet().remove(wrapper); 137 } 138 139 DOMObject::~DOMObject() 140 { 141 ASSERT(!wrapperSet().contains(this)); 142 } 143 144 #endif 145 146 DOMWrapperWorld::DOMWrapperWorld(JSC::JSGlobalData* globalData, bool isNormal) 147 : m_globalData(globalData) 148 , m_isNormal(isNormal) 149 { 150 } 151 152 DOMWrapperWorld::~DOMWrapperWorld() 153 { 154 if (m_globalData) { 155 JSGlobalData::ClientData* clientData = m_globalData->clientData; 156 ASSERT(clientData); 157 static_cast<WebCoreJSClientData*>(clientData)->forgetWorld(this); 158 } 159 160 for (HashSet<Document*>::iterator iter = documentsWithWrappers.begin(); iter != documentsWithWrappers.end(); ++iter) 161 forgetWorldOfDOMNodesForDocument(*iter, this); 162 } 163 164 class JSGlobalDataWorldIterator { 165 public: 166 JSGlobalDataWorldIterator(JSGlobalData* globalData) 167 : m_pos(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.begin()) 168 , m_end(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.end()) 169 { 170 } 171 172 operator bool() 173 { 174 return m_pos != m_end; 175 } 176 177 DOMWrapperWorld* operator*() 178 { 179 ASSERT(m_pos != m_end); 180 return *m_pos; 181 } 182 183 DOMWrapperWorld* operator->() 184 { 185 ASSERT(m_pos != m_end); 186 return *m_pos; 187 } 188 189 JSGlobalDataWorldIterator& operator++() 190 { 191 ++m_pos; 192 return *this; 193 } 194 195 private: 196 HashSet<DOMWrapperWorld*>::iterator m_pos; 197 HashSet<DOMWrapperWorld*>::iterator m_end; 198 }; 199 200 DOMWrapperWorld* normalWorld(JSC::JSGlobalData& globalData) 201 { 202 JSGlobalData::ClientData* clientData = globalData.clientData; 203 ASSERT(clientData); 204 return static_cast<WebCoreJSClientData*>(clientData)->normalWorld(); 205 } 206 207 DOMWrapperWorld* mainThreadNormalWorld() 208 { 209 ASSERT(isMainThread()); 210 static DOMWrapperWorld* cachedNormalWorld = normalWorld(*JSDOMWindow::commonJSGlobalData()); 211 return cachedNormalWorld; 212 } 213 214 DOMObjectHashTableMap& DOMObjectHashTableMap::mapFor(JSGlobalData& globalData) 215 { 216 JSGlobalData::ClientData* clientData = globalData.clientData; 217 ASSERT(clientData); 218 return static_cast<WebCoreJSClientData*>(clientData)->hashTableMap; 219 } 220 221 const JSC::HashTable* getHashTableForGlobalData(JSGlobalData& globalData, const JSC::HashTable* staticTable) 222 { 223 return DOMObjectHashTableMap::mapFor(globalData).get(staticTable); 224 } 225 226 static inline DOMObjectWrapperMap& DOMObjectWrapperMapFor(JSC::ExecState* exec) 227 { 228 return currentWorld(exec)->m_wrappers; 229 } 230 231 bool hasCachedDOMObjectWrapperUnchecked(JSGlobalData* globalData, void* objectHandle) 232 { 233 for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) { 234 if (worldIter->m_wrappers.uncheckedGet(objectHandle)) 235 return true; 236 } 237 return false; 238 } 239 240 bool hasCachedDOMObjectWrapper(JSGlobalData* globalData, void* objectHandle) 241 { 242 for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) { 243 if (worldIter->m_wrappers.get(objectHandle)) 244 return true; 245 } 246 return false; 247 } 248 249 DOMObject* getCachedDOMObjectWrapper(JSC::ExecState* exec, void* objectHandle) 250 { 251 return DOMObjectWrapperMapFor(exec).get(objectHandle); 252 } 253 254 void cacheDOMObjectWrapper(JSC::ExecState* exec, void* objectHandle, DOMObject* wrapper) 255 { 256 willCacheWrapper(wrapper); 257 DOMObjectWrapperMapFor(exec).set(objectHandle, wrapper); 258 } 259 260 bool hasCachedDOMNodeWrapperUnchecked(Document* document, Node* node) 261 { 262 if (!document) 263 return hasCachedDOMObjectWrapperUnchecked(JSDOMWindow::commonJSGlobalData(), node); 264 265 JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap(); 266 for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) { 267 if (iter->second->uncheckedGet(node)) 268 return true; 269 } 270 return false; 271 } 272 273 JSNode* getCachedDOMNodeWrapper(JSC::ExecState* exec, Document* document, Node* node) 274 { 275 if (document) 276 return document->getWrapperCache(currentWorld(exec))->get(node); 277 return static_cast<JSNode*>(DOMObjectWrapperMapFor(exec).get(node)); 278 } 279 280 void forgetDOMObject(DOMObject* wrapper, void* objectHandle) 281 { 282 JSC::JSGlobalData* globalData = Heap::heap(wrapper)->globalData(); 283 284 // Check the normal world first! 285 JSGlobalData::ClientData* clientData = globalData->clientData; 286 ASSERT(clientData); 287 DOMObjectWrapperMap& wrappers = static_cast<WebCoreJSClientData*>(clientData)->normalWorld()->m_wrappers; 288 if (wrappers.uncheckedRemove(objectHandle, wrapper)) { 289 didUncacheWrapper(wrapper); 290 return; 291 } 292 293 // We can't guarantee that a wrapper is in the cache when it uncaches itself, 294 // since a new wrapper may be cached before the old wrapper's destructor runs. 295 for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) { 296 if (worldIter->m_wrappers.uncheckedRemove(objectHandle, wrapper)) 297 break; 298 } 299 didUncacheWrapper(wrapper); 300 } 301 302 void forgetDOMNode(JSNode* wrapper, Node* node, Document* document) 303 { 304 if (!document) { 305 forgetDOMObject(wrapper, node); 306 return; 307 } 308 309 // We can't guarantee that a wrapper is in the cache when it uncaches itself, 310 // since a new wrapper may be cached before the old wrapper's destructor runs. 311 JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap(); 312 for (JSWrapperCacheMap::iterator wrappersIter = wrapperCacheMap.begin(); wrappersIter != wrapperCacheMap.end(); ++wrappersIter) { 313 if (wrappersIter->second->uncheckedRemove(node, wrapper)) 314 break; 315 } 316 didUncacheWrapper(wrapper); 317 } 318 319 void cacheDOMNodeWrapper(JSC::ExecState* exec, Document* document, Node* node, JSNode* wrapper) 320 { 321 if (!document) { 322 willCacheWrapper(wrapper); 323 DOMObjectWrapperMapFor(exec).set(node, wrapper); 324 return; 325 } 326 willCacheWrapper(wrapper); 327 document->getWrapperCache(currentWorld(exec))->set(node, wrapper); 328 } 329 330 void forgetAllDOMNodesForDocument(Document* document) 331 { 332 ASSERT(document); 333 JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap(); 334 JSWrapperCacheMap::const_iterator wrappersMapEnd = wrapperCacheMap.end(); 335 for (JSWrapperCacheMap::const_iterator wrappersMapIter = wrapperCacheMap.begin(); wrappersMapIter != wrappersMapEnd; ++wrappersMapIter) { 336 delete wrappersMapIter->second; 337 wrappersMapIter->first->forgetDocument(document); 338 } 339 } 340 341 void forgetWorldOfDOMNodesForDocument(Document* document, DOMWrapperWorld* world) 342 { 343 JSWrapperCache* wrappers = document->wrapperCacheMap().take(world); 344 ASSERT(wrappers); // 'world' should only know about 'document' if 'document' knows about 'world'! 345 delete wrappers; 346 } 347 348 static inline bool isObservableThroughDOM(JSNode* jsNode, DOMWrapperWorld* world) 349 { 350 // Certain conditions implicitly make a JS DOM node wrapper observable 351 // through the DOM, even if no explicit reference to it remains. 352 353 Node* node = jsNode->impl(); 354 355 if (node->inDocument()) { 356 // If a node is in the document, and its wrapper has custom properties, 357 // the wrapper is observable because future access to the node through the 358 // DOM must reflect those properties. 359 if (jsNode->hasCustomProperties()) 360 return true; 361 362 // If a node is in the document, and has event listeners, its wrapper is 363 // observable because its wrapper is responsible for marking those event listeners. 364 if (node->hasEventListeners()) 365 return true; // Technically, we may overzealously mark a wrapper for a node that has only non-JS event listeners. Oh well. 366 367 // If a node owns another object with a wrapper with custom properties, 368 // the wrapper must be treated as observable, because future access to 369 // those objects through the DOM must reflect those properties. 370 // FIXME: It would be better if this logic could be in the node next to 371 // the custom markChildren functions rather than here. 372 if (node->isElementNode()) { 373 if (NamedNodeMap* attributes = static_cast<Element*>(node)->attributeMap()) { 374 if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(attributes)) { 375 if (wrapper->hasCustomProperties()) 376 return true; 377 } 378 } 379 if (node->isStyledElement()) { 380 if (CSSMutableStyleDeclaration* style = static_cast<StyledElement*>(node)->inlineStyleDecl()) { 381 if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(style)) { 382 if (wrapper->hasCustomProperties()) 383 return true; 384 } 385 } 386 } 387 if (static_cast<Element*>(node)->hasTagName(canvasTag)) { 388 if (CanvasRenderingContext* context = static_cast<HTMLCanvasElement*>(node)->renderingContext()) { 389 if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(context)) { 390 if (wrapper->hasCustomProperties()) 391 return true; 392 } 393 } 394 } 395 } 396 } else { 397 // If a wrapper is the last reference to an image or script element 398 // that is loading but not in the document, the wrapper is observable 399 // because it is the only thing keeping the image element alive, and if 400 // the image element is destroyed, its load event will not fire. 401 // FIXME: The DOM should manage this issue without the help of JavaScript wrappers. 402 if (node->hasTagName(imgTag) && !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent()) 403 return true; 404 if (node->hasTagName(scriptTag) && !static_cast<HTMLScriptElement*>(node)->haveFiredLoadEvent()) 405 return true; 406 #if ENABLE(VIDEO) 407 if (node->hasTagName(audioTag) && !static_cast<HTMLAudioElement*>(node)->paused()) 408 return true; 409 #endif 410 } 411 412 // If a node is firing event listeners, its wrapper is observable because 413 // its wrapper is responsible for marking those event listeners. 414 if (node->isFiringEventListeners()) 415 return true; 416 417 return false; 418 } 419 420 void markDOMNodesForDocument(MarkStack& markStack, Document* document) 421 { 422 JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap(); 423 for (JSWrapperCacheMap::iterator wrappersIter = wrapperCacheMap.begin(); wrappersIter != wrapperCacheMap.end(); ++wrappersIter) { 424 DOMWrapperWorld* world = wrappersIter->first; 425 JSWrapperCache* nodeDict = wrappersIter->second; 426 427 JSWrapperCache::iterator nodeEnd = nodeDict->uncheckedEnd(); 428 for (JSWrapperCache::iterator nodeIt = nodeDict->uncheckedBegin(); nodeIt != nodeEnd; ++nodeIt) { 429 JSNode* jsNode = nodeIt->second; 430 if (isObservableThroughDOM(jsNode, world)) 431 markStack.append(jsNode); 432 } 433 } 434 } 435 436 void markActiveObjectsForContext(MarkStack& markStack, JSGlobalData& globalData, ScriptExecutionContext* scriptExecutionContext) 437 { 438 // If an element has pending activity that may result in event listeners being called 439 // (e.g. an XMLHttpRequest), we need to keep JS wrappers alive. 440 441 const HashMap<ActiveDOMObject*, void*>& activeObjects = scriptExecutionContext->activeDOMObjects(); 442 HashMap<ActiveDOMObject*, void*>::const_iterator activeObjectsEnd = activeObjects.end(); 443 for (HashMap<ActiveDOMObject*, void*>::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) { 444 if (iter->first->hasPendingActivity()) { 445 // Generally, an active object with pending activity must have a wrapper to mark its listeners. 446 // However, some ActiveDOMObjects don't have JS wrappers. 447 markDOMObjectWrapper(markStack, globalData, iter->second); 448 } 449 } 450 451 const HashSet<MessagePort*>& messagePorts = scriptExecutionContext->messagePorts(); 452 HashSet<MessagePort*>::const_iterator portsEnd = messagePorts.end(); 453 for (HashSet<MessagePort*>::const_iterator iter = messagePorts.begin(); iter != portsEnd; ++iter) { 454 // If the message port is remotely entangled, then always mark it as in-use because we can't determine reachability across threads. 455 if (!(*iter)->locallyEntangledPort() || (*iter)->hasPendingActivity()) 456 markDOMObjectWrapper(markStack, globalData, *iter); 457 } 458 } 459 460 typedef std::pair<JSNode*, DOMWrapperWorld*> WrapperAndWorld; 461 typedef WTF::Vector<WrapperAndWorld, 8> WrapperSet; 462 463 static inline void takeWrappers(Node* node, Document* document, WrapperSet& wrapperSet) 464 { 465 if (document) { 466 JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap(); 467 for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) { 468 if (JSNode* wrapper = iter->second->take(node)) { 469 didUncacheWrapper(wrapper); 470 wrapperSet.append(WrapperAndWorld(wrapper, iter->first)); 471 } 472 } 473 } else { 474 for (JSGlobalDataWorldIterator worldIter(JSDOMWindow::commonJSGlobalData()); worldIter; ++worldIter) { 475 DOMWrapperWorld* world = *worldIter; 476 if (JSNode* wrapper = static_cast<JSNode*>(world->m_wrappers.take(node))) { 477 didUncacheWrapper(wrapper); 478 wrapperSet.append(WrapperAndWorld(wrapper, world)); 479 } 480 } 481 } 482 } 483 484 void updateDOMNodeDocument(Node* node, Document* oldDocument, Document* newDocument) 485 { 486 ASSERT(oldDocument != newDocument); 487 488 WrapperSet wrapperSet; 489 takeWrappers(node, oldDocument, wrapperSet); 490 491 for (unsigned i = 0; i < wrapperSet.size(); ++i) { 492 JSNode* wrapper = wrapperSet[i].first; 493 willCacheWrapper(wrapper); 494 if (newDocument) 495 newDocument->getWrapperCache(wrapperSet[i].second)->set(node, wrapper); 496 else 497 wrapperSet[i].second->m_wrappers.set(node, wrapper); 498 } 499 } 500 501 void markDOMObjectWrapper(MarkStack& markStack, JSGlobalData& globalData, void* object) 502 { 503 // FIXME: This could be changed to only mark wrappers that are "observable" 504 // as markDOMNodesForDocument does, allowing us to collect more wrappers, 505 // but doing this correctly would be challenging. 506 if (!object) 507 return; 508 509 for (JSGlobalDataWorldIterator worldIter(&globalData); worldIter; ++worldIter) { 510 if (DOMObject* wrapper = worldIter->m_wrappers.uncheckedGet(object)) 511 markStack.append(wrapper); 512 } 513 } 514 515 void markDOMNodeWrapper(MarkStack& markStack, Document* document, Node* node) 516 { 517 if (document) { 518 JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap(); 519 for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) { 520 if (JSNode* wrapper = iter->second->uncheckedGet(node)) 521 markStack.append(wrapper); 522 } 523 return; 524 } 525 526 for (JSGlobalDataWorldIterator worldIter(JSDOMWindow::commonJSGlobalData()); worldIter; ++worldIter) { 527 if (DOMObject* wrapper = worldIter->m_wrappers.uncheckedGet(node)) 528 markStack.append(wrapper); 529 } 530 } 531 532 static void stringWrapperDestroyed(JSString* str, void* context) 533 { 534 StringImpl* cacheKey = static_cast<StringImpl*>(context); 535 JSC::JSGlobalData* globalData = Heap::heap(str)->globalData(); 536 537 // Check the normal world first! 538 JSGlobalData::ClientData* clientData = globalData->clientData; 539 ASSERT(clientData); 540 JSStringCache& cache = static_cast<WebCoreJSClientData*>(clientData)->normalWorld()->m_stringCache; 541 if (cache.uncheckedRemove(cacheKey, str)) { 542 cacheKey->deref(); 543 return; 544 } 545 546 for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) { 547 if (worldIter->m_stringCache.uncheckedRemove(cacheKey, str)) 548 break; 549 } 550 551 cacheKey->deref(); 552 } 553 554 JSValue jsStringSlowCase(ExecState* exec, JSStringCache& stringCache, StringImpl* stringImpl) 555 { 556 // If there is a stale entry, we have to explicitly remove it to avoid 557 // problems down the line. 558 if (JSString* wrapper = stringCache.uncheckedGet(stringImpl)) 559 stringCache.uncheckedRemove(stringImpl, wrapper); 560 561 JSString* wrapper = jsStringWithFinalizer(exec, stringImpl->ustring(), stringWrapperDestroyed, stringImpl); 562 stringCache.set(stringImpl, wrapper); 563 // ref explicitly instead of using a RefPtr-keyed hashtable because the wrapper can 564 // outlive the cache, so the stringImpl has to match the wrapper's lifetime. 565 stringImpl->ref(); 566 return wrapper; 567 } 568 569 JSValue jsStringOrNull(ExecState* exec, const String& s) 570 { 571 if (s.isNull()) 572 return jsNull(); 573 return jsString(exec, s); 574 } 575 576 JSValue jsOwnedStringOrNull(ExecState* exec, const UString& s) 577 { 578 if (s.isNull()) 579 return jsNull(); 580 return jsOwnedString(exec, s); 581 } 582 583 JSValue jsStringOrUndefined(ExecState* exec, const String& s) 584 { 585 if (s.isNull()) 586 return jsUndefined(); 587 return jsString(exec, s); 588 } 589 590 JSValue jsStringOrFalse(ExecState* exec, const String& s) 591 { 592 if (s.isNull()) 593 return jsBoolean(false); 594 return jsString(exec, s); 595 } 596 597 JSValue jsString(ExecState* exec, const KURL& url) 598 { 599 return jsString(exec, url.string()); 600 } 601 602 JSValue jsStringOrNull(ExecState* exec, const KURL& url) 603 { 604 if (url.isNull()) 605 return jsNull(); 606 return jsString(exec, url.string()); 607 } 608 609 JSValue jsStringOrUndefined(ExecState* exec, const KURL& url) 610 { 611 if (url.isNull()) 612 return jsUndefined(); 613 return jsString(exec, url.string()); 614 } 615 616 JSValue jsStringOrFalse(ExecState* exec, const KURL& url) 617 { 618 if (url.isNull()) 619 return jsBoolean(false); 620 return jsString(exec, url.string()); 621 } 622 623 UString valueToStringWithNullCheck(ExecState* exec, JSValue value) 624 { 625 if (value.isNull()) 626 return UString(); 627 return value.toString(exec); 628 } 629 630 UString valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value) 631 { 632 if (value.isUndefinedOrNull()) 633 return UString(); 634 return value.toString(exec); 635 } 636 637 JSValue jsDateOrNull(ExecState* exec, double value) 638 { 639 if (!isfinite(value)) 640 return jsNull(); 641 return new (exec) DateInstance(exec, value); 642 } 643 644 double valueToDate(ExecState* exec, JSValue value) 645 { 646 if (value.isNumber()) 647 return value.uncheckedGetNumber(); 648 if (!value.inherits(&DateInstance::info)) 649 return std::numeric_limits<double>::quiet_NaN(); 650 return static_cast<DateInstance*>(value.toObject(exec))->internalNumber(); 651 } 652 653 void reportException(ExecState* exec, JSValue exception) 654 { 655 UString errorMessage = exception.toString(exec); 656 JSObject* exceptionObject = exception.toObject(exec); 657 int lineNumber = exceptionObject->get(exec, Identifier(exec, "line")).toInt32(exec); 658 UString exceptionSourceURL = exceptionObject->get(exec, Identifier(exec, "sourceURL")).toString(exec); 659 exec->clearException(); 660 661 if (ExceptionBase* exceptionBase = toExceptionBase(exception)) 662 errorMessage = exceptionBase->message() + ": " + exceptionBase->description(); 663 664 ScriptExecutionContext* scriptExecutionContext = static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->scriptExecutionContext(); 665 ASSERT(scriptExecutionContext); 666 667 // Crash data indicates null-dereference crashes at this point in the Safari 4 Public Beta. 668 // It's harmless to return here without reporting the exception to the log and the debugger in this case. 669 if (!scriptExecutionContext) 670 return; 671 672 scriptExecutionContext->reportException(errorMessage, lineNumber, exceptionSourceURL); 673 } 674 675 void reportCurrentException(ExecState* exec) 676 { 677 JSValue exception = exec->exception(); 678 exec->clearException(); 679 reportException(exec, exception); 680 } 681 682 void setDOMException(ExecState* exec, ExceptionCode ec) 683 { 684 if (!ec || exec->hadException()) 685 return; 686 687 // FIXME: All callers to setDOMException need to pass in the right global object 688 // for now, we're going to assume the lexicalGlobalObject. Which is wrong in cases like this: 689 // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes. 690 JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec); 691 692 ExceptionCodeDescription description; 693 getExceptionCodeDescription(ec, description); 694 695 JSValue errorObject; 696 switch (description.type) { 697 case DOMExceptionType: 698 errorObject = toJS(exec, globalObject, DOMCoreException::create(description)); 699 break; 700 case RangeExceptionType: 701 errorObject = toJS(exec, globalObject, RangeException::create(description)); 702 break; 703 case EventExceptionType: 704 errorObject = toJS(exec, globalObject, EventException::create(description)); 705 break; 706 case XMLHttpRequestExceptionType: 707 errorObject = toJS(exec, globalObject, XMLHttpRequestException::create(description)); 708 break; 709 #if ENABLE(SVG) 710 case SVGExceptionType: 711 errorObject = toJS(exec, globalObject, SVGException::create(description).get(), 0 /* no context on purpose */); 712 break; 713 #endif 714 #if ENABLE(XPATH) 715 case XPathExceptionType: 716 errorObject = toJS(exec, globalObject, XPathException::create(description)); 717 break; 718 #endif 719 } 720 721 ASSERT(errorObject); 722 exec->setException(errorObject); 723 } 724 725 bool checkNodeSecurity(ExecState* exec, Node* node) 726 { 727 return node && allowsAccessFromFrame(exec, node->document()->frame()); 728 } 729 730 bool allowsAccessFromFrame(ExecState* exec, Frame* frame) 731 { 732 if (!frame) 733 return false; 734 JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec)); 735 return window && window->allowsAccessFrom(exec); 736 } 737 738 bool allowsAccessFromFrame(ExecState* exec, Frame* frame, String& message) 739 { 740 if (!frame) 741 return false; 742 JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec)); 743 return window && window->allowsAccessFrom(exec, message); 744 } 745 746 bool shouldAllowNavigation(ExecState* exec, Frame* frame) 747 { 748 Frame* lexicalFrame = toLexicalFrame(exec); 749 return lexicalFrame && lexicalFrame->loader()->shouldAllowNavigation(frame); 750 } 751 752 void printErrorMessageForFrame(Frame* frame, const String& message) 753 { 754 if (!frame) 755 return; 756 if (message.isEmpty()) 757 return; 758 759 Settings* settings = frame->settings(); 760 if (!settings) 761 return; 762 if (settings->privateBrowsingEnabled()) 763 return; 764 765 frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL. 766 } 767 768 Frame* toLexicalFrame(ExecState* exec) 769 { 770 return asJSDOMWindow(exec->lexicalGlobalObject())->impl()->frame(); 771 } 772 773 Frame* toDynamicFrame(ExecState* exec) 774 { 775 return asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame(); 776 } 777 778 bool processingUserGesture(ExecState* exec) 779 { 780 Frame* frame = toDynamicFrame(exec); 781 return frame && frame->script()->processingUserGesture(currentWorld(exec)); 782 } 783 784 KURL completeURL(ExecState* exec, const String& relativeURL) 785 { 786 // For historical reasons, we need to complete the URL using the dynamic frame. 787 Frame* frame = toDynamicFrame(exec); 788 if (!frame) 789 return KURL(); 790 return frame->loader()->completeURL(relativeURL); 791 } 792 793 JSValue objectToStringFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot&) 794 { 795 return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), 0, propertyName, objectProtoFuncToString); 796 } 797 798 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo) 799 { 800 JSDOMStructureMap& structures = globalObject->structures(); 801 return structures.get(classInfo).get(); 802 } 803 804 Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const ClassInfo* classInfo) 805 { 806 JSDOMStructureMap& structures = globalObject->structures(); 807 ASSERT(!structures.contains(classInfo)); 808 return structures.set(classInfo, structure).first->second.get(); 809 } 810 811 Structure* getCachedDOMStructure(ExecState* exec, const ClassInfo* classInfo) 812 { 813 return getCachedDOMStructure(static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), classInfo); 814 } 815 816 Structure* cacheDOMStructure(ExecState* exec, NonNullPassRefPtr<Structure> structure, const ClassInfo* classInfo) 817 { 818 return cacheDOMStructure(static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), structure, classInfo); 819 } 820 821 JSObject* getCachedDOMConstructor(ExecState* exec, const ClassInfo* classInfo) 822 { 823 JSDOMConstructorMap& constructors = static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->constructors(); 824 return constructors.get(classInfo); 825 } 826 827 void cacheDOMConstructor(ExecState* exec, const ClassInfo* classInfo, JSObject* constructor) 828 { 829 JSDOMConstructorMap& constructors = static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->constructors(); 830 ASSERT(!constructors.contains(classInfo)); 831 constructors.set(classInfo, constructor); 832 } 833 834 JSC::JSObject* toJSSequence(ExecState* exec, JSValue value, unsigned& length) 835 { 836 JSObject* object = value.getObject(); 837 if (!object) { 838 throwError(exec, TypeError); 839 return 0; 840 } 841 JSValue lengthValue = object->get(exec, exec->propertyNames().length); 842 if (exec->hadException()) 843 return 0; 844 845 if (lengthValue.isUndefinedOrNull()) { 846 throwError(exec, TypeError); 847 return 0; 848 } 849 850 length = lengthValue.toUInt32(exec); 851 if (exec->hadException()) 852 return 0; 853 854 return object; 855 } 856 857 bool DOMObject::defineOwnProperty(ExecState* exec, const Identifier&, PropertyDescriptor&, bool) 858 { 859 throwError(exec, TypeError, "defineProperty is not supported on DOM Objects"); 860 return false; 861 } 862 863 } // namespace WebCore 864