00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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 class KJSProxyImpl : public KJSProxy {
00046 public:
00047 KJSProxyImpl(KHTMLPart *part);
00048 virtual ~KJSProxyImpl();
00049 virtual QVariant evaluate(QString filename, int baseLine, const QString &, const DOM::Node &n,
00050 Completion *completion = 0);
00051 virtual void clear();
00052 virtual DOM::EventListener *createHTMLEventHandler(QString sourceUrl, QString name, QString code);
00053 virtual void finishedWithEvent(const DOM::Event &event);
00054 virtual KJS::Interpreter *interpreter();
00055
00056 virtual void setDebugEnabled(bool enabled);
00057 virtual void showDebugWindow(bool show=true);
00058 virtual bool paused() const;
00059 virtual void dataReceived();
00060
00061 void initScript();
00062 void applyUserAgent();
00063
00064 private:
00065 KJS::ScriptInterpreter* m_script;
00066 bool m_debugEnabled;
00067 #ifndef NDEBUG
00068 static int s_count;
00069 #endif
00070 };
00071
00072 #ifndef NDEBUG
00073 int KJSProxyImpl::s_count = 0;
00074 #endif
00075
00076 KJSProxyImpl::KJSProxyImpl(KHTMLPart *part)
00077 {
00078 m_script = 0;
00079 m_part = part;
00080 m_debugEnabled = false;
00081 #ifndef NDEBUG
00082 s_count++;
00083 #endif
00084 }
00085
00086 KJSProxyImpl::~KJSProxyImpl()
00087 {
00088 if ( m_script ) {
00089
00090
00091 static_cast<ObjectImp*>(m_script->globalObject().imp())->deleteAllProperties( m_script->globalExec() );
00092
00093 while (KJS::Interpreter::collect())
00094 ;
00095
00096 delete m_script;
00097
00098
00099
00100
00101 while (KJS::Interpreter::collect())
00102 ;
00103 }
00104
00105 #ifndef NDEBUG
00106 s_count--;
00107
00108 #ifdef KJS_DEBUG_MEM
00109 if ( s_count == 0 )
00110 Interpreter::finalCheck();
00111 #endif
00112 #endif
00113 }
00114
00115 QVariant KJSProxyImpl::evaluate(QString filename, int baseLine,
00116 const QString&str, const DOM::Node &n, Completion *completion) {
00117
00118
00119
00120 initScript();
00121
00122
00123
00124
00125 bool inlineCode = filename.isNull();
00126
00127
00128 #ifdef KJS_DEBUGGER
00129 if (inlineCode)
00130 filename = "(unknown file)";
00131 if (KJSDebugWin::debugWindow()) {
00132 KJSDebugWin::debugWindow()->attach(m_script);
00133 KJSDebugWin::debugWindow()->setNextSourceInfo(filename,baseLine);
00134
00135 }
00136 #else
00137 Q_UNUSED(baseLine);
00138 #endif
00139
00140 m_script->setInlineCode(inlineCode);
00141 Window* window = Window::retrieveWindow( m_part );
00142 KJS::Value thisNode = n.isNull() ? Window::retrieve( m_part ) : getDOMNode(m_script->globalExec(),n);
00143
00144 UString code( str );
00145
00146 KJSCPUGuard guard;
00147 guard.start();
00148 Completion comp = m_script->evaluate(code, thisNode);
00149 guard.stop();
00150
00151 bool success = ( comp.complType() == Normal ) || ( comp.complType() == ReturnValue );
00152
00153 if (completion)
00154 *completion = comp;
00155
00156 #ifdef KJS_DEBUGGER
00157
00158 #endif
00159
00160 window->afterScriptExecution();
00161
00162
00163 if (success && !comp.value().isNull())
00164 return ValueToVariant( m_script->globalExec(), comp.value());
00165 else
00166 {
00167 if ( comp.complType() == Throw )
00168 {
00169 UString msg = comp.value().toString(m_script->globalExec());
00170 kdDebug(6070) << "WARNING: Script threw exception: " << msg.qstring() << endl;
00171 }
00172 return QVariant();
00173 }
00174 }
00175
00176
00177 class TestFunctionImp : public ObjectImp {
00178 public:
00179 TestFunctionImp() : ObjectImp() {}
00180 virtual bool implementsCall() const { return true; }
00181 virtual Value call(ExecState *exec, Object &thisObj, const List &args);
00182 };
00183
00184 Value TestFunctionImp::call(ExecState *exec, Object &, const List &args)
00185 {
00186 fprintf(stderr,"--> %s\n",args[0].toString(exec).ascii());
00187 return Undefined();
00188 }
00189
00190 void KJSProxyImpl::clear() {
00191
00192
00193
00194 if (m_script) {
00195 #ifdef KJS_DEBUGGER
00196
00197 KJSDebugWin *debugWin = KJSDebugWin::debugWindow();
00198 if (debugWin) {
00199 if (debugWin->getExecState() &&
00200 debugWin->getExecState()->interpreter() == m_script)
00201 debugWin->slotStop();
00202 debugWin->clearInterpreter(m_script);
00203 }
00204 #endif
00205 m_script->clear();
00206
00207 Window *win = static_cast<Window *>(m_script->globalObject().imp());
00208 if (win) {
00209 win->clear( m_script->globalExec() );
00210
00211 m_script->globalObject().put(m_script->globalExec(),
00212 "debug", Value(new TestFunctionImp()), Internal);
00213 if ( !win->part().isNull() )
00214 applyUserAgent();
00215 }
00216
00217
00218
00219 while (KJS::Interpreter::collect())
00220 ;
00221 }
00222 }
00223
00224 DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QString name, QString code)
00225 {
00226 initScript();
00227
00228 #ifdef KJS_DEBUGGER
00229 if (KJSDebugWin::debugWindow()) {
00230 KJSDebugWin::debugWindow()->attach(m_script);
00231 KJSDebugWin::debugWindow()->setNextSourceInfo(sourceUrl,m_handlerLineno);
00232 }
00233 #else
00234 Q_UNUSED(sourceUrl);
00235 #endif
00236
00237
00238 KJS::Object constr = m_script->builtinFunction();
00239 KJS::List args;
00240 args.append(KJS::String("event"));
00241 args.append(KJS::String(code));
00242
00243 Object handlerFunc = constr.construct(m_script->globalExec(), args);
00244 if (m_script->globalExec()->hadException())
00245 m_script->globalExec()->clearException();
00246
00247 if (!handlerFunc.inherits(&DeclaredFunctionImp::info))
00248 return 0;
00249
00250 DeclaredFunctionImp *declFunc = static_cast<DeclaredFunctionImp*>(handlerFunc.imp());
00251 declFunc->setName(Identifier(name));
00252 return KJS::Window::retrieveWindow(m_part)->getJSEventListener(handlerFunc,true);
00253 }
00254
00255 void KJSProxyImpl::finishedWithEvent(const DOM::Event &event)
00256 {
00257
00258
00259
00260
00261 ScriptInterpreter::forgetDOMObject(event.handle());
00262 }
00263
00264 KJS::Interpreter *KJSProxyImpl::interpreter()
00265 {
00266 if (!m_script)
00267 initScript();
00268 return m_script;
00269 }
00270
00271 void KJSProxyImpl::setDebugEnabled(bool enabled)
00272 {
00273 #ifdef KJS_DEBUGGER
00274 m_debugEnabled = enabled;
00275
00276
00277
00278
00279 if (!enabled && KJSDebugWin::debugWindow()) {
00280 KJSDebugWin::destroyInstance();
00281 }
00282 else if (enabled && !KJSDebugWin::debugWindow()) {
00283 KJSDebugWin::createInstance();
00284 initScript();
00285 KJSDebugWin::debugWindow()->attach(m_script);
00286 }
00287 #else
00288 Q_UNUSED(enabled);
00289 #endif
00290 }
00291
00292 void KJSProxyImpl::showDebugWindow(bool )
00293 {
00294 #ifdef KJS_DEBUGGER
00295 if (KJSDebugWin::debugWindow())
00296 KJSDebugWin::debugWindow()->show();
00297 #else
00298
00299 #endif
00300 }
00301
00302 bool KJSProxyImpl::paused() const
00303 {
00304 #ifdef KJS_DEBUGGER
00305 if (KJSDebugWin::debugWindow())
00306 return KJSDebugWin::debugWindow()->inSession();
00307 #endif
00308 return false;
00309 }
00310
00311 void KJSProxyImpl::dataReceived()
00312 {
00313 #ifdef KJS_DEBUGGER
00314 if (KJSDebugWin::debugWindow())
00315 KJSDebugWin::debugWindow()->sourceChanged(m_script,m_part->url().url());
00316 #endif
00317 }
00318
00319 void KJSProxyImpl::initScript()
00320 {
00321 if (m_script)
00322 return;
00323
00324
00325 Object globalObject( new Window(m_part) );
00326
00327
00328 m_script = new KJS::ScriptInterpreter(globalObject, m_part);
00329 static_cast<ObjectImp*>(globalObject.imp())->setPrototype(m_script->builtinObjectPrototype());
00330
00331 #ifdef KJS_DEBUGGER
00332
00333 #endif
00334
00335 globalObject.put(m_script->globalExec(),
00336 "debug", Value(new TestFunctionImp()), Internal);
00337 applyUserAgent();
00338 }
00339
00340 void KJSProxyImpl::applyUserAgent()
00341 {
00342 assert( m_script );
00343 QString host = m_part->url().isLocalFile() ? "localhost" : m_part->url().host();
00344 QString userAgent = KProtocolManager::userAgentForHost(host);
00345 if (userAgent.find(QString::fromLatin1("Microsoft")) >= 0 ||
00346 userAgent.find(QString::fromLatin1("MSIE")) >= 0)
00347 {
00348 m_script->setCompatMode(Interpreter::IECompat);
00349 #ifdef KJS_VERBOSE
00350 kdDebug() << "Setting IE compat mode" << endl;
00351 #endif
00352 }
00353 else
00354
00355 if (userAgent.find(QString::fromLatin1("Mozilla")) >= 0 &&
00356 userAgent.find(QString::fromLatin1("compatible")) == -1)
00357 {
00358 m_script->setCompatMode(Interpreter::NetscapeCompat);
00359 #ifdef KJS_VERBOSE
00360 kdDebug() << "Setting NS compat mode" << endl;
00361 #endif
00362 }
00363 }
00364
00365
00366
00367 KJSProxy * KJSProxy::proxy( KHTMLPart *part )
00368 {
00369 return part->jScript();
00370 }
00371
00372
00373 KJSProxy *kjs_html_init(KHTMLPart *khtmlpart)
00374 {
00375 return new KJSProxyImpl(khtmlpart);
00376 }
00377
00378 void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms)
00379 {
00380 oldAlarmHandler = signal(SIGVTALRM, alarmHandler);
00381 itimerval tv = {
00382 { i_ms / 1000, (i_ms % 1000) * 1000 },
00383 { ms / 1000, (ms % 1000) * 1000 }
00384 };
00385 setitimer(ITIMER_VIRTUAL, &tv, &oldtv);
00386 }
00387
00388 void KJSCPUGuard::stop()
00389 {
00390 setitimer(ITIMER_VIRTUAL, &oldtv, 0L);
00391 signal(SIGVTALRM, oldAlarmHandler);
00392 }
00393
00394 bool KJSCPUGuard::confirmTerminate() {
00395 kdDebug(6070) << "alarmhandler" << endl;
00396 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;
00397 }
00398
00399 void KJSCPUGuard::alarmHandler(int) {
00400 ExecState::requestTerminate();
00401 ExecState::confirmTerminate = KJSCPUGuard::confirmTerminate;
00402 }