2010-07-04 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 }
80
81 HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<Element> element, PassOwnPtr<ElementRecord> next)
82     : m_element(element)
83     , m_next(next)
84 {
85     ASSERT(m_element);
86 }
87
88 HTMLElementStack::ElementRecord::~ElementRecord()
89 {
90 }
91
92 void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<Element> element)
93 {
94     ASSERT(element);
95     // FIXME: Should this call finishParsingChildren?
96     m_element = element;
97 }
98
99 bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const
100 {
101     for (ElementRecord* below = next(); below; below = below->next()) {
102         if (below == other)
103             return true;
104     }
105     return false;
106 }
107
108 HTMLElementStack::HTMLElementStack()
109     : m_htmlElement(0)
110     , m_headElement(0)
111     , m_bodyElement(0)
112 {
113 }
114
115 HTMLElementStack::~HTMLElementStack()
116 {
117 }
118
119 void HTMLElementStack::popHTMLHeadElement()
120 {
121     ASSERT(top() == m_headElement);
122     m_headElement = 0;
123     popCommon();
124 }
125
126 void HTMLElementStack::popHTMLBodyElement()
127 {
128     ASSERT(top() == m_bodyElement);
129     m_bodyElement = 0;
130     popCommon();
131 }
132
133 void HTMLElementStack::pop()
134 {
135     ASSERT(!top()->hasTagName(HTMLNames::headTag));
136     popCommon();
137 }
138
139 void HTMLElementStack::popUntil(const AtomicString& tagName)
140 {
141     while (!top()->hasLocalName(tagName)) {
142         // pop() will ASSERT at <body> if callers fail to check that there is an
143         // element with localName |tagName| on the stack of open elements.
144         pop();
145     }
146 }
147
148 void HTMLElementStack::popUntil(Element* element)
149 {
150     while (top() != element)
151         pop();
152 }
153
154 void HTMLElementStack::popUntilTableScopeMarker()
155 {
156     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context
157     while (!isTableScopeMarker(top()))
158         pop();
159 }
160
161 void HTMLElementStack::popUntilTableBodyScopeMarker()
162 {
163     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context
164     while (!isTableBodyScopeMarker(top()))
165         pop();
166 }
167
168 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element)
169 {
170     ASSERT(!m_top); // <html> should always be the bottom of the stack.
171     ASSERT(element->hasTagName(HTMLNames::htmlTag));
172     ASSERT(!m_htmlElement);
173     m_htmlElement = element.get();
174     pushCommon(element);
175 }
176
177 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element)
178 {
179     ASSERT(element->hasTagName(HTMLNames::headTag));
180     ASSERT(!m_headElement);
181     m_headElement = element.get();
182     pushCommon(element);
183 }
184
185 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element)
186 {
187     ASSERT(element->hasTagName(HTMLNames::bodyTag));
188     ASSERT(!m_bodyElement);
189     m_bodyElement = element.get();
190     pushCommon(element);
191 }
192
193 void HTMLElementStack::push(PassRefPtr<Element> element)
194 {
195     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
196     ASSERT(!element->hasTagName(HTMLNames::headTag));
197     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
198     ASSERT(m_htmlElement);
199     pushCommon(element);
200 }
201
202 void HTMLElementStack::insertAbove(PassRefPtr<Element> element, ElementRecord* recordBelow)
203 {
204     ASSERT(element);
205     ASSERT(recordBelow);
206     ASSERT(m_top);
207     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
208     ASSERT(!element->hasTagName(HTMLNames::headTag));
209     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
210     ASSERT(m_htmlElement);
211     if (recordBelow == m_top) {
212         push(element);
213         return;
214     }
215
216     for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) {
217         if (recordAbove->next() != recordBelow)
218             continue;
219
220         recordAbove->setNext(new ElementRecord(element, recordAbove->releaseNext()));
221         recordAbove->next()->element()->beginParsingChildren();
222         return;
223     }
224     ASSERT_NOT_REACHED();
225 }
226
227 HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const
228 {
229     return m_top.get();
230 }
231
232 Element* HTMLElementStack::top() const
233 {
234     return m_top->element();
235 }
236
237 Element* HTMLElementStack::bottom() const
238 {
239     return htmlElement();
240 }
241
242 void HTMLElementStack::removeHTMLHeadElement(Element* element)
243 {
244     ASSERT(m_headElement == element);
245     if (m_top->element() == element) {
246         popHTMLHeadElement();
247         return;
248     }
249     m_headElement = 0;
250     removeNonTopCommon(element);
251 }
252
253 void HTMLElementStack::remove(Element* element)
254 {
255     ASSERT(!element->hasTagName(HTMLNames::headTag));
256     if (m_top->element() == element) {
257         pop();
258         return;
259     }
260     removeNonTopCommon(element);
261 }
262
263 HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const
264 {
265     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
266         if (pos->element() == element)
267             return pos;
268     }
269     return 0;
270 }
271
272 HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const
273 {
274     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
275         if (pos->element()->hasLocalName(tagName))
276             return pos;
277     }
278     return 0;
279 }
280
281 bool HTMLElementStack::contains(Element* element) const
282 {
283     return !!find(element);
284 }
285
286 template <bool isMarker(Element*)>
287 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
288 {
289     for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
290         Element* element = pos->element();
291         if (element->hasLocalName(targetTag))
292             return true;
293         if (isMarker(element))
294             return false;
295     }
296     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
297     return false;
298 }
299
300 bool HTMLElementStack::inScope(Element* targetElement) const
301 {
302     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
303         Element* element = pos->element();
304         if (element == targetElement)
305             return true;
306         if (isScopeMarker(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(const AtomicString& targetTag) const
314 {
315     return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
316 }
317
318 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
319 {
320     return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
321 }
322
323 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
324 {
325     return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
326 }
327
328 Element* HTMLElementStack::htmlElement() const
329 {
330     ASSERT(m_htmlElement);
331     return m_htmlElement;
332 }
333
334 Element* HTMLElementStack::headElement() const
335 {
336     ASSERT(m_headElement);
337     return m_headElement;
338 }
339
340 Element* HTMLElementStack::bodyElement() const
341 {
342     ASSERT(m_bodyElement);
343     return m_bodyElement;
344 }
345
346 void HTMLElementStack::pushCommon(PassRefPtr<Element> element)
347 {
348     ASSERT(m_htmlElement);
349     m_top.set(new ElementRecord(element, m_top.release()));
350     top()->beginParsingChildren();
351 }
352
353 void HTMLElementStack::popCommon()
354 {
355     ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
356     ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement);
357     ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement);
358     top()->finishParsingChildren();
359     m_top = m_top->releaseNext();
360 }
361
362 void HTMLElementStack::removeNonTopCommon(Element* element)
363 {
364     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
365     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
366     ASSERT(top() != element);
367     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
368         if (pos->next()->element() == element) {
369             // FIXME: Is it OK to call finishParsingChildren()
370             // when the children aren't actually finished?
371             element->finishParsingChildren();
372             pos->setNext(pos->next()->releaseNext());
373             return;
374         }
375     }
376     ASSERT_NOT_REACHED();
377 }
378
379 }