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