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 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 }
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
00094
00095 static_cast<ObjectImp*>(m_script->globalObject().imp())->deleteAllProperties( m_script->globalExec() );
00096
00097 while (KJS::Interpreter::collect())
00098 ;
00099
00100 delete m_script;
00101
00102
00103
00104
00105 while (KJS::Interpreter::collect())
00106 ;
00107 }
00108
00109 #ifndef NDEBUG
00110 s_count--;
00111
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
00122
00123
00124 initScript();
00125
00126
00127
00128
00129 bool inlineCode = filename.isNull();
00130
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
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
00162 #endif
00163
00164 window->afterScriptExecution();
00165
00166
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
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 &, const List &args)
00189 {
00190 fprintf(stderr,"--> %s\n",args[0].toString(exec).ascii());
00191 return Undefined();
00192 }
00193
00194 void KJSProxyImpl::clear() {
00195
00196
00197
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
00215 m_script->globalObject().put(m_script->globalExec(),
00216 "debug", Value(new TestFunctionImp()), Internal);
00217 if ( !win->part().isNull() )
00218 applyUserAgent();
00219 }
00220
00221
00222
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
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);
00248 if (m_script->globalExec()->hadException())
00249 m_script->globalExec()->clearException();
00250
00251 if (!handlerFunc.inherits(&DeclaredFunctionImp::info))
00252 return 0;
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
00262
00263
00264
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
00280
00281
00282
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 )
00297 {
00298 #ifdef KJS_DEBUGGER
00299 if (KJSDebugWin::debugWindow())
00300 KJSDebugWin::debugWindow()->show();
00301 #else
00302
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
00329 Object globalObject( new Window(m_part) );
00330
00331
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
00337 #endif
00338
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
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
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 }