kjs_binding.cpp

00001 // -*- c-basic-offset: 2 -*-
00002 /*
00003  *  This file is part of the KDE libraries
00004  *  Copyright (C) 1999-2003 Harri Porten (porten@kde.org)
00005  *  Copyright (C) 2001-2003 David Faure (faure@kde.org)
00006  *  Copyright (C) 2003 Apple Computer, Inc.
00007  *
00008  *  This library is free software; you can redistribute it and/or
00009  *  modify it under the terms of the GNU Library General Public
00010  *  License as published by the Free Software Foundation; either
00011  *  version 2 of the License, or (at your option) any later version.
00012  *
00013  *  This library is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  *  Library General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU Library General Public
00019  *  License along with this library; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  */
00022 
00023 #include "kjs_binding.h"
00024 #include "kjs_dom.h"
00025 
00026 #include "dom/dom_exception.h"
00027 #include "dom/dom2_range.h"
00028 #include "xml/dom2_eventsimpl.h"
00029 #include "khtmlpart_p.h"
00030 
00031 #include <kdebug.h>
00032 #include <kparts/browserextension.h>
00033 
00034 #include <assert.h>
00035 
00036 using namespace KJS;
00037 
00038 /* TODO:
00039  * The catch all (...) clauses below shouldn't be necessary.
00040  * But they helped to view for example www.faz.net in an stable manner.
00041  * Those unknown exceptions should be treated as severe bugs and be fixed.
00042  *
00043  * these may be CSS exceptions - need to check - pmk
00044  */
00045 
00046 Value DOMObject::get(ExecState *exec, const Identifier &p) const
00047 {
00048   Value result;
00049   try {
00050     result = tryGet(exec,p);
00051   }
00052   catch (DOM::DOMException e) {
00053     // ### translate code into readable string ?
00054     // ### oh, and s/QString/i18n or I18N_NOOP (the code in kjs uses I18N_NOOP... but where is it translated ?)
00055     //     and where does it appear to the user ?
00056     Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit());
00057     exec->setException( err );
00058     result = Undefined();
00059   }
00060   catch (...) {
00061     kdError(6070) << "Unknown exception in DOMObject::get()" << endl;
00062     result = String("Unknown exception");
00063   }
00064 
00065   return result;
00066 }
00067 
00068 void DOMObject::put(ExecState *exec, const Identifier &propertyName,
00069                     const Value &value, int attr)
00070 {
00071   try {
00072     tryPut(exec, propertyName, value, attr);
00073   }
00074   catch (DOM::DOMException e) {
00075     Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit());
00076     exec->setException(err);
00077   }
00078   catch (...) {
00079     kdError(6070) << "Unknown exception in DOMObject::put()" << endl;
00080   }
00081 }
00082 
00083 void DOMObject::tryPut(ExecState *exec, const Identifier &propertyName,
00084                         const Value& value, int attr)
00085 {
00086     static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->customizedDOMObject(this);
00087     ObjectImp::put(exec,propertyName,value,attr);
00088 }
00089 
00090 UString DOMObject::toString(ExecState *) const
00091 {
00092   return "[object " + className() + "]";
00093 }
00094 
00095 Boolean DOMObject::hasInstance(ExecState *exec, const Value &value)
00096 {
00097   if (value.type() != ObjectType)
00098     return Boolean(false);
00099 
00100   Value prot = get(exec,prototypePropertyName);
00101   if (prot.type() != ObjectType && prot.type() != NullType) {
00102     Object err = Error::create(exec, TypeError, "Invalid prototype encountered "
00103                                "in instanceof operation.");
00104     exec->setException(err);
00105     return Boolean(false);
00106   }
00107 
00108   Object v = Object(static_cast<ObjectImp*>(value.imp()));
00109   while ((v = Object::dynamicCast(v.prototype())).imp()) {
00110     if (v.imp() == prot.imp())
00111       return Boolean(true);
00112   }
00113   return Boolean(false);
00114 }
00115 
00116 
00117 Value DOMFunction::get(ExecState *exec, const Identifier &propertyName) const
00118 {
00119   try {
00120     return tryGet(exec, propertyName);
00121   }
00122   catch (DOM::DOMException e) {
00123     Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit());
00124     exec->setException(err);
00125     return Undefined();
00126   }
00127   catch (...) {
00128     kdError(6070) << "Unknown exception in DOMFunction::get()" << endl;
00129     return String("Unknown exception");
00130   }
00131 }
00132 
00133 Value DOMFunction::call(ExecState *exec, Object &thisObj, const List &args)
00134 {
00135   try {
00136     return tryCall(exec, thisObj, args);
00137   }
00138   // pity there's no way to distinguish between these in JS code
00139   // ### Look into setting prototypes of these & the use of instanceof so the exception
00140   // type can be determined. See what other browsers do.
00141   catch (DOM::DOMException e) {
00142     Object err = Error::create(exec, GeneralError, QString("DOM Exception %1").arg(e.code).local8Bit());
00143     err.put(exec, "code", Number(e.code));
00144     exec->setException(err);
00145     return Undefined();
00146   }
00147   catch (DOM::RangeException e) {
00148     Object err = Error::create(exec, GeneralError, QString("DOM Range Exception %1").arg(e.code).local8Bit());
00149     err.put(exec, "code", Number(e.code));
00150     exec->setException(err);
00151     return Undefined();
00152   }
00153   catch (DOM::CSSException e) {
00154     Object err = Error::create(exec, GeneralError, QString("CSS Exception %1").arg(e.code).local8Bit());
00155     err.put(exec, "code", Number(e.code));
00156     exec->setException(err);
00157     return Undefined();
00158   }
00159   catch (DOM::EventException e) {
00160     Object err = Error::create(exec, GeneralError, QString("DOM Event Exception %1").arg(e.code).local8Bit());
00161     err.put(exec, "code", Number(e.code));
00162     exec->setException(err);
00163     return Undefined();
00164   }
00165   catch (...) {
00166     kdError(6070) << "Unknown exception in DOMFunction::call()" << endl;
00167     Object err = Error::create(exec, GeneralError, "Unknown exception");
00168     exec->setException(err);
00169     return Undefined();
00170   }
00171 }
00172 
00173 typedef QPtrList<ScriptInterpreter> InterpreterList;
00174 static InterpreterList *interpreterList;
00175 
00176 ScriptInterpreter::ScriptInterpreter( const Object &global, khtml::ChildFrame* frame )
00177   : Interpreter( global ), m_frame( frame ), m_domObjects(1021),
00178     m_evt( 0L ), m_inlineCode(false), m_timerCallback(false)
00179 {
00180 #ifdef KJS_VERBOSE
00181   kdDebug(6070) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_frame << endl;
00182 #endif
00183   if ( !interpreterList )
00184     interpreterList = new InterpreterList;
00185   interpreterList->append( this );
00186 }
00187 
00188 ScriptInterpreter::~ScriptInterpreter()
00189 {
00190 #ifdef KJS_VERBOSE
00191   kdDebug(6070) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_frame << endl;
00192 #endif
00193   assert( interpreterList && interpreterList->contains( this ) );
00194   interpreterList->remove( this );
00195   if ( interpreterList->isEmpty() ) {
00196     delete interpreterList;
00197     interpreterList = 0;
00198   }
00199 }
00200 
00201 void ScriptInterpreter::forgetDOMObject( void* objectHandle )
00202 {
00203   if( !interpreterList ) return;
00204 
00205   QPtrListIterator<ScriptInterpreter> it( *interpreterList );
00206   while ( it.current() ) {
00207     (*it)->deleteDOMObject( objectHandle );
00208     ++it;
00209   }
00210 }
00211 
00212 void ScriptInterpreter::mark()
00213 {
00214   Interpreter::mark();
00215 #ifdef KJS_VERBOSE
00216   kdDebug(6070) << "ScriptInterpreter::mark " << this << " marking " << m_customizedDomObjects.count() << " DOM objects" << endl;
00217 #endif
00218   QPtrDictIterator<void> it( m_customizedDomObjects );
00219   for( ; it.current(); ++it )
00220     static_cast<DOMObject*>(it.currentKey())->mark();
00221 }
00222 
00223 KParts::ReadOnlyPart* ScriptInterpreter::part() const {
00224     return m_frame->m_part;
00225 }
00226 
00227 bool ScriptInterpreter::isWindowOpenAllowed() const
00228 {
00229   if ( m_evt )
00230   {
00231     int id = m_evt->handle()->id();
00232     bool eventOk = ( // mouse events
00233       id == DOM::EventImpl::CLICK_EVENT ||
00234       id == DOM::EventImpl::MOUSEUP_EVENT || id == DOM::EventImpl::MOUSEDOWN_EVENT ||
00235       id == DOM::EventImpl::KHTML_ECMA_CLICK_EVENT || id == DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT ||
00236       // keyboard events
00237       id == DOM::EventImpl::KEYDOWN_EVENT || id == DOM::EventImpl::KEYPRESS_EVENT ||
00238       id == DOM::EventImpl::KEYUP_EVENT ||
00239       // other accepted events
00240       id == DOM::EventImpl::SELECT_EVENT || id == DOM::EventImpl::CHANGE_EVENT ||
00241       id == DOM::EventImpl::SUBMIT_EVENT );
00242     kdDebug(6070) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk << endl;
00243     if (eventOk)
00244       return true;
00245   } else // no event
00246   {
00247     if ( m_inlineCode && !m_timerCallback )
00248     {
00249       // This is the <a href="javascript:window.open('...')> case -> we let it through
00250       return true;
00251       kdDebug(6070) << "Window.open, smart policy, no event, inline code -> ok" << endl;
00252     }
00253     else // This is the <script>window.open(...)</script> case or a timer callback -> block it
00254       kdDebug(6070) << "Window.open, smart policy, no event, <script> tag -> refused" << endl;
00255   }
00256   return false;
00257 }
00258 
00259 
00260 UString::UString(const QString &d)
00261 {
00262   unsigned int len = d.length();
00263   UChar *dat = new UChar[len];
00264   memcpy(dat, d.unicode(), len * sizeof(UChar));
00265   rep = UString::Rep::create(dat, len);
00266 }
00267 
00268 UString::UString(const DOM::DOMString &d)
00269 {
00270   if (d.isNull()) {
00271     // we do a conversion here as null DOMStrings shouldn't cross
00272     // the boundary to kjs. They should either be empty strings
00273     // or explicitly converted to KJS::Null via getString().
00274     attach(&Rep::empty);
00275     return;
00276   }
00277 
00278   unsigned int len = d.length();
00279   UChar *dat = new UChar[len];
00280   memcpy(dat, d.unicode(), len * sizeof(UChar));
00281   rep = UString::Rep::create(dat, len);
00282 }
00283 
00284 DOM::DOMString UString::string() const
00285 {
00286   return DOM::DOMString((QChar*) data(), size());
00287 }
00288 
00289 QString UString::qstring() const
00290 {
00291   return QString((QChar*) data(), size());
00292 }
00293 
00294 QConstString UString::qconststring() const
00295 {
00296   return QConstString((QChar*) data(), size());
00297 }
00298 
00299 DOM::DOMString Identifier::string() const
00300 {
00301   return DOM::DOMString((QChar*) data(), size());
00302 }
00303 
00304 QString Identifier::qstring() const
00305 {
00306   return QString((QChar*) data(), size());
00307 }
00308 
00309 DOM::Node KJS::toNode(const Value& val)
00310 {
00311   Object obj = Object::dynamicCast(val);
00312   if (!obj.isValid() || !obj.inherits(&DOMNode::info))
00313     return DOM::Node();
00314 
00315   const DOMNode *dobj = static_cast<const DOMNode*>(obj.imp());
00316   return dobj->toNode();
00317 }
00318 
00319 Value KJS::getString(DOM::DOMString s)
00320 {
00321   if (s.isNull())
00322     return Null();
00323   else
00324     return String(s);
00325 }
00326 
00327 QVariant KJS::ValueToVariant(ExecState* exec, const Value &val) {
00328   QVariant res;
00329   switch (val.type()) {
00330   case BooleanType:
00331     res = QVariant(val.toBoolean(exec), 0);
00332     break;
00333   case NumberType:
00334     res = QVariant(val.toNumber(exec));
00335     break;
00336   case StringType:
00337     res = QVariant(val.toString(exec).qstring());
00338     break;
00339   default:
00340     // everything else will be 'invalid'
00341     break;
00342   }
00343   return res;
00344 }
00345 
00346 class EmbedLiveConnect : public ObjectImp
00347 {
00348   friend Value KJS::getLiveConnectValue(KParts::LiveConnectExtension *lc, const QString & name, const int type, const QString & value, int id);
00349   EmbedLiveConnect(KParts::LiveConnectExtension *lc, UString n, KParts::LiveConnectExtension::Type t, int id);
00350 public:
00351   ~EmbedLiveConnect();
00352 
00353   virtual Value get(ExecState *, const Identifier & prop) const;
00354   virtual void put(ExecState * exec, const Identifier &prop, const Value & value, int=None);
00355   virtual Value call(ExecState * exec, Object &, const List &args);
00356   virtual bool implementsCall() const;
00357   virtual bool toBoolean(ExecState *) const;
00358   virtual Value toPrimitive(ExecState *exec, Type) const;
00359   virtual UString toString(ExecState *) const;
00360 
00361 private:
00362   EmbedLiveConnect(const EmbedLiveConnect &);
00363   QGuardedPtr<KParts::LiveConnectExtension> m_liveconnect;
00364   UString name;
00365   KParts::LiveConnectExtension::Type objtype;
00366   unsigned long objid;
00367 };
00368 
00369 Value KJS::getLiveConnectValue(KParts::LiveConnectExtension *lc, const QString & name, const int type, const QString & value, int id)
00370 {
00371   KParts::LiveConnectExtension::Type t=(KParts::LiveConnectExtension::Type)type;
00372   switch(t) {
00373     case KParts::LiveConnectExtension::TypeBool: {
00374       bool ok;
00375       int i = value.toInt(&ok);
00376       if (ok)
00377         return Boolean(i);
00378       return Boolean(!strcasecmp(value.latin1(), "true"));
00379     }
00380     case KParts::LiveConnectExtension::TypeObject:
00381     case KParts::LiveConnectExtension::TypeFunction:
00382       return Value(new EmbedLiveConnect(lc, name, t, id));
00383     case KParts::LiveConnectExtension::TypeNumber: {
00384       bool ok;
00385       int i = value.toInt(&ok);
00386       if (ok)
00387         return Number(i);
00388       else
00389         return Number(value.toDouble(&ok));
00390     }
00391     case KParts::LiveConnectExtension::TypeString:
00392       return String(value);
00393     case KParts::LiveConnectExtension::TypeVoid:
00394     default:
00395       return Undefined();
00396   }
00397 }
00398 
00399 /* only with gcc > 3.4 KDE_NO_EXPORT */
00400 EmbedLiveConnect::EmbedLiveConnect(KParts::LiveConnectExtension *lc, UString n, KParts::LiveConnectExtension::Type t, int id)
00401   : m_liveconnect (lc), name(n), objtype(t), objid(id)
00402 {}
00403 
00404 /* only with gcc > 3.4 KDE_NO_EXPORT */
00405 EmbedLiveConnect::~EmbedLiveConnect() {
00406   if (m_liveconnect)
00407     m_liveconnect->unregister(objid);
00408 }
00409 
00410 KDE_NO_EXPORT
00411 Value EmbedLiveConnect::get(ExecState *, const Identifier & prop) const
00412 {
00413   if (m_liveconnect) {
00414     KParts::LiveConnectExtension::Type rettype;
00415     QString retval;
00416     unsigned long retobjid;
00417     if (m_liveconnect->get(objid, prop.qstring(), rettype, retobjid, retval))
00418       return getLiveConnectValue(m_liveconnect, prop.qstring(), rettype, retval, retobjid);
00419   }
00420   return Undefined();
00421 }
00422 
00423 KDE_NO_EXPORT
00424 void EmbedLiveConnect::put(ExecState * exec, const Identifier &prop, const Value & value, int)
00425 {
00426   if (m_liveconnect)
00427     m_liveconnect->put(objid, prop.qstring(), value.toString(exec).qstring());
00428 }
00429 
00430 KDE_NO_EXPORT
00431 bool EmbedLiveConnect::implementsCall() const {
00432   return objtype == KParts::LiveConnectExtension::TypeFunction;
00433 }
00434 
00435 KDE_NO_EXPORT
00436 Value EmbedLiveConnect::call(ExecState *exec, Object&, const List &args)
00437 {
00438   if (m_liveconnect) {
00439     QStringList qargs;
00440     for (ListIterator i = args.begin(); i != args.end(); ++i)
00441       qargs.append((*i).toString(exec).qstring());
00442     KParts::LiveConnectExtension::Type rtype;
00443     QString rval;
00444     unsigned long robjid;
00445     if (m_liveconnect->call(objid, name.qstring(), qargs, rtype, robjid, rval))
00446       return getLiveConnectValue(m_liveconnect, name.qstring(), rtype, rval, robjid);
00447   }
00448   return Undefined();
00449 }
00450 
00451 KDE_NO_EXPORT
00452 bool EmbedLiveConnect::toBoolean(ExecState *) const {
00453   return true;
00454 }
00455 
00456 KDE_NO_EXPORT
00457 Value EmbedLiveConnect::toPrimitive(ExecState *exec, Type) const {
00458   return String(toString(exec));
00459 }
00460 
00461 KDE_NO_EXPORT
00462 UString EmbedLiveConnect::toString(ExecState *) const {
00463   QString str;
00464   const char *type = objtype == KParts::LiveConnectExtension::TypeFunction ? "Function" : "Object";
00465   str.sprintf("[object %s ref=%d]", type, (int) objid);
00466   return UString(str);
00467 }
KDE Home | KDE Accessibility Home | Description of Access Keys