2011-04-18 Geoffrey Garen <ggaren@apple.com>
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMBinding.h
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:
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