From ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- khtml/ecma/AUTHORS | 4 + khtml/ecma/Makefile.am | 90 + khtml/ecma/README | 21 + khtml/ecma/THANKS | 9 + khtml/ecma/TODO | 12 + khtml/ecma/domparser.cpp | 112 ++ khtml/ecma/domparser.h | 58 + khtml/ecma/jsk.html | 113 ++ khtml/ecma/kjs_binding.cpp | 467 +++++ khtml/ecma/kjs_binding.h | 409 +++++ khtml/ecma/kjs_css.cpp | 1302 ++++++++++++++ khtml/ecma/kjs_css.h | 304 ++++ khtml/ecma/kjs_debugwin.cpp | 1139 ++++++++++++ khtml/ecma/kjs_debugwin.h | 285 +++ khtml/ecma/kjs_dom.cpp | 1849 +++++++++++++++++++ khtml/ecma/kjs_dom.h | 299 ++++ khtml/ecma/kjs_events.cpp | 993 +++++++++++ khtml/ecma/kjs_events.h | 250 +++ khtml/ecma/kjs_html.cpp | 3946 +++++++++++++++++++++++++++++++++++++++++ khtml/ecma/kjs_html.h | 294 +++ khtml/ecma/kjs_mozilla.cpp | 94 + khtml/ecma/kjs_mozilla.h | 44 + khtml/ecma/kjs_navigator.cpp | 670 +++++++ khtml/ecma/kjs_navigator.h | 53 + khtml/ecma/kjs_proxy.cpp | 411 +++++ khtml/ecma/kjs_proxy.h | 91 + khtml/ecma/kjs_range.cpp | 233 +++ khtml/ecma/kjs_range.h | 71 + khtml/ecma/kjs_traversal.cpp | 327 ++++ khtml/ecma/kjs_traversal.h | 108 ++ khtml/ecma/kjs_views.cpp | 91 + khtml/ecma/kjs_views.h | 53 + khtml/ecma/kjs_window.cpp | 2935 ++++++++++++++++++++++++++++++ khtml/ecma/kjs_window.h | 309 ++++ khtml/ecma/testecma.cpp | 67 + khtml/ecma/xmlhttprequest.cpp | 810 +++++++++ khtml/ecma/xmlhttprequest.h | 142 ++ khtml/ecma/xmlserializer.cpp | 109 ++ khtml/ecma/xmlserializer.h | 54 + 39 files changed, 18628 insertions(+) create mode 100644 khtml/ecma/AUTHORS create mode 100644 khtml/ecma/Makefile.am create mode 100644 khtml/ecma/README create mode 100644 khtml/ecma/THANKS create mode 100644 khtml/ecma/TODO create mode 100644 khtml/ecma/domparser.cpp create mode 100644 khtml/ecma/domparser.h create mode 100644 khtml/ecma/jsk.html create mode 100644 khtml/ecma/kjs_binding.cpp create mode 100644 khtml/ecma/kjs_binding.h create mode 100644 khtml/ecma/kjs_css.cpp create mode 100644 khtml/ecma/kjs_css.h create mode 100644 khtml/ecma/kjs_debugwin.cpp create mode 100644 khtml/ecma/kjs_debugwin.h create mode 100644 khtml/ecma/kjs_dom.cpp create mode 100644 khtml/ecma/kjs_dom.h create mode 100644 khtml/ecma/kjs_events.cpp create mode 100644 khtml/ecma/kjs_events.h create mode 100644 khtml/ecma/kjs_html.cpp create mode 100644 khtml/ecma/kjs_html.h create mode 100644 khtml/ecma/kjs_mozilla.cpp create mode 100644 khtml/ecma/kjs_mozilla.h create mode 100644 khtml/ecma/kjs_navigator.cpp create mode 100644 khtml/ecma/kjs_navigator.h create mode 100644 khtml/ecma/kjs_proxy.cpp create mode 100644 khtml/ecma/kjs_proxy.h create mode 100644 khtml/ecma/kjs_range.cpp create mode 100644 khtml/ecma/kjs_range.h create mode 100644 khtml/ecma/kjs_traversal.cpp create mode 100644 khtml/ecma/kjs_traversal.h create mode 100644 khtml/ecma/kjs_views.cpp create mode 100644 khtml/ecma/kjs_views.h create mode 100644 khtml/ecma/kjs_window.cpp create mode 100644 khtml/ecma/kjs_window.h create mode 100644 khtml/ecma/testecma.cpp create mode 100644 khtml/ecma/xmlhttprequest.cpp create mode 100644 khtml/ecma/xmlhttprequest.h create mode 100644 khtml/ecma/xmlserializer.cpp create mode 100644 khtml/ecma/xmlserializer.h (limited to 'khtml/ecma') diff --git a/khtml/ecma/AUTHORS b/khtml/ecma/AUTHORS new file mode 100644 index 000000000..2a129330d --- /dev/null +++ b/khtml/ecma/AUTHORS @@ -0,0 +1,4 @@ +Harri Porten +Peter Kelly +Dirk Mueller +Daniel Molkentin diff --git a/khtml/ecma/Makefile.am b/khtml/ecma/Makefile.am new file mode 100644 index 000000000..c88b51d33 --- /dev/null +++ b/khtml/ecma/Makefile.am @@ -0,0 +1,90 @@ +# This file is part of the KDE libraries +# Copyright (C) 1999 Harri Porten (porten@kde.org) + +# 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; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/kio -I$(top_srcdir)/kio/bookmarks -I$(top_srcdir)/khtml -I$(top_srcdir)/khtml/java -I$(top_srcdir)/kwallet/client -I$(top_srcdir)/kutils -I$(top_builddir)/kjs $(all_includes) + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + +noinst_LTLIBRARIES = libkjs_html.la + +noinst_HEADERS = kjs_html.h kjs_dom.h kjs_window.h kjs_navigator.h \ + kjs_binding.h kjs_proxy.h kjs_css.h kjs_range.h \ + kjs_traversal.h kjs_events.h kjs_views.h kjs_debugwin.h + +libkjs_html_la_SOURCES = kjs_binding.cpp kjs_dom.cpp kjs_html.cpp kjs_window.cpp \ + kjs_navigator.cpp kjs_proxy.cpp \ + kjs_css.cpp kjs_range.cpp kjs_traversal.cpp kjs_events.cpp \ + kjs_views.cpp kjs_debugwin.cpp kjs_mozilla.cpp xmlhttprequest.cpp \ + xmlserializer.cpp domparser.cpp +#libkjs_html_la_LDFLAGS = -module -avoid-version $(all_libraries) +libkjs_html_la_LIBADD = $(top_builddir)/kjs/libkjs.la # ../libkhtml.la +libkjs_html_la_METASOURCES = AUTO + +LUT_FILES = kjs_dom.lut.h kjs_html.lut.h kjs_css.lut.h kjs_events.lut.h kjs_navigator.lut.h \ + kjs_mozilla.lut.h kjs_range.lut.h kjs_traversal.lut.h kjs_views.lut.h kjs_window.lut.h + +CREATE_HASH_TABLE = $(top_srcdir)/kjs/create_hash_table + +kjs_dom.lut.h : $(srcdir)/kjs_dom.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_dom.cpp > $@ +kjs_dom.lo: kjs_dom.lut.h +kjs_html.lut.h : $(srcdir)/kjs_html.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_html.cpp > $@ +kjs_html.lo: kjs_html.lut.h +kjs_css.lut.h : $(srcdir)/kjs_css.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_css.cpp > $@ +kjs_css.lo: kjs_css.lut.h +kjs_events.lut.h : $(srcdir)/kjs_events.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_events.cpp > $@ +kjs_events.lo: kjs_events.lut.h +kjs_navigator.lut.h : $(srcdir)/kjs_navigator.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_navigator.cpp > $@ +kjs_navigator.lo: kjs_navigator.lut.h +kjs_mozilla.lut.h : $(srcdir)/kjs_mozilla.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_mozilla.cpp > $@ +kjs_mozilla.lo: kjs_mozilla.lut.h +kjs_range.lut.h : $(srcdir)/kjs_range.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_range.cpp > $@ +kjs_range.lo: kjs_range.lut.h +kjs_traversal.lut.h : $(srcdir)/kjs_traversal.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_traversal.cpp > $@ +kjs_traversal.lo: kjs_traversal.lut.h +kjs_views.lut.h : $(srcdir)/kjs_views.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_views.cpp > $@ +kjs_views.lo: kjs_views.lut.h +kjs_window.lut.h : $(srcdir)/kjs_window.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/kjs_window.cpp > $@ +kjs_window.lo: kjs_window.lut.h +xmlhttprequest.lut.h : $(srcdir)/xmlhttprequest.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/xmlhttprequest.cpp > $@ +xmlhttprequest.lo: xmlhttprequest.lut.h +xmlserializer.lut.h : $(srcdir)/xmlserializer.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/xmlserializer.cpp > $@ +xmlserializer.lo: xmlserializer.lut.h +domparser.lut.h : $(srcdir)/domparser.cpp $(CREATE_HASH_TABLE) + $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/domparser.cpp > $@ +domparser.lo: domparser.lut.h + + +CLEANFILES = $(LUT_FILES) + +# interactive test program +#check_PROGRAMS = testecma +#testecma_SOURCES = testecma.cpp +#testecma_LDADD = ../libkhtml.la $(top_builddir)/kjs/libkjs.la + diff --git a/khtml/ecma/README b/khtml/ecma/README new file mode 100644 index 000000000..4c341699a --- /dev/null +++ b/khtml/ecma/README @@ -0,0 +1,21 @@ +This module contains the ECMAScript a.k.a. JavaScript language bindings for +the KHTML Part. + +The module is loaded into KHTML's address space on demand. + +To test the non-HTML DOM functions you may compile a little interactive +interpreter called 'testecma' with 'make check' (see testecma.cpp for +further details). + +Harri Porten + +======================================================================== +Appendix A: Web sites with useful tests + +http://oucsace.cs.ohiou.edu/~ywang/JavaScript +http://www.xs4all.nl/~ppk/js/index.html?version5.html + +Appendix B: References for HTML JavaScript bindings + +http://msdn.microsoft.com/workshop/author/dhtml/reference/objects.asp +http://docs.sun.com/source/816-6408-10/ diff --git a/khtml/ecma/THANKS b/khtml/ecma/THANKS new file mode 100644 index 000000000..b21dce741 --- /dev/null +++ b/khtml/ecma/THANKS @@ -0,0 +1,9 @@ +Vadim Plessky for dissecting an endless number +of bug reports and therefore taking quite some load of our shoulders. + +Ferdinand Gassauer for numerous useful bug reports +and keeping track of them later. + +David Faure for taking some time off from Konqueror +and making a lotto site working fixing bugs along the way. + diff --git a/khtml/ecma/TODO b/khtml/ecma/TODO new file mode 100644 index 000000000..16a7152c5 --- /dev/null +++ b/khtml/ecma/TODO @@ -0,0 +1,12 @@ +Has to be done +============== +- frame[] correct search and sorting order (DONE ?) +- optional error message output +- change KParts::WindowArgs && friends + that we can detect if a locationbar is on/or not + +Could be done +============= +- Make the graphical debugger useable +- Improve internal structures to give a really useful + output on errors (i.e. improve backtrance capabilities) diff --git a/khtml/ecma/domparser.cpp b/khtml/ecma/domparser.cpp new file mode 100644 index 000000000..63a9dce36 --- /dev/null +++ b/khtml/ecma/domparser.cpp @@ -0,0 +1,112 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2005 Anders Carlsson (andersca@mac.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "domparser.h" +#include "domparser.lut.h" + +#include "kjs_dom.h" +#include "kjs_window.h" +#include "xml/dom_nodeimpl.h" +#include "xml/dom_docimpl.h" + +#include "html/html_documentimpl.h" + +using DOM::DocumentImpl; + +////////////////////// DOMParser Object //////////////////////// + +/* Source for DOMParserProtoTable. +@begin DOMParserProtoTable 1 + parseFromString DOMParser::ParseFromString DontDelete|Function 2 +@end +*/ + + +namespace KJS { + +KJS_DEFINE_PROTOTYPE(DOMParserProto) +IMPLEMENT_PROTOFUNC_DOM(DOMParserProtoFunc) +KJS_IMPLEMENT_PROTOTYPE("DOMParser", DOMParserProto, DOMParserProtoFunc) + + +DOMParserConstructorImp::DOMParserConstructorImp(ExecState *, DOM::DocumentImpl *d) + : doc(d) +{ +} + +bool DOMParserConstructorImp::implementsConstruct() const +{ + return true; +} + +Object DOMParserConstructorImp::construct(ExecState *exec, const List &) +{ + return Object(new DOMParser(exec, doc.get())); +} + +const ClassInfo DOMParser::info = { "DOMParser", 0, 0 /* &DOMParserTable*/, 0 }; + + +DOMParser::DOMParser(ExecState *exec, DOM::DocumentImpl *d) + : DOMObject(DOMParserProto::self(exec)), doc(d) +{ +// setPrototype(DOMParserProto::self(exec)); +} + + +Value DOMParserProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + if (!thisObj.inherits(&DOMParser::info)) { + Object err = Error::create(exec,TypeError); + exec->setException(err); + return err; + } + + DOMParser *parser = static_cast(thisObj.imp()); + + switch (id) { + case DOMParser::ParseFromString: + { + if (args.size() != 2) { + return Undefined(); + } + + QString str = args[0].toString(exec).qstring(); + QString contentType = args[1].toString(exec).qstring().stripWhiteSpace(); + + if (contentType == "text/xml" || contentType == "application/xml" || contentType == "application/xhtml+xml") { + DocumentImpl *docImpl = parser->doc->implementation()->createDocument(); + + docImpl->open(); + docImpl->write(str); + docImpl->finishParsing(); + docImpl->close(); + + return getDOMNode(exec, docImpl); + } + } + } + + return Undefined(); +} + +} // end namespace + + diff --git a/khtml/ecma/domparser.h b/khtml/ecma/domparser.h new file mode 100644 index 000000000..233872602 --- /dev/null +++ b/khtml/ecma/domparser.h @@ -0,0 +1,58 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2005 Anders Carlsson (andersca@mac.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + */ + +#ifndef DOMPARSER_H +#define DOMPARSER_H + +#include +#include +#include +#include + +#include "kjs_dom.h" + +namespace KJS { + + class DOMParserConstructorImp : public ObjectImp { + public: + DOMParserConstructorImp(ExecState *, DOM::DocumentImpl *d); + virtual bool implementsConstruct() const; + virtual Object construct(ExecState *exec, const List &args); +private: + khtml::SharedPtr doc; + }; + + class DOMParser : public DOMObject { + public: + DOMParser(ExecState *, DOM::DocumentImpl *d); + virtual bool toBoolean(ExecState *) const { return true; } + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { ParseFromString }; + + private: + QGuardedPtr doc; + + friend class DOMParserProtoFunc; + }; + +} // namespace + +#endif diff --git a/khtml/ecma/jsk.html b/khtml/ecma/jsk.html new file mode 100644 index 000000000..917c083e8 --- /dev/null +++ b/khtml/ecma/jsk.html @@ -0,0 +1,113 @@ + + + +Javascript Konsole + + + + +

JavaScript Konsole

+
+ + + + + +
Expression
Result Type
Result(s)
  + + +
+
+

Explanation

+

Operation

+
+When evaluate is pressed, the given expression is evaluated and the result is displayed in the result(s) field. +In case of list properties being pressed, the result of the expression is taken as an object +and the objects properties are displayed with their type and value in the the result(s) field. +
+

Expression

+
+Expression must be a valid javascript expression, e.g.
window +
or
document.body.innerHTML
or
+"Today: " + (new Date()).toString()
+or
+"Cablecar".match(/ab.*c/) +
It is also possible to assign a value, +e.g.
document.getElementsByTagName('H1').item(0).innerText="Hello World"
+You may execute these examples by pasting them into the expression field. +
+

Result Type

+
+The type of the result of the given expression. +
+

Result(s)

+
+The result of the expression is implicitly converted to a primitive type by the javascript interpreter, +if evaluate was pressed. When list properties was pressed, a for (var i in obj) loop +is executed to list the properties. These object properties are in turn evaluated and their types and values +are displayed. +
+

+Till Krech +

+

+
+

+ + + diff --git a/khtml/ecma/kjs_binding.cpp b/khtml/ecma/kjs_binding.cpp new file mode 100644 index 000000000..295d8c5e9 --- /dev/null +++ b/khtml/ecma/kjs_binding.cpp @@ -0,0 +1,467 @@ +// -*- 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 "kjs_dom.h" + +#include "dom/dom_exception.h" +#include "dom/dom2_range.h" +#include "xml/dom2_eventsimpl.h" +#include "khtmlpart_p.h" + +#include +#include + +#include + +using namespace KJS; + +/* TODO: + * The catch all (...) clauses below shouldn't be necessary. + * But they helped to view for example www.faz.net in an stable manner. + * Those unknown exceptions should be treated as severe bugs and be fixed. + * + * these may be CSS exceptions - need to check - pmk + */ + +Value DOMObject::get(ExecState *exec, const Identifier &p) const +{ + Value result; + try { + result = tryGet(exec,p); + } + catch (DOM::DOMException e) { + // ### translate code into readable string ? + // ### oh, and s/QString/i18n or I18N_NOOP (the code in kjs uses I18N_NOOP... but where is it translated ?) + // and where does it appear to the user ? + Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit()); + exec->setException( err ); + result = Undefined(); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMObject::get()" << endl; + result = String("Unknown exception"); + } + + return result; +} + +void DOMObject::put(ExecState *exec, const Identifier &propertyName, + const Value &value, int attr) +{ + try { + tryPut(exec, propertyName, value, attr); + } + catch (DOM::DOMException e) { + Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit()); + exec->setException(err); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMObject::put()" << endl; + } +} + +void DOMObject::tryPut(ExecState *exec, const Identifier &propertyName, + const Value& value, int attr) +{ + static_cast(exec->dynamicInterpreter())->customizedDOMObject(this); + ObjectImp::put(exec,propertyName,value,attr); +} + +UString DOMObject::toString(ExecState *) const +{ + return "[object " + className() + "]"; +} + +Boolean DOMObject::hasInstance(ExecState *exec, const Value &value) +{ + if (value.type() != ObjectType) + return Boolean(false); + + Value prot = get(exec,prototypePropertyName); + if (prot.type() != ObjectType && prot.type() != NullType) { + Object err = Error::create(exec, TypeError, "Invalid prototype encountered " + "in instanceof operation."); + exec->setException(err); + return Boolean(false); + } + + Object v = Object(static_cast(value.imp())); + while ((v = Object::dynamicCast(v.prototype())).imp()) { + if (v.imp() == prot.imp()) + return Boolean(true); + } + return Boolean(false); +} + + +Value DOMFunction::get(ExecState *exec, const Identifier &propertyName) const +{ + try { + return tryGet(exec, propertyName); + } + catch (DOM::DOMException e) { + Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit()); + exec->setException(err); + return Undefined(); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMFunction::get()" << endl; + return String("Unknown exception"); + } +} + +Value DOMFunction::call(ExecState *exec, Object &thisObj, const List &args) +{ + try { + return tryCall(exec, thisObj, args); + } + // pity there's no way to distinguish between these in JS code + // ### Look into setting prototypes of these & the use of instanceof so the exception + // type can be determined. See what other browsers do. + catch (DOM::DOMException e) { + Object err = Error::create(exec, GeneralError, QString("DOM Exception %1").arg(e.code).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (DOM::RangeException e) { + Object err = Error::create(exec, GeneralError, QString("DOM Range Exception %1").arg(e.code).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (DOM::CSSException e) { + Object err = Error::create(exec, GeneralError, QString("CSS Exception %1").arg(e.code).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (DOM::EventException e) { + Object err = Error::create(exec, GeneralError, QString("DOM Event Exception %1").arg(e.code).local8Bit()); + err.put(exec, "code", Number(e.code)); + exec->setException(err); + return Undefined(); + } + catch (...) { + kdError(6070) << "Unknown exception in DOMFunction::call()" << endl; + Object err = Error::create(exec, GeneralError, "Unknown exception"); + exec->setException(err); + return Undefined(); + } +} + +typedef QPtrList InterpreterList; +static InterpreterList *interpreterList; + +ScriptInterpreter::ScriptInterpreter( const Object &global, khtml::ChildFrame* frame ) + : Interpreter( global ), m_frame( frame ), m_domObjects(1021), + m_evt( 0L ), m_inlineCode(false), m_timerCallback(false) +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_frame << endl; +#endif + if ( !interpreterList ) + interpreterList = new InterpreterList; + interpreterList->append( this ); +} + +ScriptInterpreter::~ScriptInterpreter() +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_frame << endl; +#endif + assert( interpreterList && interpreterList->contains( this ) ); + interpreterList->remove( this ); + if ( interpreterList->isEmpty() ) { + delete interpreterList; + interpreterList = 0; + } +} + +void ScriptInterpreter::forgetDOMObject( void* objectHandle ) +{ + if( !interpreterList ) return; + + QPtrListIterator it( *interpreterList ); + while ( it.current() ) { + (*it)->deleteDOMObject( objectHandle ); + ++it; + } +} + +void ScriptInterpreter::mark() +{ + Interpreter::mark(); +#ifdef KJS_VERBOSE + kdDebug(6070) << "ScriptInterpreter::mark " << this << " marking " << m_customizedDomObjects.count() << " DOM objects" << endl; +#endif + QPtrDictIterator it( m_customizedDomObjects ); + for( ; it.current(); ++it ) + static_cast(it.currentKey())->mark(); +} + +KParts::ReadOnlyPart* ScriptInterpreter::part() const { + return m_frame->m_part; +} + +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 ); + kdDebug(6070) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk << endl; + if (eventOk) + return true; + } else // no event + { + if ( m_inlineCode && !m_timerCallback ) + { + // This is the ok" << endl; + } + else // This is the case or a timer callback -> block it + kdDebug(6070) << "Window.open, smart policy, no event, . Check if it has the + // expected value in all cases. + // See smart window.open policy for where this is used. + bool inlineCode = filename.isNull(); + //kdDebug(6070) << "KJSProxyImpl::evaluate inlineCode=" << inlineCode << endl; + +#ifdef KJS_DEBUGGER + if (inlineCode) + filename = "(unknown file)"; + if (KJSDebugWin::debugWindow()) { + KJSDebugWin::debugWindow()->attach(m_script); + KJSDebugWin::debugWindow()->setNextSourceInfo(filename,baseLine); + // KJSDebugWin::debugWindow()->setMode(KJSDebugWin::Step); + } +#else + Q_UNUSED(baseLine); +#endif + + m_script->setInlineCode(inlineCode); + Window* window = Window::retrieveWindow( m_frame->m_part ); + KJS::Value thisNode = n.isNull() ? Window::retrieve( m_frame->m_part ) : getDOMNode(m_script->globalExec(),n); + + UString code( str ); + + KJSCPUGuard guard; + guard.start(); + Completion comp = m_script->evaluate(code, thisNode); + guard.stop(); + + bool success = ( comp.complType() == Normal ) || ( comp.complType() == ReturnValue ); + + if (completion) + *completion = comp; + +#ifdef KJS_DEBUGGER + // KJSDebugWin::debugWindow()->setCode(QString::null); +#endif + + window->afterScriptExecution(); + + // let's try to convert the return value + if (success && comp.value().isValid()) + return ValueToVariant( m_script->globalExec(), comp.value()); + else + { + if ( comp.complType() == Throw ) + { + UString msg = comp.value().toString(m_script->globalExec()); + kdDebug(6070) << "WARNING: Script threw exception: " << msg.qstring() << endl; + } + return QVariant(); + } +} + +// Implementation of the debug() function +class TestFunctionImp : public ObjectImp { +public: + TestFunctionImp() : ObjectImp() {} + virtual bool implementsCall() const { return true; } + virtual Value call(ExecState *exec, Object &thisObj, const List &args); +}; + +Value TestFunctionImp::call(ExecState *exec, Object &/*thisObj*/, const List &args) +{ + fprintf(stderr,"--> %s\n",args[0].toString(exec).ascii()); + return Undefined(); +} + +void KJSProxyImpl::clear() { + // clear resources allocated by the interpreter, and make it ready to be used by another page + // We have to keep it, so that the Window object for the part remains the same. + // (we used to delete and re-create it, previously) + if (m_script) { +#ifdef KJS_DEBUGGER + // ### + KJSDebugWin *debugWin = KJSDebugWin::debugWindow(); + if (debugWin) { + if (debugWin->getExecState() && + debugWin->getExecState()->interpreter() == m_script) + debugWin->slotStop(); + debugWin->clearInterpreter(m_script); + } +#endif + m_script->clear(); + + Window *win = static_cast(m_script->globalObject().imp()); + if (win) { + win->clear( m_script->globalExec() ); + // re-add "debug", clear() removed it + m_script->globalObject().put(m_script->globalExec(), + "debug", Value(new TestFunctionImp()), Internal); + if ( win->part() ) + applyUserAgent(); + } + + // Really delete everything that can be, so that the DOM nodes get deref'ed + //kdDebug() << k_funcinfo << "all done -> collecting" << endl; + while (KJS::Interpreter::collect()) + ; + } +} + +DOM::EventListener *KJSProxyImpl::createHTMLEventHandler(QString sourceUrl, QString name, QString code, DOM::NodeImpl *node) +{ + initScript(); + +#ifdef KJS_DEBUGGER + if (KJSDebugWin::debugWindow()) { + KJSDebugWin::debugWindow()->attach(m_script); + KJSDebugWin::debugWindow()->setNextSourceInfo(sourceUrl,m_handlerLineno); + } +#else + Q_UNUSED(sourceUrl); +#endif + + return KJS::Window::retrieveWindow(m_frame->m_part)->getJSLazyEventListener(code,name,node); +} + +void KJSProxyImpl::finishedWithEvent(const DOM::Event &event) +{ + // This is called when the DOM implementation has finished with a particular event. This + // is the case in sitations where an event has been created just for temporary usage, + // e.g. an image load or mouse move. Once the event has been dispatched, it is forgotten + // by the DOM implementation and so does not need to be cached still by the interpreter + ScriptInterpreter::forgetDOMObject(event.handle()); +} + +KJS::Interpreter *KJSProxyImpl::interpreter() +{ + if (!m_script) + initScript(); + return m_script; +} + +void KJSProxyImpl::setDebugEnabled(bool enabled) +{ +#ifdef KJS_DEBUGGER + m_debugEnabled = enabled; + //if (m_script) + // m_script->setDebuggingEnabled(enabled); + // NOTE: this is consistent across all KJSProxyImpl instances, as we only + // ever have 1 debug window + if (!enabled && KJSDebugWin::debugWindow()) { + KJSDebugWin::destroyInstance(); + } + else if (enabled && !KJSDebugWin::debugWindow()) { + KJSDebugWin::createInstance(); + initScript(); + KJSDebugWin::debugWindow()->attach(m_script); + } +#else + Q_UNUSED(enabled); +#endif +} + +void KJSProxyImpl::showDebugWindow(bool /*show*/) +{ +#ifdef KJS_DEBUGGER + if (KJSDebugWin::debugWindow()) + KJSDebugWin::debugWindow()->show(); +#else + //Q_UNUSED(show); +#endif +} + +bool KJSProxyImpl::paused() const +{ +#ifdef KJS_DEBUGGER + if (KJSDebugWin::debugWindow()) + return KJSDebugWin::debugWindow()->inSession(); +#endif + return false; +} + +void KJSProxyImpl::dataReceived() +{ +#ifdef KJS_DEBUGGER + if (KJSDebugWin::debugWindow() && m_frame->m_part) + KJSDebugWin::debugWindow()->sourceChanged(m_script,m_frame->m_part->url().url()); +#endif +} + +void KJSProxyImpl::initScript() +{ + if (m_script) + return; + + // Build the global object - which is a Window instance + Object globalObject( new Window(m_frame) ); + + // Create a KJS interpreter for this part + m_script = new KJS::ScriptInterpreter(globalObject, m_frame); + static_cast(globalObject.imp())->setPrototype(m_script->builtinObjectPrototype()); + +#ifdef KJS_DEBUGGER + //m_script->setDebuggingEnabled(m_debugEnabled); +#endif + //m_script->enableDebug(); + globalObject.put(m_script->globalExec(), + "debug", Value(new TestFunctionImp()), Internal); + applyUserAgent(); +} + +void KJSProxyImpl::applyUserAgent() +{ + assert( m_script ); + QString host = m_frame->m_part->url().isLocalFile() ? "localhost" : m_frame->m_part->url().host(); + QString userAgent = KProtocolManager::userAgentForHost(host); + if (userAgent.find(QString::fromLatin1("Microsoft")) >= 0 || + userAgent.find(QString::fromLatin1("MSIE")) >= 0) + { + m_script->setCompatMode(Interpreter::IECompat); +#ifdef KJS_VERBOSE + kdDebug() << "Setting IE compat mode" << endl; +#endif + } + else + // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape + if (userAgent.find(QString::fromLatin1("Mozilla")) >= 0 && + userAgent.find(QString::fromLatin1("compatible")) == -1 && + userAgent.find(QString::fromLatin1("KHTML")) == -1) + { + m_script->setCompatMode(Interpreter::NetscapeCompat); +#ifdef KJS_VERBOSE + kdDebug() << "Setting NS compat mode" << endl; +#endif + } +} + +// Helper method, so that all classes which need jScript() don't need to be added +// as friend to KHTMLPart +KJSProxy * KJSProxy::proxy( KHTMLPart *part ) +{ + return part->jScript(); +} + +// initialize HTML module +KJSProxy *kjs_html_init(khtml::ChildFrame *childframe) +{ + return new KJSProxyImpl(childframe); +} + +void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms) +{ +#ifdef VALGRIND_SUPPORT + if (RUNNING_ON_VALGRIND) { + ms *= 50; + i_ms *= 50; + } +#endif + + oldAlarmHandler = signal(SIGVTALRM, alarmHandler); + itimerval tv = { + { i_ms / 1000, (i_ms % 1000) * 1000 }, + { ms / 1000, (ms % 1000) * 1000 } + }; + setitimer(ITIMER_VIRTUAL, &tv, &oldtv); +} + +void KJSCPUGuard::stop() +{ + setitimer(ITIMER_VIRTUAL, &oldtv, 0L); + signal(SIGVTALRM, oldAlarmHandler); +} + +bool KJSCPUGuard::confirmTerminate() { + kdDebug(6070) << "alarmhandler" << endl; + 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 abort the script?"), i18n("JavaScript"), i18n("&Abort"), KStdGuiItem::cont(), "kjscupguard_alarmhandler") == KMessageBox::Yes; +} + +void KJSCPUGuard::alarmHandler(int) { + ExecState::requestTerminate(); + ExecState::confirmTerminate = KJSCPUGuard::confirmTerminate; +} diff --git a/khtml/ecma/kjs_proxy.h b/khtml/ecma/kjs_proxy.h new file mode 100644 index 000000000..f76543451 --- /dev/null +++ b/khtml/ecma/kjs_proxy.h @@ -0,0 +1,91 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * + * 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 + */ + +#ifndef _KJS_PROXY_H_ +#define _KJS_PROXY_H_ + +#include +#include +#include + +class KHTMLPart; + +namespace DOM { + class Node; + class NodeImpl; + class EventListener; + class Event; +} + +namespace KJS { + class List; + class Interpreter; + class Completion; + class KJSDebugWin; +} + +namespace khtml { + class ChildFrame; +} + +/** + * @internal + * + * @short Proxy class serving as interface when being dlopen'ed. + */ +class KJSProxy { +public: + KJSProxy() { m_handlerLineno = 0; } + virtual ~KJSProxy() { } + virtual QVariant evaluate(QString filename, int baseLine, const QString &, const DOM::Node &n, + KJS::Completion *completion = 0) = 0; + virtual void clear() = 0; + virtual DOM::EventListener *createHTMLEventHandler(QString sourceUrl, QString name, QString code, DOM::NodeImpl* node) = 0; + virtual void finishedWithEvent(const DOM::Event &event) = 0; + virtual KJS::Interpreter *interpreter() = 0; + + virtual void setDebugEnabled(bool enabled) = 0; + virtual void showDebugWindow(bool show=true) = 0; + virtual bool paused() const = 0; + virtual void dataReceived() = 0; + + void setEventHandlerLineno(int lineno) { m_handlerLineno = lineno; } + + khtml::ChildFrame *m_frame; + int m_handlerLineno; + + // Helper method, to access the private KHTMLPart::jScript() + static KJSProxy *proxy( KHTMLPart *part ); +}; + +class KJSCPUGuard { +public: + KJSCPUGuard() {} + void start(unsigned int msec=5000, unsigned int i_msec=10000); + void stop(); +private: + void (*oldAlarmHandler)(int); + static void alarmHandler(int); + static bool confirmTerminate(); + itimerval oldtv; +}; + +#endif diff --git a/khtml/ecma/kjs_range.cpp b/khtml/ecma/kjs_range.cpp new file mode 100644 index 000000000..bf148ffe8 --- /dev/null +++ b/khtml/ecma/kjs_range.cpp @@ -0,0 +1,233 @@ + +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * 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_range.h" +#include "kjs_range.lut.h" +#include + +namespace KJS { + +// ------------------------------------------------------------------------- + +const ClassInfo DOMRange::info = { "Range", 0, &DOMRangeTable, 0 }; +/* +@begin DOMRangeTable 7 + startContainer DOMRange::StartContainer DontDelete|ReadOnly + startOffset DOMRange::StartOffset DontDelete|ReadOnly + endContainer DOMRange::EndContainer DontDelete|ReadOnly + endOffset DOMRange::EndOffset DontDelete|ReadOnly + collapsed DOMRange::Collapsed DontDelete|ReadOnly + commonAncestorContainer DOMRange::CommonAncestorContainer DontDelete|ReadOnly +@end +@begin DOMRangeProtoTable 17 +setStart DOMRange::SetStart DontDelete|Function 2 + setEnd DOMRange::SetEnd DontDelete|Function 2 + setStartBefore DOMRange::SetStartBefore DontDelete|Function 1 + setStartAfter DOMRange::SetStartAfter DontDelete|Function 1 + setEndBefore DOMRange::SetEndBefore DontDelete|Function 1 + setEndAfter DOMRange::SetEndAfter DontDelete|Function 1 + collapse DOMRange::Collapse DontDelete|Function 1 + selectNode DOMRange::SelectNode DontDelete|Function 1 + selectNodeContents DOMRange::SelectNodeContents DontDelete|Function 1 + compareBoundaryPoints DOMRange::CompareBoundaryPoints DontDelete|Function 2 + deleteContents DOMRange::DeleteContents DontDelete|Function 0 + extractContents DOMRange::ExtractContents DontDelete|Function 0 + cloneContents DOMRange::CloneContents DontDelete|Function 0 + insertNode DOMRange::InsertNode DontDelete|Function 1 + surroundContents DOMRange::SurroundContents DontDelete|Function 1 + cloneRange DOMRange::CloneRange DontDelete|Function 0 + toString DOMRange::ToString DontDelete|Function 0 + detach DOMRange::Detach DontDelete|Function 0 + createContextualFragment DOMRange::CreateContextualFragment DontDelete|Function 1 +@end +*/ +KJS_DEFINE_PROTOTYPE(DOMRangeProto) +IMPLEMENT_PROTOFUNC_DOM(DOMRangeProtoFunc) +KJS_IMPLEMENT_PROTOTYPE("DOMRange",DOMRangeProto,DOMRangeProtoFunc) + +DOMRange::DOMRange(ExecState *exec, DOM::Range r) + : DOMObject(DOMRangeProto::self(exec)), range(r) {} + +DOMRange::~DOMRange() +{ + ScriptInterpreter::forgetDOMObject(range.handle()); +} + +Value DOMRange::tryGet(ExecState *exec, const Identifier &p) const +{ + return DOMObjectLookupGetValue(exec,p,&DOMRangeTable,this); +} + +Value DOMRange::getValueProperty(ExecState *exec, int token) const +{ + switch (token) { + case StartContainer: + return getDOMNode(exec,range.startContainer()); + case StartOffset: + return Number(range.startOffset()); + case EndContainer: + return getDOMNode(exec,range.endContainer()); + case EndOffset: + return Number(range.endOffset()); + case Collapsed: + return Boolean(range.collapsed()); + case CommonAncestorContainer: { + DOM::Range range2 = range; // avoid const error + return getDOMNode(exec,range2.commonAncestorContainer()); + } + default: + kdDebug(6070) << "WARNING: Unhandled token in DOMRange::getValueProperty : " << token << endl; + return Value(); + } +} + +Value DOMRangeProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + KJS_CHECK_THIS( KJS::DOMRange, thisObj ); + DOM::Range range = static_cast(thisObj.imp())->toRange(); + Value result; + + switch (id) { + case DOMRange::SetStart: + range.setStart(toNode(args[0]),args[1].toInteger(exec)); + result = Undefined(); + break; + case DOMRange::SetEnd: + range.setEnd(toNode(args[0]),args[1].toInteger(exec)); + result = Undefined(); + break; + case DOMRange::SetStartBefore: + range.setStartBefore(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::SetStartAfter: + range.setStartAfter(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::SetEndBefore: + range.setEndBefore(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::SetEndAfter: + range.setEndAfter(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::Collapse: + range.collapse(args[0].toBoolean(exec)); + result = Undefined(); + break; + case DOMRange::SelectNode: + range.selectNode(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::SelectNodeContents: + range.selectNodeContents(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::CompareBoundaryPoints: + result = Number(range.compareBoundaryPoints(static_cast(args[0].toInteger(exec)),toRange(args[1]))); + break; + case DOMRange::DeleteContents: + range.deleteContents(); + result = Undefined(); + break; + case DOMRange::ExtractContents: + result = getDOMNode(exec,range.extractContents()); + break; + case DOMRange::CloneContents: + result = getDOMNode(exec,range.cloneContents()); + break; + case DOMRange::InsertNode: + range.insertNode(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::SurroundContents: + range.surroundContents(toNode(args[0])); + result = Undefined(); + break; + case DOMRange::CloneRange: + result = getDOMRange(exec,range.cloneRange()); + break; + case DOMRange::ToString: + result = String(range.toString()); + break; + case DOMRange::Detach: + range.detach(); + result = Undefined(); + break; + case DOMRange::CreateContextualFragment: + Value value = args[0]; + DOM::DOMString str = value.isA(NullType) ? DOM::DOMString() : value.toString(exec).string(); + result = getDOMNode(exec, range.createContextualFragment(str)); + break; + }; + + return result; +} + +Value getDOMRange(ExecState *exec, DOM::Range r) +{ + return cacheDOMObject(exec, r); +} + +// ------------------------------------------------------------------------- + +const ClassInfo RangeConstructor::info = { "RangeConstructor", 0, &RangeConstructorTable, 0 }; +/* +@begin RangeConstructorTable 5 + START_TO_START DOM::Range::START_TO_START DontDelete|ReadOnly + START_TO_END DOM::Range::START_TO_END DontDelete|ReadOnly + END_TO_END DOM::Range::END_TO_END DontDelete|ReadOnly + END_TO_START DOM::Range::END_TO_START DontDelete|ReadOnly +@end +*/ + +RangeConstructor::RangeConstructor(ExecState *exec) + : DOMObject(exec->interpreter()->builtinObjectPrototype()) { } + +Value RangeConstructor::tryGet(ExecState *exec, const Identifier &p) const +{ + return DOMObjectLookupGetValue(exec,p,&RangeConstructorTable,this); +} + +Value RangeConstructor::getValueProperty(ExecState *, int token) const +{ + return Number(token); +} + +Value getRangeConstructor(ExecState *exec) +{ + return cacheGlobalObject(exec, "[[range.constructor]]"); +} + + +DOM::Range toRange(const Value& val) +{ + Object obj = Object::dynamicCast(val); + if (!obj.isValid() || !obj.inherits(&DOMRange::info)) + return DOM::Range(); + + const DOMRange *dobj = static_cast(obj.imp()); + return dobj->toRange(); +} + +} //namespace KJS diff --git a/khtml/ecma/kjs_range.h b/khtml/ecma/kjs_range.h new file mode 100644 index 000000000..fbf47020f --- /dev/null +++ b/khtml/ecma/kjs_range.h @@ -0,0 +1,71 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * + * 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 + */ + +#ifndef _KJS_RANGE_H_ +#define _KJS_RANGE_H_ + +#include "ecma/kjs_dom.h" +#include "dom/dom2_range.h" + +namespace KJS { + + class DOMRange : public DOMObject { + public: + DOMRange(ExecState *exec, DOM::Range r); + ~DOMRange(); + virtual Value tryGet(ExecState *exec,const Identifier &p) const; + Value getValueProperty(ExecState *exec, int token) const; + // no put - all read-only + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { StartContainer, StartOffset, EndContainer, EndOffset, Collapsed, + CommonAncestorContainer, + SetStart, SetEnd, SetStartBefore, SetStartAfter, SetEndBefore, + SetEndAfter, Collapse, SelectNode, SelectNodeContents, + CompareBoundaryPoints, DeleteContents, ExtractContents, + CloneContents, InsertNode, SurroundContents, CloneRange, ToString, + Detach, CreateContextualFragment }; + DOM::Range toRange() const { return range; } + protected: + DOM::Range range; + }; + + // Constructor object Range + class RangeConstructor : public DOMObject { + public: + RangeConstructor(ExecState *); + virtual Value tryGet(ExecState *exec,const Identifier &p) const; + Value getValueProperty(ExecState *, int token) const; + // no put - all read-only + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + Value getDOMRange(ExecState *exec, DOM::Range r); + Value getRangeConstructor(ExecState *exec); + + /** + * Convert an object to a Range. Returns a null Node if not possible. + */ + DOM::Range toRange(const Value&); + +} // namespace + +#endif diff --git a/khtml/ecma/kjs_traversal.cpp b/khtml/ecma/kjs_traversal.cpp new file mode 100644 index 000000000..5b521b6d5 --- /dev/null +++ b/khtml/ecma/kjs_traversal.cpp @@ -0,0 +1,327 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * + * 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_traversal.h" +#include "kjs_traversal.lut.h" +#include "kjs_proxy.h" +#include +#include +#include +#include +#include +#include + +namespace KJS { + +// ------------------------------------------------------------------------- + +const ClassInfo DOMNodeIterator::info = { "NodeIterator", 0, &DOMNodeIteratorTable, 0 }; +/* +@begin DOMNodeIteratorTable 5 + root DOMNodeIterator::Root DontDelete|ReadOnly + whatToShow DOMNodeIterator::WhatToShow DontDelete|ReadOnly + filter DOMNodeIterator::Filter DontDelete|ReadOnly + expandEntityReferences DOMNodeIterator::ExpandEntityReferences DontDelete|ReadOnly +@end +@begin DOMNodeIteratorProtoTable 3 + nextNode DOMNodeIterator::NextNode DontDelete|Function 0 + previousNode DOMNodeIterator::PreviousNode DontDelete|Function 0 + detach DOMNodeIterator::Detach DontDelete|Function 0 +@end +*/ +KJS_DEFINE_PROTOTYPE(DOMNodeIteratorProto) +IMPLEMENT_PROTOFUNC_DOM(DOMNodeIteratorProtoFunc) +KJS_IMPLEMENT_PROTOTYPE("DOMNodeIterator", DOMNodeIteratorProto,DOMNodeIteratorProtoFunc) + +DOMNodeIterator::DOMNodeIterator(ExecState *exec, DOM::NodeIterator ni) + : DOMObject(DOMNodeIteratorProto::self(exec)), nodeIterator(ni) {} + +DOMNodeIterator::~DOMNodeIterator() +{ + ScriptInterpreter::forgetDOMObject(nodeIterator.handle()); +} + +Value DOMNodeIterator::tryGet(ExecState *exec, const Identifier &p) const +{ + return DOMObjectLookupGetValue(exec,p,&DOMNodeIteratorTable,this); +} + +Value DOMNodeIterator::getValueProperty(ExecState *exec, int token) const +{ + DOM::NodeIterator ni(nodeIterator); + switch (token) { + case Root: + return getDOMNode(exec,ni.root()); + case WhatToShow: + return Number(ni.whatToShow()); + case Filter: + return getDOMNodeFilter(exec,ni.filter()); + case ExpandEntityReferences: + return Boolean(ni.expandEntityReferences()); + default: + kdDebug(6070) << "WARNING: Unhandled token in DOMNodeIterator::getValueProperty : " << token << endl; + return Value(); + } +} + +Value DOMNodeIteratorProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &) +{ + KJS_CHECK_THIS( KJS::DOMNodeIterator, thisObj ); + DOM::NodeIterator nodeIterator = static_cast(thisObj.imp())->toNodeIterator(); + switch (id) { + case DOMNodeIterator::PreviousNode: + return getDOMNode(exec,nodeIterator.previousNode()); + case DOMNodeIterator::NextNode: + return getDOMNode(exec,nodeIterator.nextNode()); + case DOMNodeIterator::Detach: + nodeIterator.detach(); + return Undefined(); + } + return Undefined(); +} + +Value getDOMNodeIterator(ExecState *exec, DOM::NodeIterator ni) +{ + return cacheDOMObject(exec, ni); +} + + +// ------------------------------------------------------------------------- + +const ClassInfo NodeFilterConstructor::info = { "NodeFilterConstructor", 0, &NodeFilterConstructorTable, 0 }; +/* +@begin NodeFilterConstructorTable 17 + FILTER_ACCEPT DOM::NodeFilter::FILTER_ACCEPT DontDelete|ReadOnly + FILTER_REJECT DOM::NodeFilter::FILTER_REJECT DontDelete|ReadOnly + FILTER_SKIP DOM::NodeFilter::FILTER_SKIP DontDelete|ReadOnly + SHOW_ALL DOM::NodeFilter::SHOW_ALL DontDelete|ReadOnly + SHOW_ELEMENT DOM::NodeFilter::SHOW_ELEMENT DontDelete|ReadOnly + SHOW_ATTRIBUTE DOM::NodeFilter::SHOW_ATTRIBUTE DontDelete|ReadOnly + SHOW_TEXT DOM::NodeFilter::SHOW_TEXT DontDelete|ReadOnly + SHOW_CDATA_SECTION DOM::NodeFilter::SHOW_CDATA_SECTION DontDelete|ReadOnly + SHOW_ENTITY_REFERENCE DOM::NodeFilter::SHOW_ENTITY_REFERENCE DontDelete|ReadOnly + SHOW_ENTITY DOM::NodeFilter::SHOW_ENTITY DontDelete|ReadOnly + SHOW_PROCESSING_INSTRUCTION DOM::NodeFilter::SHOW_PROCESSING_INSTRUCTION DontDelete|ReadOnly + SHOW_COMMENT DOM::NodeFilter::SHOW_COMMENT DontDelete|ReadOnly + SHOW_DOCUMENT DOM::NodeFilter::SHOW_DOCUMENT DontDelete|ReadOnly + SHOW_DOCUMENT_TYPE DOM::NodeFilter::SHOW_DOCUMENT_TYPE DontDelete|ReadOnly + SHOW_DOCUMENT_FRAGMENT DOM::NodeFilter::SHOW_DOCUMENT_FRAGMENT DontDelete|ReadOnly + SHOW_NOTATION DOM::NodeFilter::SHOW_NOTATION DontDelete|ReadOnly +@end +*/ + +NodeFilterConstructor::NodeFilterConstructor(ExecState* exec) + : DOMObject(exec->interpreter()->builtinObjectPrototype()) +{ +} + +Value NodeFilterConstructor::tryGet(ExecState *exec, const Identifier &p) const +{ + return DOMObjectLookupGetValue(exec,p,&NodeFilterConstructorTable,this); +} + +Value NodeFilterConstructor::getValueProperty(ExecState *, int token) const +{ + // We use the token as the value to return directly + return Number(token); +} + +Value getNodeFilterConstructor(ExecState *exec) +{ + return cacheGlobalObject(exec, "[[nodeFilter.constructor]]"); +} + +// ------------------------------------------------------------------------- + +const ClassInfo DOMNodeFilter::info = { "NodeFilter", 0, 0, 0 }; +/* +@begin DOMNodeFilterProtoTable 1 + acceptNode DOMNodeFilter::AcceptNode DontDelete|Function 0 +@end +*/ +KJS_DEFINE_PROTOTYPE(DOMNodeFilterProto) +IMPLEMENT_PROTOFUNC_DOM(DOMNodeFilterProtoFunc) +KJS_IMPLEMENT_PROTOTYPE("DOMNodeFilter",DOMNodeFilterProto,DOMNodeFilterProtoFunc) + +DOMNodeFilter::DOMNodeFilter(ExecState *exec, DOM::NodeFilter nf) + : DOMObject(DOMNodeFilterProto::self(exec)), nodeFilter(nf) {} + +DOMNodeFilter::~DOMNodeFilter() +{ + ScriptInterpreter::forgetDOMObject(nodeFilter.handle()); +} + +Value DOMNodeFilterProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + KJS_CHECK_THIS( KJS::DOMNodeFilter, thisObj ); + DOM::NodeFilter nodeFilter = static_cast(thisObj.imp())->toNodeFilter(); + switch (id) { + case DOMNodeFilter::AcceptNode: + return Number(nodeFilter.acceptNode(toNode(args[0]))); + } + return Undefined(); +} + +Value getDOMNodeFilter(ExecState *exec, DOM::NodeFilter nf) +{ + return cacheDOMObject(exec, nf); +} + +// ------------------------------------------------------------------------- + +const ClassInfo DOMTreeWalker::info = { "TreeWalker", 0, &DOMTreeWalkerTable, 0 }; +/* +@begin DOMTreeWalkerTable 5 + root DOMTreeWalker::Root DontDelete|ReadOnly + whatToShow DOMTreeWalker::WhatToShow DontDelete|ReadOnly + filter DOMTreeWalker::Filter DontDelete|ReadOnly + expandEntityReferences DOMTreeWalker::ExpandEntityReferences DontDelete|ReadOnly + currentNode DOMTreeWalker::CurrentNode DontDelete +@end +@begin DOMTreeWalkerProtoTable 7 + parentNode DOMTreeWalker::ParentNode DontDelete|Function 0 + firstChild DOMTreeWalker::FirstChild DontDelete|Function 0 + lastChild DOMTreeWalker::LastChild DontDelete|Function 0 + previousSibling DOMTreeWalker::PreviousSibling DontDelete|Function 0 + nextSibling DOMTreeWalker::NextSibling DontDelete|Function 0 + previousNode DOMTreeWalker::PreviousNode DontDelete|Function 0 + nextNode DOMTreeWalker::NextNode DontDelete|Function 0 +@end +*/ +KJS_DEFINE_PROTOTYPE(DOMTreeWalkerProto) +IMPLEMENT_PROTOFUNC_DOM(DOMTreeWalkerProtoFunc) +KJS_IMPLEMENT_PROTOTYPE("DOMTreeWalker", DOMTreeWalkerProto,DOMTreeWalkerProtoFunc) + +DOMTreeWalker::DOMTreeWalker(ExecState *exec, DOM::TreeWalker tw) + : DOMObject(DOMTreeWalkerProto::self(exec)), treeWalker(tw) {} + +DOMTreeWalker::~DOMTreeWalker() +{ + ScriptInterpreter::forgetDOMObject(treeWalker.handle()); +} + +Value DOMTreeWalker::tryGet(ExecState *exec, const Identifier &p) const +{ + return DOMObjectLookupGetValue(exec,p,&DOMTreeWalkerTable,this); +} + +Value DOMTreeWalker::getValueProperty(ExecState *exec, int token) const +{ + DOM::TreeWalker tw(treeWalker); + switch (token) { + case Root: + return getDOMNode(exec,tw.root()); + case WhatToShow: + return Number(tw.whatToShow()); + case Filter: + return getDOMNodeFilter(exec,tw.filter()); + case ExpandEntityReferences: + return Boolean(tw.expandEntityReferences()); + case CurrentNode: + return getDOMNode(exec,tw.currentNode()); + default: + kdDebug(6070) << "WARNING: Unhandled token in DOMTreeWalker::getValueProperty : " << token << endl; + return Value(); + } +} + +void DOMTreeWalker::tryPut(ExecState *exec, const Identifier &propertyName, + const Value& value, int attr) +{ + if (propertyName == "currentNode") { + treeWalker.setCurrentNode(toNode(value)); + } + else + ObjectImp::put(exec, propertyName, value, attr); +} + +Value DOMTreeWalkerProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &) +{ + KJS_CHECK_THIS( KJS::DOMTreeWalker, thisObj ); + DOM::TreeWalker treeWalker = static_cast(thisObj.imp())->toTreeWalker(); + switch (id) { + case DOMTreeWalker::ParentNode: + return getDOMNode(exec,treeWalker.parentNode()); + case DOMTreeWalker::FirstChild: + return getDOMNode(exec,treeWalker.firstChild()); + case DOMTreeWalker::LastChild: + return getDOMNode(exec,treeWalker.lastChild()); + case DOMTreeWalker::PreviousSibling: + return getDOMNode(exec,treeWalker.previousSibling()); + case DOMTreeWalker::NextSibling: + return getDOMNode(exec,treeWalker.nextSibling()); + case DOMTreeWalker::PreviousNode: + return getDOMNode(exec,treeWalker.previousSibling()); + case DOMTreeWalker::NextNode: + return getDOMNode(exec,treeWalker.nextNode()); + } + return Undefined(); +} + +Value getDOMTreeWalker(ExecState *exec, DOM::TreeWalker tw) +{ + return cacheDOMObject(exec, tw); +} + +DOM::NodeFilter toNodeFilter(const Value& val) +{ + Object obj = Object::dynamicCast(val); + if (!obj.isValid() || !obj.inherits(&DOMNodeFilter::info)) + return DOM::NodeFilter(); + + const DOMNodeFilter *dobj = static_cast(obj.imp()); + return dobj->toNodeFilter(); +} + +// ------------------------------------------------------------------------- + +JSNodeFilter::JSNodeFilter(Object & _filter) : DOM::CustomNodeFilter(), filter( _filter ) +{ +} + +JSNodeFilter::~JSNodeFilter() +{ +} + +short JSNodeFilter::acceptNode(const DOM::Node &n) +{ + KHTMLView *view = static_cast( n.handle()->docPtr() )->view(); + if (!view) + return DOM::NodeFilter::FILTER_REJECT; + + KHTMLPart *part = view->part(); + KJSProxy *proxy = part->jScript(); + if (proxy) { + ExecState *exec = proxy->interpreter()->globalExec(); + Object acceptNodeFunc = Object::dynamicCast( filter.get(exec, "acceptNode") ); + if (!acceptNodeFunc.isNull() && acceptNodeFunc.implementsCall()) { + List args; + args.append(getDOMNode(exec,n)); + Value result = acceptNodeFunc.call(exec,filter,args); + if (exec->hadException()) + exec->clearException(); + return result.toInteger(exec); + } + } + + return DOM::NodeFilter::FILTER_REJECT; +} + +} //namespace KJS diff --git a/khtml/ecma/kjs_traversal.h b/khtml/ecma/kjs_traversal.h new file mode 100644 index 000000000..fd7e064b1 --- /dev/null +++ b/khtml/ecma/kjs_traversal.h @@ -0,0 +1,108 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * + * 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 + */ + +#ifndef _KJS_TRAVERSAL_H_ +#define _KJS_TRAVERSAL_H_ + +#include "ecma/kjs_dom.h" +#include "dom/dom2_traversal.h" + +namespace KJS { + + class DOMNodeIterator : public DOMObject { + public: + DOMNodeIterator(ExecState *exec, DOM::NodeIterator ni); + ~DOMNodeIterator(); + virtual Value tryGet(ExecState *exec,const Identifier &p) const; + Value getValueProperty(ExecState *exec, int token) const; + // no put - all read-only + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { Filter, Root, WhatToShow, ExpandEntityReferences, + NextNode, PreviousNode, Detach }; + DOM::NodeIterator toNodeIterator() const { return nodeIterator; } + protected: + DOM::NodeIterator nodeIterator; + }; + + // Constructor object NodeFilter + class NodeFilterConstructor : public DOMObject { + public: + NodeFilterConstructor(ExecState *); + virtual Value tryGet(ExecState *exec,const Identifier &p) const; + Value getValueProperty(ExecState *exec, int token) const; + // no put - all read-only + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + class DOMNodeFilter : public DOMObject { + public: + DOMNodeFilter(ExecState *exec, DOM::NodeFilter nf); + ~DOMNodeFilter(); + // no put - all read-only + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + virtual DOM::NodeFilter toNodeFilter() const { return nodeFilter; } + enum { AcceptNode }; + protected: + DOM::NodeFilter nodeFilter; + }; + + class DOMTreeWalker : public DOMObject { + public: + DOMTreeWalker(ExecState *exec, DOM::TreeWalker tw); + ~DOMTreeWalker(); + virtual Value tryGet(ExecState *exec,const Identifier &p) const; + Value getValueProperty(ExecState *exec, int token) const; + virtual void tryPut(ExecState *exec, const Identifier &propertyName, + const Value& value, int attr = None); + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { Root, WhatToShow, Filter, ExpandEntityReferences, CurrentNode, + ParentNode, FirstChild, LastChild, PreviousSibling, NextSibling, + PreviousNode, NextNode }; + DOM::TreeWalker toTreeWalker() const { return treeWalker; } + protected: + DOM::TreeWalker treeWalker; + }; + + Value getDOMNodeIterator(ExecState *exec, DOM::NodeIterator ni); + Value getNodeFilterConstructor(ExecState *exec); + Value getDOMNodeFilter(ExecState *exec, DOM::NodeFilter nf); + Value getDOMTreeWalker(ExecState *exec, DOM::TreeWalker tw); + + /** + * Convert an object to a NodeFilter. Returns a null Node if not possible. + */ + DOM::NodeFilter toNodeFilter(const Value&); + + class JSNodeFilter : public DOM::CustomNodeFilter { + public: + JSNodeFilter(Object & _filter); + virtual ~JSNodeFilter(); + virtual short acceptNode (const DOM::Node &n); + protected: + Object filter; + }; + +} // namespace + +#endif diff --git a/khtml/ecma/kjs_views.cpp b/khtml/ecma/kjs_views.cpp new file mode 100644 index 000000000..dbf354ce8 --- /dev/null +++ b/khtml/ecma/kjs_views.cpp @@ -0,0 +1,91 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * + * 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 "ecma/kjs_views.h" +#include "ecma/kjs_css.h" +#include "ecma/kjs_window.h" +#include "kjs_views.lut.h" + +using namespace KJS; + +// ------------------------------------------------------------------------- + +const ClassInfo DOMAbstractView::info = { "AbstractView", 0, &DOMAbstractViewTable, 0 }; +/* +@begin DOMAbstractViewTable 2 + document DOMAbstractView::Document DontDelete|ReadOnly + getComputedStyle DOMAbstractView::GetComputedStyle DontDelete|Function 2 +@end +*/ +IMPLEMENT_PROTOFUNC_DOM(DOMAbstractViewFunc) + +DOMAbstractView::DOMAbstractView(ExecState *exec, DOM::AbstractView av) + : DOMObject(exec->interpreter()->builtinObjectPrototype()), abstractView(av) {} + +DOMAbstractView::~DOMAbstractView() +{ + ScriptInterpreter::forgetDOMObject(abstractView.handle()); +} + +Value DOMAbstractView::tryGet(ExecState *exec, const Identifier &p) const +{ + if ( p == "document" ) + return getDOMNode(exec,abstractView.document()); + else if ( p == "getComputedStyle" ) + return lookupOrCreateFunction(exec,p,this,DOMAbstractView::GetComputedStyle,2,DontDelete|Function); + else + return DOMObject::tryGet(exec,p); +} + +Value DOMAbstractViewFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + KJS_CHECK_THIS( KJS::DOMAbstractView, thisObj ); + DOM::AbstractView abstractView = static_cast(thisObj.imp())->toAbstractView(); + switch (id) { + case DOMAbstractView::GetComputedStyle: { + DOM::Node arg0 = toNode(args[0]); + if (arg0.nodeType() != DOM::Node::ELEMENT_NODE) + return Undefined(); // throw exception? + else + return getDOMCSSStyleDeclaration(exec,abstractView.getComputedStyle(static_cast(arg0), + args[1].toString(exec).string())); + } + } + return Undefined(); +} + +Value KJS::getDOMAbstractView(ExecState *exec, DOM::AbstractView av) +{ + return cacheDOMObject(exec, av); +} + +DOM::AbstractView KJS::toAbstractView (const Value& val) +{ + Object obj = Object::dynamicCast(val); + if (!obj.isValid() || !obj.inherits(&DOMAbstractView::info)) + return DOM::AbstractView (); + + // the Window object is considered for all practical purposes as a descendant of AbstractView + if (obj.inherits(&Window::info)) + return static_cast(obj.imp())->toAbstractView(); + + const DOMAbstractView *dobj = static_cast(obj.imp()); + return dobj->toAbstractView (); +} diff --git a/khtml/ecma/kjs_views.h b/khtml/ecma/kjs_views.h new file mode 100644 index 000000000..11e103fde --- /dev/null +++ b/khtml/ecma/kjs_views.h @@ -0,0 +1,53 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * + * 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 + */ + +#ifndef _KJS_VIEWS_H_ +#define _KJS_VIEWS_H_ + +#include "ecma/kjs_dom.h" +#include "dom/dom2_views.h" + +namespace KJS { + + + class DOMAbstractView : public DOMObject { + public: + DOMAbstractView(ExecState *, DOM::AbstractView av); + ~DOMAbstractView(); + virtual Value tryGet(ExecState *exec,const Identifier &p) const; + // no put - all read-only + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + virtual DOM::AbstractView toAbstractView() const { return abstractView; } + enum { Document, GetComputedStyle }; + protected: + DOM::AbstractView abstractView; + }; + + Value getDOMAbstractView(ExecState *exec, DOM::AbstractView av); + + /** + * Convert an object to an AbstractView. Returns a null Node if not possible. + */ + DOM::AbstractView toAbstractView(const Value&); + +} // namespace + +#endif diff --git a/khtml/ecma/kjs_window.cpp b/khtml/ecma/kjs_window.cpp new file mode 100644 index 000000000..bc5a85e19 --- /dev/null +++ b/khtml/ecma/kjs_window.cpp @@ -0,0 +1,2935 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2000-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 "config.h" + +#include "khtmlview.h" +#include "khtml_part.h" +#include "khtmlpart_p.h" +#include "khtml_settings.h" +#include "xml/dom2_eventsimpl.h" +#include "xml/dom_docimpl.h" +#include "misc/htmltags.h" +#include "html/html_documentimpl.h" +#include "rendering/render_frames.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include // schroder +#endif + +#ifndef KONQ_EMBEDDED +#include +#endif +#include +#include +#include +#include +#include + +#include "kjs_proxy.h" +#include "kjs_window.h" +#include "kjs_navigator.h" +#include "kjs_mozilla.h" +#include "kjs_html.h" +#include "kjs_range.h" +#include "kjs_traversal.h" +#include "kjs_css.h" +#include "kjs_events.h" +#include "kjs_views.h" +#include "xmlhttprequest.h" +#include "xmlserializer.h" +#include "domparser.h" + +using namespace KJS; + +namespace KJS { + + class History : public ObjectImp { + friend class HistoryFunc; + public: + History(ExecState *exec, KHTMLPart *p) + : ObjectImp(exec->interpreter()->builtinObjectPrototype()), part(p) { } + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + Value getValueProperty(ExecState *exec, int token) const; + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { Back, Forward, Go, Length }; + private: + QGuardedPtr part; + }; + + class External : public ObjectImp { + friend class ExternalFunc; + public: + External(ExecState *exec, KHTMLPart *p) + : ObjectImp(exec->interpreter()->builtinObjectPrototype()), part(p) { } + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { AddFavorite }; + private: + QGuardedPtr part; + }; + + class FrameArray : public ObjectImp { + public: + FrameArray(ExecState *exec, KHTMLPart *p) + : ObjectImp(exec->interpreter()->builtinObjectPrototype()), part(p) { } + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + virtual Value call(ExecState *exec, Object &thisObj, const List &args); + virtual bool implementsCall() const { return true; } + private: + QGuardedPtr part; + }; + +#ifdef Q_WS_QWS + class KonquerorFunc : public DOMFunction { + public: + KonquerorFunc(ExecState *exec, const Konqueror* k, const char* name) + : DOMFunction(exec), konqueror(k), m_name(name) { } + virtual Value tryCall(ExecState *exec, Object &thisObj, const List &args); + + private: + const Konqueror* konqueror; + QCString m_name; + }; +#endif +} // namespace KJS + +#include "kjs_window.lut.h" +#include "rendering/render_replaced.h" + +////////////////////// Screen Object //////////////////////// +namespace KJS { +// table for screen object +/* +@begin ScreenTable 7 + height Screen::Height DontEnum|ReadOnly + width Screen::Width DontEnum|ReadOnly + colorDepth Screen::ColorDepth DontEnum|ReadOnly + pixelDepth Screen::PixelDepth DontEnum|ReadOnly + availLeft Screen::AvailLeft DontEnum|ReadOnly + availTop Screen::AvailTop DontEnum|ReadOnly + availHeight Screen::AvailHeight DontEnum|ReadOnly + availWidth Screen::AvailWidth DontEnum|ReadOnly +@end +*/ + +const ClassInfo Screen::info = { "Screen", 0, &ScreenTable, 0 }; + +// We set the object prototype so that toString is implemented +Screen::Screen(ExecState *exec) + : ObjectImp(exec->interpreter()->builtinObjectPrototype()) {} + +Value Screen::get(ExecState *exec, const Identifier &p) const +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "Screen::get " << p.qstring() << endl; +#endif + return lookupGetValue(exec,p,&ScreenTable,this); +} + +Value Screen::getValueProperty(ExecState *exec, int token) const +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + KWinModule info(0, KWinModule::INFO_DESKTOP); +#endif + QWidget *thisWidget = Window::retrieveActive(exec)->part()->widget(); + QRect sg = KGlobalSettings::desktopGeometry(thisWidget); + + switch( token ) { + case Height: + return Number(sg.height()); + case Width: + return Number(sg.width()); + case ColorDepth: + case PixelDepth: { + QPaintDeviceMetrics m(QApplication::desktop()); + return Number(m.depth()); + } + case AvailLeft: { +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + QRect clipped = info.workArea().intersect(sg); + return Number(clipped.x()-sg.x()); +#else + return Number(10); +#endif + } + case AvailTop: { +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + QRect clipped = info.workArea().intersect(sg); + return Number(clipped.y()-sg.y()); +#else + return Number(10); +#endif + } + case AvailHeight: { +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + QRect clipped = info.workArea().intersect(sg); + return Number(clipped.height()); +#else + return Number(100); +#endif + } + case AvailWidth: { +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + QRect clipped = info.workArea().intersect(sg); + return Number(clipped.width()); +#else + return Number(100); +#endif + } + default: + kdDebug(6070) << "WARNING: Screen::getValueProperty unhandled token " << token << endl; + return Undefined(); + } +} + +////////////////////// Window Object //////////////////////// + +const ClassInfo Window::info = { "Window", &DOMAbstractView::info, &WindowTable, 0 }; + +/* +@begin WindowTable 162 + atob Window::AToB DontDelete|Function 1 + btoa Window::BToA DontDelete|Function 1 + closed Window::Closed DontDelete|ReadOnly + crypto Window::Crypto DontDelete|ReadOnly + defaultStatus Window::DefaultStatus DontDelete + defaultstatus Window::DefaultStatus DontDelete + status Window::Status DontDelete + document Window::Document DontDelete|ReadOnly + frameElement Window::FrameElement DontDelete|ReadOnly + frames Window::Frames DontDelete|ReadOnly + history Window::_History DontDelete|ReadOnly + external Window::_External DontDelete|ReadOnly + event Window::Event DontDelete|ReadOnly + innerHeight Window::InnerHeight DontDelete|ReadOnly + innerWidth Window::InnerWidth DontDelete|ReadOnly + length Window::Length DontDelete|ReadOnly + location Window::_Location DontDelete + name Window::Name DontDelete + navigator Window::_Navigator DontDelete|ReadOnly + clientInformation Window::ClientInformation DontDelete|ReadOnly + konqueror Window::_Konqueror DontDelete|ReadOnly + offscreenBuffering Window::OffscreenBuffering DontDelete|ReadOnly + opener Window::Opener DontDelete|ReadOnly + outerHeight Window::OuterHeight DontDelete|ReadOnly + outerWidth Window::OuterWidth DontDelete|ReadOnly + pageXOffset Window::PageXOffset DontDelete|ReadOnly + pageYOffset Window::PageYOffset DontDelete|ReadOnly + parent Window::Parent DontDelete|ReadOnly + personalbar Window::Personalbar DontDelete|ReadOnly + screenX Window::ScreenX DontDelete|ReadOnly + screenY Window::ScreenY DontDelete|ReadOnly + scrollbars Window::Scrollbars DontDelete|ReadOnly + scroll Window::Scroll DontDelete|Function 2 + scrollBy Window::ScrollBy DontDelete|Function 2 + scrollTo Window::ScrollTo DontDelete|Function 2 + scrollX Window::ScrollX DontDelete|ReadOnly + scrollY Window::ScrollY DontDelete|ReadOnly + moveBy Window::MoveBy DontDelete|Function 2 + moveTo Window::MoveTo DontDelete|Function 2 + resizeBy Window::ResizeBy DontDelete|Function 2 + resizeTo Window::ResizeTo DontDelete|Function 2 + self Window::Self DontDelete|ReadOnly + window Window::_Window DontDelete|ReadOnly + top Window::Top DontDelete|ReadOnly + screen Window::_Screen DontDelete|ReadOnly + alert Window::Alert DontDelete|Function 1 + confirm Window::Confirm DontDelete|Function 1 + prompt Window::Prompt DontDelete|Function 2 + open Window::Open DontDelete|Function 3 + setTimeout Window::SetTimeout DontDelete|Function 2 + clearTimeout Window::ClearTimeout DontDelete|Function 1 + focus Window::Focus DontDelete|Function 0 + blur Window::Blur DontDelete|Function 0 + close Window::Close DontDelete|Function 0 + setInterval Window::SetInterval DontDelete|Function 2 + clearInterval Window::ClearInterval DontDelete|Function 1 + captureEvents Window::CaptureEvents DontDelete|Function 0 + releaseEvents Window::ReleaseEvents DontDelete|Function 0 + print Window::Print DontDelete|Function 0 + addEventListener Window::AddEventListener DontDelete|Function 3 + removeEventListener Window::RemoveEventListener DontDelete|Function 3 +# Normally found in prototype. Add to window object itself to make them +# accessible in closed and cross-site windows + valueOf Window::ValueOf DontDelete|Function 0 + toString Window::ToString DontDelete|Function 0 +# IE extension + navigate Window::Navigate DontDelete|Function 1 +# Mozilla extension + sidebar Window::SideBar DontDelete|ReadOnly + getComputedStyle Window::GetComputedStyle DontDelete|Function 2 + +# Warning, when adding a function to this object you need to add a case in Window::get + +# Event handlers +# IE also has: onactivate, onbefore/afterprint, onbeforedeactivate/unload, oncontrolselect, +# ondeactivate, onhelp, onmovestart/end, onresizestart/end, onscroll. +# It doesn't have onabort, onchange, ondragdrop (but NS has that last one). + onabort Window::Onabort DontDelete + onblur Window::Onblur DontDelete + onchange Window::Onchange DontDelete + onclick Window::Onclick DontDelete + ondblclick Window::Ondblclick DontDelete + ondragdrop Window::Ondragdrop DontDelete + onerror Window::Onerror DontDelete + onfocus Window::Onfocus DontDelete + onkeydown Window::Onkeydown DontDelete + onkeypress Window::Onkeypress DontDelete + onkeyup Window::Onkeyup DontDelete + onload Window::Onload DontDelete + onmousedown Window::Onmousedown DontDelete + onmousemove Window::Onmousemove DontDelete + onmouseout Window::Onmouseout DontDelete + onmouseover Window::Onmouseover DontDelete + onmouseup Window::Onmouseup DontDelete + onmove Window::Onmove DontDelete + onreset Window::Onreset DontDelete + onresize Window::Onresize DontDelete + onselect Window::Onselect DontDelete + onsubmit Window::Onsubmit DontDelete + onunload Window::Onunload DontDelete + +# Constructors/constant tables + Node Window::Node DontDelete + Event Window::EventCtor DontDelete + Range Window::Range DontDelete + NodeFilter Window::NodeFilter DontDelete + DOMException Window::DOMException DontDelete + CSSRule Window::CSSRule DontDelete + MutationEvent Window::MutationEventCtor DontDelete + KeyboardEvent Window::KeyboardEventCtor DontDelete + EventException Window::EventExceptionCtor DontDelete + Image Window::Image DontDelete|ReadOnly + Option Window::Option DontDelete|ReadOnly + XMLHttpRequest Window::XMLHttpRequest DontDelete|ReadOnly + XMLSerializer Window::XMLSerializer DontDelete|ReadOnly + DOMParser Window::DOMParser DontDelete|ReadOnly + +# Mozilla dom emulation ones. + Element Window::ElementCtor DontDelete + Document Window::DocumentCtor DontDelete + #this one is an alias since we don't have a separate XMLDocument + XMLDocument Window::DocumentCtor DontDelete + HTMLElement Window::HTMLElementCtor DontDelete + HTMLDocument Window::HTMLDocumentCtor DontDelete + HTMLHtmlElement Window::HTMLHtmlElementCtor DontDelete + HTMLHeadElement Window::HTMLHeadElementCtor DontDelete + HTMLLinkElement Window::HTMLLinkElementCtor DontDelete + HTMLTitleElement Window::HTMLTitleElementCtor DontDelete + HTMLMetaElement Window::HTMLMetaElementCtor DontDelete + HTMLBaseElement Window::HTMLBaseElementCtor DontDelete + HTMLIsIndexElement Window::HTMLIsIndexElementCtor DontDelete + HTMLStyleElement Window::HTMLStyleElementCtor DontDelete + HTMLBodyElement Window::HTMLBodyElementCtor DontDelete + HTMLFormElement Window::HTMLFormElementCtor DontDelete + HTMLSelectElement Window::HTMLSelectElementCtor DontDelete + HTMLOptGroupElement Window::HTMLOptGroupElementCtor DontDelete + HTMLOptionElement Window::HTMLOptionElementCtor DontDelete + HTMLInputElement Window::HTMLInputElementCtor DontDelete + HTMLTextAreaElement Window::HTMLTextAreaElementCtor DontDelete + HTMLButtonElement Window::HTMLButtonElementCtor DontDelete + HTMLLabelElement Window::HTMLLabelElementCtor DontDelete + HTMLFieldSetElement Window::HTMLFieldSetElementCtor DontDelete + HTMLLegendElement Window::HTMLLegendElementCtor DontDelete + HTMLUListElement Window::HTMLUListElementCtor DontDelete + HTMLOListElement Window::HTMLOListElementCtor DontDelete + HTMLDListElement Window::HTMLDListElementCtor DontDelete + HTMLDirectoryElement Window::HTMLDirectoryElementCtor DontDelete + HTMLMenuElement Window::HTMLMenuElementCtor DontDelete + HTMLLIElement Window::HTMLLIElementCtor DontDelete + HTMLDivElement Window::HTMLDivElementCtor DontDelete + HTMLParagraphElement Window::HTMLParagraphElementCtor DontDelete + HTMLHeadingElement Window::HTMLHeadingElementCtor DontDelete + HTMLBlockQuoteElement Window::HTMLBlockQuoteElementCtor DontDelete + HTMLQuoteElement Window::HTMLQuoteElementCtor DontDelete + HTMLPreElement Window::HTMLPreElementCtor DontDelete + HTMLBRElement Window::HTMLBRElementCtor DontDelete + HTMLBaseFontElement Window::HTMLBaseFontElementCtor DontDelete + HTMLFontElement Window::HTMLFontElementCtor DontDelete + HTMLHRElement Window::HTMLHRElementCtor DontDelete + HTMLModElement Window::HTMLModElementCtor DontDelete + HTMLAnchorElement Window::HTMLAnchorElementCtor DontDelete + HTMLImageElement Window::HTMLImageElementCtor DontDelete + HTMLObjectElement Window::HTMLObjectElementCtor DontDelete + HTMLParamElement Window::HTMLParamElementCtor DontDelete + HTMLAppletElement Window::HTMLAppletElementCtor DontDelete + HTMLMapElement Window::HTMLMapElementCtor DontDelete + HTMLAreaElement Window::HTMLAreaElementCtor DontDelete + HTMLScriptElement Window::HTMLScriptElementCtor DontDelete + HTMLTableElement Window::HTMLTableElementCtor DontDelete + HTMLTableCaptionElement Window::HTMLTableCaptionElementCtor DontDelete + HTMLTableColElement Window::HTMLTableColElementCtor DontDelete + HTMLTableSectionElement Window::HTMLTableSectionElementCtor DontDelete + HTMLTableRowElement Window::HTMLTableRowElementCtor DontDelete + HTMLTableCellElement Window::HTMLTableCellElementCtor DontDelete + HTMLFrameSetElement Window::HTMLFrameSetElementCtor DontDelete + HTMLLayerElement Window::HTMLLayerElementCtor DontDelete + HTMLFrameElement Window::HTMLFrameElementCtor DontDelete + HTMLIFrameElement Window::HTMLIFrameElementCtor DontDelete + CSSStyleDeclaration Window::CSSStyleDeclarationCtor DontDelete +@end +*/ +IMPLEMENT_PROTOFUNC_DOM(WindowFunc) + +Window::Window(khtml::ChildFrame *p) + : ObjectImp(/*no proto*/), m_frame(p), screen(0), history(0), external(0), m_frames(0), loc(0), m_evt(0) +{ + winq = new WindowQObject(this); + //kdDebug(6070) << "Window::Window this=" << this << " part=" << m_part << " " << m_part->name() << endl; +} + +Window::~Window() +{ + delete winq; +} + +Window *Window::retrieveWindow(KParts::ReadOnlyPart *p) +{ + Object obj = Object::dynamicCast( retrieve( p ) ); +#ifndef NDEBUG + // obj should never be null, except when javascript has been disabled in that part. + KHTMLPart *part = ::qt_cast(p); + if ( part && part->jScriptEnabled() ) + { + assert( obj.isValid() ); +#ifndef QWS + assert( dynamic_cast(obj.imp()) ); // type checking +#endif + } +#endif + if ( !obj.isValid() ) // JS disabled + return 0; + return static_cast(obj.imp()); +} + +Window *Window::retrieveActive(ExecState *exec) +{ + ValueImp *imp = exec->interpreter()->globalObject().imp(); + assert( imp ); +#ifndef QWS + assert( dynamic_cast(imp) ); +#endif + return static_cast(imp); +} + +Value Window::retrieve(KParts::ReadOnlyPart *p) +{ + assert(p); + KHTMLPart * part = ::qt_cast(p); + KJSProxy *proxy = 0L; + if (!part) { + part = ::qt_cast(p->parent()); + if (part) + proxy = part->framejScript(p); + } else + proxy = part->jScript(); + if (proxy) { +#ifdef KJS_VERBOSE + kdDebug(6070) << "Window::retrieve part=" << part << " '" << part->name() << "' interpreter=" << proxy->interpreter() << " window=" << proxy->interpreter()->globalObject().imp() << endl; +#endif + return proxy->interpreter()->globalObject(); // the Global object is the "window" + } else { +#ifdef KJS_VERBOSE + kdDebug(6070) << "Window::retrieve part=" << p << " '" << p->name() << "' no jsproxy." << endl; +#endif + return Undefined(); // This can happen with JS disabled on the domain of that window + } +} + +Location *Window::location() const +{ + if (!loc) + const_cast(this)->loc = new Location(m_frame); + return loc; +} + +ObjectImp* Window::frames( ExecState* exec ) const +{ + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (part) + return m_frames ? m_frames : + (const_cast(this)->m_frames = new FrameArray(exec, part)); + return 0L; +} + +// reference our special objects during garbage collection +void Window::mark() +{ + ObjectImp::mark(); + if (screen && !screen->marked()) + screen->mark(); + if (history && !history->marked()) + history->mark(); + if (external && !external->marked()) + external->mark(); + if (m_frames && !m_frames->marked()) + m_frames->mark(); + //kdDebug(6070) << "Window::mark " << this << " marking loc=" << loc << endl; + if (loc && !loc->marked()) + loc->mark(); + if (winq) + winq->mark(); +} + +bool Window::hasProperty(ExecState *exec, const Identifier &p) const +{ + // we don't want any operations on a closed window + if (m_frame.isNull() || m_frame->m_part.isNull()) + return ( p == "closed" ); + + if (ObjectImp::hasProperty(exec, p)) + return true; + + if (Lookup::findEntry(&WindowTable, p)) + return true; + + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part) + return false; + + QString q = p.qstring(); + if (part->findFramePart(p.qstring())) + return true; + // allow window[1] or parent[1] etc. (#56983) + bool ok; + unsigned int i = p.toArrayIndex(&ok); + if (ok) { + QPtrList frames = part->frames(); + unsigned int len = frames.count(); + if (i < len) + return true; + } + + // allow shortcuts like 'Image1' instead of document.images.Image1 + if (part->document().isHTMLDocument()) { // might be XML + DOM::HTMLDocument doc = part->htmlDocument(); + // Keep in sync with tryGet + + if (static_cast(doc.handle())->underDocNamedCache().get(p.qstring())) + return true; + + return !doc.getElementById(p.string()).isNull(); + } + + return false; +} + +UString Window::toString(ExecState *) const +{ + return "[object Window]"; +} + +Value Window::get(ExecState *exec, const Identifier &p) const +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "Window("<m_part.isNull()) { + const HashEntry* entry = Lookup::findEntry(&WindowTable, p); + if (entry) { + switch (entry->value) { + case Closed: + return Boolean(true); + case _Location: + return Null(); + case ValueOf: + case ToString: + return lookupOrCreateFunction(exec,p, this, entry->value, + entry->params, entry->attr); + default: + break; + } + } + return Undefined(); + } + + // Look for overrides first + ValueImp *val = getDirect(p); + if (val) { + //kdDebug(6070) << "Window::get found dynamic property '" << p.ascii() << "'" << endl; + return isSafeScript(exec) ? Value(val) : Undefined(); + } + + const HashEntry* entry = Lookup::findEntry(&WindowTable, p); + KHTMLPart *part = ::qt_cast(m_frame->m_part); + + // properties that work on all windows + if (entry) { + // ReadOnlyPart first + switch(entry->value) { + case Closed: + return Boolean( false ); + case _Location: + // No isSafeScript test here, we must be able to _set_ location.href (#49819) + return Value(location()); + case _Window: + case Self: + return retrieve(m_frame->m_part); + default: + break; + } + if (!part) + return Undefined(); + // KHTMLPart next + switch(entry->value) { + case Frames: + return Value(frames(exec)); + case Opener: + if (!part->opener()) + return Null(); // ### a null Window might be better, but == null + else // doesn't work yet + return retrieve(part->opener()); + case Parent: + return retrieve(part->parentPart() ? part->parentPart() : (KHTMLPart*)part); + case Top: { + KHTMLPart *p = part; + while (p->parentPart()) + p = p->parentPart(); + return retrieve(p); + } + case Alert: + case Confirm: + case Prompt: + case Open: + case Close: + case Focus: + case Blur: + case AToB: + case BToA: + case GetComputedStyle: + case ValueOf: + case ToString: + return lookupOrCreateFunction(exec,p,this,entry->value,entry->params,entry->attr); + default: + break; + } + } else if (!part) { + // not a KHTMLPart + QString rvalue; + KParts::LiveConnectExtension::Type rtype; + unsigned long robjid; + if (m_frame->m_liveconnect && + isSafeScript(exec) && + m_frame->m_liveconnect->get(0, p.qstring(), rtype, robjid, rvalue)) + return getLiveConnectValue(m_frame->m_liveconnect, p.qstring(), rtype, rvalue, robjid); + return Undefined(); + } + // properties that only work on safe windows + if (isSafeScript(exec) && entry) + { + //kdDebug(6070) << "token: " << entry->value << endl; + switch( entry->value ) { + case Crypto: + return Undefined(); // ### + case DefaultStatus: + return String(UString(part->jsDefaultStatusBarText())); + case Status: + return String(UString(part->jsStatusBarText())); + case Document: + if (part->document().isNull()) { + kdDebug(6070) << "Document.write: adding to create document" << endl; + part->begin(); + part->write(""); + part->end(); + } + return getDOMNode(exec,part->document()); + case FrameElement: + if (m_frame->m_frame) + return getDOMNode(exec,m_frame->m_frame->element()); + else + return Undefined(); + case Node: + return NodeConstructor::self(exec); + case Range: + return getRangeConstructor(exec); + case NodeFilter: + return getNodeFilterConstructor(exec); + case DOMException: + return getDOMExceptionConstructor(exec); + case CSSRule: + return getCSSRuleConstructor(exec); + case ElementCtor: + return ElementPseudoCtor::self(exec); + case HTMLElementCtor: + return HTMLElementPseudoCtor::self(exec); + case HTMLHtmlElementCtor: + return HTMLHtmlElementPseudoCtor::self(exec); + case HTMLHeadElementCtor: + return HTMLHeadElementPseudoCtor::self(exec); + case HTMLLinkElementCtor: + return HTMLLinkElementPseudoCtor::self(exec); + case HTMLTitleElementCtor: + return HTMLTitleElementPseudoCtor::self(exec); + case HTMLMetaElementCtor: + return HTMLMetaElementPseudoCtor::self(exec); + case HTMLBaseElementCtor: + return HTMLBaseElementPseudoCtor::self(exec); + case HTMLIsIndexElementCtor: + return HTMLIsIndexElementPseudoCtor::self(exec); + case HTMLStyleElementCtor: + return HTMLStyleElementPseudoCtor::self(exec); + case HTMLBodyElementCtor: + return HTMLBodyElementPseudoCtor::self(exec); + case HTMLFormElementCtor: + return HTMLFormElementPseudoCtor::self(exec); + case HTMLSelectElementCtor: + return HTMLSelectElementPseudoCtor::self(exec); + case HTMLOptGroupElementCtor: + return HTMLOptGroupElementPseudoCtor::self(exec); + case HTMLOptionElementCtor: + return HTMLOptionElementPseudoCtor::self(exec); + case HTMLInputElementCtor: + return HTMLInputElementPseudoCtor::self(exec); + case HTMLTextAreaElementCtor: + return HTMLTextAreaElementPseudoCtor::self(exec); + case HTMLButtonElementCtor: + return HTMLButtonElementPseudoCtor::self(exec); + case HTMLLabelElementCtor: + return HTMLLabelElementPseudoCtor::self(exec); + case HTMLFieldSetElementCtor: + return HTMLFieldSetElementPseudoCtor::self(exec); + case HTMLLegendElementCtor: + return HTMLLegendElementPseudoCtor::self(exec); + case HTMLUListElementCtor: + return HTMLUListElementPseudoCtor::self(exec); + case HTMLOListElementCtor: + return HTMLOListElementPseudoCtor::self(exec); + case HTMLDListElementCtor: + return HTMLDListElementPseudoCtor::self(exec); + case HTMLDirectoryElementCtor: + return HTMLDirectoryElementPseudoCtor::self(exec); + case HTMLMenuElementCtor: + return HTMLMenuElementPseudoCtor::self(exec); + case HTMLLIElementCtor: + return HTMLLIElementPseudoCtor::self(exec); + case HTMLDivElementCtor: + return HTMLDivElementPseudoCtor::self(exec); + case HTMLParagraphElementCtor: + return HTMLParagraphElementPseudoCtor::self(exec); + case HTMLHeadingElementCtor: + return HTMLHeadingElementPseudoCtor::self(exec); + case HTMLBlockQuoteElementCtor: + return HTMLBlockQuoteElementPseudoCtor::self(exec); + case HTMLQuoteElementCtor: + return HTMLQuoteElementPseudoCtor::self(exec); + case HTMLPreElementCtor: + return HTMLPreElementPseudoCtor::self(exec); + case HTMLBRElementCtor: + return HTMLBRElementPseudoCtor::self(exec); + case HTMLBaseFontElementCtor: + return HTMLBaseFontElementPseudoCtor::self(exec); + case HTMLFontElementCtor: + return HTMLFontElementPseudoCtor::self(exec); + case HTMLHRElementCtor: + return HTMLHRElementPseudoCtor::self(exec); + case HTMLModElementCtor: + return HTMLModElementPseudoCtor::self(exec); + case HTMLAnchorElementCtor: + return HTMLAnchorElementPseudoCtor::self(exec); + case HTMLImageElementCtor: + return HTMLImageElementPseudoCtor::self(exec); + case HTMLObjectElementCtor: + return HTMLObjectElementPseudoCtor::self(exec); + case HTMLParamElementCtor: + return HTMLParamElementPseudoCtor::self(exec); + case HTMLAppletElementCtor: + return HTMLAppletElementPseudoCtor::self(exec); + case HTMLMapElementCtor: + return HTMLMapElementPseudoCtor::self(exec); + case HTMLAreaElementCtor: + return HTMLAreaElementPseudoCtor::self(exec); + case HTMLScriptElementCtor: + return HTMLScriptElementPseudoCtor::self(exec); + case HTMLTableElementCtor: + return HTMLTableElementPseudoCtor::self(exec); + case HTMLTableCaptionElementCtor: + return HTMLTableCaptionElementPseudoCtor::self(exec); + case HTMLTableColElementCtor: + return HTMLTableColElementPseudoCtor::self(exec); + case HTMLTableSectionElementCtor: + return HTMLTableSectionElementPseudoCtor::self(exec); + case HTMLTableRowElementCtor: + return HTMLTableRowElementPseudoCtor::self(exec); + case HTMLTableCellElementCtor: + return HTMLTableCellElementPseudoCtor::self(exec); + case HTMLFrameSetElementCtor: + return HTMLFrameSetElementPseudoCtor::self(exec); + case HTMLLayerElementCtor: + return HTMLLayerElementPseudoCtor::self(exec); + case HTMLFrameElementCtor: + return HTMLFrameElementPseudoCtor::self(exec); + case HTMLIFrameElementCtor: + return HTMLIFrameElementPseudoCtor::self(exec); + case DocumentCtor: + return DocumentPseudoCtor::self(exec); + case HTMLDocumentCtor: + return HTMLDocumentPseudoCtor::self(exec); + case CSSStyleDeclarationCtor: + return CSSStyleDeclarationPseudoCtor::self(exec); + case EventCtor: + return EventConstructor::self(exec); + case MutationEventCtor: + return getMutationEventConstructor(exec); + case KeyboardEventCtor: + return getKeyboardEventConstructor(exec); + case EventExceptionCtor: + return getEventExceptionConstructor(exec); + case _History: + return Value(history ? history : + (const_cast(this)->history = new History(exec,part))); + + case _External: + return Value(external ? external : + (const_cast(this)->external = new External(exec,part))); + + case Event: + if (m_evt) + return getDOMEvent(exec,*m_evt); + else { +#ifdef KJS_VERBOSE + kdDebug(6070) << "WARNING: window(" << this << "," << part->name() << ").event, no event!" << endl; +#endif + return Undefined(); + } + case InnerHeight: + if (!part->view()) + return Undefined(); + khtml::RenderWidget::flushWidgetResizes(); // make sure frames have their final size + return Number(part->view()->visibleHeight()); + case InnerWidth: + if (!part->view()) + return Undefined(); + khtml::RenderWidget::flushWidgetResizes(); // make sure frames have their final size + return Number(part->view()->visibleWidth()); + case Length: + return Number(part->frames().count()); + case Name: + return String(part->name()); + case SideBar: + return Value(new MozillaSidebarExtension(exec, part)); + case _Navigator: + case ClientInformation: { + // Store the navigator in the object so we get the same one each time. + Value nav( new Navigator(exec, part) ); + const_cast(this)->put(exec, "navigator", nav, DontDelete|ReadOnly|Internal); + const_cast(this)->put(exec, "clientInformation", nav, DontDelete|ReadOnly|Internal); + return nav; + } +#ifdef Q_WS_QWS + case _Konqueror: { + Value k( new Konqueror(part) ); + const_cast(this)->put(exec, "konqueror", k, DontDelete|ReadOnly|Internal); + return k; + } +#endif + case OffscreenBuffering: + return Boolean(true); + case OuterHeight: + case OuterWidth: + { +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + if (!part->widget()) + return Number(0); + KWin::WindowInfo inf = KWin::windowInfo(part->widget()->topLevelWidget()->winId()); + return Number(entry->value == OuterHeight ? + inf.geometry().height() : inf.geometry().width()); +#else + return Number(entry->value == OuterHeight ? + part->view()->height() : part->view()->width()); +#endif + } + case PageXOffset: + return Number(part->view()->contentsX()); + case PageYOffset: + return Number(part->view()->contentsY()); + case Personalbar: + return Undefined(); // ### + case ScreenLeft: + case ScreenX: { + if (!part->view()) + return Undefined(); + QRect sg = KGlobalSettings::desktopGeometry(part->view()); + return Number(part->view()->mapToGlobal(QPoint(0,0)).x() + sg.x()); + } + case ScreenTop: + case ScreenY: { + if (!part->view()) + return Undefined(); + QRect sg = KGlobalSettings::desktopGeometry(part->view()); + return Number(part->view()->mapToGlobal(QPoint(0,0)).y() + sg.y()); + } + case ScrollX: { + if (!part->view()) + return Undefined(); + return Number(part->view()->contentsX()); + } + case ScrollY: { + if (!part->view()) + return Undefined(); + return Number(part->view()->contentsY()); + } + case Scrollbars: + return Undefined(); // ### + case _Screen: + return Value(screen ? screen : + (const_cast(this)->screen = new Screen(exec))); + case Image: + return Value(new ImageConstructorImp(exec, part->document())); + case Option: + return Value(new OptionConstructorImp(exec, part->document())); + case XMLHttpRequest: + return Value(new XMLHttpRequestConstructorImp(exec, part->document())); + case XMLSerializer: + return Value(new XMLSerializerConstructorImp(exec)); + case DOMParser: + return Value(new DOMParserConstructorImp(exec, part->xmlDocImpl())); + case Scroll: // compatibility + case ScrollBy: + case ScrollTo: + case MoveBy: + case MoveTo: + case ResizeBy: + case ResizeTo: + case CaptureEvents: + case ReleaseEvents: + case AddEventListener: + case RemoveEventListener: + case SetTimeout: + case ClearTimeout: + case SetInterval: + case ClearInterval: + case Print: + return lookupOrCreateFunction(exec,p,this,entry->value,entry->params,entry->attr); + // IE extension + case Navigate: + // Disabled in NS-compat mode. Supported by default - can't hurt, unless someone uses + // if (navigate) to test for IE (unlikely). + if ( exec->interpreter()->compatMode() == Interpreter::NetscapeCompat ) + return Undefined(); + return lookupOrCreateFunction(exec,p,this,entry->value,entry->params,entry->attr); + case Onabort: + return getListener(exec,DOM::EventImpl::ABORT_EVENT); + case Onblur: + return getListener(exec,DOM::EventImpl::BLUR_EVENT); + case Onchange: + return getListener(exec,DOM::EventImpl::CHANGE_EVENT); + case Onclick: + return getListener(exec,DOM::EventImpl::KHTML_ECMA_CLICK_EVENT); + case Ondblclick: + return getListener(exec,DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT); + case Ondragdrop: + return getListener(exec,DOM::EventImpl::KHTML_DRAGDROP_EVENT); + case Onerror: + return getListener(exec,DOM::EventImpl::ERROR_EVENT); + case Onfocus: + return getListener(exec,DOM::EventImpl::FOCUS_EVENT); + case Onkeydown: + return getListener(exec,DOM::EventImpl::KEYDOWN_EVENT); + case Onkeypress: + return getListener(exec,DOM::EventImpl::KEYPRESS_EVENT); + case Onkeyup: + return getListener(exec,DOM::EventImpl::KEYUP_EVENT); + case Onload: + return getListener(exec,DOM::EventImpl::LOAD_EVENT); + case Onmousedown: + return getListener(exec,DOM::EventImpl::MOUSEDOWN_EVENT); + case Onmousemove: + return getListener(exec,DOM::EventImpl::MOUSEMOVE_EVENT); + case Onmouseout: + return getListener(exec,DOM::EventImpl::MOUSEOUT_EVENT); + case Onmouseover: + return getListener(exec,DOM::EventImpl::MOUSEOVER_EVENT); + case Onmouseup: + return getListener(exec,DOM::EventImpl::MOUSEUP_EVENT); + case Onmove: + return getListener(exec,DOM::EventImpl::KHTML_MOVE_EVENT); + case Onreset: + return getListener(exec,DOM::EventImpl::RESET_EVENT); + case Onresize: + return getListener(exec,DOM::EventImpl::RESIZE_EVENT); + case Onselect: + return getListener(exec,DOM::EventImpl::SELECT_EVENT); + case Onsubmit: + return getListener(exec,DOM::EventImpl::SUBMIT_EVENT); + case Onunload: + return getListener(exec,DOM::EventImpl::UNLOAD_EVENT); + } + } + + // doing the remainder of ObjectImp::get() that is not covered by + // the getDirect() call above. + // #### guessed position. move further up or down? + Object proto = Object::dynamicCast(prototype()); + assert(proto.isValid()); + if (p == specialPrototypePropertyName) + return isSafeScript(exec) ? Value(proto) : Undefined(); + Value val2 = proto.get(exec, p); + if (!val2.isA(UndefinedType)) { + return isSafeScript(exec) ? val2 : Undefined(); + } + + KParts::ReadOnlyPart *rop = part->findFramePart( p.qstring() ); + if (rop) + return retrieve(rop); + + // allow window[1] or parent[1] etc. (#56983) + bool ok; + unsigned int i = p.toArrayIndex(&ok); + if (ok) { + QPtrList frames = part->frames(); + unsigned int len = frames.count(); + if (i < len) { + KParts::ReadOnlyPart* frame = frames.at(i); + if (frame) + return Window::retrieve(frame); + } + } + + //Check for images, forms, objects, etc. + if (isSafeScript(exec) && part->document().isHTMLDocument()) { // might be XML + DOM::DocumentImpl* docImpl = part->xmlDocImpl(); + DOM::ElementMappingCache::ItemInfo* info = docImpl->underDocNamedCache().get(p.qstring()); + if (info) { + //May be a false positive, but we can try to avoid doing it the hard way in + //simpler cases. The trickiness here is that the cache is kept under both + //name and id, but we sometimes ignore id for IE compat + DOM::DOMString propertyDOMString = p.string(); + if (info->nd && DOM::HTMLMappedNameCollectionImpl::matchesName(info->nd, + DOM::HTMLCollectionImpl::WINDOW_NAMED_ITEMS, propertyDOMString)) { + return getDOMNode(exec, info->nd); + } else { + //Can't tell it just like that, so better go through collection and count stuff. This is the slow path... + DOM::HTMLMappedNameCollection coll(docImpl, DOM::HTMLCollectionImpl::WINDOW_NAMED_ITEMS, propertyDOMString); + + if (coll.length() == 1) + return getDOMNode(exec, coll.firstItem()); + else if (coll.length() > 1) + return getHTMLCollection(exec, coll); + } + } + DOM::Element element = part->document().getElementById(p.string()); + if ( !element.isNull() ) + return getDOMNode(exec, element ); + } + + // This isn't necessarily a bug. Some code uses if(!window.blah) window.blah=1 + // But it can also mean something isn't loaded or implemented, hence the WARNING to help grepping. +#ifdef KJS_VERBOSE + kdDebug(6070) << "WARNING: Window::get property not found: " << p.qstring() << endl; +#endif + return Undefined(); +} + +void Window::put(ExecState* exec, const Identifier &propertyName, const Value &value, int attr) +{ + // we don't want any operations on a closed window + if (m_frame.isNull() || m_frame->m_part.isNull()) { + // ### throw exception? allow setting of some props like location? + return; + } + + // Called by an internal KJS call (e.g. InterpreterImp's constructor) ? + // If yes, save time and jump directly to ObjectImp. + if ( (attr != None && attr != DontDelete) || + // Same thing if we have a local override (e.g. "var location") + ( isSafeScript( exec ) && ObjectImp::getDirect(propertyName) ) ) + { + ObjectImp::put( exec, propertyName, value, attr ); + return; + } + + const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName); + if (entry && !m_frame.isNull() && !m_frame->m_part.isNull()) + { +#ifdef KJS_VERBOSE + kdDebug(6070) << "Window("<value) { + case _Location: + goURL(exec, value.toString(exec).qstring(), false /*don't lock history*/); + return; + default: + break; + } + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (part) { + switch( entry->value ) { + case Status: { + if (isSafeScript(exec) && part->settings()->windowStatusPolicy(part->url().host()) + == KHTMLSettings::KJSWindowStatusAllow) { + String s = value.toString(exec); + part->setJSStatusBarText(s.value().qstring()); + } + return; + } + case DefaultStatus: { + if (isSafeScript(exec) && part->settings()->windowStatusPolicy(part->url().host()) + == KHTMLSettings::KJSWindowStatusAllow) { + String s = value.toString(exec); + part->setJSDefaultStatusBarText(s.value().qstring()); + } + return; + } + case Onabort: + if (isSafeScript(exec)) + setListener(exec, DOM::EventImpl::ABORT_EVENT,value); + return; + case Onblur: + if (isSafeScript(exec)) + setListener(exec, DOM::EventImpl::BLUR_EVENT,value); + return; + case Onchange: + if (isSafeScript(exec)) + setListener(exec, DOM::EventImpl::CHANGE_EVENT,value); + return; + case Onclick: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::KHTML_ECMA_CLICK_EVENT,value); + return; + case Ondblclick: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::KHTML_ECMA_DBLCLICK_EVENT,value); + return; + case Ondragdrop: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::KHTML_DRAGDROP_EVENT,value); + return; + case Onerror: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::ERROR_EVENT,value); + return; + case Onfocus: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::FOCUS_EVENT,value); + return; + case Onkeydown: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::KEYDOWN_EVENT,value); + return; + case Onkeypress: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::KEYPRESS_EVENT,value); + return; + case Onkeyup: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::KEYUP_EVENT,value); + return; + case Onload: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::LOAD_EVENT,value); + return; + case Onmousedown: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::MOUSEDOWN_EVENT,value); + return; + case Onmousemove: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::MOUSEMOVE_EVENT,value); + return; + case Onmouseout: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::MOUSEOUT_EVENT,value); + return; + case Onmouseover: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::MOUSEOVER_EVENT,value); + return; + case Onmouseup: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::MOUSEUP_EVENT,value); + return; + case Onmove: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::KHTML_MOVE_EVENT,value); + return; + case Onreset: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::RESET_EVENT,value); + return; + case Onresize: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::RESIZE_EVENT,value); + return; + case Onselect: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::SELECT_EVENT,value); + return; + case Onsubmit: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::SUBMIT_EVENT,value); + return; + case Onunload: + if (isSafeScript(exec)) + setListener(exec,DOM::EventImpl::UNLOAD_EVENT,value); + return; + case Name: + if (isSafeScript(exec)) + part->setName( value.toString(exec).qstring().local8Bit().data() ); + return; + default: + break; + } + } + } + if (m_frame->m_liveconnect && + isSafeScript(exec) && + m_frame->m_liveconnect->put(0, propertyName.qstring(), value.toString(exec).qstring())) + return; + if (isSafeScript(exec)) { + //kdDebug(6070) << "Window("<m_part.isNull(); +} + +DOM::AbstractView Window::toAbstractView() const +{ + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part) + return DOM::AbstractView(); + return part->document().defaultView(); +} + +void Window::scheduleClose() +{ + kdDebug(6070) << "Window::scheduleClose window.close() " << m_frame << endl; + Q_ASSERT(winq); + QTimer::singleShot( 0, winq, SLOT( timeoutClose() ) ); +} + +void Window::closeNow() +{ + if (m_frame.isNull() || m_frame->m_part.isNull()) { + kdDebug(6070) << k_funcinfo << "part is deleted already" << endl; + } else { + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part) { + kdDebug(6070) << "closeNow on non KHTML part" << endl; + } else { + //kdDebug(6070) << k_funcinfo << " -> closing window" << endl; + // We want to make sure that window.open won't find this part by name. + part->setName( 0 ); + part->deleteLater(); + part = 0; + } + } +} + +void Window::afterScriptExecution() +{ + DOM::DocumentImpl::updateDocumentsRendering(); + QValueList delayedActions = m_delayed; + m_delayed.clear(); + QValueList::Iterator it = delayedActions.begin(); + for ( ; it != delayedActions.end() ; ++it ) + { + switch ((*it).actionId) { + case DelayedClose: + scheduleClose(); + return; // stop here, in case of multiple actions + case DelayedGoHistory: + goHistory( (*it).param.toInt() ); + break; + case NullAction: + // FIXME: anything needs to be done here? This is warning anyways. + break; + }; + } +} + +bool Window::checkIsSafeScript(KParts::ReadOnlyPart *activePart) const +{ + if (m_frame.isNull() || m_frame->m_part.isNull()) { // part deleted ? can't grant access + kdDebug(6070) << "Window::isSafeScript: accessing deleted part !" << endl; + return false; + } + if (!activePart) { + kdDebug(6070) << "Window::isSafeScript: current interpreter's part is 0L!" << endl; + return false; + } + if ( activePart == m_frame->m_part ) // Not calling from another frame, no problem. + return true; + + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part) + return true; // not a KHTMLPart + + if ( part->document().isNull() ) + return true; // allow to access a window that was just created (e.g. with window.open("about:blank")) + + DOM::HTMLDocument thisDocument = part->htmlDocument(); + if ( thisDocument.isNull() ) { + kdDebug(6070) << "Window::isSafeScript: trying to access an XML document !?" << endl; + return false; + } + + KHTMLPart *activeKHTMLPart = ::qt_cast(activePart); + if (!activeKHTMLPart) + return true; // not a KHTMLPart + + DOM::HTMLDocument actDocument = activeKHTMLPart->htmlDocument(); + if ( actDocument.isNull() ) { + kdDebug(6070) << "Window::isSafeScript: active part has no document!" << endl; + return false; + } + DOM::DOMString actDomain = actDocument.domain(); + DOM::DOMString thisDomain = thisDocument.domain(); + + if ( actDomain == thisDomain ) { +#ifdef KJS_VERBOSE + //kdDebug(6070) << "JavaScript: access granted, domain is '" << actDomain.string() << "'" << endl; +#endif + return true; + } + + kdDebug(6070) << "WARNING: JavaScript: access denied for current frame '" << actDomain.string() << "' to frame '" << thisDomain.string() << "'" << endl; + // TODO after 3.1: throw security exception (exec->setException()) + return false; +} + +void Window::setListener(ExecState *exec, int eventId, Value func) +{ + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part || !isSafeScript(exec)) + return; + DOM::DocumentImpl *doc = static_cast(part->htmlDocument().handle()); + if (!doc) + return; + + doc->setHTMLWindowEventListener(eventId,getJSEventListener(func,true)); +} + +Value Window::getListener(ExecState *exec, int eventId) const +{ + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part || !isSafeScript(exec)) + return Undefined(); + DOM::DocumentImpl *doc = static_cast(part->htmlDocument().handle()); + if (!doc) + return Undefined(); + + DOM::EventListener *listener = doc->getHTMLWindowEventListener(eventId); + if (listener && static_cast(listener)->listenerObjImp()) + return static_cast(listener)->listenerObj(); + else + return Null(); +} + + +JSEventListener *Window::getJSEventListener(const Value& val, bool html) +{ + // This function is so hot that it's worth coding it directly with imps. + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part || val.type() != ObjectType) + return 0; + + // It's ObjectType, so it must be valid. + Object listenerObject = Object::dynamicCast(val); + ObjectImp *listenerObjectImp = listenerObject.imp(); + + // 'listener' is not a simple ecma function. (Always use sanity checks: Better safe than sorry!) + if (!listenerObject.implementsCall() && part && part->jScript() && part->jScript()->interpreter()) + { + Interpreter *interpreter = part->jScript()->interpreter(); + + // 'listener' probably is an EventListener object containing a 'handleEvent' function. + Value handleEventValue = listenerObject.get(interpreter->globalExec(), Identifier("handleEvent")); + Object handleEventObject = Object::dynamicCast(handleEventValue); + + if(handleEventObject.isValid() && handleEventObject.implementsCall()) + { + listenerObject = handleEventObject; + listenerObjectImp = handleEventObject.imp(); + } + } + + JSEventListener *existingListener = jsEventListeners[listenerObjectImp]; + if (existingListener) { + if ( existingListener->isHTMLEventListener() != html ) + // The existingListener will have the wrong type, so onclick= will behave like addEventListener or vice versa. + kdWarning() << "getJSEventListener: event listener already found but with html=" << !html << " - please report this, we thought it would never happen" << endl; + return existingListener; + } + + // Note that the JSEventListener constructor adds it to our jsEventListeners list + return new JSEventListener(listenerObject, listenerObjectImp, Object(this), html); +} + +JSLazyEventListener *Window::getJSLazyEventListener(const QString& code, const QString& name, DOM::NodeImpl *node) +{ + return new JSLazyEventListener(code, name, Object(this), node); +} + +void Window::clear( ExecState *exec ) +{ + delete winq; + winq = 0L; + // Get rid of everything, those user vars could hold references to DOM nodes + deleteAllProperties( exec ); + + // Break the dependency between the listeners and their object + QPtrDictIterator it(jsEventListeners); + for (; it.current(); ++it) + it.current()->clear(); + // Forget about the listeners (the DOM::NodeImpls will delete them) + jsEventListeners.clear(); + + if (m_frame) { + KJSProxy* proxy = m_frame->m_jscript; + if (proxy) // i.e. JS not disabled + { + winq = new WindowQObject(this); + // Now recreate a working global object for the next URL that will use us + KJS::Interpreter *interpreter = proxy->interpreter(); + interpreter->initGlobalObject(); + } + } +} + +void Window::setCurrentEvent( DOM::Event *evt ) +{ + m_evt = evt; + //kdDebug(6070) << "Window " << this << " (part=" << m_part << ")::setCurrentEvent m_evt=" << evt << endl; +} + +void Window::goURL(ExecState* exec, const QString& url, bool lockHistory) +{ + Window* active = Window::retrieveActive(exec); + KHTMLPart *part = ::qt_cast(m_frame->m_part); + KHTMLPart *active_part = ::qt_cast(active->part()); + // Complete the URL using the "active part" (running interpreter) + if (active_part && part) { + if (url[0] == QChar('#')) { + part->gotoAnchor(url.mid(1)); + } else { + QString dstUrl = active_part->htmlDocument().completeURL(url).string(); + kdDebug(6070) << "Window::goURL dstUrl=" << dstUrl << endl; + + // check if we're allowed to inject javascript + // SYNC check with khtml_part.cpp::slotRedirect! + if ( isSafeScript(exec) || + dstUrl.find(QString::fromLatin1("javascript:"), 0, false) != 0 ) + part->scheduleRedirection(-1, + dstUrl, + lockHistory); + } + } else if (!part && !m_frame->m_part.isNull()) { + KParts::BrowserExtension *b = KParts::BrowserExtension::childObject(m_frame->m_part); + if (b) + b->emit openURLRequest(m_frame->m_frame->element()->getDocument()->completeURL(url)); + kdDebug() << "goURL for ROPart" << endl; + } +} + +KParts::ReadOnlyPart *Window::part() const { + return m_frame.isNull() ? 0L : static_cast(m_frame->m_part); +} + +void Window::delayedGoHistory( int steps ) +{ + m_delayed.append( DelayedAction( DelayedGoHistory, steps ) ); +} + +void Window::goHistory( int steps ) +{ + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if(!part) + // TODO history readonlypart + return; + KParts::BrowserExtension *ext = part->browserExtension(); + if(!ext) + return; + KParts::BrowserInterface *iface = ext->browserInterface(); + + if ( !iface ) + return; + + iface->callMethod( "goHistory(int)", steps ); + //emit ext->goHistory(steps); +} + +void KJS::Window::resizeTo(QWidget* tl, int width, int height) +{ + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if(!part) + // TODO resizeTo readonlypart + return; + KParts::BrowserExtension *ext = part->browserExtension(); + if (!ext) { + kdDebug(6070) << "Window::resizeTo found no browserExtension" << endl; + return; + } + + // Security check: within desktop limits and bigger than 100x100 (per spec) + if ( width < 100 || height < 100 ) { + kdDebug(6070) << "Window::resizeTo refused, window would be too small ("< sg.width() || height > sg.height() ) { + kdDebug(6070) << "Window::resizeTo refused, window would be too big ("<resizeTopLevelWidget( width, height ); + + // If the window is out of the desktop, move it up/left + // (maybe we should use workarea instead of sg, otherwise the window ends up below kicker) + int right = tl->x() + tl->frameGeometry().width(); + int bottom = tl->y() + tl->frameGeometry().height(); + int moveByX = 0; + int moveByY = 0; + if ( right > sg.right() ) + moveByX = - right + sg.right(); // always <0 + if ( bottom > sg.bottom() ) + moveByY = - bottom + sg.bottom(); // always <0 + if ( moveByX || moveByY ) + emit ext->moveTopLevelWidget( tl->x() + moveByX , tl->y() + moveByY ); +} + +Value Window::openWindow(ExecState *exec, const List& args) +{ + KHTMLPart *part = ::qt_cast(m_frame->m_part); + if (!part) + return Undefined(); + KHTMLView *widget = part->view(); + Value v = args[0]; + QString str; + if (v.isValid() && !v.isA(UndefinedType)) + str = v.toString(exec).qstring(); + + // prepare arguments + KURL url; + if (!str.isEmpty()) + { + KHTMLPart* p = ::qt_cast(Window::retrieveActive(exec)->m_frame->m_part); + if ( p ) + url = p->htmlDocument().completeURL(str).string(); + if ( !p || + !static_cast(p->htmlDocument().handle())->isURLAllowed(url.url()) ) + return Undefined(); + } + + KHTMLSettings::KJSWindowOpenPolicy policy = + part->settings()->windowOpenPolicy(part->url().host()); + if ( policy == KHTMLSettings::KJSWindowOpenAsk ) { + emit part->browserExtension()->requestFocus(part); + QString caption; + if (!part->url().host().isEmpty()) + caption = part->url().host() + " - "; + caption += i18n( "Confirmation: JavaScript Popup" ); + if ( KMessageBox::questionYesNo(widget, + str.isEmpty() ? + i18n( "This site is requesting to open up a new browser " + "window via JavaScript.\n" + "Do you want to allow this?" ) : + i18n( "This site is requesting to open

%1

in a new browser window via JavaScript.
" + "Do you want to allow this?
").arg(KStringHandler::csqueeze(url.htmlURL(), 100)), + caption, i18n("Allow"), i18n("Do Not Allow") ) == KMessageBox::Yes ) + policy = KHTMLSettings::KJSWindowOpenAllow; + } else if ( policy == KHTMLSettings::KJSWindowOpenSmart ) + { + // window.open disabled unless from a key/mouse event + if (static_cast(exec->interpreter())->isWindowOpenAllowed()) + policy = KHTMLSettings::KJSWindowOpenAllow; + } + + QString frameName = args.size() > 1 ? args[1].toString(exec).qstring() : QString("_blank"); + + v = args[2]; + QString features; + if (!v.isNull() && v.type() != UndefinedType && v.toString(exec).size() > 0) { + features = v.toString(exec).qstring(); + // Buggy scripts have ' at beginning and end, cut those + if (features.startsWith("\'") && features.endsWith("\'")) + features = features.mid(1, features.length()-2); + } + + if ( policy != KHTMLSettings::KJSWindowOpenAllow ) { + if ( url.isEmpty() ) + part->setSuppressedPopupIndicator(true, 0); + else { + part->setSuppressedPopupIndicator(true, part); + m_suppressedWindowInfo.append( SuppressedWindowInfo( url, frameName, features ) ); + } + return Undefined(); + } else { + return executeOpenWindow(exec, url, frameName, features); + } +} + +Value Window::executeOpenWindow(ExecState *exec, const KURL& url, const QString& frameName, const QString& features) +{ + KHTMLPart *p = ::qt_cast(m_frame->m_part); + KHTMLView *widget = p->view(); + KParts::WindowArgs winargs; + + // scan feature argument + if (!features.isEmpty()) { + // specifying window params means false defaults + winargs.menuBarVisible = false; + winargs.toolBarsVisible = false; + winargs.statusBarVisible = false; + winargs.scrollBarsVisible = false; + QStringList flist = QStringList::split(',', features); + QStringList::ConstIterator it = flist.begin(); + while (it != flist.end()) { + QString s = *it++; + QString key, val; + int pos = s.find('='); + if (pos >= 0) { + key = s.left(pos).stripWhiteSpace().lower(); + val = s.mid(pos + 1).stripWhiteSpace().lower(); + QRect screen = KGlobalSettings::desktopGeometry(widget->topLevelWidget()); + + if (key == "left" || key == "screenx") { + winargs.x = (int)val.toFloat() + screen.x(); + if (winargs.x < screen.x() || winargs.x > screen.right()) + winargs.x = screen.x(); // only safe choice until size is determined + } else if (key == "top" || key == "screeny") { + winargs.y = (int)val.toFloat() + screen.y(); + if (winargs.y < screen.y() || winargs.y > screen.bottom()) + winargs.y = screen.y(); // only safe choice until size is determined + } else if (key == "height") { + winargs.height = (int)val.toFloat() + 2*qApp->style().pixelMetric( QStyle::PM_DefaultFrameWidth ) + 2; + if (winargs.height > screen.height()) // should actually check workspace + winargs.height = screen.height(); + if (winargs.height < 100) + winargs.height = 100; + } else if (key == "width") { + winargs.width = (int)val.toFloat() + 2*qApp->style().pixelMetric( QStyle::PM_DefaultFrameWidth ) + 2; + if (winargs.width > screen.width()) // should actually check workspace + winargs.width = screen.width(); + if (winargs.width < 100) + winargs.width = 100; + } else { + goto boolargs; + } + continue; + } else { + // leaving away the value gives true + key = s.stripWhiteSpace().lower(); + val = "1"; + } + boolargs: + if (key == "menubar") + winargs.menuBarVisible = (val == "1" || val == "yes"); + else if (key == "toolbar") + winargs.toolBarsVisible = (val == "1" || val == "yes"); + else if (key == "location") // ### missing in WindowArgs + winargs.toolBarsVisible = (val == "1" || val == "yes"); + else if (key == "status" || key == "statusbar") + winargs.statusBarVisible = (val == "1" || val == "yes"); + else if (key == "scrollbars") + winargs.scrollBarsVisible = (val == "1" || val == "yes"); + else if (key == "resizable") + winargs.resizable = (val == "1" || val == "yes"); + else if (key == "fullscreen") + winargs.fullscreen = (val == "1" || val == "yes"); + } + } + + KParts::URLArgs uargs; + uargs.frameName = frameName; + + if ( uargs.frameName.lower() == "_top" ) + { + while ( p->parentPart() ) + p = p->parentPart(); + Window::retrieveWindow(p)->goURL(exec, url.url(), false /*don't lock history*/); + return Window::retrieve(p); + } + if ( uargs.frameName.lower() == "_parent" ) + { + if ( p->parentPart() ) + p = p->parentPart(); + Window::retrieveWindow(p)->goURL(exec, url.url(), false /*don't lock history*/); + return Window::retrieve(p); + } + if ( uargs.frameName.lower() == "_self") + { + Window::retrieveWindow(p)->goURL(exec, url.url(), false /*don't lock history*/); + return Window::retrieve(p); + } + if ( uargs.frameName.lower() == "replace" ) + { + Window::retrieveWindow(p)->goURL(exec, url.url(), true /*lock history*/); + return Window::retrieve(p); + } + uargs.serviceType = "text/html"; + + // request window (new or existing if framename is set) + KParts::ReadOnlyPart *newPart = 0L; + emit p->browserExtension()->createNewWindow(KURL(), uargs,winargs,newPart); + if (newPart && ::qt_cast(newPart)) { + KHTMLPart *khtmlpart = static_cast(newPart); + //qDebug("opener set to %p (this Window's part) in new Window %p (this Window=%p)",part,win,window); + khtmlpart->setOpener(p); + khtmlpart->setOpenedByJS(true); + if (khtmlpart->document().isNull()) { + khtmlpart->begin(); + khtmlpart->write(""); + khtmlpart->end(); + if ( p->docImpl() ) { + //kdDebug(6070) << "Setting domain to " << p->docImpl()->domain().string() << endl; + khtmlpart->docImpl()->setDomain( p->docImpl()->domain()); + khtmlpart->docImpl()->setBaseURL( p->docImpl()->baseURL() ); + } + } + uargs.serviceType = QString::null; + if (uargs.frameName.lower() == "_blank") + uargs.frameName = QString::null; + if (!url.isEmpty()) + emit khtmlpart->browserExtension()->openURLRequest(url,uargs); + return Window::retrieve(khtmlpart); // global object + } else + return Undefined(); +} + +void Window::forgetSuppressedWindows() +{ + m_suppressedWindowInfo.clear(); +} + +void Window::showSuppressedWindows() +{ + KHTMLPart *part = ::qt_cast( m_frame->m_part ); + KJS::Interpreter *interpreter = part->jScript()->interpreter(); + ExecState *exec = interpreter->globalExec(); + + QValueList suppressedWindowInfo = m_suppressedWindowInfo; + m_suppressedWindowInfo.clear(); + QValueList::Iterator it = suppressedWindowInfo.begin(); + for ( ; it != suppressedWindowInfo.end() ; ++it ) { + executeOpenWindow(exec, (*it).url, (*it).frameName, (*it).features); + } +} + +Value WindowFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + KJS_CHECK_THIS( Window, thisObj ); + + // these should work no matter whether the window is already + // closed or not + if (id == Window::ValueOf || id == Window::ToString) { + return String("[object Window]"); + } + + Window *window = static_cast(thisObj.imp()); + QString str, str2; + + KHTMLPart *part = ::qt_cast(window->m_frame->m_part); + if (!part) + return Undefined(); + + KHTMLView *widget = part->view(); + Value v = args[0]; + UString s; + if (v.isValid() && !v.isA(UndefinedType)) { + s = v.toString(exec); + str = s.qstring(); + } + + QString caption; + if (part && !part->url().host().isEmpty()) + caption = part->url().host() + " - "; + caption += "JavaScript"; // TODO: i18n + // functions that work everywhere + switch(id) { + case Window::Alert: + if (!widget->dialogsAllowed()) + return Undefined(); + if ( part && part->xmlDocImpl() ) + part->xmlDocImpl()->updateRendering(); + if ( part ) + emit part->browserExtension()->requestFocus(part); + KMessageBox::error(widget, QStyleSheet::convertFromPlainText(str, QStyleSheetItem::WhiteSpaceNormal), caption); + return Undefined(); + case Window::Confirm: + if (!widget->dialogsAllowed()) + return Undefined(); + if ( part && part->xmlDocImpl() ) + part->xmlDocImpl()->updateRendering(); + if ( part ) + emit part->browserExtension()->requestFocus(part); + return Boolean((KMessageBox::warningYesNo(widget, QStyleSheet::convertFromPlainText(str), caption, + KStdGuiItem::ok(), KStdGuiItem::cancel()) == KMessageBox::Yes)); + case Window::Prompt: +#ifndef KONQ_EMBEDDED + if (!widget->dialogsAllowed()) + return Undefined(); + if ( part && part->xmlDocImpl() ) + part->xmlDocImpl()->updateRendering(); + if ( part ) + emit part->browserExtension()->requestFocus(part); + bool ok; + if (args.size() >= 2) + str2 = KInputDialog::getText(caption, + QStyleSheet::convertFromPlainText(str), + args[1].toString(exec).qstring(), &ok, widget); + else + str2 = KInputDialog::getText(caption, + QStyleSheet::convertFromPlainText(str), + QString::null, &ok, widget); + if ( ok ) + return String(str2); + else + return Null(); +#else + return Undefined(); +#endif + case Window::GetComputedStyle: { + if ( !part || !part->xmlDocImpl() ) + return Undefined(); + DOM::Node arg0 = toNode(args[0]); + if (arg0.nodeType() != DOM::Node::ELEMENT_NODE) + return Undefined(); // throw exception? + else + return getDOMCSSStyleDeclaration(exec, part->document().defaultView().getComputedStyle(static_cast(arg0), + args[1].toString(exec).string())); + } + case Window::Open: + return window->openWindow(exec, args); + case Window::Close: { + /* From http://developer.netscape.com/docs/manuals/js/client/jsref/window.htm : + The close method closes only windows opened by JavaScript using the open method. + If you attempt to close any other window, a confirm is generated, which + lets the user choose whether the window closes. + This is a security feature to prevent "mail bombs" containing self.close(). + However, if the window has only one document (the current one) in its + session history, the close is allowed without any confirm. This is a + special case for one-off windows that need to open other windows and + then dispose of themselves. + */ + bool doClose = false; + if (!part->openedByJS()) + { + // To conform to the SPEC, we only ask if the window + // has more than one entry in the history (NS does that too). + History history(exec,part); + + if ( history.get( exec, "length" ).toInt32(exec) <= 1 ) + { + doClose = true; + } + else + { + // Can we get this dialog with tabs??? Does it close the window or the tab in that case? + emit part->browserExtension()->requestFocus(part); + if ( KMessageBox::questionYesNo( window->part()->widget(), + i18n("Close window?"), i18n("Confirmation Required"), + KStdGuiItem::close(), KStdGuiItem::cancel() ) + == KMessageBox::Yes ) + doClose = true; + } + } + else + doClose = true; + + if (doClose) + { + // If this is the current window (the one the interpreter runs in), + // then schedule a delayed close (so that the script terminates first). + // But otherwise, close immediately. This fixes w=window.open("","name");w.close();window.open("name"); + if ( Window::retrieveActive(exec) == window ) { + if (widget) { + // quit all dialogs of this view + // this fixes 'setTimeout('self.close()',1000); alert("Hi");' crash + widget->closeChildDialogs(); + } + //kdDebug() << "scheduling delayed close" << endl; + // We'll close the window at the end of the script execution + Window* w = const_cast(window); + w->m_delayed.append( Window::DelayedAction( Window::DelayedClose ) ); + } else { + //kdDebug() << "closing NOW" << endl; + (const_cast(window))->closeNow(); + } + } + return Undefined(); + } + case Window::Navigate: + window->goURL(exec, args[0].toString(exec).qstring(), false /*don't lock history*/); + return Undefined(); + case Window::Focus: { + KHTMLSettings::KJSWindowFocusPolicy policy = + part->settings()->windowFocusPolicy(part->url().host()); + if(policy == KHTMLSettings::KJSWindowFocusAllow && widget) { + widget->topLevelWidget()->raise(); + KWin::deIconifyWindow( widget->topLevelWidget()->winId() ); + widget->setActiveWindow(); + emit part->browserExtension()->requestFocus(part); + } + return Undefined(); + } + case Window::Blur: + // TODO + return Undefined(); + case Window::BToA: + case Window::AToB: { + if (!s.is8Bit()) + return Undefined(); + QByteArray in, out; + char *binData = s.ascii(); + in.setRawData( binData, s.size() ); + if (id == Window::AToB) + KCodecs::base64Decode( in, out ); + else + KCodecs::base64Encode( in, out ); + in.resetRawData( binData, s.size() ); + UChar *d = new UChar[out.size()]; + for (uint i = 0; i < out.size(); i++) + d[i].uc = (uchar) out[i]; + UString ret(d, out.size(), false /*no copy*/); + return String(ret); + } + + }; + + + // now unsafe functions.. + if (!window->isSafeScript(exec)) + return Undefined(); + + switch (id) { + case Window::ScrollBy: + if(args.size() == 2 && widget) + widget->scrollBy(args[0].toInt32(exec), args[1].toInt32(exec)); + return Undefined(); + case Window::Scroll: + case Window::ScrollTo: + if(args.size() == 2 && widget) + widget->setContentsPos(args[0].toInt32(exec), args[1].toInt32(exec)); + return Undefined(); + case Window::MoveBy: { + KHTMLSettings::KJSWindowMovePolicy policy = + part->settings()->windowMovePolicy(part->url().host()); + if(policy == KHTMLSettings::KJSWindowMoveAllow && args.size() == 2 && widget) + { + KParts::BrowserExtension *ext = part->browserExtension(); + if (ext) { + QWidget * tl = widget->topLevelWidget(); + QRect sg = KGlobalSettings::desktopGeometry(tl); + + QPoint dest = tl->pos() + QPoint( args[0].toInt32(exec), args[1].toInt32(exec) ); + // Security check (the spec talks about UniversalBrowserWrite to disable this check...) + if ( dest.x() >= sg.x() && dest.y() >= sg.x() && + dest.x()+tl->width() <= sg.width()+sg.x() && + dest.y()+tl->height() <= sg.height()+sg.y() ) + emit ext->moveTopLevelWidget( dest.x(), dest.y() ); + } + } + return Undefined(); + } + case Window::MoveTo: { + KHTMLSettings::KJSWindowMovePolicy policy = + part->settings()->windowMovePolicy(part->url().host()); + if(policy == KHTMLSettings::KJSWindowMoveAllow && args.size() == 2 && widget) + { + KParts::BrowserExtension *ext = part->browserExtension(); + if (ext) { + QWidget * tl = widget->topLevelWidget(); + QRect sg = KGlobalSettings::desktopGeometry(tl); + + QPoint dest( args[0].toInt32(exec)+sg.x(), args[1].toInt32(exec)+sg.y() ); + // Security check (the spec talks about UniversalBrowserWrite to disable this check...) + if ( dest.x() >= sg.x() && dest.y() >= sg.y() && + dest.x()+tl->width() <= sg.width()+sg.x() && + dest.y()+tl->height() <= sg.height()+sg.y() ) + emit ext->moveTopLevelWidget( dest.x(), dest.y() ); + } + } + return Undefined(); + } + case Window::ResizeBy: { + KHTMLSettings::KJSWindowResizePolicy policy = + part->settings()->windowResizePolicy(part->url().host()); + if(policy == KHTMLSettings::KJSWindowResizeAllow + && args.size() == 2 && widget) + { + QWidget * tl = widget->topLevelWidget(); + QRect geom = tl->frameGeometry(); + window->resizeTo( tl, + geom.width() + args[0].toInt32(exec), + geom.height() + args[1].toInt32(exec) ); + } + return Undefined(); + } + case Window::ResizeTo: { + KHTMLSettings::KJSWindowResizePolicy policy = + part->settings()->windowResizePolicy(part->url().host()); + if(policy == KHTMLSettings::KJSWindowResizeAllow + && args.size() == 2 && widget) + { + QWidget * tl = widget->topLevelWidget(); + window->resizeTo( tl, args[0].toInt32(exec), args[1].toInt32(exec) ); + } + return Undefined(); + } + case Window::SetTimeout: + case Window::SetInterval: { + bool singleShot; + int i; // timeout interval + if (args.size() == 0) + return Undefined(); + if (args.size() > 1) { + singleShot = (id == Window::SetTimeout); + i = args[1].toInt32(exec); + } else { + // second parameter is missing. Emulate Mozilla behavior. + singleShot = true; + i = 4; + } + if (v.isA(StringType)) { + int r = (const_cast(window))->winq->installTimeout(Identifier(s), i, singleShot ); + return Number(r); + } + else if (v.isA(ObjectType) && Object::dynamicCast(v).implementsCall()) { + Object func = Object::dynamicCast(v); + List funcArgs; + ListIterator it = args.begin(); + int argno = 0; + while (it != args.end()) { + Value arg = it++; + if (argno++ >= 2) + funcArgs.append(arg); + } + if (args.size() < 2) + funcArgs.append(Number(i)); + int r = (const_cast(window))->winq->installTimeout(func, funcArgs, i, singleShot ); + return Number(r); + } + else + return Undefined(); + } + case Window::ClearTimeout: + case Window::ClearInterval: + (const_cast(window))->winq->clearTimeout(v.toInt32(exec)); + return Undefined(); + case Window::Print: + if ( widget ) { + // ### TODO emit onbeforeprint event + widget->print(); + // ### TODO emit onafterprint event + } + case Window::CaptureEvents: + case Window::ReleaseEvents: + // Do nothing for now. These are NS-specific legacy calls. + break; + case Window::AddEventListener: { + JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]); + if (listener) { + DOM::DocumentImpl* docimpl = static_cast(part->document().handle()); + docimpl->addWindowEventListener(DOM::EventImpl::typeToId(args[0].toString(exec).string()),listener,args[2].toBoolean(exec)); + } + return Undefined(); + } + case Window::RemoveEventListener: { + JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]); + if (listener) { + DOM::DocumentImpl* docimpl = static_cast(part->document().handle()); + docimpl->removeWindowEventListener(DOM::EventImpl::typeToId(args[0].toString(exec).string()),listener,args[2].toBoolean(exec)); + } + return Undefined(); + } + + } + return Undefined(); +} + +////////////////////// ScheduledAction //////////////////////// + +// KDE 4: Make those parameters const ... & +ScheduledAction::ScheduledAction(Object _func, List _args, DateTimeMS _nextTime, int _interval, bool _singleShot, + int _timerId) +{ + //kdDebug(6070) << "ScheduledAction::ScheduledAction(isFunction) " << this << endl; + func = static_cast(_func.imp()); + args = _args; + isFunction = true; + singleShot = _singleShot; + nextTime = _nextTime; + interval = _interval; + executing = false; + timerId = _timerId; +} + +// KDE 4: Make it const QString & +ScheduledAction::ScheduledAction(QString _code, DateTimeMS _nextTime, int _interval, bool _singleShot, int _timerId) +{ + //kdDebug(6070) << "ScheduledAction::ScheduledAction(!isFunction) " << this << endl; + //func = 0; + //args = 0; + func = 0; + code = _code; + isFunction = false; + singleShot = _singleShot; + nextTime = _nextTime; + interval = _interval; + executing = false; + timerId = _timerId; +} + +bool ScheduledAction::execute(Window *window) +{ + KHTMLPart *part = ::qt_cast(window->m_frame->m_part); + if (!part || !part->jScriptEnabled()) + return false; + ScriptInterpreter *interpreter = static_cast(part->jScript()->interpreter()); + + interpreter->setProcessingTimerCallback(true); + + //kdDebug(6070) << "ScheduledAction::execute " << this << endl; + if (isFunction) { + if (func->implementsCall()) { + // #### check this + Q_ASSERT( part ); + if ( part ) + { + KJS::Interpreter *interpreter = part->jScript()->interpreter(); + ExecState *exec = interpreter->globalExec(); + Q_ASSERT( window == interpreter->globalObject().imp() ); + Object obj( window ); + func->call(exec,obj,args); // note that call() creates its own execution state for the func call + if (exec->hadException()) + exec->clearException(); + + // Update our document's rendering following the execution of the timeout callback. + part->document().updateRendering(); + } + } + } + else { + part->executeScript(DOM::Node(), code); + } + + interpreter->setProcessingTimerCallback(false); + return true; +} + +void ScheduledAction::mark() +{ + if (func && !func->marked()) + func->mark(); + args.mark(); +} + +ScheduledAction::~ScheduledAction() +{ + //kdDebug(6070) << "ScheduledAction::~ScheduledAction " << this << endl; +} + +////////////////////// WindowQObject //////////////////////// + +WindowQObject::WindowQObject(Window *w) + : parent(w) +{ + //kdDebug(6070) << "WindowQObject::WindowQObject " << this << endl; + if ( !parent->m_frame ) + kdDebug(6070) << "WARNING: null part in " << k_funcinfo << endl; + else + connect( parent->m_frame, SIGNAL( destroyed() ), + this, SLOT( parentDestroyed() ) ); + pausedTime = 0; + lastTimerId = 0; + currentlyDispatching = false; +} + +WindowQObject::~WindowQObject() +{ + //kdDebug(6070) << "WindowQObject::~WindowQObject " << this << endl; + parentDestroyed(); // reuse same code +} + +void WindowQObject::parentDestroyed() +{ + killTimers(); + + QPtrListIterator it(scheduledActions); + for (; it.current(); ++it) + delete it.current(); + scheduledActions.clear(); +} + +int WindowQObject::installTimeout(const Identifier &handler, int t, bool singleShot) +{ + int id = ++lastTimerId; + if (t < 10) t = 10; + DateTimeMS nextTime = DateTimeMS::now().addMSecs(-pausedTime + t); + + ScheduledAction *action = new ScheduledAction(handler.qstring(),nextTime,t,singleShot,id); + scheduledActions.append(action); + setNextTimer(); + return id; +} + +int WindowQObject::installTimeout(const Value &func, List args, int t, bool singleShot) +{ + Object objFunc = Object::dynamicCast( func ); + if (!objFunc.isValid()) + return 0; + int id = ++lastTimerId; + if (t < 10) t = 10; + + DateTimeMS nextTime = DateTimeMS::now().addMSecs(-pausedTime + t); + ScheduledAction *action = new ScheduledAction(objFunc,args,nextTime,t,singleShot,id); + scheduledActions.append(action); + setNextTimer(); + return id; +} + +void WindowQObject::clearTimeout(int timerId) +{ + QPtrListIterator it(scheduledActions); + for (; it.current(); ++it) { + ScheduledAction *action = it.current(); + if (action->timerId == timerId) { + scheduledActions.removeRef(action); + if (!action->executing) + delete action; + return; + } + } +} + +bool WindowQObject::hasTimers() const +{ + return scheduledActions.count(); +} + +void WindowQObject::mark() +{ + QPtrListIterator it(scheduledActions); + for (; it.current(); ++it) + it.current()->mark(); +} + +void WindowQObject::timerEvent(QTimerEvent *) +{ + killTimers(); + + if (scheduledActions.isEmpty()) + return; + + currentlyDispatching = true; + + + DateTimeMS currentActual = DateTimeMS::now(); + DateTimeMS currentAdjusted = currentActual.addMSecs(-pausedTime); + + // Work out which actions are to be executed. We take a separate copy of + // this list since the main one may be modified during action execution + QPtrList toExecute; + QPtrListIterator it(scheduledActions); + for (; it.current(); ++it) + if (currentAdjusted >= it.current()->nextTime) + toExecute.append(it.current()); + + // ### verify that the window can't be closed (and action deleted) during execution + it = QPtrListIterator(toExecute); + for (; it.current(); ++it) { + ScheduledAction *action = it.current(); + if (!scheduledActions.containsRef(action)) // removed by clearTimeout() + continue; + + action->executing = true; // prevent deletion in clearTimeout() + + if (parent->part()) { + bool ok = action->execute(parent); + if ( !ok ) // e.g. JS disabled + scheduledActions.removeRef( action ); + } + + if (action->singleShot) { + scheduledActions.removeRef(action); + } + + action->executing = false; + + if (!scheduledActions.containsRef(action)) + delete action; + else + action->nextTime = action->nextTime.addMSecs(action->interval); + } + + pausedTime += currentActual.msecsTo(DateTimeMS::now()); + + currentlyDispatching = false; + + // Work out when next event is to occur + setNextTimer(); +} + +DateTimeMS DateTimeMS::addMSecs(int s) const +{ + DateTimeMS c = *this; + c.mTime = mTime.addMSecs(s); + if (s > 0) + { + if (c.mTime < mTime) + c.mDate = mDate.addDays(1); + } + else + { + if (c.mTime > mTime) + c.mDate = mDate.addDays(-1); + } + return c; +} + +bool DateTimeMS::operator >(const DateTimeMS &other) const +{ + if (mDate > other.mDate) + return true; + + if (mDate < other.mDate) + return false; + + return mTime > other.mTime; +} + +bool DateTimeMS::operator >=(const DateTimeMS &other) const +{ + if (mDate > other.mDate) + return true; + + if (mDate < other.mDate) + return false; + + return mTime >= other.mTime; +} + +int DateTimeMS::msecsTo(const DateTimeMS &other) const +{ + int d = mDate.daysTo(other.mDate); + int ms = mTime.msecsTo(other.mTime); + return d*24*60*60*1000 + ms; +} + + +DateTimeMS DateTimeMS::now() +{ + DateTimeMS t; + QTime before = QTime::currentTime(); + t.mDate = QDate::currentDate(); + t.mTime = QTime::currentTime(); + if (t.mTime < before) + t.mDate = QDate::currentDate(); // prevent race condition in hacky way :) + return t; +} + +void WindowQObject::setNextTimer() +{ + if (currentlyDispatching) + return; // Will schedule at the end + + if (scheduledActions.isEmpty()) + return; + + QPtrListIterator it(scheduledActions); + DateTimeMS nextTime = it.current()->nextTime; + for (++it; it.current(); ++it) + if (nextTime > it.current()->nextTime) + nextTime = it.current()->nextTime; + + DateTimeMS nextTimeActual = nextTime.addMSecs(pausedTime); + int nextInterval = DateTimeMS::now().msecsTo(nextTimeActual); + if (nextInterval < 0) + nextInterval = 0; + startTimer(nextInterval); +} + +void WindowQObject::timeoutClose() +{ + parent->closeNow(); +} + +Value FrameArray::get(ExecState *exec, const Identifier &p) const +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "FrameArray::get " << p.qstring() << " part=" << (void*)part << endl; +#endif + if (part.isNull()) + return Undefined(); + + QPtrList frames = part->frames(); + unsigned int len = frames.count(); + if (p == lengthPropertyName) + return Number(len); + else if (p== "location") // non-standard property, but works in NS and IE + { + Object obj = Object::dynamicCast( Window::retrieve( part ) ); + if ( obj.isValid() ) + return obj.get( exec, "location" ); + return Undefined(); + } + + // check for the name or number + KParts::ReadOnlyPart *frame = part->findFramePart(p.qstring()); + if (!frame) { + bool ok; + unsigned int i = p.toArrayIndex(&ok); + if (ok && i < len) + frame = frames.at(i); + } + + // we are potentially fetching a reference to a another Window object here. + // i.e. we may be accessing objects from another interpreter instance. + // Therefore we have to be a bit careful with memory management. + if (frame) { + return Window::retrieve(frame); + } + + // Fun IE quirk: name lookup in there is actually done by document.all + // hence, it can find non-frame things (and even let them hide frame ones!) + // We don't quite do that, but do this as a fallback. + DOM::DocumentImpl* doc = static_cast(part->document().handle()); + if (doc) { + DOM::HTMLCollectionImpl docuAll(doc, DOM::HTMLCollectionImpl::DOC_ALL); + DOM::NodeImpl* node = docuAll.namedItem(p.string()); + if (node) { + if (node->id() == ID_FRAME || node->id() == ID_IFRAME) { + //Return the Window object. + KHTMLPart* part = static_cast(node)->contentPart(); + if (part) + return Value(Window::retrieveWindow(part)); + else + return Undefined(); + } else { + //Just a regular node.. + return getDOMNode(exec, node); + } + } + } else { + kdWarning(6070) << "Missing own document in FrameArray::get()" << endl; + } + + return ObjectImp::get(exec, p); +} + +Value FrameArray::call(ExecState *exec, Object &/*thisObj*/, const List &args) +{ + //IE supports a subset of the get functionality as call... + //... basically, when the return is a window, it supports that, otherwise it + //errors out. We do a cheap-and-easy emulation of that, and just do the same + //thing as get does. + if (args.size() == 1) + return get(exec, Identifier(args[0].toString(exec))); + + return Undefined(); +} + + +////////////////////// Location Object //////////////////////// + +const ClassInfo Location::info = { "Location", 0, &LocationTable, 0 }; +/* +@begin LocationTable 11 + hash Location::Hash DontDelete + host Location::Host DontDelete + hostname Location::Hostname DontDelete + href Location::Href DontDelete + pathname Location::Pathname DontDelete + port Location::Port DontDelete + protocol Location::Protocol DontDelete + search Location::Search DontDelete + [[==]] Location::EqualEqual DontDelete|ReadOnly + assign Location::Assign DontDelete|Function 1 + toString Location::ToString DontDelete|Function 0 + replace Location::Replace DontDelete|Function 1 + reload Location::Reload DontDelete|Function 0 +@end +*/ +IMPLEMENT_PROTOFUNC_DOM(LocationFunc) +Location::Location(khtml::ChildFrame *f) : m_frame(f) +{ + //kdDebug(6070) << "Location::Location " << this << " m_part=" << (void*)m_part << endl; +} + +Location::~Location() +{ + //kdDebug(6070) << "Location::~Location " << this << " m_part=" << (void*)m_part << endl; +} + +KParts::ReadOnlyPart *Location::part() const { + return m_frame ? static_cast(m_frame->m_part) : 0L; +} + +Value Location::get(ExecState *exec, const Identifier &p) const +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "Location::get " << p.qstring() << " m_part=" << (void*)m_frame->m_part << endl; +#endif + + if (m_frame.isNull() || m_frame->m_part.isNull()) + return Undefined(); + + const HashEntry *entry = Lookup::findEntry(&LocationTable, p); + + // properties that work on all Location objects + if ( entry && entry->value == Replace ) + return lookupOrCreateFunction(exec,p,this,entry->value,entry->params,entry->attr); + + // XSS check + const Window* window = Window::retrieveWindow( m_frame->m_part ); + if ( !window || !window->isSafeScript(exec) ) + return Undefined(); + + KURL url = m_frame->m_part->url(); + if (entry) + switch (entry->value) { + case Hash: + return String( url.ref().isNull() ? QString("") : "#" + url.ref() ); + case Host: { + UString str = url.host(); + if (url.port()) + str += ":" + QString::number((int)url.port()); + return String(str); + // Note: this is the IE spec. The NS spec swaps the two, it says + // "The hostname property is the concatenation of the host and port properties, separated by a colon." + // Bleh. + } + case Hostname: + return String( url.host() ); + case Href: + if (url.isEmpty()) + return String("about:blank"); + else if (!url.hasPath()) + return String( url.prettyURL()+"/" ); + else + return String( url.prettyURL() ); + case Pathname: + if (url.isEmpty()) + return String(""); + return String( url.path().isEmpty() ? QString("/") : url.path() ); + case Port: + return String( url.port() ? QString::number((int)url.port()) : QString::fromLatin1("") ); + case Protocol: + return String( url.protocol()+":" ); + case Search: + return String( url.query() ); + case EqualEqual: // [[==]] + return String(toString(exec)); + case ToString: + return lookupOrCreateFunction(exec,p,this,entry->value,entry->params,entry->attr); + } + // Look for overrides + ValueImp * val = ObjectImp::getDirect(p); + if (val) + return Value(val); + if (entry && (entry->attr & Function)) + return lookupOrCreateFunction(exec,p,this,entry->value,entry->params,entry->attr); + + return Undefined(); +} + +void Location::put(ExecState *exec, const Identifier &p, const Value &v, int attr) +{ +#ifdef KJS_VERBOSE + kdDebug(6070) << "Location::put " << p.qstring() << " m_part=" << (void*)m_frame->m_part << endl; +#endif + if (m_frame.isNull() || m_frame->m_part.isNull()) + return; + + const Window* window = Window::retrieveWindow( m_frame->m_part ); + if ( !window ) + return; + + KURL url = m_frame->m_part->url(); + + const HashEntry *entry = Lookup::findEntry(&LocationTable, p); + + if (entry) { + + // XSS check. Only new hrefs can be set from other sites + if (entry->value != Href && !window->isSafeScript(exec)) + return; + + QString str = v.toString(exec).qstring(); + switch (entry->value) { + case Href: { + KHTMLPart* p =::qt_cast(Window::retrieveActive(exec)->part()); + if ( p ) + url = p->htmlDocument().completeURL( str ).string(); + else + url = str; + break; + } + case Hash: + // when the hash is already the same ignore it + if (str == url.ref()) return; + url.setRef(str); + break; + case Host: { + QString host = str.left(str.find(":")); + QString port = str.mid(str.find(":")+1); + url.setHost(host); + url.setPort(port.toUInt()); + break; + } + case Hostname: + url.setHost(str); + break; + case Pathname: + url.setPath(str); + break; + case Port: + url.setPort(str.toUInt()); + break; + case Protocol: + url.setProtocol(str); + break; + case Search: + url.setQuery(str); + break; + } + } else { + ObjectImp::put(exec, p, v, attr); + return; + } + + Window::retrieveWindow(m_frame->m_part)->goURL(exec, url.url(), false /* don't lock history*/ ); +} + +Value Location::toPrimitive(ExecState *exec, Type) const +{ + if (m_frame) { + Window* window = Window::retrieveWindow( m_frame->m_part ); + if ( window && window->isSafeScript(exec) ) + return String(toString(exec)); + } + return Undefined(); +} + +UString Location::toString(ExecState *exec) const +{ + if (m_frame) { + Window* window = Window::retrieveWindow( m_frame->m_part ); + if ( window && window->isSafeScript(exec) ) + { + KURL url = m_frame->m_part->url(); + if (url.isEmpty()) + return "about:blank"; + else if (!url.hasPath()) + return url.prettyURL()+"/"; + else + return url.prettyURL(); + } + } + return ""; +} + +Value LocationFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + KJS_CHECK_THIS( Location, thisObj ); + Location *location = static_cast(thisObj.imp()); + KParts::ReadOnlyPart *part = location->part(); + + if (!part) return Undefined(); + + Window* window = Window::retrieveWindow(part); + + if ( !window->isSafeScript(exec) && id != Location::Replace) + return Undefined(); + + switch (id) { + case Location::Assign: + case Location::Replace: + Window::retrieveWindow(part)->goURL(exec, args[0].toString(exec).qstring(), + id == Location::Replace); + break; + case Location::Reload: { + KHTMLPart *khtmlpart = ::qt_cast(part); + if (khtmlpart) + khtmlpart->scheduleRedirection(-1, part->url().url(), true/*lock history*/); + else + part->openURL(part->url()); + break; + } + case Location::ToString: + return String(location->toString(exec)); + } + return Undefined(); +} + +////////////////////// External Object //////////////////////// + +const ClassInfo External::info = { "External", 0, 0, 0 }; +/* +@begin ExternalTable 4 + addFavorite External::AddFavorite DontDelete|Function 1 +@end +*/ +IMPLEMENT_PROTOFUNC_DOM(ExternalFunc) + +Value External::get(ExecState *exec, const Identifier &p) const +{ + return lookupGetFunction(exec,p,&ExternalTable,this); +} + +Value ExternalFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + KJS_CHECK_THIS( External, thisObj ); + External *external = static_cast(thisObj.imp()); + + KHTMLPart *part = external->part; + if (!part) + return Undefined(); + + KHTMLView *widget = part->view(); + + switch (id) { + case External::AddFavorite: + { +#ifndef KONQ_EMBEDDED + if (!widget->dialogsAllowed()) + return Undefined(); + part->xmlDocImpl()->updateRendering(); + if (args.size() != 1 && args.size() != 2) + return Undefined(); + + QString url = args[0].toString(exec).qstring(); + QString title; + if (args.size() == 2) + title = args[1].toString(exec).qstring(); + + // AK - don't do anything yet, for the moment i + // just wanted the base js handling code in cvs + return Undefined(); + + QString question; + if ( title.isEmpty() ) + question = i18n("Do you want a bookmark pointing to the location \"%1\" to be added to your collection?") + .arg(url); + else + question = i18n("Do you want a bookmark pointing to the location \"%1\" titled \"%2\" to be added to your collection?") + .arg(url).arg(title); + + emit part->browserExtension()->requestFocus(part); + + QString caption; + if (!part->url().host().isEmpty()) + caption = part->url().host() + " - "; + caption += i18n("JavaScript Attempted Bookmark Insert"); + + if (KMessageBox::warningYesNo( + widget, question, caption, + i18n("Insert"), i18n("Disallow")) == KMessageBox::Yes) + { + KBookmarkManager *mgr = KBookmarkManager::userBookmarksManager(); + mgr->addBookmarkDialog(url,title); + } +#else + return Undefined(); +#endif + break; + } + default: + return Undefined(); + } + + return Undefined(); +} + +////////////////////// History Object //////////////////////// + +const ClassInfo History::info = { "History", 0, 0, 0 }; +/* +@begin HistoryTable 4 + length History::Length DontDelete|ReadOnly + back History::Back DontDelete|Function 0 + forward History::Forward DontDelete|Function 0 + go History::Go DontDelete|Function 1 +@end +*/ +IMPLEMENT_PROTOFUNC_DOM(HistoryFunc) + +Value History::get(ExecState *exec, const Identifier &p) const +{ + return lookupGet(exec,p,&HistoryTable,this); +} + +Value History::getValueProperty(ExecState *, int token) const +{ + // if previous or next is implemented, make sure its not a major + // privacy leak (see i.e. http://security.greymagic.com/adv/gm005-op/) + switch (token) { + case Length: + { + if ( !part ) + return Number( 0 ); + + KParts::BrowserExtension *ext = part->browserExtension(); + if ( !ext ) + return Number( 0 ); + + KParts::BrowserInterface *iface = ext->browserInterface(); + if ( !iface ) + return Number( 0 ); + + QVariant length = iface->property( "historyLength" ); + + if ( length.type() != QVariant::UInt ) + return Number( 0 ); + + return Number( length.toUInt() ); + } + default: + kdDebug(6070) << "WARNING: Unhandled token in History::getValueProperty : " << token << endl; + return Undefined(); + } +} + +Value HistoryFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + KJS_CHECK_THIS( History, thisObj ); + History *history = static_cast(thisObj.imp()); + + Value v = args[0]; + Number n; + if(v.isValid()) + n = v.toInteger(exec); + + int steps; + switch (id) { + case History::Back: + steps = -1; + break; + case History::Forward: + steps = 1; + break; + case History::Go: + steps = n.intValue(); + break; + default: + return Undefined(); + } + + // Special case for go(0) from a frame -> reload only the frame + // go(i!=0) from a frame navigates into the history of the frame only, + // in both IE and NS (but not in Mozilla).... we can't easily do that + // in Konqueror... + if (!steps) // add && history->part->parentPart() to get only frames, but doesn't matter + { + history->part->openURL( history->part->url() ); /// ## need args.reload=true? + } else + { + // Delay it. + // Testcase: history.back(); alert("hello"); + Window* window = Window::retrieveWindow( history->part ); + window->delayedGoHistory( steps ); + } + return Undefined(); +} + +///////////////////////////////////////////////////////////////////////////// + +#ifdef Q_WS_QWS + +const ClassInfo Konqueror::info = { "Konqueror", 0, 0, 0 }; + +bool Konqueror::hasProperty(ExecState *exec, const Identifier &p) const +{ + if ( p.qstring().startsWith( "goHistory" ) ) return false; + + return true; +} + +Value Konqueror::get(ExecState *exec, const Identifier &p) const +{ + if ( p == "goHistory" || part->url().protocol() != "http" || part->url().host() != "localhost" ) + return Undefined(); + + KParts::BrowserExtension *ext = part->browserExtension(); + if ( ext ) { + KParts::BrowserInterface *iface = ext->browserInterface(); + if ( iface ) { + QVariant prop = iface->property( p.qstring().latin1() ); + + if ( prop.isValid() ) { + switch( prop.type() ) { + case QVariant::Int: + return Number( prop.toInt() ); + case QVariant::String: + return String( prop.toString() ); + default: + break; + } + } + } + } + + return Value( new KonquerorFunc(exec, this, p.qstring().latin1() ) ); +} + +Value KonquerorFunc::tryCall(ExecState *exec, Object &, const List &args) +{ + KParts::BrowserExtension *ext = konqueror->part->browserExtension(); + + if (!ext) + return Undefined(); + + KParts::BrowserInterface *iface = ext->browserInterface(); + + if ( !iface ) + return Undefined(); + + QCString n = m_name.data(); + n += "()"; + iface->callMethod( n.data(), QVariant() ); + + return Undefined(); +} + +UString Konqueror::toString(ExecState *) const +{ + return UString("[object Konqueror]"); +} + +#endif +///////////////////////////////////////////////////////////////////////////// +} //namespace KJS + +#include "kjs_window.moc" diff --git a/khtml/ecma/kjs_window.h b/khtml/ecma/kjs_window.h new file mode 100644 index 000000000..1444110f4 --- /dev/null +++ b/khtml/ecma/kjs_window.h @@ -0,0 +1,309 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 2000 Harri Porten (porten@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 + */ + +#ifndef _KJS_WINDOW_H_ +#define _KJS_WINDOW_H_ + +#include +#include +#include +#include +#include + +#include "kjs_binding.h" +#include "kjs_views.h" + +class QTimer; +class KHTMLView; +class KHTMLPart; + +namespace KParts { + class ReadOnlyPart; +} + +namespace khtml { + class ChildFrame; +} + +namespace KJS { + + class WindowFunc; + class WindowQObject; + class Location; + class History; + class External; + class FrameArray; + class JSEventListener; + class JSLazyEventListener; + + class Screen : public ObjectImp { + public: + Screen(ExecState *exec); + enum { + Height, Width, ColorDepth, PixelDepth, AvailLeft, AvailTop, AvailHeight, + AvailWidth + }; + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + Value getValueProperty(ExecState *exec, int token) const; + private: + KHTMLView *view; + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + + class KDE_EXPORT Window : public ObjectImp { + friend QGuardedPtr getInstance(); + friend class Location; + friend class WindowFunc; + friend class WindowQObject; + friend class ScheduledAction; + public: + Window(khtml::ChildFrame *p); + public: + ~Window(); + /** + * Returns and registers a window object. In case there's already a Window + * for the specified part p this will be returned in order to have unique + * bindings. + */ + static Value retrieve(KParts::ReadOnlyPart *p); + /** + * Returns the Window object for a given part + */ + static Window *retrieveWindow(KParts::ReadOnlyPart *p); + /** + * returns a pointer to the Window object this javascript interpreting instance + * was called from. + */ + static Window *retrieveActive(ExecState *exec); + KParts::ReadOnlyPart *part() const; + virtual void mark(); + virtual bool hasProperty(ExecState *exec, const Identifier &p) const; + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + virtual void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr = None); + virtual bool toBoolean(ExecState *exec) const; + virtual DOM::AbstractView toAbstractView() const; + void scheduleClose(); + void closeNow(); + void delayedGoHistory(int steps); + void goHistory(int steps); + void goURL(ExecState* exec, const QString& url, bool lockHistory); + Value openWindow(ExecState *exec, const List &args); + Value executeOpenWindow(ExecState *exec, const KURL& url, const QString& frameName, const QString& features); + void resizeTo(QWidget* tl, int width, int height); + void afterScriptExecution(); + bool isSafeScript(ExecState *exec) const { + KParts::ReadOnlyPart *activePart = static_cast( exec->interpreter() )->part(); + if ( activePart == part() ) return true; + return checkIsSafeScript( activePart ); + } + Location *location() const; + ObjectImp* frames( ExecState* exec ) const; + JSEventListener *getJSEventListener(const Value &val, bool html = false); + JSLazyEventListener *getJSLazyEventListener(const QString &code, const QString &name, DOM::NodeImpl* node); + void clear( ExecState *exec ); + virtual UString toString(ExecState *exec) const; + + // Set the current "event" object + void setCurrentEvent( DOM::Event *evt ); + + QPtrDict jsEventListeners; + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { Closed, Crypto, DefaultStatus, Status, Document, Node, EventCtor, Range, + NodeFilter, DOMException, CSSRule, Frames, _History, _External, Event, InnerHeight, + InnerWidth, Length, _Location, Navigate, Name, _Navigator, _Konqueror, ClientInformation, + OffscreenBuffering, Opener, OuterHeight, OuterWidth, PageXOffset, PageYOffset, + Parent, Personalbar, ScreenX, ScreenY, Scrollbars, Scroll, ScrollBy, + ScreenTop, ScreenLeft, AToB, BToA, FrameElement, GetComputedStyle, + ScrollTo, ScrollX, ScrollY, MoveBy, MoveTo, ResizeBy, ResizeTo, Self, _Window, Top, _Screen, + Image, Option, Alert, Confirm, Prompt, Open, SetTimeout, ClearTimeout, + XMLHttpRequest, XMLSerializer, DOMParser, + Focus, Blur, Close, SetInterval, ClearInterval, CaptureEvents, ReleaseEvents, + Print, AddEventListener, RemoveEventListener, SideBar, + ValueOf, ToString, + Onabort, Onblur, + Onchange, Onclick, Ondblclick, Ondragdrop, Onerror, Onfocus, + Onkeydown, Onkeypress, Onkeyup, Onload, Onmousedown, Onmousemove, + Onmouseout, Onmouseover, Onmouseup, Onmove, Onreset, Onresize, + Onselect, Onsubmit, Onunload, + MutationEventCtor, KeyboardEventCtor, EventExceptionCtor, + ElementCtor, DocumentCtor, HTMLDocumentCtor, + HTMLElementCtor, HTMLHtmlElementCtor, HTMLHeadElementCtor, HTMLLinkElementCtor, + HTMLTitleElementCtor, HTMLMetaElementCtor, HTMLBaseElementCtor, HTMLIsIndexElementCtor, + HTMLStyleElementCtor, HTMLBodyElementCtor, HTMLFormElementCtor, HTMLSelectElementCtor, + HTMLOptGroupElementCtor, HTMLOptionElementCtor, HTMLInputElementCtor, HTMLTextAreaElementCtor, + HTMLButtonElementCtor, HTMLLabelElementCtor, HTMLFieldSetElementCtor, HTMLLegendElementCtor, + HTMLUListElementCtor, HTMLOListElementCtor, HTMLDListElementCtor, HTMLDirectoryElementCtor, + HTMLMenuElementCtor, HTMLLIElementCtor, HTMLDivElementCtor, HTMLParagraphElementCtor, + HTMLHeadingElementCtor, HTMLBlockQuoteElementCtor, HTMLQuoteElementCtor, HTMLPreElementCtor, + HTMLBRElementCtor, HTMLBaseFontElementCtor, HTMLFontElementCtor, HTMLHRElementCtor, HTMLModElementCtor, + HTMLAnchorElementCtor, HTMLImageElementCtor, HTMLObjectElementCtor, HTMLParamElementCtor, + HTMLAppletElementCtor, HTMLMapElementCtor, HTMLAreaElementCtor, HTMLScriptElementCtor, + HTMLTableElementCtor, HTMLTableCaptionElementCtor, HTMLTableColElementCtor, + HTMLTableSectionElementCtor, HTMLTableRowElementCtor, HTMLTableCellElementCtor, + HTMLFrameSetElementCtor, HTMLLayerElementCtor, HTMLFrameElementCtor, HTMLIFrameElementCtor, + CSSStyleDeclarationCtor}; + WindowQObject *winq; + + void forgetSuppressedWindows(); + void showSuppressedWindows(); + + protected: + enum DelayedActionId { NullAction, DelayedClose, DelayedGoHistory }; + + Value getListener(ExecState *exec, int eventId) const; + void setListener(ExecState *exec, int eventId, Value func); + private: + struct DelayedAction; + friend struct DelayedAction; + + bool checkIsSafeScript( KParts::ReadOnlyPart* activePart ) const; + + QGuardedPtr m_frame; + Screen *screen; + History *history; + External *external; + FrameArray *m_frames; + Location *loc; + DOM::Event *m_evt; + + struct DelayedAction { + DelayedAction() : actionId(NullAction) {} // for QValueList + DelayedAction( DelayedActionId id, QVariant p = QVariant() ) : actionId(id), param(p) {} + DelayedActionId actionId; + QVariant param; // just in case + }; + QValueList m_delayed; + + struct SuppressedWindowInfo { + SuppressedWindowInfo() {} // for QValueList + SuppressedWindowInfo( KURL u, QString fr, QString fe ) : url(u), frameName(fr), features(fe) {} + KURL url; + QString frameName; + QString features; + }; + QValueList m_suppressedWindowInfo; + }; + + /** + * like QDateTime, but properly handles milliseconds + */ + class DateTimeMS + { + QDate mDate; + QTime mTime; + public: + DateTimeMS addMSecs(int s) const; + bool operator >(const DateTimeMS &other) const; + bool operator >=(const DateTimeMS &other) const; + + int msecsTo(const DateTimeMS &other) const; + + static DateTimeMS now(); + }; + + /** + * An action (either function or string) to be executed after a specified + * time interval, either once or repeatedly. Used for window.setTimeout() + * and window.setInterval() + */ + class ScheduledAction { + public: + ScheduledAction(Object _func, List _args, DateTimeMS _nextTime, int _interval, bool _singleShot, int _timerId); + ScheduledAction(QString _code, DateTimeMS _nextTime, int _interval, bool _singleShot, int _timerId); + ~ScheduledAction(); + bool execute(Window *window); + void mark(); + + ObjectImp *func; + List args; + QString code; + bool isFunction; + bool singleShot; + + DateTimeMS nextTime; + int interval; + bool executing; + int timerId; + }; + + class KDE_EXPORT WindowQObject : public QObject { + Q_OBJECT + public: + WindowQObject(Window *w); + ~WindowQObject(); + int installTimeout(const Identifier &handler, int t, bool singleShot); + int installTimeout(const Value &func, List args, int t, bool singleShot); + void clearTimeout(int timerId); + void mark(); + bool hasTimers() const; + public slots: + void timeoutClose(); + protected slots: + void parentDestroyed(); + protected: + void timerEvent(QTimerEvent *e); + void setNextTimer(); + private: + Window *parent; + QPtrList scheduledActions; + int pausedTime; + int lastTimerId; + bool currentlyDispatching; + }; + + class Location : public ObjectImp { + public: + ~Location(); + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + virtual void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr = None); + virtual Value toPrimitive(ExecState *exec, Type preferred) const; + virtual UString toString(ExecState *exec) const; + enum { Hash, Href, Hostname, Host, Pathname, Port, Protocol, Search, EqualEqual, + Assign, Replace, Reload, ToString }; + KParts::ReadOnlyPart *part() const; + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + private: + friend class Window; + Location(khtml::ChildFrame *f); + QGuardedPtr m_frame; + }; + +#ifdef Q_WS_QWS + class Konqueror : public ObjectImp { + friend class KonquerorFunc; + public: + Konqueror(KHTMLPart *p) : part(p) { } + virtual Value get(ExecState *exec, const Identifier &propertyName) const; + virtual bool hasProperty(ExecState *exec, const Identifier &p) const; + virtual UString toString(ExecState *exec) const; + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + private: + KHTMLPart *part; + }; +#endif + +} // namespace + +#endif diff --git a/khtml/ecma/testecma.cpp b/khtml/ecma/testecma.cpp new file mode 100644 index 000000000..44cd7ecb0 --- /dev/null +++ b/khtml/ecma/testecma.cpp @@ -0,0 +1,67 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2000 Harri Porten (porten@kde.org) + * + * 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 + */ + +/** + * An interactive interpreter to test the ECMA Script language bindings + * for the DOM of KHTML. + * The 'document' property is preset to an instance of Document and serves + * as an entrypoint. + * + * Example session: + * + * KJS> text = document.createTextNode('foo'); + * KJS> document.appendChild(text); + * KJS> debug(document.firstChild.nodeValue); + * ---> foo + */ + +#include +#include +#include +#include "dom/dom_doc.h" +#include "dom/dom_string.h" +#include "ecma/kjs_dom.h" + + +using namespace KJS; + +int main(int, char **) +{ + KJScript kjs; + kjs.enableDebug(); + DOM::Document doc; + + DOMDocument *dd = new DOMDocument(&doc); + Global::current().put("document", KJSO(dd)); + + printf("Entering interactive mode.\n" + "You may access the DOM via the 'document' property.\n" + "Use debug() to print to the console. Press C-d or C-c to exit.\n\n"); + + char buffer[1000]; + FILE *in = fdopen(0, "r"); + + while (1) { + printf("KJS> "); + if (!fgets(buffer, 999, in)) + break; + kjs.evaluate(buffer); + } + printf("\n"); +} diff --git a/khtml/ecma/xmlhttprequest.cpp b/khtml/ecma/xmlhttprequest.cpp new file mode 100644 index 000000000..de77f3d7e --- /dev/null +++ b/khtml/ecma/xmlhttprequest.cpp @@ -0,0 +1,810 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * 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 Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "xmlhttprequest.h" +#include "xmlhttprequest.lut.h" +#include "kjs_window.h" +#include "kjs_events.h" + +#include "dom/dom_doc.h" +#include "dom/dom_exception.h" +#include "dom/dom_string.h" +#include "misc/loader.h" +#include "html/html_documentimpl.h" +#include "xml/dom2_eventsimpl.h" + +#include "khtml_part.h" +#include "khtmlview.h" + +#include +#include +#include +#include + +#ifdef APPLE_CHANGES +#include "KWQLoader.h" +#else +#include +using KIO::NetAccess; +#endif + +#define BANNED_HTTP_HEADERS "authorization,proxy-authorization,"\ + "content-length,host,connect,copy,move,"\ + "delete,head,trace,put,propfind,proppatch,"\ + "mkcol,lock,unlock,options,via,"\ + "accept-charset,accept-encoding,expect,date,"\ + "keep-alive,te,trailer,"\ + "transfer-encoding,upgrade" + +using khtml::Decoder; + +namespace KJS { + +////////////////////// XMLHttpRequest Object //////////////////////// + +/* Source for XMLHttpRequestProtoTable. +@begin XMLHttpRequestProtoTable 7 + abort XMLHttpRequest::Abort DontDelete|Function 0 + getAllResponseHeaders XMLHttpRequest::GetAllResponseHeaders DontDelete|Function 0 + getResponseHeader XMLHttpRequest::GetResponseHeader DontDelete|Function 1 + open XMLHttpRequest::Open DontDelete|Function 5 + overrideMimeType XMLHttpRequest::OverrideMIMEType DontDelete|Function 1 + send XMLHttpRequest::Send DontDelete|Function 1 + setRequestHeader XMLHttpRequest::SetRequestHeader DontDelete|Function 2 +@end +*/ +KJS_DEFINE_PROTOTYPE(XMLHttpRequestProto) +IMPLEMENT_PROTOFUNC_DOM(XMLHttpRequestProtoFunc) +KJS_IMPLEMENT_PROTOTYPE("XMLHttpRequest", XMLHttpRequestProto,XMLHttpRequestProtoFunc) + + +XMLHttpRequestQObject::XMLHttpRequestQObject(XMLHttpRequest *_jsObject) +{ + jsObject = _jsObject; +} + +#ifdef APPLE_CHANGES +void XMLHttpRequestQObject::slotData( KIO::Job* job, const char *data, int size ) +{ + jsObject->slotData(job, data, size); +} +#else +void XMLHttpRequestQObject::slotData( KIO::Job* job, const QByteArray &data ) +{ + jsObject->slotData(job, data); +} +#endif + +void XMLHttpRequestQObject::slotFinished( KIO::Job* job ) +{ + jsObject->slotFinished(job); +} + +void XMLHttpRequestQObject::slotRedirection( KIO::Job* job, const KURL& url) +{ + jsObject->slotRedirection( job, url ); +} + +XMLHttpRequestConstructorImp::XMLHttpRequestConstructorImp(ExecState *, const DOM::Document &d) + : ObjectImp(), doc(d) +{ +} + +bool XMLHttpRequestConstructorImp::implementsConstruct() const +{ + return true; +} + +Object XMLHttpRequestConstructorImp::construct(ExecState *exec, const List &) +{ + return Object(new XMLHttpRequest(exec, doc)); +} + +const ClassInfo XMLHttpRequest::info = { "XMLHttpRequest", 0, &XMLHttpRequestTable, 0 }; + + +/* Source for XMLHttpRequestTable. +@begin XMLHttpRequestTable 7 + readyState XMLHttpRequest::ReadyState DontDelete|ReadOnly + responseText XMLHttpRequest::ResponseText DontDelete|ReadOnly + responseXML XMLHttpRequest::ResponseXML DontDelete|ReadOnly + status XMLHttpRequest::Status DontDelete|ReadOnly + statusText XMLHttpRequest::StatusText DontDelete|ReadOnly + onreadystatechange XMLHttpRequest::Onreadystatechange DontDelete + onload XMLHttpRequest::Onload DontDelete +@end +*/ + +Value XMLHttpRequest::tryGet(ExecState *exec, const Identifier &propertyName) const +{ + return DOMObjectLookupGetValue(exec, propertyName, &XMLHttpRequestTable, this); +} + +Value XMLHttpRequest::getValueProperty(ExecState *exec, int token) const +{ + switch (token) { + case ReadyState: + return Number(state); + case ResponseText: + return getString(DOM::DOMString(response)); + case ResponseXML: + if (state != Completed) { + return Null(); + } + if (!createdDocument) { + QString mimeType = "text/xml"; + + if (!m_mimeTypeOverride.isEmpty()) { + mimeType = m_mimeTypeOverride; + } else { + Value header = getResponseHeader("Content-Type"); + if (header.type() != UndefinedType) { + mimeType = QStringList::split(";", header.toString(exec).qstring())[0].stripWhiteSpace(); + } + } + + if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "application/xhtml+xml") { + responseXML = DOM::Document(doc->implementation()->createDocument()); + + DOM::DocumentImpl *docImpl = static_cast(responseXML.handle()); + + docImpl->open(); + docImpl->write(response); + docImpl->finishParsing(); + docImpl->close(); + + typeIsXML = true; + } else { + typeIsXML = false; + } + createdDocument = true; + } + + if (!typeIsXML) { + return Undefined(); + } + + return getDOMNode(exec,responseXML); + case Status: + return getStatus(); + case StatusText: + return getStatusText(); + case Onreadystatechange: + if (onReadyStateChangeListener && onReadyStateChangeListener->listenerObjImp()) { + return onReadyStateChangeListener->listenerObj(); + } else { + return Null(); + } + case Onload: + if (onLoadListener && onLoadListener->listenerObjImp()) { + return onLoadListener->listenerObj(); + } else { + return Null(); + } + default: + kdWarning() << "XMLHttpRequest::getValueProperty unhandled token " << token << endl; + return Value(); + } +} + +void XMLHttpRequest::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr) +{ + DOMObjectLookupPut(exec, propertyName, value, attr, &XMLHttpRequestTable, this ); +} + +void XMLHttpRequest::putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/) +{ + JSEventListener* newListener; + switch(token) { + case Onreadystatechange: + newListener = Window::retrieveActive(exec)->getJSEventListener(value, true); + if (newListener != onReadyStateChangeListener) { + if (onReadyStateChangeListener) onReadyStateChangeListener->deref(); + onReadyStateChangeListener = newListener; + if (onReadyStateChangeListener) onReadyStateChangeListener->ref(); + } + break; + case Onload: + newListener = Window::retrieveActive(exec)->getJSEventListener(value, true); + if (newListener != onLoadListener) { + if (onLoadListener) onLoadListener->deref(); + onLoadListener = newListener; + if (onLoadListener) onLoadListener->ref(); + } + break; + default: + kdWarning() << "XMLHttpRequest::putValue unhandled token " << token << endl; + } +} + +XMLHttpRequest::XMLHttpRequest(ExecState *exec, const DOM::Document &d) + : DOMObject(XMLHttpRequestProto::self(exec)), + qObject(new XMLHttpRequestQObject(this)), + doc(static_cast(d.handle())), + async(true), + contentType(QString::null), + job(0), + state(Uninitialized), + onReadyStateChangeListener(0), + onLoadListener(0), + decoder(0), + createdDocument(false), + aborted(false) +{ +} + +XMLHttpRequest::~XMLHttpRequest() +{ + if (onReadyStateChangeListener) + onReadyStateChangeListener->deref(); + if (onLoadListener) + onLoadListener->deref(); + delete qObject; + qObject = 0; + delete decoder; + decoder = 0; +} + +void XMLHttpRequest::changeState(XMLHttpRequestState newState) +{ + if (state != newState) { + state = newState; + + ref(); + + if (onReadyStateChangeListener != 0 && doc->view() && doc->view()->part()) { + DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents"); + ev.initEvent("readystatechange", true, true); + onReadyStateChangeListener->handleEvent(ev); + } + + if (state == Completed && onLoadListener != 0 && doc->view() && doc->view()->part()) { + DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents"); + ev.initEvent("load", true, true); + onLoadListener->handleEvent(ev); + } + + deref(); + } +} + +bool XMLHttpRequest::urlMatchesDocumentDomain(const KURL& _url) const +{ + // No need to do work if _url is not valid... + if (!_url.isValid()) + return false; + + KURL documentURL(doc->URL()); + + // a local file can load anything + if (documentURL.protocol().lower() == "file") { + return true; + } + + // but a remote document can only load from the same port on the server + if (documentURL.protocol().lower() == _url.protocol().lower() && + documentURL.host().lower() == _url.host().lower() && + documentURL.port() == _url.port()) { + return true; + } + + return false; +} + +void XMLHttpRequest::open(const QString& _method, const KURL& _url, bool _async) +{ + abort(); + aborted = false; + + // clear stuff from possible previous load + requestHeaders.clear(); + responseHeaders = QString(); + response = QString(); + createdDocument = false; + responseXML = DOM::Document(); + + changeState(Uninitialized); + + if (aborted) { + return; + } + + if (!urlMatchesDocumentDomain(_url)) { + return; + } + + + method = _method.lower(); + url = _url; + async = _async; + + changeState(Loading); +} + +void XMLHttpRequest::send(const QString& _body) +{ + aborted = false; + + if (method == "post") { + QString protocol = url.protocol().lower(); + + // Abondon the request when the protocol is other than "http", + // instead of blindly changing it to a "get" request. + if (!protocol.startsWith("http") && !protocol.startsWith("webdav")) + { + abort(); + return; + } + + // FIXME: determine post encoding correctly by looking in headers + // for charset. + QByteArray buf; + QCString str = _body.utf8(); + buf.duplicate(str.data(), str.size() - 1); + + job = KIO::http_post( url, buf, false ); + if(contentType.isNull()) + job->addMetaData( "content-type", "Content-type: text/plain" ); + else + job->addMetaData( "content-type", contentType ); + } + else { + job = KIO::get( url, false, false ); + } + + if (!requestHeaders.isEmpty()) { + QString rh; + QMap::ConstIterator begin = requestHeaders.begin(); + QMap::ConstIterator end = requestHeaders.end(); + for (QMap::ConstIterator i = begin; i != end; ++i) { + QString key = i.key(); + QString value = i.data(); + if (key == "accept") { + // The HTTP KIO slave supports an override this way + job->addMetaData("accept", value); + } else { + if (i != begin) + rh += "\r\n"; + rh += key + ": " + value; + } + } + + job->addMetaData("customHTTPHeader", rh); + } + + job->addMetaData("PropagateHttpHeader", "true"); + + // Set the default referrer if one is not already supplied + // through setRequestHeader. NOTE: the user can still disable + // this feature at the protocol level (kio_http). + // ### does find() ever succeed? the headers are stored in lower case! + if (requestHeaders.find("Referer") == requestHeaders.end()) { + KURL documentURL(doc->URL()); + documentURL.setPass(QString::null); + documentURL.setUser(QString::null); + job->addMetaData("referrer", documentURL.url()); + // kdDebug() << "Adding referrer: " << documentURL << endl; + } + + if (!async) { + QByteArray data; + KURL finalURL; + QString headers; + +#ifdef APPLE_CHANGES + data = KWQServeSynchronousRequest(khtml::Cache::loader(), doc->docLoader(), job, finalURL, headers); +#else + QMap metaData; + if ( NetAccess::synchronousRun( job, 0, &data, &finalURL, &metaData ) ) { + headers = metaData[ "HTTP-Headers" ]; + } +#endif + job = 0; + processSyncLoadResults(data, finalURL, headers); + return; + } + + qObject->connect( job, SIGNAL( result( KIO::Job* ) ), + SLOT( slotFinished( KIO::Job* ) ) ); +#ifdef APPLE_CHANGES + qObject->connect( job, SIGNAL( data( KIO::Job*, const char*, int ) ), + SLOT( slotData( KIO::Job*, const char*, int ) ) ); +#else + qObject->connect( job, SIGNAL( data( KIO::Job*, const QByteArray& ) ), + SLOT( slotData( KIO::Job*, const QByteArray& ) ) ); +#endif + qObject->connect( job, SIGNAL(redirection(KIO::Job*, const KURL& ) ), + SLOT( slotRedirection(KIO::Job*, const KURL&) ) ); + +#ifdef APPLE_CHANGES + KWQServeRequest(khtml::Cache::loader(), doc->docLoader(), job); +#else + KIO::Scheduler::scheduleJob( job ); +#endif +} + +void XMLHttpRequest::abort() +{ + if (job) { + job->kill(); + job = 0; + } + delete decoder; + decoder = 0; + aborted = true; +} + +void XMLHttpRequest::overrideMIMEType(const QString& override) +{ + m_mimeTypeOverride = override; +} + +void XMLHttpRequest::setRequestHeader(const QString& _name, const QString &value) +{ + QString name = _name.lower().stripWhiteSpace(); + + // Content-type needs to be set seperately from the other headers + if(name == "content-type") { + contentType = "Content-type: " + value; + return; + } + + // Sanitize the referrer header to protect against spoofing... + if(name == "referer") { + KURL referrerURL(value); + if (urlMatchesDocumentDomain(referrerURL)) + requestHeaders[name] = referrerURL.url(); + return; + } + + // Sanitize the request headers below and handle them as if they are + // calls to open. Otherwise, we will end up ignoring them all together! + // TODO: Do something about "put" which kio_http sort of supports and + // the webDAV headers such as PROPFIND etc... + if (name == "get" || name == "post") { + KURL reqURL (doc->URL(), value.stripWhiteSpace()); + open(name, reqURL, async); + return; + } + + // Reject all banned headers. See BANNED_HTTP_HEADERS above. + // kdDebug() << "Banned HTTP Headers: " << BANNED_HTTP_HEADERS << endl; + QStringList bannedHeaders = QStringList::split(',', + QString::fromLatin1(BANNED_HTTP_HEADERS)); + + if (bannedHeaders.contains(name)) + return; // Denied + + requestHeaders[name] = value.stripWhiteSpace(); +} + +Value XMLHttpRequest::getAllResponseHeaders() const +{ + if (responseHeaders.isEmpty()) { + return Undefined(); + } + + int endOfLine = responseHeaders.find("\n"); + + if (endOfLine == -1) { + return Undefined(); + } + + return String(responseHeaders.mid(endOfLine + 1) + "\n"); +} + +Value XMLHttpRequest::getResponseHeader(const QString& name) const +{ + if (responseHeaders.isEmpty()) { + return Undefined(); + } + + QRegExp headerLinePattern(name + ":", false); + + int matchLength; + int headerLinePos = headerLinePattern.search(responseHeaders, 0); + matchLength = headerLinePattern.matchedLength(); + while (headerLinePos != -1) { + if (headerLinePos == 0 || responseHeaders[headerLinePos-1] == '\n') { + break; + } + + headerLinePos = headerLinePattern.search(responseHeaders, headerLinePos + 1); + matchLength = headerLinePattern.matchedLength(); + } + + + if (headerLinePos == -1) { + return Undefined(); + } + + int endOfLine = responseHeaders.find("\n", headerLinePos + matchLength); + + return String(responseHeaders.mid(headerLinePos + matchLength, endOfLine - (headerLinePos + matchLength)).stripWhiteSpace()); +} + +static Value httpStatus(const QString& response, bool textStatus = false) +{ + if (response.isEmpty()) { + return Undefined(); + } + + int endOfLine = response.find("\n"); + QString firstLine = (endOfLine == -1) ? response : response.left(endOfLine); + int codeStart = firstLine.find(" "); + int codeEnd = firstLine.find(" ", codeStart + 1); + + if (codeStart == -1 || codeEnd == -1) { + return Undefined(); + } + + if (textStatus) { + QString statusText = firstLine.mid(codeEnd + 1, endOfLine - (codeEnd + 1)).stripWhiteSpace(); + return String(statusText); + } + + QString number = firstLine.mid(codeStart + 1, codeEnd - (codeStart + 1)); + + bool ok = false; + int code = number.toInt(&ok); + if (!ok) { + return Undefined(); + } + + return Number(code); +} + +Value XMLHttpRequest::getStatus() const +{ + return httpStatus(responseHeaders); +} + +Value XMLHttpRequest::getStatusText() const +{ + return httpStatus(responseHeaders, true); +} + +void XMLHttpRequest::processSyncLoadResults(const QByteArray &data, const KURL &finalURL, const QString &headers) +{ + if (!urlMatchesDocumentDomain(finalURL)) { + abort(); + return; + } + + responseHeaders = headers; + changeState(Loaded); + if (aborted) { + return; + } + +#ifdef APPLE_CHANGES + const char *bytes = (const char *)data.data(); + int len = (int)data.size(); + + slotData(0, bytes, len); +#else + slotData(0, data); +#endif + + if (aborted) { + return; + } + + slotFinished(0); +} + +void XMLHttpRequest::slotFinished(KIO::Job *) +{ + if (decoder) { + response += decoder->flush(); + } + + // make sure to forget about the job before emitting completed, + // since changeState triggers JS code, which might e.g. call abort. + job = 0; + changeState(Completed); + + delete decoder; + decoder = 0; +} + +void XMLHttpRequest::slotRedirection(KIO::Job*, const KURL& url) +{ + if (!urlMatchesDocumentDomain(url)) { + abort(); + } +} + +#ifdef APPLE_CHANGES +void XMLHttpRequest::slotData( KIO::Job*, const char *data, int len ) +#else +void XMLHttpRequest::slotData(KIO::Job*, const QByteArray &_data) +#endif +{ + if (state < Loaded ) { + responseHeaders = job->queryMetaData("HTTP-Headers"); + + // NOTE: Replace a 304 response with a 200! Both IE and Mozilla do this. + // Problem first reported through bug# 110272. + int codeStart = responseHeaders.find("304"); + if ( codeStart != -1) { + int codeEnd = responseHeaders.find("\n", codeStart+3); + if (codeEnd != -1) + responseHeaders.replace(codeStart, (codeEnd-codeStart), "200 OK"); + } + + changeState(Loaded); + } + +#ifndef APPLE_CHANGES + const char *data = (const char *)_data.data(); + int len = (int)_data.size(); +#endif + + if ( decoder == NULL ) { + int pos = responseHeaders.find("content-type:", 0, false); + + if ( pos > -1 ) { + pos += 13; + int index = responseHeaders.find('\n', pos); + QString type = responseHeaders.mid(pos, (index-pos)); + index = type.find (';'); + if (index > -1) + encoding = type.mid( index+1 ).remove(QRegExp("charset[ ]*=[ ]*", false)).stripWhiteSpace(); + } + + decoder = new Decoder; + if (!encoding.isNull()) + decoder->setEncoding(encoding.latin1(), Decoder::EncodingFromHTTPHeader); + else { + // Per section 2 of W3C working draft spec, fall back to "UTF-8". + decoder->setEncoding("UTF-8", Decoder::DefaultEncoding); + } + } + if (len == 0) + return; + + if (len == -1) + len = strlen(data); + + QString decoded = decoder->decode(data, len); + + response += decoded; + + if (!aborted) { + changeState(Interactive); + } +} + +Value XMLHttpRequestProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + if (!thisObj.inherits(&XMLHttpRequest::info)) { + Object err = Error::create(exec,TypeError); + exec->setException(err); + return err; + } + + XMLHttpRequest *request = static_cast(thisObj.imp()); + switch (id) { + case XMLHttpRequest::Abort: + request->abort(); + return Undefined(); + case XMLHttpRequest::GetAllResponseHeaders: + if (args.size() != 0) { + return Undefined(); + } + + return request->getAllResponseHeaders(); + case XMLHttpRequest::GetResponseHeader: + if (args.size() != 1) { + return Undefined(); + } + + return request->getResponseHeader(args[0].toString(exec).qstring()); + case XMLHttpRequest::Open: + { + if (args.size() < 2 || args.size() > 5) { + return Undefined(); + } + + QString method = args[0].toString(exec).qstring(); + KHTMLPart *part = ::qt_cast(Window::retrieveActive(exec)->part()); + if (!part) + return Undefined(); + KURL url = KURL(part->document().completeURL(args[1].toString(exec).qstring()).string()); + + bool async = true; + if (args.size() >= 3) { + async = args[2].toBoolean(exec); + } + + if (args.size() >= 4) { + url.setUser(args[3].toString(exec).qstring()); + } + + if (args.size() >= 5) { + url.setPass(args[4].toString(exec).qstring()); + } + + request->open(method, url, async); + + return Undefined(); + } + case XMLHttpRequest::Send: + { + if (args.size() > 1) { + return Undefined(); + } + + if (request->state != Loading) { + return Undefined(); + } + + QString body; + if (args.size() >= 1) { + Object obj = Object::dynamicCast(args[0]); + if (obj.isValid() && obj.inherits(&DOMDocument::info)) { + DOM::Node docNode = static_cast(obj.imp())->toNode(); + DOM::DocumentImpl *doc = static_cast(docNode.handle()); + + try { + body = doc->toString().string(); + // FIXME: also need to set content type, including encoding! + + } catch(DOM::DOMException& e) { + Object err = Error::create(exec, GeneralError, "Exception serializing document"); + exec->setException(err); + } + } else { + body = args[0].toString(exec).qstring(); + } + } + + request->send(body); + + return Undefined(); + } + case XMLHttpRequest::SetRequestHeader: + if (args.size() != 2) { + return Undefined(); + } + + request->setRequestHeader(args[0].toString(exec).qstring(), args[1].toString(exec).qstring()); + + return Undefined(); + + case XMLHttpRequest::OverrideMIMEType: + if (args.size() < 1) { + Object err = Error::create(exec, SyntaxError, "Not enough arguments"); + exec->setException(err); + return err; + } + + request->overrideMIMEType(args[0].toString(exec).qstring()); + return Undefined(); + } + + return Undefined(); +} + +} // end namespace + + +#include "xmlhttprequest.moc" diff --git a/khtml/ecma/xmlhttprequest.h b/khtml/ecma/xmlhttprequest.h new file mode 100644 index 000000000..49582a905 --- /dev/null +++ b/khtml/ecma/xmlhttprequest.h @@ -0,0 +1,142 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * 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 Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + */ + +#ifndef _XMLHTTPREQUEST_H_ +#define _XMLHTTPREQUEST_H_ + +#include "ecma/kjs_binding.h" +#include "ecma/kjs_dom.h" +#include "misc/decoder.h" +#include "kio/jobclasses.h" + +namespace KJS { + + class JSEventListener; + class XMLHttpRequestQObject; + + // these exact numeric values are important because JS expects them + enum XMLHttpRequestState { + Uninitialized = 0, + Loading = 1, + Loaded = 2, + Interactive = 3, + Completed = 4 + }; + + class XMLHttpRequestConstructorImp : public ObjectImp { + public: + XMLHttpRequestConstructorImp(ExecState *exec, const DOM::Document &d); + virtual bool implementsConstruct() const; + virtual Object construct(ExecState *exec, const List &args); + private: + DOM::Document doc; + }; + + class XMLHttpRequest : public DOMObject { + public: + XMLHttpRequest(ExecState *, const DOM::Document &d); + ~XMLHttpRequest(); + virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const; + Value getValueProperty(ExecState *exec, int token) const; + virtual void tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr = None); + void putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/); + virtual bool toBoolean(ExecState *) const { return true; } + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { Onload, Onreadystatechange, ReadyState, ResponseText, ResponseXML, Status, StatusText, Abort, + GetAllResponseHeaders, GetResponseHeader, Open, Send, SetRequestHeader, + OverrideMIMEType }; + + private: + friend class XMLHttpRequestProtoFunc; + friend class XMLHttpRequestQObject; + + Value getStatusText() const; + Value getStatus() const; + bool urlMatchesDocumentDomain(const KURL&) const; + + XMLHttpRequestQObject *qObject; + +#ifdef APPLE_CHANGES + void slotData( KIO::Job* job, const char *data, int size ); +#else + void slotData( KIO::Job* job, const QByteArray &data ); +#endif + void slotFinished( KIO::Job* ); + void slotRedirection( KIO::Job*, const KURL& ); + + void processSyncLoadResults(const QByteArray &data, const KURL &finalURL, const QString &headers); + + void open(const QString& _method, const KURL& _url, bool _async); + void send(const QString& _body); + void abort(); + void setRequestHeader(const QString& name, const QString &value); + void overrideMIMEType(const QString& override); + Value getAllResponseHeaders() const; + Value getResponseHeader(const QString& name) const; + + void changeState(XMLHttpRequestState newState); + + QGuardedPtr doc; + + KURL url; + QString method; + bool async; + QMap requestHeaders; + QString m_mimeTypeOverride; + QString contentType; + + KIO::TransferJob * job; + + XMLHttpRequestState state; + JSEventListener *onReadyStateChangeListener; + JSEventListener *onLoadListener; + + khtml::Decoder *decoder; + QString encoding; + QString responseHeaders; + + QString response; + mutable bool createdDocument; + mutable bool typeIsXML; + mutable DOM::Document responseXML; + + bool aborted; + }; + + + class XMLHttpRequestQObject : public QObject { + Q_OBJECT + + public: + XMLHttpRequestQObject(XMLHttpRequest *_jsObject); + + public slots: + void slotData( KIO::Job* job, const QByteArray &data ); + void slotFinished( KIO::Job* job ); + void slotRedirection( KIO::Job* job, const KURL& url); + + private: + XMLHttpRequest *jsObject; + }; + +} // namespace + +#endif diff --git a/khtml/ecma/xmlserializer.cpp b/khtml/ecma/xmlserializer.cpp new file mode 100644 index 000000000..6abcfe77f --- /dev/null +++ b/khtml/ecma/xmlserializer.cpp @@ -0,0 +1,109 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * 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 Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "xmlserializer.h" +#include "xmlserializer.lut.h" + +#include "dom/dom_exception.h" +#include "dom/dom_doc.h" +#include "xml/dom_docimpl.h" + +#include + + +////////////////////// XMLSerializer Object //////////////////////// + +/* Source for XMLSerializerProtoTable. +@begin XMLSerializerProtoTable 1 + serializeToString XMLSerializer::SerializeToString DontDelete|Function 1 +@end +*/ + +namespace KJS { + +KJS_DEFINE_PROTOTYPE(XMLSerializerProto) +IMPLEMENT_PROTOFUNC_DOM(XMLSerializerProtoFunc) +KJS_IMPLEMENT_PROTOTYPE("XMLSerializer", XMLSerializerProto, XMLSerializerProtoFunc) + +XMLSerializerConstructorImp::XMLSerializerConstructorImp(ExecState *) + : ObjectImp() +{ +} + +bool XMLSerializerConstructorImp::implementsConstruct() const +{ + return true; +} + +Object XMLSerializerConstructorImp::construct(ExecState *exec, const List &) +{ + return Object(new XMLSerializer(exec)); +} + +const ClassInfo XMLSerializer::info = { "XMLSerializer", 0, 0, 0 }; + +XMLSerializer::XMLSerializer(ExecState *exec) + : DOMObject(XMLSerializerProto::self(exec)) +{ +} + +Value XMLSerializerProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args) +{ + if (!thisObj.inherits(&XMLSerializer::info)) { + Object err = Error::create(exec,TypeError); + exec->setException(err); + return err; + } + + switch (id) { + case XMLSerializer::SerializeToString: + { + if (args.size() != 1) { + return Undefined(); + } + + if (!args[0].toObject(exec).inherits(&DOMNode::info)) { + return Undefined(); + } + + DOM::NodeImpl *node = static_cast(static_cast(args[0].toObject(exec).imp())->toNode().handle()); + + if (!node) { + return Undefined(); + } + + QString body; + + try { + body = node->toString().string(); + } catch(DOM::DOMException& e) { + Object err = Error::create(exec, GeneralError, "Exception serializing document"); + exec->setException(err); + return err; + } + + return getString(body); + } + } + + return Undefined(); +} + +} // end namespace diff --git a/khtml/ecma/xmlserializer.h b/khtml/ecma/xmlserializer.h new file mode 100644 index 000000000..629d09afe --- /dev/null +++ b/khtml/ecma/xmlserializer.h @@ -0,0 +1,54 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * 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 Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + */ + +#ifndef _XMLSERIALIZER_H_ +#define _XMLSERIALIZER_H_ + +#include "ecma/kjs_binding.h" +#include "ecma/kjs_dom.h" +#include "misc/decoder.h" +#include "kio/jobclasses.h" + +namespace KJS { + + class JSEventListener; + + class XMLSerializerConstructorImp : public ObjectImp { + public: + XMLSerializerConstructorImp(ExecState *); + virtual bool implementsConstruct() const; + virtual Object construct(ExecState *exec, const List &args); + }; + + class XMLSerializer : public DOMObject { + public: + XMLSerializer(ExecState *); + virtual bool toBoolean(ExecState *) const { return true; } + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + enum { SerializeToString }; + + private: + friend class XMLSerializerProtoFunc; + }; + +} // namespace + +#endif -- cgit v1.2.1