kjs_proxy.cpp Source File (khtml Library)

| # khtml Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# kjs_proxy.cpp

    00001 // -*- c-basic-offset: 2 -*-
    00002 /*
    00003  *  This file is part of the KDE libraries
    00004  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
    00005  *  Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
    00006  *  Copyright (C) 2001-2003 David Faure (faure@kde.org)
    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    00021  */
    00022
    00023 #include "kjs_proxy.h"
    00024
    00025 #include "kjs_window.h"
    00026 #include "kjs_events.h"
    00027 #include "kjs_debugwin.h"
    00028 #include <khtml_part.h>
    00029 #include <kprotocolmanager.h>
    00030 #include <kdebug.h>
    00031 #include <kmessagebox.h>
    00032 #include <klocale.h>
    00033 #include <unistd.h>
    00034 #include <signal.h>
    00035 #include <sys/time.h>
    00036 #include <kjs/collector.h>
    00037 #include <assert.h>
    00038
    00039 using namespace KJS;
    00040
    00041 extern "C" {
    00042   KJSProxy *kjs_html_init(KHTMLPart *khtmlpart);
    00043 }
    00044
    00045 class KJSProxyImpl : public KJSProxy {
    00046 public:
    00047   KJSProxyImpl(KHTMLPart *part);
    00048   virtual ~KJSProxyImpl();
    00049   virtual QVariant evaluate(QString filename, int baseLine, const QString &, const DOM::Node &n,
    00050                             Completion *completion = 0);
    00051   virtual void clear();
    00052   virtual DOM::EventListener *createHTMLEventHandler(QString sourceUrl, QString code);
    00053   virtual void finishedWithEvent(const DOM::Event &event);
    00054   virtual KJS::ScriptInterpreter *interpreter();
    00055
    00056   virtual void setDebugEnabled(bool enabled);
    00057   virtual bool paused() const;
    00058   virtual void setSourceFile(QString url, QString code);
    00059   virtual void appendSourceFile(QString url, QString code);
    00060
    00061   void initScript();
    00062   void applyUserAgent();
    00063
    00064 private:
    00065   KJS::ScriptInterpreter* m_script;
    00066   bool m_debugEnabled;
    00067 #ifndef NDEBUG
    00068   static int s_count;
    00069 #endif
    00070 };
    00071
    00072 #ifndef NDEBUG
    00073 int KJSProxyImpl::s_count = 0;
    00074 #endif
    00075
    00076 KJSProxyImpl::KJSProxyImpl(KHTMLPart *part)
    00077 {
    00078   m_script = 0;
    00079   m_part = part;
    00080   m_debugEnabled = false;
    00081 #ifndef NDEBUG
    00082   s_count++;
    00083 #endif
    00084 }
    00085
    00086 KJSProxyImpl::~KJSProxyImpl()
    00087 {
    00088   if ( m_script ) {
    00089     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl clearing global object " << m_script->globalObject().imp() << endl;
    00090     // This allows to delete the global-object properties, like all the protos
    00091     static_cast<ObjectImp*>(m_script->globalObject().imp())->deleteAllProperties( m_script->globalExec() );
    00092     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting" << endl;
    00093     while (KJS::Collector::collect())
    00094         ;
    00095     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl deleting interpreter " << m_script << endl;
    00096     delete m_script;
    00097     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting again" << endl;
    00098     // Garbage collect - as many times as necessary
    00099     // (we could delete an object which was holding another object, so
    00100     // the deref() will happen too late for deleting the impl of the 2nd object).
    00101     while (KJS::Collector::collect())
    00102         ;
    00103   }
    00104
    00105 #ifndef NDEBUG
    00106   s_count--;
    00107   // If it was the last interpreter, we should have nothing left
    00108 #ifdef KJS_DEBUG_MEM
    00109   if ( s_count == 0 )
    00110     Interpreter::finalCheck();
    00111 #endif
    00112 #endif
    00113 }
    00114
    00115 QVariant KJSProxyImpl::evaluate(QString filename, int baseLine,
    00116                                 const QString&str, const DOM::Node &n, Completion *completion) {
    00117   // evaluate code. Returns the JS return value or an invalid QVariant
    00118   // if there was none, an error occured or the type couldn't be converted.
    00119
    00120   initScript();
    00121   // inlineCode is true for <a href="javascript:doSomething()">
    00122   // and false for <script>doSomething()</script>. Check if it has the
    00123   // expected value in all cases.
    00124   // See smart window.open policy for where this is used.
    00125   bool inlineCode = filename.isNull();
    00126   //kdDebug(6070) << "KJSProxyImpl::evaluate inlineCode=" << inlineCode << endl;
    00127
    00128 #ifdef KJS_DEBUGGER
    00129   if (inlineCode)
    00130     filename = "(unknown file)";
    00131   if (KJSDebugWin::instance()) {
    00132     KJSDebugWin::instance()->attach(m_script);
    00133     KJSDebugWin::instance()->setNextSourceInfo(filename,baseLine);
    00134   //    KJSDebugWin::instance()->setMode(KJSDebugWin::Step);
    00135   }
    00136 #else
    00137   Q_UNUSED(baseLine);
    00138 #endif
    00139
    00140   m_script->setInlineCode(inlineCode);
    00141   Window* window = Window::retrieveWindow( m_part );
    00142   KJS::Value thisNode = n.isNull() ? Window::retrieve( m_part ) : getDOMNode(m_script->globalExec(),n);
    00143
    00144   UString code( str );
    00145
    00146   KJSCPUGuard guard;
    00147   guard.start();
    00148   Completion comp = m_script->evaluate(code, thisNode);
    00149   guard.stop();
    00150
    00151   bool success = ( comp.complType() == Normal ) || ( comp.complType() == ReturnValue );
    00152
    00153   if (completion)
    00154     *completion = comp;
    00155
    00156 #ifdef KJS_DEBUGGER
    00157     //    KJSDebugWin::instance()->setCode(QString::null);
    00158 #endif
    00159
    00160   window->afterScriptExecution();
    00161
    00162   // let's try to convert the return value
    00163   if (success && !comp.value().isNull())
    00164     return ValueToVariant( m_script->globalExec(), comp.value());
    00165   else
    00166   {
    00167     if ( comp.complType() == Throw )
    00168     {
    00169         UString msg = comp.value().toString(m_script->globalExec());
    00170         kdWarning(6070) << "Script threw exception: " << msg.qstring() << endl;
    00171     }
    00172     return QVariant();
    00173   }
    00174 }
    00175
    00176 // Implementation of the debug() function
    00177 class TestFunctionImp : public ObjectImp {
    00178 public:
    00179   TestFunctionImp() : ObjectImp() {}
    00180   virtual bool implementsCall() const { return true; }
    00181   virtual Value call(ExecState *exec, Object &thisObj, const List &args);
    00182 };
    00183
    00184 Value TestFunctionImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
    00185 {
    00186   fprintf(stderr,"--> %s\n",args[0].toString(exec).ascii());
    00187   return Undefined();
    00188 }
    00189
    00190 void KJSProxyImpl::clear() {
    00191   // clear resources allocated by the interpreter, and make it ready to be used by another page
    00192   // We have to keep it, so that the Window object for the part remains the same.
    00193   // (we used to delete and re-create it, previously)
    00194   if (m_script) {
    00195 #ifdef KJS_DEBUGGER
    00196     //KJSDebugWin *debugWin = KJSDebugWin::instance();
    00197     //if (debugWin && debugWin->currentScript() == m_script) {
    00198     //    debugWin->setMode(KJSDebugWin::Stop);
    00199 //        debugWin->leaveSession();
    00200     //}
    00201 #endif
    00202     m_script->clear();
    00203
    00204     Window *win = static_cast<Window *>(m_script->globalObject().imp());
    00205     if (win) {
    00206       win->clear( m_script->globalExec() );
    00207       // re-add "debug", clear() removed it
    00208       m_script->globalObject().put(m_script->globalExec(),
    00209                                    "debug", Value(new TestFunctionImp()), Internal);
    00210       if ( !win->part().isNull() )
    00211         applyUserAgent();
    00212     }
    00213   }
    00214 }
    00215
    00216 DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QString code)
    00217 {
    00218 #ifdef KJS_DEBUGGER
    00219   if (KJSDebugWin::instance())
    00220     KJSDebugWin::instance()->setNextSourceInfo(sourceUrl,m_handlerLineno);
    00221 #else
    00222   Q_UNUSED(sourceUrl);
    00223 #endif
    00224
    00225   initScript();
    00226   //KJS::Constructor constr(KJS::Global::current().get("Function").imp());
    00227   KJS::Object constr = m_script->builtinFunction();
    00228   KJS::List args;
    00229   args.append(KJS::String("event"));
    00230   args.append(KJS::String(code));
    00231   Object handlerFunc = constr.construct(m_script->globalExec(), args); // ### is globalExec ok ?
    00232
    00233   return KJS::Window::retrieveWindow(m_part)->getJSEventListener(handlerFunc,true);
    00234 }
    00235
    00236 void KJSProxyImpl::finishedWithEvent(const DOM::Event &event)
    00237 {
    00238   // This is called when the DOM implementation has finished with a particular event. This
    00239   // is the case in sitations where an event has been created just for temporary usage,
    00240   // e.g. an image load or mouse move. Once the event has been dispatched, it is forgotten
    00241   // by the DOM implementation and so does not need to be cached still by the interpreter
    00242   m_script->forgetDOMObject(event.handle());
    00243 }
    00244
    00245 KJS::ScriptInterpreter *KJSProxyImpl::interpreter()
    00246 {
    00247   if (!m_script)
    00248     initScript();
    00249   return m_script;
    00250 }
    00251
    00252 void KJSProxyImpl::setDebugEnabled(bool enabled)
    00253 {
    00254 #ifdef KJS_DEBUGGER
    00255   m_debugEnabled = enabled;
    00256   //if (m_script)
    00257   //    m_script->setDebuggingEnabled(enabled);
    00258   // NOTE: this is consistent across all KJSProxyImpl instances, as we only
    00259   // ever have 1 debug window
    00260   if (!enabled && KJSDebugWin::instance()) {
    00261     KJSDebugWin::destroyInstance();
    00262   }
    00263   else if (enabled && !KJSDebugWin::instance()) {
    00264     KJSDebugWin::createInstance();
    00265     initScript();
    00266     KJSDebugWin::instance()->attach(m_script);
    00267   }
    00268 #else
    00269   Q_UNUSED(enabled);
    00270 #endif
    00271 }
    00272
    00273 bool KJSProxyImpl::paused() const
    00274 {
    00275 #ifdef KJS_DEBUGGER
    00276   if (KJSDebugWin::instance())
    00277     return KJSDebugWin::instance()->inSession();
    00278 #endif
    00279   return false;
    00280 }
    00281
    00282 void KJSProxyImpl::setSourceFile(QString url, QString code)
    00283 {
    00284 #ifdef KJS_DEBUGGER
    00285   if (KJSDebugWin::instance())
    00286     KJSDebugWin::instance()->setSourceFile(url,code);
    00287 #else
    00288   Q_UNUSED(url);
    00289   Q_UNUSED(code);
    00290 #endif
    00291
    00292 }
    00293
    00294 void KJSProxyImpl::appendSourceFile(QString url, QString code)
    00295 {
    00296 #ifdef KJS_DEBUGGER
    00297   if (KJSDebugWin::instance())
    00298     KJSDebugWin::instance()->appendSourceFile(url,code);
    00299 #else
    00300   Q_UNUSED(url);
    00301   Q_UNUSED(code);
    00302 #endif
    00303 }
    00304
    00305 void KJSProxyImpl::initScript()
    00306 {
    00307   if (m_script)
    00308     return;
    00309
    00310   // Build the global object - which is a Window instance
    00311   Object globalObject( new Window(m_part) );
    00312
    00313   // Create a KJS interpreter for this part
    00314   m_script = new KJS::ScriptInterpreter(globalObject, m_part);
    00315   static_cast<ObjectImp*>(globalObject.imp())->setPrototype(m_script->builtinObjectPrototype());
    00316
    00317 #ifdef KJS_DEBUGGER
    00318   //m_script->setDebuggingEnabled(m_debugEnabled);
    00319 #endif
    00320   //m_script->enableDebug();
    00321   globalObject.put(m_script->globalExec(),
    00322                    "debug", Value(new TestFunctionImp()), Internal);
    00323   applyUserAgent();
    00324 }
    00325
    00326 void KJSProxyImpl::applyUserAgent()
    00327 {
    00328   assert( m_script );
    00329   QString userAgent = KProtocolManager::userAgentForHost(m_part->url().host());
    00330   if (userAgent.find(QString::fromLatin1("Microsoft")) >= 0 ||
    00331       userAgent.find(QString::fromLatin1("MSIE")) >= 0)
    00332   {
    00333     m_script->setCompatMode(Interpreter::IECompat);
    00334 #ifdef KJS_VERBOSE
    00335     kdDebug() << "Setting IE compat mode" << endl;
    00336 #endif
    00337   }
    00338   else
    00339     // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape
    00340     if (userAgent.find(QString::fromLatin1("Mozilla")) >= 0 &&
    00341         userAgent.find(QString::fromLatin1("compatible")) == -1)
    00342     {
    00343       m_script->setCompatMode(Interpreter::NetscapeCompat);
    00344 #ifdef KJS_VERBOSE
    00345       kdDebug() << "Setting NS compat mode" << endl;
    00346 #endif
    00347     }
    00348 }
    00349
    00350 // Helper method, so that all classes which need jScript() don't need to be added
    00351 // as friend to KHTMLPart
    00352 KJSProxy * KJSProxy::proxy( KHTMLPart *part )
    00353 {
    00354     return part->jScript();
    00355 }
    00356
    00357 // initialize HTML module
    00358 KJSProxy *kjs_html_init(KHTMLPart *khtmlpart)
    00359 {
    00360   return new KJSProxyImpl(khtmlpart);
    00361 }
    00362
    00363 void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms)
    00364 {
    00365   oldAlarmHandler = signal(SIGVTALRM, alarmHandler);
    00366   itimerval tv = {
    00367       { i_ms / 1000, (i_ms % 1000) * 1000 },
    00368       { ms / 1000, (ms % 1000) * 1000 }
    00369   };
    00370   setitimer(ITIMER_VIRTUAL, &tv, &oldtv);
    00371 }
    00372
    00373 void KJSCPUGuard::stop()
    00374 {
    00375   setitimer(ITIMER_VIRTUAL, &oldtv, 0L);
    00376   signal(SIGVTALRM, oldAlarmHandler);
    00377 }
    00378
    00379 void KJSCPUGuard::alarmHandler(int) {
    00380   kdDebug(6070) << "alarmhandler" << endl;
    00381   if (KMessageBox::warningYesNo(0L, i18n("A script on this page is causing KHTML to freeze. If it continues to run, other applications may become less responsive.\nDo you want to abort the script?"), i18n("JavaScript"), i18n("Abort"), KStdGuiItem::cont(), "kjscupguard_alarmhandler") == KMessageBox::Yes)
    00382     ExecState::requestTerminate();
    00383 }

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:24 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001