khtml Library API Documentation

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 <assert.h>
00037 #include <kjs/function.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 name, QString code);
00053   virtual void finishedWithEvent(const DOM::Event &event);
00054   virtual KJS::Interpreter *interpreter();
00055 
00056   virtual void setDebugEnabled(bool enabled);
00057   virtual void showDebugWindow(bool show=true);
00058   virtual bool paused() const;
00059   virtual void dataReceived();
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::Interpreter::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::Interpreter::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 occurred 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::debugWindow()) {
00132     KJSDebugWin::debugWindow()->attach(m_script);
00133     KJSDebugWin::debugWindow()->setNextSourceInfo(filename,baseLine);
00134   //    KJSDebugWin::debugWindow()->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::debugWindow()->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         kdDebug(6070) << "WARNING: 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     // ###
00197     KJSDebugWin *debugWin = KJSDebugWin::debugWindow();
00198     if (debugWin) {
00199       if (debugWin->getExecState() &&
00200           debugWin->getExecState()->interpreter() == m_script)
00201         debugWin->slotStop();
00202       debugWin->clearInterpreter(m_script);
00203     }
00204 #endif
00205     m_script->clear();
00206 
00207     Window *win = static_cast<Window *>(m_script->globalObject().imp());
00208     if (win) {
00209       win->clear( m_script->globalExec() );
00210       // re-add "debug", clear() removed it
00211       m_script->globalObject().put(m_script->globalExec(),
00212                                    "debug", Value(new TestFunctionImp()), Internal);
00213       if ( !win->part().isNull() )
00214         applyUserAgent();
00215     }
00216 
00217     // Really delete everything that can be, so that the DOM nodes get deref'ed
00218     //kdDebug() << k_funcinfo << "all done -> collecting" << endl;
00219     while (KJS::Interpreter::collect())
00220         ;
00221   }
00222 }
00223 
00224 DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QString name, QString code)
00225 {
00226   initScript();
00227 
00228 #ifdef KJS_DEBUGGER
00229   if (KJSDebugWin::debugWindow()) {
00230     KJSDebugWin::debugWindow()->attach(m_script);
00231     KJSDebugWin::debugWindow()->setNextSourceInfo(sourceUrl,m_handlerLineno);
00232   }
00233 #else
00234   Q_UNUSED(sourceUrl);
00235 #endif
00236 
00237   //KJS::Constructor constr(KJS::Global::current().get("Function").imp());
00238   KJS::Object constr = m_script->builtinFunction();
00239   KJS::List args;
00240   args.append(KJS::String("event"));
00241   args.append(KJS::String(code));
00242 
00243   Object handlerFunc = constr.construct(m_script->globalExec(), args); // ### is globalExec ok ?
00244   if (m_script->globalExec()->hadException())
00245     m_script->globalExec()->clearException();
00246 
00247   if (!handlerFunc.inherits(&DeclaredFunctionImp::info))
00248     return 0; // Error creating function
00249 
00250   DeclaredFunctionImp *declFunc = static_cast<DeclaredFunctionImp*>(handlerFunc.imp());
00251   declFunc->setName(Identifier(name));
00252   return KJS::Window::retrieveWindow(m_part)->getJSEventListener(handlerFunc,true);
00253 }
00254 
00255 void KJSProxyImpl::finishedWithEvent(const DOM::Event &event)
00256 {
00257   // This is called when the DOM implementation has finished with a particular event. This
00258   // is the case in sitations where an event has been created just for temporary usage,
00259   // e.g. an image load or mouse move. Once the event has been dispatched, it is forgotten
00260   // by the DOM implementation and so does not need to be cached still by the interpreter
00261   ScriptInterpreter::forgetDOMObject(event.handle());
00262 }
00263 
00264 KJS::Interpreter *KJSProxyImpl::interpreter()
00265 {
00266   if (!m_script)
00267     initScript();
00268   return m_script;
00269 }
00270 
00271 void KJSProxyImpl::setDebugEnabled(bool enabled)
00272 {
00273 #ifdef KJS_DEBUGGER
00274   m_debugEnabled = enabled;
00275   //if (m_script)
00276   //    m_script->setDebuggingEnabled(enabled);
00277   // NOTE: this is consistent across all KJSProxyImpl instances, as we only
00278   // ever have 1 debug window
00279   if (!enabled && KJSDebugWin::debugWindow()) {
00280     KJSDebugWin::destroyInstance();
00281   }
00282   else if (enabled && !KJSDebugWin::debugWindow()) {
00283     KJSDebugWin::createInstance();
00284     initScript();
00285     KJSDebugWin::debugWindow()->attach(m_script);
00286   }
00287 #else
00288   Q_UNUSED(enabled);
00289 #endif
00290 }
00291 
00292 void KJSProxyImpl::showDebugWindow(bool /*show*/)
00293 {
00294 #ifdef KJS_DEBUGGER
00295   if (KJSDebugWin::debugWindow())
00296     KJSDebugWin::debugWindow()->show();
00297 #else
00298   //Q_UNUSED(show);
00299 #endif
00300 }
00301 
00302 bool KJSProxyImpl::paused() const
00303 {
00304 #ifdef KJS_DEBUGGER
00305   if (KJSDebugWin::debugWindow())
00306     return KJSDebugWin::debugWindow()->inSession();
00307 #endif
00308   return false;
00309 }
00310 
00311 void KJSProxyImpl::dataReceived()
00312 {
00313 #ifdef KJS_DEBUGGER
00314   if (KJSDebugWin::debugWindow())
00315     KJSDebugWin::debugWindow()->sourceChanged(m_script,m_part->url().url());
00316 #endif
00317 }
00318 
00319 void KJSProxyImpl::initScript()
00320 {
00321   if (m_script)
00322     return;
00323 
00324   // Build the global object - which is a Window instance
00325   Object globalObject( new Window(m_part) );
00326 
00327   // Create a KJS interpreter for this part
00328   m_script = new KJS::ScriptInterpreter(globalObject, m_part);
00329   static_cast<ObjectImp*>(globalObject.imp())->setPrototype(m_script->builtinObjectPrototype());
00330 
00331 #ifdef KJS_DEBUGGER
00332   //m_script->setDebuggingEnabled(m_debugEnabled);
00333 #endif
00334   //m_script->enableDebug();
00335   globalObject.put(m_script->globalExec(),
00336            "debug", Value(new TestFunctionImp()), Internal);
00337   applyUserAgent();
00338 }
00339 
00340 void KJSProxyImpl::applyUserAgent()
00341 {
00342   assert( m_script );
00343   QString host = m_part->url().isLocalFile() ? "localhost" : m_part->url().host();
00344   QString userAgent = KProtocolManager::userAgentForHost(host);
00345   if (userAgent.find(QString::fromLatin1("Microsoft")) >= 0 ||
00346       userAgent.find(QString::fromLatin1("MSIE")) >= 0)
00347   {
00348     m_script->setCompatMode(Interpreter::IECompat);
00349 #ifdef KJS_VERBOSE
00350     kdDebug() << "Setting IE compat mode" << endl;
00351 #endif
00352   }
00353   else
00354     // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape
00355     if (userAgent.find(QString::fromLatin1("Mozilla")) >= 0 &&
00356         userAgent.find(QString::fromLatin1("compatible")) == -1)
00357     {
00358       m_script->setCompatMode(Interpreter::NetscapeCompat);
00359 #ifdef KJS_VERBOSE
00360       kdDebug() << "Setting NS compat mode" << endl;
00361 #endif
00362     }
00363 }
00364 
00365 // Helper method, so that all classes which need jScript() don't need to be added
00366 // as friend to KHTMLPart
00367 KJSProxy * KJSProxy::proxy( KHTMLPart *part )
00368 {
00369     return part->jScript();
00370 }
00371 
00372 // initialize HTML module
00373 KJSProxy *kjs_html_init(KHTMLPart *khtmlpart)
00374 {
00375   return new KJSProxyImpl(khtmlpart);
00376 }
00377 
00378 void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms)
00379 {
00380   oldAlarmHandler = signal(SIGVTALRM, alarmHandler);
00381   itimerval tv = {
00382       { i_ms / 1000, (i_ms % 1000) * 1000 },
00383       { ms / 1000, (ms % 1000) * 1000 }
00384   };
00385   setitimer(ITIMER_VIRTUAL, &tv, &oldtv);
00386 }
00387 
00388 void KJSCPUGuard::stop()
00389 {
00390   setitimer(ITIMER_VIRTUAL, &oldtv, 0L);
00391   signal(SIGVTALRM, oldAlarmHandler);
00392 }
00393 
00394 bool KJSCPUGuard::confirmTerminate() {
00395   kdDebug(6070) << "alarmhandler" << endl;
00396   return 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;
00397 }
00398 
00399 void KJSCPUGuard::alarmHandler(int) {
00400     ExecState::requestTerminate();
00401     ExecState::confirmTerminate = KJSCPUGuard::confirmTerminate;
00402 }
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Feb 4 12:37:22 2004 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2003