2010-07-05 Eric Seidel <eric@webkit.org>
[WebKit-https.git] / WebCore / html / HTMLElementStack.cpp
1 /*
2  * Copyright (C) 2010 Google, 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GOOGLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "HTMLElementStack.h"
28
29 #include "Element.h"
30 #include "HTMLNames.h"
31 #include <wtf/PassOwnPtr.h>
32
33 #if ENABLE(SVG)
34 #include "SVGNames.h"
35 #endif
36
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
41 namespace {
42
43 inline bool isScopeMarker(Element* element)
44 {
45     return element->hasTagName(appletTag)
46         || element->hasTagName(buttonTag)
47         || element->hasTagName(captionTag)
48 #if ENABLE(SVG_FOREIGN_OBJECT)
49         || element->hasTagName(SVGNames::foreignObjectTag)
50 #endif
51         || element->hasTagName(htmlTag)
52         || element->hasTagName(marqueeTag)
53         || element->hasTagName(objectTag)
54         || element->hasTagName(tableTag)
55         || element->hasTagName(tdTag)
56         || element->hasTagName(thTag);
57 }
58
59 inline bool isListItemScopeMarker(Element* element)
60 {
61     return isScopeMarker(element)
62         || element->hasTagName(olTag)
63         || element->hasTagName(ulTag);
64 }
65 inline bool isTableScopeMarker(Element* element)
66 {
67     return element->hasTagName(tableTag)
68         || element->hasTagName(htmlTag);
69 }
70
71 inline bool isTableBodyScopeMarker(Element* element)
72 {
73     return element->hasTagName(tbodyTag)
74         || element->hasTagName(tfootTag)
75         || element->hasTagName(theadTag)
76         || element->hasTagName(htmlTag);
77 }
78
79 inline bool isTableRowScopeMarker(Element* element)
80 {
81     return element->hasTagName(trTag)
82         || element->hasTagName(htmlTag);
83 }
84
85 }
86
87 HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<Element> element, PassOwnPtr<ElementRecord> next)
88     : m_element(element)
89     , m_next(next)
90 {
91     ASSERT(m_element);
92 }
93
94 HTMLElementStack::ElementRecord::~ElementRecord()
95 {
96 }
97
98 void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<Element> element)
99 {
100     ASSERT(element);
101     // FIXME: Should this call finishParsingChildren?
102     m_element = element;
103 }
104
105 bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const
106 {
107     for (ElementRecord* below = next(); below; below = below->next()) {
108         if (below == other)
109             return true;
110     }
111     return false;
112 }
113
114 HTMLElementStack::HTMLElementStack()
115     : m_htmlElement(0)
116     , m_headElement(0)
117     , m_bodyElement(0)
118 {
119 }
120
121 HTMLElementStack::~HTMLElementStack()
122 {
123 }
124
125 void HTMLElementStack::popHTMLHeadElement()
126 {
127     ASSERT(top() == m_headElement);
128     m_headElement = 0;
129     popCommon();
130 }
131
132 void HTMLElementStack::popHTMLBodyElement()
133 {
134     ASSERT(top() == m_bodyElement);
135     m_bodyElement = 0;
136     popCommon();
137 }
138
139 void HTMLElementStack::pop()
140 {
141     ASSERT(!top()->hasTagName(HTMLNames::headTag));
142     popCommon();
143 }
144
145 void HTMLElementStack::popUntil(const AtomicString& tagName)
146 {
147     while (!top()->hasLocalName(tagName)) {
148         // pop() will ASSERT at <body> if callers fail to check that there is an
149         // element with localName |tagName| on the stack of open elements.
150         pop();
151     }
152 }
153
154 void HTMLElementStack::popUntil(Element* element)
155 {
156     while (top() != element)
157         pop();
158 }
159
160 void HTMLElementStack::popUntilTableScopeMarker()
161 {
162     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context
163     while (!isTableScopeMarker(top()))
164         pop();
165 }
166
167 void HTMLElementStack::popUntilTableBodyScopeMarker()
168 {
169     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context
170     while (!isTableBodyScopeMarker(top()))
171         pop();
172 }
173
174 void HTMLElementStack::popUntilTableRowScopeMarker()
175 {
176     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-row-context
177     while (!isTableRowScopeMarker(top()))
178         pop();
179 }
180
181 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element)
182 {
183     ASSERT(!m_top); // <html> should always be the bottom of the stack.
184     ASSERT(element->hasTagName(HTMLNames::htmlTag));
185     ASSERT(!m_htmlElement);
186     m_htmlElement = element.get();
187     pushCommon(element);
188 }
189
190 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element)
191 {
192     ASSERT(element->hasTagName(HTMLNames::headTag));
193     ASSERT(!m_headElement);
194     m_headElement = element.get();
195     pushCommon(element);
196 }
197
198 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element)
199 {
200     ASSERT(element->hasTagName(HTMLNames::bodyTag));
201     ASSERT(!m_bodyElement);
202     m_bodyElement = element.get();
203     pushCommon(element);
204 }
205
206 void HTMLElementStack::push(PassRefPtr<Element> element)
207 {
208     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
209     ASSERT(!element->hasTagName(HTMLNames::headTag));
210     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
211     ASSERT(m_htmlElement);
212     pushCommon(element);
213 }
214
215 void HTMLElementStack::insertAbove(PassRefPtr<Element> element, ElementRecord* recordBelow)
216 {
217     ASSERT(element);
218     ASSERT(recordBelow);
219     ASSERT(m_top);
220     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
221     ASSERT(!element->hasTagName(HTMLNames::headTag));
222     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
223     ASSERT(m_htmlElement);
224     if (recordBelow == m_top) {
225         push(element);
226         return;
227     }
228
229     for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) {
230         if (recordAbove->next() != recordBelow)
231             continue;
232
233         recordAbove->setNext(new ElementRecord(element, recordAbove->releaseNext()));
234         recordAbove->next()->element()->beginParsingChildren();
235         return;
236     }
237     ASSERT_NOT_REACHED();
238 }
239
240 HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const
241 {
242     return m_top.get();
243 }
244
245 Element* HTMLElementStack::top() const
246 {
247     return m_top->element();
248 }
249
250 Element* HTMLElementStack::bottom() const
251 {
252     return htmlElement();
253 }
254
255 void HTMLElementStack::removeHTMLHeadElement(Element* element)
256 {
257     ASSERT(m_headElement == element);
258     if (m_top->element() == element) {
259         popHTMLHeadElement();
260         return;
261     }
262     m_headElement = 0;
263     removeNonTopCommon(element);
264 }
265
266 void HTMLElementStack::remove(Element* element)
267 {
268     ASSERT(!element->hasTagName(HTMLNames::headTag));
269     if (m_top->element() == element) {
270         pop();
271         return;
272     }
273     removeNonTopCommon(element);
274 }
275
276 HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const
277 {
278     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
279         if (pos->element() == element)
280             return pos;
281     }
282     return 0;
283 }
284
285 HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const
286 {
287     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
288         if (pos->element()->hasLocalName(tagName))
289             return pos;
290     }
291     return 0;
292 }
293
294 bool HTMLElementStack::contains(Element* element) const
295 {
296     return !!find(element);
297 }
298
299 template <bool isMarker(Element*)>
300 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
301 {
302     for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
303         Element* element = pos->element();
304         if (element->hasLocalName(targetTag))
305             return true;
306         if (isMarker(element))
307             return false;
308     }
309     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
310     return false;
311 }
312
313 bool HTMLElementStack::inScope(Element* targetElement) const
314 {
315     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
316         Element* element = pos->element();
317         if (element == targetElement)
318             return true;
319         if (isScopeMarker(element))
320             return false;
321     }
322     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
323     return false;
324 }
325
326 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
327 {
328     return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
329 }
330
331 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
332 {
333     return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
334 }
335
336 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
337 {
338     return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
339 }
340
341 Element* HTMLElementStack::htmlElement() const
342 {
343     ASSERT(m_htmlElement);
344     return m_htmlElement;
345 }
346
347 Element* HTMLElementStack::headElement() const
348 {
349     ASSERT(m_headElement);
350     return m_headElement;
351 }
352
353 Element* HTMLElementStack::bodyElement() const
354 {
355     ASSERT(m_bodyElement);
356     return m_bodyElement;
357 }
358
359 void HTMLElementStack::pushCommon(PassRefPtr<Element> element)
360 {
361     ASSERT(m_htmlElement);
362     m_top.set(new ElementRecord(element, m_top.release()));
363     top()->beginParsingChildren();
364 }
365
366 void HTMLElementStack::popCommon()
367 {
368     ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
369     ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement);
370     ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement);
371     top()->finishParsingChildren();
372     m_top = m_top->releaseNext();
373 }
374
375 void HTMLElementStack::removeNonTopCommon(Element* element)
376 {
377     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
378     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
379     ASSERT(top() != element);
380     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
381         if (pos->next()->element() == element) {
382             // FIXME: Is it OK to call finishParsingChildren()
383             // when the children aren't actually finished?
384             element->finishParsingChildren();
385             pos->setNext(pos->next()->releaseNext());
386             return;
387         }
388     }
389     ASSERT_NOT_REACHED();
390 }
391
392 }