1/*
2 * Copyright (C) 2008, 2011, 2012, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "HTMLTableRowsCollection.h"
31
32#include "ElementIterator.h"
33#include "HTMLNames.h"
34#include "HTMLTableElement.h"
35#include "HTMLTableRowElement.h"
36#include <wtf/IsoMallocInlines.h>
37
38namespace WebCore {
39
40using namespace HTMLNames;
41
42WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLTableRowsCollection);
43
44static inline void assertRowIsInTable(HTMLTableElement& table, HTMLTableRowElement* row)
45{
46#if !ASSERT_DISABLED
47 UNUSED_PARAM(table);
48 UNUSED_PARAM(row);
49#else
50 if (!row)
51 return;
52 if (row->parentNode() == &table)
53 return;
54 ASSERT(row->parentNode());
55 ASSERT(row->parentNode()->hasTagName(theadTag) || row->parentNode()->hasTagName(tbodyTag) || row->parentNode()->hasTagName(tfootTag));
56 ASSERT(row->parentNode()->parentNode() == &table);
57#endif
58}
59
60static inline bool isInSection(HTMLTableRowElement& row, const HTMLQualifiedName& sectionTag)
61{
62 // Because we know that the parent is a table or a section, it's safe to cast it to an HTMLElement
63 // giving us access to the faster hasTagName overload from that class.
64 return downcast<HTMLElement>(*row.parentNode()).hasTagName(sectionTag);
65}
66
67HTMLTableRowElement* HTMLTableRowsCollection::rowAfter(HTMLTableElement& table, HTMLTableRowElement* previous)
68{
69 // The HTMLCollection caching mechanism, along with the code in this class, will guarantee that the previous row
70 // is an immediate child of either the table, or a table section that is itself an immediate child of the table.
71 assertRowIsInTable(table, previous);
72
73 // Start by looking for the next row in this section. Continue only if there is none.
74 if (previous && previous->parentNode() != &table) {
75 auto childRows = childrenOfType<HTMLTableRowElement>(*previous->parentNode());
76 auto row = childRows.beginAt(*previous);
77 if (++row != childRows.end())
78 return &*row;
79 }
80
81 RefPtr<Element> child;
82
83 // If still looking at head sections, find the first row in the next head section.
84 if (!previous)
85 child = ElementTraversal::firstChild(table);
86 else if (isInSection(*previous, theadTag))
87 child = ElementTraversal::nextSibling(*previous->parentNode());
88 for (; child; child = ElementTraversal::nextSibling(*child)) {
89 if (child->hasTagName(theadTag)) {
90 if (auto row = childrenOfType<HTMLTableRowElement>(*child).first())
91 return row;
92 }
93 }
94
95 // If still looking at top level and bodies, find the next row in top level or the first in the next body section.
96 if (!previous || isInSection(*previous, theadTag))
97 child = ElementTraversal::firstChild(table);
98 else if (previous->parentNode() == &table)
99 child = ElementTraversal::nextSibling(*previous);
100 else if (isInSection(*previous, tbodyTag))
101 child = ElementTraversal::nextSibling(*previous->parentNode());
102 for (; child; child = ElementTraversal::nextSibling(*child)) {
103 if (is<HTMLTableRowElement>(*child))
104 return downcast<HTMLTableRowElement>(child.get());
105 if (child->hasTagName(tbodyTag)) {
106 if (auto row = childrenOfType<HTMLTableRowElement>(*child).first())
107 return row;
108 }
109 }
110
111 // Find the first row in the next foot section.
112 if (!previous || !isInSection(*previous, tfootTag))
113 child = ElementTraversal::firstChild(table);
114 else
115 child = ElementTraversal::nextSibling(*previous->parentNode());
116 for (; child; child = ElementTraversal::nextSibling(*child)) {
117 if (child->hasTagName(tfootTag)) {
118 if (auto row = childrenOfType<HTMLTableRowElement>(*child).first())
119 return row;
120 }
121 }
122
123 return nullptr;
124}
125
126HTMLTableRowElement* HTMLTableRowsCollection::lastRow(HTMLTableElement& table)
127{
128 for (auto* child = ElementTraversal::lastChild(table); child; child = ElementTraversal::previousSibling(*child)) {
129 if (child->hasTagName(tfootTag)) {
130 if (auto* row = childrenOfType<HTMLTableRowElement>(*child).last())
131 return row;
132 }
133 }
134
135 for (auto* child = ElementTraversal::lastChild(table); child; child = ElementTraversal::previousSibling(*child)) {
136 if (is<HTMLTableRowElement>(*child))
137 return downcast<HTMLTableRowElement>(child);
138 if (child->hasTagName(tbodyTag)) {
139 if (auto* row = childrenOfType<HTMLTableRowElement>(*child).last())
140 return row;
141 }
142 }
143
144 for (auto* child = ElementTraversal::lastChild(table); child; child = ElementTraversal::previousSibling(*child)) {
145 if (child->hasTagName(theadTag)) {
146 if (auto* row = childrenOfType<HTMLTableRowElement>(*child).last())
147 return row;
148 }
149 }
150
151 return nullptr;
152}
153
154HTMLTableRowsCollection::HTMLTableRowsCollection(HTMLTableElement& table)
155 : CachedHTMLCollection<HTMLTableRowsCollection, CollectionTypeTraits<TableRows>::traversalType>(table, TableRows)
156{
157}
158
159Ref<HTMLTableRowsCollection> HTMLTableRowsCollection::create(HTMLTableElement& table, CollectionType type)
160{
161 ASSERT_UNUSED(type, type == TableRows);
162 return adoptRef(*new HTMLTableRowsCollection(table));
163}
164
165Element* HTMLTableRowsCollection::customElementAfter(Element* previous) const
166{
167 return rowAfter(const_cast<HTMLTableElement&>(tableElement()), downcast<HTMLTableRowElement>(previous));
168}
169
170}
171