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 namespace KJS {
00046 
00047 class KJSProxyImpl : public KJSProxy {
00048 public:
00049   KJSProxyImpl(KHTMLPart *part);
00050   virtual ~KJSProxyImpl();
00051   virtual QVariant evaluate(QString filename, int baseLine, const QString &, const DOM::Node &n,
00052                 Completion *completion = 0);
00053   virtual void clear();
00054   virtual DOM::EventListener *createHTMLEventHandler(QString sourceUrl, QString name, QString code);
00055   virtual void finishedWithEvent(const DOM::Event &event);
00056   virtual KJS::Interpreter *interpreter();
00057 
00058   virtual void setDebugEnabled(bool enabled);
00059   virtual void showDebugWindow(bool show=true);
00060   virtual bool paused() const;
00061   virtual void dataReceived();
00062 
00063   void initScript();
00064   void applyUserAgent();
00065 
00066 private:
00067   KJS::ScriptInterpreter* m_script;
00068   bool m_debugEnabled;
00069 #ifndef NDEBUG
00070   static int s_count;
00071 #endif
00072 };
00073 
00074 } // namespace KJS
00075 
00076 #ifndef NDEBUG
00077 int KJSProxyImpl::s_count = 0;
00078 #endif
00079 
00080 KJSProxyImpl::KJSProxyImpl(KHTMLPart *part)
00081 {
00082   m_script = 0;
00083   m_part = part;
00084   m_debugEnabled = false;
00085 #ifndef NDEBUG
00086   s_count++;
00087 #endif
00088 }
00089 
00090 KJSProxyImpl::~KJSProxyImpl()
00091 {
00092   if ( m_script ) {
00093     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl clearing global object " << m_script->globalObject().imp() << endl;
00094     // This allows to delete the global-object properties, like all the protos
00095     static_cast<ObjectImp*>(m_script->globalObject().imp())->deleteAllProperties( m_script->globalExec() );
00096     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting" << endl;
00097     while (KJS::Interpreter::collect())
00098         ;
00099     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl deleting interpreter " << m_script << endl;
00100     delete m_script;
00101     //kdDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting again" << endl;
00102     // Garbage collect - as many times as necessary
00103     // (we could delete an object which was holding another object, so
00104     // the deref() will happen too late for deleting the impl of the 2nd object).
00105     while (KJS::Interpreter::collect())
00106         ;
00107   }
00108 
00109 #ifndef NDEBUG
00110   s_count--;
00111   // If it was the last interpreter, we should have nothing left
00112 #ifdef KJS_DEBUG_MEM
00113   if ( s_count == 0 )
00114     Interpreter::finalCheck();
00115 #endif
00116 #endif
00117 }
00118 
00119 QVariant KJSProxyImpl::evaluate(QString filename, int baseLine,
00120                                 const QString&str, const DOM::Node &n, Completion *completion) {
00121   // evaluate code. Returns the JS return value or an invalid QVariant
00122   // if there was none, an error occurred or the type couldn't be converted.
00123 
00124   initScript();
00125   // inlineCode is true for <a href="javascript:doSomething()">
00126   // and false for <script>doSomething()</script>. Check if it has the
00127   // expected value in all cases.
00128   // See smart window.open policy for where this is used.
00129   bool inlineCode = filename.isNull();
00130   //kdDebug(6070) << "KJSProxyImpl::evaluate inlineCode=" << inlineCode << endl;
00131 
00132 #ifdef KJS_DEBUGGER
00133   if (inlineCode)
00134     filename = "(unknown file)";
00135   if (KJSDebugWin::debugWindow()) {
00136     KJSDebugWin::debugWindow()->attach(m_script);
00137     KJSDebugWin::debugWindow()->setNextSourceInfo(filename,baseLine);
00138   //    KJSDebugWin::debugWindow()->setMode(KJSDebugWin::Step);
00139   }
00140 #else
00141   Q_UNUSED(baseLine);
00142 #endif
00143 
00144   m_script->setInlineCode(inlineCode);
00145   Window* window = Window::retrieveWindow( m_part );
00146   KJS::Value thisNode = n.isNull() ? Window::retrieve( m_part ) : getDOMNode(m_script->globalExec(),n);
00147 
00148   UString code( str );
00149 
00150   KJSCPUGuard guard;
00151   guard.start();
00152   Completion comp = m_script->evaluate(code, thisNode);
00153   guard.stop();
00154 
00155   bool success = ( comp.complType() == Normal ) || ( comp.complType() == ReturnValue );
00156 
00157   if (completion)
00158     *completion = comp;
00159 
00160 #ifdef KJS_DEBUGGER
00161     //    KJSDebugWin::debugWindow()->setCode(QString::null);
00162 #endif
00163 
00164   window->afterScriptExecution();
00165 
00166   // let's try to convert the return value
00167   if (success && !comp.value().isNull())
00168     return ValueToVariant( m_script->globalExec(), comp.value());
00169   else
00170   {
00171     if ( comp.complType() == Throw )
00172     {
00173         UString msg = comp.value().toString(m_script->globalExec());
00174         kdDebug(6070) << "WARNING: Script threw exception: " << msg.qstring() << endl;
00175     }
00176     return QVariant();
00177   }
00178 }
00179 
00180 // Implementation of the debug() function
00181 class TestFunctionImp : public ObjectImp {
00182 public:
00183   TestFunctionImp() : ObjectImp() {}
00184   virtual bool implementsCall() const { return true; }
00185   virtual Value call(ExecState *exec, Object &thisObj, const List &args);
00186 };
00187 
00188 Value TestFunctionImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00189 {
00190   fprintf(stderr,"--> %s\n",args[0].toString(exec).ascii());
00191   return Undefined();
00192 }
00193 
00194 void KJSProxyImpl::clear() {
00195   // clear resources allocated by the interpreter, and make it ready to be used by another page
00196   // We have to keep it, so that the Window object for the part remains the same.
00197   // (we used to delete and re-create it, previously)
00198   if (m_script) {
00199 #ifdef KJS_DEBUGGER
00200     // ###
00201     KJSDebugWin *debugWin = KJSDebugWin::debugWindow();
00202     if (debugWin) {
00203       if (debugWin->getExecState() &&
00204           debugWin->getExecState()->interpreter() == m_script)
00205         debugWin->slotStop();
00206       debugWin->clearInterpreter(m_script);
00207     }
00208 #endif
00209     m_script->clear();
00210 
00211     Window *win = static_cast<Window *>(m_script->globalObject().imp());
00212     if (win) {
00213       win->clear( m_script->globalExec() );
00214       // re-add "debug", clear() removed it
00215       m_script->globalObject().put(m_script->globalExec(),
00216                                    "debug", Value(new TestFunctionImp()), Internal);
00217       if ( !win->part().isNull() )
00218         applyUserAgent();
00219     }
00220 
00221     // Really delete everything that can be, so that the DOM nodes get deref'ed
00222     //kdDebug() << k_funcinfo << "all done -> collecting" << endl;
00223     while (KJS::Interpreter::collect())
00224         ;
00225   }
00226 }
00227 
00228 DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QString name, QString code)
00229 {
00230   initScript();
00231 
00232 #ifdef KJS_DEBUGGER
00233   if (KJSDebugWin::debugWindow()) {
00234     KJSDebugWin::debugWindow()->attach(m_script);
00235     KJSDebugWin::debugWindow()->setNextSourceInfo(sourceUrl,m_handlerLineno);
00236   }
00237 #else
00238   Q_UNUSED(sourceUrl);
00239 #endif
00240 
00241   //KJS::Constructor constr(KJS::Global::current().get("Function").imp());
00242   KJS::Object constr = m_script->builtinFunction();
00243   KJS::List args;
00244   args.append(KJS::String("event"));
00245   args.append(KJS::String(code));
00246 
00247   Object handlerFunc = constr.construct(m_script->globalExec(), args); // ### is globalExec ok ?
00248   if (m_script->globalExec()->hadException())
00249     m_script->globalExec()->clearException();
00250 
00251   if (!handlerFunc.inherits(&DeclaredFunctionImp::info))
00252     return 0; // Error creating function
00253 
00254   DeclaredFunctionImp *declFunc = static_cast<DeclaredFunctionImp*>(handlerFunc.imp());
00255   declFunc->setName(Identifier(name));
00256   return KJS::Window::retrieveWindow(m_part)->getJSEventListener(handlerFunc,true);
00257 }
00258 
00259 void KJSProxyImpl::finishedWithEvent(const DOM::Event &event)
00260 {
00261   // This is called when the DOM implementation has finished with a particular event. This
00262   // is the case in sitations where an event has been created just for temporary usage,
00263   // e.g. an image load or mouse move. Once the event has been dispatched, it is forgotten
00264   // by the DOM implementation and so does not need to be cached still by the interpreter
00265   ScriptInterpreter::forgetDOMObject(event.handle());
00266 }
00267 
00268 KJS::Interpreter *KJSProxyImpl::interpreter()
00269 {
00270   if (!m_script)
00271     initScript();
00272   return m_script;
00273 }
00274 
00275 void KJSProxyImpl::setDebugEnabled(bool enabled)
00276 {
00277 #ifdef KJS_DEBUGGER
00278   m_debugEnabled = enabled;
00279   //if (m_script)
00280   //    m_script->setDebuggingEnabled(enabled);
00281   // NOTE: this is consistent across all KJSProxyImpl instances, as we only
00282   // ever have 1 debug window
00283   if (!enabled && KJSDebugWin::debugWindow()) {
00284     KJSDebugWin::destroyInstance();
00285   }
00286   else if (enabled && !KJSDebugWin::debugWindow()) {
00287     KJSDebugWin::createInstance();
00288     initScript();
00289     KJSDebugWin::debugWindow()->attach(m_script);
00290   }
00291 #else
00292   Q_UNUSED(enabled);
00293 #endif
00294 }
00295 
00296 void KJSProxyImpl::showDebugWindow(bool /*show*/)
00297 {
00298 #ifdef KJS_DEBUGGER
00299   if (KJSDebugWin::debugWindow())
00300     KJSDebugWin::debugWindow()->show();
00301 #else
00302   //Q_UNUSED(show);
00303 #endif
00304 }
00305 
00306 bool KJSProxyImpl::paused() const
00307 {
00308 #ifdef KJS_DEBUGGER
00309   if (KJSDebugWin::debugWindow())
00310     return KJSDebugWin::debugWindow()->inSession();
00311 #endif
00312   return false;
00313 }
00314 
00315 void KJSProxyImpl::dataReceived()
00316 {
00317 #ifdef KJS_DEBUGGER
00318   if (KJSDebugWin::debugWindow())
00319     KJSDebugWin::debugWindow()->sourceChanged(m_script,m_part->url().url());
00320 #endif
00321 }
00322 
00323 void KJSProxyImpl::initScript()
00324 {
00325   if (m_script)
00326     return;
00327 
00328   // Build the global object - which is a Window instance
00329   Object globalObject( new Window(m_part) );
00330 
00331   // Create a KJS interpreter for this part
00332   m_script = new KJS::ScriptInterpreter(globalObject, m_part);
00333   static_cast<ObjectImp*>(globalObject.imp())->setPrototype(m_script->builtinObjectPrototype());
00334 
00335 #ifdef KJS_DEBUGGER
00336   //m_script->setDebuggingEnabled(m_debugEnabled);
00337 #endif
00338   //m_script->enableDebug();
00339   globalObject.put(m_script->globalExec(),
00340            "debug", Value(new TestFunctionImp()), Internal);
00341   applyUserAgent();
00342 }
00343 
00344 void KJSProxyImpl::applyUserAgent()
00345 {
00346   assert( m_script );
00347   QString host = m_part->url().isLocalFile() ? "localhost" : m_part->url().host();
00348   QString userAgent = KProtocolManager::userAgentForHost(host);
00349   if (userAgent.find(QString::fromLatin1("Microsoft")) >= 0 ||
00350       userAgent.find(QString::fromLatin1("MSIE")) >= 0)
00351   {
00352     m_script->setCompatMode(Interpreter::IECompat);
00353 #ifdef KJS_VERBOSE
00354     kdDebug() << "Setting IE compat mode" << endl;
00355 #endif
00356   }
00357   else
00358     // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape
00359     if (userAgent.find(QString::fromLatin1("Mozilla")) >= 0 &&
00360         userAgent.find(QString::fromLatin1("compatible")) == -1)
00361     {
00362       m_script->setCompatMode(Interpreter::NetscapeCompat);
00363 #ifdef KJS_VERBOSE
00364       kdDebug() << "Setting NS compat mode" << endl;
00365 #endif
00366     }
00367 }
00368 
00369 // initialize HTML module
00370 KJSProxy *kjs_html_init(KHTMLPart *khtmlpart)
00371 {
00372   return new KJSProxyImpl(khtmlpart);
00373 }
00374 
00375 void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms)
00376 {
00377   oldAlarmHandler = signal(SIGVTALRM, alarmHandler);
00378   itimerval tv = {
00379       { i_ms / 1000, (i_ms % 1000) * 1000 },
00380       { ms / 1000, (ms % 1000) * 1000 }
00381   };
00382   setitimer(ITIMER_VIRTUAL, &tv, &oldtv);
00383 }
00384 
00385 void KJSCPUGuard::stop()
00386 {
00387   setitimer(ITIMER_VIRTUAL, &oldtv, 0L);
00388   signal(SIGVTALRM, oldAlarmHandler);
00389 }
00390 
00391 bool KJSCPUGuard::confirmTerminate() {
00392   kdDebug(6070) << "alarmhandler" << endl;
00393   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;
00394 }
00395 
00396 void KJSCPUGuard::alarmHandler(int) {
00397     ExecState::requestTerminate();
00398     ExecState::confirmTerminate = KJSCPUGuard::confirmTerminate;
00399 }
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed May 5 07:18:29 2004 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003