2011-01-27 Oliver Hunt <oliver@apple.com>
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMBinding.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
4  *  Copyright (C) 2007 Samuel Weinig <sam@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 "DOMObjectHashTableMap.h"
29 #include "Document.h"
30 #include "EventException.h"
31 #include "ExceptionBase.h"
32 #include "ExceptionCode.h"
33 #include "Frame.h"
34 #include "HTMLAudioElement.h"
35 #include "HTMLCanvasElement.h"
36 #include "HTMLFrameElementBase.h"
37 #include "HTMLImageElement.h"
38 #include "HTMLLinkElement.h"
39 #include "HTMLNames.h"
40 #include "HTMLScriptElement.h"
41 #include "HTMLStyleElement.h"
42 #include "JSDOMCoreException.h"
43 #include "JSDOMWindowCustom.h"
44 #include "JSDebugWrapperSet.h"
45 #include "JSEventException.h"
46 #include "JSExceptionBase.h"
47 #include "JSMainThreadExecState.h"
48 #include "JSNode.h"
49 #include "JSRangeException.h"
50 #include "JSXMLHttpRequestException.h"
51 #include "KURL.h"
52 #include "MessagePort.h"
53 #include "ProcessingInstruction.h"
54 #include "RangeException.h"
55 #include "ScriptCachedFrameData.h"
56 #include "ScriptCallStack.h"
57 #include "ScriptController.h"
58 #include "Settings.h"
59 #include "WebCoreJSClientData.h"
60 #include "XMLHttpRequestException.h"
61 #include <runtime/DateInstance.h>
62 #include <runtime/Error.h>
63 #include <runtime/JSFunction.h>
64 #include <runtime/PrototypeFunction.h>
65 #include <wtf/MathExtras.h>
66 #include <wtf/StdLibExtras.h>
67
68 #if ENABLE(SVG)
69 #include "JSSVGException.h"
70 #include "SVGException.h"
71 #endif
72
73 #if ENABLE(XPATH)
74 #include "JSXPathException.h"
75 #include "XPathException.h"
76 #endif
77
78 #if ENABLE(DATABASE)
79 #include "JSSQLException.h"
80 #include "SQLException.h"
81 #endif
82
83 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
84 #include "FileException.h"
85 #include "JSFileException.h"
86 #endif
87
88 #if ENABLE(INDEXED_DATABASE)
89 #include "IDBDatabaseException.h"
90 #include "JSIDBDatabaseException.h"
91 #endif
92
93 using namespace JSC;
94
95 namespace WebCore {
96
97 using namespace HTMLNames;
98
99 typedef Document::JSWrapperCache JSWrapperCache;
100 typedef Document::JSWrapperCacheMap JSWrapperCacheMap;
101
102 class JSGlobalDataWorldIterator {
103 public:
104     JSGlobalDataWorldIterator(JSGlobalData* globalData)
105         : m_pos(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.begin())
106         , m_end(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.end())
107     {
108     }
109
110     operator bool()
111     {
112         return m_pos != m_end;
113     }
114
115     DOMWrapperWorld* operator*()
116     {
117         ASSERT(m_pos != m_end);
118         return *m_pos;
119     }
120
121     DOMWrapperWorld* operator->()
122     {
123         ASSERT(m_pos != m_end);
124         return *m_pos;
125     }
126
127     JSGlobalDataWorldIterator& operator++()
128     {
129         ++m_pos;
130         return *this;
131     }
132
133 private:
134     HashSet<DOMWrapperWorld*>::iterator m_pos;
135     HashSet<DOMWrapperWorld*>::iterator m_end;
136 };
137
138 const JSC::HashTable* getHashTableForGlobalData(JSGlobalData& globalData, const JSC::HashTable* staticTable)
139 {
140     return DOMObjectHashTableMap::mapFor(globalData).get(staticTable);
141 }
142
143 bool hasCachedDOMObjectWrapperUnchecked(JSGlobalData* globalData, void* objectHandle)
144 {
145     for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
146         if (worldIter->m_wrappers.uncheckedGet(objectHandle))
147             return true;
148     }
149     return false;
150 }
151
152 bool hasCachedDOMObjectWrapper(JSGlobalData* globalData, void* objectHandle)
153 {
154     for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
155         if (worldIter->m_wrappers.get(objectHandle))
156             return true;
157     }
158     return false;
159 }
160
161 DOMObject* getCachedDOMObjectWrapper(JSC::ExecState* exec, void* objectHandle) 
162 {
163     return domObjectWrapperMapFor(exec).get(objectHandle);
164 }
165
166 void cacheDOMObjectWrapper(JSC::ExecState* exec, void* objectHandle, DOMObject* wrapper) 
167 {
168     JSDebugWrapperSet::willCacheWrapper(wrapper);
169     domObjectWrapperMapFor(exec).set(objectHandle, wrapper);
170 }
171
172 bool hasCachedDOMNodeWrapperUnchecked(Document* document, Node* node)
173 {
174     if (!document)
175         return hasCachedDOMObjectWrapperUnchecked(JSDOMWindow::commonJSGlobalData(), node);
176
177     JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
178     for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) {
179         if (iter->second->uncheckedGet(node))
180             return true;
181     }
182     return false;
183 }
184
185 void forgetDOMObject(DOMObject* wrapper, void* objectHandle)
186 {
187     JSC::JSGlobalData* globalData = Heap::heap(wrapper)->globalData();
188
189     // Check the normal world first!
190     JSGlobalData::ClientData* clientData = globalData->clientData;
191     ASSERT(clientData);
192     DOMObjectWrapperMap& wrappers = static_cast<WebCoreJSClientData*>(clientData)->normalWorld()->m_wrappers;
193     if (wrappers.uncheckedRemove(objectHandle, wrapper)) {
194         JSDebugWrapperSet::didUncacheWrapper(wrapper);
195         return;
196     }
197
198     // We can't guarantee that a wrapper is in the cache when it uncaches itself,
199     // since a new wrapper may be cached before the old wrapper's destructor runs.
200     for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
201         if (worldIter->m_wrappers.uncheckedRemove(objectHandle, wrapper))
202             break;
203     }
204     JSDebugWrapperSet::didUncacheWrapper(wrapper);
205 }
206
207 void forgetDOMNode(JSNode* wrapper, Node* node, Document* document)
208 {
209     node->clearWrapper(wrapper);
210
211     if (!document) {
212         forgetDOMObject(wrapper, node);
213         return;
214     }
215
216     // We can't guarantee that a wrapper is in the cache when it uncaches itself,
217     // since a new wrapper may be cached before the old wrapper's destructor runs.
218     JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
219     for (JSWrapperCacheMap::iterator wrappersIter = wrapperCacheMap.begin(); wrappersIter != wrapperCacheMap.end(); ++wrappersIter) {
220         if (wrappersIter->second->uncheckedRemove(node, wrapper))
221             break;
222     }
223     JSDebugWrapperSet::didUncacheWrapper(wrapper);
224 }
225
226 void cacheDOMNodeWrapper(JSC::ExecState* exec, Document* document, Node* node, JSNode* wrapper)
227 {
228     JSDebugWrapperSet::willCacheWrapper(wrapper);
229
230     if (!document)
231         domObjectWrapperMapFor(exec).set(node, wrapper);
232     else
233         document->getWrapperCache(currentWorld(exec))->set(node, wrapper);
234
235     if (currentWorld(exec)->isNormal())
236         node->setWrapper(wrapper);
237 }
238
239 static inline bool isObservableThroughDOM(JSNode* jsNode, DOMWrapperWorld* world)
240 {
241     // Certain conditions implicitly make existence of a JS DOM node wrapper observable
242     // through the DOM, even if no explicit reference to it remains.
243
244     Node* node = jsNode->impl();
245
246     if (node->inDocument()) {
247         // If a node is in the document, and its wrapper has custom properties,
248         // the wrapper is observable because future access to the node through the
249         // DOM must reflect those properties.
250         if (jsNode->hasCustomProperties())
251             return true;
252
253         // If a node is in the document, and has event listeners, its wrapper is
254         // observable because its wrapper is responsible for marking those event listeners.
255         if (node->hasEventListeners())
256             return true; // Technically, we may overzealously mark a wrapper for a node that has only non-JS event listeners. Oh well.
257
258         // If a node owns another object with a wrapper with custom properties,
259         // the wrapper must be treated as observable, because future access to
260         // those objects through the DOM must reflect those properties.
261         // FIXME: It would be better if this logic could be in the node next to
262         // the custom markChildren functions rather than here.
263         // Note that for some compound objects like stylesheets and CSSStyleDeclarations,
264         // we don't descend to check children for custom properties, and just conservatively
265         // keep the node wrappers protecting them alive.
266         if (node->isElementNode()) {
267             if (NamedNodeMap* attributes = static_cast<Element*>(node)->attributeMap()) {
268                 if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(attributes)) {
269                     // FIXME: This check seems insufficient, because NamedNodeMap items can have custom properties themselves.
270                     // Maybe it would be OK to just keep the wrapper alive, as it is done for CSSOM objects below.
271                     if (wrapper->hasCustomProperties())
272                         return true;
273                 }
274             }
275             if (node->isStyledElement()) {
276                 if (CSSMutableStyleDeclaration* style = static_cast<StyledElement*>(node)->inlineStyleDecl()) {
277                     if (world->m_wrappers.uncheckedGet(style))
278                         return true;
279                 }
280             }
281             if (static_cast<Element*>(node)->hasTagName(canvasTag)) {
282                 if (CanvasRenderingContext* context = static_cast<HTMLCanvasElement*>(node)->renderingContext()) {
283                     if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(context)) {
284                         if (wrapper->hasCustomProperties())
285                             return true;
286                     }
287                 }
288             } else if (static_cast<Element*>(node)->hasTagName(linkTag)) {
289                 if (StyleSheet* sheet = static_cast<HTMLLinkElement*>(node)->sheet()) {
290                     if (world->m_wrappers.uncheckedGet(sheet))
291                         return true;
292                 }
293             } else if (static_cast<Element*>(node)->hasTagName(styleTag)) {
294                 if (StyleSheet* sheet = static_cast<HTMLStyleElement*>(node)->sheet()) {
295                     if (world->m_wrappers.uncheckedGet(sheet))
296                         return true;
297                 }
298             }
299         } else if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
300             if (StyleSheet* sheet = static_cast<ProcessingInstruction*>(node)->sheet()) {
301                 if (world->m_wrappers.uncheckedGet(sheet))
302                     return true;
303             }
304         }
305     } else {
306         // If a wrapper is the last reference to an image or script element
307         // that is loading but not in the document, the wrapper is observable
308         // because it is the only thing keeping the image element alive, and if
309         // the image element is destroyed, its load event will not fire.
310         // FIXME: The DOM should manage this issue without the help of JavaScript wrappers.
311         if (node->hasTagName(imgTag) && !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent())
312             return true;
313         if (node->hasTagName(scriptTag) && !static_cast<HTMLScriptElement*>(node)->haveFiredLoadEvent())
314             return true;
315 #if ENABLE(VIDEO)
316         if (node->hasTagName(audioTag) && !static_cast<HTMLAudioElement*>(node)->paused())
317             return true;
318 #endif
319     }
320
321     // If a node is firing event listeners, its wrapper is observable because
322     // its wrapper is responsible for marking those event listeners.
323     if (node->isFiringEventListeners())
324         return true;
325
326     return false;
327 }
328
329 void markDOMNodesForDocument(MarkStack& markStack, Document* document)
330 {
331     JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
332     for (JSWrapperCacheMap::iterator wrappersIter = wrapperCacheMap.begin(); wrappersIter != wrapperCacheMap.end(); ++wrappersIter) {
333         DOMWrapperWorld* world = wrappersIter->first;
334         JSWrapperCache* nodeDict = wrappersIter->second;
335
336         JSWrapperCache::iterator nodeEnd = nodeDict->uncheckedEnd();
337         for (JSWrapperCache::iterator nodeIt = nodeDict->uncheckedBegin(); nodeIt != nodeEnd; ++nodeIt) {
338             DeprecatedPtr<JSNode>& jsNode = nodeIt->second;
339             if (isObservableThroughDOM(jsNode.get(), world))
340                 markStack.append(&jsNode);
341         }
342     }
343 }
344
345 void markActiveObjectsForContext(MarkStack& markStack, JSGlobalData& globalData, ScriptExecutionContext* scriptExecutionContext)
346 {
347     // If an element has pending activity that may result in event listeners being called
348     // (e.g. an XMLHttpRequest), we need to keep JS wrappers alive.
349
350     const HashMap<ActiveDOMObject*, void*>& activeObjects = scriptExecutionContext->activeDOMObjects();
351     HashMap<ActiveDOMObject*, void*>::const_iterator activeObjectsEnd = activeObjects.end();
352     for (HashMap<ActiveDOMObject*, void*>::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
353         if (iter->first->hasPendingActivity()) {
354             // Generally, an active object with pending activity must have a wrapper to mark its listeners.
355             // However, some ActiveDOMObjects don't have JS wrappers.
356             markDOMObjectWrapper(markStack, globalData, iter->second);
357         }
358     }
359
360     const HashSet<MessagePort*>& messagePorts = scriptExecutionContext->messagePorts();
361     HashSet<MessagePort*>::const_iterator portsEnd = messagePorts.end();
362     for (HashSet<MessagePort*>::const_iterator iter = messagePorts.begin(); iter != portsEnd; ++iter) {
363         // If the message port is remotely entangled, then always mark it as in-use because we can't determine reachability across threads.
364         if (!(*iter)->locallyEntangledPort() || (*iter)->hasPendingActivity())
365             markDOMObjectWrapper(markStack, globalData, *iter);
366     }
367 }
368
369 typedef std::pair<JSNode*, DOMWrapperWorld*> WrapperAndWorld;
370 typedef WTF::Vector<WrapperAndWorld, 8> WrapperSet;
371
372 static inline void takeWrappers(Node* node, Document* document, WrapperSet& wrapperSet)
373 {
374     if (document) {
375         JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
376         for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) {
377             if (JSNode* wrapper = iter->second->take(node)) {
378                 JSDebugWrapperSet::didUncacheWrapper(wrapper);
379                 wrapperSet.append(WrapperAndWorld(wrapper, iter->first));
380             }
381         }
382     } else {
383         for (JSGlobalDataWorldIterator worldIter(JSDOMWindow::commonJSGlobalData()); worldIter; ++worldIter) {
384             DOMWrapperWorld* world = *worldIter;
385             if (JSNode* wrapper = static_cast<JSNode*>(world->m_wrappers.take(node))) {
386                 JSDebugWrapperSet::didUncacheWrapper(wrapper);
387                 wrapperSet.append(WrapperAndWorld(wrapper, world));
388             }
389         }
390     }
391 }
392
393 void updateDOMNodeDocument(Node* node, Document* oldDocument, Document* newDocument)
394 {
395     ASSERT(oldDocument != newDocument);
396
397     WrapperSet wrapperSet;
398     takeWrappers(node, oldDocument, wrapperSet);
399
400     for (unsigned i = 0; i < wrapperSet.size(); ++i) {
401         JSNode* wrapper = wrapperSet[i].first;
402         JSDebugWrapperSet::willCacheWrapper(wrapper);
403         if (newDocument)
404             newDocument->getWrapperCache(wrapperSet[i].second)->set(node, wrapper);
405         else
406             wrapperSet[i].second->m_wrappers.set(node, wrapper);
407     }
408 }
409
410 void markDOMObjectWrapper(MarkStack& markStack, JSGlobalData& globalData, void* object)
411 {
412     // FIXME: This could be changed to only mark wrappers that are "observable"
413     // as markDOMNodesForDocument does, allowing us to collect more wrappers,
414     // but doing this correctly would be challenging.
415     if (!object)
416         return;
417
418     for (JSGlobalDataWorldIterator worldIter(&globalData); worldIter; ++worldIter) {
419         if (DeprecatedPtr<DOMObject>* wrapperSlot = worldIter->m_wrappers.uncheckedGetSlot(object))
420             markStack.append(wrapperSlot);
421     }
422 }
423
424 void markDOMNodeWrapper(MarkStack& markStack, Document* document, Node* node)
425 {
426     if (document) {
427         JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
428         for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) {
429             if (DeprecatedPtr<JSNode>* wrapperSlot = iter->second->uncheckedGetSlot(node))
430                 markStack.append(wrapperSlot);
431         }
432         return;
433     }
434
435     for (JSGlobalDataWorldIterator worldIter(JSDOMWindow::commonJSGlobalData()); worldIter; ++worldIter) {
436         if (DeprecatedPtr<DOMObject>* wrapperSlot = worldIter->m_wrappers.uncheckedGetSlot(node))
437             markStack.append(wrapperSlot);
438     }
439 }
440
441 static void stringWrapperDestroyed(JSString* str, void* context)
442 {
443     StringImpl* cacheKey = static_cast<StringImpl*>(context);
444     JSC::JSGlobalData* globalData = Heap::heap(str)->globalData();
445
446     // Check the normal world first!
447     JSGlobalData::ClientData* clientData = globalData->clientData;
448     ASSERT(clientData);
449     JSStringCache& cache = static_cast<WebCoreJSClientData*>(clientData)->normalWorld()->m_stringCache;
450     if (cache.uncheckedRemove(cacheKey, str)) {
451         cacheKey->deref();
452         return;
453     }
454
455     for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
456         if (worldIter->m_stringCache.uncheckedRemove(cacheKey, str))
457             break;
458     }
459
460     cacheKey->deref();
461 }
462
463 JSValue jsStringSlowCase(ExecState* exec, JSStringCache& stringCache, StringImpl* stringImpl)
464 {
465     // If there is a stale entry, we have to explicitly remove it to avoid
466     // problems down the line.
467     if (JSString* wrapper = stringCache.uncheckedGet(stringImpl))
468         stringCache.uncheckedRemove(stringImpl, wrapper);
469
470     JSString* wrapper = jsStringWithFinalizer(exec, UString(stringImpl), stringWrapperDestroyed, stringImpl);
471     stringCache.set(stringImpl, wrapper);
472     // ref explicitly instead of using a RefPtr-keyed hashtable because the wrapper can
473     // outlive the cache, so the stringImpl has to match the wrapper's lifetime.
474     stringImpl->ref();
475     return wrapper;
476 }
477
478 JSValue jsStringOrNull(ExecState* exec, const String& s)
479 {
480     if (s.isNull())
481         return jsNull();
482     return jsString(exec, s);
483 }
484
485 JSValue jsOwnedStringOrNull(ExecState* exec, const String& s)
486 {
487     if (s.isNull())
488         return jsNull();
489     return jsOwnedString(exec, stringToUString(s));
490 }
491
492 JSValue jsStringOrUndefined(ExecState* exec, const String& s)
493 {
494     if (s.isNull())
495         return jsUndefined();
496     return jsString(exec, s);
497 }
498
499 JSValue jsStringOrFalse(ExecState* exec, const String& s)
500 {
501     if (s.isNull())
502         return jsBoolean(false);
503     return jsString(exec, s);
504 }
505
506 JSValue jsString(ExecState* exec, const KURL& url)
507 {
508     return jsString(exec, url.string());
509 }
510
511 JSValue jsStringOrNull(ExecState* exec, const KURL& url)
512 {
513     if (url.isNull())
514         return jsNull();
515     return jsString(exec, url.string());
516 }
517
518 JSValue jsStringOrUndefined(ExecState* exec, const KURL& url)
519 {
520     if (url.isNull())
521         return jsUndefined();
522     return jsString(exec, url.string());
523 }
524
525 JSValue jsStringOrFalse(ExecState* exec, const KURL& url)
526 {
527     if (url.isNull())
528         return jsBoolean(false);
529     return jsString(exec, url.string());
530 }
531
532 AtomicStringImpl* findAtomicString(const Identifier& identifier)
533 {
534     if (identifier.isNull())
535         return 0;
536     StringImpl* impl = identifier.impl();
537     ASSERT(impl->existingHash());
538     return AtomicString::find(impl->characters(), impl->length(), impl->existingHash());
539 }
540
541 String valueToStringWithNullCheck(ExecState* exec, JSValue value)
542 {
543     if (value.isNull())
544         return String();
545     return ustringToString(value.toString(exec));
546 }
547
548 String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
549 {
550     if (value.isUndefinedOrNull())
551         return String();
552     return ustringToString(value.toString(exec));
553 }
554
555 JSValue jsDateOrNull(ExecState* exec, double value)
556 {
557     if (!isfinite(value))
558         return jsNull();
559     return new (exec) DateInstance(exec, value);
560 }
561
562 double valueToDate(ExecState* exec, JSValue value)
563 {
564     if (value.isNumber())
565         return value.uncheckedGetNumber();
566     if (!value.inherits(&DateInstance::info))
567         return std::numeric_limits<double>::quiet_NaN();
568     return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
569 }
570
571 void reportException(ExecState* exec, JSValue exception)
572 {
573     if (exception.isObject() && asObject(exception)->exceptionType() == Terminated)
574         return;
575
576     UString errorMessage = exception.toString(exec);
577     JSObject* exceptionObject = exception.toObject(exec);
578     int lineNumber = exceptionObject->get(exec, Identifier(exec, "line")).toInt32(exec);
579     UString exceptionSourceURL = exceptionObject->get(exec, Identifier(exec, "sourceURL")).toString(exec);
580     exec->clearException();
581
582     if (ExceptionBase* exceptionBase = toExceptionBase(exception))
583         errorMessage = stringToUString(exceptionBase->message() + ": "  + exceptionBase->description());
584
585     ScriptExecutionContext* scriptExecutionContext = static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->scriptExecutionContext();
586     ASSERT(scriptExecutionContext);
587
588     // Crash data indicates null-dereference crashes at this point in the Safari 4 Public Beta.
589     // It's harmless to return here without reporting the exception to the log and the debugger in this case.
590     if (!scriptExecutionContext)
591         return;
592
593     scriptExecutionContext->reportException(ustringToString(errorMessage), lineNumber, ustringToString(exceptionSourceURL), 0);
594 }
595
596 void reportCurrentException(ExecState* exec)
597 {
598     JSValue exception = exec->exception();
599     exec->clearException();
600     reportException(exec, exception);
601 }
602
603 void setDOMException(ExecState* exec, ExceptionCode ec)
604 {
605     if (!ec || exec->hadException())
606         return;
607
608     // FIXME: All callers to setDOMException need to pass in the right global object
609     // for now, we're going to assume the lexicalGlobalObject.  Which is wrong in cases like this:
610     // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes.
611     JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec);
612
613     ExceptionCodeDescription description;
614     getExceptionCodeDescription(ec, description);
615
616     JSValue errorObject;
617     switch (description.type) {
618         case DOMExceptionType:
619             errorObject = toJS(exec, globalObject, DOMCoreException::create(description));
620             break;
621         case RangeExceptionType:
622             errorObject = toJS(exec, globalObject, RangeException::create(description));
623             break;
624         case EventExceptionType:
625             errorObject = toJS(exec, globalObject, EventException::create(description));
626             break;
627         case XMLHttpRequestExceptionType:
628             errorObject = toJS(exec, globalObject, XMLHttpRequestException::create(description));
629             break;
630 #if ENABLE(SVG)
631         case SVGExceptionType:
632             errorObject = toJS(exec, globalObject, SVGException::create(description).get());
633             break;
634 #endif
635 #if ENABLE(XPATH)
636         case XPathExceptionType:
637             errorObject = toJS(exec, globalObject, XPathException::create(description));
638             break;
639 #endif
640 #if ENABLE(DATABASE)
641         case SQLExceptionType:
642             errorObject = toJS(exec, globalObject, SQLException::create(description));
643             break;
644 #endif
645 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
646         case FileExceptionType:
647             errorObject = toJS(exec, globalObject, FileException::create(description));
648             break;
649 #endif
650 #if ENABLE(INDEXED_DATABASE)
651         case IDBDatabaseExceptionType:
652             errorObject = toJS(exec, globalObject, IDBDatabaseException::create(description));
653             break;
654 #endif
655     }
656
657     ASSERT(errorObject);
658     throwError(exec, errorObject);
659 }
660
661 DOMWindow* activeDOMWindow(ExecState* exec)
662 {
663     return asJSDOMWindow(exec->lexicalGlobalObject())->impl();
664 }
665
666 DOMWindow* firstDOMWindow(ExecState* exec)
667 {
668     return asJSDOMWindow(exec->dynamicGlobalObject())->impl();
669 }
670
671 bool checkNodeSecurity(ExecState* exec, Node* node)
672 {
673     return node && allowsAccessFromFrame(exec, node->document()->frame());
674 }
675
676 bool allowsAccessFromFrame(ExecState* exec, Frame* frame)
677 {
678     if (!frame)
679         return false;
680     JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec));
681     return window && window->allowsAccessFrom(exec);
682 }
683
684 bool allowsAccessFromFrame(ExecState* exec, Frame* frame, String& message)
685 {
686     if (!frame)
687         return false;
688     JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec));
689     return window && window->allowsAccessFrom(exec, message);
690 }
691
692 void printErrorMessageForFrame(Frame* frame, const String& message)
693 {
694     if (!frame)
695         return;
696     frame->domWindow()->printErrorMessage(message);
697 }
698
699 // FIXME: We should remove or at least deprecate this function. Callers can use firstDOMWindow directly.
700 Frame* toDynamicFrame(ExecState* exec)
701 {
702     return firstDOMWindow(exec)->frame();
703 }
704
705 // FIXME: We should remove this function. Callers can use ScriptController directly.
706 bool processingUserGesture()
707 {
708     return ScriptController::processingUserGesture();
709 }
710
711 JSValue objectToStringFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName)
712 {
713     return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->prototypeFunctionStructure(), 0, propertyName, objectProtoFuncToString);
714 }
715
716 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo)
717 {
718     JSDOMStructureMap& structures = globalObject->structures();
719     return structures.get(classInfo).get();
720 }
721
722 Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const ClassInfo* classInfo)
723 {
724     JSDOMStructureMap& structures = globalObject->structures();
725     ASSERT(!structures.contains(classInfo));
726     return structures.set(classInfo, structure).first->second.get();
727 }
728
729 JSC::JSObject* toJSSequence(ExecState* exec, JSValue value, unsigned& length)
730 {
731     JSObject* object = value.getObject();
732     if (!object) {
733         throwTypeError(exec);
734         return 0;
735     }
736     JSValue lengthValue = object->get(exec, exec->propertyNames().length);
737     if (exec->hadException())
738         return 0;
739
740     if (lengthValue.isUndefinedOrNull()) {
741         throwTypeError(exec);
742         return 0;
743     }
744
745     length = lengthValue.toUInt32(exec);
746     if (exec->hadException())
747         return 0;
748
749     return object;
750 }
751
752 } // namespace WebCore