20a450be827aa6485e76dc360f46c7cb76cb3819
[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 "CSSImportRule.h"
26 #include "CSSStyleDeclaration.h"
27 #include "CSSStyleSheet.h"
28 #include "JSDOMGlobalObject.h"
29 #include "JSDOMWrapper.h"
30 #include "DOMWrapperWorld.h"
31 #include "Document.h"
32 #include "Element.h"
33 #include "MediaList.h"
34 #include "StylePropertySet.h"
35 #include "StyledElement.h"
36 #include <heap/Weak.h>
37 #include <runtime/FunctionPrototype.h>
38 #include <runtime/JSArray.h>
39 #include <runtime/Lookup.h>
40 #include <runtime/ObjectPrototype.h>
41 #include <wtf/Forward.h>
42 #include <wtf/Noncopyable.h>
43 #include <wtf/Vector.h>
44
45 namespace WebCore {
46
47 enum ParameterDefaultPolicy {
48     DefaultIsUndefined,
49     DefaultIsNullString
50 };
51
52 #define MAYBE_MISSING_PARAMETER(exec, index, policy) (((policy) == DefaultIsNullString && (index) >= (exec)->argumentCount()) ? (JSValue()) : ((exec)->argument(index)))
53
54     class Frame;
55     class KURL;
56
57     typedef int ExceptionCode;
58
59     // Base class for all constructor objects in the JSC bindings.
60     class DOMConstructorObject : public JSDOMWrapper {
61         typedef JSDOMWrapper Base;
62     public:
63         static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
64         {
65             return JSC::Structure::create(globalData, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), &s_info);
66         }
67
68     protected:
69         static const unsigned StructureFlags = JSC::ImplementsHasInstance | JSC::OverridesVisitChildren | JSDOMWrapper::StructureFlags;
70         DOMConstructorObject(JSC::Structure* structure, JSDOMGlobalObject* globalObject)
71             : JSDOMWrapper(structure, globalObject)
72         {
73         }
74     };
75
76     // Constructors using this base class depend on being in a Document and
77     // can never be used from a WorkerContext.
78     class DOMConstructorWithDocument : public DOMConstructorObject {
79         typedef DOMConstructorObject Base;
80     public:
81         Document* document() const
82         {
83             return static_cast<Document*>(scriptExecutionContext());
84         }
85
86     protected:
87         DOMConstructorWithDocument(JSC::Structure* structure, JSDOMGlobalObject* globalObject)
88             : DOMConstructorObject(structure, globalObject)
89         {
90         }
91
92         void finishCreation(JSDOMGlobalObject* globalObject)
93         {
94             Base::finishCreation(globalObject->globalData());
95             ASSERT(globalObject->scriptExecutionContext()->isDocument());
96         }
97     };
98     
99     JSC::Structure* getCachedDOMStructure(JSDOMGlobalObject*, const JSC::ClassInfo*);
100     JSC::Structure* cacheDOMStructure(JSDOMGlobalObject*, JSC::Structure*, const JSC::ClassInfo*);
101
102     inline JSDOMGlobalObject* deprecatedGlobalObjectForPrototype(JSC::ExecState* exec)
103     {
104         // FIXME: Callers to this function should be using the global object
105         // from which the object is being created, instead of assuming the lexical one.
106         // e.g. subframe.document.body should use the subframe's global object, not the lexical one.
107         return static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
108     }
109
110     template<class WrapperClass> inline JSC::Structure* getDOMStructure(JSC::ExecState* exec, JSDOMGlobalObject* globalObject)
111     {
112         if (JSC::Structure* structure = getCachedDOMStructure(globalObject, &WrapperClass::s_info))
113             return structure;
114         return cacheDOMStructure(globalObject, WrapperClass::createStructure(exec->globalData(), globalObject, WrapperClass::createPrototype(exec, globalObject)), &WrapperClass::s_info);
115     }
116
117     template<class WrapperClass> inline JSC::Structure* deprecatedGetDOMStructure(JSC::ExecState* exec)
118     {
119         // FIXME: This function is wrong.  It uses the wrong global object for creating the prototype structure.
120         return getDOMStructure<WrapperClass>(exec, deprecatedGlobalObjectForPrototype(exec));
121     }
122
123     template<class WrapperClass> inline JSC::JSObject* getDOMPrototype(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject)
124     {
125         return static_cast<JSC::JSObject*>(asObject(getDOMStructure<WrapperClass>(exec, static_cast<JSDOMGlobalObject*>(globalObject))->storedPrototype()));
126     }
127
128     // Overload these functions to provide a fast path for wrapper access.
129     inline JSDOMWrapper* getInlineCachedWrapper(DOMWrapperWorld*, void*) { return 0; }
130     inline bool setInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; }
131     inline bool clearInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; }
132
133     template <typename DOMClass> inline JSDOMWrapper* getCachedWrapper(DOMWrapperWorld* world, DOMClass* domObject)
134     {
135         if (JSDOMWrapper* wrapper = getInlineCachedWrapper(world, domObject))
136             return wrapper;
137         return world->m_wrappers.get(domObject);
138     }
139
140     template <typename DOMClass> inline void cacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, JSDOMWrapper* wrapper)
141     {
142         if (setInlineCachedWrapper(world, domObject, wrapper))
143             return;
144         JSC::PassWeak<JSDOMWrapper> passWeak(*world->globalData(), wrapper, wrapperOwner(world, domObject), wrapperContext(world, domObject));
145         pair<DOMObjectWrapperMap::iterator, bool> result = world->m_wrappers.add(domObject, passWeak);
146         ASSERT_UNUSED(result, result.second);
147     }
148
149     template <typename DOMClass> inline void uncacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, JSDOMWrapper* wrapper)
150     {
151         if (clearInlineCachedWrapper(world, domObject, wrapper))
152             return;
153         ASSERT(world->m_wrappers.find(domObject)->second.get() == wrapper);
154         world->m_wrappers.remove(domObject);
155     }
156     
157     #define CREATE_DOM_WRAPPER(exec, globalObject, className, object) createWrapper<JS##className>(exec, globalObject, static_cast<className*>(object))
158     template<class WrapperClass, class DOMClass> inline JSDOMWrapper* createWrapper(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* node)
159     {
160         ASSERT(node);
161         ASSERT(!getCachedWrapper(currentWorld(exec), node));
162         WrapperClass* wrapper = WrapperClass::create(getDOMStructure<WrapperClass>(exec, globalObject), globalObject, node);
163         // FIXME: The entire function can be removed, once we fix caching.
164         // This function is a one-off hack to make Nodes cache in the right global object.
165         cacheWrapper(currentWorld(exec), node, wrapper);
166         return wrapper;
167     }
168
169     template<class WrapperClass, class DOMClass> inline JSC::JSValue wrap(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* domObject)
170     {
171         if (!domObject)
172             return JSC::jsNull();
173         if (JSDOMWrapper* wrapper = getCachedWrapper(currentWorld(exec), domObject))
174             return wrapper;
175         return createWrapper<WrapperClass>(exec, globalObject, domObject);
176     }
177
178     inline void* root(Node* node)
179     {
180         if (node->inDocument())
181             return node->document();
182
183         while (node->parentOrHostNode())
184             node = node->parentOrHostNode();
185         return node;
186     }
187
188     inline void* root(StyleSheet*);
189
190     inline void* root(CSSRule* rule)
191     {
192         if (rule->parentRule())
193             return root(rule->parentRule());
194         if (rule->parentStyleSheet())
195             return root(rule->parentStyleSheet());
196         return rule;
197     }
198
199     inline void* root(StyleSheet* styleSheet)
200     {
201         if (styleSheet->ownerRule())
202             return root(styleSheet->ownerRule());
203         if (styleSheet->ownerNode())
204             return root(styleSheet->ownerNode());
205         return styleSheet;
206     }
207
208     inline void* root(CSSStyleDeclaration* style)
209     {
210         if (CSSRule* parentRule = style->parentRule())
211             return root(parentRule);
212         if (CSSStyleSheet* styleSheet = style->parentStyleSheet())
213             return root(styleSheet);
214         return style;
215     }
216
217     inline void* root(MediaList* mediaList)
218     {
219         if (CSSStyleSheet* parentStyleSheet = mediaList->parentStyleSheet())
220             return root(parentStyleSheet);
221         return mediaList;
222     }
223
224     const JSC::HashTable* getHashTableForGlobalData(JSC::JSGlobalData&, const JSC::HashTable* staticTable);
225
226     void reportException(JSC::ExecState*, JSC::JSValue exception);
227     void reportCurrentException(JSC::ExecState*);
228
229     // Convert a DOM implementation exception code into a JavaScript exception in the execution state.
230     void setDOMException(JSC::ExecState*, ExceptionCode);
231
232     JSC::JSValue jsString(JSC::ExecState*, const String&); // empty if the string is null
233     JSC::JSValue jsStringSlowCase(JSC::ExecState*, JSStringCache&, StringImpl*);
234     JSC::JSValue jsString(JSC::ExecState*, const KURL&); // empty if the URL is null
235     inline JSC::JSValue jsString(JSC::ExecState* exec, const AtomicString& s)
236     { 
237         return jsString(exec, s.string());
238     }
239         
240     JSC::JSValue jsStringOrNull(JSC::ExecState*, const String&); // null if the string is null
241     JSC::JSValue jsStringOrNull(JSC::ExecState*, const KURL&); // null if the URL is null
242
243     JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const String&); // undefined if the string is null
244     JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const KURL&); // undefined if the URL is null
245
246     JSC::JSValue jsStringOrFalse(JSC::ExecState*, const String&); // boolean false if the string is null
247     JSC::JSValue jsStringOrFalse(JSC::ExecState*, const KURL&); // boolean false if the URL is null
248
249     // See JavaScriptCore for explanation: Should be used for any string that is already owned by another
250     // object, to let the engine know that collecting the JSString wrapper is unlikely to save memory.
251     JSC::JSValue jsOwnedStringOrNull(JSC::ExecState*, const String&); 
252
253     String identifierToString(const JSC::Identifier&);
254     String ustringToString(const JSC::UString&);
255     JSC::UString stringToUString(const String&);
256
257     AtomicString identifierToAtomicString(const JSC::Identifier&);
258     AtomicString ustringToAtomicString(const JSC::UString&);
259     AtomicStringImpl* findAtomicString(const JSC::Identifier&);
260
261     String valueToStringWithNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null
262     String valueToStringWithUndefinedOrNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null or undefined
263
264     inline int32_t finiteInt32Value(JSC::JSValue value, JSC::ExecState* exec, bool& okay)
265     {
266         double number = value.toNumber(exec);
267         okay = isfinite(number);
268         return JSC::toInt32(number);
269     }
270
271     // Returns a Date instance for the specified value, or null if the value is NaN or infinity.
272     JSC::JSValue jsDateOrNull(JSC::ExecState*, double);
273     // NaN if the value can't be converted to a date.
274     double valueToDate(JSC::ExecState*, JSC::JSValue);
275
276     template <typename T>
277     inline JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<T> ptr)
278     {
279         return toJS(exec, globalObject, ptr.get());
280     }
281
282     template <typename Iterable>
283     JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Iterable& iterator)
284     {
285         JSC::MarkedArgumentBuffer list;
286         typename Iterable::const_iterator end = iterator.end();
287
288         for (typename Iterable::const_iterator iter = iterator.begin(); iter != end; ++iter)
289             list.append(toJS(exec, globalObject, WTF::getPtr(*iter)));
290
291         return JSC::constructArray(exec, list);
292     }
293
294     template <class T>
295     Vector<T> toNativeArray(JSC::ExecState* exec, JSC::JSValue value)
296     {
297         if (!isJSArray(value))
298             return Vector<T>();
299
300         Vector<T> result;
301         JSC::JSArray* array = asArray(value);
302
303         for (unsigned i = 0; i < array->length(); ++i) {
304             String indexedValue = ustringToString(array->getIndex(i).toString(exec)->value(exec));
305             result.append(indexedValue);
306         }
307         return result;
308     }
309
310     // Validates that the passed object is a sequence type per section 4.1.13 of the WebIDL spec.
311     JSC::JSObject* toJSSequence(JSC::ExecState*, JSC::JSValue, unsigned&);
312
313     // FIXME: Implement allowAccessToContext(JSC::ExecState*, ScriptExecutionContext*);
314     bool shouldAllowAccessToNode(JSC::ExecState*, Node*);
315     bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*);
316     bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*, String& message);
317     // FIXME: Implement allowAccessToDOMWindow(JSC::ExecState*, DOMWindow*);
318
319     // FIXME: Remove these functions in favor of activeContext and
320     // firstContext, which return ScriptExecutionContext*. We prefer to use
321     // ScriptExecutionContext* as the context object in the bindings.
322     DOMWindow* activeDOMWindow(JSC::ExecState*);
323     DOMWindow* firstDOMWindow(JSC::ExecState*);
324
325     void printErrorMessageForFrame(Frame*, const String& message);
326     JSC::JSValue objectToStringFunctionGetter(JSC::ExecState*, JSC::JSValue, const JSC::Identifier& propertyName);
327
328     inline JSC::JSValue jsString(JSC::ExecState* exec, const String& s)
329     {
330         StringImpl* stringImpl = s.impl();
331         if (!stringImpl || !stringImpl->length())
332             return jsEmptyString(exec);
333
334         if (stringImpl->length() == 1 && stringImpl->characters()[0] <= 0xFF)
335             return jsString(exec, stringToUString(s));
336
337         JSStringCache& stringCache = currentWorld(exec)->m_stringCache;
338         JSStringCache::iterator it = stringCache.find(stringImpl);
339         if (it != stringCache.end())
340             return it->second.get();
341
342         return jsStringSlowCase(exec, stringCache, stringImpl);
343     }
344
345     inline DOMObjectWrapperMap& domObjectWrapperMapFor(JSC::ExecState* exec)
346     {
347         return currentWorld(exec)->m_wrappers;
348     }
349
350     inline String ustringToString(const JSC::UString& u)
351     {
352         return u.impl();
353     }
354
355     inline JSC::UString stringToUString(const String& s)
356     {
357         return JSC::UString(s.impl());
358     }
359
360     inline String identifierToString(const JSC::Identifier& i)
361     {
362         return i.impl();
363     }
364
365     inline AtomicString ustringToAtomicString(const JSC::UString& u)
366     {
367         return AtomicString(u.impl());
368     }
369
370     inline AtomicString identifierToAtomicString(const JSC::Identifier& identifier)
371     {
372         return AtomicString(identifier.impl());
373     }
374
375     inline Vector<unsigned long> jsUnsignedLongArrayToVector(JSC::ExecState* exec, JSC::JSValue value)
376     {
377         unsigned length;
378         JSC::JSObject* object = toJSSequence(exec, value, length);
379         if (exec->hadException())
380             return Vector<unsigned long>();
381
382         Vector<unsigned long> result;
383         for (unsigned i = 0; i < length; i++) {
384             JSC::JSValue indexedValue;
385             indexedValue = object->get(exec, i);
386             if (exec->hadException() || indexedValue.isUndefinedOrNull() || !indexedValue.isNumber())
387                 return Vector<unsigned long>();
388             result.append(indexedValue.toUInt32(exec));
389         }
390         return result;
391     }
392 } // namespace WebCore
393
394 #endif // JSDOMBinding_h