1/* 2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) 3 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2013 Apple Inc. All rights reserved. 4 * Copyright (C) 2007 Samuel Weinig <sam@webkit.org> 5 * Copyright (C) 2009 Google, Inc. All rights reserved. 6 * Copyright (C) 2012 Ericsson AB. All rights reserved. 7 * Copyright (C) 2013 Michael Pruett <michael@68k.org> 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24#ifndef JSDOMBinding_h 25#define JSDOMBinding_h 26 27#include "BindingState.h" 28#include "JSDOMGlobalObject.h" 29#include "JSDOMWrapper.h" 30#include "DOMWrapperWorld.h" 31#include "Document.h" 32#include "ScriptWrappable.h" 33#include "ScriptWrappableInlines.h" 34#include <heap/SlotVisitor.h> 35#include <heap/Weak.h> 36#include <heap/WeakInlines.h> 37#include <runtime/Error.h> 38#include <runtime/FunctionPrototype.h> 39#include <runtime/JSArray.h> 40#include <runtime/Lookup.h> 41#include <runtime/ObjectPrototype.h> 42#include <runtime/Operations.h> 43#include <wtf/Forward.h> 44#include <wtf/Noncopyable.h> 45#include <wtf/NullPtr.h> 46#include <wtf/Vector.h> 47 48namespace JSC { 49class HashEntry; 50} 51 52namespace WebCore { 53 54class DOMStringList; 55 56 class CachedScript; 57 class Frame; 58 class KURL; 59 60 typedef int ExceptionCode; 61 62 // Base class for all constructor objects in the JSC bindings. 63 class DOMConstructorObject : public JSDOMWrapper { 64 typedef JSDOMWrapper Base; 65 public: 66 static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) 67 { 68 return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), &s_info); 69 } 70 71 protected: 72 static const unsigned StructureFlags = JSC::ImplementsHasInstance | JSC::OverridesVisitChildren | JSDOMWrapper::StructureFlags; 73 DOMConstructorObject(JSC::Structure* structure, JSDOMGlobalObject* globalObject) 74 : JSDOMWrapper(structure, globalObject) 75 { 76 } 77 }; 78 79 // Constructors using this base class depend on being in a Document and 80 // can never be used from a WorkerContext. 81 class DOMConstructorWithDocument : public DOMConstructorObject { 82 typedef DOMConstructorObject Base; 83 public: 84 Document* document() const 85 { 86 return toDocument(scriptExecutionContext()); 87 } 88 89 protected: 90 DOMConstructorWithDocument(JSC::Structure* structure, JSDOMGlobalObject* globalObject) 91 : DOMConstructorObject(structure, globalObject) 92 { 93 } 94 95 void finishCreation(JSDOMGlobalObject* globalObject) 96 { 97 Base::finishCreation(globalObject->vm()); 98 ASSERT(globalObject->scriptExecutionContext()->isDocument()); 99 } 100 }; 101 102 JSC::Structure* getCachedDOMStructure(JSDOMGlobalObject*, const JSC::ClassInfo*); 103 JSC::Structure* cacheDOMStructure(JSDOMGlobalObject*, JSC::Structure*, const JSC::ClassInfo*); 104 105 inline JSDOMGlobalObject* deprecatedGlobalObjectForPrototype(JSC::ExecState* exec) 106 { 107 // FIXME: Callers to this function should be using the global object 108 // from which the object is being created, instead of assuming the lexical one. 109 // e.g. subframe.document.body should use the subframe's global object, not the lexical one. 110 return JSC::jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()); 111 } 112 113 template<class WrapperClass> inline JSC::Structure* getDOMStructure(JSC::ExecState* exec, JSDOMGlobalObject* globalObject) 114 { 115 if (JSC::Structure* structure = getCachedDOMStructure(globalObject, &WrapperClass::s_info)) 116 return structure; 117 return cacheDOMStructure(globalObject, WrapperClass::createStructure(exec->vm(), globalObject, WrapperClass::createPrototype(exec, globalObject)), &WrapperClass::s_info); 118 } 119 120 template<class WrapperClass> inline JSC::Structure* deprecatedGetDOMStructure(JSC::ExecState* exec) 121 { 122 // FIXME: This function is wrong. It uses the wrong global object for creating the prototype structure. 123 return getDOMStructure<WrapperClass>(exec, deprecatedGlobalObjectForPrototype(exec)); 124 } 125 126 template<class WrapperClass> inline JSC::JSObject* getDOMPrototype(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject) 127 { 128 return JSC::jsCast<JSC::JSObject*>(asObject(getDOMStructure<WrapperClass>(exec, JSC::jsCast<JSDOMGlobalObject*>(globalObject))->storedPrototype())); 129 } 130 131 inline JSDOMWrapper* getInlineCachedWrapper(DOMWrapperWorld*, void*) { return 0; } 132 inline bool setInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*, JSC::WeakHandleOwner*, void*) { return false; } 133 inline bool clearInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; } 134 135 inline JSDOMWrapper* getInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject) 136 { 137 if (!world->isNormal()) 138 return 0; 139 return domObject->wrapper(); 140 } 141 142 inline bool setInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject, JSDOMWrapper* wrapper, JSC::WeakHandleOwner* wrapperOwner, void* context) 143 { 144 if (!world->isNormal()) 145 return false; 146 domObject->setWrapper(*world->vm(), wrapper, wrapperOwner, context); 147 return true; 148 } 149 150 inline bool clearInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject, JSDOMWrapper* wrapper) 151 { 152 if (!world->isNormal()) 153 return false; 154 domObject->clearWrapper(wrapper); 155 return true; 156 } 157 158 template <typename DOMClass> inline JSDOMWrapper* getCachedWrapper(DOMWrapperWorld* world, DOMClass* domObject) 159 { 160 if (JSDOMWrapper* wrapper = getInlineCachedWrapper(world, domObject)) 161 return wrapper; 162 return world->m_wrappers.get(domObject); 163 } 164 165 template <typename DOMClass> inline void cacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, JSDOMWrapper* wrapper) 166 { 167 JSC::WeakHandleOwner* owner = wrapperOwner(world, domObject); 168 void* context = wrapperContext(world, domObject); 169 if (setInlineCachedWrapper(world, domObject, wrapper, owner, context)) 170 return; 171 JSC::PassWeak<JSDOMWrapper> passWeak(wrapper, owner, context); 172 weakAdd(world->m_wrappers, (void*)domObject, passWeak); 173 } 174 175 template <typename DOMClass> inline void uncacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, JSDOMWrapper* wrapper) 176 { 177 if (clearInlineCachedWrapper(world, domObject, wrapper)) 178 return; 179 weakRemove(world->m_wrappers, (void*)domObject, wrapper); 180 } 181 182 #define CREATE_DOM_WRAPPER(exec, globalObject, className, object) createWrapper<JS##className>(exec, globalObject, static_cast<className*>(object)) 183 template<class WrapperClass, class DOMClass> inline JSDOMWrapper* createWrapper(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* node) 184 { 185 ASSERT(node); 186 ASSERT(!getCachedWrapper(currentWorld(exec), node)); 187 WrapperClass* wrapper = WrapperClass::create(getDOMStructure<WrapperClass>(exec, globalObject), globalObject, node); 188 // FIXME: The entire function can be removed, once we fix caching. 189 // This function is a one-off hack to make Nodes cache in the right global object. 190 cacheWrapper(currentWorld(exec), node, wrapper); 191 return wrapper; 192 } 193 194 template<class WrapperClass, class DOMClass> inline JSC::JSValue wrap(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* domObject) 195 { 196 if (!domObject) 197 return JSC::jsNull(); 198 if (JSDOMWrapper* wrapper = getCachedWrapper(currentWorld(exec), domObject)) 199 return wrapper; 200 return createWrapper<WrapperClass>(exec, globalObject, domObject); 201 } 202 203 template<class WrapperClass, class DOMClass> inline JSC::JSValue getExistingWrapper(JSC::ExecState* exec, DOMClass* domObject) 204 { 205 ASSERT(domObject); 206 return getCachedWrapper(currentWorld(exec), domObject); 207 } 208 209 template<class WrapperClass, class DOMClass> inline JSC::JSValue createNewWrapper(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* domObject) 210 { 211 ASSERT(domObject); 212 ASSERT(!getCachedWrapper(currentWorld(exec), domObject)); 213 return createWrapper<WrapperClass>(exec, globalObject, domObject); 214 } 215 216 inline JSC::JSValue argumentOrNull(JSC::ExecState* exec, unsigned index) 217 { 218 return index >= exec->argumentCount() ? JSC::JSValue() : exec->argument(index); 219 } 220 221 const JSC::HashTable* getHashTableForGlobalData(JSC::VM&, const JSC::HashTable* staticTable); 222 223 void reportException(JSC::ExecState*, JSC::JSValue exception, CachedScript* = 0); 224 void reportCurrentException(JSC::ExecState*); 225 226 // Convert a DOM implementation exception code into a JavaScript exception in the execution state. 227 void setDOMException(JSC::ExecState*, ExceptionCode); 228 229 JSC::JSValue jsStringWithCache(JSC::ExecState*, const String&); 230 JSC::JSValue jsString(JSC::ExecState*, const KURL&); // empty if the URL is null 231 inline JSC::JSValue jsStringWithCache(JSC::ExecState* exec, const AtomicString& s) 232 { 233 return jsStringWithCache(exec, s.string()); 234 } 235 236 JSC::JSValue jsStringOrNull(JSC::ExecState*, const String&); // null if the string is null 237 JSC::JSValue jsStringOrNull(JSC::ExecState*, const KURL&); // null if the URL is null 238 239 JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const String&); // undefined if the string is null 240 JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const KURL&); // undefined if the URL is null 241 242 // See JavaScriptCore for explanation: Should be used for any string that is already owned by another 243 // object, to let the engine know that collecting the JSString wrapper is unlikely to save memory. 244 JSC::JSValue jsOwnedStringOrNull(JSC::ExecState*, const String&); 245 246 String propertyNameToString(JSC::PropertyName); 247 248 AtomicString propertyNameToAtomicString(JSC::PropertyName); 249 AtomicStringImpl* findAtomicString(JSC::PropertyName); 250 251 String valueToStringWithNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null 252 String valueToStringWithUndefinedOrNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null or undefined 253 254 inline int32_t finiteInt32Value(JSC::JSValue value, JSC::ExecState* exec, bool& okay) 255 { 256 double number = value.toNumber(exec); 257 okay = std::isfinite(number); 258 return JSC::toInt32(number); 259 } 260 261 enum IntegerConversionConfiguration { 262 NormalConversion, 263 EnforceRange, 264 // FIXME: Implement Clamp 265 }; 266 267 int32_t toInt32EnforceRange(JSC::ExecState*, JSC::JSValue); 268 uint32_t toUInt32EnforceRange(JSC::ExecState*, JSC::JSValue); 269 270 int8_t toInt8(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration); 271 uint8_t toUInt8(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration); 272 273 /* 274 Convert a value to an integer as per <http://www.w3.org/TR/WebIDL/>. 275 The conversion fails if the value cannot be converted to a number or, 276 if EnforceRange is specified, the value is outside the range of the 277 destination integer type. 278 */ 279 inline int32_t toInt32(JSC::ExecState* exec, JSC::JSValue value, IntegerConversionConfiguration configuration) 280 { 281 if (configuration == EnforceRange) 282 return toInt32EnforceRange(exec, value); 283 return value.toInt32(exec); 284 } 285 286 inline uint32_t toUInt32(JSC::ExecState* exec, JSC::JSValue value, IntegerConversionConfiguration configuration) 287 { 288 if (configuration == EnforceRange) 289 return toUInt32EnforceRange(exec, value); 290 return value.toUInt32(exec); 291 } 292 293 int64_t toInt64(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration); 294 uint64_t toUInt64(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration); 295 296 // Returns a Date instance for the specified value, or null if the value is NaN or infinity. 297 JSC::JSValue jsDateOrNull(JSC::ExecState*, double); 298 // NaN if the value can't be converted to a date. 299 double valueToDate(JSC::ExecState*, JSC::JSValue); 300 301 // Validates that the passed object is a sequence type per section 4.1.13 of the WebIDL spec. 302 inline JSC::JSObject* toJSSequence(JSC::ExecState* exec, JSC::JSValue value, unsigned& length) 303 { 304 JSC::JSObject* object = value.getObject(); 305 if (!object) { 306 throwTypeError(exec); 307 return 0; 308 } 309 310 JSC::JSValue lengthValue = object->get(exec, exec->propertyNames().length); 311 if (exec->hadException()) 312 return 0; 313 314 if (lengthValue.isUndefinedOrNull()) { 315 throwTypeError(exec); 316 return 0; 317 } 318 319 length = lengthValue.toUInt32(exec); 320 if (exec->hadException()) 321 return 0; 322 323 return object; 324 } 325 326 template <typename T> 327 inline JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<T> ptr) 328 { 329 return toJS(exec, globalObject, ptr.get()); 330 } 331 332 template <class T> 333 struct JSValueTraits { 334 static inline JSC::JSValue arrayJSValue(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const T& value) 335 { 336 return toJS(exec, globalObject, WTF::getPtr(value)); 337 } 338 }; 339 340 template<> 341 struct JSValueTraits<String> { 342 static inline JSC::JSValue arrayJSValue(JSC::ExecState* exec, JSDOMGlobalObject*, const String& value) 343 { 344 return jsStringWithCache(exec, value); 345 } 346 }; 347 348 template<> 349 struct JSValueTraits<float> { 350 static inline JSC::JSValue arrayJSValue(JSC::ExecState*, JSDOMGlobalObject*, const float& value) 351 { 352 return JSC::jsNumber(value); 353 } 354 }; 355 356 template<> 357 struct JSValueTraits<unsigned long> { 358 static inline JSC::JSValue arrayJSValue(JSC::ExecState*, JSDOMGlobalObject*, const unsigned long& value) 359 { 360 return JSC::jsNumber(value); 361 } 362 }; 363 364 template <typename T, size_t inlineCapacity> 365 JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Vector<T, inlineCapacity>& iterator) 366 { 367 JSC::MarkedArgumentBuffer list; 368 typename Vector<T, inlineCapacity>::const_iterator end = iterator.end(); 369 typedef JSValueTraits<T> TraitsType; 370 371 for (typename Vector<T, inlineCapacity>::const_iterator iter = iterator.begin(); iter != end; ++iter) 372 list.append(TraitsType::arrayJSValue(exec, globalObject, *iter)); 373 374 return JSC::constructArray(exec, 0, globalObject, list); 375 } 376 377 JSC::JSValue jsArray(JSC::ExecState*, JSDOMGlobalObject*, PassRefPtr<DOMStringList>); 378 379 template<class T> struct NativeValueTraits; 380 381 template<> 382 struct NativeValueTraits<String> { 383 static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, String& indexedValue) 384 { 385 indexedValue = jsValue.toString(exec)->value(exec); 386 return true; 387 } 388 }; 389 390 template<> 391 struct NativeValueTraits<unsigned> { 392 static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, unsigned& indexedValue) 393 { 394 if (!jsValue.isNumber()) 395 return false; 396 397 indexedValue = jsValue.toUInt32(exec); 398 if (exec->hadException()) 399 return false; 400 401 return true; 402 } 403 }; 404 405 template<> 406 struct NativeValueTraits<float> { 407 static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, float& indexedValue) 408 { 409 indexedValue = jsValue.toFloat(exec); 410 return !exec->hadException(); 411 } 412 }; 413 414 template <class T, class JST> 415 Vector<RefPtr<T> > toRefPtrNativeArray(JSC::ExecState* exec, JSC::JSValue value, T* (*toT)(JSC::JSValue value)) 416 { 417 if (!isJSArray(value)) 418 return Vector<RefPtr<T> >(); 419 420 Vector<RefPtr<T> > result; 421 JSC::JSArray* array = asArray(value); 422 for (size_t i = 0; i < array->length(); ++i) { 423 JSC::JSValue element = array->getIndex(exec, i); 424 if (element.inherits(&JST::s_info)) 425 result.append((*toT)(element)); 426 else { 427 throwVMError(exec, createTypeError(exec, "Invalid Array element type")); 428 return Vector<RefPtr<T> >(); 429 } 430 } 431 return result; 432 } 433 434 template <class T> 435 Vector<T> toNativeArray(JSC::ExecState* exec, JSC::JSValue value) 436 { 437 unsigned length = 0; 438 if (isJSArray(value)) { 439 JSC::JSArray* array = asArray(value); 440 length = array->length(); 441 } else 442 toJSSequence(exec, value, length); 443 444 JSC::JSObject* object = value.getObject(); 445 Vector<T> result; 446 typedef NativeValueTraits<T> TraitsType; 447 448 for (unsigned i = 0; i < length; ++i) { 449 T indexValue; 450 if (!TraitsType::nativeValue(exec, object->get(exec, i), indexValue)) 451 return Vector<T>(); 452 result.append(indexValue); 453 } 454 return result; 455 } 456 457 template <class T> 458 Vector<T> toNativeArguments(JSC::ExecState* exec, size_t startIndex = 0) 459 { 460 size_t length = exec->argumentCount(); 461 ASSERT(startIndex <= length); 462 463 Vector<T> result; 464 typedef NativeValueTraits<T> TraitsType; 465 466 for (size_t i = startIndex; i < length; ++i) { 467 T indexValue; 468 if (!TraitsType::nativeValue(exec, exec->argument(i), indexValue)) 469 return Vector<T>(); 470 result.append(indexValue); 471 } 472 return result; 473 } 474 475 bool shouldAllowAccessToNode(JSC::ExecState*, Node*); 476 bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*); 477 bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*, String& message); 478 bool shouldAllowAccessToDOMWindow(BindingState*, DOMWindow*, String& message); 479 480 void printErrorMessageForFrame(Frame*, const String& message); 481 JSC::JSValue objectToStringFunctionGetter(JSC::ExecState*, JSC::JSValue, JSC::PropertyName); 482 483 inline JSC::JSValue jsStringWithCache(JSC::ExecState* exec, const String& s) 484 { 485 StringImpl* stringImpl = s.impl(); 486 if (!stringImpl || !stringImpl->length()) 487 return jsEmptyString(exec); 488 489 if (stringImpl->length() == 1) { 490 UChar singleCharacter = (*stringImpl)[0u]; 491 if (singleCharacter <= JSC::maxSingleCharacterString) { 492 JSC::VM* vm = &exec->vm(); 493 return vm->smallStrings.singleCharacterString(vm, static_cast<unsigned char>(singleCharacter)); 494 } 495 } 496 497 JSStringCache& stringCache = currentWorld(exec)->m_stringCache; 498 JSStringCache::AddResult addResult = stringCache.add(stringImpl, nullptr); 499 if (addResult.isNewEntry) 500 addResult.iterator->value = JSC::jsString(exec, String(stringImpl)); 501 return JSC::JSValue(addResult.iterator->value.get()); 502 } 503 504 inline String propertyNameToString(JSC::PropertyName propertyName) 505 { 506 return propertyName.publicName(); 507 } 508 509 inline AtomicString propertyNameToAtomicString(JSC::PropertyName propertyName) 510 { 511 return AtomicString(propertyName.publicName()); 512 } 513 514 template <class ThisImp> 515 inline const JSC::HashEntry* getStaticValueSlotEntryWithoutCaching(JSC::ExecState* exec, JSC::PropertyName propertyName) 516 { 517 const JSC::HashEntry* entry = ThisImp::s_info.propHashTable(exec)->entry(exec, propertyName); 518 if (!entry) // not found, forward to parent 519 return getStaticValueSlotEntryWithoutCaching<typename ThisImp::Base>(exec, propertyName); 520 return entry; 521 } 522 523 template <> 524 inline const JSC::HashEntry* getStaticValueSlotEntryWithoutCaching<JSDOMWrapper>(JSC::ExecState*, JSC::PropertyName) 525 { 526 return 0; 527 } 528 529 template<typename T> 530 class HasMemoryCostMemberFunction { 531 typedef char YesType; 532 struct NoType { 533 char padding[8]; 534 }; 535 536 struct BaseMixin { 537 size_t memoryCost(); 538 }; 539 540 struct Base : public T, public BaseMixin { }; 541 542 template<typename U, U> struct 543 TypeChecker { }; 544 545 template<typename U> 546 static NoType dummy(U*, TypeChecker<size_t (BaseMixin::*)(), &U::memoryCost>* = 0); 547 static YesType dummy(...); 548 549 public: 550 static const bool value = sizeof(dummy(static_cast<Base*>(0))) == sizeof(YesType); 551 }; 552 template <typename T, bool hasReportCostFunction = HasMemoryCostMemberFunction<T>::value > struct ReportMemoryCost; 553 template <typename T> struct ReportMemoryCost<T, true> { 554 static void reportMemoryCost(JSC::ExecState* exec, T* impl) 555 { 556 exec->heap()->reportExtraMemoryCost(impl->memoryCost()); 557 } 558 }; 559 template <typename T> struct ReportMemoryCost<T, false> { 560 static void reportMemoryCost(JSC::ExecState*, T*) 561 { 562 } 563 }; 564 565} // namespace WebCore 566 567#endif // JSDOMBinding_h 568