File indexing completed on 2024-05-05 16:10:14

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2005 Maksim Orlovich (maksim@kde.org)
0007  *
0008  * This library is free software; you can redistribute it and/or
0009  * modify it under the terms of the GNU Library General Public
0010  * License as published by the Free Software Foundation; either
0011  * version 2 of the License, or (at your option) any later version.
0012  *
0013  * This library is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * Library General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU Library General Public License
0019  * along with this library; see the file COPYING.LIB.  If not, write to
0020  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022  *
0023  */
0024 // -------------------------------------------------------------------------
0025 #include "html_miscimpl.h"
0026 #include "html_tableimpl.h"
0027 #include "html_formimpl.h"
0028 #include "html_documentimpl.h"
0029 
0030 #include <dom/dom_node.h>
0031 
0032 using namespace DOM;
0033 
0034 #include "khtml_debug.h"
0035 
0036 HTMLBaseFontElementImpl::HTMLBaseFontElementImpl(DocumentImpl *doc)
0037     : HTMLElementImpl(doc)
0038 {
0039 }
0040 
0041 HTMLBaseFontElementImpl::~HTMLBaseFontElementImpl()
0042 {
0043 }
0044 
0045 NodeImpl::Id HTMLBaseFontElementImpl::id() const
0046 {
0047     return ID_BASEFONT;
0048 }
0049 
0050 // -------------------------------------------------------------------------
0051 
0052 struct CollectionCache: public DynamicNodeListImpl::Cache {
0053     static Cache *make()
0054     {
0055         return new CollectionCache;
0056     }
0057 
0058     QHash<DOMString, QList<NodeImpl *>* > nameCache;
0059 
0060     CollectionCache(): Cache(DocumentImpl::TV_IDNameHref) {}
0061 
0062     void clear(DocumentImpl *doc) override
0063     {
0064         Cache::clear(doc);
0065         qDeleteAll(nameCache);
0066         nameCache.clear();
0067     }
0068 
0069     virtual ~CollectionCache()
0070     {
0071         qDeleteAll(nameCache);
0072     }
0073 };
0074 
0075 HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type):
0076     DynamicNodeListImpl(_base, _type, CollectionCache::make)
0077 {
0078     type = _type;
0079 }
0080 
0081 bool HTMLCollectionImpl::nodeMatches(NodeImpl *current, bool &deep) const
0082 {
0083     if (current->nodeType() != Node::ELEMENT_NODE) {
0084         deep = false;
0085         return false;
0086     }
0087 
0088     bool check = false;
0089     HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
0090     switch (type) {
0091     case DOC_IMAGES:
0092         if (e->id() == ID_IMG) {
0093             check = true;
0094         }
0095         break;
0096     case DOC_SCRIPTS:
0097         if (e->id() == ID_SCRIPT) {
0098             check = true;
0099         }
0100         break;
0101     case DOC_FORMS:
0102         if (e->id() == ID_FORM) {
0103             check = true;
0104         }
0105         break;
0106     case DOC_LAYERS:
0107         if (e->id() == ID_LAYER || e->id() == ID_ILAYER) {
0108             check = true;
0109         }
0110         break;
0111     case TABLE_TBODIES:
0112         if (e->id() == ID_TBODY) {
0113             check = true;
0114         } else if (e->id() == ID_TABLE) {
0115             deep = false;
0116         }
0117         break;
0118     case TR_CELLS:
0119         if (e->id() == ID_TD || e->id() == ID_TH) {
0120             check = true;
0121         } else if (e->id() == ID_TABLE) {
0122             deep = false;
0123         }
0124         break;
0125     case TABLE_ROWS:
0126     case TSECTION_ROWS:
0127         if (e->id() == ID_TR) {
0128             check = true;
0129         } else if (e->id() == ID_TABLE) {
0130             deep = false;
0131         }
0132         break;
0133     case SELECT_OPTIONS:
0134         if (e->id() == ID_OPTION) {
0135             check = true;
0136         }
0137         break;
0138     case MAP_AREAS:
0139         if (e->id() == ID_AREA) {
0140             check = true;
0141         }
0142         break;
0143     case FORMLESS_INPUT:
0144         if (e->id() == ID_INPUT && !static_cast<HTMLInputElementImpl *>(e)->form()) {
0145             check = true;
0146         }
0147         break;
0148     case DOC_APPLETS:   // all OBJECT and APPLET elements
0149         if (e->id() == ID_OBJECT || e->id() == ID_APPLET || e->id() == ID_EMBED) {
0150             check = true;
0151         }
0152         break;
0153     case DOC_LINKS:     // all A _and_ AREA elements with a value for href
0154         if (e->id() == ID_A || e->id() == ID_AREA)
0155             if (!e->getAttribute(ATTR_HREF).isNull()) {
0156                 check = true;
0157             }
0158         break;
0159     case DOC_ANCHORS:      // all A elements with a value for name and/or id
0160         if (e->id() == ID_A) {
0161             if (e->hasID() || !e->getAttribute(ATTR_NAME).isNull()) {
0162                 check = true;
0163             }
0164         }
0165         break;
0166     case DOC_ALL:      // "all" elements
0167         check = true;
0168         break;
0169     case NODE_CHILDREN: // first-level children
0170         check = true;
0171         deep = false;
0172         break;
0173     default:
0174         // qCDebug(KHTML_LOG) << "Error in HTMLCollection, wrong tagId!";
0175         break;
0176     }
0177 
0178     return check;
0179 }
0180 
0181 bool HTMLCollectionImpl::checkForNameMatch(NodeImpl *node, const DOMString &name) const
0182 {
0183     if (node->nodeType() != Node::ELEMENT_NODE) {
0184         return false;
0185     }
0186 
0187     HTMLElementImpl *e = static_cast<HTMLElementImpl *>(node);
0188 
0189     //If ID matches, this is definitely a match
0190     if (e->getAttribute(ATTR_ID) == name) {
0191         return true;
0192     }
0193 
0194     //Despite what the DOM spec says, neither IE nor Gecko actually
0195     //care to prefer IDs. Instead, they just match everything
0196     //that has ID or a name for nodes that have a name.
0197     //Except for the form elements collection, Gecko always returns
0198     //just one item. IE is more complex: its namedItem
0199     //and call notation access return everything that matches,
0200     //but the subscript notation is sometimes different.
0201     //For now, we try to match IE, but without the subscript
0202     //oddness, which I don't understand -- Maks.
0203 
0204     bool checkName;
0205     switch (e->id()) {
0206     case ID_A:
0207     case ID_APPLET:
0208     case ID_BUTTON:
0209     case ID_EMBED:
0210     case ID_FORM:
0211     case ID_IMG:
0212     case ID_INPUT:
0213     case ID_MAP:
0214     case ID_META:
0215     case ID_OBJECT:
0216     case ID_SELECT:
0217     case ID_TEXTAREA:
0218     case ID_FRAME:
0219     case ID_IFRAME:
0220     case ID_FRAMESET:
0221         checkName = true;
0222         break;
0223     default:
0224         checkName = false;
0225     }
0226 
0227     if (checkName) {
0228         return e->getAttribute(ATTR_NAME) == name;
0229     } else {
0230         return false;
0231     }
0232 }
0233 
0234 NodeImpl *HTMLCollectionImpl::item(unsigned long index) const
0235 {
0236     //Most of the time, we just go in normal document order
0237     if (type != TABLE_ROWS) {
0238         return DynamicNodeListImpl::item(index);
0239     }
0240 
0241     //For table.rows, we first need to check header, then bodies, then footer.
0242     //we pack any extra headers/footer with bodies. This matches IE, and
0243     //means doing the usual thing with length is right
0244     const HTMLTableElementImpl *table = static_cast<const HTMLTableElementImpl *>(m_refNode);
0245 
0246     long                          sectionIndex;
0247     HTMLTableSectionElementImpl  *section;
0248 
0249     NodeImpl *found = nullptr;
0250     if (table->findRowSection(index, section, sectionIndex)) {
0251         HTMLCollectionImpl rows(section, TSECTION_ROWS);
0252         found = rows.item(sectionIndex);
0253     }
0254 
0255     m_cache->current.node = found; //namedItem needs this.
0256     m_cache->position     = index;
0257     return found;
0258 }
0259 
0260 unsigned long HTMLCollectionImpl::calcLength(NodeImpl *start) const
0261 {
0262     if (type != TABLE_ROWS) {
0263         return DynamicNodeListImpl::calcLength(start);
0264     }
0265 
0266     unsigned length = 0;
0267     const HTMLTableElementImpl *table = static_cast<const HTMLTableElementImpl *>(m_refNode);
0268     for (NodeImpl *kid = table->firstChild(); kid; kid = kid->nextSibling()) {
0269         HTMLCollectionImpl rows(kid, TSECTION_ROWS);
0270         length += rows.length();
0271     }
0272     return length;
0273 }
0274 
0275 NodeImpl *HTMLCollectionImpl::firstItem() const
0276 {
0277     return item(0);
0278 }
0279 
0280 NodeImpl *HTMLCollectionImpl::nextItem() const
0281 {
0282     //### this assumes this is called immediately after nextItem --
0283     //it this sane?
0284     return item(m_cache->position + 1);
0285 }
0286 
0287 NodeImpl *HTMLCollectionImpl::namedItem(const DOMString &name) const
0288 {
0289     if (name.isEmpty()) {
0290         return nullptr;
0291     }
0292 
0293     //Reset the position. The invariant is that nextNamedItem will start looking
0294     //from the current position.
0295     firstItem();
0296 
0297     return nextNamedItem(name);
0298 }
0299 
0300 NodeImpl *HTMLCollectionImpl::nextNamedItem(const DOMString &name) const
0301 {
0302     if (name.isEmpty()) {
0303         return nullptr;
0304     }
0305 
0306     while (NodeImpl *candidate = m_cache->current.node) {
0307         //Always advance, for next call
0308         nextItem();
0309         if (checkForNameMatch(candidate, name)) {
0310             return candidate;
0311         }
0312     }
0313     return nullptr;
0314 }
0315 
0316 QList<NodeImpl *> HTMLCollectionImpl::namedItems(const DOMString &name) const
0317 {
0318     if (name.isEmpty()) {
0319         return QList<NodeImpl *>();
0320     }
0321 
0322     //We use a work-conserving design for the name cache presently -- only
0323     //remember stuff about elements we were asked for.
0324     m_cache->updateNodeListInfo(m_refNode->document());
0325     CollectionCache *cache = static_cast<CollectionCache *>(m_cache);
0326     if (QList<NodeImpl *> *info = cache->nameCache.value(name)) {
0327         return *info;
0328     } else {
0329         QList<NodeImpl *> *newInfo = new QList<NodeImpl *>;
0330 
0331         NodeImpl *match = namedItem(name);
0332         while (match) {
0333             newInfo->append(match);
0334             match = nextNamedItem(name);
0335         }
0336 
0337         cache->nameCache.insertMulti(name, newInfo);
0338         return *newInfo;
0339     }
0340 }
0341 
0342 // -----------------------------------------------------------------------------
0343 
0344 HTMLFormCollectionImpl::HTMLFormCollectionImpl(NodeImpl *_base)
0345     : HTMLCollectionImpl(_base, FORM_ELEMENTS), currentNamePos(0), currentNameImgPos(0)
0346 {}
0347 
0348 NodeImpl *HTMLFormCollectionImpl::item(unsigned long index) const
0349 {
0350     m_cache->updateNodeListInfo(m_refNode->document());
0351 
0352     unsigned int dist = index;
0353     unsigned int strt = 0;
0354     if (m_cache->current.index && m_cache->position <= index) {
0355         dist = index - m_cache->position;
0356         strt = m_cache->current.index;
0357     }
0358 
0359     QList<HTMLGenericFormElementImpl *> &l = static_cast<HTMLFormElementImpl *>(m_refNode)->formElements;
0360     for (unsigned i = strt; i < (unsigned)l.count(); i++) {
0361         if (l.at(i)->isEnumerable()) {
0362             if (dist == 0) {
0363                 //Found it!
0364                 m_cache->position      = index;
0365                 m_cache->current.index = i;
0366                 return l.at(i);
0367             } else {
0368                 --dist;
0369             }
0370         }
0371     }
0372     return nullptr;
0373 }
0374 
0375 unsigned long HTMLFormCollectionImpl::calcLength(NodeImpl *start) const
0376 {
0377     Q_UNUSED(start);
0378     unsigned length = 0;
0379     QList<HTMLGenericFormElementImpl *> l = static_cast<HTMLFormElementImpl *>(m_refNode)->formElements;
0380     for (unsigned i = 0; i < (unsigned)l.count(); i++)
0381         if (l.at(i)->isEnumerable()) {
0382             ++length;
0383         }
0384     return length;
0385 }
0386 
0387 NodeImpl *HTMLFormCollectionImpl::namedItem(const DOMString &name) const
0388 {
0389     currentNamePos    = 0;
0390     currentNameImgPos = 0;
0391     foundInput        = false;
0392     return nextNamedItem(name);
0393 }
0394 
0395 NodeImpl *HTMLFormCollectionImpl::nextNamedItem(const DOMString &name) const
0396 {
0397     QList<HTMLGenericFormElementImpl *> &l = static_cast<HTMLFormElementImpl *>(m_refNode)->formElements;
0398 
0399     //Go through the list, trying to find the appropriate named form element.
0400     for (; currentNamePos < (unsigned)l.count(); ++currentNamePos) {
0401         HTMLGenericFormElementImpl *el = l.at(currentNamePos);
0402         if (el->isEnumerable() &&
0403                 ((el->getAttribute(ATTR_ID)   == name) ||
0404                  (el->getAttribute(ATTR_NAME) == name))) {
0405             ++currentNamePos; //Make next call start after this
0406             foundInput = true;//No need to look for img
0407             return el;
0408         }
0409     }
0410 
0411     //If we got this far, we may need to start looking through the images,
0412     //but only if no input tags were matched
0413     if (foundInput) {
0414         return nullptr;
0415     }
0416 
0417     QList<HTMLImageElementImpl *> &il = static_cast<HTMLFormElementImpl *>(m_refNode)->imgElements;
0418     for (; currentNameImgPos < (unsigned)il.count(); ++currentNameImgPos) {
0419         HTMLImageElementImpl *el = il.at(currentNameImgPos);
0420         if ((el->getAttribute(ATTR_ID)   == name) ||
0421                 (el->getAttribute(ATTR_NAME) == name)) {
0422             ++currentNameImgPos; //Make next call start after this
0423             return el;
0424         }
0425     }
0426 
0427     return nullptr;
0428 }
0429 
0430 // -------------------------------------------------------------------------
0431 HTMLMappedNameCollectionImpl::HTMLMappedNameCollectionImpl(NodeImpl *_base, int _type, const DOMString &_name):
0432     HTMLCollectionImpl(_base, DynamicNodeListImpl::UNCACHEABLE), name(_name)
0433 {
0434     type = _type; //We pass uncacheable to collection, but need our own type internally.
0435 }
0436 
0437 bool HTMLMappedNameCollectionImpl::nodeMatches(NodeImpl *current, bool &deep) const
0438 {
0439     if (current->nodeType() != Node::ELEMENT_NODE) {
0440         deep = false;
0441         return false;
0442     }
0443 
0444     HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
0445 
0446     return matchesName(e, type, name);
0447 }
0448 
0449 bool HTMLMappedNameCollectionImpl::matchesName(ElementImpl *el, int type, const DOMString &name)
0450 {
0451     switch (el->id()) {
0452     case ID_IMG:
0453     case ID_FORM:
0454         //Under document. these require non-empty name to see the element
0455         if (type == DOCUMENT_NAMED_ITEMS && el->getAttribute(ATTR_NAME).isNull()) {
0456             return false;
0457         }
0458     //Otherwise, fallthrough
0459     case ID_OBJECT:
0460     case ID_EMBED:
0461     case ID_APPLET:
0462     case ID_LAYER:
0463         if (el->getAttribute(ATTR_NAME) == name || el->getAttribute(ATTR_ID) == name) {
0464             return true;
0465         } else {
0466             return false;
0467         }
0468     default:
0469         return false;
0470     }
0471 }
0472