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