#include "xml_tokenizer.h"
#include "xml/dom_docimpl.h"
#include "xml/dom_textimpl.h"
#include "xml/dom_xmlimpl.h"
#include "html/html_headimpl.h"
#include "rendering/render_object.h"
#include "misc/htmltags.h"
#include "misc/htmlattrs.h"
#include "misc/loader.h"
#include "khtmlview.h"
#include "khtml_part.h"
#include <qvariant.h>
#include <kdebug.h>
#include <klocale.h>
using namespace DOM;
namespace khtml {
const int maxErrors = 25;
XMLHandler::XMLHandler(DocumentPtr *_doc, KHTMLView *_view)
: errorLine(0), m_errorCount(0)
{
m_doc = _doc;
if ( m_doc ) m_doc->ref();
m_view = _view;
m_currentNode = _doc->document();
}
XMLHandler::~XMLHandler()
{
if ( m_doc ) m_doc->deref();
}
QString XMLHandler::errorProtocol()
{
return errorProt;
}
bool XMLHandler::startDocument()
{
errorProt = "";
m_errorCount = 0;
state = StateInit;
return true;
}
bool XMLHandler::startElement( const QString& namespaceURI, const QString& , const QString& qName, const QXmlAttributes& atts )
{
if (m_errorCount) return true;
if (m_currentNode->nodeType() == Node::TEXT_NODE)
exitText();
int exceptioncode = 0;
ElementImpl *newElement = m_doc->document()->createElementNS(namespaceURI,qName,exceptioncode);
if (!newElement)
return false;
int i;
for (i = 0; i < atts.length(); i++) {
DOMString uri(atts.uri(i));
DOMString ln(atts.localName(i));
DOMString val(atts.value(i));
NodeImpl::Id id = m_doc->document()->attrId(uri.implementation(),
ln.implementation(),
false );
newElement->setAttribute(id, val.implementation(), exceptioncode);
if (exceptioncode) return false;
}
if (m_currentNode->id() == ID_TABLE &&
newElement->id() == ID_TR &&
m_currentNode->isHTMLElement() && newElement->isHTMLElement()) {
NodeImpl* implicitTBody =
new HTMLTableSectionElementImpl( m_doc, ID_TBODY, true );
m_currentNode->addChild(implicitTBody);
if (m_view && !implicitTBody->attached())
implicitTBody->attach();
m_currentNode = implicitTBody;
}
if (m_currentNode->addChild(newElement)) {
if (m_view && !newElement->attached())
newElement->attach();
m_currentNode = newElement;
return true;
}
else {
delete newElement;
return false;
}
}
bool XMLHandler::endElement( const QString& , const QString& , const QString& )
{
if (m_errorCount) return true;
if (m_currentNode->nodeType() == Node::TEXT_NODE)
exitText();
if (m_currentNode->parentNode() != 0) {
m_currentNode->closeRenderer();
do {
m_currentNode = m_currentNode->parentNode();
} while (m_currentNode && m_currentNode->implicitNode());
}
return true;
}
bool XMLHandler::startCDATA()
{
if (m_errorCount) return true;
if (m_currentNode->nodeType() == Node::TEXT_NODE)
exitText();
NodeImpl *newNode = m_doc->document()->createCDATASection("");
if (m_currentNode->addChild(newNode)) {
if (m_view && !newNode->attached())
newNode->attach();
m_currentNode = newNode;
return true;
}
else {
delete newNode;
return false;
}
}
bool XMLHandler::endCDATA()
{
if (m_errorCount) return true;
if (m_currentNode->parentNode() != 0)
m_currentNode = m_currentNode->parentNode();
return true;
}
bool XMLHandler::characters( const QString& ch )
{
if (m_errorCount) return true;
if (m_currentNode->nodeType() == Node::TEXT_NODE ||
m_currentNode->nodeType() == Node::CDATA_SECTION_NODE ||
enterText()) {
int exceptioncode = 0;
static_cast<TextImpl*>(m_currentNode)->appendData(ch,exceptioncode);
if (exceptioncode)
return false;
return true;
}
else
return false;
}
bool XMLHandler::comment(const QString & ch)
{
if (m_errorCount) return true;
if (m_currentNode->nodeType() == Node::TEXT_NODE)
exitText();
m_currentNode->addChild(m_doc->document()->createComment(ch));
return true;
}
bool XMLHandler::processingInstruction(const QString &target, const QString &data)
{
if (m_errorCount) return true;
if (m_currentNode->nodeType() == Node::TEXT_NODE)
exitText();
ProcessingInstructionImpl *pi = m_doc->document()->createProcessingInstruction(target,data);
m_currentNode->addChild(pi);
if (m_doc->document()->part()) {
pi->checkStyleSheet();
}
return true;
}
QString XMLHandler::errorString()
{
#if APPLE_CHANGES
return "error";
#else
return i18n("the document is not in the correct file format");
#endif
}
bool XMLHandler::warning( const QXmlParseException& exception )
{
#if APPLE_CHANGES
errorProt += QString("warning on line %2 at column %3: %1")
#else
errorProt += i18n( "warning: %1 in line %2, column %3\n" )
#endif
.arg( exception.message() )
.arg( exception.lineNumber() )
.arg( exception.columnNumber() );
errorLine = exception.lineNumber();
errorCol = exception.columnNumber();
return true;
}
bool XMLHandler::error( const QXmlParseException& exception )
{
if (m_errorCount >= maxErrors) return true;
if (errorLine == exception.lineNumber() && errorCol == exception.columnNumber())
return true;
m_errorCount++;
#if APPLE_CHANGES
errorProt += QString("error on line %2 at column %3: %1")
#else
errorProt += i18n( "error: %1 in line %2, column %3\n" )
#endif
.arg( exception.message() )
.arg( exception.lineNumber() )
.arg( exception.columnNumber() );
errorLine = exception.lineNumber();
errorCol = exception.columnNumber();
return true;
}
bool XMLHandler::fatalError( const QXmlParseException& exception )
{
#if APPLE_CHANGES
errorProt += QString("error on line %2 at column %3: %1")
#else
errorProt += i18n( "fatal error: %1 in line %2, column %3\n" )
#endif
.arg( exception.message() )
.arg( exception.lineNumber() )
.arg( exception.columnNumber() );
errorLine = exception.lineNumber();
errorCol = exception.columnNumber();
return false;
}
bool XMLHandler::enterText()
{
NodeImpl *newNode = m_doc->document()->createTextNode("");
if (m_currentNode->addChild(newNode)) {
m_currentNode = newNode;
return true;
}
else {
delete newNode;
return false;
}
}
void XMLHandler::exitText()
{
if (m_view && m_currentNode && !m_currentNode->attached())
m_currentNode->attach();
NodeImpl* par = m_currentNode->parentNode();
if (par != 0)
m_currentNode = par;
}
bool XMLHandler::attributeDecl(const QString &, const QString &, const QString &,
const QString &, const QString &)
{
return true;
}
bool XMLHandler::externalEntityDecl(const QString &, const QString &, const QString &)
{
return true;
}
bool XMLHandler::internalEntityDecl(const QString &name, const QString &value)
{
EntityImpl *e = new EntityImpl(m_doc,name);
e->addChild(m_doc->document()->createTextNode(value));
return true;
}
bool XMLHandler::notationDecl(const QString &name, const QString &publicId, const QString &systemId)
{
return true;
}
bool XMLHandler::unparsedEntityDecl(const QString &, const QString &,
const QString &, const QString &)
{
return true;
}
XMLTokenizer::XMLTokenizer(DOM::DocumentPtr *_doc, KHTMLView *_view)
{
m_doc = _doc;
if ( m_doc ) m_doc->ref();
m_view = _view;
m_xmlCode = "";
m_scriptsIt = 0;
m_cachedScript = 0;
}
XMLTokenizer::~XMLTokenizer()
{
if ( m_doc ) m_doc->deref();
if (m_scriptsIt)
delete m_scriptsIt;
if (m_cachedScript)
m_cachedScript->deref(this);
}
void XMLTokenizer::begin()
{
}
void XMLTokenizer::write(const TokenizerString &s, bool )
{
m_xmlCode += s.toString();
}
void XMLTokenizer::end()
{
emit finishedParsing();
}
void XMLTokenizer::finish()
{
XMLHandler* handler = m_doc->document()->createTokenHandler();
QXmlInputSource source;
source.setData(m_xmlCode);
QXmlSimpleReader reader;
reader.setContentHandler( handler );
reader.setLexicalHandler( handler );
reader.setErrorHandler( handler );
reader.setDeclHandler( handler );
reader.setDTDHandler( handler );
bool ok = reader.parse( source );
if (!ok) {
int exceptioncode = 0;
DocumentImpl *doc = m_doc->document();
NodeImpl* root = doc->documentElement();
if (!root) {
root = doc->createElementNS(XHTML_NAMESPACE, "html", exceptioncode);
NodeImpl* body = doc->createElementNS(XHTML_NAMESPACE, "body", exceptioncode);
root->appendChild(body, exceptioncode);
doc->appendChild(root, exceptioncode);
root = body;
}
ElementImpl* reportDiv = doc->createElementNS(XHTML_NAMESPACE, "div", exceptioncode);
reportDiv->setAttribute(ATTR_STYLE, "white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black");
ElementImpl* h3 = doc->createElementNS(XHTML_NAMESPACE, "h3", exceptioncode);
h3->appendChild(doc->createTextNode("This page contains the following errors:"), exceptioncode);
reportDiv->appendChild(h3, exceptioncode);
ElementImpl* fixed = doc->createElementNS(XHTML_NAMESPACE, "div", exceptioncode);
fixed->setAttribute(ATTR_STYLE, "font-family:monospace;font-size:12px");
NodeImpl* textNode = doc->createTextNode(handler->errorProtocol());
fixed->appendChild(textNode, exceptioncode);
reportDiv->appendChild(fixed, exceptioncode);
h3 = doc->createElementNS(XHTML_NAMESPACE, "h3", exceptioncode);
h3->appendChild(doc->createTextNode("Below is a rendering of the page up to the first error."), exceptioncode);
reportDiv->appendChild(h3, exceptioncode);
root->insertBefore(reportDiv, root->firstChild(), exceptioncode);
m_doc->document()->recalcStyle( NodeImpl::Inherit );
m_doc->document()->updateRendering();
end();
}
else {
addScripts(m_doc->document());
m_scriptsIt = new QPtrListIterator<HTMLScriptElementImpl>(m_scripts);
executeScripts();
}
delete handler;
}
void XMLTokenizer::addScripts(NodeImpl *n)
{
if (n->id() == ID_SCRIPT) {
m_scripts.append(static_cast<HTMLScriptElementImpl*>(n));
}
NodeImpl *child;
for (child = n->firstChild(); child; child = child->nextSibling())
addScripts(child);
}
void XMLTokenizer::executeScripts()
{
while (m_scriptsIt->current()) {
DOMString scriptSrc = m_scriptsIt->current()->getAttribute(ATTR_SRC);
QString charset = m_scriptsIt->current()->getAttribute(ATTR_CHARSET).string();
if (scriptSrc != "" && m_doc->document()->part()) {
m_cachedScript = m_doc->document()->docLoader()->requestScript(scriptSrc, charset);
++(*m_scriptsIt);
m_cachedScript->ref(this); return;
}
else {
QString scriptCode = "";
NodeImpl *child;
for (child = m_scriptsIt->current()->firstChild(); child; child = child->nextSibling()) {
if (child->nodeType() == Node::TEXT_NODE || child->nodeType() == Node::CDATA_SECTION_NODE) {
scriptCode += static_cast<TextImpl*>(child)->data().string();
}
}
if (m_view) {
m_view->part()->executeScript(scriptCode);
}
++(*m_scriptsIt);
}
}
m_doc->document()->updateStyleSelector();
end();
}
void XMLTokenizer::notifyFinished(CachedObject *finishedObj)
{
if (finishedObj == m_cachedScript) {
DOMString scriptSource = m_cachedScript->script();
m_cachedScript->deref(this);
m_cachedScript = 0;
m_view->part()->executeScript(scriptSource.string());
executeScripts();
}
}
bool XMLTokenizer::isWaitingForScripts()
{
return m_cachedScript != 0;
}
}
#include "xml_tokenizer.moc"