Home | History | Annotate | Download | only in js
      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