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