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 "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 }
KDE Logo
This file is part of the documentation for khtml Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Apr 12 23:31:34 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003