You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/khtml/html/html_miscimpl.cpp

444 lines
13 KiB

/**
* 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)
* (C) 2005 Maksim Orlovich (maksim@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.
*
*/
// -------------------------------------------------------------------------
#include "html/html_tableimpl.h"
#include "html/html_miscimpl.h"
#include "html/html_formimpl.h"
#include "html/html_documentimpl.h"
#include "misc/htmlhashes.h"
#include "dom/dom_node.h"
using namespace DOM;
#include <kdebug.h>
HTMLBaseFontElementImpl::HTMLBaseFontElementImpl(DocumentImpl *doc)
: HTMLElementImpl(doc)
{
}
HTMLBaseFontElementImpl::~HTMLBaseFontElementImpl()
{
}
NodeImpl::Id HTMLBaseFontElementImpl::id() const
{
return ID_BASEFONT;
}
// -------------------------------------------------------------------------
struct CollectionCache: public NodeListImpl::Cache
{
static Cache* make() { return new CollectionCache; }
QDict<QValueList<NodeImpl*> > nameCache;
CollectionCache(): nameCache(127)
{
nameCache.setAutoDelete(true);
}
virtual void clear(DocumentImpl* doc)
{
Cache::clear(doc);
//qDeletaAll here in Qt4
nameCache.clear();
}
};
HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type):
NodeListImpl(_base, _type, CollectionCache::make)
{
type = _type;
}
bool HTMLCollectionImpl::nodeMatches(NodeImpl *current, bool& deep) const
{
if ( current->nodeType() != Node::ELEMENT_NODE )
{
deep = false;
return false;
}
bool check = false;
HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
switch(type)
{
case DOC_IMAGES:
if(e->id() == ID_IMG)
check = true;
break;
case DOC_SCRIPTS:
if(e->id() == ID_SCRIPT)
check = true;
break;
case DOC_FORMS:
if(e->id() == ID_FORM)
check = true;
break;
case DOC_LAYERS:
if(e->id() == ID_LAYER || e->id() == ID_ILAYER)
check = true;
break;
case TABLE_TBODIES:
if(e->id() == ID_TBODY)
check = true;
else if(e->id() == ID_TABLE)
deep = false;
break;
case TR_CELLS:
if(e->id() == ID_TD || e->id() == ID_TH)
check = true;
else if(e->id() == ID_TABLE)
deep = false;
break;
case TABLE_ROWS:
case TSECTION_ROWS:
if(e->id() == ID_TR)
check = true;
else if(e->id() == ID_TABLE)
deep = false;
break;
case SELECT_OPTIONS:
if(e->id() == ID_OPTION)
check = true;
break;
case MAP_AREAS:
if(e->id() == ID_AREA)
check = true;
break;
case DOC_APPLETS: // all OBJECT and APPLET elements
if(e->id() == ID_OBJECT || e->id() == ID_APPLET || e->id() == ID_EMBED)
check = true;
break;
case DOC_LINKS: // all A _and_ AREA elements with a value for href
if(e->id() == ID_A || e->id() == ID_AREA)
if(!e->getAttribute(ATTR_HREF).isNull())
check = true;
break;
case DOC_ANCHORS: // all A elements with a value for name and/or id
if(e->id() == ID_A) {
if(e->hasID() || !e->getAttribute(ATTR_NAME).isNull())
check = true;
}
break;
case DOC_ALL: // "all" elements
check = true;
break;
case NODE_CHILDREN: // first-level children
check = true;
deep = false;
break;
default:
kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
}
return check;
}
bool HTMLCollectionImpl::checkForNameMatch(NodeImpl *node, const DOMString &name) const
{
if ( node->nodeType() != Node::ELEMENT_NODE )
return false;
HTMLElementImpl *e = static_cast<HTMLElementImpl *>(node);
//If ID matches, this is definitely a match
if (e->getAttribute(ATTR_ID) == name)
return true;
//Despite what the DOM spec says, neither IE nor Gecko actually
//care to prefer IDs. Instead, they just match everything
//that has ID or a name for nodes that have a name.
//Except for the form elements collection, Gecko always returns
//just one item. IE is more complex: its namedItem
//and call notation access return everything that matches,
//but the subscript notation is sometimes different.
//For now, we try to match IE, but without the subscript
//oddness, which I don't understand -- Maks.
bool checkName;
switch (e->id())
{
case ID_A:
case ID_APPLET:
case ID_BUTTON:
case ID_EMBED:
case ID_FORM:
case ID_IMG:
case ID_INPUT:
case ID_MAP:
case ID_META:
case ID_OBJECT:
case ID_SELECT:
case ID_TEXTAREA:
case ID_FRAME:
case ID_IFRAME:
case ID_FRAMESET:
checkName = true;
break;
default:
checkName = false;
}
if (checkName)
return e->getAttribute(ATTR_NAME) == name;
else
return false;
}
NodeImpl *HTMLCollectionImpl::item ( unsigned long index ) const
{
//Most of the time, we just go in normal document order
if (type != TABLE_ROWS)
return NodeListImpl::item(index);
//For table.rows, we first need to check header, then bodies, then footer.
//we pack any extra headers/footer with bodies. This matches IE, and
//means doing the usual thing with length is right
const HTMLTableElementImpl* table = static_cast<const HTMLTableElementImpl*>(m_refNode);
long sectionIndex;
HTMLTableSectionElementImpl* section;
NodeImpl* found = 0;
if (table->findRowSection(index, section, sectionIndex)) {
HTMLCollectionImpl rows(section, TSECTION_ROWS);
found = rows.item(sectionIndex);
}
m_cache->current.node = found; //namedItem needs this.
m_cache->position = index;
return found;
}
unsigned long HTMLCollectionImpl::calcLength(NodeImpl *start) const
{
if (type != TABLE_ROWS)
return NodeListImpl::calcLength(start);
unsigned length = 0;
const HTMLTableElementImpl* table = static_cast<const HTMLTableElementImpl*>(m_refNode);
for (NodeImpl* kid = table->firstChild(); kid; kid = kid->nextSibling()) {
HTMLCollectionImpl rows(kid, TSECTION_ROWS);
length += rows.length();
}
return length;
}
NodeImpl *HTMLCollectionImpl::firstItem() const
{
return item(0);
}
NodeImpl *HTMLCollectionImpl::nextItem() const
{
//### this assumes this is called immediately after nextItem --
//it this sane?
return item(m_cache->position + 1);
}
NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name ) const
{
//Reset the position. The invariant is that nextNamedItem will start looking
//from the current position.
firstItem();
return nextNamedItem(name);
}
NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const
{
while (NodeImpl* candidate = m_cache->current.node)
{
//Always advance, for next call
nextItem();
if (checkForNameMatch(candidate, name))
return candidate;
}
return 0;
}
QValueList<NodeImpl*> HTMLCollectionImpl::namedItems( const DOMString &name ) const
{
QString key = name.string();
//We use a work-conserving design for the name cache presently -- only
//remember stuff about elements we were asked for.
m_cache->updateNodeListInfo(m_refNode->getDocument());
CollectionCache* cache = static_cast<CollectionCache*>(m_cache);
if (QValueList<NodeImpl*>* info = cache->nameCache.find(key)) {
return *info;
}
else {
QValueList<NodeImpl*>* newInfo = new QValueList<NodeImpl*>;
NodeImpl* match = namedItem(name);
while (match) {
newInfo->append(match);
match = nextNamedItem(name);
}
cache->nameCache.insert(key, newInfo);
return *newInfo;
}
}
// -----------------------------------------------------------------------------
HTMLFormCollectionImpl::HTMLFormCollectionImpl(NodeImpl* _base)
: HTMLCollectionImpl(_base, FORM_ELEMENTS), currentNamePos(0), currentNameImgPos(0)
{}
NodeImpl *HTMLFormCollectionImpl::item( unsigned long index ) const
{
m_cache->updateNodeListInfo(m_refNode->getDocument());
unsigned int dist = index;
unsigned int strt = 0;
if (m_cache->current.index && m_cache->position <= index)
{
dist = index - m_cache->position;
strt = m_cache->current.index;
}
QPtrList<HTMLGenericFormElementImpl>& l = static_cast<HTMLFormElementImpl*>( m_refNode )->formElements;
for (unsigned i = strt; i < l.count(); i++)
{
if (l.at( i )->isEnumeratable())
{
if (dist == 0)
{
//Found it!
m_cache->position = index;
m_cache->current.index = i;
return l.at( i );
}
else
--dist;
}
}
return 0;
}
unsigned long HTMLFormCollectionImpl::calcLength(NodeImpl *start) const
{
unsigned length = 0;
QPtrList<HTMLGenericFormElementImpl> l = static_cast<HTMLFormElementImpl*>( start )->formElements;
for ( unsigned i = 0; i < l.count(); i++ )
if ( l.at( i )->isEnumeratable() )
++length;
return length;
}
NodeImpl *HTMLFormCollectionImpl::namedItem( const DOMString &name ) const
{
currentNamePos = 0;
currentNameImgPos = 0;
foundInput = false;
return nextNamedItem(name);
}
NodeImpl *HTMLFormCollectionImpl::nextNamedItem( const DOMString &name ) const
{
QPtrList<HTMLGenericFormElementImpl>& l = static_cast<HTMLFormElementImpl*>( m_refNode )->formElements;
//Go through the list, trying to find the appropriate named form element.
for ( ; currentNamePos < l.count(); ++currentNamePos )
{
HTMLGenericFormElementImpl* el = l.at(currentNamePos);
if (el->isEnumeratable() &&
((el->getAttribute(ATTR_ID) == name) ||
(el->getAttribute(ATTR_NAME) == name)))
{
++currentNamePos; //Make next call start after this
foundInput = true;//No need to look for img
return el;
}
}
//If we got this far, we may need to start looking through the images,
//but only if no input tags were matched
if (foundInput) return 0;
QPtrList<HTMLImageElementImpl>& il = static_cast<HTMLFormElementImpl*>( m_refNode )->imgElements;
for ( ; currentNameImgPos < il.count(); ++currentNameImgPos )
{
HTMLImageElementImpl* el = il.at(currentNameImgPos);
if ((el->getAttribute(ATTR_ID) == name) ||
(el->getAttribute(ATTR_NAME) == name))
{
++currentNameImgPos; //Make next call start after this
return el;
}
}
return 0;
}
// -------------------------------------------------------------------------
HTMLMappedNameCollectionImpl::HTMLMappedNameCollectionImpl(NodeImpl* _base, int _type, const DOMString& _name):
HTMLCollectionImpl(_base, NodeListImpl::UNCACHEABLE), name(_name)
{
type = _type; //We pass uncacheable to collection, but need our own type internally.
}
bool HTMLMappedNameCollectionImpl::nodeMatches(NodeImpl *current, bool& deep) const
{
if ( current->nodeType() != Node::ELEMENT_NODE )
{
deep = false;
return false;
}
HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
return matchesName(e, type, name);
}
bool HTMLMappedNameCollectionImpl::matchesName( ElementImpl* el, int type, const DOMString& name )
{
switch (el->id())
{
case ID_IMG:
case ID_FORM:
//Under document. these require non-empty name to see the element
if (type == DOCUMENT_NAMED_ITEMS && el->getAttribute(ATTR_NAME).isNull())
return false;
//Otherwise, fallthrough
case ID_OBJECT:
case ID_EMBED:
case ID_APPLET:
case ID_LAYER:
if (el->getAttribute(ATTR_NAME) == name || el->getAttribute(ATTR_ID) == name)
return true;
else
return false;
default:
return false;
}
}