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