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

This file is part of the documentation for khtml Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:33:19 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003