2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
4 * Copyright (C) 2007 Samuel Weinig <sam@webkit.org>
5 * Copyright (C) 2013 Michael Pruett <michael@68k.org>
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.
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.
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
23 #include "JSDOMBinding.h"
25 #include "CachedScript.h"
26 #include "DOMConstructorWithDocument.h"
27 #include "DOMObjectHashTableMap.h"
28 #include "DOMStringList.h"
29 #include "ExceptionCode.h"
30 #include "ExceptionHeaders.h"
31 #include "ExceptionInterfaces.h"
33 #include "HTMLParserIdioms.h"
34 #include "JSDOMWindowCustom.h"
35 #include "JSExceptionBase.h"
36 #include "SecurityOrigin.h"
37 #include <inspector/ScriptCallStack.h>
38 #include <inspector/ScriptCallStackFactory.h>
39 #include <interpreter/Interpreter.h>
40 #include <runtime/DateInstance.h>
41 #include <runtime/Error.h>
42 #include <runtime/ErrorHandlingScope.h>
43 #include <runtime/ExceptionHelpers.h>
44 #include <runtime/JSFunction.h>
46 #include <wtf/MathExtras.h>
49 using namespace Inspector;
53 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DOMConstructorObject);
54 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DOMConstructorWithDocument);
56 void addImpureProperty(const AtomicString& propertyName)
58 JSDOMWindow::commonVM().addImpureProperty(propertyName);
61 const JSC::HashTable& getHashTableForGlobalData(VM& vm, const JSC::HashTable& staticTable)
63 return DOMObjectHashTableMap::mapFor(vm).get(staticTable);
66 JSValue jsStringOrNull(ExecState* exec, const String& s)
70 return jsStringWithCache(exec, s);
73 JSValue jsOwnedStringOrNull(ExecState* exec, const String& s)
77 return jsOwnedString(exec, s);
80 JSValue jsStringOrUndefined(ExecState* exec, const String& s)
84 return jsStringWithCache(exec, s);
87 JSValue jsString(ExecState* exec, const URL& url)
89 return jsStringWithCache(exec, url.string());
92 JSValue jsStringOrNull(ExecState* exec, const URL& url)
96 return jsStringWithCache(exec, url.string());
99 JSValue jsStringOrUndefined(ExecState* exec, const URL& url)
102 return jsUndefined();
103 return jsStringWithCache(exec, url.string());
106 AtomicStringImpl* findAtomicString(PropertyName propertyName)
108 StringImpl* impl = propertyName.publicName();
111 ASSERT(impl->existingHash());
112 return AtomicString::findStringWithHash(*impl);
115 String valueToStringWithNullCheck(ExecState* exec, JSValue value)
119 return value.toString(exec)->value(exec);
122 String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
124 if (value.isUndefinedOrNull())
126 return value.toString(exec)->value(exec);
129 JSValue jsDateOrNull(ExecState* exec, double value)
131 if (!std::isfinite(value))
133 return DateInstance::create(exec->vm(), exec->lexicalGlobalObject()->dateStructure(), value);
136 double valueToDate(ExecState* exec, JSValue value)
138 if (value.isNumber())
139 return value.asNumber();
140 if (!value.inherits(DateInstance::info()))
141 return std::numeric_limits<double>::quiet_NaN();
142 return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
145 JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<DOMStringList> stringList)
147 JSC::MarkedArgumentBuffer list;
149 for (unsigned i = 0; i < stringList->length(); ++i)
150 list.append(jsStringWithCache(exec, stringList->item(i)));
152 return JSC::constructArray(exec, 0, globalObject, list);
155 void reportException(ExecState* exec, JSValue exception, CachedScript* cachedScript)
157 RELEASE_ASSERT(exec->vm().currentThreadIsHoldingAPILock());
158 if (isTerminatedExecutionException(exception))
161 ErrorHandlingScope errorScope(exec->vm());
163 RefPtr<ScriptCallStack> callStack(createScriptCallStackFromException(exec, exception, ScriptCallStack::maxCallStackSizeToCapture));
164 exec->clearException();
165 exec->clearSupplementaryExceptionInfo();
167 JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
168 if (JSDOMWindow* window = jsDynamicCast<JSDOMWindow*>(globalObject)) {
169 if (!window->impl().isCurrentlyDisplayedInFrame())
174 int columnNumber = 0;
175 String exceptionSourceURL;
176 if (const ScriptCallFrame* callFrame = callStack->firstNonNativeCallFrame()) {
177 lineNumber = callFrame->lineNumber();
178 columnNumber = callFrame->columnNumber();
179 exceptionSourceURL = callFrame->sourceURL();
183 if (ExceptionBase* exceptionBase = toExceptionBase(exception))
184 errorMessage = exceptionBase->message() + ": " + exceptionBase->description();
186 // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions
187 // If this is a custon exception object, call toString on it to try and get a nice string representation for the exception.
188 errorMessage = exception.toString(exec)->value(exec);
189 exec->clearException();
190 exec->clearSupplementaryExceptionInfo();
193 ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
194 scriptExecutionContext->reportException(errorMessage, lineNumber, columnNumber, exceptionSourceURL, callStack->size() ? callStack : 0, cachedScript);
197 void reportCurrentException(ExecState* exec)
199 JSValue exception = exec->exception();
200 exec->clearException();
201 reportException(exec, exception);
204 #define TRY_TO_CREATE_EXCEPTION(interfaceName) \
205 case interfaceName##Type: \
206 errorObject = toJS(exec, globalObject, interfaceName::create(description)); \
209 void setDOMException(ExecState* exec, ExceptionCode ec)
211 if (!ec || exec->hadException())
214 // FIXME: Handle other WebIDL exception types.
215 if (ec == TypeError) {
216 throwTypeError(exec);
220 // FIXME: All callers to setDOMException need to pass in the right global object
221 // for now, we're going to assume the lexicalGlobalObject. Which is wrong in cases like this:
222 // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes.
223 JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec);
225 ExceptionCodeDescription description(ec);
228 switch (description.type) {
229 DOM_EXCEPTION_INTERFACES_FOR_EACH(TRY_TO_CREATE_EXCEPTION)
233 exec->vm().throwException(exec, errorObject);
236 #undef TRY_TO_CREATE_EXCEPTION
238 bool shouldAllowAccessToNode(ExecState* exec, Node* node)
240 return BindingSecurity::shouldAllowAccessToNode(exec, node);
243 bool shouldAllowAccessToFrame(ExecState* exec, Frame* target)
245 return BindingSecurity::shouldAllowAccessToFrame(exec, target);
248 bool shouldAllowAccessToFrame(ExecState* exec, Frame* frame, String& message)
252 if (BindingSecurity::shouldAllowAccessToFrame(exec, frame, DoNotReportSecurityError))
254 message = frame->document()->domWindow()->crossDomainAccessErrorMessage(activeDOMWindow(exec));
258 bool shouldAllowAccessToDOMWindow(ExecState* exec, DOMWindow& target, String& message)
260 if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, target, DoNotReportSecurityError))
262 message = target.crossDomainAccessErrorMessage(activeDOMWindow(exec));
266 void printErrorMessageForFrame(Frame* frame, const String& message)
270 frame->document()->domWindow()->printErrorMessage(message);
273 EncodedJSValue objectToStringFunctionGetter(ExecState* exec, JSObject*, EncodedJSValue, PropertyName propertyName)
275 return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 0, propertyName.publicName(), objectProtoFuncToString));
278 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo)
280 JSDOMStructureMap& structures = globalObject->structures();
281 return structures.get(classInfo).get();
284 Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, Structure* structure, const ClassInfo* classInfo)
286 JSDOMStructureMap& structures = globalObject->structures();
287 ASSERT(!structures.contains(classInfo));
288 return structures.set(classInfo, WriteBarrier<Structure>(globalObject->vm(), globalObject, structure)).iterator->value.get();
291 static const int32_t kMaxInt32 = 0x7fffffff;
292 static const int32_t kMinInt32 = -kMaxInt32 - 1;
293 static const uint32_t kMaxUInt32 = 0xffffffffU;
294 static const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, largest integer exactly representable in ECMAScript.
296 static double enforceRange(ExecState* exec, double x, double minimum, double maximum)
298 if (std::isnan(x) || std::isinf(x)) {
299 throwTypeError(exec);
303 if (x < minimum || x > maximum) {
304 throwTypeError(exec);
310 template <typename T>
311 struct IntTypeLimits {
315 struct IntTypeLimits<int8_t> {
316 static const int8_t minValue = -128;
317 static const int8_t maxValue = 127;
318 static const unsigned numberOfValues = 256; // 2^8
322 struct IntTypeLimits<uint8_t> {
323 static const uint8_t maxValue = 255;
324 static const unsigned numberOfValues = 256; // 2^8
328 struct IntTypeLimits<int16_t> {
329 static const short minValue = -32768;
330 static const short maxValue = 32767;
331 static const unsigned numberOfValues = 65536; // 2^16
335 struct IntTypeLimits<uint16_t> {
336 static const unsigned short maxValue = 65535;
337 static const unsigned numberOfValues = 65536; // 2^16
340 template <typename T>
341 static inline T toSmallerInt(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
343 typedef IntTypeLimits<T> LimitsTrait;
344 // Fast path if the value is already a 32-bit signed integer in the right range.
345 if (value.isInt32()) {
346 int32_t d = value.asInt32();
347 if (d >= LimitsTrait::minValue && d <= LimitsTrait::maxValue)
348 return static_cast<T>(d);
349 if (configuration == EnforceRange) {
350 throwTypeError(exec);
353 d %= LimitsTrait::numberOfValues;
354 return static_cast<T>(d > LimitsTrait::maxValue ? d - LimitsTrait::numberOfValues : d);
357 double x = value.toNumber(exec);
358 if (exec->hadException())
361 if (configuration == EnforceRange)
362 return enforceRange(exec, x, LimitsTrait::minValue, LimitsTrait::maxValue);
364 if (std::isnan(x) || std::isinf(x) || !x)
367 x = x < 0 ? -floor(fabs(x)) : floor(fabs(x));
368 x = fmod(x, LimitsTrait::numberOfValues);
370 return static_cast<T>(x > LimitsTrait::maxValue ? x - LimitsTrait::numberOfValues : x);
373 template <typename T>
374 static inline T toSmallerUInt(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
376 typedef IntTypeLimits<T> LimitsTrait;
377 // Fast path if the value is already a 32-bit unsigned integer in the right range.
378 if (value.isUInt32()) {
379 uint32_t d = value.asUInt32();
380 if (d <= LimitsTrait::maxValue)
381 return static_cast<T>(d);
382 if (configuration == EnforceRange) {
383 throwTypeError(exec);
386 return static_cast<T>(d);
389 double x = value.toNumber(exec);
390 if (exec->hadException())
393 if (configuration == EnforceRange)
394 return enforceRange(exec, x, 0, LimitsTrait::maxValue);
396 if (std::isnan(x) || std::isinf(x) || !x)
399 x = x < 0 ? -floor(fabs(x)) : floor(fabs(x));
400 return static_cast<T>(fmod(x, LimitsTrait::numberOfValues));
403 // http://www.w3.org/TR/WebIDL/#es-byte
404 int8_t toInt8(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
406 return toSmallerInt<int8_t>(exec, value, configuration);
409 // http://www.w3.org/TR/WebIDL/#es-octet
410 uint8_t toUInt8(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
412 return toSmallerUInt<uint8_t>(exec, value, configuration);
415 // http://www.w3.org/TR/WebIDL/#es-short
416 int16_t toInt16(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
418 return toSmallerInt<int16_t>(exec, value, configuration);
421 // http://www.w3.org/TR/WebIDL/#es-unsigned-short
422 uint16_t toUInt16(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
424 return toSmallerUInt<uint16_t>(exec, value, configuration);
427 // http://www.w3.org/TR/WebIDL/#es-long
428 int32_t toInt32EnforceRange(ExecState* exec, JSValue value)
431 return value.asInt32();
433 double x = value.toNumber(exec);
434 if (exec->hadException())
436 return enforceRange(exec, x, kMinInt32, kMaxInt32);
439 // http://www.w3.org/TR/WebIDL/#es-unsigned-long
440 uint32_t toUInt32EnforceRange(ExecState* exec, JSValue value)
442 if (value.isUInt32())
443 return value.asUInt32();
445 double x = value.toNumber(exec);
446 if (exec->hadException())
448 return enforceRange(exec, x, 0, kMaxUInt32);
451 // http://www.w3.org/TR/WebIDL/#es-long-long
452 int64_t toInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
455 return value.asInt32();
457 double x = value.toNumber(exec);
458 if (exec->hadException())
461 if (configuration == EnforceRange)
462 return enforceRange(exec, x, -kJSMaxInteger, kJSMaxInteger);
464 // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
465 unsigned long long n;
466 doubleToInteger(x, n);
470 // http://www.w3.org/TR/WebIDL/#es-unsigned-long-long
471 uint64_t toUInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
473 if (value.isUInt32())
474 return value.asUInt32();
476 double x = value.toNumber(exec);
477 if (exec->hadException())
480 if (configuration == EnforceRange)
481 return enforceRange(exec, x, 0, kJSMaxInteger);
483 // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
484 unsigned long long n;
485 doubleToInteger(x, n);
489 DOMWindow& activeDOMWindow(ExecState* exec)
491 return asJSDOMWindow(exec->lexicalGlobalObject())->impl();
494 DOMWindow& firstDOMWindow(ExecState* exec)
496 return asJSDOMWindow(exec->vmEntryGlobalObject())->impl();
499 static inline bool canAccessDocument(JSC::ExecState* state, Document* targetDocument, SecurityReportingOption reportingOption = ReportSecurityError)
504 DOMWindow& active = activeDOMWindow(state);
506 if (active.document()->securityOrigin()->canAccess(targetDocument->securityOrigin()))
509 if (reportingOption == ReportSecurityError)
510 printErrorMessageForFrame(targetDocument->frame(), targetDocument->domWindow()->crossDomainAccessErrorMessage(active));
515 bool BindingSecurity::shouldAllowAccessToDOMWindow(JSC::ExecState* state, DOMWindow& target, SecurityReportingOption reportingOption)
517 return canAccessDocument(state, target.document(), reportingOption);
520 bool BindingSecurity::shouldAllowAccessToFrame(JSC::ExecState* state, Frame* target, SecurityReportingOption reportingOption)
522 return target && canAccessDocument(state, target->document(), reportingOption);
525 bool BindingSecurity::shouldAllowAccessToNode(JSC::ExecState* state, Node* target)
527 return target && canAccessDocument(state, &target->document());
530 static EncodedJSValue throwTypeError(JSC::ExecState& state, const String& errorMessage)
532 return throwVMError(&state, createTypeError(&state, errorMessage));
535 static void appendArgumentMustBe(StringBuilder& builder, unsigned argumentIndex, const char* argumentName, const char* interfaceName, const char* functionName)
537 builder.appendLiteral("Argument ");
538 builder.appendNumber(argumentIndex + 1);
539 builder.appendLiteral(" ('");
540 builder.append(argumentName);
541 builder.appendLiteral("') to ");
543 builder.appendLiteral("the ");
544 builder.append(interfaceName);
545 builder.appendLiteral(" constructor");
547 builder.append(interfaceName);
549 builder.append(functionName);
551 builder.appendLiteral(" must be ");
554 JSC::EncodedJSValue reportDeprecatedGetterError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
556 auto& context = *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->scriptExecutionContext();
557 context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString("Deprecated attempt to access property '", attributeName, "' on a non-", interfaceName, " object."));
558 return JSValue::encode(jsUndefined());
561 void reportDeprecatedSetterError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
563 auto& context = *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->scriptExecutionContext();
564 context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString("Deprecated attempt to set property '", attributeName, "' on a non-", interfaceName, " object."));
567 JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::ExecState& state, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedValues)
569 StringBuilder builder;
570 appendArgumentMustBe(builder, argumentIndex, argumentName, functionInterfaceName, functionName);
571 builder.appendLiteral("one of: ");
572 builder.append(expectedValues);
573 return throwTypeError(state, builder.toString());
576 JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::ExecState& state, unsigned argumentIndex, const char* argumentName, const char* interfaceName, const char* functionName)
578 StringBuilder builder;
579 appendArgumentMustBe(builder, argumentIndex, argumentName, interfaceName, functionName);
580 builder.appendLiteral("a function");
581 return throwTypeError(state, builder.toString());
584 JSC::EncodedJSValue throwArgumentTypeError(JSC::ExecState& state, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedType)
586 StringBuilder builder;
587 appendArgumentMustBe(builder, argumentIndex, argumentName, functionInterfaceName, functionName);
588 builder.appendLiteral("an instance of ");
589 builder.append(expectedType);
590 return throwTypeError(state, builder.toString());
593 void throwArrayElementTypeError(JSC::ExecState& state)
595 throwTypeError(state, "Invalid Array element type");
598 void throwAttributeTypeError(JSC::ExecState& state, const char* interfaceName, const char* attributeName, const char* expectedType)
600 throwTypeError(state, makeString("The ", interfaceName, '.', attributeName, " attribute must be an instance of ", expectedType));
603 JSC::EncodedJSValue throwConstructorDocumentUnavailableError(JSC::ExecState& state, const char* interfaceName)
605 // FIXME: This is confusing exception wording. Can we reword to be clearer and more specific?
606 return throwVMError(&state, createReferenceError(&state, makeString(interfaceName, " constructor associated document is unavailable")));
609 JSC::EncodedJSValue throwGetterTypeError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
611 return throwTypeError(state, makeString("The ", interfaceName, '.', attributeName, " getter can only be used on instances of ", interfaceName));
614 void throwSequenceTypeError(JSC::ExecState& state)
616 throwTypeError(state, "Value is not a sequence");
619 void throwSetterTypeError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
621 throwTypeError(state, makeString("The ", interfaceName, '.', attributeName, " setter can only be used on instances of ", interfaceName));
624 EncodedJSValue throwThisTypeError(JSC::ExecState& state, const char* interfaceName, const char* functionName)
626 return throwTypeError(state, makeString("Can only call ", interfaceName, '.', functionName, " on instances of ", interfaceName));
629 } // namespace WebCore