📄 kjs_binding.h
字号:
// -*- 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 Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _KJS_BINDING_H_
#define _KJS_BINDING_H_
#include <kjs/interpreter.h>
#include <dom/dom_node.h>
#include <dom/dom_doc.h>
#include <qvariant.h>
#include <qptrdict.h>
#include <kurl.h>
#include <kjs/lookup.h>
#include <kjs/protect.h>
#if APPLE_CHANGES && !NOKIA_CHANGES
#include <JavaScriptCore/runtime.h>
#endif
class KHTMLPart;
#if NOKIA_CHANGES
#define lengthPropertyName KJS::lengthProperty()
#define messagePropertyName KJS::messageProperty()
#endif
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) {}
DOMObject() : ObjectImp() {}
virtual Value get(ExecState *exec, const Identifier &propertyName) const;
virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const
{ return ObjectImp::get(exec, propertyName); }
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)
{ ObjectImp::put(exec,propertyName,value,attr); }
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 ObjectImp {
public:
DOMFunction() : 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 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; }
virtual Value toPrimitive(ExecState *exec, Type) const { return String(toString(exec)); }
virtual UString toString(ExecState *) const { return UString("[function]"); }
};
/**
* 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 ScriptInterpreter : public Interpreter
{
public:
ScriptInterpreter( const Object &global, KHTMLPart* part );
virtual ~ScriptInterpreter();
static DOMObject* getDOMObject( void* objectHandle ) {
return domObjects()[objectHandle];
}
static void putDOMObject( void* objectHandle, DOMObject* obj ) {
domObjects().insert( objectHandle, obj );
}
static bool deleteDOMObject( void* objectHandle ) {
return domObjects().remove( objectHandle );
}
static DOMObject* getDOMObjectForDocument( DOM::DocumentImpl* documentHandle, void *objectHandle );
static void putDOMObjectForDocument( DOM::DocumentImpl* documentHandle, void *objectHandle, DOMObject *obj );
static bool deleteDOMObjectsForDocument( DOM::DocumentImpl* documentHandle );
/**
* Static method. Makes all interpreters forget about the object
*/
static void forgetDOMObject( void* objectHandle );
static void forgetDOMObjectsForDocument( DOM::DocumentImpl* documentHandle );
static void updateDOMObjectDocument(void *objectHandle, DOM::DocumentImpl *oldDoc, DOM::DocumentImpl *newDoc);
KHTMLPart* part() const { return m_part; }
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 wasRunByUserGesture() const;
virtual void mark();
DOM::Event *getCurrentEvent() const { return m_evt; }
#if APPLE_CHANGES && !NOKIA_CHANGES
virtual bool isGlobalObject(const Value &v);
virtual Interpreter *interpreterForGlobalObject (const ValueImp *imp);
virtual bool isSafeScript (const Interpreter *target);
virtual void *createLanguageInstanceForValue (ExecState *exec, Bindings::Instance::BindingLanguage language, const Object &value, const Bindings::RootObject *origin, const Bindings::RootObject *current);
void *createObjcInstanceForValue (ExecState *exec, const Object &value, const Bindings::RootObject *origin, const Bindings::RootObject *current);
#endif
private:
KHTMLPart* m_part;
static QPtrDict<DOMObject> &domObjects();
static QPtrDict<QPtrDict<DOMObject> > &domObjectsPerDocument();
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->dynamicInterpreter());
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 getStringOrNull(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);
#ifndef NOKIA_CHANGES
if (entry->attr & Function)
fprintf(stderr, "Function bit set! Shouldn't happen in lookupValue!\n" );
#endif
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,"Attempt to change value of readonly property '%s'\n",propertyName.ascii());
#else
; // do nothing
#endif
else
thisObj->putValue(exec, entry->value, value, attr);
}
/**
* This template method retrieves or create an object that is unique
* (for a given interpreter) The first time this is called (for a given
* property name), the Object will be constructed, and set as a property
* of the interpreter's global object. Later calls will simply retrieve
* that cached object. Note that the object constructor must take 1 argument, exec.
*/
template <class ClassCtor>
inline Object cacheGlobalObject(ExecState *exec, const Identifier &propertyName)
{
ValueImp *obj = static_cast<ObjectImp*>(exec->lexicalInterpreter()->globalObject().imp())->getDirect(propertyName);
if (obj)
return Object::dynamicCast(Value(obj));
else
{
Object newObject(new ClassCtor(exec));
exec->lexicalInterpreter()->globalObject().put(exec, propertyName, newObject, Internal);
return newObject;
}
}
/**
* Helpers to define prototype objects (each of which simply implements
* the functions for a type of objects).
* Sorry for this not being very readable, but it actually saves much copy-n-paste.
* ParentProto is not our base class, it's the object we use as fallback.
* The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.),
* not one in each derived class. So we link the (unique) prototypes between them.
*
* Using those macros is very simple: define the hashtable (e.g. "DOMNodeProtoTable"), then
* DEFINE_PROTOTYPE("DOMNode",DOMNodeProto)
* IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc)
* IMPLEMENT_PROTOTYPE(DOMNodeProto,DOMNodeProtoFunc)
* and use DOMNodeProto::self(exec) as prototype in the DOMNode constructor.
* If the prototype has a "parent prototype", e.g. DOMElementProto falls back on DOMNodeProto,
* then the last line will use IMPLEMENT_PROTOTYPE_WITH_PARENT, with DOMNodeProto as last argument.
*/
#define DEFINE_PROTOTYPE(ClassName,ClassProto) \
namespace KJS { \
class ClassProto : public ObjectImp { \
friend Object cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \
public: \
static Object self(ExecState *exec) \
{ \
return cacheGlobalObject<ClassProto>( exec, "[[" ClassName ".prototype]]" ); \
} \
protected: \
ClassProto( ExecState *exec ) \
: ObjectImp( exec->lexicalInterpreter()->builtinObjectPrototype() ) {} \
\
public: \
virtual const ClassInfo *classInfo() const { return &info; } \
static const ClassInfo info; \
Value get(ExecState *exec, const Identifier &propertyName) const; \
bool hasProperty(ExecState *exec, const Identifier &propertyName) const; \
}; \
const ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 }; \
};
#define IMPLEMENT_PROTOTYPE(ClassProto,ClassFunc) \
Value KJS::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 ); \
} \
bool KJS::ClassProto::hasProperty(ExecState *exec, const Identifier &propertyName) const \
{ /*stupid but we need this to have a common macro for the declaration*/ \
return ObjectImp::hasProperty(exec, propertyName); \
}
#define IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassFunc,ParentProto) \
Value KJS::ClassProto::get(ExecState *exec, const Identifier &propertyName) const \
{ \
/*fprintf( stderr, "%sProto::get(%s) [in macro]\n", info.className, propertyName.ascii());*/ \
Value val = lookupGetFunction<ClassFunc,ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \
if ( val.type() != UndefinedType ) return val; \
/* Not found -> forward request to "parent" prototype */ \
return ParentProto::self(exec).get( exec, propertyName ); \
} \
bool KJS::ClassProto::hasProperty(ExecState *exec, const Identifier &propertyName) const \
{ \
if (ObjectImp::hasProperty(exec, propertyName)) \
return true; \
return ParentProto::self(exec).hasProperty(exec, propertyName); \
}
#define IMPLEMENT_PROTOFUNC(ClassFunc) \
namespace KJS { \
class ClassFunc : public DOMFunction { \
public: \
ClassFunc(ExecState *exec, int i, int len) \
: DOMFunction( /*proto? */ ), 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; \
}; \
};
}; // namespace
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -