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#include <wtf/MathExtras.h>
43
44using namespace JSC;
45
46namespace WebCore {
47
48ASSERT_HAS_TRIVIAL_DESTRUCTOR(DOMConstructorObject);
49ASSERT_HAS_TRIVIAL_DESTRUCTOR(DOMConstructorWithDocument);
50
51const JSC::HashTable* getHashTableForGlobalData(VM& vm, const JSC::HashTable* staticTable)
52{
53    return DOMObjectHashTableMap::mapFor(vm).get(staticTable);
54}
55
56JSValue jsStringOrNull(ExecState* exec, const String& s)
57{
58    if (s.isNull())
59        return jsNull();
60    return jsStringWithCache(exec, s);
61}
62
63JSValue jsOwnedStringOrNull(ExecState* exec, const String& s)
64{
65    if (s.isNull())
66        return jsNull();
67    return jsOwnedString(exec, s);
68}
69
70JSValue jsStringOrUndefined(ExecState* exec, const String& s)
71{
72    if (s.isNull())
73        return jsUndefined();
74    return jsStringWithCache(exec, s);
75}
76
77JSValue jsString(ExecState* exec, const KURL& url)
78{
79    return jsStringWithCache(exec, url.string());
80}
81
82JSValue jsStringOrNull(ExecState* exec, const KURL& url)
83{
84    if (url.isNull())
85        return jsNull();
86    return jsStringWithCache(exec, url.string());
87}
88
89JSValue jsStringOrUndefined(ExecState* exec, const KURL& url)
90{
91    if (url.isNull())
92        return jsUndefined();
93    return jsStringWithCache(exec, url.string());
94}
95
96AtomicStringImpl* findAtomicString(PropertyName propertyName)
97{
98    StringImpl* impl = propertyName.publicName();
99    if (!impl)
100        return 0;
101    ASSERT(impl->existingHash());
102    return AtomicString::find(impl);
103}
104
105String valueToStringWithNullCheck(ExecState* exec, JSValue value)
106{
107    if (value.isNull())
108        return String();
109    return value.toString(exec)->value(exec);
110}
111
112String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
113{
114    if (value.isUndefinedOrNull())
115        return String();
116    return value.toString(exec)->value(exec);
117}
118
119JSValue jsDateOrNull(ExecState* exec, double value)
120{
121    if (!std::isfinite(value))
122        return jsNull();
123    return DateInstance::create(exec, exec->lexicalGlobalObject()->dateStructure(), value);
124}
125
126double valueToDate(ExecState* exec, JSValue value)
127{
128    if (value.isNumber())
129        return value.asNumber();
130    if (!value.inherits(&DateInstance::s_info))
131        return std::numeric_limits<double>::quiet_NaN();
132    return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
133}
134
135JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<DOMStringList> stringList)
136{
137    JSC::MarkedArgumentBuffer list;
138    if (stringList) {
139        for (unsigned i = 0; i < stringList->length(); ++i)
140            list.append(jsStringWithCache(exec, stringList->item(i)));
141    }
142    return JSC::constructArray(exec, 0, globalObject, list);
143}
144
145void reportException(ExecState* exec, JSValue exception, CachedScript* cachedScript)
146{
147    if (isTerminatedExecutionException(exception))
148        return;
149
150    Interpreter::ErrorHandlingMode mode(exec);
151
152    RefPtr<ScriptCallStack> callStack(createScriptCallStackFromException(exec, exception, ScriptCallStack::maxCallStackSizeToCapture));
153    exec->clearException();
154    exec->clearSupplementaryExceptionInfo();
155
156    JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
157    if (JSDOMWindow* window = jsDynamicCast<JSDOMWindow*>(globalObject)) {
158        if (!window->impl()->isCurrentlyDisplayedInFrame())
159            return;
160    }
161
162    int lineNumber = 0;
163    int columnNumber = 0;
164    String exceptionSourceURL;
165    if (callStack->size()) {
166        const ScriptCallFrame& frame = callStack->at(0);
167        lineNumber = frame.lineNumber();
168        columnNumber = frame.columnNumber();
169        exceptionSourceURL = frame.sourceURL();
170    } else {
171        // There may not be an exceptionStack for a <script> SyntaxError. Fallback to getting at least the line and sourceURL from the exception.
172        JSObject* exceptionObject = exception.toObject(exec);
173        JSValue lineValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "line"));
174        lineNumber = lineValue && lineValue.isNumber() ? int(lineValue.toNumber(exec)) : 0;
175        JSValue sourceURLValue = exceptionObject->getDirect(exec->vm(), Identifier(exec, "sourceURL"));
176        exceptionSourceURL = sourceURLValue && sourceURLValue.isString() ? sourceURLValue.toString(exec)->value(exec) : ASCIILiteral("undefined");
177    }
178
179    String errorMessage;
180    if (ExceptionBase* exceptionBase = toExceptionBase(exception))
181        errorMessage = exceptionBase->message() + ": "  + exceptionBase->description();
182    else {
183        // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions
184        // If this is a custon exception object, call toString on it to try and get a nice string representation for the exception.
185        errorMessage = exception.toString(exec)->value(exec);
186        exec->clearException();
187        exec->clearSupplementaryExceptionInfo();
188    }
189
190    ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
191    scriptExecutionContext->reportException(errorMessage, lineNumber, columnNumber, exceptionSourceURL, callStack->size() ? callStack : 0, cachedScript);
192}
193
194void reportCurrentException(ExecState* exec)
195{
196    JSValue exception = exec->exception();
197    exec->clearException();
198    reportException(exec, exception);
199}
200
201#define TRY_TO_CREATE_EXCEPTION(interfaceName) \
202    case interfaceName##Type: \
203        errorObject = toJS(exec, globalObject, interfaceName::create(description)); \
204        break;
205
206void setDOMException(ExecState* exec, ExceptionCode ec)
207{
208    if (!ec || exec->hadException())
209        return;
210
211    // FIXME: Handle other WebIDL exception types.
212    if (ec == TypeError) {
213        throwTypeError(exec);
214        return;
215    }
216
217    // FIXME: All callers to setDOMException need to pass in the right global object
218    // for now, we're going to assume the lexicalGlobalObject.  Which is wrong in cases like this:
219    // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes.
220    JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec);
221
222    ExceptionCodeDescription description(ec);
223
224    JSValue errorObject;
225    switch (description.type) {
226        DOM_EXCEPTION_INTERFACES_FOR_EACH(TRY_TO_CREATE_EXCEPTION)
227    }
228
229    ASSERT(errorObject);
230    throwError(exec, errorObject);
231}
232
233#undef TRY_TO_CREATE_EXCEPTION
234
235bool shouldAllowAccessToNode(ExecState* exec, Node* node)
236{
237    return BindingSecurity::shouldAllowAccessToNode(exec, node);
238}
239
240bool shouldAllowAccessToFrame(ExecState* exec, Frame* target)
241{
242    return BindingSecurity::shouldAllowAccessToFrame(exec, target);
243}
244
245bool shouldAllowAccessToFrame(ExecState* exec, Frame* frame, String& message)
246{
247    if (!frame)
248        return false;
249    if (BindingSecurity::shouldAllowAccessToFrame(exec, frame, DoNotReportSecurityError))
250        return true;
251    message = frame->document()->domWindow()->crossDomainAccessErrorMessage(activeDOMWindow(exec));
252    return false;
253}
254
255bool shouldAllowAccessToDOMWindow(ExecState* exec, DOMWindow* target, String& message)
256{
257    if (!target)
258        return false;
259    if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, target, DoNotReportSecurityError))
260        return true;
261    message = target->crossDomainAccessErrorMessage(activeDOMWindow(exec));
262    return false;
263}
264
265void printErrorMessageForFrame(Frame* frame, const String& message)
266{
267    if (!frame)
268        return;
269    frame->document()->domWindow()->printErrorMessage(message);
270}
271
272JSValue objectToStringFunctionGetter(ExecState* exec, JSValue, PropertyName propertyName)
273{
274    return JSFunction::create(exec, exec->lexicalGlobalObject(), 0, propertyName.publicName(), objectProtoFuncToString);
275}
276
277Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo)
278{
279    JSDOMStructureMap& structures = globalObject->structures();
280    return structures.get(classInfo).get();
281}
282
283Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, Structure* structure, const ClassInfo* classInfo)
284{
285    JSDOMStructureMap& structures = globalObject->structures();
286    ASSERT(!structures.contains(classInfo));
287    return structures.set(classInfo, WriteBarrier<Structure>(globalObject->vm(), globalObject, structure)).iterator->value.get();
288}
289
290static const int8_t kMaxInt8 = 127;
291static const int8_t kMinInt8 = -128;
292static const uint8_t kMaxUInt8 = 255;
293static const int32_t kMaxInt32 = 0x7fffffff;
294static const int32_t kMinInt32 = -kMaxInt32 - 1;
295static const uint32_t kMaxUInt32 = 0xffffffffU;
296static const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, largest integer exactly representable in ECMAScript.
297
298static double enforceRange(ExecState* exec, double x, double minimum, double maximum)
299{
300    if (std::isnan(x) || std::isinf(x)) {
301        throwTypeError(exec);
302        return 0;
303    }
304    x = trunc(x);
305    if (x < minimum || x > maximum) {
306        throwTypeError(exec);
307        return 0;
308    }
309    return x;
310}
311
312// http://www.w3.org/TR/WebIDL/#es-byte
313int8_t toInt8(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
314{
315    // Fast path if the value is already a 32-bit signed integer in the right range.
316    if (value.isInt32()) {
317        int32_t d = value.asInt32();
318        if (d >= kMinInt8 && d <= kMaxInt8)
319            return static_cast<int8_t>(d);
320        if (configuration == EnforceRange) {
321            throwTypeError(exec);
322            return 0;
323        }
324        d %= 256;
325        return static_cast<int8_t>(d > kMaxInt8 ? d - 256 : d);
326    }
327
328    double x = value.toNumber(exec);
329    if (exec->hadException())
330        return 0;
331
332    if (configuration == EnforceRange)
333        return enforceRange(exec, x, kMinInt8, kMaxInt8);
334
335    if (std::isnan(x) || std::isinf(x) || !x)
336        return 0;
337
338    x = x < 0 ? -floor(abs(x)) : floor(abs(x));
339    x = fmod(x, 256); // 2^8.
340
341    return static_cast<int8_t>(x > kMaxInt8 ? x - 256 : x);
342}
343
344// http://www.w3.org/TR/WebIDL/#es-octet
345uint8_t toUInt8(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
346{
347    // Fast path if the value is already a 32-bit unsigned integer in the right range.
348    if (value.isUInt32()) {
349        uint32_t d = value.asUInt32();
350        if (d <= kMaxUInt8)
351            return static_cast<uint8_t>(d);
352        if (configuration == EnforceRange) {
353            throwTypeError(exec);
354            return 0;
355        }
356        return static_cast<uint8_t>(d % 256); // 2^8.
357    }
358
359    double x = value.toNumber(exec);
360    if (exec->hadException())
361        return 0;
362
363    if (configuration == EnforceRange)
364        return enforceRange(exec, x, 0, kMaxUInt8);
365
366    if (std::isnan(x) || std::isinf(x) || !x)
367        return 0;
368
369    x = x < 0 ? -floor(abs(x)) : floor(abs(x));
370    return static_cast<uint8_t>(fmod(x, 256)); // 2^8.
371}
372
373// http://www.w3.org/TR/WebIDL/#es-long
374int32_t toInt32EnforceRange(ExecState* exec, JSValue value)
375{
376    if (value.isInt32())
377        return value.asInt32();
378
379    double x = value.toNumber(exec);
380    if (exec->hadException())
381        return 0;
382    return enforceRange(exec, x, kMinInt32, kMaxInt32);
383}
384
385// http://www.w3.org/TR/WebIDL/#es-unsigned-long
386uint32_t toUInt32EnforceRange(ExecState* exec, JSValue value)
387{
388    if (value.isUInt32())
389        return value.asUInt32();
390
391    double x = value.toNumber(exec);
392    if (exec->hadException())
393        return 0;
394    return enforceRange(exec, x, 0, kMaxUInt32);
395}
396
397// http://www.w3.org/TR/WebIDL/#es-long-long
398int64_t toInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
399{
400    if (value.isInt32())
401        return value.asInt32();
402
403    double x = value.toNumber(exec);
404    if (exec->hadException())
405        return 0;
406
407    if (configuration == EnforceRange)
408        return enforceRange(exec, x, -kJSMaxInteger, kJSMaxInteger);
409
410    // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
411    unsigned long long n;
412    doubleToInteger(x, n);
413    return n;
414}
415
416// http://www.w3.org/TR/WebIDL/#es-unsigned-long-long
417uint64_t toUInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
418{
419    if (value.isUInt32())
420        return value.asUInt32();
421
422    double x = value.toNumber(exec);
423    if (exec->hadException())
424        return 0;
425
426    if (configuration == EnforceRange)
427        return enforceRange(exec, x, 0, kJSMaxInteger);
428
429    // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
430    unsigned long long n;
431    doubleToInteger(x, n);
432    return n;
433}
434
435} // namespace WebCore
436