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