Home | History | Annotate | Download | only in js
      1 /*
      2  *  Copyright (C) 1999-2001 Harri Porten (porten (at) kde.org)
      3  *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
      4  *  Copyright (C) 2007 Samuel Weinig <sam (at) 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:
     56         JSDOMGlobalObject* globalObject() const
     57         {
     58             return static_cast<JSDOMGlobalObject*>(JSDOMWrapper::globalObject());
     59         }
     60 
     61         ScriptExecutionContext* scriptExecutionContext() const
     62         {
     63             // FIXME: Should never be 0, but can be due to bug 27640.
     64             return globalObject()->scriptExecutionContext();
     65         }
     66 
     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:
     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:
     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;
     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:
    103         Document* document() const
    104         {
    105             return static_cast<Document*>(scriptExecutionContext());
    106         }
    107 
    108     protected:
    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 
    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 
    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 
    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 
    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.
    149     inline JSDOMWrapper* getInlineCachedWrapper(DOMWrapperWorld*, void*) { return 0; }
    150     inline bool setInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; }
    151     inline bool clearInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; }
    152 
    153     // Overload these functions to provide a custom WeakHandleOwner.
    154     inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld* world, void*) { return world->defaultWrapperOwner(); }
    155     inline void* wrapperContext(DOMWrapperWorld*, void* domObject) { return domObject; }
    156 
    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 
    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 
    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)))
    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 
    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
    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 
    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>
    256     inline JSC::JSValue toJS(JSC::ExecState* exec, T* ptr)
    257     {
    258         return toJS(exec, deprecatedGlobalObjectForPrototype(exec), ptr);
    259     }
    260     template <typename T>
    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>
    266     inline JSC::JSValue toJSNewlyCreated(JSC::ExecState* exec, T* ptr)
    267     {
    268         return toJSNewlyCreated(exec, deprecatedGlobalObjectForPrototype(exec), ptr);
    269     }
    270 
    271     template <typename T>
    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 
    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 
    313     inline DOMObjectWrapperMap& domObjectWrapperMapFor(JSC::ExecState* exec)
    314     {
    315         return currentWorld(exec)->m_wrappers;
    316     }
    317 
    318     inline String ustringToString(const JSC::UString& u)
    319     {
    320         return u.impl();
    321     }
    322 
    323     inline JSC::UString stringToUString(const String& s)
    324     {
    325         return JSC::UString(s.impl());
    326     }
    327 
    328     inline String identifierToString(const JSC::Identifier& i)
    329     {
    330         return i.impl();
    331     }
    332 
    333     inline AtomicString ustringToAtomicString(const JSC::UString& u)
    334     {
    335         return AtomicString(u.impl());
    336     }
    337 
    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