Index: trunk/kdelibs/khtml/xml/dom_docimpl.cpp
===================================================================
--- trunk/kdelibs/khtml/xml/dom_docimpl.cpp (revision 121402)
+++ trunk/kdelibs/khtml/xml/dom_docimpl.cpp (revision 121403)
@@ -1,1610 +1,1612 @@
/**
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* $Id$
*/
#include "dom_docimpl.h"
#include "dom_node.h"
#include "dom_elementimpl.h"
#include "dom_textimpl.h"
#include "dom_exception.h"
#include "dom_xmlimpl.h"
#include "dom2_rangeimpl.h"
#include "dom2_traversalimpl.h"
#include "dom2_viewsimpl.h"
#include "dom2_eventsimpl.h"
#include "css/cssstyleselector.h"
#include "css/css_stylesheetimpl.h"
#include "misc/helper.h"
#include "css/csshelper.h"
#include "ecma/kjs_proxy.h"
#include
#include
#include
#include
#include "misc/htmlhashes.h"
#include "misc/loader.h"
#include
#include "htmltokenizer.h"
#include "xml_tokenizer.h"
#include "rendering/render_object.h"
#include "rendering/render_root.h"
#include "rendering/render_style.h"
#include "khtmlview.h"
#include "khtml_part.h"
#include
#include
#include
#include "khtml_settings.h"
#include "html_baseimpl.h"
#include "html_blockimpl.h"
#include "html_documentimpl.h"
#include "html_elementimpl.h"
#include "html_formimpl.h"
#include "html_headimpl.h"
#include "html_imageimpl.h"
#include "html_inlineimpl.h"
#include "html_listimpl.h"
#include "html_miscimpl.h"
#include "html_tableimpl.h"
#include "html_objectimpl.h"
using namespace DOM;
using namespace khtml;
//template class QStack; // needed ?
DOMImplementationImpl::DOMImplementationImpl()
{
}
DOMImplementationImpl::~DOMImplementationImpl()
{
}
bool DOMImplementationImpl::hasFeature ( const DOMString &feature, const DOMString &version )
{
QString lower = feature.string().lower();
if ((lower == "html" || lower == "xml") &&
(version == "1.0" || version == "" || version.isNull()))
return true;
else
return false;
}
CSSStyleSheetImpl *DOMImplementationImpl::createCSSStyleSheet(DOMStringImpl */*title*/, DOMStringImpl */*media*/)
{
return 0; // ###
}
// ------------------------------------------------------------------------
// KHTMLView might be 0
DocumentImpl::DocumentImpl(KHTMLView *v)
: NodeBaseImpl( new DocumentPtr() )
{
document->doc = this;
m_styleSelector = 0;
m_paintDeviceMetrics = 0;
m_view = v;
if ( v ) {
m_docLoader = new DocLoader(v->part(), this );
setPaintDevice( m_view );
}
else
m_docLoader = new DocLoader( 0, this );
visuallyOrdered = false;
m_loadingSheet = false;
m_sheet = 0;
m_elemSheet = 0;
m_tokenizer = 0;
m_doctype = new DocumentTypeImpl( docPtr() );
m_doctype->ref();
m_implementation = new DOMImplementationImpl();
m_implementation->ref();
pMode = Strict;
m_textColor = "#000000";
m_elementNames = 0;
m_elementNameAlloc = 0;
m_elementNameCount = 0;
m_focusNode = 0;
m_defaultView = new AbstractViewImpl(this);
m_defaultView->ref();
m_listenerTypes = 0;
m_styleSheets = new StyleSheetListImpl;
m_styleSheets->ref();
}
DocumentImpl::~DocumentImpl()
{
document->doc = 0;
delete m_sheet;
delete m_styleSelector;
delete m_docLoader;
if (m_elemSheet ) m_elemSheet->deref();
delete m_tokenizer;
m_doctype->deref();
m_implementation->deref();
delete m_paintDeviceMetrics;
if (m_elementNames) {
unsigned short id;
for (id = 0; id < m_elementNameCount; id++) {
m_elementNames[id]->deref();
}
delete [] m_elementNames;
}
m_defaultView->deref();
m_styleSheets->deref();
}
const DOMString DocumentImpl::nodeName() const
{
return "#document";
}
unsigned short DocumentImpl::nodeType() const
{
return Node::DOCUMENT_NODE;
}
ElementImpl *DocumentImpl::documentElement() const
{
NodeImpl *n = firstChild();
while (n && n->nodeType() != Node::ELEMENT_NODE)
n = n->nextSibling();
return static_cast(n);
}
ElementImpl *DocumentImpl::createElement( const DOMString &name )
{
return new XMLElementImpl( document, name.implementation() );
}
ElementImpl *DocumentImpl::createElementNS ( const DOMString &_namespaceURI, const DOMString &_qualifiedName )
{
ElementImpl *e = 0;
// ### somehow set the namespace for html elements to http://www.w3.org/1999/xhtml ?
if (_namespaceURI == "http://www.w3.org/1999/xhtml") {
QString qName = _qualifiedName.string();
int colonPos = qName.find(':',0);
e = createHTMLElement(colonPos ? qName.mid(colonPos+1) : qName);
}
if (!e)
e = new XMLElementImpl( document, _qualifiedName.implementation(), _namespaceURI.implementation() );
return e;
}
ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name )
{
uint id = khtml::getTagID( name.string().lower().latin1(), name.string().length() );
ElementImpl *n = 0;
switch(id)
{
case ID_HTML:
n = new HTMLHtmlElementImpl(docPtr());
break;
case ID_HEAD:
n = new HTMLHeadElementImpl(docPtr());
break;
case ID_BODY:
n = new HTMLBodyElementImpl(docPtr());
break;
// head elements
case ID_BASE:
n = new HTMLBaseElementImpl(docPtr());
break;
case ID_LINK:
n = new HTMLLinkElementImpl(docPtr());
break;
case ID_META:
n = new HTMLMetaElementImpl(docPtr());
break;
case ID_STYLE:
n = new HTMLStyleElementImpl(docPtr());
break;
case ID_TITLE:
n = new HTMLTitleElementImpl(docPtr());
break;
// frames
case ID_FRAME:
n = new HTMLFrameElementImpl(docPtr());
break;
case ID_FRAMESET:
n = new HTMLFrameSetElementImpl(docPtr());
break;
case ID_IFRAME:
n = new HTMLIFrameElementImpl(docPtr());
break;
// form elements
// ### FIXME: we need a way to set form dependency after we have made the form elements
case ID_FORM:
n = new HTMLFormElementImpl(docPtr());
break;
case ID_BUTTON:
n = new HTMLButtonElementImpl(docPtr());
break;
case ID_FIELDSET:
n = new HTMLFieldSetElementImpl(docPtr());
break;
case ID_INPUT:
n = new HTMLInputElementImpl(docPtr());
break;
case ID_ISINDEX:
n = new HTMLIsIndexElementImpl(docPtr());
break;
case ID_LABEL:
n = new HTMLLabelElementImpl(docPtr());
break;
case ID_LEGEND:
n = new HTMLLegendElementImpl(docPtr());
break;
case ID_OPTGROUP:
n = new HTMLOptGroupElementImpl(docPtr());
break;
case ID_OPTION:
n = new HTMLOptionElementImpl(docPtr());
break;
case ID_SELECT:
n = new HTMLSelectElementImpl(docPtr());
break;
case ID_TEXTAREA:
n = new HTMLTextAreaElementImpl(docPtr());
break;
// lists
case ID_DL:
n = new HTMLDListElementImpl(docPtr());
break;
case ID_DD:
n = new HTMLGenericElementImpl(docPtr(), id);
break;
case ID_DT:
n = new HTMLGenericElementImpl(docPtr(), id);
break;
case ID_UL:
n = new HTMLUListElementImpl(docPtr());
break;
case ID_OL:
n = new HTMLOListElementImpl(docPtr());
break;
case ID_DIR:
n = new HTMLDirectoryElementImpl(docPtr());
break;
case ID_MENU:
n = new HTMLMenuElementImpl(docPtr());
break;
case ID_LI:
n = new HTMLLIElementImpl(docPtr());
break;
// formatting elements (block)
case ID_BLOCKQUOTE:
n = new HTMLBlockquoteElementImpl(docPtr());
break;
case ID_DIV:
n = new HTMLDivElementImpl(docPtr());
break;
case ID_H1:
case ID_H2:
case ID_H3:
case ID_H4:
case ID_H5:
case ID_H6:
n = new HTMLHeadingElementImpl(docPtr(), id);
break;
case ID_HR:
n = new HTMLHRElementImpl(docPtr());
break;
case ID_P:
n = new HTMLParagraphElementImpl(docPtr());
break;
case ID_PRE:
n = new HTMLPreElementImpl(docPtr());
break;
// font stuff
case ID_BASEFONT:
n = new HTMLBaseFontElementImpl(docPtr());
break;
case ID_FONT:
n = new HTMLFontElementImpl(docPtr());
break;
// ins/del
case ID_DEL:
case ID_INS:
n = new HTMLModElementImpl(docPtr(), id);
break;
// anchor
case ID_A:
n = new HTMLAnchorElementImpl(docPtr());
break;
// images
case ID_IMG:
n = new HTMLImageElementImpl(docPtr());
break;
case ID_MAP:
n = new HTMLMapElementImpl(docPtr());
/*n = map;*/
break;
case ID_AREA:
n = new HTMLAreaElementImpl(docPtr());
break;
// objects, applets and scripts
case ID_APPLET:
n = new HTMLAppletElementImpl(docPtr());
break;
case ID_OBJECT:
n = new HTMLObjectElementImpl(docPtr());
break;
case ID_PARAM:
n = new HTMLParamElementImpl(docPtr());
break;
case ID_SCRIPT:
n = new HTMLScriptElementImpl(docPtr());
break;
// tables
case ID_TABLE:
n = new HTMLTableElementImpl(docPtr());
break;
case ID_CAPTION:
n = new HTMLTableCaptionElementImpl(docPtr());
break;
case ID_COLGROUP:
case ID_COL:
n = new HTMLTableColElementImpl(docPtr(), id);
break;
case ID_TR:
n = new HTMLTableRowElementImpl(docPtr());
break;
case ID_TD:
case ID_TH:
n = new HTMLTableCellElementImpl(docPtr(), id);
break;
case ID_THEAD:
case ID_TBODY:
case ID_TFOOT:
n = new HTMLTableSectionElementImpl(docPtr(), id);
break;
// inline elements
case ID_BR:
n = new HTMLBRElementImpl(docPtr());
break;
case ID_Q:
n = new HTMLQuoteElementImpl(docPtr());
break;
// elements with no special representation in the DOM
// block:
case ID_ADDRESS:
case ID_CENTER:
n = new HTMLGenericElementImpl(docPtr(), id);
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_NOBR:
+ case ID_WBR:
n = new HTMLGenericElementImpl(docPtr(), id);
break;
case ID_BDO:
break;
// text
case ID_TEXT:
kdDebug( 6020 ) << "Use document->createTextNode()" << endl;
break;
default:
break;
}
return n;
}
// Used to maintain list of all forms in document
QString DocumentImpl::registerElement(ElementImpl *e)
{
m_registeredElements.append(e);
QString state;
if (!m_state.isEmpty())
{
state = m_state.first();
m_state.remove(m_state.begin());
}
return state;
}
// Used to maintain list of all forms in document
void DocumentImpl::removeElement(ElementImpl *e)
{
m_registeredElements.removeRef(e);
}
QStringList DocumentImpl::state()
{
QStringList s;
for( ElementImpl *e = m_registeredElements.first();
e; e = m_registeredElements.next())
{
s.append(e->state());
}
return s;
}
RangeImpl *DocumentImpl::createRange()
{
return new RangeImpl( docPtr() );
}
NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow,
NodeFilter filter, bool entityReferenceExpansion,
int &exceptioncode)
{
if (!root) {
exceptioncode = DOMException::NOT_SUPPORTED_ERR;
return 0;
}
return new NodeIteratorImpl(root,whatToShow,filter,entityReferenceExpansion);
}
TreeWalkerImpl *DocumentImpl::createTreeWalker(Node /*root*/, unsigned long /*whatToShow*/, NodeFilter /*filter*/,
bool /*entityReferenceExpansion*/)
{
return new TreeWalkerImpl;
}
void DocumentImpl::applyChanges(bool,bool force)
{
if ( !m_render && attached() ) return;
// ### move the following two lines to createSelector????
delete m_styleSelector;
m_styleSelector = 0;
m_styleSelector = new CSSStyleSelector(this);
if(!m_render) return;
recalcStyle();
// a style change can influence the children, so we just go
// through them and trigger an appplyChanges there too
NodeImpl *n = _first;
while(n) {
n->applyChanges(false,force || changed());
n = n->nextSibling();
}
// force a relayout of this part of the document
m_render->layout();
// force a repaint of this part.
// ### if updateSize() changes any size, it will already force a
// repaint, so we might do double work here...
m_render->repaint();
setChanged(false);
}
DocumentTypeImpl *DocumentImpl::doctype() const
{
return m_doctype;
}
DOMImplementationImpl *DocumentImpl::implementation() const
{
return m_implementation;
}
void DocumentImpl::setChanged(bool b)
{
if (b)
changedNodes.append(this);
NodeBaseImpl::setChanged(b);
}
void DocumentImpl::recalcStyle()
{
QTime qt;
qt.start();
if( !m_render ) return;
setStyle(new RenderStyle());
m_style->setDisplay(BLOCK);
m_style->setVisuallyOrdered( visuallyOrdered );
// ### make the font stuff _really_ work!!!!
QFont f = KGlobalSettings::generalFont();
if (m_view)
{
const KHTMLSettings *settings = m_view->part()->settings();
f.setFamily(settings->stdFontName());
QValueList fs = settings->fontSizes();
float dpiY = 72.; // fallback
if ( !khtml::printpainter )
dpiY = paintDeviceMetrics()->logicalDpiY();
if ( !khtml::printpainter && dpiY < 96 )
dpiY = 96.;
float size = fs[3] * dpiY / 72.;
if(size < settings->minFontSize())
size = settings->minFontSize();
khtml::setFontSize( f, int(size), settings, paintDeviceMetrics() );
#if QT_VERSION < 300
KGlobal::charsets()->setQFont(f, settings->charset());
#endif
}
//kdDebug() << "DocumentImpl::attach: setting to charset " << settings->charset() << endl;
m_style->setFont(f);
if ( parseMode() != Strict )
m_style->setHtmlHacks(true); // enable html specific rendering tricks
if(m_render)
m_render->setStyle(m_style);
NodeImpl *n;
for (n = _first; n; n = n->nextSibling())
n->recalcStyle();
//kdDebug( 6020 ) << "TIME: recalcStyle() dt=" << qt.elapsed() << endl;
}
// ------------------------------------------------------------------------
DocumentFragmentImpl *DocumentImpl::createDocumentFragment( )
{
return new DocumentFragmentImpl( docPtr() );
}
TextImpl *DocumentImpl::createTextNode( const DOMString &data )
{
return new TextImpl( docPtr(), data);
}
CommentImpl *DocumentImpl::createComment ( const DOMString &data )
{
return new CommentImpl( docPtr(), data );
}
CDATASectionImpl *DocumentImpl::createCDATASection ( const DOMString &data )
{
return new CDATASectionImpl( docPtr(), data );
}
ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, const DOMString &data )
{
return new ProcessingInstructionImpl( docPtr(),target,data);
}
AttrImpl *DocumentImpl::createAttribute( const DOMString &name )
{
AttrImpl *attr = new AttrImpl( docPtr(), name );
attr->setValue("");
return attr;
}
AttrImpl *DocumentImpl::createAttributeNS( const DOMString &/*_namespaceURI*/, const DOMString &/*_qualifiedName*/ )
{
// ###
return 0;
}
EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name )
{
return new EntityReferenceImpl(docPtr(), name.implementation());
}
NodeListImpl *DocumentImpl::getElementsByTagName( const DOMString &tagname )
{
return new TagNodeListImpl( this, tagname );
}
void DocumentImpl::updateRendering()
{
int o=changedNodes.count();
if (!o) return;
// kdDebug() << "UPDATERENDERING "< it(changedNodes);
for (; it.current(); ) {
// applyChanges removes current from the list
NodeImpl* t = it.current();
NodeImpl* n = t;
while (t)
{
if (t->changed())
n=t;
t=t->parentNode();
}
// kdDebug() << n->nodeName().string() << ": applyChanges opt=" << (n!=it.current()) << endl;
n->applyChanges( true, true );
a++;
}
// kdDebug() << "UPDATERENDERING orig="<lastChild() )
o->setParsing( false );
if ( m_render )
m_render->close();
delete m_tokenizer;
m_tokenizer = 0;
}
void DocumentImpl::write( const DOMString &text )
{
write(text.string());
}
void DocumentImpl::write( const QString &text )
{
if ( m_tokenizer ) {
if(m_tokenizer)
m_tokenizer->write(text, false);
if (m_view->part()->jScript())
m_view->part()->jScript()->appendSourceFile(m_url,text);
}
else {
NodeImpl* n = firstChild();
// ### create it if nonexistant?
if ( n && n->id() == ID_HTML ) {
for ( n = n->firstChild(); n; n = n->nextSibling() ) {
if ( n->id() == ID_BODY ) {
HTMLElementImpl* e = static_cast( n );
e->setInnerHTML( text );
// ###
if (m_view->part()->jScript())
m_view->part()->jScript()->appendSourceFile(m_url,text);
break;
}
}
}
}
}
void DocumentImpl::writeln( const DOMString &text )
{
write(text);
write(DOMString("\n"));
}
void DocumentImpl::finishParsing ( )
{
if(m_tokenizer)
m_tokenizer->finish();
}
void DocumentImpl::clear()
{
delete m_tokenizer;
m_tokenizer = 0;
removeChildren();
}
ElementImpl *DocumentImpl::getElementById( const DOMString &elementId )
{
QStack nodeStack;
NodeImpl *current = _first;
while(1)
{
if(!current)
{
if(nodeStack.isEmpty()) break;
current = nodeStack.pop();
current = current->nextSibling();
}
else
{
if(current->isElementNode())
{
ElementImpl *e = static_cast(current);
if(e->getAttribute(ATTR_ID) == elementId)
return e;
}
NodeImpl *child = current->firstChild();
if(child)
{
nodeStack.push(current);
current = child;
}
else
{
current = current->nextSibling();
}
}
}
return 0;
}
void DocumentImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet)
{
// kdDebug( 6030 ) << "HTMLDocument::setStyleSheet()" << endl;
m_sheet = new CSSStyleSheetImpl(this, url);
m_sheet->ref();
m_sheet->parseString(sheet);
m_loadingSheet = false;
createSelector();
}
void DocumentImpl::setUserStyleSheet( const QString& sheet )
{
if ( m_usersheet != sheet ) {
m_usersheet = sheet;
applyChanges();
}
}
CSSStyleSheetImpl* DocumentImpl::elementSheet()
{
if (!m_elemSheet) {
m_elemSheet = new CSSStyleSheetImpl(this, baseURL() );
m_elemSheet->ref();
}
return m_elemSheet;
}
static bool isTransitional(const QString &spec, int start)
{
if((spec.find("TRANSITIONAL", start, false ) != -1 ) ||
(spec.find("LOOSE", start, false ) != -1 ) ||
(spec.find("FRAMESET", start, false ) != -1 ) ||
(spec.find("LATIN1", start, false ) != -1 ) ||
(spec.find("SYMBOLS", start, false ) != -1 ) ||
(spec.find("SPECIAL", start, false ) != -1 ) ) {
//kdDebug() << "isTransitional" << endl;
return true;
}
return false;
}
enum HTMLMode {
Html3 = 0,
Html4 = 1,
XHtml = 2
};
void DocumentImpl::determineParseMode( const QString &str )
{
// determines the parse mode for HTML
// quite some hints here are inspired by the mozilla code.
// default parsing mode is Loose
pMode = Compat;
ParseMode systemId = Unknown;
ParseMode publicId = Unknown;
HTMLMode htmlMode = Html3;
int pos = 0;
int doctype = str.find("!doctype", 0, false);
if( doctype > 2 )
pos = doctype - 2;
// get the first tag (or the doctype tag
int start = str.find('<', pos);
int stop = str.find('>', pos);
if( start > -1 && stop > start ) {
QString spec = str.mid( start + 1, stop - start - 1 );
//kdDebug() << "DocumentImpl::determineParseMode dtd=" << spec<< endl;
start = 0;
int quote = -1;
if( doctype != -1 ) {
while( (quote = spec.find( "\"", start )) != -1 ) {
int quote2 = spec.find( "\"", quote+1 );
if(quote2 < 0) quote2 = spec.length();
QString val = spec.mid( quote+1, quote2 - quote-1 );
//kdDebug() << "DocumentImpl::determineParseMode val = " << val << endl;
// find system id
pos = val.find("http://www.w3.org/tr/", 0, false);
if ( pos != -1 ) {
// loose or strict dtd?
if ( val.find("strict.dtd", pos, false) != -1 )
systemId = Strict;
else if (isTransitional(val, pos))
systemId = Transitional;
}
// find public id
pos = val.find("//dtd", 0, false );
if ( pos != -1 ) {
if( val.find( "xhtml", pos+6, false ) != -1 ) {
htmlMode = XHtml;
if( isTransitional( val, pos ) )
publicId = Transitional;
else
publicId = Strict;
} else if ( val.find( "15445:1999", pos+6 ) != -1 ) {
htmlMode = Html4;
publicId = Strict;
} else {
int tagPos = val.find( "html", pos+6, false );
if( tagPos == -1 )
tagPos = val.find( "hypertext markup", pos+6, false );
if ( tagPos != -1 ) {
tagPos = val.find(QRegExp("[0-9]"), tagPos );
int version = val.mid( tagPos, 1 ).toInt();
//kdDebug() << "DocumentImpl::determineParseMode tagPos = " << tagPos << " version=" << version << endl;
if( version > 3 ) {
htmlMode = Html4;
publicId = isTransitional( val, tagPos ) ? Transitional : Strict;
}
}
}
}
start = quote2 + 1;
}
}
if( systemId == publicId )
pMode = publicId;
else if ( systemId == Unknown )
pMode = htmlMode == Html4 ? Compat : publicId;
else if ( publicId == Transitional && systemId == Strict ) {
if ( htmlMode == Html3 )
pMode = Compat;
else
pMode = Strict;
} else
pMode = Compat;
if ( htmlMode == XHtml )
pMode = Strict;
}
//kdDebug() << "DocumentImpl::determineParseMode: publicId =" << publicId << " systemId = " << systemId << endl;
//kdDebug() << "DocumentImpl::determineParseMode: htmlMode = " << htmlMode<< endl;
if( pMode == Strict )
kdDebug(6020) << " using strict parseMode" << endl;
else if (pMode == Compat )
kdDebug(6020) << " using compatibility parseMode" << endl;
else
kdDebug(6020) << " using transitional parseMode" << endl;
}
// Please see if there`s a possibility to merge that code
// with the next function and getElementByID().
NodeImpl *DocumentImpl::findElement( int id )
{
QStack nodeStack;
NodeImpl *current = _first;
while(1)
{
if(!current)
{
if(nodeStack.isEmpty()) break;
current = nodeStack.pop();
current = current->nextSibling();
}
else
{
if(current->id() == id)
return current;
NodeImpl *child = current->firstChild();
if(child)
{
nodeStack.push(current);
current = child;
}
else
{
current = current->nextSibling();
}
}
}
return 0;
}
ElementImpl *DocumentImpl::findSelectableElement(NodeImpl *start, bool forward)
{
if (!start)
start = forward?_first:_last;
if (!start)
return 0;
if (forward)
while(1)
{
if (!start->isSelectable() && start->firstChild())
start = start->firstChild();
else if (start->nextSibling())
start = start->nextSibling();
else // find the next sibling of the first parent that has a nextSibling
{
NodeImpl *pa = start;
while (pa)
{
pa = pa->parentNode();
if (!pa)
return 0;
if (pa->nextSibling())
{
start = pa->nextSibling();
pa = 0;
}
}
}
if (start->isElementNode() && start->isSelectable())
return static_cast(start);
}
else
while (1)
{
if (!start->isSelectable() && start->lastChild())
start = start->lastChild();
else if (start->previousSibling())
start = start->previousSibling();
else
{
NodeImpl *pa = start;
while (pa)
{
// find the previous sibling of the first parent that has a prevSibling
pa = pa->parentNode();
if (!pa)
return 0;
if (pa->previousSibling())
{
start = pa->previousSibling();
pa = 0;
break;
}
}
}
if (start->isElementNode() && start->isSelectable())
return static_cast(start);
}
kdFatal(6000) << "some error in findElement\n";
}
int DocumentImpl::findHighestTabIndex()
{
NodeImpl *n=this;
NodeImpl *next=0;
ElementImpl *a;
int retval=-1;
int tmpval;
while(n)
{
//find out tabindex of current element, if availiable
if (n->isElementNode())
{
a=static_cast(n);
tmpval=a->tabIndex();
if (tmpval>retval)
retval=tmpval;
}
//iterate to next element.
if (!n->isSelectable() && n->firstChild())
n=n->firstChild();
else if (n->nextSibling())
n=n->nextSibling();
else
{
next=0;
while(!next)
{
n=n->parentNode();
if (!n)
return retval;
next=n->nextSibling();
}
n=next;
}
}
return retval;
}
ElementImpl *DocumentImpl::findNextLink(ElementImpl *cur, bool forward)
{
int curTabIndex = (cur?cur->tabIndex():(forward?1:-1));
switch(curTabIndex)
{
case -1:
return notabindex(cur, forward);
case 0:
return tabindexzero(cur, forward);
default:
return intabindex(cur, forward);
}
}
ElementImpl *DocumentImpl::findLink(ElementImpl *n, bool forward, int tabIndexHint)
{
// tabIndexHint is the tabIndex that should be found.
// if tabIndex is -1, items containing tabIndex are skipped.
// kdDebug(6000)<<"DocumentImpl:findLink: Node: "<tabIndex()!=tabIndexHint));
return n;
}
ElementImpl *DocumentImpl::notabindex(ElementImpl *cur, bool forward)
{
// REQ: n must be after the current node and its tabindex must be -1
if ((cur = findLink(cur, forward, -1)))
return cur;
if (forward)
return 0;
else
return tabindexzero(cur, forward);
}
ElementImpl *DocumentImpl::intabindex(ElementImpl *cur, bool forward)
{
short tmptabindex;
short maxtabindex = findHighestTabIndex();
short increment=(forward?1:-1);
if (cur)
{
tmptabindex = cur->tabIndex();
}
else tmptabindex=(forward?1:maxtabindex);
while(tmptabindex>0 && tmptabindex<=maxtabindex)
{
if ((cur = findLink(cur, forward, tmptabindex)))
return cur;
tmptabindex+=increment;
}
if (forward)
return tabindexzero(cur, forward);
else
return 0;
}
ElementImpl *DocumentImpl::tabindexzero(ElementImpl *cur, bool forward)
{
//REQ: tabindex of result must be 0 and it must be after the current node ;
if ((cur = findLink(cur, forward, 0)))
return cur;
if (forward)
return notabindex(cur, forward);
else
return intabindex(cur, forward);
}
bool DocumentImpl::prepareMouseEvent( int _x, int _y,
int, int,
MouseEvent *ev )
{
NodeImpl *n = documentElement();
if ( n )
return n->prepareMouseEvent( _x, _y, 0, 0, ev );
else
return false;
}
// DOM Section 1.1.1
bool DocumentImpl::childAllowed( NodeImpl *newChild )
{
// ### maximum of one Element
// ### maximum of one DocumentType
return childTypeAllowed(newChild->nodeType());
}
bool DocumentImpl::childTypeAllowed( unsigned short type )
{
switch (type) {
case Node::ELEMENT_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::COMMENT_NODE:
case Node::DOCUMENT_TYPE_NODE:
return true;
break;
default:
return false;
}
}
NodeImpl *DocumentImpl::cloneNode ( bool /*deep*/, int &exceptioncode )
{
exceptioncode = DOMException::NOT_SUPPORTED_ERR;
return 0;
}
unsigned short DocumentImpl::elementId(DOMStringImpl *_name)
{
unsigned short id = 0;
// note: this does not take namespaces into account, as it is only really used for css at the moment
if (_name->isLower()) // use the html id instead (if one exists)
id = khtml::getTagID(DOMString(_name).string().ascii(), _name->l);
if (id)
return id;
// first try and find the element
for (id = 0; id < m_elementNameCount; id++)
if (!strcmp(m_elementNames[id],_name))
return id+1000;
// we don't have it yet, assign it an id
if (m_elementNameCount+1 > m_elementNameAlloc) {
m_elementNameAlloc += 100;
DOMStringImpl **newNames = new DOMStringImpl* [m_elementNameAlloc];
if (m_elementNames) {
unsigned short i;
for (i = 0; i < m_elementNameCount; i++)
newNames[i] = m_elementNames[i];
delete [] m_elementNames;
}
m_elementNames = newNames;
}
id = m_elementNameCount++;
m_elementNames[id] = _name;
_name->ref();
// we add 1000 to the XML element id to avoid clashes with HTML element ids
return id+1000;
}
DOMStringImpl *DocumentImpl::elementName(unsigned short _id) const
{
if (_id >= 1000)
return m_elementNames[_id-1000];
else
return getTagName(_id).implementation()->lower();
}
StyleSheetListImpl* DocumentImpl::styleSheets()
{
return m_styleSheets;
}
void DocumentImpl::createSelector()
{
if ( !m_render || !attached() ) return;
QList oldStyleSheets = m_styleSheets->styleSheets;
m_styleSheets->styleSheets.clear();
NodeImpl *n;
for (n = this; n; n = n->traverseNextNode()) {
StyleSheetImpl *sheet = 0;
if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
{
ProcessingInstructionImpl* pi = static_cast(n);
sheet = pi->sheet();
if (!sheet && !pi->localHref().isEmpty())
{
ElementImpl* elem = getElementById(pi->localHref());
if (elem->firstChild() && elem->firstChild()->isTextNode())
{
DOMString sheetText= static_cast(elem->firstChild())->data();
sheet = new CSSStyleSheetImpl(this);
sheet->parseString(sheetText);
pi->setStyleSheet(sheet);
}
}
}
else if (n->id() == ID_LINK)
sheet = static_cast(n)->sheet();
else if (n->id() == ID_STYLE)
sheet = static_cast(n)->sheet();
else if (n->id() == ID_BODY)
sheet = static_cast(n)->sheet();
if (sheet) {
sheet->ref();
m_styleSheets->styleSheets.append(sheet);
}
if (isHTMLDocument() && n->id() == ID_BODY)
break;
}
QListIterator it(oldStyleSheets);
for (; it.current(); ++it)
it.current()->deref();
applyChanges(true,true);
}
void DocumentImpl::setFocusNode(ElementImpl *n)
{
// ### add check for same Document
if (m_focusNode != n)
{
if (m_focusNode)
{
if (m_focusNode->active()) m_focusNode->setActive(false);
m_focusNode->setFocus(false);
int exceptioncode;
UIEventImpl *ue = new UIEventImpl(EventImpl::DOMFOCUSOUT_EVENT,
true,false,defaultView(),
0);
ue->ref();
m_focusNode->dispatchEvent(ue,exceptioncode);
ue->deref();
}
m_focusNode = n;
//kdDebug(6020)<<"DOM::DocumentImpl::setFocusNode("<ref();
m_focusNode->dispatchEvent(ue,exceptioncode);
ue->deref();
n->setFocus();
}
}
}
ElementImpl *DocumentImpl::focusNode()
{
return m_focusNode;
}
void DocumentImpl::attachNodeIterator(NodeIteratorImpl *ni)
{
m_nodeIterators.append(ni);
}
void DocumentImpl::detachNodeIterator(NodeIteratorImpl *ni)
{
m_nodeIterators.remove(ni);
}
void DocumentImpl::notifyBeforeNodeRemoval(NodeImpl *n)
{
QListIterator it(m_nodeIterators);
for (; it.current(); ++it)
it.current()->notifyBeforeNodeRemoval(n);
}
AbstractViewImpl *DocumentImpl::defaultView() const
{
return m_defaultView;
}
EventImpl *DocumentImpl::createEvent(const DOMString &eventType, int &exceptioncode)
{
if (eventType == "UIEvents")
return new UIEventImpl();
else if (eventType == "MouseEvents")
return new MouseEventImpl();
else if (eventType == "MutationEvents")
return new MutationEventImpl();
else if (eventType == "HTMLEvents")
return new EventImpl();
else {
exceptioncode = DOMException::NOT_SUPPORTED_ERR;
return 0;
}
}
CSSStyleDeclarationImpl *DocumentImpl::getOverrideStyle(ElementImpl */*elt*/, DOMStringImpl */*pseudoElt*/)
{
return 0; // ###
}
void DocumentImpl::defaultEventHandler(EventImpl *evt)
{
// if any html event listeners are registered on the window, then dispatch them here
QListIterator it(m_windowEventListeners);
Event ev = evt;
for (; it.current(); ++it) {
if (it.current()->id == evt->id()) {
it.current()->listener->handleEvent(ev);
return;
}
}
}
void DocumentImpl::setWindowEventListener(int id, EventListener *listener)
{
removeWindowEventListener(id);
if (listener) {
RegisteredEventListener *rl = new RegisteredEventListener(static_cast(id),listener,false);
m_windowEventListeners.append(rl);
}
}
EventListener *DocumentImpl::getWindowEventListener(int id)
{
QListIterator it(m_windowEventListeners);
for (; it.current(); ++it) {
if (it.current()->id == id) {
return it.current()->listener;
}
}
return 0;
}
void DocumentImpl::removeWindowEventListener(int id)
{
QListIterator it(m_windowEventListeners);
for (; it.current(); ++it) {
if (it.current()->id == id) {
m_windowEventListeners.removeRef(it.current());
return;
}
}
}
EventListener *DocumentImpl::createHTMLEventListener(QString code)
{
return view()->part()->createHTMLEventListener(code);
}
// ----------------------------------------------------------------------------
DocumentFragmentImpl::DocumentFragmentImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
{
}
DocumentFragmentImpl::DocumentFragmentImpl(const DocumentFragmentImpl &other)
: NodeBaseImpl(other)
{
}
const DOMString DocumentFragmentImpl::nodeName() const
{
return "#document-fragment";
}
unsigned short DocumentFragmentImpl::nodeType() const
{
return Node::DOCUMENT_FRAGMENT_NODE;
}
// DOM Section 1.1.1
bool DocumentFragmentImpl::childTypeAllowed( unsigned short type )
{
switch (type) {
case Node::ELEMENT_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::COMMENT_NODE:
case Node::TEXT_NODE:
case Node::CDATA_SECTION_NODE:
case Node::ENTITY_REFERENCE_NODE:
return true;
break;
default:
return false;
}
}
NodeImpl *DocumentFragmentImpl::cloneNode ( bool deep, int &exceptioncode )
{
DocumentFragmentImpl *clone = new DocumentFragmentImpl( docPtr() );
if (deep)
cloneChildNodes(clone,exceptioncode);
return clone;
}
// ----------------------------------------------------------------------------
DocumentTypeImpl::DocumentTypeImpl(DocumentPtr *doc) : NodeImpl(doc)
{
m_entities = new GenericRONamedNodeMapImpl();
m_entities->ref();
m_notations = new GenericRONamedNodeMapImpl();
m_notations->ref();
}
DocumentTypeImpl::~DocumentTypeImpl()
{
m_entities->deref();
m_notations->deref();
}
const DOMString DocumentTypeImpl::name() const
{
// ###
return DOMString();
}
NamedNodeMapImpl *DocumentTypeImpl::entities() const
{
return m_entities;
}
NamedNodeMapImpl *DocumentTypeImpl::notations() const
{
return m_notations;
}
const DOMString DocumentTypeImpl::nodeName() const
{
return name();
}
unsigned short DocumentTypeImpl::nodeType() const
{
return Node::DOCUMENT_TYPE_NODE;
}
// DOM Section 1.1.1
bool DocumentTypeImpl::childTypeAllowed( unsigned short /*type*/ )
{
return false;
}
NodeImpl *DocumentTypeImpl::cloneNode ( bool /*deep*/, int &exceptioncode )
{
exceptioncode = DOMException::NOT_SUPPORTED_ERR;
return 0;
}
#include "dom_docimpl.moc"