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