1 /*
2  *  This file is part of the KDE libraries
3  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2003 Apple Computer, Inc.
5  *  Copyright (C) 2007, 2008 Maksim Orlovich (maksim@kde.org)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library 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  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library 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 _KJS_BINDING_H_
23 #define _KJS_BINDING_H_
24 
25 #include <khtml_export.h>
26 #include <kjs/interpreter.h>
27 #include <kjs/global.h>
28 #include <wtf/HashMap.h>
29 
30 #include <dom/dom_node.h>
31 #include <QVariant>
32 #include <QHash>
33 #include <QString>
34 #include <kjs/lookup.h>
35 #include <kjs/function.h>
36 #include <kjs/JSVariableObject.h>
37 #include <kjs/object_object.h>
38 #include <misc/shared.h>
39 
40 #include <stdlib.h> // for abort
41 
42 #define KJS_CHECK_THIS( ClassName, theObj ) \
43     if (!theObj || !theObj->inherits(&ClassName::info)) { \
44         KJS::UString errMsg = "Attempt at calling a function that expects a "; \
45         errMsg += ClassName::info.className; \
46         errMsg += " on a "; \
47         errMsg += theObj->className(); \
48         KJS::JSObject *err = KJS::Error::create(exec, KJS::TypeError, errMsg.ascii()); \
49         exec->setException(err); \
50         return err; \
51     }
52 
53 namespace KParts
54 {
55 class ReadOnlyPart;
56 class LiveConnectExtension;
57 }
58 
59 namespace khtml
60 {
61 class ChildFrame;
62 }
63 
64 namespace KJS
65 {
66 
67 // For the ecma debugger we provide our own conversion rather than the
68 // use the native one, since using the toString
69 // method on object can invoke code
70 QString valueToString(KJS::JSValue *value);
71 
72 // Serializes an exception for human consumption.
73 QString exceptionToString(ExecState *exec, JSValue *exception);
74 
75 /**
76  * Base class for all objects in this binding. Doesn't manage exceptions any more
77  */
78 class DOMObject : public JSObject
79 {
80 protected:
DOMObject()81     DOMObject() : JSObject() {}
DOMObject(JSObject * proto)82     DOMObject(JSObject *proto) : JSObject(proto) {}
83 public:
shouldMark()84     bool shouldMark() const
85     {
86         return !_prop.isEmpty();
87     }
88     UString toString(ExecState *exec) const override;
89 };
90 
91 /**
92  * We inherit from Interpreter, to save a pointer to the HTML part
93  * that the interpreter runs for.
94  * The interpreter also stores the DOM object - >KJS::DOMObject cache.
95  */
96 class ScriptInterpreter : public Interpreter
97 {
98 public:
99     ScriptInterpreter(JSGlobalObject *global, khtml::ChildFrame *frame);
100     virtual ~ScriptInterpreter();
101 
102     // We need to keep track of wrappers in 2 ways:
103     //  - we want the same wrapper for the same node (see #145775)
104     //  - we want to drop all the references from this interpreter on clear, so
105     //  wrappers don't stick around. Hence we have a global set and a per-interpreter one.
106 
107     // Reuses an existing wrapper, perhaps also updating the current map
108     // to refer to it as well.
getDOMObject(void * objectHandle)109     DOMObject *getDOMObject(void *objectHandle)
110     {
111         DOMObject *existing = allDomObjects()->get(objectHandle);
112         if (existing) {
113             m_domObjects.set(objectHandle, existing);
114         }
115         return existing;
116     }
117 
putDOMObject(void * objectHandle,DOMObject * obj)118     void putDOMObject(void *objectHandle, DOMObject *obj)
119     {
120         allDomObjects()->set(objectHandle, obj);
121         m_domObjects.set(objectHandle, obj);
122     }
123 
124     static void forgetDOMObject(void *objectHandle);
125 
clear()126     void clear()
127     {
128         m_domObjects.clear(); // Global set will be cleared at GC time.
129     }
130 
131     /**
132      * Mark objects in the DOMObject cache.
133      */
134     void mark(bool isMain) override;
135     KParts::ReadOnlyPart *part() const;
136 
rtti()137     int rtti() override
138     {
139         return 1;
140     }
141 
142     /**
143      * Set the event that is triggering the execution of a script, if any
144      */
setCurrentEvent(DOM::Event * evt)145     void setCurrentEvent(DOM::Event *evt)
146     {
147         m_evt = evt;
148     }
setInlineCode(bool inlineCode)149     void setInlineCode(bool inlineCode)
150     {
151         m_inlineCode = inlineCode;
152     }
setProcessingTimerCallback(bool timerCallback)153     void setProcessingTimerCallback(bool timerCallback)
154     {
155         m_timerCallback = timerCallback;
156     }
157     /**
158      * "Smart" window.open policy
159      */
160     bool isWindowOpenAllowed() const;
161 
162     /**
163      * CPU guard API. This should be used instead of Interpreter
164      * methods as it manages the timeouts, including VG support
165      */
166     bool shouldInterruptScript() const override;
167     void startCPUGuard();
168     void stopCPUGuard();
169 
turnOffCPUGuard()170     static void turnOffCPUGuard()
171     {
172         s_disableCPUGuard = true;
173     }
174 private:
175     khtml::ChildFrame *m_frame;
176     HashMap<void *, DOMObject *> m_domObjects;
177     static HashMap<void *, DOMObject *> *s_allDomObjects;
allDomObjects()178     static HashMap<void *, DOMObject *> *allDomObjects()
179     {
180         if (!s_allDomObjects) {
181             s_allDomObjects = new HashMap<void *, DOMObject *>();
182         }
183         return s_allDomObjects;
184     }
185 
186     DOM::Event *m_evt;
187     bool m_inlineCode;
188     bool m_timerCallback;
189     static bool s_disableCPUGuard;
190 };
191 
192 /** Some templates to help make wrappers. example:
193     class Foo: public DOMWrapperObject<DOM::FooImpl> {
194     }
195 
196     ...
197 
198     getWrapper<Foo>(exec, someImpl);
199 */
200 template<typename Wrapper>
getWrapper(ExecState * exec,typename Wrapper::wrappedType * g)201 JSValue *getWrapper(ExecState *exec, typename Wrapper::wrappedType *g)
202 {
203     DOMObject *ret = nullptr;
204     if (!g) {
205         return jsNull();
206     }
207 
208     ScriptInterpreter *interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
209     if ((ret = interp->getDOMObject(g))) {
210         return ret;
211     }
212 
213     ret = new Wrapper(exec, g);
214     interp->putDOMObject(g, ret);
215     return ret;
216 }
217 
218 template<typename Wrapped>
219 class DOMWrapperObject : public DOMObject
220 {
221 public:
222     typedef Wrapped wrappedType;
223     typedef DOMWrapperObject<Wrapped> WrapperBase;
224 
DOMWrapperObject(JSObject * proto,Wrapped * wrapee)225     DOMWrapperObject(JSObject *proto, Wrapped *wrapee):
226         DOMObject(proto), m_impl(wrapee)
227     {}
228 
~DOMWrapperObject()229     virtual ~DOMWrapperObject()
230     {
231         ScriptInterpreter::forgetDOMObject(m_impl.get());
232     }
233 
toBoolean(ExecState *)234     bool toBoolean(ExecState *) const override
235     {
236         return true;
237     }
238 
impl()239     Wrapped *impl()
240     {
241         return m_impl.get();
242     }
impl()243     const Wrapped *impl() const
244     {
245         return m_impl.get();
246     }
247 private:
248     SharedPtr<Wrapped> m_impl;
249 };
250 
251 /**
252  A little helper for setting stuff up given an entry
253 */
254 template<class FuncImp, class ThisImp>
getSlotFromEntry(const HashEntry * entry,ThisImp * thisObj,PropertySlot & slot)255 inline void getSlotFromEntry(const HashEntry *entry, ThisImp *thisObj, PropertySlot &slot)
256 {
257     if (entry->attr & Function) {
258         slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
259     } else {
260         slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
261     }
262 }
263 
264 /**
265   Like getStaticPropertySlot but doesn't check the parent. Handy when there
266   are both functions and values
267  */
268 template <class FuncImp, class ThisImp>
getStaticOwnPropertySlot(const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)269 inline bool getStaticOwnPropertySlot(const HashTable *table,
270                                      ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot)
271 {
272     const HashEntry *entry = Lookup::findEntry(table, propertyName);
273 
274     if (!entry) { // not found, forward to parent
275         return false;
276     }
277 
278     if (entry->attr & Function) {
279         slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
280     } else {
281         slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
282     }
283 
284     return true;
285 }
286 
287 /**
288   Handler for local table-looked up things.
289 */
290 template<class ThisImp>
getStaticOwnValueSlot(const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)291 inline bool getStaticOwnValueSlot(const HashTable *table,
292                                   ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot)
293 {
294     const HashEntry *entry = Lookup::findEntry(table, propertyName);
295     if (!entry) {
296         return false;
297     }
298 
299     assert(!(entry->attr & Function));
300     slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
301     return true;
302 }
303 
304 /* Helper for the below*/
305 template<class JSTypeImp>
indexGetterAdapter(ExecState * exec,JSObject *,unsigned,const PropertySlot & slot)306 JSValue *indexGetterAdapter(ExecState *exec, JSObject *, unsigned, const PropertySlot &slot)
307 {
308     JSTypeImp *thisObj = static_cast<JSTypeImp *>(slot.slotBase());
309     return thisObj->indexGetter(exec, slot.index());
310 }
311 
312 /**
313  Handler for index properties. Will call "length" method on the listObj
314  to determine whether it's in range, and arrange to have indexGetter called
315 */
316 template<class ThisImp, class BaseObj>
getIndexSlot(ThisImp * thisObj,const BaseObj & listObj,const Identifier & propertyName,PropertySlot & slot)317 inline bool getIndexSlot(ThisImp *thisObj, const BaseObj &listObj,
318                          const Identifier &propertyName, PropertySlot &slot)
319 {
320     bool ok;
321     unsigned u = propertyName.toArrayIndex(&ok);
322     if (ok && u < listObj.length()) {
323         slot.setCustomIndex(thisObj, u, indexGetterAdapter<ThisImp>);
324         return true;
325     }
326     return false;
327 }
328 
329 /**
330  Version that takes an external bound
331 */
332 template<class ThisImp>
getIndexSlot(ThisImp * thisObj,unsigned lengthLimit,const Identifier & propertyName,PropertySlot & slot)333 inline bool getIndexSlot(ThisImp *thisObj, unsigned lengthLimit,
334                          const Identifier &propertyName, PropertySlot &slot)
335 {
336     bool ok;
337     unsigned u = propertyName.toArrayIndex(&ok);
338     if (ok && u < lengthLimit) {
339         slot.setCustomIndex(thisObj, u, indexGetterAdapter<ThisImp>);
340         return true;
341     }
342     return false;
343 }
344 
345 template<class ThisImp>
getIndexSlot(ThisImp * thisObj,int lengthLimit,const Identifier & propertyName,PropertySlot & slot)346 inline bool getIndexSlot(ThisImp *thisObj, int lengthLimit,
347                          const Identifier &propertyName, PropertySlot &slot)
348 {
349     return getIndexSlot(thisObj, (unsigned)lengthLimit, propertyName, slot);
350 }
351 
352 /**
353  Version w/o the bounds check
354 */
355 template<class ThisImp>
getIndexSlot(ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)356 inline bool getIndexSlot(ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot)
357 {
358     bool ok;
359     unsigned u = propertyName.toArrayIndex(&ok);
360     if (ok) {
361         slot.setCustomIndex(thisObj, u, indexGetterAdapter<ThisImp>);
362         return true;
363     }
364     return false;
365 }
366 
367 /* Helper for below */
368 JSValue *valueGetterAdapter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot);
369 
370 /**
371  This sets up the slot to return a particular JSValue*; unlike
372  setValueSlot, it does not require there to be a location to point at
373 */
getImmediateValueSlot(JSObject * thisObj,JSValue * value,PropertySlot & slot)374 inline bool getImmediateValueSlot(JSObject *thisObj, JSValue *value, PropertySlot &slot)
375 {
376     slot.setCustomValue(thisObj, value, valueGetterAdapter);
377     return true;
378 }
379 
380 /**
381  * Retrieve from cache, or create, a KJS object around a DOM object
382  */
383 template<class DOMObj, class KJSDOMObj>
cacheDOMObject(ExecState * exec,DOMObj * domObj)384 inline JSValue *cacheDOMObject(ExecState *exec, DOMObj *domObj)
385 {
386     DOMObject *ret;
387     if (!domObj) {
388         return jsNull();
389     }
390     ScriptInterpreter *interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
391     if ((ret = interp->getDOMObject(domObj))) {
392         return ret;
393     } else {
394         ret = new KJSDOMObj(exec, domObj);
395         interp->putDOMObject(domObj, ret);
396         return ret;
397     }
398 }
399 
400 /**
401  * Convert an object to a Node. Returns 0 if not possible.
402  */
403 DOM::NodeImpl *toNode(JSValue *);
404 /**
405  *  Get a String object, or Null() if s is null
406  */
407 JSValue *getStringOrNull(DOM::DOMString s);
408 
409 /**
410  * Null string if the value is null
411  */
412 DOM::DOMString valueToStringWithNullCheck(ExecState *exec, JSValue *v);
413 
414 /**
415  * Convert a KJS value into a QVariant
416  */
417 QVariant ValueToVariant(ExecState *exec, JSValue *val);
418 
419 // Convert a DOM implementation exception code into a JavaScript exception in the execution state.
420 void setDOMException(ExecState *exec, int DOMExceptionCode);
421 
422 // Helper class to call setDOMException on exit without adding lots of separate calls to that function.
423 class DOMExceptionTranslator
424 {
425 public:
DOMExceptionTranslator(ExecState * exec)426     explicit DOMExceptionTranslator(ExecState *exec) : m_exec(exec), m_code(0) { }
~DOMExceptionTranslator()427     ~DOMExceptionTranslator()
428     {
429         setDOMException(m_exec, m_code);
430     }
431     operator int &()
432     {
433         return m_code;
434     }
435     operator int *()
436     {
437         return &m_code;
438     }
439 
triggered()440     bool triggered()
441     {
442         return m_code;
443     }
444 private:
445     ExecState *m_exec;
446     int m_code;
447 };
448 
449 // convenience function
jsString(const QString & s)450 inline JSCell *jsString(const QString &s)
451 {
452     return jsString(UString(s));
453 }
454 
455 // This is used to create pseudo-constructor objects, like Mozillaish
456 // Element, HTMLDocument, etc., which do not act like real constructors,
457 // but do have the prototype property pointing to prototype of "instances"
458 #define DEFINE_PSEUDO_CONSTRUCTOR(ClassName) \
459     class ClassName : public DOMObject { \
460     public: \
461         ClassName(ExecState *); \
462         virtual const ClassInfo* classInfo() const { return &info; } \
463         static const ClassInfo info; \
464         static JSObject* self(ExecState *exec); \
465         virtual bool implementsHasInstance() const; \
466     };
467 
468 #define IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProto) \
469     const ClassInfo Class::info = { ClassName, nullptr, nullptr, nullptr }; \
470     Class::Class(ExecState* exec): DOMObject(ParentProto) {\
471         /* Since ProtoClass ctor might need us, make sure we're registered */ \
472         exec->lexicalInterpreter()->globalObject()->put(exec, "[[" ClassName ".constructor]]", this, KJS::Internal | KJS::DontEnum); \
473         JSObject* proto = ProtoClass::self(exec); \
474         putDirect(exec->propertyNames().prototype, proto, DontDelete|ReadOnly); \
475     }\
476     JSObject* Class::self(ExecState *exec) { \
477         return cacheGlobalObject<Class>(exec, "[[" ClassName ".constructor]]"); \
478     } \
479     bool Class::implementsHasInstance() const { \
480         return true; \
481     }
482 
483 #define IMPLEMENT_PSEUDO_CONSTRUCTOR(Class,ClassName,ProtoClass) \
484     IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,exec->lexicalInterpreter()->builtinObjectPrototype())
485 
486 #define IMPLEMENT_PSEUDO_CONSTRUCTOR_WITH_PARENT(Class,ClassName,ProtoClass,ParentProtoClass) \
487     IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProtoClass::self(exec))
488 
489 // This declares a constant table, which merely maps everything in its
490 // table to its token value. Can be used as a prototype
491 #define DEFINE_CONSTANT_TABLE(Class) \
492     class Class : public DOMObject { \
493     public: \
494         Class(ExecState *exec): DOMObject(exec->lexicalInterpreter()->builtinObjectPrototype()) {} \
495         \
496         using KJS::JSObject::getOwnPropertySlot;\
497         virtual bool getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot);\
498         JSValue* getValueProperty(ExecState *exec, int token) const; \
499         virtual const ClassInfo* classInfo() const { return &info; } \
500         static const ClassInfo info; \
501         static JSObject* self(ExecState *exec);\
502         static Identifier* s_name; \
503         static Identifier* name(); \
504     };
505 
506 // Emits an implementation of a constant table
507 #define IMPLEMENT_CONSTANT_TABLE(Class,ClassName) \
508     bool Class::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) \
509     { \
510         return getStaticValueSlot<Class, DOMObject>(exec, &Class##Table, this, propertyName, slot);\
511     }\
512     JSValue* Class::getValueProperty(ExecState * /*exec*/, int token) const { \
513         /* We use the token as the value to return directly*/ \
514         return jsNumber((unsigned int)token); \
515     }  \
516     JSObject* Class::self(ExecState *exec) { \
517         return cacheGlobalObject<Class>(exec,  *name()); \
518     } \
519     Identifier* Class::s_name = nullptr; \
520     Identifier* Class::name() { \
521         if (!s_name) s_name = new Identifier("[[" ClassName ".constant_table]]"); \
522         return s_name; \
523     } \
524     const ClassInfo Class::info = { ClassName, nullptr, &Class##Table, nullptr };
525 
526 // cacheGlobalObject<> is not in namespace KJS - need to use ::cacheGlobalObject<>
527 #define KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ProtoCode) \
528     class ClassProto : public JSObject { \
529         friend JSObject* KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \
530     public: \
531         static JSObject* self(ExecState *exec) { \
532             return KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(exec, *name()); \
533         } \
534         virtual const ClassInfo *classInfo() const { return &info; } \
535         static const ClassInfo info; \
536     protected: \
537         ClassProto( ExecState *exec ) \
538             : JSObject( ProtoCode ) {} \
539         \
540         static Identifier* s_name; \
541         static Identifier* name() { \
542             if (!s_name) s_name = new Identifier("[[" ClassName ".prototype]]"); \
543             return s_name; \
544         }\
545     }; \
546     Identifier* ClassProto::s_name = nullptr; \
547     const ClassInfo ClassProto::info = { ClassName, nullptr, nullptr, nullptr };
548 
549 #define KJS_EMPTY_PROTOTYPE_WITH_PROTOTYPE(ClassName, ClassProto, ClassProtoProto) \
550     KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ClassProtoProto::self(exec))
551 
552 } // namespace
553 
554 #endif
555