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.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 20 09:50:31 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003