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
48namespace JSC {
49class HashEntry;
50}
51
52namespace WebCore {
53
54class 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 WorkerContext.
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
568