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 Value DOMFunction::get(ExecState *exec, const Identifier &propertyName) const
00096 {
00097   try {
00098     return tryGet(exec, propertyName);
00099   }
00100   catch (DOM::DOMException e) {
00101     Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit());
00102     exec->setException(err);
00103     return Undefined();
00104   }
00105   catch (...) {
00106     kdError(6070) << "Unknown exception in DOMFunction::get()" << endl;
00107     return String("Unknown exception");
00108   }
00109 }
00110 
00111 Value DOMFunction::call(ExecState *exec, Object &thisObj, const List &args)
00112 {
00113   try {
00114     return tryCall(exec, thisObj, args);
00115   }
00116   // pity there's no way to distinguish between these in JS code
00117   // ### Look into setting prototypes of these & the use of instanceof so the exception
00118   // type can be determined. See what other browsers do.
00119   catch (DOM::DOMException e) {
00120     Object err = Error::create(exec, GeneralError, QString("DOM Exception %1").arg(e.code).local8Bit());
00121     err.put(exec, "code", Number(e.code));
00122     exec->setException(err);
00123     return Undefined();
00124   }
00125   catch (DOM::RangeException e) {
00126     Object err = Error::create(exec, GeneralError, QString("DOM Range Exception %1").arg(e.code).local8Bit());
00127     err.put(exec, "code", Number(e.code));
00128     exec->setException(err);
00129     return Undefined();
00130   }
00131   catch (DOM::CSSException e) {
00132     Object err = Error::create(exec, GeneralError, QString("CSS Exception %1").arg(e.code).local8Bit());
00133     err.put(exec, "code", Number(e.code));
00134     exec->setException(err);
00135     return Undefined();
00136   }
00137   catch (DOM::EventException e) {
00138     Object err = Error::create(exec, GeneralError, QString("DOM Event Exception %1").arg(e.code).local8Bit());
00139     err.put(exec, "code", Number(e.code));
00140     exec->setException(err);
00141     return Undefined();
00142   }
00143   catch (...) {
00144     kdError(6070) << "Unknown exception in DOMFunction::call()" << endl;
00145     Object err = Error::create(exec, GeneralError, "Unknown exception");
00146     exec->setException(err);
00147     return Undefined();
00148   }
00149 }
00150 
00151 typedef QPtrList<ScriptInterpreter> InterpreterList;
00152 static InterpreterList *interpreterList;
00153 
00154 ScriptInterpreter::ScriptInterpreter( const Object &global, khtml::ChildFrame* frame )
00155   : Interpreter( global ), m_frame( frame ), m_domObjects(1021),
00156     m_evt( 0L ), m_inlineCode(false), m_timerCallback(false)
00157 {
00158 #ifdef KJS_VERBOSE
00159   kdDebug(6070) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_frame << endl;
00160 #endif
00161   if ( !interpreterList )
00162     interpreterList = new InterpreterList;
00163   interpreterList->append( this );
00164 }
00165 
00166 ScriptInterpreter::~ScriptInterpreter()
00167 {
00168 #ifdef KJS_VERBOSE
00169   kdDebug(6070) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_frame << endl;
00170 #endif
00171   assert( interpreterList && interpreterList->contains( this ) );
00172   interpreterList->remove( this );
00173   if ( interpreterList->isEmpty() ) {
00174     delete interpreterList;
00175     interpreterList = 0;
00176   }
00177 }
00178 
00179 void ScriptInterpreter::forgetDOMObject( void* objectHandle )
00180 {
00181   if( !interpreterList ) return;
00182 
00183   QPtrListIterator<ScriptInterpreter> it( *interpreterList );
00184   while ( it.current() ) {
00185     (*it)->deleteDOMObject( objectHandle );
00186     ++it;
00187   }
00188 }
00189 
00190 void ScriptInterpreter::mark()
00191 {
00192   Interpreter::mark();
00193 #ifdef KJS_VERBOSE
00194   kdDebug(6070) << "ScriptInterpreter::mark " << this << " marking " << m_customizedDomObjects.count() << " DOM objects" << endl;
00195 #endif
00196   QPtrDictIterator<void> it( m_customizedDomObjects );
00197   for( ; it.current(); ++it )
00198     static_cast<DOMObject*>(it.currentKey())->mark();
00199 }
00200 
00201 KParts::ReadOnlyPart* ScriptInterpreter::part() const {
00202     return m_frame->m_part;
00203 }
00204 
00205 bool ScriptInterpreter::isWindowOpenAllowed() const
00206 {
00207   if ( m_evt )
00208   {
00209     int id = m_evt->handle()->id();
00210     bool eventOk = ( // mouse events
00211       id == DOM::EventImpl::CLICK_EVENT ||
00212       id == DOM::EventImpl::MOUSEUP_EVENT || id == DOM::EventImpl::MOUSEDOWN_EVENT ||
00213       id == DOM::EventImpl::KHTML_ECMA_CLICK_EVENT || id == DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT ||
00214       // keyboard events
00215       id == DOM::EventImpl::KEYDOWN_EVENT || id == DOM::EventImpl::KEYPRESS_EVENT ||
00216       id == DOM::EventImpl::KEYUP_EVENT ||
00217       // other accepted events
00218       id == DOM::EventImpl::SELECT_EVENT || id == DOM::EventImpl::CHANGE_EVENT ||
00219       id == DOM::EventImpl::SUBMIT_EVENT );
00220     kdDebug(6070) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk << endl;
00221     if (eventOk)
00222       return true;
00223   } else // no event
00224   {
00225     if ( m_inlineCode && !m_timerCallback )
00226     {
00227       // This is the <a href="javascript:window.open('...')> case -> we let it through
00228       return true;
00229       kdDebug(6070) << "Window.open, smart policy, no event, inline code -> ok" << endl;
00230     }
00231     else // This is the <script>window.open(...)</script> case or a timer callback -> block it
00232       kdDebug(6070) << "Window.open, smart policy, no event, <script> tag -> refused" << endl;
00233   }
00234   return false;
00235 }
00236 
00237 
00238 UString::UString(const QString &d)
00239 {
00240   unsigned int len = d.length();
00241   UChar *dat = new UChar[len];
00242   memcpy(dat, d.unicode(), len * sizeof(UChar));
00243   rep = UString::Rep::create(dat, len);
00244 }
00245 
00246 UString::UString(const DOM::DOMString &d)
00247 {
00248   if (d.isNull()) {
00249     // we do a conversion here as null DOMStrings shouldn't cross
00250     // the boundary to kjs. They should either be empty strings
00251     // or explicitly converted to KJS::Null via getString().
00252     attach(&Rep::empty);
00253     return;
00254   }
00255 
00256   unsigned int len = d.length();
00257   UChar *dat = new UChar[len];
00258   memcpy(dat, d.unicode(), len * sizeof(UChar));
00259   rep = UString::Rep::create(dat, len);
00260 }
00261 
00262 DOM::DOMString UString::string() const
00263 {
00264   return DOM::DOMString((QChar*) data(), size());
00265 }
00266 
00267 QString UString::qstring() const
00268 {
00269   return QString((QChar*) data(), size());
00270 }
00271 
00272 QConstString UString::qconststring() const
00273 {
00274   return QConstString((QChar*) data(), size());
00275 }
00276 
00277 DOM::DOMString Identifier::string() const
00278 {
00279   return DOM::DOMString((QChar*) data(), size());
00280 }
00281 
00282 QString Identifier::qstring() const
00283 {
00284   return QString((QChar*) data(), size());
00285 }
00286 
00287 DOM::Node KJS::toNode(const Value& val)
00288 {
00289   Object obj = Object::dynamicCast(val);
00290   if (!obj.isValid() || !obj.inherits(&DOMNode::info))
00291     return DOM::Node();
00292 
00293   const DOMNode *dobj = static_cast<const DOMNode*>(obj.imp());
00294   return dobj->toNode();
00295 }
00296 
00297 Value KJS::getString(DOM::DOMString s)
00298 {
00299   if (s.isNull())
00300     return Null();
00301   else
00302     return String(s);
00303 }
00304 
00305 QVariant KJS::ValueToVariant(ExecState* exec, const Value &val) {
00306   QVariant res;
00307   switch (val.type()) {
00308   case BooleanType:
00309     res = QVariant(val.toBoolean(exec), 0);
00310     break;
00311   case NumberType:
00312     res = QVariant(val.toNumber(exec));
00313     break;
00314   case StringType:
00315     res = QVariant(val.toString(exec).qstring());
00316     break;
00317   default:
00318     // everything else will be 'invalid'
00319     break;
00320   }
00321   return res;
00322 }
00323 
00324 class EmbedLiveConnect : public ObjectImp
00325 {
00326   friend Value KJS::getLiveConnectValue(KParts::LiveConnectExtension *lc, const QString & name, const int type, const QString & value, int id);
00327   EmbedLiveConnect(KParts::LiveConnectExtension *lc, UString n, KParts::LiveConnectExtension::Type t, int id);
00328 public:
00329   ~EmbedLiveConnect();
00330 
00331   virtual Value get(ExecState *, const Identifier & prop) const;
00332   virtual void put(ExecState * exec, const Identifier &prop, const Value & value, int=None);
00333   virtual Value call(ExecState * exec, Object &, const List &args);
00334   virtual bool implementsCall() const;
00335   virtual bool toBoolean(ExecState *) const;
00336   virtual Value toPrimitive(ExecState *exec, Type) const;
00337   virtual UString toString(ExecState *) const;
00338 
00339 private:
00340   EmbedLiveConnect(const EmbedLiveConnect &);
00341   QGuardedPtr<KParts::LiveConnectExtension> m_liveconnect;
00342   UString name;
00343   KParts::LiveConnectExtension::Type objtype;
00344   unsigned long objid;
00345 };
00346 
00347 Value KJS::getLiveConnectValue(KParts::LiveConnectExtension *lc, const QString & name, const int type, const QString & value, int id)
00348 {
00349   KParts::LiveConnectExtension::Type t=(KParts::LiveConnectExtension::Type)type;
00350   switch(t) {
00351     case KParts::LiveConnectExtension::TypeBool: {
00352       bool ok;
00353       int i = value.toInt(&ok);
00354       if (ok)
00355         return Boolean(i);
00356       return Boolean(!strcasecmp(value.latin1(), "true"));
00357     }
00358     case KParts::LiveConnectExtension::TypeObject:
00359     case KParts::LiveConnectExtension::TypeFunction:
00360       return Value(new EmbedLiveConnect(lc, name, t, id));
00361     case KParts::LiveConnectExtension::TypeNumber: {
00362       bool ok;
00363       int i = value.toInt(&ok);
00364       if (ok)
00365         return Number(i);
00366       else
00367         return Number(value.toDouble(&ok));
00368     }
00369     case KParts::LiveConnectExtension::TypeString:
00370       return String(value);
00371     case KParts::LiveConnectExtension::TypeVoid:
00372     default:
00373       return Undefined();
00374   }
00375 }
00376 
00377 /* only with gcc > 3.4 KDE_NO_EXPORT */
00378 EmbedLiveConnect::EmbedLiveConnect(KParts::LiveConnectExtension *lc, UString n, KParts::LiveConnectExtension::Type t, int id)
00379   : m_liveconnect (lc), name(n), objtype(t), objid(id)
00380 {}
00381 
00382 /* only with gcc > 3.4 KDE_NO_EXPORT */
00383 EmbedLiveConnect::~EmbedLiveConnect() {
00384   if (m_liveconnect)
00385     m_liveconnect->unregister(objid);
00386 }
00387 
00388 KDE_NO_EXPORT
00389 Value EmbedLiveConnect::get(ExecState *, const Identifier & prop) const
00390 {
00391   if (m_liveconnect) {
00392     KParts::LiveConnectExtension::Type rettype;
00393     QString retval;
00394     unsigned long retobjid;
00395     if (m_liveconnect->get(objid, prop.qstring(), rettype, retobjid, retval))
00396       return getLiveConnectValue(m_liveconnect, prop.qstring(), rettype, retval, retobjid);
00397   }
00398   return Undefined();
00399 }
00400 
00401 KDE_NO_EXPORT
00402 void EmbedLiveConnect::put(ExecState * exec, const Identifier &prop, const Value & value, int)
00403 {
00404   if (m_liveconnect)
00405     m_liveconnect->put(objid, prop.qstring(), value.toString(exec).qstring());
00406 }
00407 
00408 KDE_NO_EXPORT
00409 bool EmbedLiveConnect::implementsCall() const {
00410   return objtype == KParts::LiveConnectExtension::TypeFunction;
00411 }
00412 
00413 KDE_NO_EXPORT
00414 Value EmbedLiveConnect::call(ExecState *exec, Object&, const List &args)
00415 {
00416   if (m_liveconnect) {
00417     QStringList qargs;
00418     for (ListIterator i = args.begin(); i != args.end(); ++i)
00419       qargs.append((*i).toString(exec).qstring());
00420     KParts::LiveConnectExtension::Type rtype;
00421     QString rval;
00422     unsigned long robjid;
00423     if (m_liveconnect->call(objid, name.qstring(), qargs, rtype, robjid, rval))
00424       return getLiveConnectValue(m_liveconnect, name.qstring(), rtype, rval, robjid);
00425   }
00426   return Undefined();
00427 }
00428 
00429 KDE_NO_EXPORT
00430 bool EmbedLiveConnect::toBoolean(ExecState *) const {
00431   return true;
00432 }
00433 
00434 KDE_NO_EXPORT
00435 Value EmbedLiveConnect::toPrimitive(ExecState *exec, Type) const {
00436   return String(toString(exec));
00437 }
00438 
00439 KDE_NO_EXPORT
00440 UString EmbedLiveConnect::toString(ExecState *) const {
00441   QString str;
00442   const char *type = objtype == KParts::LiveConnectExtension::TypeFunction ? "Function" : "Object";
00443   str.sprintf("[object %s ref=%d]", type, (int) objid);
00444   return UString(str);
00445 }
KDE Home | KDE Accessibility Home | Description of Access Keys