2
* This file is part of the DOM implementation for KDE.
4
2
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5
3
* (C) 1999 Antti Koivisto (koivisto@kde.org)
6
* Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
4
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
8
6
* This library is free software; you can redistribute it and/or
9
7
* modify it under the terms of the GNU Library General Public
18
16
* You should have received a copy of the GNU Library General Public License
19
17
* along with this library; see the file COPYING.LIB. If not, write to
20
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
* Boston, MA 02111-1307, USA.
18
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
* Boston, MA 02110-1301, USA.
38
36
using namespace HTMLNames;
40
HTMLCollection::HTMLCollection(Node *_base, HTMLCollection::Type _type)
47
if (_base->isDocumentNode())
48
info = _base->document()->collectionInfo(type);
38
HTMLCollection::HTMLCollection(PassRefPtr<Node> base, Type type)
42
, m_info(m_base->isDocumentNode() ? static_cast<Document*>(m_base.get())->collectionInfo(type) : 0)
47
HTMLCollection::HTMLCollection(PassRefPtr<Node> base, Type type, CollectionInfo* info)
56
PassRefPtr<HTMLCollection> HTMLCollection::create(PassRefPtr<Node> base, Type type)
58
return adoptRef(new HTMLCollection(base, type));
51
61
HTMLCollection::~HTMLCollection()
57
HTMLCollection::CollectionInfo::CollectionInfo() :
67
HTMLCollection::CollectionInfo::CollectionInfo()
73
inline void HTMLCollection::CollectionInfo::copyCacheMap(NodeCacheMap& dest, const NodeCacheMap& src)
75
ASSERT(dest.isEmpty());
76
NodeCacheMap::const_iterator end = src.end();
77
for (NodeCacheMap::const_iterator it = src.begin(); it != end; ++it)
78
dest.add(it->first, new Vector<Element*>(*it->second));
63
81
HTMLCollection::CollectionInfo::CollectionInfo(const CollectionInfo& other)
82
: version(other.version)
83
, current(other.current)
84
, position(other.position)
85
, length(other.length)
86
, elementsArrayPosition(other.elementsArrayPosition)
87
, hasLength(other.hasLength)
88
, hasNameCache(other.hasNameCache)
65
version = other.version;
66
current = other.current;
67
position = other.position;
68
length = other.length;
69
elementsArrayPosition = other.elementsArrayPosition;
71
90
copyCacheMap(idCache, other.idCache);
72
91
copyCacheMap(nameCache, other.nameCache);
74
haslength = other.haslength;
75
hasNameCache = other.hasNameCache;
78
94
void HTMLCollection::CollectionInfo::swap(CollectionInfo& other)
113
129
void HTMLCollection::resetCollectionInfo() const
115
unsigned int docversion = static_cast<HTMLDocument*>(m_base->document())->domTreeVersion();
131
unsigned docversion = static_cast<HTMLDocument*>(m_base->document())->domTreeVersion();
118
info = new CollectionInfo;
134
m_info = new CollectionInfo;
119
135
m_ownsInfo = true;
120
info->version = docversion;
136
m_info->version = docversion;
124
if (info->version != docversion) {
126
info->version = docversion;
131
Node *HTMLCollection::traverseNextItem(Node *current) const
135
if (type == NodeChildren && m_base.get() != current)
136
current = current->nextSibling();
140
if (m_info->version != docversion) {
142
m_info->version = docversion;
146
static Node* nextNodeOrSibling(Node* base, Node* node, bool includeChildren)
148
return includeChildren ? node->traverseNextNode(base) : node->traverseNextSibling(base);
151
Element* HTMLCollection::itemAfter(Element* previous) const
165
case DocumentNamedItems:
169
case WindowNamedItems:
181
current = m_base->firstChild();
138
current = current->traverseNextNode(m_base.get());
183
current = nextNodeOrSibling(m_base.get(), previous, deep);
141
if (current->isElementNode()) {
144
HTMLElement *e = static_cast<HTMLElement *>(current);
185
for (; current; current = nextNodeOrSibling(m_base.get(), current, deep)) {
186
if (!current->isElementNode())
188
Element* e = static_cast<Element*>(current);
147
191
if (e->hasLocalName(imgTag))
151
195
if (e->hasLocalName(scriptTag))
155
if(e->hasLocalName(formTag))
199
if (e->hasLocalName(formTag))
158
202
case TableTBodies:
159
203
if (e->hasLocalName(tbodyTag))
161
else if (e->hasLocalName(tableTag))
165
207
if (e->hasLocalName(tdTag) || e->hasLocalName(thTag))
167
else if (e->hasLocalName(tableTag))
171
210
case TSectionRows:
172
211
if (e->hasLocalName(trTag))
174
else if (e->hasLocalName(tableTag))
177
214
case SelectOptions:
178
215
if (e->hasLocalName(optionTag))
182
219
if (e->hasLocalName(areaTag))
185
case DocApplets: // all APPLET elements and OBJECT elements that contain Java Applets
186
if (e->hasLocalName(appletTag) ||
187
(e->hasLocalName(objectTag) && static_cast<HTMLObjectElement*>(e)->containsJavaApplet()))
190
case DocEmbeds: // all EMBED elements
222
case DocApplets: // all <applet> elements and <object> elements that contain Java Applets
223
if (e->hasLocalName(appletTag))
225
if (e->hasLocalName(objectTag) && static_cast<HTMLObjectElement*>(e)->containsJavaApplet())
191
229
if (e->hasLocalName(embedTag))
194
case DocObjects: // all OBJECT elements
195
233
if (e->hasLocalName(objectTag))
198
case DocLinks: // all A _and_ AREA elements with a value for href
199
if (e->hasLocalName(aTag) || e->hasLocalName(areaTag))
200
if (!e->getAttribute(hrefAttr).isNull())
203
case DocAnchors: // all A elements with a value for name or an id attribute
204
if (e->hasLocalName(aTag))
205
if (!e->getAttribute(nameAttr).isNull())
236
case DocLinks: // all <a> and <area> elements with a value for href
237
if ((e->hasLocalName(aTag) || e->hasLocalName(areaTag)) && (!e->getAttribute(hrefAttr).isNull()))
240
case DocAnchors: // all <a> elements with a value for name
241
if (e->hasLocalName(aTag) && !e->getAttribute(nameAttr).isNull())
211
245
case NodeChildren:
222
current = current->traverseNextNode(m_base.get());
247
case DocumentNamedItems:
249
case WindowNamedItems:
250
ASSERT_NOT_REACHED();
226
current = current->traverseNextSibling(m_base.get());
232
258
unsigned HTMLCollection::calcLength() const
234
260
unsigned len = 0;
236
for (Node *current = traverseNextItem(m_base.get()); current; current = traverseNextItem(current)) {
261
for (Element* current = itemAfter(0); current; current = itemAfter(current))
245
268
unsigned HTMLCollection::length() const
247
270
resetCollectionInfo();
248
if (!info->haslength) {
249
info->length = calcLength();
250
info->haslength = true;
271
if (!m_info->hasLength) {
272
m_info->length = calcLength();
273
m_info->hasLength = true;
275
return m_info->length;
255
Node *HTMLCollection::item( unsigned index ) const
278
Node* HTMLCollection::item(unsigned index) const
257
280
resetCollectionInfo();
258
if (info->current && info->position == index) {
259
return info->current;
261
if (info->haslength && info->length <= index) {
281
if (m_info->current && m_info->position == index)
282
return m_info->current;
283
if (m_info->hasLength && m_info->length <= index)
264
if (!info->current || info->position > index) {
265
info->current = traverseNextItem(m_base.get());
285
if (!m_info->current || m_info->position > index) {
286
m_info->current = itemAfter(0);
287
m_info->position = 0;
288
if (!m_info->current)
270
Node *node = info->current;
271
for (unsigned pos = info->position; node && pos < index; pos++) {
272
node = traverseNextItem(node);
274
info->current = node;
275
info->position = index;
276
return info->current;
291
Element* e = m_info->current;
292
for (unsigned pos = m_info->position; e && pos < index; pos++)
295
m_info->position = index;
296
return m_info->current;
279
Node *HTMLCollection::firstItem() const
299
Node* HTMLCollection::firstItem() const
284
Node *HTMLCollection::nextItem() const
304
Node* HTMLCollection::nextItem() const
286
306
resetCollectionInfo();
288
308
// Look for the 'second' item. The first one is currentItem, already given back.
289
Node *retval = traverseNextItem(info->current);
290
info->current = retval;
309
Element* retval = itemAfter(m_info->current);
310
m_info->current = retval;
295
bool HTMLCollection::checkForNameMatch(Node *node, bool checkName, const String &name, bool caseSensitive) const
315
bool HTMLCollection::checkForNameMatch(Element* element, bool checkName, const String& name, bool caseSensitive) const
297
if (!node->isHTMLElement())
317
if (!element->isHTMLElement())
300
HTMLElement *e = static_cast<HTMLElement*>(node);
320
HTMLElement* e = static_cast<HTMLElement*>(element);
301
321
if (caseSensitive) {
303
323
// document.all returns only images, forms, applets, objects and embeds
304
324
// by name (though everything by id)
305
if (type == DocAll &&
325
if (m_type == DocAll &&
306
326
!(e->hasLocalName(imgTag) || e->hasLocalName(formTag) ||
307
327
e->hasLocalName(appletTag) || e->hasLocalName(objectTag) ||
308
328
e->hasLocalName(embedTag) || e->hasLocalName(inputTag) ||
317
337
// document.all returns only images, forms, applets, objects and embeds
318
338
// by name (though everything by id)
319
if (type == DocAll &&
339
if (m_type == DocAll &&
320
340
!(e->hasLocalName(imgTag) || e->hasLocalName(formTag) ||
321
341
e->hasLocalName(appletTag) || e->hasLocalName(objectTag) ||
322
342
e->hasLocalName(embedTag) || e->hasLocalName(inputTag) ||
323
343
e->hasLocalName(selectTag)))
326
return e->getAttribute(nameAttr).domString().lower() == name.lower() &&
327
e->getAttribute(idAttr).domString().lower() != name.lower();
346
return e->getAttribute(nameAttr).string().lower() == name.lower() &&
347
e->getAttribute(idAttr).string().lower() != name.lower();
329
return e->getAttribute(idAttr).domString().lower() == name.lower();
349
return e->getAttribute(idAttr).string().lower() == name.lower();
340
360
// object with a matching name attribute, but only on those elements
341
361
// that are allowed a name attribute.
342
362
resetCollectionInfo();
346
for (n = traverseNextItem(m_base.get()); n; n = traverseNextItem(n)) {
347
if (checkForNameMatch(n, idsDone, name, caseSensitive))
365
for (Element* e = itemAfter(0); e; e = itemAfter(e)) {
366
if (checkForNameMatch(e, m_idsDone, name, caseSensitive)) {
353
return info->current;
356
for (n = traverseNextItem(m_base.get()); n; n = traverseNextItem(n)) {
357
if (checkForNameMatch(n, idsDone, name, caseSensitive))
374
for (Element* e = itemAfter(0); e; e = itemAfter(e)) {
375
if (checkForNameMatch(e, m_idsDone, name, caseSensitive)) {
362
return info->current;
365
385
void HTMLCollection::updateNameCache() const
367
if (info->hasNameCache)
387
if (m_info->hasNameCache)
370
for (Node *n = traverseNextItem(m_base.get()); n; n = traverseNextItem(n)) {
371
if (!n->isHTMLElement())
390
for (Element* element = itemAfter(0); element; element = itemAfter(element)) {
391
if (!element->isHTMLElement())
373
HTMLElement* e = static_cast<HTMLElement*>(n);
393
HTMLElement* e = static_cast<HTMLElement*>(element);
374
394
const AtomicString& idAttrVal = e->getAttribute(idAttr);
375
395
const AtomicString& nameAttrVal = e->getAttribute(nameAttr);
376
396
if (!idAttrVal.isEmpty()) {
377
397
// add to id cache
378
Vector<Node*>* idVector = info->idCache.get(idAttrVal.impl());
398
Vector<Element*>* idVector = m_info->idCache.get(idAttrVal.impl());
380
idVector = new Vector<Node*>;
381
info->idCache.add(idAttrVal.impl(), idVector);
400
idVector = new Vector<Element*>;
401
m_info->idCache.add(idAttrVal.impl(), idVector);
385
405
if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal
386
&& (type != DocAll ||
406
&& (m_type != DocAll ||
387
407
(e->hasLocalName(imgTag) || e->hasLocalName(formTag) ||
388
408
e->hasLocalName(appletTag) || e->hasLocalName(objectTag) ||
389
409
e->hasLocalName(embedTag) || e->hasLocalName(inputTag) ||
390
410
e->hasLocalName(selectTag)))) {
391
411
// add to name cache
392
Vector<Node*>* nameVector = info->nameCache.get(nameAttrVal.impl());
412
Vector<Element*>* nameVector = m_info->nameCache.get(nameAttrVal.impl());
393
413
if (!nameVector) {
394
nameVector = new Vector<Node*>;
395
info->nameCache.add(nameAttrVal.impl(), nameVector);
414
nameVector = new Vector<Element*>;
415
m_info->nameCache.add(nameAttrVal.impl(), nameVector);
397
nameVector->append(n);
417
nameVector->append(e);
401
info->hasNameCache = true;
421
m_info->hasNameCache = true;
404
void HTMLCollection::namedItems(const AtomicString &name, Vector<RefPtr<Node> >& result) const
424
void HTMLCollection::namedItems(const AtomicString& name, Vector<RefPtr<Node> >& result) const
406
426
ASSERT(result.isEmpty());
425
Node *HTMLCollection::nextNamedItem(const String &name) const
445
Node* HTMLCollection::nextNamedItem(const String& name) const
427
447
resetCollectionInfo();
429
for (Node *n = traverseNextItem(info->current ? info->current : m_base.get()); n; n = traverseNextItem(n)) {
430
if (checkForNameMatch(n, idsDone, name, true)) {
449
for (Element* e = itemAfter(m_info->current); e; e = itemAfter(e)) {
450
if (checkForNameMatch(e, m_idsDone, name, true)) {
442
for (Node *n = traverseNextItem(info->current ? info->current : m_base.get()); n; n = traverseNextItem(n)) {
443
if (checkForNameMatch(n, idsDone, name, true)) {
462
for (Element* e = itemAfter(m_info->current); e; e = itemAfter(e)) {
463
if (checkForNameMatch(e, m_idsDone, name, true)) {