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