diff options
Diffstat (limited to 'tdehtml/ecma/kjs_binding.cpp')
-rw-r--r-- | tdehtml/ecma/kjs_binding.cpp | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/tdehtml/ecma/kjs_binding.cpp b/tdehtml/ecma/kjs_binding.cpp new file mode 100644 index 000000000..09961fbb5 --- /dev/null +++ b/tdehtml/ecma/kjs_binding.cpp @@ -0,0 +1,467 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2003 Harri Porten (porten@kde.org) + * Copyright (C) 2001-2003 David Faure (faure@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 + */ + +#include "kjs_binding.h" +#include "kjs_dom.h" + +#include "dom/dom_exception.h" +#include "dom/dom2_range.h" +#include "xml/dom2_eventsimpl.h" +#include "tdehtmlpart_p.h" + +#include <kdebug.h> +#include <tdeparts/browserextension.h> + +#include <assert.h> + +using namespace KJS; + +/* TODO: + * The catch all (...) clauses below shouldn't be necessary. + * But they helped to view for example www.faz.net in an stable manner. + * Those unknown exceptions should be treated as severe bugs and be fixed. + * + * these may be CSS exceptions - need to check - pmk + */ + +Value DOMObject::get(ExecState *exec, const Identifier &p) const +{ + Value result; + try { + result = tryGet(exec,p); + } + catch (DOM::DOMException e) { + // ### translate code into readable string ? + // ### oh, and s/QString/i18n or I18N_NOOP (the code in kjs uses I18N_NOOP... but where is it translated ?) + // and where does it appear to the user ? + Object err = Error::create(exec, GeneralError, TQString(TQString("DOM exception %1").arg(e.code)).local8Bit()); + exec->setException( err ); + result = Undefined(); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMObject::get()" << endl; + result = String("Unknown exception"); + } + + return result; +} + +void DOMObject::put(ExecState *exec, const Identifier &propertyName, + const Value &value, int attr) +{ + try { + tryPut(exec, propertyName, value, attr); + } + catch (DOM::DOMException e) { + Object err = Error::create(exec, GeneralError, TQString(TQString("DOM exception %1").arg(e.code)).local8Bit()); + exec->setException(err); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMObject::put()" << endl; + } +} + +void DOMObject::tryPut(ExecState *exec, const Identifier &propertyName, + const Value& value, int attr) +{ + static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->customizedDOMObject(this); + ObjectImp::put(exec,propertyName,value,attr); +} + +UString DOMObject::toString(ExecState *) const +{ + return "[object " + className() + "]"; +} + +Boolean DOMObject::hasInstance(ExecState *exec, const Value &value) +{ + if (value.type() != ObjectType) + return Boolean(false); + + Value prot = get(exec,prototypePropertyName); + if (prot.type() != ObjectType && prot.type() != NullType) { + Object err = Error::create(exec, TypeError, "Invalid prototype encountered " + "in instanceof operation."); + exec->setException(err); + return Boolean(false); + } + + Object v = Object(static_cast<ObjectImp*>(value.imp())); + while ((v = Object::dynamicCast(v.prototype())).imp()) { + if (v.imp() == prot.imp()) + return Boolean(true); + } + return Boolean(false); +} + + +Value DOMFunction::get(ExecState *exec, const Identifier &propertyName) const +{ + try { + return tryGet(exec, propertyName); + } + catch (DOM::DOMException e) { + Object err = Error::create(exec, GeneralError, TQString(TQString("DOM exception %1").arg(e.code)).local8Bit()); + exec->setException(err); + return Undefined(); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMFunction::get()" << endl; + return String("Unknown exception"); + } +} + +Value DOMFunction::call(ExecState *exec, Object &thisObj, const List &args) +{ + try { + return tryCall(exec, thisObj, args); + } + // pity there's no way to distinguish between these in JS code + // ### Look into setting prototypes of these & the use of instanceof so the exception + // type can be determined. See what other browsers do. + catch (DOM::DOMException e) { + Object err = Error::create(exec, GeneralError, TQString(TQString("DOM Exception %1").arg(e.code)).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (DOM::RangeException e) { + Object err = Error::create(exec, GeneralError, TQString(TQString("DOM Range Exception %1").arg(e.code)).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (DOM::CSSException e) { + Object err = Error::create(exec, GeneralError, TQString(TQString("CSS Exception %1").arg(e.code)).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (DOM::EventException e) { + Object err = Error::create(exec, GeneralError, TQString(TQString("DOM Event Exception %1").arg(e.code)).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMFunction::call()" << endl; + Object err = Error::create(exec, GeneralError, "Unknown exception"); + exec->setException(err); + return Undefined(); + } +} + +typedef TQPtrList<ScriptInterpreter> InterpreterList; +static InterpreterList *interpreterList; + +ScriptInterpreter::ScriptInterpreter( const Object &global, tdehtml::ChildFrame* frame ) + : Interpreter( global ), m_frame( frame ), m_domObjects(1021), + m_evt( 0L ), m_inlineCode(false), m_timerCallback(false) +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_frame << endl; +#endif + if ( !interpreterList ) + interpreterList = new InterpreterList; + interpreterList->append( this ); +} + +ScriptInterpreter::~ScriptInterpreter() +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_frame << endl; +#endif + assert( interpreterList && interpreterList->contains( this ) ); + interpreterList->remove( this ); + if ( interpreterList->isEmpty() ) { + delete interpreterList; + interpreterList = 0; + } +} + +void ScriptInterpreter::forgetDOMObject( void* objectHandle ) +{ + if( !interpreterList ) return; + + TQPtrListIterator<ScriptInterpreter> it( *interpreterList ); + while ( it.current() ) { + (*it)->deleteDOMObject( objectHandle ); + ++it; + } +} + +void ScriptInterpreter::mark() +{ + Interpreter::mark(); +#ifdef KJS_VERBOSE + kdDebug(6070) << "ScriptInterpreter::mark " << this << " marking " << m_customizedDomObjects.count() << " DOM objects" << endl; +#endif + TQPtrDictIterator<void> it( m_customizedDomObjects ); + for( ; it.current(); ++it ) + static_cast<DOMObject*>(it.currentKey())->mark(); +} + +KParts::ReadOnlyPart* ScriptInterpreter::part() const { + return m_frame->m_part; +} + +bool ScriptInterpreter::isWindowOpenAllowed() const +{ + if ( m_evt ) + { + int id = m_evt->handle()->id(); + bool eventOk = ( // mouse events + id == DOM::EventImpl::CLICK_EVENT || + id == DOM::EventImpl::MOUSEUP_EVENT || id == DOM::EventImpl::MOUSEDOWN_EVENT || + id == DOM::EventImpl::KHTML_ECMA_CLICK_EVENT || id == DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT || + // keyboard events + id == DOM::EventImpl::KEYDOWN_EVENT || id == DOM::EventImpl::KEYPRESS_EVENT || + id == DOM::EventImpl::KEYUP_EVENT || + // other accepted events + id == DOM::EventImpl::SELECT_EVENT || id == DOM::EventImpl::CHANGE_EVENT || + id == DOM::EventImpl::SUBMIT_EVENT ); + kdDebug(6070) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk << endl; + if (eventOk) + return true; + } else // no event + { + if ( m_inlineCode && !m_timerCallback ) + { + // This is the <a href="javascript:window.open('...')> case -> we let it through + return true; + kdDebug(6070) << "Window.open, smart policy, no event, inline code -> ok" << endl; + } + else // This is the <script>window.open(...)</script> case or a timer callback -> block it + kdDebug(6070) << "Window.open, smart policy, no event, <script> tag -> refused" << endl; + } + return false; +} + + +UString::UString(const TQString &d) +{ + unsigned int len = d.length(); + UChar *dat = new UChar[len]; + memcpy(dat, d.unicode(), len * sizeof(UChar)); + rep = UString::Rep::create(dat, len); +} + +UString::UString(const DOM::DOMString &d) +{ + if (d.isNull()) { + // we do a conversion here as null DOMStrings shouldn't cross + // the boundary to kjs. They should either be empty strings + // or explicitly converted to KJS::Null via getString(). + attach(&Rep::empty); + return; + } + + unsigned int len = d.length(); + UChar *dat = new UChar[len]; + memcpy(dat, d.unicode(), len * sizeof(UChar)); + rep = UString::Rep::create(dat, len); +} + +DOM::DOMString UString::string() const +{ + return DOM::DOMString((TQChar*) data(), size()); +} + +TQString UString::qstring() const +{ + return TQString((TQChar*) data(), size()); +} + +TQConstString UString::qconststring() const +{ + return TQConstString((TQChar*) data(), size()); +} + +DOM::DOMString Identifier::string() const +{ + return DOM::DOMString((TQChar*) data(), size()); +} + +TQString Identifier::qstring() const +{ + return TQString((TQChar*) data(), size()); +} + +DOM::Node KJS::toNode(const Value& val) +{ + Object obj = Object::dynamicCast(val); + if (!obj.isValid() || !obj.inherits(&DOMNode::info)) + return DOM::Node(); + + const DOMNode *dobj = static_cast<const DOMNode*>(obj.imp()); + return dobj->toNode(); +} + +Value KJS::getString(DOM::DOMString s) +{ + if (s.isNull()) + return Null(); + else + return String(s); +} + +TQVariant KJS::ValueToVariant(ExecState* exec, const Value &val) { + TQVariant res; + switch (val.type()) { + case BooleanType: + res = TQVariant(val.toBoolean(exec), 0); + break; + case NumberType: + res = TQVariant(val.toNumber(exec)); + break; + case StringType: + res = TQVariant(val.toString(exec).qstring()); + break; + default: + // everything else will be 'invalid' + break; + } + return res; +} + +class EmbedLiveConnect : public ObjectImp +{ + friend Value KJS::getLiveConnectValue(KParts::LiveConnectExtension *lc, const TQString & name, const int type, const TQString & value, int id); + EmbedLiveConnect(KParts::LiveConnectExtension *lc, UString n, KParts::LiveConnectExtension::Type t, int id); +public: + ~EmbedLiveConnect(); + + virtual Value get(ExecState *, const Identifier & prop) const; + virtual void put(ExecState * exec, const Identifier &prop, const Value & value, int=None); + virtual Value call(ExecState * exec, Object &, const List &args); + virtual bool implementsCall() const; + virtual bool toBoolean(ExecState *) const; + virtual Value toPrimitive(ExecState *exec, Type) const; + virtual UString toString(ExecState *) const; + +private: + EmbedLiveConnect(const EmbedLiveConnect &); + TQGuardedPtr<KParts::LiveConnectExtension> m_liveconnect; + UString name; + KParts::LiveConnectExtension::Type objtype; + unsigned long objid; +}; + +Value KJS::getLiveConnectValue(KParts::LiveConnectExtension *lc, const TQString & name, const int type, const TQString & value, int id) +{ + KParts::LiveConnectExtension::Type t=(KParts::LiveConnectExtension::Type)type; + switch(t) { + case KParts::LiveConnectExtension::TypeBool: { + bool ok; + int i = value.toInt(&ok); + if (ok) + return Boolean(i); + return Boolean(!strcasecmp(value.latin1(), "true")); + } + case KParts::LiveConnectExtension::TypeObject: + case KParts::LiveConnectExtension::TypeFunction: + return Value(new EmbedLiveConnect(lc, name, t, id)); + case KParts::LiveConnectExtension::TypeNumber: { + bool ok; + int i = value.toInt(&ok); + if (ok) + return Number(i); + else + return Number(value.toDouble(&ok)); + } + case KParts::LiveConnectExtension::TypeString: + return String(value); + case KParts::LiveConnectExtension::TypeVoid: + default: + return Undefined(); + } +} + +/* only with gcc > 3.4 KDE_NO_EXPORT */ +EmbedLiveConnect::EmbedLiveConnect(KParts::LiveConnectExtension *lc, UString n, KParts::LiveConnectExtension::Type t, int id) + : m_liveconnect (lc), name(n), objtype(t), objid(id) +{} + +/* only with gcc > 3.4 KDE_NO_EXPORT */ +EmbedLiveConnect::~EmbedLiveConnect() { + if (m_liveconnect) + m_liveconnect->unregister(objid); +} + +KDE_NO_EXPORT +Value EmbedLiveConnect::get(ExecState *, const Identifier & prop) const +{ + if (m_liveconnect) { + KParts::LiveConnectExtension::Type rettype; + TQString retval; + unsigned long retobjid; + if (m_liveconnect->get(objid, prop.qstring(), rettype, retobjid, retval)) + return getLiveConnectValue(m_liveconnect, prop.qstring(), rettype, retval, retobjid); + } + return Undefined(); +} + +KDE_NO_EXPORT +void EmbedLiveConnect::put(ExecState * exec, const Identifier &prop, const Value & value, int) +{ + if (m_liveconnect) + m_liveconnect->put(objid, prop.qstring(), value.toString(exec).qstring()); +} + +KDE_NO_EXPORT +bool EmbedLiveConnect::implementsCall() const { + return objtype == KParts::LiveConnectExtension::TypeFunction; +} + +KDE_NO_EXPORT +Value EmbedLiveConnect::call(ExecState *exec, Object&, const List &args) +{ + if (m_liveconnect) { + TQStringList qargs; + for (ListIterator i = args.begin(); i != args.end(); ++i) + qargs.append((*i).toString(exec).qstring()); + KParts::LiveConnectExtension::Type rtype; + TQString rval; + unsigned long robjid; + if (m_liveconnect->call(objid, name.qstring(), qargs, rtype, robjid, rval)) + return getLiveConnectValue(m_liveconnect, name.qstring(), rtype, rval, robjid); + } + return Undefined(); +} + +KDE_NO_EXPORT +bool EmbedLiveConnect::toBoolean(ExecState *) const { + return true; +} + +KDE_NO_EXPORT +Value EmbedLiveConnect::toPrimitive(ExecState *exec, Type) const { + return String(toString(exec)); +} + +KDE_NO_EXPORT +UString EmbedLiveConnect::toString(ExecState *) const { + TQString str; + const char *type = objtype == KParts::LiveConnectExtension::TypeFunction ? "Function" : "Object"; + str.sprintf("[object %s ref=%d]", type, (int) objid); + return UString(str); +} |