summaryrefslogtreecommitdiffstats
path: root/khtml/ecma/kjs_proxy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/ecma/kjs_proxy.cpp')
-rw-r--r--khtml/ecma/kjs_proxy.cpp411
1 files changed, 411 insertions, 0 deletions
diff --git a/khtml/ecma/kjs_proxy.cpp b/khtml/ecma/kjs_proxy.cpp
new file mode 100644
index 000000000..789f99f38
--- /dev/null
+++ b/khtml/ecma/kjs_proxy.cpp
@@ -0,0 +1,411 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2001-2003 David Faure (faure@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#if defined(HAVE_VALGRIND_MEMCHECK_H) && !defined(NDEBUG)
+
+#include <valgrind/memcheck.h>
+#define VALGRIND_SUPPORT
+
+#endif
+
+
+#include "kjs_proxy.h"
+
+#include "kjs_window.h"
+#include "kjs_events.h"
+#include "kjs_debugwin.h"
+#include "xml/dom_nodeimpl.h"
+#include "khtmlpart_p.h"
+#include <khtml_part.h>
+#include <kprotocolmanager.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <kjs/function.h>
+
+using namespace KJS;
+
+extern "C" {
+ KJSProxy *kjs_html_init(khtml::ChildFrame *childframe);
+}
+
+namespace KJS {
+
+class KJSProxyImpl : public KJSProxy {
+public:
+ KJSProxyImpl(khtml::ChildFrame *frame);
+ virtual ~KJSProxyImpl();
+ virtual QVariant evaluate(QString filename, int baseLine, const QString &, const DOM::Node &n,
+ Completion *completion = 0);
+ virtual void clear();
+ virtual DOM::EventListener *createHTMLEventHandler(QString sourceUrl, QString name, QString code, DOM::NodeImpl *node);
+ virtual void finishedWithEvent(const DOM::Event &event);
+ virtual KJS::Interpreter *interpreter();
+
+ virtual void setDebugEnabled(bool enabled);
+ virtual void showDebugWindow(bool show=true);
+ virtual bool paused() const;
+ virtual void dataReceived();
+
+ void initScript();
+ void applyUserAgent();
+
+private:
+ KJS::ScriptInterpreter* m_script;
+ bool m_debugEnabled;
+#ifndef NDEBUG
+ static int s_count;
+#endif
+};
+
+} // namespace KJS
+
+#ifndef NDEBUG
+int KJSProxyImpl::s_count = 0;
+#endif
+
+KJSProxyImpl::KJSProxyImpl(khtml::ChildFrame *frame)
+{
+ m_script = 0;
+ m_frame = frame;
+ m_debugEnabled = false;
+#ifndef NDEBUG
+ s_count++;
+#endif
+}
+
+KJSProxyImpl::~KJSProxyImpl()
+{
+ if ( m_script ) {
+ //kdDebug() << "KJSProxyImpl::~KJSProxyImpl clearing global object " << m_script->globalObject().imp() << endl;
+ // This allows to delete the global-object properties, like all the protos
+ static_cast<ObjectImp*>(m_script->globalObject().imp())->deleteAllProperties( m_script->globalExec() );
+ //kdDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting" << endl;
+ while (KJS::Interpreter::collect())
+ ;
+ //kdDebug() << "KJSProxyImpl::~KJSProxyImpl deleting interpreter " << m_script << endl;
+ delete m_script;
+ //kdDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting again" << endl;
+ // Garbage collect - as many times as necessary
+ // (we could delete an object which was holding another object, so
+ // the deref() will happen too late for deleting the impl of the 2nd object).
+ while (KJS::Interpreter::collect())
+ ;
+ }
+
+#ifndef NDEBUG
+ s_count--;
+ // If it was the last interpreter, we should have nothing left
+#ifdef KJS_DEBUG_MEM
+ if ( s_count == 0 )
+ Interpreter::finalCheck();
+#endif
+#endif
+}
+
+QVariant KJSProxyImpl::evaluate(QString filename, int baseLine,
+ const QString&str, const DOM::Node &n, Completion *completion) {
+ // evaluate code. Returns the JS return value or an invalid QVariant
+ // if there was none, an error occurred or the type couldn't be converted.
+
+ initScript();
+ // inlineCode is true for <a href="javascript:doSomething()">
+ // and false for <script>doSomething()</script>. Check if it has the
+ // expected value in all cases.
+ // See smart window.open policy for where this is used.
+ bool inlineCode = filename.isNull();
+ //kdDebug(6070) << "KJSProxyImpl::evaluate inlineCode=" << inlineCode << endl;
+
+#ifdef KJS_DEBUGGER
+ if (inlineCode)
+ filename = "(unknown file)";
+ if (KJSDebugWin::debugWindow()) {
+ KJSDebugWin::debugWindow()->attach(m_script);
+ KJSDebugWin::debugWindow()->setNextSourceInfo(filename,baseLine);
+ // KJSDebugWin::debugWindow()->setMode(KJSDebugWin::Step);
+ }
+#else
+ Q_UNUSED(baseLine);
+#endif
+
+ m_script->setInlineCode(inlineCode);
+ Window* window = Window::retrieveWindow( m_frame->m_part );
+ KJS::Value thisNode = n.isNull() ? Window::retrieve( m_frame->m_part ) : getDOMNode(m_script->globalExec(),n);
+
+ UString code( str );
+
+ KJSCPUGuard guard;
+ guard.start();
+ Completion comp = m_script->evaluate(code, thisNode);
+ guard.stop();
+
+ bool success = ( comp.complType() == Normal ) || ( comp.complType() == ReturnValue );
+
+ if (completion)
+ *completion = comp;
+
+#ifdef KJS_DEBUGGER
+ // KJSDebugWin::debugWindow()->setCode(QString::null);
+#endif
+
+ window->afterScriptExecution();
+
+ // let's try to convert the return value
+ if (success && comp.value().isValid())
+ return ValueToVariant( m_script->globalExec(), comp.value());
+ else
+ {
+ if ( comp.complType() == Throw )
+ {
+ UString msg = comp.value().toString(m_script->globalExec());
+ kdDebug(6070) << "WARNING: Script threw exception: " << msg.qstring() << endl;
+ }
+ return QVariant();
+ }
+}
+
+// Implementation of the debug() function
+class TestFunctionImp : public ObjectImp {
+public:
+ TestFunctionImp() : ObjectImp() {}
+ virtual bool implementsCall() const { return true; }
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+};
+
+Value TestFunctionImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ fprintf(stderr,"--> %s\n",args[0].toString(exec).ascii());
+ return Undefined();
+}
+
+void KJSProxyImpl::clear() {
+ // clear resources allocated by the interpreter, and make it ready to be used by another page
+ // We have to keep it, so that the Window object for the part remains the same.
+ // (we used to delete and re-create it, previously)
+ if (m_script) {
+#ifdef KJS_DEBUGGER
+ // ###
+ KJSDebugWin *debugWin = KJSDebugWin::debugWindow();
+ if (debugWin) {
+ if (debugWin->getExecState() &&
+ debugWin->getExecState()->interpreter() == m_script)
+ debugWin->slotStop();
+ debugWin->clearInterpreter(m_script);
+ }
+#endif
+ m_script->clear();
+
+ Window *win = static_cast<Window *>(m_script->globalObject().imp());
+ if (win) {
+ win->clear( m_script->globalExec() );
+ // re-add "debug", clear() removed it
+ m_script->globalObject().put(m_script->globalExec(),
+ "debug", Value(new TestFunctionImp()), Internal);
+ if ( win->part() )
+ applyUserAgent();
+ }
+
+ // Really delete everything that can be, so that the DOM nodes get deref'ed
+ //kdDebug() << k_funcinfo << "all done -> collecting" << endl;
+ while (KJS::Interpreter::collect())
+ ;
+ }
+}
+
+DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QString name, QString code, DOM::NodeImpl *node)
+{
+ initScript();
+
+#ifdef KJS_DEBUGGER
+ if (KJSDebugWin::debugWindow()) {
+ KJSDebugWin::debugWindow()->attach(m_script);
+ KJSDebugWin::debugWindow()->setNextSourceInfo(sourceUrl,m_handlerLineno);
+ }
+#else
+ Q_UNUSED(sourceUrl);
+#endif
+
+ return KJS::Window::retrieveWindow(m_frame->m_part)->getJSLazyEventListener(code,name,node);
+}
+
+void KJSProxyImpl::finishedWithEvent(const DOM::Event &event)
+{
+ // This is called when the DOM implementation has finished with a particular event. This
+ // is the case in sitations where an event has been created just for temporary usage,
+ // e.g. an image load or mouse move. Once the event has been dispatched, it is forgotten
+ // by the DOM implementation and so does not need to be cached still by the interpreter
+ ScriptInterpreter::forgetDOMObject(event.handle());
+}
+
+KJS::Interpreter *KJSProxyImpl::interpreter()
+{
+ if (!m_script)
+ initScript();
+ return m_script;
+}
+
+void KJSProxyImpl::setDebugEnabled(bool enabled)
+{
+#ifdef KJS_DEBUGGER
+ m_debugEnabled = enabled;
+ //if (m_script)
+ // m_script->setDebuggingEnabled(enabled);
+ // NOTE: this is consistent across all KJSProxyImpl instances, as we only
+ // ever have 1 debug window
+ if (!enabled && KJSDebugWin::debugWindow()) {
+ KJSDebugWin::destroyInstance();
+ }
+ else if (enabled && !KJSDebugWin::debugWindow()) {
+ KJSDebugWin::createInstance();
+ initScript();
+ KJSDebugWin::debugWindow()->attach(m_script);
+ }
+#else
+ Q_UNUSED(enabled);
+#endif
+}
+
+void KJSProxyImpl::showDebugWindow(bool /*show*/)
+{
+#ifdef KJS_DEBUGGER
+ if (KJSDebugWin::debugWindow())
+ KJSDebugWin::debugWindow()->show();
+#else
+ //Q_UNUSED(show);
+#endif
+}
+
+bool KJSProxyImpl::paused() const
+{
+#ifdef KJS_DEBUGGER
+ if (KJSDebugWin::debugWindow())
+ return KJSDebugWin::debugWindow()->inSession();
+#endif
+ return false;
+}
+
+void KJSProxyImpl::dataReceived()
+{
+#ifdef KJS_DEBUGGER
+ if (KJSDebugWin::debugWindow() && m_frame->m_part)
+ KJSDebugWin::debugWindow()->sourceChanged(m_script,m_frame->m_part->url().url());
+#endif
+}
+
+void KJSProxyImpl::initScript()
+{
+ if (m_script)
+ return;
+
+ // Build the global object - which is a Window instance
+ Object globalObject( new Window(m_frame) );
+
+ // Create a KJS interpreter for this part
+ m_script = new KJS::ScriptInterpreter(globalObject, m_frame);
+ static_cast<ObjectImp*>(globalObject.imp())->setPrototype(m_script->builtinObjectPrototype());
+
+#ifdef KJS_DEBUGGER
+ //m_script->setDebuggingEnabled(m_debugEnabled);
+#endif
+ //m_script->enableDebug();
+ globalObject.put(m_script->globalExec(),
+ "debug", Value(new TestFunctionImp()), Internal);
+ applyUserAgent();
+}
+
+void KJSProxyImpl::applyUserAgent()
+{
+ assert( m_script );
+ QString host = m_frame->m_part->url().isLocalFile() ? "localhost" : m_frame->m_part->url().host();
+ QString userAgent = KProtocolManager::userAgentForHost(host);
+ if (userAgent.find(QString::fromLatin1("Microsoft")) >= 0 ||
+ userAgent.find(QString::fromLatin1("MSIE")) >= 0)
+ {
+ m_script->setCompatMode(Interpreter::IECompat);
+#ifdef KJS_VERBOSE
+ kdDebug() << "Setting IE compat mode" << endl;
+#endif
+ }
+ else
+ // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape
+ if (userAgent.find(QString::fromLatin1("Mozilla")) >= 0 &&
+ userAgent.find(QString::fromLatin1("compatible")) == -1 &&
+ userAgent.find(QString::fromLatin1("KHTML")) == -1)
+ {
+ m_script->setCompatMode(Interpreter::NetscapeCompat);
+#ifdef KJS_VERBOSE
+ kdDebug() << "Setting NS compat mode" << endl;
+#endif
+ }
+}
+
+// Helper method, so that all classes which need jScript() don't need to be added
+// as friend to KHTMLPart
+KJSProxy * KJSProxy::proxy( KHTMLPart *part )
+{
+ return part->jScript();
+}
+
+// initialize HTML module
+KJSProxy *kjs_html_init(khtml::ChildFrame *childframe)
+{
+ return new KJSProxyImpl(childframe);
+}
+
+void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms)
+{
+#ifdef VALGRIND_SUPPORT
+ if (RUNNING_ON_VALGRIND) {
+ ms *= 50;
+ i_ms *= 50;
+ }
+#endif
+
+ oldAlarmHandler = signal(SIGVTALRM, alarmHandler);
+ itimerval tv = {
+ { i_ms / 1000, (i_ms % 1000) * 1000 },
+ { ms / 1000, (ms % 1000) * 1000 }
+ };
+ setitimer(ITIMER_VIRTUAL, &tv, &oldtv);
+}
+
+void KJSCPUGuard::stop()
+{
+ setitimer(ITIMER_VIRTUAL, &oldtv, 0L);
+ signal(SIGVTALRM, oldAlarmHandler);
+}
+
+bool KJSCPUGuard::confirmTerminate() {
+ kdDebug(6070) << "alarmhandler" << endl;
+ 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;
+}
+
+void KJSCPUGuard::alarmHandler(int) {
+ ExecState::requestTerminate();
+ ExecState::confirmTerminate = KJSCPUGuard::confirmTerminate;
+}