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;
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;
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
void append(const T &value)
DOMString codeAsString() const
Returns the name of this error.
QString number(int n, int base)
QString toHtmlEscaped() const const
KGuiItem cont()
int removeAll(const T &value)
static bool isEventExceptionCode(int exceptioncode)
QByteArray toLatin1() const const
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))
static bool isCSSExceptionCode(int exceptioncode)
bool contains(const T &value) const const
int size() const const
KJS_EXTERNAL_EXPORT QString qstring() const
QString i18n(const char *text, const TYPE &arg...)
bool isEmpty() const const
const T & at(int i) const const
DOMString codeAsString() const
Returns the name of this error.
Definition: dom_node.cpp:624
bool isEmpty() const const
DOMString codeAsString() const
Returns the name of this error.
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
QString name(StandardShortcut id)
Base class for all objects in this binding.
Definition: kjs_binding.h:78
char * data()
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri May 20 2022 04:21:24 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.