File indexing completed on 2024-04-28 15:23:07

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
0004  *  Copyright (C) 2003 Apple Computer, Inc.
0005  *  Copyright (C) 2007, 2008 Maksim Orlovich (maksim@kde.org)
0006  *
0007  *  This library is free software; you can redistribute it and/or
0008  *  modify it under the terms of the GNU Library General Public
0009  *  License as published by the Free Software Foundation; either
0010  *  version 2 of the License, or (at your option) any later version.
0011  *
0012  *  This library is distributed in the hope that it will be useful,
0013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  *  Library General Public License for more details.
0016  *
0017  *  You should have received a copy of the GNU Library General Public
0018  *  License along with this library; if not, write to the Free Software
0019  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0020  */
0021 
0022 #ifndef _KJS_BINDING_H_
0023 #define _KJS_BINDING_H_
0024 
0025 #include <khtml_export.h>
0026 #include <kjs/interpreter.h>
0027 #include <kjs/global.h>
0028 #include <wtf/HashMap.h>
0029 
0030 #include <dom/dom_node.h>
0031 #include <QVariant>
0032 #include <QHash>
0033 #include <QString>
0034 #include <kjs/lookup.h>
0035 #include <kjs/function.h>
0036 #include <kjs/JSVariableObject.h>
0037 #include <kjs/object_object.h>
0038 #include <misc/shared.h>
0039 
0040 #include <stdlib.h> // for abort
0041 
0042 #define KJS_CHECK_THIS( ClassName, theObj ) \
0043     if (!theObj || !theObj->inherits(&ClassName::info)) { \
0044         KJS::UString errMsg = "Attempt at calling a function that expects a "; \
0045         errMsg += ClassName::info.className; \
0046         errMsg += " on a "; \
0047         errMsg += theObj->className(); \
0048         KJS::JSObject *err = KJS::Error::create(exec, KJS::TypeError, errMsg.ascii()); \
0049         exec->setException(err); \
0050         return err; \
0051     }
0052 
0053 namespace KParts
0054 {
0055 class ReadOnlyPart;
0056 class LiveConnectExtension;
0057 }
0058 
0059 namespace khtml
0060 {
0061 class ChildFrame;
0062 }
0063 
0064 namespace KJS
0065 {
0066 
0067 // For the ecma debugger we provide our own conversion rather than the
0068 // use the native one, since using the toString
0069 // method on object can invoke code
0070 QString valueToString(KJS::JSValue *value);
0071 
0072 // Serializes an exception for human consumption.
0073 QString exceptionToString(ExecState *exec, JSValue *exception);
0074 
0075 /**
0076  * Base class for all objects in this binding. Doesn't manage exceptions any more
0077  */
0078 class DOMObject : public JSObject
0079 {
0080 protected:
0081     DOMObject() : JSObject() {}
0082     DOMObject(JSObject *proto) : JSObject(proto) {}
0083 public:
0084     bool shouldMark() const
0085     {
0086         return !_prop.isEmpty();
0087     }
0088     UString toString(ExecState *exec) const override;
0089 };
0090 
0091 /**
0092  * We inherit from Interpreter, to save a pointer to the HTML part
0093  * that the interpreter runs for.
0094  * The interpreter also stores the DOM object - >KJS::DOMObject cache.
0095  */
0096 class ScriptInterpreter : public Interpreter
0097 {
0098 public:
0099     ScriptInterpreter(JSGlobalObject *global, khtml::ChildFrame *frame);
0100     virtual ~ScriptInterpreter();
0101 
0102     // We need to keep track of wrappers in 2 ways:
0103     //  - we want the same wrapper for the same node (see #145775)
0104     //  - we want to drop all the references from this interpreter on clear, so
0105     //  wrappers don't stick around. Hence we have a global set and a per-interpreter one.
0106 
0107     // Reuses an existing wrapper, perhaps also updating the current map
0108     // to refer to it as well.
0109     DOMObject *getDOMObject(void *objectHandle)
0110     {
0111         DOMObject *existing = allDomObjects()->get(objectHandle);
0112         if (existing) {
0113             m_domObjects.set(objectHandle, existing);
0114         }
0115         return existing;
0116     }
0117 
0118     void putDOMObject(void *objectHandle, DOMObject *obj)
0119     {
0120         allDomObjects()->set(objectHandle, obj);
0121         m_domObjects.set(objectHandle, obj);
0122     }
0123 
0124     static void forgetDOMObject(void *objectHandle);
0125 
0126     void clear()
0127     {
0128         m_domObjects.clear(); // Global set will be cleared at GC time.
0129     }
0130 
0131     /**
0132      * Mark objects in the DOMObject cache.
0133      */
0134     void mark(bool isMain) override;
0135     KParts::ReadOnlyPart *part() const;
0136 
0137     int rtti() override
0138     {
0139         return 1;
0140     }
0141 
0142     /**
0143      * Set the event that is triggering the execution of a script, if any
0144      */
0145     void setCurrentEvent(DOM::Event *evt)
0146     {
0147         m_evt = evt;
0148     }
0149     void setInlineCode(bool inlineCode)
0150     {
0151         m_inlineCode = inlineCode;
0152     }
0153     void setProcessingTimerCallback(bool timerCallback)
0154     {
0155         m_timerCallback = timerCallback;
0156     }
0157     /**
0158      * "Smart" window.open policy
0159      */
0160     bool isWindowOpenAllowed() const;
0161 
0162     /**
0163      * CPU guard API. This should be used instead of Interpreter
0164      * methods as it manages the timeouts, including VG support
0165      */
0166     bool shouldInterruptScript() const override;
0167     void startCPUGuard();
0168     void stopCPUGuard();
0169 
0170     static void turnOffCPUGuard()
0171     {
0172         s_disableCPUGuard = true;
0173     }
0174 private:
0175     khtml::ChildFrame *m_frame;
0176     HashMap<void *, DOMObject *> m_domObjects;
0177     static HashMap<void *, DOMObject *> *s_allDomObjects;
0178     static HashMap<void *, DOMObject *> *allDomObjects()
0179     {
0180         if (!s_allDomObjects) {
0181             s_allDomObjects = new HashMap<void *, DOMObject *>();
0182         }
0183         return s_allDomObjects;
0184     }
0185 
0186     DOM::Event *m_evt;
0187     bool m_inlineCode;
0188     bool m_timerCallback;
0189     static bool s_disableCPUGuard;
0190 };
0191 
0192 /** Some templates to help make wrappers. example:
0193     class Foo: public DOMWrapperObject<DOM::FooImpl> {
0194     }
0195 
0196     ...
0197 
0198     getWrapper<Foo>(exec, someImpl);
0199 */
0200 template<typename Wrapper>
0201 JSValue *getWrapper(ExecState *exec, typename Wrapper::wrappedType *g)
0202 {
0203     DOMObject *ret = nullptr;
0204     if (!g) {
0205         return jsNull();
0206     }
0207 
0208     ScriptInterpreter *interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
0209     if ((ret = interp->getDOMObject(g))) {
0210         return ret;
0211     }
0212 
0213     ret = new Wrapper(exec, g);
0214     interp->putDOMObject(g, ret);
0215     return ret;
0216 }
0217 
0218 template<typename Wrapped>
0219 class DOMWrapperObject : public DOMObject
0220 {
0221 public:
0222     typedef Wrapped wrappedType;
0223     typedef DOMWrapperObject<Wrapped> WrapperBase;
0224 
0225     DOMWrapperObject(JSObject *proto, Wrapped *wrapee):
0226         DOMObject(proto), m_impl(wrapee)
0227     {}
0228 
0229     virtual ~DOMWrapperObject()
0230     {
0231         ScriptInterpreter::forgetDOMObject(m_impl.get());
0232     }
0233 
0234     bool toBoolean(ExecState *) const override
0235     {
0236         return true;
0237     }
0238 
0239     Wrapped *impl()
0240     {
0241         return m_impl.get();
0242     }
0243     const Wrapped *impl() const
0244     {
0245         return m_impl.get();
0246     }
0247 private:
0248     SharedPtr<Wrapped> m_impl;
0249 };
0250 
0251 /**
0252  A little helper for setting stuff up given an entry
0253 */
0254 template<class FuncImp, class ThisImp>
0255 inline void getSlotFromEntry(const HashEntry *entry, ThisImp *thisObj, PropertySlot &slot)
0256 {
0257     if (entry->attr & Function) {
0258         slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
0259     } else {
0260         slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
0261     }
0262 }
0263 
0264 /**
0265   Like getStaticPropertySlot but doesn't check the parent. Handy when there
0266   are both functions and values
0267  */
0268 template <class FuncImp, class ThisImp>
0269 inline bool getStaticOwnPropertySlot(const HashTable *table,
0270                                      ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot)
0271 {
0272     const HashEntry *entry = Lookup::findEntry(table, propertyName);
0273 
0274     if (!entry) { // not found, forward to parent
0275         return false;
0276     }
0277 
0278     if (entry->attr & Function) {
0279         slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
0280     } else {
0281         slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
0282     }
0283 
0284     return true;
0285 }
0286 
0287 /**
0288   Handler for local table-looked up things.
0289 */
0290 template<class ThisImp>
0291 inline bool getStaticOwnValueSlot(const HashTable *table,
0292                                   ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot)
0293 {
0294     const HashEntry *entry = Lookup::findEntry(table, propertyName);
0295     if (!entry) {
0296         return false;
0297     }
0298 
0299     assert(!(entry->attr & Function));
0300     slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
0301     return true;
0302 }
0303 
0304 /* Helper for the below*/
0305 template<class JSTypeImp>
0306 JSValue *indexGetterAdapter(ExecState *exec, JSObject *, unsigned, const PropertySlot &slot)
0307 {
0308     JSTypeImp *thisObj = static_cast<JSTypeImp *>(slot.slotBase());
0309     return thisObj->indexGetter(exec, slot.index());
0310 }
0311 
0312 /**
0313  Handler for index properties. Will call "length" method on the listObj
0314  to determine whether it's in range, and arrange to have indexGetter called
0315 */
0316 template<class ThisImp, class BaseObj>
0317 inline bool getIndexSlot(ThisImp *thisObj, const BaseObj &listObj,
0318                          const Identifier &propertyName, PropertySlot &slot)
0319 {
0320     bool ok;
0321     unsigned u = propertyName.toArrayIndex(&ok);
0322     if (ok && u < listObj.length()) {
0323         slot.setCustomIndex(thisObj, u, indexGetterAdapter<ThisImp>);
0324         return true;
0325     }
0326     return false;
0327 }
0328 
0329 /**
0330  Version that takes an external bound
0331 */
0332 template<class ThisImp>
0333 inline bool getIndexSlot(ThisImp *thisObj, unsigned lengthLimit,
0334                          const Identifier &propertyName, PropertySlot &slot)
0335 {
0336     bool ok;
0337     unsigned u = propertyName.toArrayIndex(&ok);
0338     if (ok && u < lengthLimit) {
0339         slot.setCustomIndex(thisObj, u, indexGetterAdapter<ThisImp>);
0340         return true;
0341     }
0342     return false;
0343 }
0344 
0345 template<class ThisImp>
0346 inline bool getIndexSlot(ThisImp *thisObj, int lengthLimit,
0347                          const Identifier &propertyName, PropertySlot &slot)
0348 {
0349     return getIndexSlot(thisObj, (unsigned)lengthLimit, propertyName, slot);
0350 }
0351 
0352 /**
0353  Version w/o the bounds check
0354 */
0355 template<class ThisImp>
0356 inline bool getIndexSlot(ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot)
0357 {
0358     bool ok;
0359     unsigned u = propertyName.toArrayIndex(&ok);
0360     if (ok) {
0361         slot.setCustomIndex(thisObj, u, indexGetterAdapter<ThisImp>);
0362         return true;
0363     }
0364     return false;
0365 }
0366 
0367 /* Helper for below */
0368 JSValue *valueGetterAdapter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot);
0369 
0370 /**
0371  This sets up the slot to return a particular JSValue*; unlike
0372  setValueSlot, it does not require there to be a location to point at
0373 */
0374 inline bool getImmediateValueSlot(JSObject *thisObj, JSValue *value, PropertySlot &slot)
0375 {
0376     slot.setCustomValue(thisObj, value, valueGetterAdapter);
0377     return true;
0378 }
0379 
0380 /**
0381  * Retrieve from cache, or create, a KJS object around a DOM object
0382  */
0383 template<class DOMObj, class KJSDOMObj>
0384 inline JSValue *cacheDOMObject(ExecState *exec, DOMObj *domObj)
0385 {
0386     DOMObject *ret;
0387     if (!domObj) {
0388         return jsNull();
0389     }
0390     ScriptInterpreter *interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
0391     if ((ret = interp->getDOMObject(domObj))) {
0392         return ret;
0393     } else {
0394         ret = new KJSDOMObj(exec, domObj);
0395         interp->putDOMObject(domObj, ret);
0396         return ret;
0397     }
0398 }
0399 
0400 /**
0401  * Convert an object to a Node. Returns 0 if not possible.
0402  */
0403 DOM::NodeImpl *toNode(JSValue *);
0404 /**
0405  *  Get a String object, or Null() if s is null
0406  */
0407 JSValue *getStringOrNull(DOM::DOMString s);
0408 
0409 /**
0410  * Null string if the value is null
0411  */
0412 DOM::DOMString valueToStringWithNullCheck(ExecState *exec, JSValue *v);
0413 
0414 /**
0415  * Convert a KJS value into a QVariant
0416  */
0417 QVariant ValueToVariant(ExecState *exec, JSValue *val);
0418 
0419 // Convert a DOM implementation exception code into a JavaScript exception in the execution state.
0420 void setDOMException(ExecState *exec, int DOMExceptionCode);
0421 
0422 // Helper class to call setDOMException on exit without adding lots of separate calls to that function.
0423 class DOMExceptionTranslator
0424 {
0425 public:
0426     explicit DOMExceptionTranslator(ExecState *exec) : m_exec(exec), m_code(0) { }
0427     ~DOMExceptionTranslator()
0428     {
0429         setDOMException(m_exec, m_code);
0430     }
0431     operator int &()
0432     {
0433         return m_code;
0434     }
0435     operator int *()
0436     {
0437         return &m_code;
0438     }
0439 
0440     bool triggered()
0441     {
0442         return m_code;
0443     }
0444 private:
0445     ExecState *m_exec;
0446     int m_code;
0447 };
0448 
0449 // convenience function
0450 inline JSCell *jsString(const QString &s)
0451 {
0452     return jsString(UString(s));
0453 }
0454 
0455 // This is used to create pseudo-constructor objects, like Mozillaish
0456 // Element, HTMLDocument, etc., which do not act like real constructors,
0457 // but do have the prototype property pointing to prototype of "instances"
0458 #define DEFINE_PSEUDO_CONSTRUCTOR(ClassName) \
0459     class ClassName : public DOMObject { \
0460     public: \
0461         ClassName(ExecState *); \
0462         virtual const ClassInfo* classInfo() const { return &info; } \
0463         static const ClassInfo info; \
0464         static JSObject* self(ExecState *exec); \
0465         virtual bool implementsHasInstance() const; \
0466     };
0467 
0468 #define IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProto) \
0469     const ClassInfo Class::info = { ClassName, nullptr, nullptr, nullptr }; \
0470     Class::Class(ExecState* exec): DOMObject(ParentProto) {\
0471         /* Since ProtoClass ctor might need us, make sure we're registered */ \
0472         exec->lexicalInterpreter()->globalObject()->put(exec, "[[" ClassName ".constructor]]", this, KJS::Internal | KJS::DontEnum); \
0473         JSObject* proto = ProtoClass::self(exec); \
0474         putDirect(exec->propertyNames().prototype, proto, DontDelete|ReadOnly); \
0475     }\
0476     JSObject* Class::self(ExecState *exec) { \
0477         return cacheGlobalObject<Class>(exec, "[[" ClassName ".constructor]]"); \
0478     } \
0479     bool Class::implementsHasInstance() const { \
0480         return true; \
0481     }
0482 
0483 #define IMPLEMENT_PSEUDO_CONSTRUCTOR(Class,ClassName,ProtoClass) \
0484     IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,exec->lexicalInterpreter()->builtinObjectPrototype())
0485 
0486 #define IMPLEMENT_PSEUDO_CONSTRUCTOR_WITH_PARENT(Class,ClassName,ProtoClass,ParentProtoClass) \
0487     IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProtoClass::self(exec))
0488 
0489 // This declares a constant table, which merely maps everything in its
0490 // table to its token value. Can be used as a prototype
0491 #define DEFINE_CONSTANT_TABLE(Class) \
0492     class Class : public DOMObject { \
0493     public: \
0494         Class(ExecState *exec): DOMObject(exec->lexicalInterpreter()->builtinObjectPrototype()) {} \
0495         \
0496         using KJS::JSObject::getOwnPropertySlot;\
0497         virtual bool getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot);\
0498         JSValue* getValueProperty(ExecState *exec, int token) const; \
0499         virtual const ClassInfo* classInfo() const { return &info; } \
0500         static const ClassInfo info; \
0501         static JSObject* self(ExecState *exec);\
0502         static Identifier* s_name; \
0503         static Identifier* name(); \
0504     };
0505 
0506 // Emits an implementation of a constant table
0507 #define IMPLEMENT_CONSTANT_TABLE(Class,ClassName) \
0508     bool Class::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) \
0509     { \
0510         return getStaticValueSlot<Class, DOMObject>(exec, &Class##Table, this, propertyName, slot);\
0511     }\
0512     JSValue* Class::getValueProperty(ExecState * /*exec*/, int token) const { \
0513         /* We use the token as the value to return directly*/ \
0514         return jsNumber((unsigned int)token); \
0515     }  \
0516     JSObject* Class::self(ExecState *exec) { \
0517         return cacheGlobalObject<Class>(exec,  *name()); \
0518     } \
0519     Identifier* Class::s_name = nullptr; \
0520     Identifier* Class::name() { \
0521         if (!s_name) s_name = new Identifier("[[" ClassName ".constant_table]]"); \
0522         return s_name; \
0523     } \
0524     const ClassInfo Class::info = { ClassName, nullptr, &Class##Table, nullptr };
0525 
0526 // cacheGlobalObject<> is not in namespace KJS - need to use ::cacheGlobalObject<>
0527 #define KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ProtoCode) \
0528     class ClassProto : public JSObject { \
0529         friend JSObject* KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \
0530     public: \
0531         static JSObject* self(ExecState *exec) { \
0532             return KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(exec, *name()); \
0533         } \
0534         virtual const ClassInfo *classInfo() const { return &info; } \
0535         static const ClassInfo info; \
0536     protected: \
0537         ClassProto( ExecState *exec ) \
0538             : JSObject( ProtoCode ) {} \
0539         \
0540         static Identifier* s_name; \
0541         static Identifier* name() { \
0542             if (!s_name) s_name = new Identifier("[[" ClassName ".prototype]]"); \
0543             return s_name; \
0544         }\
0545     }; \
0546     Identifier* ClassProto::s_name = nullptr; \
0547     const ClassInfo ClassProto::info = { ClassName, nullptr, nullptr, nullptr };
0548 
0549 #define KJS_EMPTY_PROTOTYPE_WITH_PROTOTYPE(ClassName, ClassProto, ClassProtoProto) \
0550     KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ClassProtoProto::self(exec))
0551 
0552 } // namespace
0553 
0554 #endif