1 /* 2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) 3 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. 4 * Copyright (C) 2007 Samuel Weinig <sam@webkit.org> 5 * Copyright (C) 2009 Google, Inc. All rights reserved. 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 #ifndef JSDOMBinding_h 23 #define JSDOMBinding_h 24 25 #include "JSDOMGlobalObject.h" 26 #include "JSDOMWrapper.h" 27 #include "DOMWrapperWorld.h" 28 #include "Document.h" 29 #include <heap/Weak.h> 30 #include <runtime/Completion.h> 31 #include <runtime/Lookup.h> 32 #include <wtf/Forward.h> 33 #include <wtf/Noncopyable.h> 34 35 namespace JSC { 36 class JSGlobalData; 37 class DebuggerCallFrame; 38 } 39 40 namespace WebCore { 41 42 class Document; 43 class Frame; 44 class JSNode; 45 class KURL; 46 class Node; 47 class ScriptController; 48 class ScriptCachedFrameData; 49 50 typedef int ExceptionCode; 51 52 // FIXME: This class should collapse into JSDOMWrapper once all JSDOMWrappers are 53 // updated to store a globalObject pointer. 54 class JSDOMWrapperWithGlobalPointer : public JSDOMWrapper { 55 public: globalObject()56 JSDOMGlobalObject* globalObject() const 57 { 58 return static_cast<JSDOMGlobalObject*>(JSDOMWrapper::globalObject()); 59 } 60 scriptExecutionContext()61 ScriptExecutionContext* scriptExecutionContext() const 62 { 63 // FIXME: Should never be 0, but can be due to bug 27640. 64 return globalObject()->scriptExecutionContext(); 65 } 66 createStructure(JSC::JSGlobalData & globalData,JSC::JSValue prototype)67 static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSValue prototype) 68 { 69 return JSC::Structure::create(globalData, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), AnonymousSlotCount, &s_info); 70 } 71 72 protected: JSDOMWrapperWithGlobalPointer(JSC::Structure * structure,JSDOMGlobalObject * globalObject)73 JSDOMWrapperWithGlobalPointer(JSC::Structure* structure, JSDOMGlobalObject* globalObject) 74 : JSDOMWrapper(globalObject, structure) 75 { 76 // FIXME: This ASSERT is valid, but fires in fast/dom/gc-6.html when trying to create 77 // new JavaScript objects on detached windows due to DOMWindow::document() 78 // needing to reach through the frame to get to the Document*. See bug 27640. 79 // ASSERT(globalObject->scriptExecutionContext()); 80 } 81 }; 82 83 // Base class for all constructor objects in the JSC bindings. 84 class DOMConstructorObject : public JSDOMWrapperWithGlobalPointer { 85 public: createStructure(JSC::JSGlobalData & globalData,JSC::JSValue prototype)86 static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSValue prototype) 87 { 88 return JSC::Structure::create(globalData, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), AnonymousSlotCount, &s_info); 89 } 90 91 protected: 92 static const unsigned StructureFlags = JSC::ImplementsHasInstance | JSC::OverridesMarkChildren | JSDOMWrapperWithGlobalPointer::StructureFlags; DOMConstructorObject(JSC::Structure * structure,JSDOMGlobalObject * globalObject)93 DOMConstructorObject(JSC::Structure* structure, JSDOMGlobalObject* globalObject) 94 : JSDOMWrapperWithGlobalPointer(structure, globalObject) 95 { 96 } 97 }; 98 99 // Constructors using this base class depend on being in a Document and 100 // can never be used from a WorkerContext. 101 class DOMConstructorWithDocument : public DOMConstructorObject { 102 public: document()103 Document* document() const 104 { 105 return static_cast<Document*>(scriptExecutionContext()); 106 } 107 108 protected: DOMConstructorWithDocument(JSC::Structure * structure,JSDOMGlobalObject * globalObject)109 DOMConstructorWithDocument(JSC::Structure* structure, JSDOMGlobalObject* globalObject) 110 : DOMConstructorObject(structure, globalObject) 111 { 112 ASSERT(globalObject->scriptExecutionContext()->isDocument()); 113 } 114 }; 115 116 void markActiveObjectsForContext(JSC::MarkStack&, JSC::JSGlobalData&, ScriptExecutionContext*); 117 void markDOMObjectWrapper(JSC::MarkStack&, JSC::JSGlobalData& globalData, void* object); 118 119 JSC::Structure* getCachedDOMStructure(JSDOMGlobalObject*, const JSC::ClassInfo*); 120 JSC::Structure* cacheDOMStructure(JSDOMGlobalObject*, JSC::Structure*, const JSC::ClassInfo*); 121 deprecatedGlobalObjectForPrototype(JSC::ExecState * exec)122 inline JSDOMGlobalObject* deprecatedGlobalObjectForPrototype(JSC::ExecState* exec) 123 { 124 // FIXME: Callers to this function should be using the global object 125 // from which the object is being created, instead of assuming the lexical one. 126 // e.g. subframe.document.body should use the subframe's global object, not the lexical one. 127 return static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()); 128 } 129 getDOMStructure(JSC::ExecState * exec,JSDOMGlobalObject * globalObject)130 template<class WrapperClass> inline JSC::Structure* getDOMStructure(JSC::ExecState* exec, JSDOMGlobalObject* globalObject) 131 { 132 if (JSC::Structure* structure = getCachedDOMStructure(globalObject, &WrapperClass::s_info)) 133 return structure; 134 return cacheDOMStructure(globalObject, WrapperClass::createStructure(exec->globalData(), WrapperClass::createPrototype(exec, globalObject)), &WrapperClass::s_info); 135 } 136 deprecatedGetDOMStructure(JSC::ExecState * exec)137 template<class WrapperClass> inline JSC::Structure* deprecatedGetDOMStructure(JSC::ExecState* exec) 138 { 139 // FIXME: This function is wrong. It uses the wrong global object for creating the prototype structure. 140 return getDOMStructure<WrapperClass>(exec, deprecatedGlobalObjectForPrototype(exec)); 141 } 142 getDOMPrototype(JSC::ExecState * exec,JSC::JSGlobalObject * globalObject)143 template<class WrapperClass> inline JSC::JSObject* getDOMPrototype(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject) 144 { 145 return static_cast<JSC::JSObject*>(asObject(getDOMStructure<WrapperClass>(exec, static_cast<JSDOMGlobalObject*>(globalObject))->storedPrototype())); 146 } 147 148 // Overload these functions to provide a fast path for wrapper access. getInlineCachedWrapper(DOMWrapperWorld *,void *)149 inline JSDOMWrapper* getInlineCachedWrapper(DOMWrapperWorld*, void*) { return 0; } setInlineCachedWrapper(DOMWrapperWorld *,void *,JSDOMWrapper *)150 inline bool setInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; } clearInlineCachedWrapper(DOMWrapperWorld *,void *,JSDOMWrapper *)151 inline bool clearInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; } 152 153 // Overload these functions to provide a custom WeakHandleOwner. wrapperOwner(DOMWrapperWorld * world,void *)154 inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld* world, void*) { return world->defaultWrapperOwner(); } wrapperContext(DOMWrapperWorld *,void * domObject)155 inline void* wrapperContext(DOMWrapperWorld*, void* domObject) { return domObject; } 156 getCachedWrapper(DOMWrapperWorld * world,DOMClass * domObject)157 template <typename DOMClass> inline JSDOMWrapper* getCachedWrapper(DOMWrapperWorld* world, DOMClass* domObject) 158 { 159 if (JSDOMWrapper* wrapper = getInlineCachedWrapper(world, domObject)) 160 return wrapper; 161 return world->m_wrappers.get(domObject).get(); 162 } 163 cacheWrapper(DOMWrapperWorld * world,DOMClass * domObject,JSDOMWrapper * wrapper)164 template <typename DOMClass> inline void cacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, JSDOMWrapper* wrapper) 165 { 166 if (setInlineCachedWrapper(world, domObject, wrapper)) 167 return; 168 ASSERT(!world->m_wrappers.contains(domObject)); 169 world->m_wrappers.set(domObject, JSC::Weak<JSDOMWrapper>(*world->globalData(), wrapper, wrapperOwner(world, domObject), wrapperContext(world, domObject))); 170 } 171 uncacheWrapper(DOMWrapperWorld * world,DOMClass * domObject,JSDOMWrapper * wrapper)172 template <typename DOMClass> inline void uncacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, JSDOMWrapper* wrapper) 173 { 174 if (clearInlineCachedWrapper(world, domObject, wrapper)) 175 return; 176 ASSERT(world->m_wrappers.find(domObject)->second.get() == wrapper); 177 world->m_wrappers.remove(domObject); 178 } 179 180 #define CREATE_DOM_OBJECT_WRAPPER(exec, globalObject, className, object) createWrapper<JS##className>(exec, globalObject, static_cast<className*>(object)) 181 #define CREATE_DOM_NODE_WRAPPER(exec, globalObject, className, object) static_cast<JSNode*>(createWrapper<JS##className>(exec, globalObject, static_cast<className*>(object))) createWrapper(JSC::ExecState * exec,JSDOMGlobalObject * globalObject,DOMClass * node)182 template<class WrapperClass, class DOMClass> inline JSDOMWrapper* createWrapper(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* node) 183 { 184 ASSERT(node); 185 ASSERT(!getCachedWrapper(currentWorld(exec), node)); 186 WrapperClass* wrapper = new (exec) WrapperClass(getDOMStructure<WrapperClass>(exec, globalObject), globalObject, node); 187 // FIXME: The entire function can be removed, once we fix caching. 188 // This function is a one-off hack to make Nodes cache in the right global object. 189 cacheWrapper(currentWorld(exec), node, wrapper); 190 return wrapper; 191 } 192 wrap(JSC::ExecState * exec,JSDOMGlobalObject * globalObject,DOMClass * domObject)193 template<class WrapperClass, class DOMClass> inline JSC::JSValue wrap(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* domObject) 194 { 195 if (!domObject) 196 return JSC::jsNull(); 197 if (JSDOMWrapper* wrapper = getCachedWrapper(currentWorld(exec), domObject)) 198 return wrapper; 199 return createWrapper<WrapperClass>(exec, globalObject, domObject); 200 } 201 202 const JSC::HashTable* getHashTableForGlobalData(JSC::JSGlobalData&, const JSC::HashTable* staticTable); 203 204 void reportException(JSC::ExecState*, JSC::JSValue exception); 205 void reportCurrentException(JSC::ExecState*); 206 207 // Convert a DOM implementation exception code into a JavaScript exception in the execution state. 208 void setDOMException(JSC::ExecState*, ExceptionCode); 209 210 JSC::JSValue jsString(JSC::ExecState*, const String&); // empty if the string is null 211 JSC::JSValue jsStringSlowCase(JSC::ExecState*, JSStringCache&, StringImpl*); 212 JSC::JSValue jsString(JSC::ExecState*, const KURL&); // empty if the URL is null jsString(JSC::ExecState * exec,const AtomicString & s)213 inline JSC::JSValue jsString(JSC::ExecState* exec, const AtomicString& s) 214 { 215 return jsString(exec, s.string()); 216 } 217 218 JSC::JSValue jsStringOrNull(JSC::ExecState*, const String&); // null if the string is null 219 JSC::JSValue jsStringOrNull(JSC::ExecState*, const KURL&); // null if the URL is null 220 221 JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const String&); // undefined if the string is null 222 JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const KURL&); // undefined if the URL is null 223 224 JSC::JSValue jsStringOrFalse(JSC::ExecState*, const String&); // boolean false if the string is null 225 JSC::JSValue jsStringOrFalse(JSC::ExecState*, const KURL&); // boolean false if the URL is null 226 227 // See JavaScriptCore for explanation: Should be used for any string that is already owned by another 228 // object, to let the engine know that collecting the JSString wrapper is unlikely to save memory. 229 JSC::JSValue jsOwnedStringOrNull(JSC::ExecState*, const String&); 230 231 String identifierToString(const JSC::Identifier&); 232 String ustringToString(const JSC::UString&); 233 JSC::UString stringToUString(const String&); 234 235 AtomicString identifierToAtomicString(const JSC::Identifier&); 236 AtomicString ustringToAtomicString(const JSC::UString&); 237 AtomicStringImpl* findAtomicString(const JSC::Identifier&); 238 239 String valueToStringWithNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null 240 String valueToStringWithUndefinedOrNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null or undefined 241 finiteInt32Value(JSC::JSValue value,JSC::ExecState * exec,bool & okay)242 inline int32_t finiteInt32Value(JSC::JSValue value, JSC::ExecState* exec, bool& okay) 243 { 244 double number = value.toNumber(exec); 245 okay = isfinite(number); 246 return JSC::toInt32(number); 247 } 248 249 // Returns a Date instance for the specified value, or null if the value is NaN or infinity. 250 JSC::JSValue jsDateOrNull(JSC::ExecState*, double); 251 // NaN if the value can't be converted to a date. 252 double valueToDate(JSC::ExecState*, JSC::JSValue); 253 254 // FIXME: These are a stop-gap until all toJS calls can be converted to pass a globalObject 255 template <typename T> toJS(JSC::ExecState * exec,T * ptr)256 inline JSC::JSValue toJS(JSC::ExecState* exec, T* ptr) 257 { 258 return toJS(exec, deprecatedGlobalObjectForPrototype(exec), ptr); 259 } 260 template <typename T> toJS(JSC::ExecState * exec,PassRefPtr<T> ptr)261 inline JSC::JSValue toJS(JSC::ExecState* exec, PassRefPtr<T> ptr) 262 { 263 return toJS(exec, deprecatedGlobalObjectForPrototype(exec), ptr.get()); 264 } 265 template <typename T> toJSNewlyCreated(JSC::ExecState * exec,T * ptr)266 inline JSC::JSValue toJSNewlyCreated(JSC::ExecState* exec, T* ptr) 267 { 268 return toJSNewlyCreated(exec, deprecatedGlobalObjectForPrototype(exec), ptr); 269 } 270 271 template <typename T> toJS(JSC::ExecState * exec,JSDOMGlobalObject * globalObject,PassRefPtr<T> ptr)272 inline JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<T> ptr) 273 { 274 return toJS(exec, globalObject, ptr.get()); 275 } 276 277 // Validates that the passed object is a sequence type per section 4.1.13 of the WebIDL spec. 278 JSC::JSObject* toJSSequence(JSC::ExecState*, JSC::JSValue, unsigned&); 279 280 bool checkNodeSecurity(JSC::ExecState*, Node*); 281 282 // Helpers for Window, History, and Location classes to implement cross-domain policy. 283 // Besides the cross-domain check, they need non-caching versions of staticFunctionGetter for 284 // because we do not want current property values involved at all. 285 // FIXME: These functions should be named frameAllowsAccessFrom, because the access is *to* the frame. 286 bool allowsAccessFromFrame(JSC::ExecState*, Frame*); 287 bool allowsAccessFromFrame(JSC::ExecState*, Frame*, String& message); 288 DOMWindow* activeDOMWindow(JSC::ExecState*); 289 DOMWindow* firstDOMWindow(JSC::ExecState*); 290 291 void printErrorMessageForFrame(Frame*, const String& message); 292 JSC::JSValue objectToStringFunctionGetter(JSC::ExecState*, JSC::JSValue, const JSC::Identifier& propertyName); 293 294 Frame* toDynamicFrame(JSC::ExecState*); 295 bool processingUserGesture(); 296 jsString(JSC::ExecState * exec,const String & s)297 inline JSC::JSValue jsString(JSC::ExecState* exec, const String& s) 298 { 299 StringImpl* stringImpl = s.impl(); 300 if (!stringImpl || !stringImpl->length()) 301 return jsEmptyString(exec); 302 303 if (stringImpl->length() == 1 && stringImpl->characters()[0] <= 0xFF) 304 return jsString(exec, stringToUString(s)); 305 306 JSStringCache& stringCache = currentWorld(exec)->m_stringCache; 307 if (JSC::JSString* wrapper = stringCache.get(stringImpl)) 308 return wrapper; 309 310 return jsStringSlowCase(exec, stringCache, stringImpl); 311 } 312 domObjectWrapperMapFor(JSC::ExecState * exec)313 inline DOMObjectWrapperMap& domObjectWrapperMapFor(JSC::ExecState* exec) 314 { 315 return currentWorld(exec)->m_wrappers; 316 } 317 ustringToString(const JSC::UString & u)318 inline String ustringToString(const JSC::UString& u) 319 { 320 return u.impl(); 321 } 322 stringToUString(const String & s)323 inline JSC::UString stringToUString(const String& s) 324 { 325 return JSC::UString(s.impl()); 326 } 327 identifierToString(const JSC::Identifier & i)328 inline String identifierToString(const JSC::Identifier& i) 329 { 330 return i.impl(); 331 } 332 ustringToAtomicString(const JSC::UString & u)333 inline AtomicString ustringToAtomicString(const JSC::UString& u) 334 { 335 return AtomicString(u.impl()); 336 } 337 identifierToAtomicString(const JSC::Identifier & identifier)338 inline AtomicString identifierToAtomicString(const JSC::Identifier& identifier) 339 { 340 return AtomicString(identifier.impl()); 341 } 342 343 } // namespace WebCore 344 345 #endif // JSDOMBinding_h 346