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