ScriptExecutionContext log exception should include a column number
[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  *  Copyright (C) 2013 Michael Pruett <michael@68k.org>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "config.h"
23 #include "JSDOMBinding.h"
24
25 #include "BindingSecurity.h"
26 #include "CachedScript.h"
27 #include "DOMObjectHashTableMap.h"
28 #include "DOMStringList.h"
29 #include "ExceptionCode.h"
30 #include "ExceptionHeaders.h"
31 #include "ExceptionInterfaces.h"
32 #include "Frame.h"
33 #include "JSDOMWindowCustom.h"
34 #include "JSExceptionBase.h"
35 #include "ScriptCallStack.h"
36 #include "ScriptCallStackFactory.h"
37 #include <interpreter/Interpreter.h>
38 #include <runtime/DateInstance.h>
39 #include <runtime/Error.h>
40 #include <runtime/ExceptionHelpers.h>
41 #include <runtime/JSFunction.h>
42
43 using namespace JSC;
44
45 namespace WebCore {
46
47 ASSERT_HAS_TRIVIAL_DESTRUCTOR(DOMConstructorObject);
48 ASSERT_HAS_TRIVIAL_DESTRUCTOR(DOMConstructorWithDocument);
49
50 const JSC::HashTable* getHashTableForGlobalData(VM& vm, const JSC::HashTable* staticTable)
51 {
52     return DOMObjectHashTableMap::mapFor(vm).get(staticTable);
53 }
54
55 JSValue jsStringOrNull(ExecState* exec, const String& s)
56 {
57     if (s.isNull())
58         return jsNull();
59     return jsStringWithCache(exec, s);
60 }
61
62 JSValue jsOwnedStringOrNull(ExecState* exec, const String& s)
63 {
64     if (s.isNull())
65         return jsNull();
66     return jsOwnedString(exec, s);
67 }
68
69 JSValue jsStringOrUndefined(ExecState* exec, const String& s)
70 {
71     if (s.isNull())
72         return jsUndefined();
73     return jsStringWithCache(exec, s);
74 }
75
76 JSValue jsString(ExecState* exec, const KURL& url)
77 {
78     return jsStringWithCache(exec, url.string());
79 }
80
81 JSValue jsStringOrNull(ExecState* exec, const KURL& url)
82 {
83     if (url.isNull())
84         return jsNull();
85     return jsStringWithCache(exec, url.string());
86 }
87
88 JSValue jsStringOrUndefined(ExecState* exec, const KURL& url)
89 {
90     if (url.isNull())
91         return jsUndefined();
92     return jsStringWithCache(exec, url.string());
93 }
94
95 AtomicStringImpl* findAtomicString(PropertyName propertyName)
96 {
97     StringImpl* impl = propertyName.publicName();
98     if (!impl)
99         return 0;
100     ASSERT(impl->existingHash());
101     return AtomicString::find(impl);
102 }
103
104 String valueToStringWithNullCheck(ExecState* exec, JSValue value)
105 {
106     if (value.isNull())
107         return String();
108     return value.toString(exec)->value(exec);
109 }
110
111 String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
112 {
113     if (value.isUndefinedOrNull())
114         return String();
115     return value.toString(exec)->value(exec);
116 }
117
118 JSValue jsDateOrNull(ExecState* exec, double value)
119 {
120     if (!std::isfinite(value))
121         return jsNull();
122     return DateInstance::create(exec, exec->lexicalGlobalObject()->dateStructure(), value);
123 }
124
125 double valueToDate(ExecState* exec, JSValue value)
126 {
127     if (value.isNumber())
128         return value.asNumber();
129     if (!value.inherits(&DateInstance::s_info))
130         return std::numeric_limits<double>::quiet_NaN();
131     return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
132 }
133
134 JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<DOMStringList> stringList)
135 {
136     JSC::MarkedArgumentBuffer list;
137     if (stringList) {
138         for (unsigned i = 0; i < stringList->length(); ++i)
139             list.append(jsStringWithCache(exec, stringList->item(i)));
140     }
141     return JSC::constructArray(exec, 0, globalObject, list);
142 }
143
144 void reportException(ExecState* exec, JSValue exception, CachedScript* cachedScript)
145 {
146     if (isTerminatedExecutionException(exception))
147         return;
148
149     Interpreter::ErrorHandlingMode mode(exec);
150
151     RefPtr<ScriptCallStack> callStack(createScriptCallStackFromException(exec, exception, ScriptCallStack::maxCallStackSizeToCapture));
152     exec->clearException();
153     exec->clearSupplementaryExceptionInfo();
154
155     JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
156     if (JSDOMWindow* window = jsDynamicCast<JSDOMWindow*>(globalObject)) {
157         if (!window->impl()->isCurrentlyDisplayedInFrame())
158             return;
159     }
160
161     int lineNumber = 0;
162     int columnNumber = 0;
163     String exceptionSourceURL;
164     if (callStack->size()) {
165         const ScriptCallFrame& frame = callStack->at(0);
166         lineNumber = frame.lineNumber();
167         columnNumber = frame.columnNumber();
168         exceptionSourceURL = frame.sourceURL();
169     } else {
170         // There may not be an exceptionStack for a <script> SyntaxError. Fallback to getting at least the line and sourceURL from the exception.
171         JSObject* exceptionObject = exception.toObject(exec);
172         JSValue lineValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "line"));
173         lineNumber = lineValue && lineValue.isNumber() ? int(lineValue.toNumber(exec)) : 0;
174         JSValue sourceURLValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "sourceURL"));
175         exceptionSourceURL = sourceURLValue && sourceURLValue.isString() ? sourceURLValue.toString(exec)->value(exec) : ASCIILiteral("undefined");
176     }
177
178     String errorMessage;
179     if (ExceptionBase* exceptionBase = toExceptionBase(exception))
180         errorMessage = exceptionBase->message() + ": "  + exceptionBase->description();
181     else {
182         // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions
183         // If this is a custon exception object, call toString on it to try and get a nice string representation for the exception.
184         errorMessage = exception.toString(exec)->value(exec);
185         exec->clearException();
186         exec->clearSupplementaryExceptionInfo();
187     }
188
189     ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
190     scriptExecutionContext->reportException(errorMessage, lineNumber, columnNumber, exceptionSourceURL, callStack->size() ? callStack : 0, cachedScript);
191 }
192
193 void reportCurrentException(ExecState* exec)
194 {
195     JSValue exception = exec->exception();
196     exec->clearException();
197     reportException(exec, exception);
198 }
199
200 #define TRY_TO_CREATE_EXCEPTION(interfaceName) \
201     case interfaceName##Type: \
202         errorObject = toJS(exec, globalObject, interfaceName::create(description)); \
203         break;
204
205 void setDOMException(ExecState* exec, ExceptionCode ec)
206 {
207     if (!ec || exec->hadException())
208         return;
209
210     // FIXME: Handle other WebIDL exception types.
211     if (ec == TypeError) {
212         throwTypeError(exec);
213         return;
214     }
215
216     // FIXME: All callers to setDOMException need to pass in the right global object
217     // for now, we're going to assume the lexicalGlobalObject.  Which is wrong in cases like this:
218     // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes.
219     JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec);
220
221     ExceptionCodeDescription description(ec);
222
223     JSValue errorObject;
224     switch (description.type) {
225         DOM_EXCEPTION_INTERFACES_FOR_EACH(TRY_TO_CREATE_EXCEPTION)
226     }
227
228     ASSERT(errorObject);
229     throwError(exec, errorObject);
230 }
231
232 #undef TRY_TO_CREATE_EXCEPTION
233
234 bool shouldAllowAccessToNode(ExecState* exec, Node* node)
235 {
236     return BindingSecurity::shouldAllowAccessToNode(exec, node);
237 }
238
239 bool shouldAllowAccessToFrame(ExecState* exec, Frame* target)
240 {
241     return BindingSecurity::shouldAllowAccessToFrame(exec, target);
242 }
243
244 bool shouldAllowAccessToFrame(ExecState* exec, Frame* frame, String& message)
245 {
246     if (!frame)
247         return false;
248     if (BindingSecurity::shouldAllowAccessToFrame(exec, frame, DoNotReportSecurityError))
249         return true;
250     message = frame->document()->domWindow()->crossDomainAccessErrorMessage(activeDOMWindow(exec));
251     return false;
252 }
253
254 bool shouldAllowAccessToDOMWindow(ExecState* exec, DOMWindow* target, String& message)
255 {
256     if (!target)
257         return false;
258     if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, target, DoNotReportSecurityError))
259         return true;
260     message = target->crossDomainAccessErrorMessage(activeDOMWindow(exec));
261     return false;
262 }
263
264 void printErrorMessageForFrame(Frame* frame, const String& message)
265 {
266     if (!frame)
267         return;
268     frame->document()->domWindow()->printErrorMessage(message);
269 }
270
271 JSValue objectToStringFunctionGetter(ExecState* exec, JSValue, PropertyName propertyName)
272 {
273     return JSFunction::create(exec, exec->lexicalGlobalObject(), 0, propertyName.publicName(), objectProtoFuncToString);
274 }
275
276 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo)
277 {
278     JSDOMStructureMap& structures = globalObject->structures();
279     return structures.get(classInfo).get();
280 }
281
282 Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, Structure* structure, const ClassInfo* classInfo)
283 {
284     JSDOMStructureMap& structures = globalObject->structures();
285     ASSERT(!structures.contains(classInfo));
286     return structures.set(classInfo, WriteBarrier<Structure>(globalObject->vm(), globalObject, structure)).iterator->value.get();
287 }
288
289 static const int32_t kMaxInt32 = 0x7fffffff;
290 static const int32_t kMinInt32 = -kMaxInt32 - 1;
291 static const uint32_t kMaxUInt32 = 0xffffffffU;
292 static const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, largest integer exactly representable in ECMAScript.
293
294 static double enforceRange(ExecState* exec, double x, double minimum, double maximum)
295 {
296     if (std::isnan(x) || std::isinf(x)) {
297         throwTypeError(exec);
298         return 0;
299     }
300     x = trunc(x);
301     if (x < minimum || x > maximum) {
302         throwTypeError(exec);
303         return 0;
304     }
305     return x;
306 }
307
308 // http://www.w3.org/TR/WebIDL/#es-long
309 int32_t toInt32EnforceRange(ExecState* exec, JSValue value)
310 {
311     if (value.isInt32())
312         return value.toInt32(exec);
313
314     double x = value.toNumber(exec);
315     if (exec->hadException())
316         return 0;
317     return enforceRange(exec, x, kMinInt32, kMaxInt32);
318 }
319
320 // http://www.w3.org/TR/WebIDL/#es-unsigned-long
321 uint32_t toUInt32EnforceRange(ExecState* exec, JSValue value)
322 {
323     if (value.isUInt32())
324         return value.toUInt32(exec);
325
326     double x = value.toNumber(exec);
327     if (exec->hadException())
328         return 0;
329     return enforceRange(exec, x, 0, kMaxUInt32);
330 }
331
332 // http://www.w3.org/TR/WebIDL/#es-long-long
333 int64_t toInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
334 {
335     if (value.isInt32())
336         return value.toInt32(exec);
337
338     double x = value.toNumber(exec);
339     if (exec->hadException())
340         return 0;
341
342     if (configuration == EnforceRange)
343         return enforceRange(exec, x, -kJSMaxInteger, kJSMaxInteger);
344
345     // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
346     unsigned long long n;
347     doubleToInteger(x, n);
348     return n;
349 }
350
351 // http://www.w3.org/TR/WebIDL/#es-unsigned-long-long
352 uint64_t toUInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
353 {
354     if (value.isUInt32())
355         return value.toUInt32(exec);
356
357     double x = value.toNumber(exec);
358     if (exec->hadException())
359         return 0;
360
361     if (configuration == EnforceRange)
362         return enforceRange(exec, x, 0, kJSMaxInteger);
363
364     // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
365     unsigned long long n;
366     doubleToInteger(x, n);
367     return n;
368 }
369
370 } // namespace WebCore