2010-06-30 Patrick Gansterer <paroga@paroga.com>
[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 #if ENABLE(SVG_FOREIGN_OBJECT)
172         || element->hasTagName(SVGNames::foreignObjectTag)
173 #endif
174         ;
175 }
176
177 inline bool isListItemScopeMarker(const Element* element)
178 {
179     return isScopeMarker(element)
180         || element->hasTagName(olTag)
181         || element->hasTagName(ulTag);
182 }
183 inline bool isTableScopeMarker(const Element* element)
184 {
185     return element->hasTagName(htmlTag)
186         || element->hasTagName(tableTag);
187 }
188
189 }
190
191 template <bool isMarker(const Element*)>
192 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
193 {
194     for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
195         Element* element = pos->element();
196         if (element->hasLocalName(targetTag))
197             return true;
198         if (isMarker(element))
199             return false;
200     }
201     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
202     return false;
203 }
204
205 bool HTMLElementStack::inScope(Element* targetElement) const
206 {
207     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
208         Element* element = pos->element();
209         if (element == targetElement)
210             return true;
211         if (isScopeMarker(element))
212             return false;
213     }
214     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
215     return false;
216 }
217
218 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
219 {
220     return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
221 }
222
223 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
224 {
225     return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
226 }
227
228 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
229 {
230     return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
231 }
232
233 Element* HTMLElementStack::htmlElement()
234 {
235     ASSERT(m_htmlElement);
236     return m_htmlElement;
237 }
238
239 Element* HTMLElementStack::headElement()
240 {
241     ASSERT(m_headElement);
242     return m_headElement;
243 }
244
245 Element* HTMLElementStack::bodyElement()
246 {
247     ASSERT(m_bodyElement);
248     return m_bodyElement;
249 }
250
251 void HTMLElementStack::pushCommon(PassRefPtr<Element> element)
252 {
253     m_top.set(new ElementRecord(element, m_top.release()));
254     top()->beginParsingChildren();
255 }
256
257 void HTMLElementStack::popCommon()
258 {
259     ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
260     ASSERT(!top()->hasTagName(HTMLNames::bodyTag));
261     top()->finishParsingChildren();
262     m_top = m_top->releaseNext();
263 }
264
265 void HTMLElementStack::removeNonFirstCommon(Element* element)
266 {
267     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
268     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
269     ElementRecord* pos = m_top.get();
270     ASSERT(pos->element() != element);
271     while (pos->next()) {
272         if (pos->next()->element() == element) {
273             // FIXME: Is it OK to call finishParsingChildren()
274             // when the children aren't actually finished?
275             element->finishParsingChildren();
276             pos->setNext(pos->next()->releaseNext());
277             return;
278         }
279     }
280     ASSERT_NOT_REACHED();
281 }
282
283 }