KHtml

kjs_proxy.cpp
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (C) 1999-2001 Harri Porten ([email protected])
4  * Copyright (C) 2001,2003 Peter Kelly ([email protected])
5  * Copyright (C) 2001-2003 David Faure ([email protected])
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "kjs_proxy.h"
23 
24 #include "kjs_window.h"
25 #include "kjs_events.h"
26 #ifdef KJS_DEBUGGER
27 #include "debugger/debugwindow.h"
28 #endif
29 #include <xml/dom_nodeimpl.h>
30 #include <khtmlpart_p.h>
31 #include <khtml_part.h>
32 #include <kprotocolmanager.h>
33 #include "khtml_debug.h"
34 #include <klocalizedstring.h>
35 #include <assert.h>
36 #include <kjs/function.h>
37 #include <kjs/JSLock.h>
38 
39 using namespace KJS;
40 using namespace KJSDebugger;
41 
42 #ifndef NDEBUG
43 int KJSProxy::s_count = 0;
44 #endif
45 
46 KJSProxy::KJSProxy(khtml::ChildFrame *frame)
47 {
48  m_script = nullptr;
49  m_frame = frame;
50  m_debugEnabled = false;
51  m_running = 0;
52  m_handlerLineno = 0;
53 #ifndef NDEBUG
54  s_count++;
55 #endif
56 }
57 
58 KJSProxy::~KJSProxy()
59 {
60  if (m_script) {
61  //qCDebug(KHTML_LOG) << "KJSProxy::~KJSProxyImpl clearing global object " << m_script->globalObject().imp();
62  // This allows to delete the global-object properties, like all the protos
63  m_script->globalObject()->clearProperties();
64  //qCDebug(KHTML_LOG) << "KJSProxy::~KJSProxyImpl garbage collecting";
65 
66  JSLock::lock();
67  while (Interpreter::collect())
68  ;
69  JSLock::unlock();
70  //qCDebug(KHTML_LOG) << "KJSProxy::~KJSProxyImpl deleting interpreter " << m_script;
71  delete m_script;
72  //qCDebug(KHTML_LOG) << "KJSProxy::~KJSProxyImpl garbage collecting again";
73  // Garbage collect - as many times as necessary
74  // (we could delete an object which was holding another object, so
75  // the deref() will happen too late for deleting the impl of the 2nd object).
76  JSLock::lock();
77  while (Interpreter::collect())
78  ;
79  JSLock::unlock();
80  }
81 
82 #ifndef NDEBUG
83  s_count--;
84  // If it was the last interpreter, we should have nothing left
85 #ifdef KJS_DEBUG_MEM
86  if (s_count == 0) {
87  Interpreter::finalCheck();
88  }
89 #endif
90 #endif
91 }
92 
93 QVariant KJSProxy::evaluate(QString filename, int baseLine,
94  const QString &str, const DOM::Node &n, Completion *completion)
95 {
96  ++m_running;
97  // evaluate code. Returns the JS return value or an invalid QVariant
98  // if there was none, an error occurred or the type couldn't be converted.
99 
100  initScript();
101  // inlineCode is true for <a href="javascript:doSomething()">
102  // and false for <script>doSomething()</script>. Check if it has the
103  // expected value in all cases.
104  // See smart window.open policy for where this is used.
105  bool inlineCode = filename.isNull();
106  //qCDebug(KHTML_LOG) << "KJSProxy::evaluate inlineCode=" << inlineCode;
107 
108 #ifdef KJS_DEBUGGER
109  if (inlineCode) {
110  filename = "(unknown file)";
111  }
112  if (m_debugWindow) {
113  m_debugWindow->attach(m_script);
114  }
115 #else
116  Q_UNUSED(baseLine);
117 #endif
118 
119  m_script->setInlineCode(inlineCode);
120  Window *window = Window::retrieveWindow(m_frame->m_part);
121  KJS::JSValue *thisNode = n.isNull() ? Window::retrieve(m_frame->m_part) : getDOMNode(m_script->globalExec(), n.handle());
122 
123  UString code(str);
124 
125  m_script->startCPUGuard();
126  Completion comp = m_script->evaluate(filename, baseLine, code, thisNode);
127  m_script->stopCPUGuard();
128 
129  bool success = (comp.complType() == KJS::Normal) || (comp.complType() == ReturnValue);
130 
131  if (completion) {
132  *completion = comp;
133  }
134 
135 #ifdef KJS_DEBUGGER
136  // KJSDebugWin::debugWindow()->setCode(QString());
137 #endif
138 
139  window->afterScriptExecution();
140 
141  --m_running;
142 
143  // let's try to convert the return value
144  if (success && comp.value()) {
145  return ValueToVariant(m_script->globalExec(), comp.value());
146  } else {
147  if (comp.complType() == Throw) {
148  UString msg = comp.value()->toString(m_script->globalExec());
149  // qCDebug(KHTML_LOG) << "WARNING: Script threw exception: " << msg.qstring();
150  }
151  return QVariant();
152  }
153 }
154 
155 bool KJSProxy::isRunningScript()
156 {
157  return m_running != 0;
158 }
159 
160 // Implementation of the debug() function
161 class TestFunctionImp : public JSObject
162 {
163 public:
164  TestFunctionImp() : JSObject() {}
165  bool implementsCall() const override
166  {
167  return true;
168  }
169  JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) override;
170 };
171 
172 JSValue *TestFunctionImp::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &args)
173 {
174  fprintf(stderr, "--> %s\n", args[0]->toString(exec).ascii());
175  return jsUndefined();
176 }
177 
178 void KJSProxy::clear()
179 {
180  // clear resources allocated by the interpreter, and make it ready to be used by another page
181  // We have to keep it, so that the Window object for the part remains the same.
182  // (we used to delete and re-create it, previously)
183  if (m_script) {
184 #ifdef KJS_DEBUGGER
185  if (m_debugWindow) {
186  m_debugWindow->clearInterpreter(m_script);
187  }
188 #endif
189  m_script->clear();
190 
191  Window *win = static_cast<Window *>(m_script->globalObject());
192  if (win) {
193  win->clear(m_script->globalExec());
194  // re-add "debug", clear() removed it
195  m_script->globalObject()->put(m_script->globalExec(),
196  "debug", new TestFunctionImp(), Internal);
197  if (win->part()) {
198  applyUserAgent();
199  }
200  }
201 
202  // Really delete everything that can be, so that the DOM nodes get deref'ed
203  //qCDebug(KHTML_LOG) << "all done -> collecting";
204  JSLock::lock();
205  while (Interpreter::collect())
206  ;
207  JSLock::unlock();
208  }
209 
210 #ifdef KJS_DEBUGGER
211  // Detach from debugging entirely if it's been turned off.
212  if (m_debugWindow && !m_debugEnabled) {
213  m_debugWindow->detach(m_script);
214  m_debugWindow = 0;
215  }
216 #endif
217 }
218 
219 DOM::EventListener *KJSProxy::createHTMLEventHandler(QString sourceUrl, QString name, QString code, DOM::NodeImpl *node, bool svg)
220 {
221  initScript();
222 
223 #ifdef KJS_DEBUGGER
224  if (m_debugWindow) {
225  m_debugWindow->attach(m_script);
226  }
227 #else
228  Q_UNUSED(sourceUrl);
229 #endif
230 
231  return KJS::Window::retrieveWindow(m_frame->m_part)->getJSLazyEventListener(
232  code, sourceUrl, m_handlerLineno, name, node, svg);
233 }
234 
235 void KJSProxy::finishedWithEvent(const DOM::Event &event)
236 {
237  // This is called when the DOM implementation has finished with a particular event. This
238  // is the case in sitations where an event has been created just for temporary usage,
239  // e.g. an image load or mouse move. Once the event has been dispatched, it is forgotten
240  // by the DOM implementation and so does not need to be cached still by the interpreter
241  ScriptInterpreter::forgetDOMObject(event.handle());
242 }
243 
244 KJS::Interpreter *KJSProxy::interpreter()
245 {
246  if (!m_script) {
247  initScript();
248  }
249  return m_script;
250 }
251 
252 void KJSProxy::setDebugEnabled(bool enabled)
253 {
254 #ifdef KJS_DEBUGGER
255  m_debugEnabled = enabled;
256 
257  // Note that we attach to the debugger only before
258  // running a script. Detaches/disabling are done between
259  // documents, at clear. Both are done so the debugger
260  // see the entire session
261  if (enabled) {
262  m_debugWindow = DebugWindow::window();
263  }
264 #else
265  Q_UNUSED(enabled)
266 #endif
267 }
268 
269 bool KJSProxy::debugEnabled() const
270 {
271 #ifdef KJS_DEBUGGER
272  return m_debugEnabled;
273 #else
274  return false;
275 #endif
276 }
277 
278 void KJSProxy::showDebugWindow(bool /*show*/)
279 {
280 #ifdef KJS_DEBUGGER
281  if (m_debugWindow) {
282  m_debugWindow->show();
283  }
284 #else
285  //Q_UNUSED(show);
286 #endif
287 }
288 
289 bool KJSProxy::paused() const
290 {
291 #ifdef KJS_DEBUGGER
292  // if (DebugWindow::window())
293  // return DebugWindow::window()->inSession();
294 #endif
295  return false;
296 }
297 
298 KJS_QT_UNICODE_IMPL
299 
300 void KJSProxy::initScript()
301 {
302  if (m_script) {
303  return;
304  }
305 
306  // Build the global object - which is a Window instance
307  JSGlobalObject *globalObject(new Window(m_frame));
308 
309  // Create a KJS interpreter for this part
310  m_script = new KJS::ScriptInterpreter(globalObject, m_frame);
311  KJS_QT_UNICODE_SET;
312  globalObject->setPrototype(m_script->builtinObjectPrototype());
313 
314 #ifdef KJS_DEBUGGER
315  //m_script->setDebuggingEnabled(m_debugEnabled);
316 #endif
317  //m_script->enableDebug();
318  globalObject->put(m_script->globalExec(),
319  "debug", new TestFunctionImp(), Internal);
320  applyUserAgent();
321 
322 #ifdef KJS_DEBUGGER
323  // Attach debugger as early as possible as not all scrips have a direct DOM-relation
324  // NOTE: attach can be called multiple times
325  if (m_debugEnabled) {
326  m_debugWindow->attach(m_script);
327  }
328 #endif
329 }
330 
331 void KJSProxy::applyUserAgent()
332 {
333  assert(m_script);
334  QUrl url = m_frame->m_part.data()->url();
335  QString host = url.isLocalFile() ? "localhost" : url.host();
337  if (userAgent.indexOf(QLatin1String("Microsoft"), 0, Qt::CaseSensitive) >= 0 ||
338  userAgent.indexOf(QLatin1String("MSIE"), 0, Qt::CaseSensitive) >= 0) {
339  m_script->setCompatMode(Interpreter::IECompat);
340 #ifdef KJS_VERBOSE
341  qCDebug(KHTML_LOG) << "Setting IE compat mode";
342 #endif
343  } else
344  // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape
345  if (userAgent.indexOf(QLatin1String("Mozilla"), 0, Qt::CaseSensitive) >= 0 &&
346  userAgent.indexOf(QLatin1String("compatible"), 0, Qt::CaseSensitive) == -1 &&
347  userAgent.indexOf(QLatin1String("KHTML"), 0, Qt::CaseSensitive) == -1) {
348  m_script->setCompatMode(Interpreter::NetscapeCompat);
349 #ifdef KJS_VERBOSE
350  qCDebug(KHTML_LOG) << "Setting NS compat mode";
351 #endif
352  }
353 }
354 
355 // Helper method, so that all classes which need jScript() don't need to be added
356 // as friend to KHTMLPart
357 KJSProxy *KJSProxy::proxy(KHTMLPart *part)
358 {
359  return part->jScript();
360 }
QString url(QUrl::FormattingOptions options) const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
The Node interface is the primary datatype for the entire Document Object Model.
Definition: dom_node.h:278
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
static bool collect()
static QString userAgentForHost(const QString &hostname)
QString host(QUrl::ComponentFormattingOptions options) const const
Proxy class serving as interface when being dlopen&#39;ed.
Definition: kjs_proxy.h:61
This class is khtml&#39;s main class.
Definition: khtml_part.h:208
bool isNull() const const
We inherit from Interpreter, to save a pointer to the HTML part that the interpreter runs for...
Definition: kjs_binding.h:96
EventImpl * handle() const
CaseSensitive
bool isNull() const
tests if this Node is 0.
Definition: dom_node.h:928
ComplType complType() const
Introduced in DOM Level 2.
Definition: dom2_events.h:69
Introduced in DOM Level 2.
Definition: dom2_events.h:116
JSValue * value() const
char * toString(const T &value)
Window
Definition: kjs_window.h:393
bool isLocalFile() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:05 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.