|
|
|
// -*- 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 "khtmlpart_p.h"
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kparts/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, khtml::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->tqcontains( 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.tqunicode(), 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.tqunicode(), 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);
|
|
|
|
}
|