KHtml

kjs_binding.cpp
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (C) 1999-2003 Harri Porten ([email protected])
4  * Copyright (C) 2001-2003 David Faure ([email protected])
5  * Copyright (C) 2003 Apple Computer, Inc.
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_binding.h"
23 
24 #include <config-khtml.h>
25 #if HAVE_VALGRIND_MEMCHECK_H
26 
27 #include <valgrind/memcheck.h>
28 #define VALGRIND_SUPPORT
29 
30 #endif
31 
32 #include "kjs_dom.h"
33 #include "kjs_range.h"
34 
35 #include <dom/css_stylesheet.h>
36 #include <dom/dom_exception.h>
37 #include <dom/dom2_range.h>
38 #include <dom/dom3_xpath.h>
39 #include <xml/dom2_eventsimpl.h>
40 #include <khtmlpart_p.h>
41 
42 #include "khtml_debug.h"
43 #include <kparts/browserextension.h>
44 #include <kmessagebox.h>
45 #include <QTextDocument> // Qt::escape
46 
47 #ifdef KJS_DEBUGGER
48 #include "debugger/debugwindow.h"
49 #endif
50 
51 #include <QList>
52 
53 #include <assert.h>
54 
55 using namespace KJSDebugger;
56 
57 namespace KJS
58 {
59 
60 UString DOMObject::toString(ExecState *) const
61 {
62  return "[object " + className() + "]";
63 }
64 
65 HashMap<void *, DOMObject *> *ScriptInterpreter::s_allDomObjects;
66 
67 typedef QList<ScriptInterpreter *> InterpreterList;
68 static InterpreterList *interpreterList;
69 
70 ScriptInterpreter::ScriptInterpreter(JSGlobalObject *global, khtml::ChildFrame *frame)
71  : Interpreter(global), m_frame(frame),
72  m_evt(nullptr), m_inlineCode(false), m_timerCallback(false)
73 {
74 #ifdef KJS_VERBOSE
75  qCDebug(KHTML_LOG) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_frame;
76 #endif
77  if (!interpreterList) {
78  interpreterList = new InterpreterList;
79  }
80  interpreterList->append(this);
81 }
82 
83 ScriptInterpreter::~ScriptInterpreter()
84 {
85 #ifdef KJS_VERBOSE
86  qCDebug(KHTML_LOG) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_frame;
87 #endif
88  assert(interpreterList && interpreterList->contains(this));
89  interpreterList->removeAll(this);
90  if (interpreterList->isEmpty()) {
91  delete interpreterList;
92  interpreterList = nullptr;
93  }
94 }
95 
96 void ScriptInterpreter::forgetDOMObject(void *objectHandle)
97 {
98  if (!interpreterList) {
99  return;
100  }
101 
102  for (int i = 0; i < interpreterList->size(); ++i) {
103  interpreterList->at(i)->m_domObjects.remove(objectHandle);
104  }
105  allDomObjects()->remove(objectHandle);
106 }
107 
108 void ScriptInterpreter::mark(bool isMain)
109 {
110  Interpreter::mark(isMain);
111 #ifdef KJS_VERBOSE
112  qCDebug(KHTML_LOG) << "ScriptInterpreter::mark " << this << " marking " << m_domObjects.size() << " DOM objects";
113 #endif
114  HashMap<void *, DOMObject *>::iterator it = m_domObjects.begin();
115  while (it != m_domObjects.end()) {
116  DOMObject *obj = it->second;
117  if (obj->shouldMark()) {
118  obj->mark();
119  }
120  ++it;
121  }
122 }
123 
124 KParts::ReadOnlyPart *ScriptInterpreter::part() const
125 {
126  return m_frame->m_part.data();
127 }
128 
129 bool ScriptInterpreter::isWindowOpenAllowed() const
130 {
131  if (m_evt) {
132  int id = m_evt->handle()->id();
133  bool eventOk = ( // mouse events
134  id == DOM::EventImpl::CLICK_EVENT ||
135  id == DOM::EventImpl::MOUSEUP_EVENT || id == DOM::EventImpl::MOUSEDOWN_EVENT ||
136  id == DOM::EventImpl::KHTML_ECMA_CLICK_EVENT || id == DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT ||
137  // keyboard events
138  id == DOM::EventImpl::KEYDOWN_EVENT || id == DOM::EventImpl::KEYPRESS_EVENT ||
139  id == DOM::EventImpl::KEYUP_EVENT ||
140  // other accepted events
141  id == DOM::EventImpl::SELECT_EVENT || id == DOM::EventImpl::CHANGE_EVENT ||
142  id == DOM::EventImpl::SUBMIT_EVENT);
143  // qCDebug(KHTML_LOG) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk;
144  if (eventOk) {
145  return true;
146  }
147  } else { // no event
148  if (m_inlineCode && !m_timerCallback) {
149  // This is the <a href="javascript:window.open('...')> case -> we let it through
150  return true;
151  // qCDebug(KHTML_LOG) << "Window.open, smart policy, no event, inline code -> ok";
152  } else { // This is the <script>window.open(...)</script> case or a timer callback -> block it
153  // qCDebug(KHTML_LOG) << "Window.open, smart policy, no event, <script> tag -> refused";
154  }
155  }
156  return false;
157 }
158 
159 bool ScriptInterpreter::s_disableCPUGuard = false;
160 
161 void ScriptInterpreter::startCPUGuard()
162 {
163  if (s_disableCPUGuard) {
164  return;
165  }
166 
167  unsigned time = 5000;
168 #ifdef VALGRIND_SUPPORT
169  if (RUNNING_ON_VALGRIND) {
170  time *= 50;
171  }
172 #endif
173 
174  setTimeoutTime(time);
175  startTimeoutCheck();
176 }
177 
178 void ScriptInterpreter::stopCPUGuard()
179 {
180  if (s_disableCPUGuard) {
181  return;
182  }
183  stopTimeoutCheck();
184 }
185 
186 bool ScriptInterpreter::shouldInterruptScript() const
187 {
188 #ifdef KJS_DEBUGGER
189  if (DebugWindow::isBlocked()) {
190  return false;
191  }
192 #endif
193 
194  // qCDebug(KHTML_LOG) << "alarmhandler";
195  return KMessageBox::warningYesNo(nullptr, 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 stop the script?"), i18n("JavaScript"), KGuiItem(i18n("&Stop Script")), KStandardGuiItem::cont(), "kjscupguard_alarmhandler") == KMessageBox::Yes;
196 }
197 
198 UString::UString(const QString &d)
199 {
200  if (d.length() > UString::maxUChars()) {
201  m_rep = &Rep::null;
202  return;
203  }
204 
205  unsigned int len = d.length();
206  UChar *dat = static_cast<UChar *>(fastMalloc(sizeof(UChar) * len));
207  memcpy(dat, d.unicode(), len * sizeof(UChar));
208  m_rep = UString::Rep::create(dat, len);
209 }
210 
211 UString::UString(const DOM::DOMString &d)
212 {
213  if (d.isNull()) {
214  // we do a conversion here as null DOMStrings shouldn't cross
215  // the boundary to kjs. They should either be empty strings
216  // or explicitly converted to KJS::Null via getString().
217  m_rep = &Rep::empty;
218  return;
219  }
220  if (d.length() > UString::maxUChars()) {
221  m_rep = &Rep::null;
222  return;
223  }
224 
225  unsigned int len = d.length();
226  UChar *dat = static_cast<UChar *>(fastMalloc(sizeof(UChar) * len));
227  memcpy(dat, d.unicode(), len * sizeof(UChar));
228  m_rep = UString::Rep::create(dat, len);
229 }
230 
231 DOM::DOMString UString::domString() const
232 {
233  return DOM::DOMString((QChar *) data(), size());
234 }
235 
236 QString UString::qstring() const
237 {
238  return QString((QChar *) data(), size());
239 }
240 
241 DOM::DOMString Identifier::domString() const
242 {
243  return DOM::DOMString((QChar *) data(), size());
244 }
245 
246 QString Identifier::qstring() const
247 {
248  return QString((QChar *) data(), size());
249 }
250 
251 JSValue *valueGetterAdapter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot)
252 {
253  Q_UNUSED(exec);
254  return static_cast<JSValue *>(slot.customValue());
255 }
256 
257 DOM::NodeImpl *toNode(JSValue *val)
258 {
259  JSObject *obj = val->getObject();
260  if (!obj || !obj->inherits(&DOMNode::info)) {
261  return nullptr;
262  }
263 
264  const DOMNode *dobj = static_cast<const DOMNode *>(obj);
265  return dobj->impl();
266 }
267 
268 JSValue *getStringOrNull(DOM::DOMString s)
269 {
270  if (s.isNull()) {
271  return jsNull();
272  } else {
273  return jsString(s);
274  }
275 }
276 
277 DOM::DOMString valueToStringWithNullCheck(ExecState *exec, JSValue *val)
278 {
279  if (val->isNull()) {
280  return DOM::DOMString();
281  }
282  return val->toString(exec).domString();
283 }
284 
285 QVariant ValueToVariant(ExecState *exec, JSValue *val)
286 {
287  QVariant res;
288  switch (val->type()) {
289  case BooleanType:
290  res = QVariant(val->toBoolean(exec));
291  break;
292  case NumberType:
293  res = QVariant(val->toNumber(exec));
294  break;
295  case StringType:
296  res = QVariant(val->toString(exec).qstring());
297  break;
298  default:
299  // everything else will be 'invalid'
300  break;
301  }
302  return res;
303 }
304 
305 void setDOMException(ExecState *exec, int internalCode)
306 {
307  if (internalCode == 0 || exec->hadException()) {
308  return;
309  }
310 
311  const char *type = nullptr;
312 
313  DOMString name;
314  DOMString exceptionString;
315  JSObject *errorObject = nullptr;
316  int code = -1; // this will get the public exception code,
317  // as opposed to the internal one
318 
319  // ### we should probably introduce classes for things other than range + core
320  if (DOM::RangeException::isRangeExceptionCode(internalCode)) {
321  type = "DOM Range";
322  code = internalCode - DOM::RangeException::_EXCEPTION_OFFSET;
323  name = DOM::RangeException::codeAsString(code);
324  errorObject = new RangeException(exec);
325  } else if (DOM::CSSException::isCSSExceptionCode(internalCode)) {
326  type = "CSS";
327  code = internalCode - DOM::CSSException::_EXCEPTION_OFFSET;
328  name = DOM::CSSException::codeAsString(code);
329  } else if (DOM::EventException::isEventExceptionCode(internalCode)) {
330  type = "DOM Events";
331  code = internalCode - DOM::EventException::_EXCEPTION_OFFSET;
333  } else if (DOM::XPathException::isXPathExceptionCode(internalCode)) {
334  type = "XPath";
335  code = internalCode - DOM::XPathException::_EXCEPTION_OFFSET;
336  name = DOM::XPathException::codeAsString(code);
337  } else {
338  // Generic DOM.
339  type = "DOM";
340  code = internalCode;
341  name = DOM::DOMException::codeAsString(code);
342  errorObject = new JSDOMException(exec);
343  }
344 
345  if (!errorObject) {
346  // 100 characters is a big enough buffer, because there are:
347  // 13 characters in the message
348  // 10 characters in the longest type, "DOM Events"
349  // 27 characters in the longest name, "NO_MODIFICATION_ALLOWED_ERR"
350  // 20 or so digits in the longest integer's ASCII form (even if int is 64-bit)
351  // 1 byte for a null character
352  // That adds up to about 70 bytes.
353  char buffer[100];
354 
355  if (!name.isEmpty()) {
356  qsnprintf(buffer, 99, "%s: %s Exception %d", name.string().toLatin1().data(), type, code);
357  } else {
358  qsnprintf(buffer, 99, "%s Exception %d", type, code);
359  }
360  errorObject = throwError(exec, GeneralError, buffer);
361  } else {
362  exec->setException(errorObject);
363  }
364 
365  errorObject->put(exec, exec->propertyNames().name, jsString(UString(type) + " Exception"));
366  errorObject->put(exec, exec->propertyNames().message, jsString(name));
367  errorObject->put(exec, "code", jsNumber(code));
368 }
369 
370 QString valueToString(KJS::JSValue *value)
371 {
372  switch (value->type()) {
373  case KJS::NumberType: {
374  double v = 0.0;
375  value->getNumber(v);
376  return QString::number(v);
377  }
378  case KJS::BooleanType:
379  return value->getBoolean() ? "true" : "false";
380  case KJS::StringType: {
381  KJS::UString s;
382  value->getString(s);
383  return '"' + s.qstring() + '"';
384  }
385  case KJS::UndefinedType:
386  return "undefined";
387  case KJS::NullType:
388  return "null";
389  case KJS::ObjectType:
390  return "[object " + static_cast<KJS::JSObject *>(value)->className().qstring() + "]";
391  case KJS::GetterSetterType:
392  case KJS::UnspecifiedType:
393  default:
394  return QString();
395  }
396 }
397 
398 QString exceptionToString(ExecState *exec, JSValue *exceptionObj)
399 {
400  QString exceptionMsg = valueToString(exceptionObj);
401 
402  // Since we purposefully bypass toString, we need to figure out
403  // string serialization ourselves.
404  //### might be easier to export class info for ErrorInstance ---
405 
406  JSObject *valueObj = exceptionObj->getObject();
407  JSValue *protoObj = valueObj ? valueObj->prototype() : nullptr;
408 
409  bool exception = false;
410  bool syntaxError = false;
411  if (protoObj == exec->lexicalInterpreter()->builtinSyntaxErrorPrototype()) {
412  exception = true;
413  syntaxError = true;
414  }
415 
416  if (protoObj == exec->lexicalInterpreter()->builtinErrorPrototype() ||
417  protoObj == exec->lexicalInterpreter()->builtinEvalErrorPrototype() ||
418  protoObj == exec->lexicalInterpreter()->builtinReferenceErrorPrototype() ||
419  protoObj == exec->lexicalInterpreter()->builtinRangeErrorPrototype() ||
420  protoObj == exec->lexicalInterpreter()->builtinTypeErrorPrototype() ||
421  protoObj == exec->lexicalInterpreter()->builtinURIErrorPrototype()) {
422  exception = true;
423  }
424 
425  if (!exception) {
426  return exceptionMsg;
427  }
428 
429  // Clear exceptions temporarily so we can get/call a few things.
430  // We memorize the old exception first, of course. Note that
431  // This is not always the same as exceptionObj since we may be
432  // asked to translate a non-active exception
433  JSValue *oldExceptionObj = exec->exception();
434  exec->clearException();
435 
436  // We want to serialize the syntax errors ourselves, to provide the line number.
437  // The URL is in "sourceURL" and the line is in "line"
438  // ### TODO: Perhaps we want to use 'sourceId' in case of eval contexts.
439  if (syntaxError) {
440  JSValue *lineValue = valueObj->get(exec, "line");
441  JSValue *urlValue = valueObj->get(exec, "sourceURL");
442 
443  int line = lineValue->toNumber(exec);
444  QString url = urlValue->toString(exec).qstring();
445  exceptionMsg = i18n("Parse error at %1 line %2",
446  url.toHtmlEscaped(), line + 1);
447  } else {
448  // ### it's still not 100% safe to call toString here, even on
449  // native exception objects, since someone might have changed the toString property
450  // of the exception prototype, but I'll punt on this case for now.
451  exceptionMsg = exceptionObj->toString(exec).qstring();
452  }
453  exec->setException(oldExceptionObj);
454  return exceptionMsg;
455 }
456 
457 } //namespace KJS
Base class for all objects in this binding.
Definition: kjs_binding.h:78
KJS_EXTERNAL_EXPORT QString qstring() const
KGuiItem cont()
void setException(JSValue *e)
QSize size() const const
QString number(int n, int base)
DOMString codeAsString() const
Returns the name of this error.
Definition: dom_node.cpp:624
JSValue * exception() const
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
bool hadException() const
QString toHtmlEscaped() const const
DOMString codeAsString() const
Returns the name of this error.
DOMString codeAsString() const
Returns the name of this error.
static bool isEventExceptionCode(int exceptioncode)
QString i18n(const char *text, const TYPE &arg...)
JSObject * builtinErrorPrototype() const
const QChar * unicode() const const
QByteArray toLatin1() const const
void clearException()
int length() const const
char * data()
Interpreter * lexicalInterpreter() const
static bool isCSSExceptionCode(int exceptioncode)
ButtonCode warningYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 16 2021 22:47:57 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.