WebKit should be lazy-finalization-safe (esp. the DOM)
[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, 2011 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 "DOMObjectHashTableMap.h"
25 #include "ExceptionCode.h"
26 #include "ExceptionHeaders.h"
27 #include "ExceptionInterfaces.h"
28 #include "Frame.h"
29 #include "JSDOMWindowCustom.h"
30 #include "JSExceptionBase.h"
31 #include "ScriptCallStack.h"
32 #include <runtime/DateInstance.h>
33 #include <runtime/Error.h>
34 #include <runtime/ExceptionHelpers.h>
35 #include <runtime/JSFunction.h>
36
37 using namespace JSC;
38
39 namespace WebCore {
40
41 ASSERT_HAS_TRIVIAL_DESTRUCTOR(DOMConstructorObject);
42 ASSERT_HAS_TRIVIAL_DESTRUCTOR(DOMConstructorWithDocument);
43
44 const JSC::HashTable* getHashTableForGlobalData(JSGlobalData& globalData, const JSC::HashTable* staticTable)
45 {
46     return DOMObjectHashTableMap::mapFor(globalData).get(staticTable);
47 }
48
49 JSValue jsStringSlowCase(ExecState* exec, JSStringCache& stringCache, StringImpl* stringImpl)
50 {
51     JSString* wrapper = jsString(exec, UString(stringImpl));
52     weakAdd(stringCache, stringImpl, PassWeak<JSString>(wrapper, currentWorld(exec)->stringWrapperOwner(), stringImpl));
53     return wrapper;
54 }
55
56 JSValue jsStringOrNull(ExecState* exec, const String& s)
57 {
58     if (s.isNull())
59         return jsNull();
60     return jsString(exec, s);
61 }
62
63 JSValue jsOwnedStringOrNull(ExecState* exec, const String& s)
64 {
65     if (s.isNull())
66         return jsNull();
67     return jsOwnedString(exec, stringToUString(s));
68 }
69
70 JSValue jsStringOrUndefined(ExecState* exec, const String& s)
71 {
72     if (s.isNull())
73         return jsUndefined();
74     return jsString(exec, s);
75 }
76
77 JSValue jsStringOrFalse(ExecState* exec, const String& s)
78 {
79     if (s.isNull())
80         return jsBoolean(false);
81     return jsString(exec, s);
82 }
83
84 JSValue jsString(ExecState* exec, const KURL& url)
85 {
86     return jsString(exec, url.string());
87 }
88
89 JSValue jsStringOrNull(ExecState* exec, const KURL& url)
90 {
91     if (url.isNull())
92         return jsNull();
93     return jsString(exec, url.string());
94 }
95
96 JSValue jsStringOrUndefined(ExecState* exec, const KURL& url)
97 {
98     if (url.isNull())
99         return jsUndefined();
100     return jsString(exec, url.string());
101 }
102
103 JSValue jsStringOrFalse(ExecState* exec, const KURL& url)
104 {
105     if (url.isNull())
106         return jsBoolean(false);
107     return jsString(exec, url.string());
108 }
109
110 AtomicStringImpl* findAtomicString(PropertyName propertyName)
111 {
112     StringImpl* impl = propertyName.publicName();
113     if (!impl)
114         return 0;
115     ASSERT(impl->existingHash());
116     return AtomicString::find(impl->characters(), impl->length(), impl->existingHash());
117 }
118
119 String valueToStringWithNullCheck(ExecState* exec, JSValue value)
120 {
121     if (value.isNull())
122         return String();
123     return ustringToString(value.toString(exec)->value(exec));
124 }
125
126 String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
127 {
128     if (value.isUndefinedOrNull())
129         return String();
130     return ustringToString(value.toString(exec)->value(exec));
131 }
132
133 JSValue jsDateOrNull(ExecState* exec, double value)
134 {
135     if (!isfinite(value))
136         return jsNull();
137     return DateInstance::create(exec, exec->lexicalGlobalObject()->dateStructure(), value);
138 }
139
140 double valueToDate(ExecState* exec, JSValue value)
141 {
142     if (value.isNumber())
143         return value.asNumber();
144     if (!value.inherits(&DateInstance::s_info))
145         return std::numeric_limits<double>::quiet_NaN();
146     return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
147 }
148
149 void reportException(ExecState* exec, JSValue exception)
150 {
151     if (isTerminatedExecutionException(exception))
152         return;
153
154     UString errorMessage = exception.toString(exec)->value(exec);
155     JSObject* exceptionObject = exception.toObject(exec);
156     int lineNumber = exceptionObject->get(exec, Identifier(exec, "line")).toInt32(exec);
157     UString exceptionSourceURL = exceptionObject->get(exec, Identifier(exec, "sourceURL")).toString(exec)->value(exec);
158     exec->clearException();
159
160     if (ExceptionBase* exceptionBase = toExceptionBase(exception))
161         errorMessage = stringToUString(exceptionBase->message() + ": "  + exceptionBase->description());
162
163     ScriptExecutionContext* scriptExecutionContext = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->scriptExecutionContext();
164
165     // scriptExecutionContext can be null when the relevant global object is a stale inner window object.
166     // It's harmless to return here without reporting the exception to the log and the debugger in this case.
167     if (!scriptExecutionContext)
168         return;
169
170     scriptExecutionContext->reportException(ustringToString(errorMessage), lineNumber, ustringToString(exceptionSourceURL), 0);
171 }
172
173 void reportCurrentException(ExecState* exec)
174 {
175     JSValue exception = exec->exception();
176     exec->clearException();
177     reportException(exec, exception);
178 }
179
180 #define TRY_TO_CREATE_EXCEPTION(interfaceName) \
181     case interfaceName##Type: \
182         errorObject = toJS(exec, globalObject, interfaceName::create(description)); \
183         break;
184
185 void setDOMException(ExecState* exec, ExceptionCode ec)
186 {
187     if (!ec || exec->hadException())
188         return;
189
190     // FIXME: All callers to setDOMException need to pass in the right global object
191     // for now, we're going to assume the lexicalGlobalObject.  Which is wrong in cases like this:
192     // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes.
193     JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec);
194
195     ExceptionCodeDescription description(ec);
196
197     JSValue errorObject;
198     switch (description.type) {
199         DOM_EXCEPTION_INTERFACES_FOR_EACH(TRY_TO_CREATE_EXCEPTION)
200     }
201
202     ASSERT(errorObject);
203     throwError(exec, errorObject);
204 }
205
206 #undef TRY_TO_CREATE_EXCEPTION
207
208 DOMWindow* activeDOMWindow(ExecState* exec)
209 {
210     return asJSDOMWindow(exec->lexicalGlobalObject())->impl();
211 }
212
213 DOMWindow* firstDOMWindow(ExecState* exec)
214 {
215     return asJSDOMWindow(exec->dynamicGlobalObject())->impl();
216 }
217
218 bool shouldAllowAccessToNode(ExecState* exec, Node* node)
219 {
220     return node && shouldAllowAccessToFrame(exec, node->document()->frame());
221 }
222
223 bool shouldAllowAccessToFrame(ExecState* exec, Frame* frame)
224 {
225     if (!frame)
226         return false;
227     JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec));
228     return window && window->allowsAccessFrom(exec);
229 }
230
231 bool shouldAllowAccessToFrame(ExecState* exec, Frame* frame, String& message)
232 {
233     if (!frame)
234         return false;
235     JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec));
236     return window && window->allowsAccessFrom(exec, message);
237 }
238
239 void printErrorMessageForFrame(Frame* frame, const String& message)
240 {
241     if (!frame)
242         return;
243     frame->domWindow()->printErrorMessage(message);
244 }
245
246 JSValue objectToStringFunctionGetter(ExecState* exec, JSValue, PropertyName propertyName)
247 {
248     return JSFunction::create(exec, exec->lexicalGlobalObject(), 0, propertyName.publicName(), objectProtoFuncToString);
249 }
250
251 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo)
252 {
253     JSDOMStructureMap& structures = globalObject->structures();
254     return structures.get(classInfo).get();
255 }
256
257 Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, Structure* structure, const ClassInfo* classInfo)
258 {
259     JSDOMStructureMap& structures = globalObject->structures();
260     ASSERT(!structures.contains(classInfo));
261     return structures.set(classInfo, WriteBarrier<Structure>(globalObject->globalData(), globalObject, structure)).iterator->second.get();
262 }
263
264 JSC::JSObject* toJSSequence(ExecState* exec, JSValue value, unsigned& length)
265 {
266     JSObject* object = value.getObject();
267     if (!object) {
268         throwTypeError(exec);
269         return 0;
270     }
271     JSValue lengthValue = object->get(exec, exec->propertyNames().length);
272     if (exec->hadException())
273         return 0;
274
275     if (lengthValue.isUndefinedOrNull()) {
276         throwTypeError(exec);
277         return 0;
278     }
279
280     length = lengthValue.toUInt32(exec);
281     if (exec->hadException())
282         return 0;
283
284     return object;
285 }
286
287 } // namespace WebCore