mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-25 11:22:50 +00:00
346 lines
10 KiB
C++
346 lines
10 KiB
C++
![]() |
// -*- c-basic-offset: 2 -*-
|
||
|
/*
|
||
|
* This file is part of the KDE libraries
|
||
|
* Copyright (C) 1999-2003 Harri Porten (porten@kde.org)
|
||
|
* Copyright (C) 2001-2003 David Faure (faure@kde.org)
|
||
|
* Copyright (C) 2003 Apple Computer, Inc.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this library; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
|
||
|
#include "kjs_binding.h"
|
||
|
|
||
|
#include <config-khtml.h>
|
||
|
#if defined(HAVE_VALGRIND_MEMCHECK_H)
|
||
|
|
||
|
#include <valgrind/memcheck.h>
|
||
|
#define VALGRIND_SUPPORT
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#include "kjs_dom.h"
|
||
|
#include "kjs_range.h"
|
||
|
|
||
|
#include <dom/css_stylesheet.h>
|
||
|
#include <dom/dom_exception.h>
|
||
|
#include <dom/dom2_range.h>
|
||
|
#include <dom/dom3_xpath.h>
|
||
|
#include <xml/dom2_eventsimpl.h>
|
||
|
#include <khtmlpart_p.h>
|
||
|
|
||
|
#include <kdebug.h>
|
||
|
#include <kparts/browserextension.h>
|
||
|
#include <kmessagebox.h>
|
||
|
|
||
|
#include "debugger/debugwindow.h"
|
||
|
|
||
|
#include <QtCore/QList>
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
using namespace KJSDebugger;
|
||
|
|
||
|
namespace KJS {
|
||
|
|
||
|
UString DOMObject::toString(ExecState *) const
|
||
|
{
|
||
|
return "[object " + className() + "]";
|
||
|
}
|
||
|
|
||
|
HashMap<void*, DOMObject*>* ScriptInterpreter::s_allDomObjects;
|
||
|
|
||
|
typedef QList<ScriptInterpreter*> InterpreterList;
|
||
|
static InterpreterList *interpreterList;
|
||
|
|
||
|
ScriptInterpreter::ScriptInterpreter( JSGlobalObject *global, khtml::ChildFrame* frame )
|
||
|
: Interpreter( global ), m_frame( frame ),
|
||
|
m_evt( 0L ), m_inlineCode(false), m_timerCallback(false)
|
||
|
{
|
||
|
#ifdef KJS_VERBOSE
|
||
|
kDebug(6070) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_frame;
|
||
|
#endif
|
||
|
if ( !interpreterList )
|
||
|
interpreterList = new InterpreterList;
|
||
|
interpreterList->append( this );
|
||
|
}
|
||
|
|
||
|
ScriptInterpreter::~ScriptInterpreter()
|
||
|
{
|
||
|
#ifdef KJS_VERBOSE
|
||
|
kDebug(6070) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_frame;
|
||
|
#endif
|
||
|
assert( interpreterList && interpreterList->contains( this ) );
|
||
|
interpreterList->removeAll( this );
|
||
|
if ( interpreterList->isEmpty() ) {
|
||
|
delete interpreterList;
|
||
|
interpreterList = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ScriptInterpreter::forgetDOMObject( void* objectHandle )
|
||
|
{
|
||
|
if( !interpreterList ) return;
|
||
|
|
||
|
for (int i = 0; i < interpreterList->size(); ++i)
|
||
|
interpreterList->at(i)->m_domObjects.remove( objectHandle );
|
||
|
allDomObjects()->remove( objectHandle );
|
||
|
}
|
||
|
|
||
|
void ScriptInterpreter::mark(bool isMain)
|
||
|
{
|
||
|
Interpreter::mark(isMain);
|
||
|
#ifdef KJS_VERBOSE
|
||
|
kDebug(6070) << "ScriptInterpreter::mark " << this << " marking " << m_domObjects.size() << " DOM objects";
|
||
|
#endif
|
||
|
HashMap<void*, DOMObject*>::iterator it = m_domObjects.begin();
|
||
|
while (it != m_domObjects.end()) {
|
||
|
DOMObject* obj = it->second;
|
||
|
if (obj->shouldMark())
|
||
|
obj->mark();
|
||
|
++it;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KParts::ReadOnlyPart* ScriptInterpreter::part() const {
|
||
|
return m_frame->m_part.data();
|
||
|
}
|
||
|
|
||
|
bool ScriptInterpreter::isWindowOpenAllowed() const
|
||
|
{
|
||
|
if ( m_evt )
|
||
|
{
|
||
|
int id = m_evt->handle()->id();
|
||
|
bool eventOk = ( // mouse events
|
||
|
id == DOM::EventImpl::CLICK_EVENT ||
|
||
|
id == DOM::EventImpl::MOUSEUP_EVENT || id == DOM::EventImpl::MOUSEDOWN_EVENT ||
|
||
|
id == DOM::EventImpl::KHTML_ECMA_CLICK_EVENT || id == DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT ||
|
||
|
// keyboard events
|
||
|
id == DOM::EventImpl::KEYDOWN_EVENT || id == DOM::EventImpl::KEYPRESS_EVENT ||
|
||
|
id == DOM::EventImpl::KEYUP_EVENT ||
|
||
|
// other accepted events
|
||
|
id == DOM::EventImpl::SELECT_EVENT || id == DOM::EventImpl::CHANGE_EVENT ||
|
||
|
id == DOM::EventImpl::SUBMIT_EVENT );
|
||
|
kDebug(6070) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk;
|
||
|
if (eventOk)
|
||
|
return true;
|
||
|
} else // no event
|
||
|
{
|
||
|
if ( m_inlineCode && !m_timerCallback )
|
||
|
{
|
||
|
// This is the <a href="javascript:window.open('...')> case -> we let it through
|
||
|
return true;
|
||
|
kDebug(6070) << "Window.open, smart policy, no event, inline code -> ok";
|
||
|
}
|
||
|
else // This is the <script>window.open(...)</script> case or a timer callback -> block it
|
||
|
kDebug(6070) << "Window.open, smart policy, no event, <script> tag -> refused";
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ScriptInterpreter::s_disableCPUGuard = false;
|
||
|
|
||
|
void ScriptInterpreter::startCPUGuard()
|
||
|
{
|
||
|
if (s_disableCPUGuard) return;
|
||
|
|
||
|
unsigned time = 5000;
|
||
|
#ifdef VALGRIND_SUPPORT
|
||
|
if (RUNNING_ON_VALGRIND)
|
||
|
time *= 50;
|
||
|
#endif
|
||
|
|
||
|
setTimeoutTime(time);
|
||
|
startTimeoutCheck();
|
||
|
}
|
||
|
|
||
|
void ScriptInterpreter::stopCPUGuard()
|
||
|
{
|
||
|
if (s_disableCPUGuard) return;
|
||
|
stopTimeoutCheck();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool ScriptInterpreter::shouldInterruptScript() const
|
||
|
{
|
||
|
#ifdef KJS_DEBUGGER
|
||
|
if (DebugWindow::isBlocked())
|
||
|
return false;
|
||
|
#endif
|
||
|
|
||
|
kDebug(6070) << "alarmhandler";
|
||
|
return KMessageBox::warningYesNo(0L, 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;
|
||
|
}
|
||
|
|
||
|
UString::UString(const QString &d)
|
||
|
{
|
||
|
unsigned int len = d.length();
|
||
|
UChar *dat = static_cast<UChar*>(fastMalloc(sizeof(UChar)*len));
|
||
|
memcpy(dat, d.unicode(), len * sizeof(UChar));
|
||
|
m_rep = UString::Rep::create(dat, len);
|
||
|
}
|
||
|
|
||
|
UString::UString(const DOM::DOMString &d)
|
||
|
{
|
||
|
if (d.isNull()) {
|
||
|
// we do a conversion here as null DOMStrings shouldn't cross
|
||
|
// the boundary to kjs. They should either be empty strings
|
||
|
// or explicitly converted to KJS::Null via getString().
|
||
|
m_rep = &Rep::empty;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
unsigned int len = d.length();
|
||
|
UChar *dat = static_cast<UChar*>(fastMalloc(sizeof(UChar)*len));
|
||
|
memcpy(dat, d.unicode(), len * sizeof(UChar));
|
||
|
m_rep = UString::Rep::create(dat, len);
|
||
|
}
|
||
|
|
||
|
DOM::DOMString UString::domString() const
|
||
|
{
|
||
|
return DOM::DOMString((QChar*) data(), size());
|
||
|
}
|
||
|
|
||
|
QString UString::qstring() const
|
||
|
{
|
||
|
return QString((QChar*) data(), size());
|
||
|
}
|
||
|
|
||
|
DOM::DOMString Identifier::domString() const
|
||
|
{
|
||
|
return DOM::DOMString((QChar*) data(), size());
|
||
|
}
|
||
|
|
||
|
QString Identifier::qstring() const
|
||
|
{
|
||
|
return QString((QChar*) data(), size());
|
||
|
}
|
||
|
|
||
|
JSValue* valueGetterAdapter(ExecState* exec, JSObject*, const Identifier& , const PropertySlot& slot)
|
||
|
{
|
||
|
Q_UNUSED(exec);
|
||
|
return static_cast<JSValue*>(slot.customValue());
|
||
|
}
|
||
|
|
||
|
DOM::NodeImpl* toNode(JSValue *val)
|
||
|
{
|
||
|
JSObject *obj = val->getObject();
|
||
|
if (!obj || !obj->inherits(&DOMNode::info))
|
||
|
return 0;
|
||
|
|
||
|
const DOMNode *dobj = static_cast<const DOMNode*>(obj);
|
||
|
return dobj->impl();
|
||
|
}
|
||
|
|
||
|
JSValue* getStringOrNull(DOM::DOMString s)
|
||
|
{
|
||
|
if (s.isNull())
|
||
|
return jsNull();
|
||
|
else
|
||
|
return jsString(s);
|
||
|
}
|
||
|
|
||
|
DOM::DOMString valueToStringWithNullCheck(ExecState* exec, JSValue* val)
|
||
|
{
|
||
|
if (val->isNull())
|
||
|
return DOM::DOMString();
|
||
|
return val->toString(exec).domString();
|
||
|
}
|
||
|
|
||
|
QVariant ValueToVariant(ExecState* exec, JSValue *val) {
|
||
|
QVariant res;
|
||
|
switch (val->type()) {
|
||
|
case BooleanType:
|
||
|
res = QVariant(val->toBoolean(exec));
|
||
|
break;
|
||
|
case NumberType:
|
||
|
res = QVariant(val->toNumber(exec));
|
||
|
break;
|
||
|
case StringType:
|
||
|
res = QVariant(val->toString(exec).qstring());
|
||
|
break;
|
||
|
default:
|
||
|
// everything else will be 'invalid'
|
||
|
break;
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
void setDOMException(ExecState *exec, int internalCode)
|
||
|
{
|
||
|
if (internalCode == 0 || exec->hadException())
|
||
|
return;
|
||
|
|
||
|
const char* type = 0;
|
||
|
|
||
|
DOMString name;
|
||
|
DOMString exceptionString;
|
||
|
JSObject* errorObject = 0;
|
||
|
int code = -1; // this will get the public exception code,
|
||
|
// as opposed to the internal one
|
||
|
|
||
|
// ### we should probably introduce classes for things other than range + core
|
||
|
if (DOM::RangeException::isRangeExceptionCode(internalCode)) {
|
||
|
type = "DOM Range";
|
||
|
code = internalCode - DOM::RangeException::_EXCEPTION_OFFSET;
|
||
|
name = DOM::RangeException::codeAsString(code);
|
||
|
errorObject = new RangeException(exec);
|
||
|
} else if (DOM::CSSException::isCSSExceptionCode(internalCode)) {
|
||
|
type = "CSS";
|
||
|
code = internalCode - DOM::CSSException::_EXCEPTION_OFFSET;
|
||
|
name = DOM::CSSException::codeAsString(code);
|
||
|
} else if (DOM::EventException::isEventExceptionCode(internalCode)) {
|
||
|
type = "DOM Events";
|
||
|
code = internalCode - DOM::EventException::_EXCEPTION_OFFSET;
|
||
|
name = DOM::EventException::codeAsString(code);
|
||
|
} else if (DOM::XPathException::isXPathExceptionCode(internalCode)) {
|
||
|
type = "XPath";
|
||
|
code = internalCode - DOM::XPathException::_EXCEPTION_OFFSET;
|
||
|
name = DOM::XPathException::codeAsString(code);
|
||
|
} else {
|
||
|
// Generic DOM.
|
||
|
type = "DOM";
|
||
|
code = internalCode;
|
||
|
name = DOM::DOMException::codeAsString(code);
|
||
|
errorObject = new JSDOMException(exec);
|
||
|
}
|
||
|
|
||
|
if (!errorObject) {
|
||
|
// 100 characters is a big enough buffer, because there are:
|
||
|
// 13 characters in the message
|
||
|
// 10 characters in the longest type, "DOM Events"
|
||
|
// 27 characters in the longest name, "NO_MODIFICATION_ALLOWED_ERR"
|
||
|
// 20 or so digits in the longest integer's ASCII form (even if int is 64-bit)
|
||
|
// 1 byte for a null character
|
||
|
// That adds up to about 70 bytes.
|
||
|
char buffer[100];
|
||
|
|
||
|
if (!name.isEmpty())
|
||
|
snprintf(buffer, 99, "%s: %s Exception %d", name.string().toLatin1().data(), type, code);
|
||
|
else
|
||
|
snprintf(buffer, 99, "%s Exception %d", type, code);
|
||
|
errorObject = throwError(exec, GeneralError, buffer);
|
||
|
} else {
|
||
|
exec->setException(errorObject);
|
||
|
}
|
||
|
|
||
|
errorObject->put(exec, exec->propertyNames().name, jsString(UString(type) + " Exception"));
|
||
|
errorObject->put(exec, exec->propertyNames().message, jsString(name));
|
||
|
errorObject->put(exec, "code", jsNumber(code));
|
||
|
}
|
||
|
|
||
|
} //namespace KJS
|