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

KDE's Doxygen guidelines are available online.