Unreviewed, rolling out r151978.
[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, 2013 Apple Inc. All rights reserved.
4  *  Copyright (C) 2007 Samuel Weinig <sam@webkit.org>
5  *  Copyright (C) 2009 Google, Inc. All rights reserved.
6  *  Copyright (C) 2012 Ericsson AB. All rights reserved.
7  *  Copyright (C) 2013 Michael Pruett <michael@68k.org>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23
24 #ifndef JSDOMBinding_h
25 #define JSDOMBinding_h
26
27 #include "BindingState.h"
28 #include "JSDOMGlobalObject.h"
29 #include "JSDOMWrapper.h"
30 #include "DOMWrapperWorld.h"
31 #include "Document.h"
32 #include "ScriptWrappable.h"
33 #include "ScriptWrappableInlines.h"
34 #include <heap/SlotVisitor.h>
35 #include <heap/Weak.h>
36 #include <heap/WeakInlines.h>
37 #include <runtime/Error.h>
38 #include <runtime/FunctionPrototype.h>
39 #include <runtime/JSArray.h>
40 #include <runtime/Lookup.h>
41 #include <runtime/ObjectPrototype.h>
42 #include <runtime/Operations.h>
43 #include <wtf/Forward.h>
44 #include <wtf/Noncopyable.h>
45 #include <wtf/NullPtr.h>
46 #include <wtf/Vector.h>
47
48 namespace JSC {
49 class HashEntry;
50 }
51
52 namespace WebCore {
53
54 class DOMStringList;
55
56     class CachedScript;
57     class Frame;
58     class KURL;
59
60     typedef int ExceptionCode;
61
62     // Base class for all constructor objects in the JSC bindings.
63     class DOMConstructorObject : public JSDOMWrapper {
64         typedef JSDOMWrapper Base;
65     public:
66         static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
67         {
68             return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), &s_info);
69         }
70
71     protected:
72         static const unsigned StructureFlags = JSC::ImplementsHasInstance | JSC::OverridesVisitChildren | JSDOMWrapper::StructureFlags;
73         DOMConstructorObject(JSC::Structure* structure, JSDOMGlobalObject* globalObject)
74             : JSDOMWrapper(structure, globalObject)
75         {
76         }
77     };
78
79     // Constructors using this base class depend on being in a Document and
80     // can never be used from a WorkerGlobalScope.
81     class DOMConstructorWithDocument : public DOMConstructorObject {
82         typedef DOMConstructorObject Base;
83     public:
84         Document* document() const
85         {
86             return toDocument(scriptExecutionContext());
87         }
88
89     protected:
90         DOMConstructorWithDocument(JSC::Structure* structure, JSDOMGlobalObject* globalObject)
91             : DOMConstructorObject(structure, globalObject)
92         {
93         }
94
95         void finishCreation(JSDOMGlobalObject* globalObject)
96         {
97             Base::finishCreation(globalObject->vm());
98             ASSERT(globalObject->scriptExecutionContext()->isDocument());
99         }
100     };
101     
102     JSC::Structure* getCachedDOMStructure(JSDOMGlobalObject*, const JSC::ClassInfo*);
103     JSC::Structure* cacheDOMStructure(JSDOMGlobalObject*, JSC::Structure*, const JSC::ClassInfo*);
104
105     inline JSDOMGlobalObject* deprecatedGlobalObjectForPrototype(JSC::ExecState* exec)
106     {
107         // FIXME: Callers to this function should be using the global object
108         // from which the object is being created, instead of assuming the lexical one.
109         // e.g. subframe.document.body should use the subframe's global object, not the lexical one.
110         return JSC::jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
111     }
112
113     template<class WrapperClass> inline JSC::Structure* getDOMStructure(JSC::ExecState* exec, JSDOMGlobalObject* globalObject)
114     {
115         if (JSC::Structure* structure = getCachedDOMStructure(globalObject, &WrapperClass::s_info))
116             return structure;
117         return cacheDOMStructure(globalObject, WrapperClass::createStructure(exec->vm(), globalObject, WrapperClass::createPrototype(exec, globalObject)), &WrapperClass::s_info);
118     }
119
120     template<class WrapperClass> inline JSC::Structure* deprecatedGetDOMStructure(JSC::ExecState* exec)
121     {
122         // FIXME: This function is wrong.  It uses the wrong global object for creating the prototype structure.
123         return getDOMStructure<WrapperClass>(exec, deprecatedGlobalObjectForPrototype(exec));
124     }
125
126     template<class WrapperClass> inline JSC::JSObject* getDOMPrototype(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject)
127     {
128         return JSC::jsCast<JSC::JSObject*>(asObject(getDOMStructure<WrapperClass>(exec, JSC::jsCast<JSDOMGlobalObject*>(globalObject))->storedPrototype()));
129     }
130
131     inline JSDOMWrapper* getInlineCachedWrapper(DOMWrapperWorld*, void*) { return 0; }
132     inline bool setInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*, JSC::WeakHandleOwner*, void*) { return false; }
133     inline bool clearInlineCachedWrapper(DOMWrapperWorld*, void*, JSDOMWrapper*) { return false; }
134
135     inline JSDOMWrapper* getInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject)
136     {
137         if (!world->isNormal())
138             return 0;
139         return domObject->wrapper();
140     }
141
142     inline bool setInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject, JSDOMWrapper* wrapper, JSC::WeakHandleOwner* wrapperOwner, void* context)
143     {
144         if (!world->isNormal())
145             return false;
146         domObject->setWrapper(*world->vm(), wrapper, wrapperOwner, context);
147         return true;
148     }
149
150     inline bool clearInlineCachedWrapper(DOMWrapperWorld* world, ScriptWrappable* domObject, JSDOMWrapper* wrapper)
151     {
152         if (!world->isNormal())
153             return false;
154         domObject->clearWrapper(wrapper);
155         return true;
156     }
157
158     template <typename DOMClass> inline JSDOMWrapper* getCachedWrapper(DOMWrapperWorld* world, DOMClass* domObject)
159     {
160         if (JSDOMWrapper* wrapper = getInlineCachedWrapper(world, domObject))
161             return wrapper;
162         return world->m_wrappers.get(domObject);
163     }
164
165     template <typename DOMClass> inline void cacheWrapper(DOMWrapperWorld* world, DOMClass* domObject, JSDOMWrapper* wrapper)
166     {
167         JSC::WeakHandleOwner* owner = wrapperOwner(world, domObject);
168         void* context = wrapperContext(world, domObject);
169         if (setInlineCachedWrapper(world, domObject, wrapper, owner, context))
170             return;
171         JSC::PassWeak<JSDOMWrapper> passWeak(wrapper, owner, context);
172         weakAdd(world->m_wrappers, (void*)domObject, passWeak);
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         weakRemove(world->m_wrappers, (void*)domObject, wrapper);
180     }
181     
182     #define CREATE_DOM_WRAPPER(exec, globalObject, className, object) createWrapper<JS##className>(exec, globalObject, static_cast<className*>(object))
183     template<class WrapperClass, class DOMClass> inline JSDOMWrapper* createWrapper(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* node)
184     {
185         ASSERT(node);
186         ASSERT(!getCachedWrapper(currentWorld(exec), node));
187         WrapperClass* wrapper = WrapperClass::create(getDOMStructure<WrapperClass>(exec, globalObject), globalObject, node);
188         // FIXME: The entire function can be removed, once we fix caching.
189         // This function is a one-off hack to make Nodes cache in the right global object.
190         cacheWrapper(currentWorld(exec), node, wrapper);
191         return wrapper;
192     }
193
194     template<class WrapperClass, class DOMClass> inline JSC::JSValue wrap(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* domObject)
195     {
196         if (!domObject)
197             return JSC::jsNull();
198         if (JSDOMWrapper* wrapper = getCachedWrapper(currentWorld(exec), domObject))
199             return wrapper;
200         return createWrapper<WrapperClass>(exec, globalObject, domObject);
201     }
202
203     template<class WrapperClass, class DOMClass> inline JSC::JSValue getExistingWrapper(JSC::ExecState* exec, DOMClass* domObject)
204     {
205         ASSERT(domObject);
206         return getCachedWrapper(currentWorld(exec), domObject);
207     }
208
209     template<class WrapperClass, class DOMClass> inline JSC::JSValue createNewWrapper(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* domObject)
210     {
211         ASSERT(domObject);
212         ASSERT(!getCachedWrapper(currentWorld(exec), domObject));
213         return createWrapper<WrapperClass>(exec, globalObject, domObject);
214     }
215
216     inline JSC::JSValue argumentOrNull(JSC::ExecState* exec, unsigned index)
217     {
218         return index >= exec->argumentCount() ? JSC::JSValue() : exec->argument(index);
219     }
220
221     const JSC::HashTable* getHashTableForGlobalData(JSC::VM&, const JSC::HashTable* staticTable);
222
223     void reportException(JSC::ExecState*, JSC::JSValue exception, CachedScript* = 0);
224     void reportCurrentException(JSC::ExecState*);
225
226     // Convert a DOM implementation exception code into a JavaScript exception in the execution state.
227     void setDOMException(JSC::ExecState*, ExceptionCode);
228
229     JSC::JSValue jsStringWithCache(JSC::ExecState*, const String&);
230     JSC::JSValue jsString(JSC::ExecState*, const KURL&); // empty if the URL is null
231     inline JSC::JSValue jsStringWithCache(JSC::ExecState* exec, const AtomicString& s)
232     { 
233         return jsStringWithCache(exec, s.string());
234     }
235         
236     JSC::JSValue jsStringOrNull(JSC::ExecState*, const String&); // null if the string is null
237     JSC::JSValue jsStringOrNull(JSC::ExecState*, const KURL&); // null if the URL is null
238
239     JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const String&); // undefined if the string is null
240     JSC::JSValue jsStringOrUndefined(JSC::ExecState*, const KURL&); // undefined if the URL is null
241
242     // See JavaScriptCore for explanation: Should be used for any string that is already owned by another
243     // object, to let the engine know that collecting the JSString wrapper is unlikely to save memory.
244     JSC::JSValue jsOwnedStringOrNull(JSC::ExecState*, const String&); 
245
246     String propertyNameToString(JSC::PropertyName);
247
248     AtomicString propertyNameToAtomicString(JSC::PropertyName);
249     AtomicStringImpl* findAtomicString(JSC::PropertyName);
250
251     String valueToStringWithNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null
252     String valueToStringWithUndefinedOrNullCheck(JSC::ExecState*, JSC::JSValue); // null if the value is null or undefined
253
254     inline int32_t finiteInt32Value(JSC::JSValue value, JSC::ExecState* exec, bool& okay)
255     {
256         double number = value.toNumber(exec);
257         okay = std::isfinite(number);
258         return JSC::toInt32(number);
259     }
260
261     enum IntegerConversionConfiguration {
262         NormalConversion,
263         EnforceRange,
264         // FIXME: Implement Clamp
265     };
266
267     int32_t toInt32EnforceRange(JSC::ExecState*, JSC::JSValue);
268     uint32_t toUInt32EnforceRange(JSC::ExecState*, JSC::JSValue);
269
270     int8_t toInt8(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration);
271     uint8_t toUInt8(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration);
272
273     /*
274         Convert a value to an integer as per <http://www.w3.org/TR/WebIDL/>.
275         The conversion fails if the value cannot be converted to a number or,
276         if EnforceRange is specified, the value is outside the range of the
277         destination integer type.
278     */
279     inline int32_t toInt32(JSC::ExecState* exec, JSC::JSValue value, IntegerConversionConfiguration configuration)
280     {
281         if (configuration == EnforceRange)
282             return toInt32EnforceRange(exec, value);
283         return value.toInt32(exec);
284     }
285
286     inline uint32_t toUInt32(JSC::ExecState* exec, JSC::JSValue value, IntegerConversionConfiguration configuration)
287     {
288         if (configuration == EnforceRange)
289             return toUInt32EnforceRange(exec, value);
290         return value.toUInt32(exec);
291     }
292
293     int64_t toInt64(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration);
294     uint64_t toUInt64(JSC::ExecState*, JSC::JSValue, IntegerConversionConfiguration);
295
296     // Returns a Date instance for the specified value, or null if the value is NaN or infinity.
297     JSC::JSValue jsDateOrNull(JSC::ExecState*, double);
298     // NaN if the value can't be converted to a date.
299     double valueToDate(JSC::ExecState*, JSC::JSValue);
300
301     // Validates that the passed object is a sequence type per section 4.1.13 of the WebIDL spec.
302     inline JSC::JSObject* toJSSequence(JSC::ExecState* exec, JSC::JSValue value, unsigned& length)
303     {
304         JSC::JSObject* object = value.getObject();
305         if (!object) {
306             throwTypeError(exec);
307             return 0;
308         }
309
310         JSC::JSValue lengthValue = object->get(exec, exec->propertyNames().length);
311         if (exec->hadException())
312             return 0;
313
314         if (lengthValue.isUndefinedOrNull()) {
315             throwTypeError(exec);
316             return 0;
317         }
318
319         length = lengthValue.toUInt32(exec);
320         if (exec->hadException())
321             return 0;
322
323         return object;
324     }
325
326     template <typename T>
327     inline JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<T> ptr)
328     {
329         return toJS(exec, globalObject, ptr.get());
330     }
331
332     template <class T>
333     struct JSValueTraits {
334         static inline JSC::JSValue arrayJSValue(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const T& value)
335         {
336             return toJS(exec, globalObject, WTF::getPtr(value));
337         }
338     };
339
340     template<>
341     struct JSValueTraits<String> {
342         static inline JSC::JSValue arrayJSValue(JSC::ExecState* exec, JSDOMGlobalObject*, const String& value)
343         {
344             return jsStringWithCache(exec, value);
345         }
346     };
347
348     template<>
349     struct JSValueTraits<float> {
350         static inline JSC::JSValue arrayJSValue(JSC::ExecState*, JSDOMGlobalObject*, const float& value)
351         {
352             return JSC::jsNumber(value);
353         }
354     };
355
356     template<>
357     struct JSValueTraits<unsigned long> {
358         static inline JSC::JSValue arrayJSValue(JSC::ExecState*, JSDOMGlobalObject*, const unsigned long& value)
359         {
360             return JSC::jsNumber(value);
361         }
362     };
363
364     template <typename T, size_t inlineCapacity>
365     JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Vector<T, inlineCapacity>& iterator)
366     {
367         JSC::MarkedArgumentBuffer list;
368         typename Vector<T, inlineCapacity>::const_iterator end = iterator.end();        
369         typedef JSValueTraits<T> TraitsType;
370
371         for (typename Vector<T, inlineCapacity>::const_iterator iter = iterator.begin(); iter != end; ++iter)
372             list.append(TraitsType::arrayJSValue(exec, globalObject, *iter));
373
374         return JSC::constructArray(exec, 0, globalObject, list);
375     }
376
377     JSC::JSValue jsArray(JSC::ExecState*, JSDOMGlobalObject*, PassRefPtr<DOMStringList>);
378
379     template<class T> struct NativeValueTraits;
380
381     template<>
382     struct NativeValueTraits<String> {
383         static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, String& indexedValue)
384         {
385             indexedValue = jsValue.toString(exec)->value(exec);
386             return true;
387         }
388     };
389
390     template<>
391     struct NativeValueTraits<unsigned> {
392         static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, unsigned& indexedValue)
393         {
394             if (!jsValue.isNumber())
395                 return false;
396
397             indexedValue = jsValue.toUInt32(exec);
398             if (exec->hadException())
399                 return false;
400
401             return true;
402         }
403     };
404
405     template<>
406     struct NativeValueTraits<float> {
407         static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, float& indexedValue)
408         {
409             indexedValue = jsValue.toFloat(exec);
410             return !exec->hadException();
411         }
412     };
413
414     template <class T, class JST>
415     Vector<RefPtr<T> > toRefPtrNativeArray(JSC::ExecState* exec, JSC::JSValue value, T* (*toT)(JSC::JSValue value))
416     {
417         if (!isJSArray(value))
418             return Vector<RefPtr<T> >();
419
420         Vector<RefPtr<T> > result;
421         JSC::JSArray* array = asArray(value);
422         for (size_t i = 0; i < array->length(); ++i) {
423             JSC::JSValue element = array->getIndex(exec, i);
424             if (element.inherits(&JST::s_info))
425                 result.append((*toT)(element));
426             else {
427                 throwVMError(exec, createTypeError(exec, "Invalid Array element type"));
428                 return Vector<RefPtr<T> >();
429             }
430         }
431         return result;
432     }
433
434     template <class T>
435     Vector<T> toNativeArray(JSC::ExecState* exec, JSC::JSValue value)
436     {
437         unsigned length = 0;
438         if (isJSArray(value)) {
439             JSC::JSArray* array = asArray(value);
440             length = array->length();
441         } else
442             toJSSequence(exec, value, length);
443
444         JSC::JSObject* object = value.getObject();
445         Vector<T> result;
446         typedef NativeValueTraits<T> TraitsType;
447
448         for (unsigned i = 0; i < length; ++i) {
449             T indexValue;
450             if (!TraitsType::nativeValue(exec, object->get(exec, i), indexValue))
451                 return Vector<T>();
452             result.append(indexValue);
453         }
454         return result;
455     }
456
457     template <class T>
458     Vector<T> toNativeArguments(JSC::ExecState* exec, size_t startIndex = 0)
459     {
460         size_t length = exec->argumentCount();
461         ASSERT(startIndex <= length);
462
463         Vector<T> result;
464         typedef NativeValueTraits<T> TraitsType;
465
466         for (size_t i = startIndex; i < length; ++i) {
467             T indexValue;
468             if (!TraitsType::nativeValue(exec, exec->argument(i), indexValue))
469                 return Vector<T>();
470             result.append(indexValue);
471         }
472         return result;
473     }
474
475     bool shouldAllowAccessToNode(JSC::ExecState*, Node*);
476     bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*);
477     bool shouldAllowAccessToFrame(JSC::ExecState*, Frame*, String& message);
478     bool shouldAllowAccessToDOMWindow(BindingState*, DOMWindow*, String& message);
479
480     void printErrorMessageForFrame(Frame*, const String& message);
481     JSC::JSValue objectToStringFunctionGetter(JSC::ExecState*, JSC::JSValue, JSC::PropertyName);
482
483     inline JSC::JSValue jsStringWithCache(JSC::ExecState* exec, const String& s)
484     {
485         StringImpl* stringImpl = s.impl();
486         if (!stringImpl || !stringImpl->length())
487             return jsEmptyString(exec);
488
489         if (stringImpl->length() == 1) {
490             UChar singleCharacter = (*stringImpl)[0u];
491             if (singleCharacter <= JSC::maxSingleCharacterString) {
492                 JSC::VM* vm = &exec->vm();
493                 return vm->smallStrings.singleCharacterString(vm, static_cast<unsigned char>(singleCharacter));
494             }
495         }
496
497         JSStringCache& stringCache = currentWorld(exec)->m_stringCache;
498         JSStringCache::AddResult addResult = stringCache.add(stringImpl, nullptr);
499         if (addResult.isNewEntry)
500             addResult.iterator->value = JSC::jsString(exec, String(stringImpl));
501         return JSC::JSValue(addResult.iterator->value.get());
502     }
503
504     inline String propertyNameToString(JSC::PropertyName propertyName)
505     {
506         return propertyName.publicName();
507     }
508
509     inline AtomicString propertyNameToAtomicString(JSC::PropertyName propertyName)
510     {
511         return AtomicString(propertyName.publicName());
512     }
513
514     template <class ThisImp>
515     inline const JSC::HashEntry* getStaticValueSlotEntryWithoutCaching(JSC::ExecState* exec, JSC::PropertyName propertyName)
516     {
517         const JSC::HashEntry* entry = ThisImp::s_info.propHashTable(exec)->entry(exec, propertyName);
518         if (!entry) // not found, forward to parent
519             return getStaticValueSlotEntryWithoutCaching<typename ThisImp::Base>(exec, propertyName);
520         return entry;
521     }
522
523     template <>
524     inline const JSC::HashEntry* getStaticValueSlotEntryWithoutCaching<JSDOMWrapper>(JSC::ExecState*, JSC::PropertyName)
525     {
526         return 0;
527     }
528
529     template<typename T>
530     class HasMemoryCostMemberFunction {
531         typedef char YesType;
532         struct NoType {
533             char padding[8];
534         };
535
536         struct BaseMixin {
537             size_t memoryCost();
538         };
539
540         struct Base : public T, public BaseMixin { };
541
542         template<typename U, U> struct
543         TypeChecker { };
544
545         template<typename U>
546         static NoType dummy(U*, TypeChecker<size_t (BaseMixin::*)(), &U::memoryCost>* = 0);
547         static YesType dummy(...);
548
549     public:
550         static const bool value = sizeof(dummy(static_cast<Base*>(0))) == sizeof(YesType);
551     };
552     template <typename T, bool hasReportCostFunction = HasMemoryCostMemberFunction<T>::value > struct ReportMemoryCost;
553     template <typename T> struct ReportMemoryCost<T, true> {
554         static void reportMemoryCost(JSC::ExecState* exec, T* impl)
555         {
556             exec->heap()->reportExtraMemoryCost(impl->memoryCost());
557         }
558     };
559     template <typename T> struct ReportMemoryCost<T, false> {
560         static void reportMemoryCost(JSC::ExecState*, T*)
561         {
562         }
563     };
564
565 } // namespace WebCore
566
567 #endif // JSDOMBinding_h