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::pushHTMLHtmlElement(PassRefPtr<Element> element)
81 {
82     ASSERT(element->hasTagName(HTMLNames::htmlTag));
83     ASSERT(!m_htmlElement);
84     m_htmlElement = element.get();
85     pushCommon(element);
86 }
87
88 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element)
89 {
90     ASSERT(element->hasTagName(HTMLNames::headTag));
91     ASSERT(!m_headElement);
92     m_headElement = element.get();
93     pushCommon(element);
94 }
95
96 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element)
97 {
98     ASSERT(element->hasTagName(HTMLNames::bodyTag));
99     ASSERT(!m_bodyElement);
100     m_bodyElement = element.get();
101     pushCommon(element);
102 }
103
104 void HTMLElementStack::push(PassRefPtr<Element> element)
105 {
106     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
107     ASSERT(!element->hasTagName(HTMLNames::headTag));
108     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
109     ASSERT(m_htmlElement);
110     pushCommon(element);
111 }
112
113 Element* HTMLElementStack::top() const
114 {
115     return m_top->element();
116 }
117
118 void HTMLElementStack::removeHTMLHeadElement(Element* element)
119 {
120     ASSERT(m_headElement == element);
121     if (m_top->element() == element) {
122         popHTMLHeadElement();
123         return;
124     }
125     m_headElement = 0;
126     removeNonFirstCommon(element);
127 }
128
129 void HTMLElementStack::remove(Element* element)
130 {
131     ASSERT(!element->hasTagName(HTMLNames::headTag));
132     if (m_top->element() == element) {
133         pop();
134         return;
135     }
136     removeNonFirstCommon(element);
137 }
138
139 bool HTMLElementStack::contains(Element* element) const
140 {
141     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
142         if (pos->element() == element)
143             return true;
144     }
145     return false;
146 }
147
148 namespace {
149
150 inline bool isScopeMarker(const Element* element)
151 {
152     return element->hasTagName(appletTag)
153         || element->hasTagName(captionTag)
154         || element->hasTagName(appletTag)
155         || element->hasTagName(htmlTag)
156         || element->hasTagName(tableTag)
157         || element->hasTagName(tdTag)
158         || element->hasTagName(thTag)
159         || element->hasTagName(buttonTag)
160         || element->hasTagName(marqueeTag)
161         || element->hasTagName(objectTag)
162         || element->hasTagName(SVGNames::foreignObjectTag);
163 }
164
165 inline bool isListItemScopeMarker(const Element* element)
166 {
167     return isScopeMarker(element)
168         || element->hasTagName(olTag)
169         || element->hasTagName(ulTag);
170 }
171 inline bool isTableScopeMarker(const Element* element)
172 {
173     return element->hasTagName(htmlTag)
174         || element->hasTagName(tableTag);
175 }
176
177 }
178
179 template <bool isMarker(const Element*)>
180 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
181 {
182     for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
183         Element* element = pos->element();
184         if (element->hasLocalName(targetTag))
185             return true;
186         if (isMarker(element))
187             return false;
188     }
189     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
190     return false;
191 }
192
193 bool HTMLElementStack::inScope(Element* targetElement) const
194 {
195     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
196         Element* element = pos->element();
197         if (element == targetElement)
198             return true;
199         if (isScopeMarker(element))
200             return false;
201     }
202     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
203     return false;
204 }
205
206 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
207 {
208     return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
209 }
210
211 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
212 {
213     return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
214 }
215
216 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
217 {
218     return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
219 }
220
221 Element* HTMLElementStack::htmlElement()
222 {
223     ASSERT(m_htmlElement);
224     return m_htmlElement;
225 }
226
227 Element* HTMLElementStack::headElement()
228 {
229     ASSERT(m_headElement);
230     return m_headElement;
231 }
232
233 Element* HTMLElementStack::bodyElement()
234 {
235     ASSERT(m_bodyElement);
236     return m_bodyElement;
237 }
238
239 void HTMLElementStack::pushCommon(PassRefPtr<Element> element)
240 {
241     m_top.set(new ElementRecord(element, m_top.release()));
242     top()->beginParsingChildren();
243 }
244
245 void HTMLElementStack::popCommon()
246 {
247     ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
248     ASSERT(!top()->hasTagName(HTMLNames::bodyTag));
249     top()->finishParsingChildren();
250     m_top = m_top->releaseNext();
251 }
252
253 void HTMLElementStack::removeNonFirstCommon(Element* element)
254 {
255     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
256     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
257     ElementRecord* pos = m_top.get();
258     ASSERT(pos->element() != element);
259     while (pos->next()) {
260         if (pos->next()->element() == element) {
261             // FIXME: Is it OK to call finishParsingChildren()
262             // when the children aren't actually finished?
263             element->finishParsingChildren();
264             pos->setNext(pos->next()->releaseNext());
265             return;
266         }
267     }
268     ASSERT_NOT_REACHED();
269 }
270
271 }