diff options
Diffstat (limited to 'khtml/ecma/kjs_binding.h')
-rw-r--r-- | khtml/ecma/kjs_binding.h | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/khtml/ecma/kjs_binding.h b/khtml/ecma/kjs_binding.h new file mode 100644 index 000000000..4b49e866b --- /dev/null +++ b/khtml/ecma/kjs_binding.h @@ -0,0 +1,409 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2003 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _KJS_BINDING_H_ +#define _KJS_BINDING_H_ + +#include <kjs/interpreter.h> +#include <kjs/function_object.h> /// for FunctionPrototypeImp + +#include <dom/dom_node.h> +#include <qvariant.h> +#include <qptrdict.h> +#include <kurl.h> +#include <kjs/lookup.h> + +namespace KParts { + class ReadOnlyPart; + class LiveConnectExtension; +} + +namespace khtml { + class ChildFrame; +} + +namespace KJS { + + /** + * Base class for all objects in this binding - get() and put() run + * tryGet() and tryPut() respectively, and catch exceptions if they + * occur. + */ + class DOMObject : public ObjectImp { + public: + DOMObject(const Object &proto) : ObjectImp(proto) {} + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const + { return ObjectImp::get(exec, propertyName); } + virtual bool implementsHasInstance() const { return true; } + virtual Boolean hasInstance(ExecState *exec, const Value &value); + virtual void put(ExecState *exec, const Identifier &propertyName, + const Value &value, int attr = None); + virtual void tryPut(ExecState *exec, const Identifier &propertyName, + const Value& value, int attr = None); + + virtual UString toString(ExecState *exec) const; + }; + + /** + * Base class for all functions in this binding - get() and call() run + * tryGet() and tryCall() respectively, and catch exceptions if they + * occur. + */ + class DOMFunction : public InternalFunctionImp { + public: + DOMFunction(ExecState* exec) : InternalFunctionImp( + static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp()) + ) {} + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const + { return ObjectImp::get(exec, propertyName); } + + virtual bool implementsCall() const { return true; } + virtual Value call(ExecState *exec, Object &thisObj, const List &args); + + virtual Value tryCall(ExecState *exec, Object &thisObj, const List&args) + { return ObjectImp::call(exec, thisObj, args); } + virtual bool toBoolean(ExecState *) const { return true; } + }; + + /** + * We inherit from Interpreter, to save a pointer to the HTML part + * that the interpreter runs for. + * The interpreter also stores the DOM object - >KJS::DOMObject cache. + */ + class KDE_EXPORT ScriptInterpreter : public Interpreter + { + public: + ScriptInterpreter( const Object &global, khtml::ChildFrame* frame ); + virtual ~ScriptInterpreter(); + + DOMObject* getDOMObject( void* objectHandle ) const { + return m_domObjects[objectHandle]; + } + void putDOMObject( void* objectHandle, DOMObject* obj ) { + m_domObjects.insert( objectHandle, obj ); + } + void customizedDOMObject( DOMObject* obj ) { + m_customizedDomObjects.replace( obj, this ); + } + bool deleteDOMObject( void* objectHandle ) { + DOMObject* obj = m_domObjects.take( objectHandle ); + if (obj) { + m_customizedDomObjects.remove( obj ); + return true; + } + else + return false; + } + void clear() { + m_customizedDomObjects.clear(); + m_domObjects.clear(); + } + /** + * Static method. Makes all interpreters forget about the object + */ + static void forgetDOMObject( void* objectHandle ); + + /** + * Mark objects in the DOMObject cache. + */ + virtual void mark(); + KParts::ReadOnlyPart* part() const; + + virtual int rtti() { return 1; } + + /** + * Set the event that is triggering the execution of a script, if any + */ + void setCurrentEvent( DOM::Event *evt ) { m_evt = evt; } + void setInlineCode( bool inlineCode ) { m_inlineCode = inlineCode; } + void setProcessingTimerCallback( bool timerCallback ) { m_timerCallback = timerCallback; } + /** + * "Smart" window.open policy + */ + bool isWindowOpenAllowed() const; + + private: + khtml::ChildFrame* m_frame; + QPtrDict<DOMObject> m_domObjects; + QPtrDict<void> m_customizedDomObjects; //Objects which had custom properties set, + //and should not be GC'd. key is DOMObject* + DOM::Event *m_evt; + bool m_inlineCode; + bool m_timerCallback; + }; + /** + * Retrieve from cache, or create, a KJS object around a DOM object + */ + template<class DOMObj, class KJSDOMObj> + inline Value cacheDOMObject(ExecState *exec, DOMObj domObj) + { + DOMObject *ret; + if (domObj.isNull()) + return Null(); + ScriptInterpreter* interp = static_cast<ScriptInterpreter *>(exec->interpreter()); + if ((ret = interp->getDOMObject(domObj.handle()))) + return Value(ret); + else { + ret = new KJSDOMObj(exec, domObj); + interp->putDOMObject(domObj.handle(),ret); + return Value(ret); + } + } + + /** + * Convert an object to a Node. Returns a null Node if not possible. + */ + DOM::Node toNode(const Value&); + /** + * Get a String object, or Null() if s is null + */ + Value getString(DOM::DOMString s); + + /** + * Convery a KJS value into a QVariant + */ + QVariant ValueToVariant(ExecState* exec, const Value& val); + + /** + * We need a modified version of lookupGet because + * we call tryGet instead of get, in DOMObjects. + */ + template <class FuncImp, class ThisImp, class ParentImp> + inline Value DOMObjectLookupGet(ExecState *exec, const Identifier &propertyName, + const HashTable* table, const ThisImp* thisObj) + { + const HashEntry* entry = Lookup::findEntry(table, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::tryGet(exec, propertyName); + + if (entry->attr & Function) { + return lookupOrCreateFunction<FuncImp>(exec, propertyName, thisObj, entry->value, entry->params, entry->attr); + } + return thisObj->getValueProperty(exec, entry->value); + } + + /** + * Simplified version of DOMObjectLookupGet in case there are no + * functions, only "values". + */ + template <class ThisImp, class ParentImp> + inline Value DOMObjectLookupGetValue(ExecState *exec, const Identifier &propertyName, + const HashTable* table, const ThisImp* thisObj) + { + const HashEntry* entry = Lookup::findEntry(table, propertyName); + + if (!entry) // not found, forward to parent + return thisObj->ParentImp::tryGet(exec, propertyName); + + if (entry->attr & Function) + fprintf(stderr, "Function bit set! Shouldn't happen in lookupValue!\n" ); + return thisObj->getValueProperty(exec, entry->value); + } + + /** + * We need a modified version of lookupPut because + * we call tryPut instead of put, in DOMObjects. + */ + template <class ThisImp, class ParentImp> + inline void DOMObjectLookupPut(ExecState *exec, const Identifier &propertyName, + const Value& value, int attr, + const HashTable* table, ThisImp* thisObj) + { + const HashEntry* entry = Lookup::findEntry(table, propertyName); + + if (!entry) // not found: forward to parent + thisObj->ParentImp::tryPut(exec, propertyName, value, attr); + else if (entry->attr & Function) // function: put as override property + thisObj->ObjectImp::put(exec, propertyName, value, attr); + else if (entry->attr & ReadOnly) // readonly! Can't put! +#ifdef KJS_VERBOSE + fprintf(stderr,"WARNING: Attempt to change value of readonly property '%s'\n",propertyName.ascii()); +#else + ; // do nothing +#endif + else + thisObj->putValueProperty(exec, entry->value, value, attr); + } + +// Versions of prototype functions that properly support instanceof, +// and are compatible with trunk. +#define KJS_DEFINE_PROTOTYPE_IMP(ClassProto,ProtoCode) \ + class ClassProto : public ObjectImp { \ + friend Object cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \ + public: \ + static Object self(ExecState *exec); \ + virtual const ClassInfo *classInfo() const { return &info; } \ + static const ClassInfo info; \ + Value get(ExecState *exec, const Identifier &propertyName) const; \ + protected: \ + ClassProto( ExecState *exec ) \ + : ObjectImp( ProtoCode ) {} \ + \ + static Identifier* s_name; \ + static Identifier* name(); \ + }; + +#define KJS_DEFINE_PROTOTYPE(ClassProto) \ + KJS_DEFINE_PROTOTYPE_IMP(ClassProto, exec->interpreter()->builtinObjectPrototype()) + +#define KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(ClassProto, ClassProtoProto) \ + KJS_DEFINE_PROTOTYPE_IMP(ClassProto, ClassProtoProto::self(exec)) + +#define KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ProtoCode) \ + class ClassProto : public ObjectImp { \ + friend Object cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \ + public: \ + static Object self(ExecState *exec) { \ + return cacheGlobalObject<ClassProto>(exec, *name()); \ + } \ + virtual const ClassInfo *classInfo() const { return &info; } \ + static const ClassInfo info; \ + protected: \ + ClassProto( ExecState *exec ) \ + : ObjectImp( ProtoCode ) {} \ + \ + static Identifier* s_name; \ + static Identifier* name() { \ + if (!s_name) s_name = new Identifier("[[" ClassName ".prototype]]"); \ + return s_name; \ + }\ + }; \ + Identifier* ClassProto::s_name = 0; \ + const ClassInfo ClassProto::info = { ClassName, 0, 0, 0 }; + +#define KJS_EMPTY_PROTOTYPE_WITH_PROTOTYPE(ClassName, ClassProto, ClassProtoProto) \ + KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ClassProtoProto::self(exec)) + +//### this doesn't implement hasProperty, but stuff in lookup.h didn't +//either (just did the forward) +#define KJS_IMPLEMENT_PROTOTYPE(ClassName, ClassProto, ClassFunc) \ + const ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 }; \ + Identifier* ClassProto::s_name = 0; \ + Object ClassProto::self(ExecState *exec) \ + { \ + return cacheGlobalObject<ClassProto>(exec, *name()); \ + } \ + Value ClassProto::get(ExecState *exec, const Identifier &propertyName) const \ + { \ + /*fprintf( stderr, "%sProto::get(%s) [in macro, no parent]\n", info.className, propertyName.ascii());*/ \ + return lookupGetFunction<ClassFunc,ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \ + } \ + Identifier* ClassProto::name() \ + { \ + if (!s_name) s_name = new Identifier("[[" ClassName ".prototype]]"); \ + return s_name; \ + } + + // Modified version of IMPLEMENT_PROTOFUNC, to use DOMFunction and tryCall +#define IMPLEMENT_PROTOFUNC_DOM(ClassFunc) \ + class ClassFunc : public DOMFunction { \ + public: \ + ClassFunc(ExecState *exec, int i, int len) \ + : DOMFunction( exec ), id(i) { \ + Value protect(this); \ + put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum); \ + } \ + /** You need to implement that one */ \ + virtual Value tryCall(ExecState *exec, Object &thisObj, const List &args); \ + private: \ + int id; \ + }; + + Value getLiveConnectValue(KParts::LiveConnectExtension *lc, const QString & name, const int type, const QString & value, int id); + + +// This is used to create pseudo-constructor objects, like Mozillaish +// Element, HTMLDocument, etc., which do not act like real constructors, +// but do have the prototype property pointing to prototype of "instances" +#define DEFINE_PSEUDO_CONSTRUCTOR(ClassName) \ + class ClassName : public DOMObject { \ + public: \ + ClassName(ExecState *); \ + virtual const ClassInfo* classInfo() const { return &info; } \ + static const ClassInfo info; \ + static Object self(ExecState *exec); \ + }; + +#define IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProto) \ + const ClassInfo Class::info = { ClassName, 0, 0, 0 }; \ + Class::Class(ExecState* exec): DOMObject(ParentProto) {\ + Object proto = ProtoClass::self(exec); \ + putDirect(prototypePropertyName, proto.imp(), DontDelete|ReadOnly); \ + }\ + Object Class::self(ExecState *exec) { \ + return Object(cacheGlobalObject<Class>(exec, "[[" ClassName ".constructor]]")); \ + } + +#define IMPLEMENT_PSEUDO_CONSTRUCTOR(Class,ClassName,ProtoClass) \ + IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,exec->lexicalInterpreter()->builtinObjectPrototype()) + +#define IMPLEMENT_PSEUDO_CONSTRUCTOR_WITH_PARENT(Class,ClassName,ProtoClass,ParentProtoClass) \ + IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProtoClass::self(exec)) + +// This declares a constant table, which merely maps everything in its +// table to its token value. Can be used as a prototype +#define DEFINE_CONSTANT_TABLE(Class) \ + class Class : public DOMObject { \ + public: \ + Class(ExecState *exec): DOMObject(exec->lexicalInterpreter()->builtinObjectPrototype()) {} \ + \ + virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const;\ + Value getValueProperty(ExecState * /*exec*/, int token) const; \ + virtual const ClassInfo* classInfo() const { return &info; } \ + static const ClassInfo info; \ + static Object self(ExecState *exec);\ + static Identifier* s_name; \ + static Identifier* name(); \ + }; + +// Emits an implementation of a constant table +#define IMPLEMENT_CONSTANT_TABLE(Class,ClassName) \ + Value Class::tryGet(ExecState *exec, const Identifier &propertyName) const { \ + return DOMObjectLookupGetValue<Class, DOMObject>(exec, propertyName, &Class##Table, this);\ + } \ + Value Class::getValueProperty(ExecState * /*exec*/, int token) const { \ + /* We use the token as the value to return directly*/ \ + return Number((unsigned int)token); \ + } \ + Object Class::self(ExecState *exec) { \ + return cacheGlobalObject<Class>(exec, *name()); \ + } \ + Identifier* Class::s_name = 0; \ + Identifier* Class::name() { \ + if (!s_name) s_name = new Identifier("[[" ClassName ".constant_table]]"); \ + return s_name; \ + } \ + const ClassInfo Class::info = { ClassName, 0, &Class##Table, 0 }; + + +// Hide some of the stuff in lookup.h.. +#undef PUBLIC_DEFINE_PROTOTYPE +#undef DEFINE_PROTOTYPE +#undef IMPLEMENT_PROTOTYPE +#undef PUBLIC_IMPLEMENT_PROTOTYPE +#undef IMPLEMENT_PROTOTYPE_WITH_PARENT + +} // namespace + +#endif |