2011-04-17 Geoffrey Garen <ggaren@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 "JSEventException.h"
45 #include "JSExceptionBase.h"
46 #include "JSMainThreadExecState.h"
47 #include "JSNode.h"
48 #include "JSRangeException.h"
49 #include "JSXMLHttpRequestException.h"
50 #include "KURL.h"
51 #include "MessagePort.h"
52 #include "ProcessingInstruction.h"
53 #include "RangeException.h"
54 #include "ScriptCachedFrameData.h"
55 #include "ScriptCallStack.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 <wtf/MathExtras.h>
64 #include <wtf/StdLibExtras.h>
65
66 #if ENABLE(SVG)
67 #include "JSSVGException.h"
68 #include "SVGException.h"
69 #endif
70
71 #if ENABLE(XPATH)
72 #include "JSXPathException.h"
73 #include "XPathException.h"
74 #endif
75
76 #if ENABLE(DATABASE)
77 #include "JSSQLException.h"
78 #include "SQLException.h"
79 #endif
80
81 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
82 #include "FileException.h"
83 #include "JSFileException.h"
84 #endif
85
86 #if ENABLE(INDEXED_DATABASE)
87 #include "IDBDatabaseException.h"
88 #include "JSIDBDatabaseException.h"
89 #endif
90
91 using namespace JSC;
92
93 namespace WebCore {
94
95 using namespace HTMLNames;
96
97 class JSGlobalDataWorldIterator {
98 public:
99     JSGlobalDataWorldIterator(JSGlobalData* globalData)
100         : m_pos(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.begin())
101         , m_end(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.end())
102     {
103     }
104
105     operator bool()
106     {
107         return m_pos != m_end;
108     }
109
110     DOMWrapperWorld* operator*()
111     {
112         ASSERT(m_pos != m_end);
113         return *m_pos;
114     }
115
116     DOMWrapperWorld* operator->()
117     {
118         ASSERT(m_pos != m_end);
119         return *m_pos;
120     }
121
122     JSGlobalDataWorldIterator& operator++()
123     {
124         ++m_pos;
125         return *this;
126     }
127
128 private:
129     HashSet<DOMWrapperWorld*>::iterator m_pos;
130     HashSet<DOMWrapperWorld*>::iterator m_end;
131 };
132
133 const JSC::HashTable* getHashTableForGlobalData(JSGlobalData& globalData, const JSC::HashTable* staticTable)
134 {
135     return DOMObjectHashTableMap::mapFor(globalData).get(staticTable);
136 }
137
138 JSDOMWrapper* getCachedDOMObjectWrapper(DOMWrapperWorld* world, void* objectHandle) 
139 {
140     return world->m_wrappers.get(objectHandle).get();
141 }
142
143 void cacheDOMObjectWrapper(DOMWrapperWorld* world, void* objectHandle, JSDOMWrapper* wrapper) 
144 {
145     world->m_wrappers.set(objectHandle, Weak<JSDOMWrapper>(*world->globalData(), wrapper, world->domObjectHandleOwner(), objectHandle));
146 }
147
148 void uncacheDOMObjectWrapper(DOMWrapperWorld* world, void* objectHandle, JSDOMWrapper* wrapper)
149 {
150     ASSERT_UNUSED(wrapper, world->m_wrappers.find(objectHandle)->second.get() == wrapper);
151     world->m_wrappers.remove(objectHandle);
152 }
153
154 void markActiveObjectsForContext(MarkStack& markStack, JSGlobalData& globalData, ScriptExecutionContext* scriptExecutionContext)
155 {
156     // If an element has pending activity that may result in event listeners being called
157     // (e.g. an XMLHttpRequest), we need to keep JS wrappers alive.
158
159     const HashMap<ActiveDOMObject*, void*>& activeObjects = scriptExecutionContext->activeDOMObjects();
160     HashMap<ActiveDOMObject*, void*>::const_iterator activeObjectsEnd = activeObjects.end();
161     for (HashMap<ActiveDOMObject*, void*>::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
162         if (iter->first->hasPendingActivity()) {
163             // Generally, an active object with pending activity must have a wrapper to mark its listeners.
164             // However, some ActiveDOMObjects don't have JS wrappers.
165             markDOMObjectWrapper(markStack, globalData, iter->second);
166         }
167     }
168
169     const HashSet<MessagePort*>& messagePorts = scriptExecutionContext->messagePorts();
170     HashSet<MessagePort*>::const_iterator portsEnd = messagePorts.end();
171     for (HashSet<MessagePort*>::const_iterator iter = messagePorts.begin(); iter != portsEnd; ++iter) {
172         // If the message port is remotely entangled, then always mark it as in-use because we can't determine reachability across threads.
173         if (!(*iter)->locallyEntangledPort() || (*iter)->hasPendingActivity())
174             markDOMObjectWrapper(markStack, globalData, *iter);
175     }
176 }
177
178 void markDOMObjectWrapper(MarkStack& markStack, JSGlobalData& globalData, void* object)
179 {
180     // FIXME: This could be changed to only mark wrappers that are "observable"
181     // as markDOMNodesForDocument does, allowing us to collect more wrappers,
182     // but doing this correctly would be challenging.
183     if (!object)
184         return;
185
186     for (JSGlobalDataWorldIterator worldIter(&globalData); worldIter; ++worldIter) {
187         if (JSDOMWrapper* wrapper = worldIter->m_wrappers.get(object).get())
188             markStack.deprecatedAppend(reinterpret_cast<JSCell**>(&wrapper));
189     }
190 }
191
192 static void stringWrapperDestroyed(JSString*, void* context)
193 {
194     StringImpl* cacheKey = static_cast<StringImpl*>(context);
195     cacheKey->deref();
196 }
197
198 JSValue jsStringSlowCase(ExecState* exec, JSStringCache& stringCache, StringImpl* stringImpl)
199 {
200     JSString* wrapper = jsStringWithFinalizer(exec, UString(stringImpl), stringWrapperDestroyed, stringImpl);
201     stringCache.set(exec->globalData(), stringImpl, wrapper);
202     // ref explicitly instead of using a RefPtr-keyed hashtable because the wrapper can
203     // outlive the cache, so the stringImpl has to match the wrapper's lifetime.
204     stringImpl->ref();
205     return wrapper;
206 }
207
208 JSValue jsStringOrNull(ExecState* exec, const String& s)
209 {
210     if (s.isNull())
211         return jsNull();
212     return jsString(exec, s);
213 }
214
215 JSValue jsOwnedStringOrNull(ExecState* exec, const String& s)
216 {
217     if (s.isNull())
218         return jsNull();
219     return jsOwnedString(exec, stringToUString(s));
220 }
221
222 JSValue jsStringOrUndefined(ExecState* exec, const String& s)
223 {
224     if (s.isNull())
225         return jsUndefined();
226     return jsString(exec, s);
227 }
228
229 JSValue jsStringOrFalse(ExecState* exec, const String& s)
230 {
231     if (s.isNull())
232         return jsBoolean(false);
233     return jsString(exec, s);
234 }
235
236 JSValue jsString(ExecState* exec, const KURL& url)
237 {
238     return jsString(exec, url.string());
239 }
240
241 JSValue jsStringOrNull(ExecState* exec, const KURL& url)
242 {
243     if (url.isNull())
244         return jsNull();
245     return jsString(exec, url.string());
246 }
247
248 JSValue jsStringOrUndefined(ExecState* exec, const KURL& url)
249 {
250     if (url.isNull())
251         return jsUndefined();
252     return jsString(exec, url.string());
253 }
254
255 JSValue jsStringOrFalse(ExecState* exec, const KURL& url)
256 {
257     if (url.isNull())
258         return jsBoolean(false);
259     return jsString(exec, url.string());
260 }
261
262 AtomicStringImpl* findAtomicString(const Identifier& identifier)
263 {
264     if (identifier.isNull())
265         return 0;
266     StringImpl* impl = identifier.impl();
267     ASSERT(impl->existingHash());
268     return AtomicString::find(impl->characters(), impl->length(), impl->existingHash());
269 }
270
271 String valueToStringWithNullCheck(ExecState* exec, JSValue value)
272 {
273     if (value.isNull())
274         return String();
275     return ustringToString(value.toString(exec));
276 }
277
278 String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
279 {
280     if (value.isUndefinedOrNull())
281         return String();
282     return ustringToString(value.toString(exec));
283 }
284
285 JSValue jsDateOrNull(ExecState* exec, double value)
286 {
287     if (!isfinite(value))
288         return jsNull();
289     return new (exec) DateInstance(exec, exec->lexicalGlobalObject()->dateStructure(), value);
290 }
291
292 double valueToDate(ExecState* exec, JSValue value)
293 {
294     if (value.isNumber())
295         return value.uncheckedGetNumber();
296     if (!value.inherits(&DateInstance::s_info))
297         return std::numeric_limits<double>::quiet_NaN();
298     return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
299 }
300
301 void reportException(ExecState* exec, JSValue exception)
302 {
303     if (exception.isObject() && asObject(exception)->exceptionType() == Terminated)
304         return;
305
306     UString errorMessage = exception.toString(exec);
307     JSObject* exceptionObject = exception.toObject(exec);
308     int lineNumber = exceptionObject->get(exec, Identifier(exec, "line")).toInt32(exec);
309     UString exceptionSourceURL = exceptionObject->get(exec, Identifier(exec, "sourceURL")).toString(exec);
310     exec->clearException();
311
312     if (ExceptionBase* exceptionBase = toExceptionBase(exception))
313         errorMessage = stringToUString(exceptionBase->message() + ": "  + exceptionBase->description());
314
315     ScriptExecutionContext* scriptExecutionContext = static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->scriptExecutionContext();
316     ASSERT(scriptExecutionContext);
317
318     // Crash data indicates null-dereference crashes at this point in the Safari 4 Public Beta.
319     // It's harmless to return here without reporting the exception to the log and the debugger in this case.
320     if (!scriptExecutionContext)
321         return;
322
323     scriptExecutionContext->reportException(ustringToString(errorMessage), lineNumber, ustringToString(exceptionSourceURL), 0);
324 }
325
326 void reportCurrentException(ExecState* exec)
327 {
328     JSValue exception = exec->exception();
329     exec->clearException();
330     reportException(exec, exception);
331 }
332
333 void setDOMException(ExecState* exec, ExceptionCode ec)
334 {
335     if (!ec || exec->hadException())
336         return;
337
338     // FIXME: All callers to setDOMException need to pass in the right global object
339     // for now, we're going to assume the lexicalGlobalObject.  Which is wrong in cases like this:
340     // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes.
341     JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec);
342
343     ExceptionCodeDescription description;
344     getExceptionCodeDescription(ec, description);
345
346     JSValue errorObject;
347     switch (description.type) {
348         case DOMExceptionType:
349             errorObject = toJS(exec, globalObject, DOMCoreException::create(description));
350             break;
351         case RangeExceptionType:
352             errorObject = toJS(exec, globalObject, RangeException::create(description));
353             break;
354         case EventExceptionType:
355             errorObject = toJS(exec, globalObject, EventException::create(description));
356             break;
357         case XMLHttpRequestExceptionType:
358             errorObject = toJS(exec, globalObject, XMLHttpRequestException::create(description));
359             break;
360 #if ENABLE(SVG)
361         case SVGExceptionType:
362             errorObject = toJS(exec, globalObject, SVGException::create(description).get());
363             break;
364 #endif
365 #if ENABLE(XPATH)
366         case XPathExceptionType:
367             errorObject = toJS(exec, globalObject, XPathException::create(description));
368             break;
369 #endif
370 #if ENABLE(DATABASE)
371         case SQLExceptionType:
372             errorObject = toJS(exec, globalObject, SQLException::create(description));
373             break;
374 #endif
375 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
376         case FileExceptionType:
377             errorObject = toJS(exec, globalObject, FileException::create(description));
378             break;
379 #endif
380 #if ENABLE(INDEXED_DATABASE)
381         case IDBDatabaseExceptionType:
382             errorObject = toJS(exec, globalObject, IDBDatabaseException::create(description));
383             break;
384 #endif
385     }
386
387     ASSERT(errorObject);
388     throwError(exec, errorObject);
389 }
390
391 DOMWindow* activeDOMWindow(ExecState* exec)
392 {
393     return asJSDOMWindow(exec->lexicalGlobalObject())->impl();
394 }
395
396 DOMWindow* firstDOMWindow(ExecState* exec)
397 {
398     return asJSDOMWindow(exec->dynamicGlobalObject())->impl();
399 }
400
401 bool checkNodeSecurity(ExecState* exec, Node* node)
402 {
403     return node && allowsAccessFromFrame(exec, node->document()->frame());
404 }
405
406 bool allowsAccessFromFrame(ExecState* exec, Frame* frame)
407 {
408     if (!frame)
409         return false;
410     JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec));
411     return window && window->allowsAccessFrom(exec);
412 }
413
414 bool allowsAccessFromFrame(ExecState* exec, Frame* frame, String& message)
415 {
416     if (!frame)
417         return false;
418     JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec));
419     return window && window->allowsAccessFrom(exec, message);
420 }
421
422 void printErrorMessageForFrame(Frame* frame, const String& message)
423 {
424     if (!frame)
425         return;
426     frame->domWindow()->printErrorMessage(message);
427 }
428
429 // FIXME: We should remove or at least deprecate this function. Callers can use firstDOMWindow directly.
430 Frame* toDynamicFrame(ExecState* exec)
431 {
432     return firstDOMWindow(exec)->frame();
433 }
434
435 // FIXME: We should remove this function. Callers can use ScriptController directly.
436 bool processingUserGesture()
437 {
438     return ScriptController::processingUserGesture();
439 }
440
441 JSValue objectToStringFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName)
442 {
443     return new (exec) JSFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->functionStructure(), 0, propertyName, objectProtoFuncToString);
444 }
445
446 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo)
447 {
448     JSDOMStructureMap& structures = globalObject->structures();
449     return structures.get(classInfo).get();
450 }
451
452 Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, Structure* structure, const ClassInfo* classInfo)
453 {
454     JSDOMStructureMap& structures = globalObject->structures();
455     ASSERT(!structures.contains(classInfo));
456     return structures.set(classInfo, WriteBarrier<Structure>(globalObject->globalData(), globalObject, structure)).first->second.get();
457 }
458
459 JSC::JSObject* toJSSequence(ExecState* exec, JSValue value, unsigned& length)
460 {
461     JSObject* object = value.getObject();
462     if (!object) {
463         throwTypeError(exec);
464         return 0;
465     }
466     JSValue lengthValue = object->get(exec, exec->propertyNames().length);
467     if (exec->hadException())
468         return 0;
469
470     if (lengthValue.isUndefinedOrNull()) {
471         throwTypeError(exec);
472         return 0;
473     }
474
475     length = lengthValue.toUInt32(exec);
476     if (exec->hadException())
477         return 0;
478
479     return object;
480 }
481
482 } // namespace WebCore