diff options
Diffstat (limited to 'khtml/html/htmlparser.cpp')
-rw-r--r-- | khtml/html/htmlparser.cpp | 1731 |
1 files changed, 0 insertions, 1731 deletions
diff --git a/khtml/html/htmlparser.cpp b/khtml/html/htmlparser.cpp deleted file mode 100644 index 9da99f4b3..000000000 --- a/khtml/html/htmlparser.cpp +++ /dev/null @@ -1,1731 +0,0 @@ -/* - This file is part of the KDE libraries - - Copyright (C) 1997 Martin Jones (mjones@kde.org) - (C) 1997 Torben Weis (weis@kde.org) - (C) 1999,2001 Lars Knoll (knoll@kde.org) - (C) 2000,2001 Dirk Mueller (mueller@kde.org) - (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; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -//---------------------------------------------------------------------------- -// -// KDE HTML Widget -- HTML Parser -// #define PARSER_DEBUG - -#include "dom/dom_exception.h" - -#include "html/html_baseimpl.h" -#include "html/html_blockimpl.h" -#include "html/html_documentimpl.h" -#include "html/html_elementimpl.h" -#include "html/html_formimpl.h" -#include "html/html_headimpl.h" -#include "html/html_imageimpl.h" -#include "html/html_inlineimpl.h" -#include "html/html_listimpl.h" -#include "html/html_miscimpl.h" -#include "html/html_tableimpl.h" -#include "html/html_objectimpl.h" -#include "xml/dom_textimpl.h" -#include "xml/dom_nodeimpl.h" -#include "misc/htmlhashes.h" -#include "html/htmltokenizer.h" -#include "khtmlview.h" -#include "khtml_part.h" -#include "khtml_factory.h" -#include "css/cssproperties.h" -#include "css/cssvalues.h" -#include "css/csshelper.h" - -#include "rendering/render_object.h" - -#include "html/htmlparser.h" -#include <kdebug.h> -#include <klocale.h> - -using namespace DOM; -using namespace khtml; - -//---------------------------------------------------------------------------- - -/** - * @internal - */ -class HTMLStackElem -{ -public: - HTMLStackElem( int _id, - int _level, - DOM::NodeImpl *_node, - bool _inline, - HTMLStackElem * _next ) - : - id(_id), - level(_level), - strayTableContent(false), - m_inline(_inline), - node(_node), - next(_next) - { node->ref(); } - - ~HTMLStackElem() - { node->deref(); } - - void setNode(NodeImpl* newNode) - { - newNode->ref(); - node->deref(); - node = newNode; - } - - int id; - int level; - bool strayTableContent; - bool m_inline; - NodeImpl *node; - HTMLStackElem *next; -}; - -/** - * @internal - * - * The parser parses tokenized input into the document, building up the - * document tree. If the document is wellformed, parsing it is - * straightforward. - * Unfortunately, people can't write wellformed HTML documents, so the parser - * has to be tolerant about errors. - * - * We have to take care of the following error conditions: - * 1. The element being added is explicitly forbidden inside some outer tag. - * In this case we should close all tags up to the one, which forbids - * the element, and add it afterwards. - * 2. We are not allowed to add the element directly. It could be, that - * the person writing the document forgot some tag inbetween (or that the - * tag inbetween is optional...) This could be the case with the following - * tags: HTML HEAD BODY TBODY TR TD LI (did I forget any?) - * 3. We wan't to add a block element inside to an inline element. Close all - * inline elements up to the next higher block element. - * 4. If this doesn't help close elements, until we are allowed to add the - * element or ignore the tag. - * - */ - -KHTMLParser::KHTMLParser( KHTMLView *_parent, DocumentImpl *doc) -{ - //kdDebug( 6035 ) << "parser constructor" << endl; -#if SPEED_DEBUG > 0 - qt.start(); -#endif - - HTMLWidget = _parent; - document = doc; - - blockStack = 0; - current = 0; - - // ID_CLOSE_TAG == Num of tags - forbiddenTag = new ushort[ID_CLOSE_TAG+1]; - - reset(); -} - -KHTMLParser::KHTMLParser( DOM::DocumentFragmentImpl *i, DocumentImpl *doc ) -{ - HTMLWidget = 0; - document = doc; - - forbiddenTag = new ushort[ID_CLOSE_TAG+1]; - - blockStack = 0; - current = 0; - - reset(); - - setCurrent(i); - - inBody = true; -} - -KHTMLParser::~KHTMLParser() -{ -#if SPEED_DEBUG > 0 - kdDebug( ) << "TIME: parsing time was = " << qt.elapsed() << endl; -#endif - - freeBlock(); - - if (current) current->deref(); - - delete [] forbiddenTag; - delete isindex; -} - -void KHTMLParser::reset() -{ - setCurrent ( document ); - - freeBlock(); - - // before parsing no tags are forbidden... - memset(forbiddenTag, 0, (ID_CLOSE_TAG+1)*sizeof(ushort)); - - inBody = false; - haveFrameSet = false; - haveContent = false; - haveBody = false; - haveTitle = false; - inSelect = false; - inStrayTableContent = 0; - m_inline = false; - - form = 0; - map = 0; - end = false; - isindex = 0; - - discard_until = 0; -} - -void KHTMLParser::parseToken(Token *t) -{ - if (t->tid > 2*ID_CLOSE_TAG) - { - kdDebug( 6035 ) << "Unknown tag!! tagID = " << t->tid << endl; - return; - } - if(discard_until) { - if(t->tid == discard_until) - discard_until = 0; - - // do not skip </iframe> - if ( discard_until || current->id() + ID_CLOSE_TAG != t->tid ) - return; - } - -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "\n\n==> parser: processing token " << getTagName(t->tid) << "(" << t->tid << ")" - << " current = " << getTagName(current->id()) << "(" << current->id() << ")" << endl; - kdDebug(6035) << "inline=" << m_inline << " inBody=" << inBody << " haveFrameSet=" << haveFrameSet << " haveContent=" << haveContent << endl; -#endif - - // holy shit. apparently some sites use </br> instead of <br> - // be compatible with IE and NS - if(t->tid == ID_BR+ID_CLOSE_TAG && document->inCompatMode()) - t->tid -= ID_CLOSE_TAG; - - if(t->tid > ID_CLOSE_TAG) - { - processCloseTag(t); - return; - } - - // ignore spaces, if we're not inside a paragraph or other inline code - if( t->tid == ID_TEXT && t->text ) { - if(inBody && !skipMode() && - current->id() != ID_STYLE && current->id() != ID_TITLE && - current->id() != ID_SCRIPT && - !t->text->containsOnlyWhitespace()) haveContent = true; -#ifdef PARSER_DEBUG - kdDebug(6035) << "length="<< t->text->l << " text='" << TQConstString(t->text->s, t->text->l).string() << "'" << endl; -#endif - } - - NodeImpl *n = getElement(t); - // just to be sure, and to catch currently unimplemented stuff - if(!n) - return; - - // set attributes - if(n->isElementNode() && t->tid != ID_ISINDEX) - { - ElementImpl *e = static_cast<ElementImpl *>(n); - e->setAttributeMap(t->attrs); - - // take care of optional close tags - if(endTag[e->id()] == DOM::OPTIONAL) - popBlock(t->tid); - } - - // if this tag is forbidden inside the current context, pop - // blocks until we are allowed to add it... - while(blockStack && forbiddenTag[t->tid]) { -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "t->id: " << t->tid << " is forbidden :-( " << endl; -#endif - popOneBlock(); - } - - // sometimes flat doesn't make sense - switch(t->tid) { - case ID_SELECT: - case ID_OPTION: - t->flat = false; - } - - // the tokenizer needs the feedback for space discarding - if ( tagPriority[t->tid] == 0 ) - t->flat = true; - - if ( !insertNode(n, t->flat) ) { - // we couldn't insert the node... -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "insertNode failed current=" << current->id() << ", new=" << n->id() << "!" << endl; -#endif - if (map == n) - { -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << " --> resetting map!" << endl; -#endif - map = 0; - } - if (form == n) - { -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << " --> resetting form!" << endl; -#endif - form = 0; - } - delete n; - } -} - -static bool isTableRelatedTag(int id) -{ - return (id == ID_TR || id == ID_TD || id == ID_TABLE || id == ID_TBODY || id == ID_TFOOT || id == ID_THEAD || - id == ID_TH); -} - -bool KHTMLParser::insertNode(NodeImpl *n, bool flat) -{ - int id = n->id(); - - // let's be stupid and just try to insert it. - // this should work if the document is wellformed -#ifdef PARSER_DEBUG - NodeImpl *tmp = current; -#endif - NodeImpl *newNode = current->addChild(n); - if ( newNode ) { -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "added " << n->nodeName().string() << " to " << tmp->nodeName().string() << ", new current=" << newNode->nodeName().string() << endl; -#endif - // We allow TABLE > FORM in dtd.cpp, but do not allow the form have children in this case - if (current->id() == ID_TABLE && id == ID_FORM) { - flat = true; - static_cast<HTMLFormElementImpl*>(n)->setMalformed(true); - } - - // don't push elements without end tag on the stack - if(tagPriority[id] != 0 && !flat) { -#if SPEED_DEBUG < 2 - if(!n->attached() && HTMLWidget ) - n->attach(); -#endif - if(n->isInline()) m_inline = true; - pushBlock(id, tagPriority[id]); - setCurrent( newNode ); - } else { -#if SPEED_DEBUG < 2 - if(!n->attached() && HTMLWidget) - n->attach(); - if (n->maintainsState()) { - document->registerMaintainsState(n); - TQString state(document->nextState()); - if (!state.isNull()) n->restoreState(state); - } - n->close(); -#endif - if(n->isInline()) m_inline = true; - } - - -#if SPEED_DEBUG < 1 - if(tagPriority[id] == 0 && n->renderer()) - n->renderer()->calcMinMaxWidth(); -#endif - return true; - } else { -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "ADDING NODE FAILED!!!! current = " << current->nodeName().string() << ", new = " << n->nodeName().string() << endl; -#endif - // error handling... - HTMLElementImpl *e; - bool handled = false; - - // first switch on current element for elements with optional end-tag and inline-only content - switch(current->id()) - { - case ID_P: - case ID_DT: - if(!n->isInline()) - { - popBlock(current->id()); - return insertNode(n); - } - break; - default: - break; - } - - // switch according to the element to insert - switch(id) - { - case ID_TR: - case ID_TH: - case ID_TD: - if (inStrayTableContent && !isTableRelatedTag(current->id())) { - // pop out to the nearest enclosing table-related tag. - while (blockStack && !isTableRelatedTag(current->id())) - popOneBlock(); - return insertNode(n); - } - break; - case ID_COMMENT: - break; - case ID_HEAD: - // ### allow not having <HTML> in at all, as per HTML spec - if (!current->isDocumentNode() && current->id() != ID_HTML ) - return false; - break; - case ID_META: - case ID_LINK: - case ID_ISINDEX: - case ID_BASE: - if( !head ) - createHead(); - if( head ) { - if ( head->addChild(n) ) { -#if SPEED_DEBUG < 2 - if(!n->attached() && HTMLWidget) - n->attach(); -#endif - } - - return true; - } - - break; - case ID_HTML: - if (!current->isDocumentNode() ) { - if ( doc()->firstChild()->id() == ID_HTML) { - // we have another <HTML> element.... apply attributes to existing one - // make sure we don't overwrite already existing attributes - NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true); - NamedAttrMapImpl *bmap = static_cast<ElementImpl*>(doc()->firstChild())->attributes(false); - bool changed = false; - for (unsigned long l = 0; map && l < map->length(); ++l) { - NodeImpl::Id attrId = map->idAt(l); - DOMStringImpl *attrValue = map->valueAt(l); - changed = !bmap->getValue(attrId); - bmap->setValue(attrId,attrValue); - } - if ( changed ) - doc()->recalcStyle( NodeImpl::Inherit ); - } - return false; - } - break; - case ID_TITLE: - case ID_STYLE: - if ( !head ) - createHead(); - if ( head ) { - DOM::NodeImpl *newNode = head->addChild(n); - if ( newNode ) { - pushBlock(id, tagPriority[id]); - setCurrent ( newNode ); -#if SPEED_DEBUG < 2 - if(!n->attached() && HTMLWidget) - n->attach(); -#endif - } else { -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "adding style before to body failed!!!!" << endl; -#endif - discard_until = ID_STYLE + ID_CLOSE_TAG; - return false; - } - return true; - } else if(inBody) { - discard_until = id + ID_CLOSE_TAG; - return false; - } - break; - case ID_SCRIPT: - // if we failed to insert it, go into skip mode - discard_until = id + ID_CLOSE_TAG; - break; - case ID_BODY: - if(inBody && doc()->body()) { - // we have another <BODY> element.... apply attributes to existing one - // make sure we don't overwrite already existing attributes - // some sites use <body bgcolor=rightcolor>...<body bgcolor=wrongcolor> - NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true); - NamedAttrMapImpl *bmap = doc()->body()->attributes(false); - bool changed = false; - for (unsigned long l = 0; map && l < map->length(); ++l) { - NodeImpl::Id attrId = map->idAt(l); - DOMStringImpl *attrValue = map->valueAt(l); - if ( !bmap->getValue(attrId) ) { - bmap->setValue(attrId,attrValue); - changed = true; - } - } - if ( changed ) - doc()->recalcStyle( NodeImpl::Inherit ); - } else if ( current->isDocumentNode() ) - break; - return false; - break; - - // the following is a hack to move non rendered elements - // outside of tables. - // needed for broken constructs like <table><form ...><tr>.... - case ID_INPUT: - { - ElementImpl *e = static_cast<ElementImpl *>(n); - DOMString type = e->getAttribute(ATTR_TYPE); - - if ( strcasecmp( type, "hidden" ) != 0 ) - break; - // Fall through! - } - case ID_TEXT: - { - // Don't try to fit random white-space anywhere - TextImpl *t = static_cast<TextImpl *>(n); - if (t->containsOnlyWhitespace()) - return false; - // ignore text inside the following elements. - switch(current->id()) - { - case ID_SELECT: - return false; - default: - ; - // fall through!! - }; - break; - } - case ID_DL: - popBlock( ID_DT ); - if ( current->id() == ID_DL ) { - e = new HTMLGenericElementImpl( document, ID_DD ); - insertNode( e ); - handled = true; - } - break; - case ID_DT: - e = new HTMLDListElementImpl(document); - if ( insertNode(e) ) { - insertNode(n); - return true; - } - break; - case ID_AREA: - { - if(map) - { - map->addChild(n); -#if SPEED_DEBUG < 2 - if(!n->attached() && HTMLWidget) - n->attach(); -#endif - handled = true; - return true; - } - else - return false; - } - case ID_CAPTION: { - switch (current->id()) { - case ID_THEAD: - case ID_TBODY: - case ID_TFOOT: - case ID_TR: - case ID_TH: - case ID_TD: { - NodeImpl* tsection = current; - if (current->id() == ID_TR) - tsection = current->parent(); - else if (current->id() == ID_TD || current->id() == ID_TH) - tsection = current->parent()->parent(); - NodeImpl* table = tsection->parent(); - int exceptioncode = 0; - table->insertBefore(n, tsection, exceptioncode); - pushBlock(id, tagPriority[id]); - setCurrent(n); - inStrayTableContent++; - blockStack->strayTableContent = true; - return true; - } - default: - break; - } - break; - } - - case ID_THEAD: - case ID_TBODY: - case ID_TFOOT: - case ID_COLGROUP: { - if (isTableRelatedTag(current->id())) { - while (blockStack && current->id() != ID_TABLE && isTableRelatedTag(current->id())) - popOneBlock(); - return insertNode(n); - } - } - default: - break; - } - - // switch on the currently active element - switch(current->id()) - { - case ID_HTML: - switch(id) - { - case ID_SCRIPT: - case ID_STYLE: - case ID_META: - case ID_LINK: - case ID_OBJECT: - case ID_EMBED: - case ID_TITLE: - case ID_ISINDEX: - case ID_BASE: - if(!head) { - head = new HTMLHeadElementImpl(document); - insertNode(head.get()); - handled = true; - } - break; - case ID_TEXT: { - TextImpl *t = static_cast<TextImpl *>(n); - if (t->containsOnlyWhitespace()) - return false; - /* Fall through to default */ - } - default: - if ( haveFrameSet ) break; - e = new HTMLBodyElementImpl(document); - startBody(); - insertNode(e); - handled = true; - break; - } - break; - case ID_HEAD: - // we can get here only if the element is not allowed in head. - if (id == ID_HTML) - return false; - else { - // This means the body starts here... - if ( haveFrameSet ) break; - popBlock(ID_HEAD); - e = new HTMLBodyElementImpl(document); - startBody(); - insertNode(e); - handled = true; - } - break; - case ID_BODY: - break; - case ID_CAPTION: - // Illegal content in a caption. Close the caption and try again. - popBlock(ID_CAPTION); - switch( id ) { - case ID_THEAD: - case ID_TFOOT: - case ID_TBODY: - case ID_TR: - case ID_TD: - case ID_TH: - return insertNode(n, flat); - } - break; - case ID_TABLE: - case ID_THEAD: - case ID_TFOOT: - case ID_TBODY: - case ID_TR: - switch(id) - { - case ID_TABLE: - popBlock(ID_TABLE); // end the table - handled = checkChild( current->id(), id, !doc()->inCompatMode()); - break; - default: - { - NodeImpl *node = current; - NodeImpl *parent = node->parentNode(); - // A script may have removed the current node's parent from the DOM - // http://bugzilla.opendarwin.org/show_bug.cgi?id=7137 - // FIXME: we should do real recovery here and re-parent with the correct node. - if (!parent) - return false; - NodeImpl *parentparent = parent->parentNode(); - - if (n->isTextNode() || - ( node->id() == ID_TR && - ( parent->id() == ID_THEAD || - parent->id() == ID_TBODY || - parent->id() == ID_TFOOT ) && parentparent->id() == ID_TABLE ) || - ( !checkChild( ID_TR, id ) && ( node->id() == ID_THEAD || node->id() == ID_TBODY || node->id() == ID_TFOOT ) && - parent->id() == ID_TABLE ) ) - { - node = (node->id() == ID_TABLE) ? node : - ((node->id() == ID_TR ) ? parentparent : parent); - NodeImpl *parent = node->parentNode(); - if (!parent) - return false; - int exceptioncode = 0; -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "calling insertBefore(" << n->nodeName().string() << "," << node->nodeName().string() << ")" << endl; -#endif - parent->insertBefore(n, node, exceptioncode); - if (exceptioncode) { -#ifndef PARSER_DEBUG - if (!n->isTextNode()) -#endif - kdDebug(6035) << "adding content before table failed.." << endl; - break; - } - if ( n->isElementNode() && tagPriority[id] != 0 && - !flat && endTag[id] != DOM::FORBIDDEN ) { - - pushBlock(id, tagPriority[id]); - setCurrent ( n ); - inStrayTableContent++; - blockStack->strayTableContent = true; - } - return true; - } - - if ( current->id() == ID_TR ) - e = new HTMLTableCellElementImpl(document, ID_TD); - else if ( current->id() == ID_TABLE ) - e = new HTMLTableSectionElementImpl( document, ID_TBODY, true /* implicit */ ); - else - e = new HTMLTableRowElementImpl( document ); - - insertNode(e); - handled = true; - break; - } // end default - } // end switch - break; - case ID_OBJECT: - discard_until = id + ID_CLOSE_TAG; - return false; - case ID_UL: - case ID_OL: - case ID_DIR: - case ID_MENU: - e = new HTMLLIElementImpl(document); - e->addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_NONE); - insertNode(e); - handled = true; - break; - case ID_DL: - popBlock(ID_DL); - handled = true; - break; - case ID_DT: - popBlock(ID_DT); - handled = true; - break; - case ID_FORM: - popBlock(ID_FORM); - handled = true; - break; - case ID_SELECT: - if( n->isInline() ) - return false; - break; - case ID_P: - case ID_H1: - case ID_H2: - case ID_H3: - case ID_H4: - case ID_H5: - case ID_H6: - if(!n->isInline()) - { - popBlock(current->id()); - handled = true; - } - break; - case ID_OPTION: - case ID_OPTGROUP: - if (id == ID_OPTGROUP) - { - popBlock(current->id()); - handled = true; - } - else if(id == ID_SELECT) - { - // IE treats a nested select as </select>. Let's do the same - popBlock( ID_SELECT ); - break; - } - break; - // head elements in the body should be ignored. - - case ID_ADDRESS: - case ID_COLGROUP: - case ID_FONT: - popBlock(current->id()); - handled = true; - break; - default: - if(current->isDocumentNode()) - { - if(current->firstChild() == 0) { - e = new HTMLHtmlElementImpl(document); - insertNode(e); - handled = true; - } - } - else if(current->isInline()) - { - popInlineBlocks(); - handled = true; - } - } - - // if we couldn't handle the error, just rethrow the exception... - if(!handled) - { - //kdDebug( 6035 ) << "Exception handler failed in HTMLPArser::insertNode()" << endl; - return false; - } - - return insertNode(n); - } -} - - -NodeImpl *KHTMLParser::getElement(Token* t) -{ - NodeImpl *n = 0; - - switch(t->tid) - { - case ID_HTML: - n = new HTMLHtmlElementImpl(document); - break; - case ID_HEAD: - if(!head && current->id() == ID_HTML) { - head = new HTMLHeadElementImpl(document); - n = head.get(); - } - break; - case ID_BODY: - // body no longer allowed if we have a frameset - if(haveFrameSet) break; - popBlock(ID_HEAD); - n = new HTMLBodyElementImpl(document); - haveBody = true; - startBody(); - break; - -// head elements - case ID_BASE: - n = new HTMLBaseElementImpl(document); - break; - case ID_LINK: - n = new HTMLLinkElementImpl(document); - break; - case ID_META: - n = new HTMLMetaElementImpl(document); - break; - case ID_STYLE: - n = new HTMLStyleElementImpl(document); - break; - case ID_TITLE: - // only one non-empty <title> allowed - if (haveTitle) { - discard_until = ID_TITLE+ID_CLOSE_TAG; - break; - } - n = new HTMLTitleElementImpl(document); - // we'll set haveTitle when closing the tag - break; - -// frames - case ID_FRAME: - n = new HTMLFrameElementImpl(document); - break; - case ID_FRAMESET: - popBlock(ID_HEAD); - if ( inBody && !haveFrameSet && !haveContent && !haveBody) { - popBlock( ID_BODY ); - // ### actually for IE document.body returns the now hidden "body" element - // we can't implement that behavior now because it could cause too many - // regressions and the headaches are not worth the work as long as there is - // no site actually relying on that detail (Dirk) - if (static_cast<HTMLDocumentImpl*>(document)->body()) - static_cast<HTMLDocumentImpl*>(document)->body() - ->addCSSProperty(CSS_PROP_DISPLAY, CSS_VAL_NONE); - inBody = false; - } - if ( (haveBody || haveContent || haveFrameSet) && current->id() == ID_HTML) - break; - n = new HTMLFrameSetElementImpl(document); - haveFrameSet = true; - startBody(); - break; - // a bit a special case, since the frame is inlined... - case ID_IFRAME: - n = new HTMLIFrameElementImpl(document); - if (!t->flat) discard_until = ID_IFRAME+ID_CLOSE_TAG; - break; - -// form elements - case ID_FORM: - // thou shall not nest <form> - NS/IE quirk - if (form) break; - n = form = new HTMLFormElementImpl(document, false); - break; - case ID_BUTTON: - n = new HTMLButtonElementImpl(document, form); - break; - case ID_FIELDSET: - n = new HTMLFieldSetElementImpl(document, form); - break; - case ID_INPUT: - if ( t->attrs && - KHTMLFactory::defaultHTMLSettings()->isAdFilterEnabled() && - KHTMLFactory::defaultHTMLSettings()->isHideAdsEnabled() && - !strcasecmp( t->attrs->getValue( ATTR_TYPE ), "image" ) ) - { - if (KHTMLFactory::defaultHTMLSettings()->isAdFiltered( doc()->completeURL( khtml::parseURL(t->attrs->getValue(ATTR_SRC)).string() ) )) - return 0; - } - n = new HTMLInputElementImpl(document, form); - break; - case ID_ISINDEX: - n = handleIsindex(t); - if( !inBody ) { - isindex = n; - n = 0; - } else - t->flat = true; - break; - case ID_KEYGEN: - n = new HTMLKeygenElementImpl(document, form); - break; - case ID_LABEL: - n = new HTMLLabelElementImpl(document); - break; - case ID_LEGEND: - n = new HTMLLegendElementImpl(document, form); - break; - case ID_OPTGROUP: - n = new HTMLOptGroupElementImpl(document, form); - break; - case ID_OPTION: - n = new HTMLOptionElementImpl(document, form); - break; - case ID_SELECT: - inSelect = true; - n = new HTMLSelectElementImpl(document, form); - break; - case ID_TEXTAREA: - n = new HTMLTextAreaElementImpl(document, form); - break; - -// lists - case ID_DL: - n = new HTMLDListElementImpl(document); - break; - case ID_DD: - n = new HTMLGenericElementImpl(document, t->tid); - popBlock(ID_DT); - popBlock(ID_DD); - break; - case ID_DT: - n = new HTMLGenericElementImpl(document, t->tid); - popBlock(ID_DD); - popBlock(ID_DT); - break; - case ID_UL: - { - n = new HTMLUListElementImpl(document); - break; - } - case ID_OL: - { - n = new HTMLOListElementImpl(document); - break; - } - case ID_DIR: - n = new HTMLDirectoryElementImpl(document); - break; - case ID_MENU: - n = new HTMLMenuElementImpl(document); - break; - case ID_LI: - popBlock(ID_LI); - n = new HTMLLIElementImpl(document); - break; -// formatting elements (block) - case ID_BLOCKQUOTE: - n = new HTMLGenericElementImpl(document, t->tid); - break; - case ID_LAYER: - case ID_ILAYER: - n = new HTMLLayerElementImpl(document, t->tid); - break; - case ID_P: - case ID_DIV: - n = new HTMLDivElementImpl(document, t->tid); - break; - case ID_H1: - case ID_H2: - case ID_H3: - case ID_H4: - case ID_H5: - case ID_H6: - n = new HTMLGenericElementImpl(document, t->tid); - break; - case ID_HR: - n = new HTMLHRElementImpl(document); - break; - case ID_PRE: - case ID_XMP: - case ID_PLAINTEXT: - n = new HTMLPreElementImpl(document, t->tid); - break; - -// font stuff - case ID_BASEFONT: - n = new HTMLBaseFontElementImpl(document); - break; - case ID_FONT: - n = new HTMLFontElementImpl(document); - break; - -// ins/del - case ID_DEL: - case ID_INS: - n = new HTMLGenericElementImpl(document, t->tid); - break; - -// anchor - case ID_A: - popBlock(ID_A); - - n = new HTMLAnchorElementImpl(document); - break; - -// images - case ID_IMG: - if (t->attrs&& - KHTMLFactory::defaultHTMLSettings()->isAdFilterEnabled()&& - KHTMLFactory::defaultHTMLSettings()->isHideAdsEnabled()) - { - TQString url = doc()->completeURL( khtml::parseURL(t->attrs->getValue(ATTR_SRC)).string() ); - if (KHTMLFactory::defaultHTMLSettings()->isAdFiltered(url)) - return 0; - } - n = new HTMLImageElementImpl(document, form); - break; - - case ID_MAP: - map = new HTMLMapElementImpl(document); - n = map; - break; - case ID_AREA: - n = new HTMLAreaElementImpl(document); - break; - -// objects, applets and scripts - case ID_APPLET: - n = new HTMLAppletElementImpl(document); - break; - case ID_EMBED: - n = new HTMLEmbedElementImpl(document); - break; - case ID_OBJECT: - n = new HTMLObjectElementImpl(document); - break; - case ID_PARAM: - n = new HTMLParamElementImpl(document); - break; - case ID_SCRIPT: - { - HTMLScriptElementImpl *scriptElement = new HTMLScriptElementImpl(document); - scriptElement->setCreatedByParser(true); - n = scriptElement; - break; - } - -// tables - case ID_TABLE: - n = new HTMLTableElementImpl(document); - break; - case ID_CAPTION: - n = new HTMLTableCaptionElementImpl(document); - break; - case ID_COLGROUP: - case ID_COL: - n = new HTMLTableColElementImpl(document, t->tid); - break; - case ID_TR: - popBlock(ID_TR); - n = new HTMLTableRowElementImpl(document); - break; - case ID_TD: - case ID_TH: - popBlock(ID_TH); - popBlock(ID_TD); - n = new HTMLTableCellElementImpl(document, t->tid); - break; - case ID_TBODY: - case ID_THEAD: - case ID_TFOOT: - popBlock( ID_THEAD ); - popBlock( ID_TBODY ); - popBlock( ID_TFOOT ); - n = new HTMLTableSectionElementImpl(document, t->tid, false); - break; - -// inline elements - case ID_BR: - n = new HTMLBRElementImpl(document); - break; - case ID_Q: - n = new HTMLGenericElementImpl(document, t->tid); - break; - -// elements with no special representation in the DOM - -// block: - case ID_ADDRESS: - case ID_CENTER: - n = new HTMLGenericElementImpl(document, t->tid); - break; -// inline - // %fontstyle - case ID_TT: - case ID_U: - case ID_B: - case ID_I: - case ID_S: - case ID_STRIKE: - case ID_BIG: - case ID_SMALL: - - // %phrase - case ID_EM: - case ID_STRONG: - case ID_DFN: - case ID_CODE: - case ID_SAMP: - case ID_KBD: - case ID_VAR: - case ID_CITE: - case ID_ABBR: - case ID_ACRONYM: - - // %special - case ID_SUB: - case ID_SUP: - case ID_SPAN: - case ID_WBR: - case ID_NOBR: - if ( t->tid == ID_NOBR || t->tid == ID_WBR ) - popBlock( t->tid ); - case ID_BDO: - n = new HTMLGenericElementImpl(document, t->tid); - break; - - // these are special, and normally not rendered - case ID_NOEMBED: - if (!t->flat) { - n = new HTMLGenericElementImpl(document, t->tid); - discard_until = ID_NOEMBED + ID_CLOSE_TAG; - } - return n; - case ID_NOFRAMES: - if (!t->flat) { - n = new HTMLGenericElementImpl(document, t->tid); - discard_until = ID_NOFRAMES + ID_CLOSE_TAG; - } - return n; - case ID_NOSCRIPT: - if (!t->flat) { - n = new HTMLGenericElementImpl(document, t->tid); - if(HTMLWidget && HTMLWidget->part()->jScriptEnabled()) - discard_until = ID_NOSCRIPT + ID_CLOSE_TAG; - } - return n; - case ID_NOLAYER: -// discard_until = ID_NOLAYER + ID_CLOSE_TAG; - return 0; - break; - case ID_MARQUEE: - n = new HTMLMarqueeElementImpl(document); - break; -// text - case ID_TEXT: -// kdDebug(6035) << "ID_TEXT: \"" << DOMString(t->text).string() << "\"" << endl; - n = new TextImpl(document, t->text); - break; - case ID_COMMENT: -#ifdef COMMENTS_IN_DOM - n = new CommentImpl(document, t->text); -#endif - break; - default: - kdDebug( 6035 ) << "Unknown tag " << t->tid << "!" << endl; - } - return n; -} - -void KHTMLParser::processCloseTag(Token *t) -{ - // support for really broken html. Can't believe I'm supporting such crap (lars) - switch(t->tid) - { - case ID_HTML+ID_CLOSE_TAG: - case ID_BODY+ID_CLOSE_TAG: - // we never trust those close tags, since stupid webpages close - // them prematurely - return; - case ID_FORM+ID_CLOSE_TAG: - form = 0; - // this one is to get the right style on the body element - break; - case ID_MAP+ID_CLOSE_TAG: - map = 0; - break; - case ID_SELECT+ID_CLOSE_TAG: - inSelect = false; - break; - case ID_TITLE+ID_CLOSE_TAG: - // Set haveTitle only if <title> isn't empty - if ( current->firstChild() ) - haveTitle = true; - break; - default: - break; - } - -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "added the following children to " << current->nodeName().string() << endl; - NodeImpl *child = current->firstChild(); - while(child != 0) - { - kdDebug( 6035 ) << " " << child->nodeName().string() << endl; - child = child->nextSibling(); - } -#endif - popBlock(t->tid-ID_CLOSE_TAG); -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "closeTag --> current = " << current->nodeName().string() << endl; -#endif -} - -bool KHTMLParser::isResidualStyleTag(int _id) -{ - switch (_id) { - case ID_A: - case ID_FONT: - case ID_TT: - case ID_U: - case ID_B: - case ID_I: - case ID_S: - case ID_STRIKE: - case ID_BIG: - case ID_SMALL: - case ID_EM: - case ID_STRONG: - case ID_DFN: - case ID_CODE: - case ID_SAMP: - case ID_KBD: - case ID_VAR: - case ID_DEL: - case ID_INS: - case ID_WBR: - case ID_NOBR: - return true; - default: - return false; - } -} - -bool KHTMLParser::isAffectedByResidualStyle(int _id) -{ - if (isResidualStyleTag(_id)) - return true; - - switch (_id) { - case ID_P: - case ID_DIV: - case ID_BLOCKQUOTE: - case ID_ADDRESS: - case ID_H1: - case ID_H2: - case ID_H3: - case ID_H4: - case ID_H5: - case ID_H6: - case ID_CENTER: - case ID_UL: - case ID_OL: - case ID_LI: - case ID_DL: - case ID_DT: - case ID_DD: - case ID_PRE: - return true; - default: - return false; - } -} - -void KHTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem) -{ - // Find the element that crosses over to a higher level. - // ### For now, if there is more than one, we will only make sure we close the residual style. - int exceptionCode = 0; - HTMLStackElem* curr = blockStack; - HTMLStackElem* maxElem = 0; - HTMLStackElem* endElem = 0; - HTMLStackElem* prev = 0; - HTMLStackElem* prevMaxElem = 0; - bool advancedResidual = false; // ### if set we only close the residual style - while (curr && curr != elem) { - if (curr->level > elem->level) { - if (!isAffectedByResidualStyle(curr->id)) return; - if (maxElem) advancedResidual = true; - else - endElem = curr; - maxElem = curr; - prevMaxElem = prev; - } - - prev = curr; - curr = curr->next; - } - - if (!curr || !maxElem ) return; - - NodeImpl* residualElem = prev->node; - NodeImpl* blockElem = prevMaxElem ? prevMaxElem->node : current; - NodeImpl* parentElem = elem->node; - - // Check to see if the reparenting that is going to occur is allowed according to the DOM. - // FIXME: We should either always allow it or perform an additional fixup instead of - // just bailing here. - // Example: <p><font><center>blah</font></center></p> isn't doing a fixup right now. - if (!parentElem->childAllowed(blockElem)) - return; - - if (maxElem->node->parentNode() != elem->node && !advancedResidual) { - // Walk the stack and remove any elements that aren't residual style tags. These - // are basically just being closed up. Example: - // <font><span>Moo<p>Goo</font></p>. - // In the above example, the <span> doesn't need to be reopened. It can just close. - HTMLStackElem* currElem = maxElem->next; - HTMLStackElem* prevElem = maxElem; - while (currElem != elem) { - HTMLStackElem* nextElem = currElem->next; - if (!isResidualStyleTag(currElem->id)) { - prevElem->next = nextElem; - prevElem->setNode(currElem->node); - delete currElem; - } - else - prevElem = currElem; - currElem = nextElem; - } - - // We have to reopen residual tags in between maxElem and elem. An example of this case s: - // <font><i>Moo<p>Foo</font>. - // In this case, we need to transform the part before the <p> into: - // <font><i>Moo</i></font><i> - // so that the <i> will remain open. This involves the modification of elements - // in the block stack. - // This will also affect how we ultimately reparent the block, since we want it to end up - // under the reopened residual tags (e.g., the <i> in the above example.) - NodeImpl* prevNode = 0; - NodeImpl* currNode = 0; - currElem = maxElem; - while (currElem->node != residualElem) { - if (isResidualStyleTag(currElem->node->id())) { - // Create a clone of this element. - currNode = currElem->node->cloneNode(false); - currElem->node->close(); - removeForbidden(currElem->id, forbiddenTag); - - // Change the stack element's node to point to the clone. - currElem->setNode(currNode); - - // Attach the previous node as a child of this new node. - if (prevNode) - currNode->appendChild(prevNode, exceptionCode); - else // The new parent for the block element is going to be the innermost clone. - parentElem = currNode; - - prevNode = currNode; - } - - currElem = currElem->next; - } - - // Now append the chain of new residual style elements if one exists. - if (prevNode) - elem->node->appendChild(prevNode, exceptionCode); - } - - // We need to make a clone of |residualElem| and place it just inside |blockElem|. - // All content of |blockElem| is reparented to be under this clone. We then - // reparent |blockElem| using real DOM calls so that attachment/detachment will - // be performed to fix up the rendering tree. - // So for this example: <b>...<p>Foo</b>Goo</p> - // The end result will be: <b>...</b><p><b>Foo</b>Goo</p> - // - // Step 1: Remove |blockElem| from its parent, doing a batch detach of all the kids. - SharedPtr<NodeImpl> guard(blockElem); - blockElem->parentNode()->removeChild(blockElem, exceptionCode); - - if (!advancedResidual) { - // Step 2: Clone |residualElem|. - NodeImpl* newNode = residualElem->cloneNode(false); // Shallow clone. We don't pick up the same kids. - - // Step 3: Place |blockElem|'s children under |newNode|. Remove all of the children of |blockElem| - // before we've put |newElem| into the document. That way we'll only do one attachment of all - // the new content (instead of a bunch of individual attachments). - NodeImpl* currNode = blockElem->firstChild(); - while (currNode) { - NodeImpl* nextNode = currNode->nextSibling(); - SharedPtr<NodeImpl> guard(currNode); //Protect from deletion while moving - blockElem->removeChild(currNode, exceptionCode); - newNode->appendChild(currNode, exceptionCode); - currNode = nextNode; - - // TODO - To be replaced. - // Re-register form elements with currently active form, step 1 will have removed them - if (form && currNode && currNode->isGenericFormElement()) - { - HTMLGenericFormElementImpl *e = static_cast<HTMLGenericFormElementImpl *>(currNode); - form->registerFormElement(e); - } - } - - // Step 4: Place |newNode| under |blockElem|. |blockElem| is still out of the document, so no - // attachment can occur yet. - blockElem->appendChild(newNode, exceptionCode); - } - - // Step 5: Reparent |blockElem|. Now the full attachment of the fixed up tree takes place. - parentElem->appendChild(blockElem, exceptionCode); - - // Step 6: Elide |elem|, since it is effectively no longer open. Also update - // the node associated with the previous stack element so that when it gets popped, - // it doesn't make the residual element the next current node. - HTMLStackElem* currElem = maxElem; - HTMLStackElem* prevElem = 0; - while (currElem != elem) { - prevElem = currElem; - currElem = currElem->next; - } - prevElem->next = elem->next; - prevElem->setNode(elem->node); - delete elem; - - // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>. - // In the above example, Goo should stay italic. - curr = blockStack; - HTMLStackElem* residualStyleStack = 0; - while (curr && curr != endElem) { - // We will actually schedule this tag for reopening - // after we complete the close of this entire block. - NodeImpl* currNode = current; - if (isResidualStyleTag(curr->id)) { - // We've overloaded the use of stack elements and are just reusing the - // struct with a slightly different meaning to the variables. Instead of chaining - // from innermost to outermost, we build up a list of all the tags we need to reopen - // from the outermost to the innermost, i.e., residualStyleStack will end up pointing - // to the outermost tag we need to reopen. - // We also set curr->node to be the actual element that corresponds to the ID stored in - // curr->id rather than the node that you should pop to when the element gets pulled off - // the stack. - popOneBlock(false); - curr->setNode(currNode); - curr->next = residualStyleStack; - residualStyleStack = curr; - } - else - popOneBlock(); - - curr = blockStack; - } - - reopenResidualStyleTags(residualStyleStack, 0); // FIXME: Deal with stray table content some day - // if it becomes necessary to do so. -} - -void KHTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, DOM::NodeImpl* malformedTableParent) -{ - // Loop for each tag that needs to be reopened. - while (elem) { - // Create a shallow clone of the DOM node for this element. - NodeImpl* newNode = elem->node->cloneNode(false); - - // Append the new node. In the malformed table case, we need to insert before the table, - // which will be the last child. - int exceptionCode = 0; - if (malformedTableParent) - malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), exceptionCode); - else - current->appendChild(newNode, exceptionCode); - // FIXME: Is it really OK to ignore the exceptions here? - - // Now push a new stack element for this node we just created. - pushBlock(elem->id, elem->level); - - // Set our strayTableContent boolean if needed, so that the reopened tag also knows - // that it is inside a malformed table. - blockStack->strayTableContent = malformedTableParent != 0; - if (blockStack->strayTableContent) - inStrayTableContent++; - - // Clear our malformed table parent variable. - malformedTableParent = 0; - - // Update |current| manually to point to the new node. - setCurrent(newNode); - - // Advance to the next tag that needs to be reopened. - HTMLStackElem* next = elem->next; - delete elem; - elem = next; - } -} - -void KHTMLParser::pushBlock(int _id, int _level) -{ - HTMLStackElem *Elem = new HTMLStackElem(_id, _level, current, m_inline, blockStack); - - blockStack = Elem; - addForbidden(_id, forbiddenTag); -} - -void KHTMLParser::popBlock( int _id ) -{ - HTMLStackElem *Elem = blockStack; - int maxLevel = 0; - -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "popBlock(" << getTagName(_id) << ")" << endl; - while(Elem) { - kdDebug( 6035) << " > " << getTagName(Elem->id) << endl; - Elem = Elem->next; - } - Elem = blockStack; -#endif - - while( Elem && (Elem->id != _id)) - { - if (maxLevel < Elem->level) - { - maxLevel = Elem->level; - } - Elem = Elem->next; - } - if (!Elem) - return; - - if (maxLevel > Elem->level) { - // We didn't match because the tag is in a different scope, e.g., - // <b><p>Foo</b>. Try to correct the problem. - if (!isResidualStyleTag(_id)) - return; - return handleResidualStyleCloseTagAcrossBlocks(Elem); - } - - bool isAffectedByStyle = isAffectedByResidualStyle(Elem->id); - HTMLStackElem* residualStyleStack = 0; - NodeImpl* malformedTableParent = 0; - - Elem = blockStack; - - while (Elem) - { - if (Elem->id == _id) - { - int strayTable = inStrayTableContent; - popOneBlock(); - Elem = 0; - - // This element was the root of some malformed content just inside an implicit or - // explicit <tbody> or <tr>. - // If we end up needing to reopen residual style tags, the root of the reopened chain - // must also know that it is the root of malformed content inside a <tbody>/<tr>. - if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) { - NodeImpl* curr = current; - while (curr && curr->id() != ID_TABLE) - curr = curr->parentNode(); - malformedTableParent = curr ? curr->parentNode() : 0; - } - } - else - { - // Schedule this tag for reopening - // after we complete the close of this entire block. - NodeImpl* currNode = current; - if (isAffectedByStyle && isResidualStyleTag(Elem->id)) { - // We've overloaded the use of stack elements and are just reusing the - // struct with a slightly different meaning to the variables. Instead of chaining - // from innermost to outermost, we build up a list of all the tags we need to reopen - // from the outermost to the innermost, i.e., residualStyleStack will end up pointing - // to the outermost tag we need to reopen. - // We also set Elem->node to be the actual element that corresponds to the ID stored in - // Elem->id rather than the node that you should pop to when the element gets pulled off - // the stack. - popOneBlock(false); - Elem->next = residualStyleStack; - Elem->setNode(currNode); - residualStyleStack = Elem; - } - else - popOneBlock(); - Elem = blockStack; - } - } - - reopenResidualStyleTags(residualStyleStack, malformedTableParent); -} - -void KHTMLParser::popOneBlock(bool delBlock) -{ - HTMLStackElem *Elem = blockStack; - - // we should never get here, but some bad html might cause it. -#ifndef PARSER_DEBUG - if(!Elem) return; -#else - kdDebug( 6035 ) << "popping block: " << getTagName(Elem->id) << "(" << Elem->id << ")" << endl; -#endif - -#if SPEED_DEBUG < 1 - if((Elem->node != current)) { - if (current->maintainsState() && document){ - document->registerMaintainsState(current); - TQString state(document->nextState()); - if (!state.isNull()) current->restoreState(state); - } - current->close(); - } -#endif - - removeForbidden(Elem->id, forbiddenTag); - - blockStack = Elem->next; - // we only set inline to false, if the element we close is a block level element. - // This helps getting cases as <p><b>bla</b> <b>bla</b> right. - - m_inline = Elem->m_inline; - - if (current->id() == ID_FORM && form && inStrayTableContent) - form->setMalformed(true); - - setCurrent( Elem->node ); - - if (Elem->strayTableContent) - inStrayTableContent--; - - if (delBlock) - delete Elem; -} - -void KHTMLParser::popInlineBlocks() -{ - while(blockStack && current->isInline() && current->id() != ID_FONT) - popOneBlock(); -} - -void KHTMLParser::freeBlock() -{ - while (blockStack) - popOneBlock(); - blockStack = 0; -} - -void KHTMLParser::createHead() -{ - if(head || !doc()->firstChild()) - return; - - head = new HTMLHeadElementImpl(document); - HTMLElementImpl *body = doc()->body(); - int exceptioncode = 0; - doc()->firstChild()->insertBefore(head.get(), body, exceptioncode); - if ( exceptioncode ) { -#ifdef PARSER_DEBUG - kdDebug( 6035 ) << "creation of head failed!!!!" << endl; -#endif - delete head.get(); - head = 0; - } -} - -NodeImpl *KHTMLParser::handleIsindex( Token *t ) -{ - NodeImpl *n; - HTMLFormElementImpl *myform = form; - if ( !myform ) { - myform = new HTMLFormElementImpl(document, true); - n = myform; - } else - n = new HTMLDivElementImpl( document, ID_DIV ); - NodeImpl *child = new HTMLHRElementImpl( document ); - n->addChild( child ); - DOMStringImpl* a = t->attrs ? t->attrs->getValue(ATTR_PROMPT) : 0; - DOMString text = i18n("This is a searchable index. Enter search keywords: "); - if (a) - text = a; - child = new TextImpl(document, text.implementation()); - n->addChild( child ); - child = new HTMLIsIndexElementImpl(document, myform); - static_cast<ElementImpl *>(child)->setAttribute(ATTR_TYPE, "khtml_isindex"); - n->addChild( child ); - child = new HTMLHRElementImpl( document ); - n->addChild( child ); - - return n; -} - -void KHTMLParser::startBody() -{ - if(inBody) return; - - inBody = true; - - if( isindex ) { - insertNode( isindex, true /* don't decend into this node */ ); - isindex = 0; - } -} |