fix logic
[personal-kdelibs.git] / khtml / ecma / kjs_proxy.cpp
blob866ba1cdd6156a053ed92f8ea710b0236645a13b
1 // -*- c-basic-offset: 2 -*-
2 /*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5 * Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
6 * Copyright (C) 2001-2003 David Faure (faure@kde.org)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "kjs_proxy.h"
25 #include <config.h>
27 #include "kjs_window.h"
28 #include "kjs_events.h"
29 //#include "kjs_debugwin.h"
30 #include "debugger/debugwindow.h"
31 #include <xml/dom_nodeimpl.h>
32 #include <khtmlpart_p.h>
33 #include <khtml_part.h>
34 #include <kprotocolmanager.h>
35 #include <kdebug.h>
36 #include <klocale.h>
37 #include <unistd.h>
38 #include <assert.h>
39 #include <kjs/function.h>
40 #include <kjs/JSLock.h>
42 using namespace KJS;
43 using namespace KJSDebugger;
45 extern "C" {
46 KJSProxy *kjs_html_init(khtml::ChildFrame *childframe);
49 namespace KJS {
51 class KJSProxyImpl : public KJSProxy {
52 public:
53 KJSProxyImpl(khtml::ChildFrame *frame);
54 virtual ~KJSProxyImpl();
55 virtual QVariant evaluate(QString filename, int baseLine, const QString &, const DOM::Node &n,
56 Completion *completion = 0);
57 virtual void clear();
58 virtual DOM::EventListener *createHTMLEventHandler(QString sourceUrl, QString name, QString code, DOM::NodeImpl *node, bool svg = false);
59 virtual void finishedWithEvent(const DOM::Event &event);
60 virtual KJS::Interpreter *interpreter();
62 virtual void setDebugEnabled(bool enabled);
63 virtual bool debugEnabled() const;
64 virtual void showDebugWindow(bool show=true);
65 virtual bool paused() const;
66 virtual void dataReceived();
68 void initScript();
69 void applyUserAgent();
70 private:
71 KJS::ScriptInterpreter* m_script;
72 WTF::RefPtr<DebugWindow> m_debugWindow;
73 bool m_debugEnabled;
74 #ifndef NDEBUG
75 static int s_count;
76 #endif
79 } // namespace KJS
81 #ifndef NDEBUG
82 int KJSProxyImpl::s_count = 0;
83 #endif
85 KJSProxyImpl::KJSProxyImpl(khtml::ChildFrame *frame)
87 m_script = 0;
88 m_frame = frame;
89 m_debugEnabled = false;
90 #ifndef NDEBUG
91 s_count++;
92 #endif
95 KJSProxyImpl::~KJSProxyImpl()
97 if ( m_script ) {
98 //kDebug() << "KJSProxyImpl::~KJSProxyImpl clearing global object " << m_script->globalObject().imp();
99 // This allows to delete the global-object properties, like all the protos
100 m_script->globalObject()->clearProperties();
101 //kDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting";
103 JSLock::lock();
104 while (Interpreter::collect())
106 JSLock::unlock();
107 //kDebug() << "KJSProxyImpl::~KJSProxyImpl deleting interpreter " << m_script;
108 delete m_script;
109 //kDebug() << "KJSProxyImpl::~KJSProxyImpl garbage collecting again";
110 // Garbage collect - as many times as necessary
111 // (we could delete an object which was holding another object, so
112 // the deref() will happen too late for deleting the impl of the 2nd object).
113 JSLock::lock();
114 while (Interpreter::collect())
116 JSLock::unlock();
119 #ifndef NDEBUG
120 s_count--;
121 // If it was the last interpreter, we should have nothing left
122 #ifdef KJS_DEBUG_MEM
123 if ( s_count == 0 )
124 Interpreter::finalCheck();
125 #endif
126 #endif
129 QVariant KJSProxyImpl::evaluate(QString filename, int baseLine,
130 const QString&str, const DOM::Node &n, Completion *completion) {
131 // evaluate code. Returns the JS return value or an invalid QVariant
132 // if there was none, an error occurred or the type couldn't be converted.
134 initScript();
135 // inlineCode is true for <a href="javascript:doSomething()">
136 // and false for <script>doSomething()</script>. Check if it has the
137 // expected value in all cases.
138 // See smart window.open policy for where this is used.
139 bool inlineCode = filename.isNull();
140 //kDebug(6070) << "KJSProxyImpl::evaluate inlineCode=" << inlineCode;
142 #ifdef KJS_DEBUGGER
143 if (inlineCode)
144 filename = "(unknown file)";
145 if (m_debugWindow)
146 m_debugWindow->attach(m_script);
147 #else
148 Q_UNUSED(baseLine);
149 #endif
151 m_script->setInlineCode(inlineCode);
152 Window* window = Window::retrieveWindow( m_frame->m_part );
153 KJS::JSValue *thisNode = n.isNull() ? Window::retrieve( m_frame->m_part ) : getDOMNode(m_script->globalExec(),n.handle());
155 UString code( str );
157 m_script->startCPUGuard();
158 Completion comp = m_script->evaluate(filename, baseLine, code, thisNode);
159 m_script->stopCPUGuard();
161 bool success = ( comp.complType() == Normal ) || ( comp.complType() == ReturnValue );
163 if (completion)
164 *completion = comp;
166 #ifdef KJS_DEBUGGER
167 // KJSDebugWin::debugWindow()->setCode(QString());
168 #endif
170 window->afterScriptExecution();
172 // let's try to convert the return value
173 if (success && comp.value())
174 return ValueToVariant( m_script->globalExec(), comp.value());
175 else
177 if ( comp.complType() == Throw )
179 UString msg = comp.value()->toString(m_script->globalExec());
180 kDebug(6070) << "WARNING: Script threw exception: " << msg.qstring();
182 return QVariant();
186 // Implementation of the debug() function
187 class TestFunctionImp : public JSObject {
188 public:
189 TestFunctionImp() : JSObject() {}
190 virtual bool implementsCall() const { return true; }
191 virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args);
194 JSValue *TestFunctionImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
196 fprintf(stderr,"--> %s\n",args[0]->toString(exec).ascii());
197 return jsUndefined();
200 void KJSProxyImpl::clear() {
201 // clear resources allocated by the interpreter, and make it ready to be used by another page
202 // We have to keep it, so that the Window object for the part remains the same.
203 // (we used to delete and re-create it, previously)
204 if (m_script) {
205 #ifdef KJS_DEBUGGER
206 if (m_debugWindow)
207 m_debugWindow->clearInterpreter(m_script);
208 #endif
209 m_script->clear();
211 Window *win = static_cast<Window *>(m_script->globalObject());
212 if (win) {
213 win->clear( m_script->globalExec() );
214 // re-add "debug", clear() removed it
215 m_script->globalObject()->put(m_script->globalExec(),
216 "debug", new TestFunctionImp(), Internal);
217 if ( win->part() )
218 applyUserAgent();
221 // Really delete everything that can be, so that the DOM nodes get deref'ed
222 //kDebug() << "all done -> collecting";
223 JSLock::lock();
224 while (Interpreter::collect())
226 JSLock::unlock();
229 #ifdef KJS_DEBUGGER
230 // Detach from debugging entirely if it's been turned off.
231 if (m_debugWindow && !m_debugEnabled) {
232 m_debugWindow->detach(m_script);
233 m_debugWindow = 0;
235 #endif
238 DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QString name, QString code, DOM::NodeImpl *node, bool svg)
240 initScript();
242 #ifdef KJS_DEBUGGER
243 if (m_debugWindow)
244 m_debugWindow->attach(m_script);
245 #else
246 Q_UNUSED(sourceUrl);
247 #endif
249 return KJS::Window::retrieveWindow(m_frame->m_part)->getJSLazyEventListener(
250 code, sourceUrl, m_handlerLineno, name, node, svg);
253 void KJSProxyImpl::finishedWithEvent(const DOM::Event &event)
255 // This is called when the DOM implementation has finished with a particular event. This
256 // is the case in sitations where an event has been created just for temporary usage,
257 // e.g. an image load or mouse move. Once the event has been dispatched, it is forgotten
258 // by the DOM implementation and so does not need to be cached still by the interpreter
259 ScriptInterpreter::forgetDOMObject(event.handle());
262 KJS::Interpreter *KJSProxyImpl::interpreter()
264 if (!m_script)
265 initScript();
266 return m_script;
269 void KJSProxyImpl::setDebugEnabled(bool enabled)
271 #ifdef KJS_DEBUGGER
272 m_debugEnabled = enabled;
274 // Note that we attach to the debugger only before
275 // running a script. Detaches/disabling are done between
276 // documents, at clear. Both are done so the debugger
277 // see the entire session
278 if (enabled)
279 m_debugWindow = DebugWindow::window();
280 #endif
283 bool KJSProxyImpl::debugEnabled() const
285 #ifdef KJS_DEBUGGER
286 return m_debugEnabled;
287 #else
288 return false;
289 #endif
292 void KJSProxyImpl::showDebugWindow(bool /*show*/)
294 #ifdef KJS_DEBUGGER
295 if (m_debugWindow)
296 m_debugWindow->show();
297 #else
298 //Q_UNUSED(show);
299 #endif
302 bool KJSProxyImpl::paused() const
304 #ifdef KJS_DEBUGGER
305 // if (DebugWindow::window())
306 // return DebugWindow::window()->inSession();
307 #endif
308 return false;
311 void KJSProxyImpl::dataReceived()
313 #ifdef KJS_DEBUGGER
314 // if (DebugWindow::window() && m_frame->m_part)
315 // DebugWindow::window()->sourceChanged(m_script, m_frame->m_part->url().url());
316 #endif
319 KJS_QT_UNICODE_IMPL
321 void KJSProxyImpl::initScript()
323 if (m_script)
324 return;
326 // Build the global object - which is a Window instance
327 JSGlobalObject *globalObject( new Window(m_frame) );
329 // Create a KJS interpreter for this part
330 m_script = new KJS::ScriptInterpreter(globalObject, m_frame);
331 KJS_QT_UNICODE_SET;
332 globalObject->setPrototype(m_script->builtinObjectPrototype());
334 #ifdef KJS_DEBUGGER
335 //m_script->setDebuggingEnabled(m_debugEnabled);
336 #endif
337 //m_script->enableDebug();
338 globalObject->put(m_script->globalExec(),
339 "debug", new TestFunctionImp(), Internal);
340 applyUserAgent();
343 void KJSProxyImpl::applyUserAgent()
345 assert( m_script );
346 QString host = m_frame->m_part->url().isLocalFile() ? "localhost" : m_frame->m_part->url().host();
347 QString userAgent = KProtocolManager::userAgentForHost(host);
348 if (userAgent.indexOf(QLatin1String("Microsoft"), 0, Qt::CaseSensitive) >= 0 ||
349 userAgent.indexOf(QLatin1String("MSIE"), 0, Qt::CaseSensitive) >= 0)
351 m_script->setCompatMode(Interpreter::IECompat);
352 #ifdef KJS_VERBOSE
353 kDebug() << "Setting IE compat mode";
354 #endif
356 else
357 // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape
358 if (userAgent.indexOf(QLatin1String("Mozilla"), 0, Qt::CaseSensitive) >= 0 &&
359 userAgent.indexOf(QLatin1String("compatible"), 0, Qt::CaseSensitive) == -1 &&
360 userAgent.indexOf(QLatin1String("KHTML"), 0, Qt::CaseSensitive) == -1)
362 m_script->setCompatMode(Interpreter::NetscapeCompat);
363 #ifdef KJS_VERBOSE
364 kDebug() << "Setting NS compat mode";
365 #endif
369 // Helper method, so that all classes which need jScript() don't need to be added
370 // as friend to KHTMLPart
371 KJSProxy * KJSProxy::proxy( KHTMLPart *part )
373 return part->jScript();
376 // initialize HTML module
377 KJSProxy *kjs_html_init(khtml::ChildFrame *childframe)
379 return new KJSProxyImpl(childframe);