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