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