2010-07-02 Adam Barth <abarth@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 class HTMLElementStack::ElementRecord : public Noncopyable {
42 public:
43     ElementRecord(PassRefPtr<Element> element, PassOwnPtr<ElementRecord> next)
44         : m_element(element)
45         , m_next(next)
46     {
47     }
48
49     Element* element() const { return m_element.get(); }
50     ElementRecord* next() const { return m_next.get(); }
51     PassOwnPtr<ElementRecord> releaseNext() { return m_next.release(); }
52     void setNext(PassOwnPtr<ElementRecord> next) { m_next = next; }
53
54 private:
55     RefPtr<Element> m_element;
56     OwnPtr<ElementRecord> m_next;
57 };
58
59 HTMLElementStack::HTMLElementStack()
60     : m_htmlElement(0)
61     , m_headElement(0)
62     , m_bodyElement(0)
63 {
64 }
65
66 HTMLElementStack::~HTMLElementStack()
67 {
68 }
69
70 void HTMLElementStack::popHTMLHeadElement()
71 {
72     ASSERT(top() == m_headElement);
73     m_headElement = 0;
74     popCommon();
75 }
76
77 void HTMLElementStack::popHTMLBodyElement()
78 {
79     ASSERT(top() == m_bodyElement);
80     m_bodyElement = 0;
81     popCommon();
82 }
83
84 void HTMLElementStack::pop()
85 {
86     ASSERT(!top()->hasTagName(HTMLNames::headTag));
87     popCommon();
88 }
89
90 void HTMLElementStack::popUntil(const AtomicString& tagName)
91 {
92     while (!top()->hasLocalName(tagName)) {
93         // pop() will ASSERT at <body> if callers fail to check that there is an
94         // element with localName |tagName| on the stack of open elements.
95         pop();
96     }
97 }
98
99 void HTMLElementStack::popUntil(Element* element)
100 {
101     while (top() != element)
102         pop();
103 }
104
105 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element)
106 {
107     ASSERT(element->hasTagName(HTMLNames::htmlTag));
108     ASSERT(!m_htmlElement);
109     m_htmlElement = element.get();
110     pushCommon(element);
111 }
112
113 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element)
114 {
115     ASSERT(element->hasTagName(HTMLNames::headTag));
116     ASSERT(!m_headElement);
117     m_headElement = element.get();
118     pushCommon(element);
119 }
120
121 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element)
122 {
123     ASSERT(element->hasTagName(HTMLNames::bodyTag));
124     ASSERT(!m_bodyElement);
125     m_bodyElement = element.get();
126     pushCommon(element);
127 }
128
129 void HTMLElementStack::push(PassRefPtr<Element> element)
130 {
131     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
132     ASSERT(!element->hasTagName(HTMLNames::headTag));
133     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
134     ASSERT(m_htmlElement);
135     pushCommon(element);
136 }
137
138 Element* HTMLElementStack::top() const
139 {
140     return m_top->element();
141 }
142
143 void HTMLElementStack::removeHTMLHeadElement(Element* element)
144 {
145     ASSERT(m_headElement == element);
146     if (m_top->element() == element) {
147         popHTMLHeadElement();
148         return;
149     }
150     m_headElement = 0;
151     removeNonFirstCommon(element);
152 }
153
154 void HTMLElementStack::remove(Element* element)
155 {
156     ASSERT(!element->hasTagName(HTMLNames::headTag));
157     if (m_top->element() == element) {
158         pop();
159         return;
160     }
161     removeNonFirstCommon(element);
162 }
163
164 bool HTMLElementStack::contains(Element* element) const
165 {
166     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
167         if (pos->element() == element)
168             return true;
169     }
170     return false;
171 }
172
173 namespace {
174
175 inline bool isScopeMarker(const Element* element)
176 {
177     return element->hasTagName(appletTag)
178         || element->hasTagName(captionTag)
179         || element->hasTagName(appletTag)
180         || element->hasTagName(htmlTag)
181         || element->hasTagName(tableTag)
182         || element->hasTagName(tdTag)
183         || element->hasTagName(thTag)
184         || element->hasTagName(buttonTag)
185         || element->hasTagName(marqueeTag)
186         || element->hasTagName(objectTag)
187 #if ENABLE(SVG_FOREIGN_OBJECT)
188         || element->hasTagName(SVGNames::foreignObjectTag)
189 #endif
190         ;
191 }
192
193 inline bool isListItemScopeMarker(const Element* element)
194 {
195     return isScopeMarker(element)
196         || element->hasTagName(olTag)
197         || element->hasTagName(ulTag);
198 }
199 inline bool isTableScopeMarker(const Element* element)
200 {
201     return element->hasTagName(htmlTag)
202         || element->hasTagName(tableTag);
203 }
204
205 }
206
207 template <bool isMarker(const Element*)>
208 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
209 {
210     for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
211         Element* element = pos->element();
212         if (element->hasLocalName(targetTag))
213             return true;
214         if (isMarker(element))
215             return false;
216     }
217     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
218     return false;
219 }
220
221 bool HTMLElementStack::inScope(Element* targetElement) const
222 {
223     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
224         Element* element = pos->element();
225         if (element == targetElement)
226             return true;
227         if (isScopeMarker(element))
228             return false;
229     }
230     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
231     return false;
232 }
233
234 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
235 {
236     return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
237 }
238
239 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
240 {
241     return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
242 }
243
244 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
245 {
246     return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
247 }
248
249 Element* HTMLElementStack::htmlElement()
250 {
251     ASSERT(m_htmlElement);
252     return m_htmlElement;
253 }
254
255 Element* HTMLElementStack::headElement()
256 {
257     ASSERT(m_headElement);
258     return m_headElement;
259 }
260
261 Element* HTMLElementStack::bodyElement()
262 {
263     ASSERT(m_bodyElement);
264     return m_bodyElement;
265 }
266
267 void HTMLElementStack::pushCommon(PassRefPtr<Element> element)
268 {
269     m_top.set(new ElementRecord(element, m_top.release()));
270     top()->beginParsingChildren();
271 }
272
273 void HTMLElementStack::popCommon()
274 {
275     ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
276     ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement);
277     ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement);
278     top()->finishParsingChildren();
279     m_top = m_top->releaseNext();
280 }
281
282 void HTMLElementStack::removeNonFirstCommon(Element* element)
283 {
284     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
285     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
286     ElementRecord* pos = m_top.get();
287     ASSERT(pos->element() != element);
288     while (pos->next()) {
289         if (pos->next()->element() == element) {
290             // FIXME: Is it OK to call finishParsingChildren()
291             // when the children aren't actually finished?
292             element->finishParsingChildren();
293             pos->setNext(pos->next()->releaseNext());
294             return;
295         }
296     }
297     ASSERT_NOT_REACHED();
298 }
299
300 }