File indexing completed on 2024-10-06 09:37:06
0001 /* 0002 * This file is part of the KDE libraries 0003 * Copyright (C) 1999-2003 Harri Porten (porten@kde.org) 0004 * Copyright (C) 2001-2003 David Faure (faure@kde.org) 0005 * Copyright (C) 2003 Apple Computer, Inc. 0006 * 0007 * This library is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU Library General Public 0009 * License as published by the Free Software Foundation; either 0010 * version 2 of the License, or (at your option) any later version. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Library General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Library General Public 0018 * License along with this library; if not, write to the Free Software 0019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0020 */ 0021 0022 #include "kjs_binding.h" 0023 0024 #include <config-khtml.h> 0025 #if HAVE_VALGRIND_MEMCHECK_H 0026 0027 #include <valgrind/memcheck.h> 0028 #define VALGRIND_SUPPORT 0029 0030 #endif 0031 0032 #include "kjs_dom.h" 0033 #include "kjs_range.h" 0034 0035 #include <dom/css_stylesheet.h> 0036 #include <dom/dom_exception.h> 0037 #include <dom/dom2_range.h> 0038 #include <dom/dom3_xpath.h> 0039 #include <xml/dom2_eventsimpl.h> 0040 #include <khtmlpart_p.h> 0041 0042 #include "khtml_debug.h" 0043 #include <kparts/browserextension.h> 0044 #include <kmessagebox.h> 0045 #include <QTextDocument> // Qt::escape 0046 0047 #ifdef KJS_DEBUGGER 0048 #include "debugger/debugwindow.h" 0049 #endif 0050 0051 #include <QList> 0052 0053 #include <assert.h> 0054 0055 using namespace KJSDebugger; 0056 0057 namespace KJS 0058 { 0059 0060 UString DOMObject::toString(ExecState *) const 0061 { 0062 return "[object " + className() + "]"; 0063 } 0064 0065 HashMap<void *, DOMObject *> *ScriptInterpreter::s_allDomObjects; 0066 0067 typedef QList<ScriptInterpreter *> InterpreterList; 0068 static InterpreterList *interpreterList; 0069 0070 ScriptInterpreter::ScriptInterpreter(JSGlobalObject *global, khtml::ChildFrame *frame) 0071 : Interpreter(global), m_frame(frame), 0072 m_evt(nullptr), m_inlineCode(false), m_timerCallback(false) 0073 { 0074 #ifdef KJS_VERBOSE 0075 qCDebug(KHTML_LOG) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_frame; 0076 #endif 0077 if (!interpreterList) { 0078 interpreterList = new InterpreterList; 0079 } 0080 interpreterList->append(this); 0081 } 0082 0083 ScriptInterpreter::~ScriptInterpreter() 0084 { 0085 #ifdef KJS_VERBOSE 0086 qCDebug(KHTML_LOG) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_frame; 0087 #endif 0088 assert(interpreterList && interpreterList->contains(this)); 0089 interpreterList->removeAll(this); 0090 if (interpreterList->isEmpty()) { 0091 delete interpreterList; 0092 interpreterList = nullptr; 0093 } 0094 } 0095 0096 void ScriptInterpreter::forgetDOMObject(void *objectHandle) 0097 { 0098 if (!interpreterList) { 0099 return; 0100 } 0101 0102 for (int i = 0; i < interpreterList->size(); ++i) { 0103 interpreterList->at(i)->m_domObjects.remove(objectHandle); 0104 } 0105 allDomObjects()->remove(objectHandle); 0106 } 0107 0108 void ScriptInterpreter::mark(bool isMain) 0109 { 0110 Interpreter::mark(isMain); 0111 #ifdef KJS_VERBOSE 0112 qCDebug(KHTML_LOG) << "ScriptInterpreter::mark " << this << " marking " << m_domObjects.size() << " DOM objects"; 0113 #endif 0114 HashMap<void *, DOMObject *>::iterator it = m_domObjects.begin(); 0115 while (it != m_domObjects.end()) { 0116 DOMObject *obj = it->second; 0117 if (obj->shouldMark()) { 0118 obj->mark(); 0119 } 0120 ++it; 0121 } 0122 } 0123 0124 KParts::ReadOnlyPart *ScriptInterpreter::part() const 0125 { 0126 return m_frame->m_part.data(); 0127 } 0128 0129 bool ScriptInterpreter::isWindowOpenAllowed() const 0130 { 0131 if (m_evt) { 0132 int id = m_evt->handle()->id(); 0133 bool eventOk = ( // mouse events 0134 id == DOM::EventImpl::CLICK_EVENT || 0135 id == DOM::EventImpl::MOUSEUP_EVENT || id == DOM::EventImpl::MOUSEDOWN_EVENT || 0136 id == DOM::EventImpl::KHTML_ECMA_CLICK_EVENT || id == DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT || 0137 // keyboard events 0138 id == DOM::EventImpl::KEYDOWN_EVENT || id == DOM::EventImpl::KEYPRESS_EVENT || 0139 id == DOM::EventImpl::KEYUP_EVENT || 0140 // other accepted events 0141 id == DOM::EventImpl::SELECT_EVENT || id == DOM::EventImpl::CHANGE_EVENT || 0142 id == DOM::EventImpl::SUBMIT_EVENT); 0143 // qCDebug(KHTML_LOG) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk; 0144 if (eventOk) { 0145 return true; 0146 } 0147 } else { // no event 0148 if (m_inlineCode && !m_timerCallback) { 0149 // This is the <a href="javascript:window.open('...')> case -> we let it through 0150 return true; 0151 // qCDebug(KHTML_LOG) << "Window.open, smart policy, no event, inline code -> ok"; 0152 } else { // This is the <script>window.open(...)</script> case or a timer callback -> block it 0153 // qCDebug(KHTML_LOG) << "Window.open, smart policy, no event, <script> tag -> refused"; 0154 } 0155 } 0156 return false; 0157 } 0158 0159 bool ScriptInterpreter::s_disableCPUGuard = false; 0160 0161 void ScriptInterpreter::startCPUGuard() 0162 { 0163 if (s_disableCPUGuard) { 0164 return; 0165 } 0166 0167 unsigned time = 5000; 0168 #ifdef VALGRIND_SUPPORT 0169 if (RUNNING_ON_VALGRIND) { 0170 time *= 50; 0171 } 0172 #endif 0173 0174 setTimeoutTime(time); 0175 startTimeoutCheck(); 0176 } 0177 0178 void ScriptInterpreter::stopCPUGuard() 0179 { 0180 if (s_disableCPUGuard) { 0181 return; 0182 } 0183 stopTimeoutCheck(); 0184 } 0185 0186 bool ScriptInterpreter::shouldInterruptScript() const 0187 { 0188 #ifdef KJS_DEBUGGER 0189 if (DebugWindow::isBlocked()) { 0190 return false; 0191 } 0192 #endif 0193 0194 // qCDebug(KHTML_LOG) << "alarmhandler"; 0195 return KMessageBox::warningTwoActions(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::PrimaryAction; 0196 } 0197 0198 UString::UString(const QString &d) 0199 { 0200 if (d.length() > UString::maxUChars()) { 0201 m_rep = &Rep::null; 0202 return; 0203 } 0204 0205 unsigned int len = d.length(); 0206 UChar *dat = static_cast<UChar *>(fastMalloc(sizeof(UChar) * len)); 0207 memcpy(dat, d.unicode(), len * sizeof(UChar)); 0208 m_rep = UString::Rep::create(dat, len); 0209 } 0210 0211 UString::UString(const DOM::DOMString &d) 0212 { 0213 if (d.isNull()) { 0214 // we do a conversion here as null DOMStrings shouldn't cross 0215 // the boundary to kjs. They should either be empty strings 0216 // or explicitly converted to KJS::Null via getString(). 0217 m_rep = &Rep::empty; 0218 return; 0219 } 0220 if (d.length() > UString::maxUChars()) { 0221 m_rep = &Rep::null; 0222 return; 0223 } 0224 0225 unsigned int len = d.length(); 0226 UChar *dat = static_cast<UChar *>(fastMalloc(sizeof(UChar) * len)); 0227 memcpy(dat, d.unicode(), len * sizeof(UChar)); 0228 m_rep = UString::Rep::create(dat, len); 0229 } 0230 0231 DOM::DOMString UString::domString() const 0232 { 0233 return DOM::DOMString((QChar *) data(), size()); 0234 } 0235 0236 QString UString::qstring() const 0237 { 0238 return QString((QChar *) data(), size()); 0239 } 0240 0241 DOM::DOMString Identifier::domString() const 0242 { 0243 return DOM::DOMString((QChar *) data(), size()); 0244 } 0245 0246 QString Identifier::qstring() const 0247 { 0248 return QString((QChar *) data(), size()); 0249 } 0250 0251 JSValue *valueGetterAdapter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot) 0252 { 0253 Q_UNUSED(exec); 0254 return static_cast<JSValue *>(slot.customValue()); 0255 } 0256 0257 DOM::NodeImpl *toNode(JSValue *val) 0258 { 0259 JSObject *obj = val->getObject(); 0260 if (!obj || !obj->inherits(&DOMNode::info)) { 0261 return nullptr; 0262 } 0263 0264 const DOMNode *dobj = static_cast<const DOMNode *>(obj); 0265 return dobj->impl(); 0266 } 0267 0268 JSValue *getStringOrNull(DOM::DOMString s) 0269 { 0270 if (s.isNull()) { 0271 return jsNull(); 0272 } else { 0273 return jsString(s); 0274 } 0275 } 0276 0277 DOM::DOMString valueToStringWithNullCheck(ExecState *exec, JSValue *val) 0278 { 0279 if (val->isNull()) { 0280 return DOM::DOMString(); 0281 } 0282 return val->toString(exec).domString(); 0283 } 0284 0285 QVariant ValueToVariant(ExecState *exec, JSValue *val) 0286 { 0287 QVariant res; 0288 switch (val->type()) { 0289 case BooleanType: 0290 res = QVariant(val->toBoolean(exec)); 0291 break; 0292 case NumberType: 0293 res = QVariant(val->toNumber(exec)); 0294 break; 0295 case StringType: 0296 res = QVariant(val->toString(exec).qstring()); 0297 break; 0298 default: 0299 // everything else will be 'invalid' 0300 break; 0301 } 0302 return res; 0303 } 0304 0305 void setDOMException(ExecState *exec, int internalCode) 0306 { 0307 if (internalCode == 0 || exec->hadException()) { 0308 return; 0309 } 0310 0311 const char *type = nullptr; 0312 0313 DOMString name; 0314 DOMString exceptionString; 0315 JSObject *errorObject = nullptr; 0316 int code = -1; // this will get the public exception code, 0317 // as opposed to the internal one 0318 0319 // ### we should probably introduce classes for things other than range + core 0320 if (DOM::RangeException::isRangeExceptionCode(internalCode)) { 0321 type = "DOM Range"; 0322 code = internalCode - DOM::RangeException::_EXCEPTION_OFFSET; 0323 name = DOM::RangeException::codeAsString(code); 0324 errorObject = new RangeException(exec); 0325 } else if (DOM::CSSException::isCSSExceptionCode(internalCode)) { 0326 type = "CSS"; 0327 code = internalCode - DOM::CSSException::_EXCEPTION_OFFSET; 0328 name = DOM::CSSException::codeAsString(code); 0329 } else if (DOM::EventException::isEventExceptionCode(internalCode)) { 0330 type = "DOM Events"; 0331 code = internalCode - DOM::EventException::_EXCEPTION_OFFSET; 0332 name = DOM::EventException::codeAsString(code); 0333 } else if (DOM::XPathException::isXPathExceptionCode(internalCode)) { 0334 type = "XPath"; 0335 code = internalCode - DOM::XPathException::_EXCEPTION_OFFSET; 0336 name = DOM::XPathException::codeAsString(code); 0337 } else { 0338 // Generic DOM. 0339 type = "DOM"; 0340 code = internalCode; 0341 name = DOM::DOMException::codeAsString(code); 0342 errorObject = new JSDOMException(exec); 0343 } 0344 0345 if (!errorObject) { 0346 // 100 characters is a big enough buffer, because there are: 0347 // 13 characters in the message 0348 // 10 characters in the longest type, "DOM Events" 0349 // 27 characters in the longest name, "NO_MODIFICATION_ALLOWED_ERR" 0350 // 20 or so digits in the longest integer's ASCII form (even if int is 64-bit) 0351 // 1 byte for a null character 0352 // That adds up to about 70 bytes. 0353 char buffer[100]; 0354 0355 if (!name.isEmpty()) { 0356 qsnprintf(buffer, 99, "%s: %s Exception %d", name.string().toLatin1().data(), type, code); 0357 } else { 0358 qsnprintf(buffer, 99, "%s Exception %d", type, code); 0359 } 0360 errorObject = throwError(exec, GeneralError, buffer); 0361 } else { 0362 exec->setException(errorObject); 0363 } 0364 0365 errorObject->put(exec, exec->propertyNames().name, jsString(UString(type) + " Exception")); 0366 errorObject->put(exec, exec->propertyNames().message, jsString(name)); 0367 errorObject->put(exec, "code", jsNumber(code)); 0368 } 0369 0370 QString valueToString(KJS::JSValue *value) 0371 { 0372 switch (value->type()) { 0373 case KJS::NumberType: { 0374 double v = 0.0; 0375 value->getNumber(v); 0376 return QString::number(v); 0377 } 0378 case KJS::BooleanType: 0379 return value->getBoolean() ? "true" : "false"; 0380 case KJS::StringType: { 0381 KJS::UString s; 0382 value->getString(s); 0383 return '"' + s.qstring() + '"'; 0384 } 0385 case KJS::UndefinedType: 0386 return "undefined"; 0387 case KJS::NullType: 0388 return "null"; 0389 case KJS::ObjectType: 0390 return "[object " + static_cast<KJS::JSObject *>(value)->className().qstring() + "]"; 0391 case KJS::GetterSetterType: 0392 case KJS::UnspecifiedType: 0393 default: 0394 return QString(); 0395 } 0396 } 0397 0398 QString exceptionToString(ExecState *exec, JSValue *exceptionObj) 0399 { 0400 QString exceptionMsg = valueToString(exceptionObj); 0401 0402 // Since we purposefully bypass toString, we need to figure out 0403 // string serialization ourselves. 0404 //### might be easier to export class info for ErrorInstance --- 0405 0406 JSObject *valueObj = exceptionObj->getObject(); 0407 JSValue *protoObj = valueObj ? valueObj->prototype() : nullptr; 0408 0409 bool exception = false; 0410 bool syntaxError = false; 0411 if (protoObj == exec->lexicalInterpreter()->builtinSyntaxErrorPrototype()) { 0412 exception = true; 0413 syntaxError = true; 0414 } 0415 0416 if (protoObj == exec->lexicalInterpreter()->builtinErrorPrototype() || 0417 protoObj == exec->lexicalInterpreter()->builtinEvalErrorPrototype() || 0418 protoObj == exec->lexicalInterpreter()->builtinReferenceErrorPrototype() || 0419 protoObj == exec->lexicalInterpreter()->builtinRangeErrorPrototype() || 0420 protoObj == exec->lexicalInterpreter()->builtinTypeErrorPrototype() || 0421 protoObj == exec->lexicalInterpreter()->builtinURIErrorPrototype()) { 0422 exception = true; 0423 } 0424 0425 if (!exception) { 0426 return exceptionMsg; 0427 } 0428 0429 // Clear exceptions temporarily so we can get/call a few things. 0430 // We memorize the old exception first, of course. Note that 0431 // This is not always the same as exceptionObj since we may be 0432 // asked to translate a non-active exception 0433 JSValue *oldExceptionObj = exec->exception(); 0434 exec->clearException(); 0435 0436 // We want to serialize the syntax errors ourselves, to provide the line number. 0437 // The URL is in "sourceURL" and the line is in "line" 0438 // ### TODO: Perhaps we want to use 'sourceId' in case of eval contexts. 0439 if (syntaxError) { 0440 JSValue *lineValue = valueObj->get(exec, "line"); 0441 JSValue *urlValue = valueObj->get(exec, "sourceURL"); 0442 0443 int line = lineValue->toNumber(exec); 0444 QString url = urlValue->toString(exec).qstring(); 0445 exceptionMsg = i18n("Parse error at %1 line %2", 0446 url.toHtmlEscaped(), line + 1); 0447 } else { 0448 // ### it's still not 100% safe to call toString here, even on 0449 // native exception objects, since someone might have changed the toString property 0450 // of the exception prototype, but I'll punt on this case for now. 0451 exceptionMsg = exceptionObj->toString(exec).qstring(); 0452 } 0453 exec->setException(oldExceptionObj); 0454 return exceptionMsg; 0455 } 0456 0457 } //namespace KJS