File indexing completed on 2024-12-01 09:50:27
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