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