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