• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3  * Copyright (C) 2011 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GOOGLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #include "HTMLElementStack.h"
29 
30 #include "DocumentFragment.h"
31 #include "Element.h"
32 #include "HTMLNames.h"
33 #include "MathMLNames.h"
34 #include "SVGNames.h"
35 #include <wtf/PassOwnPtr.h>
36 
37 namespace WebCore {
38 
39 using namespace HTMLNames;
40 
41 namespace {
42 
isNumberedHeaderElement(ContainerNode * node)43 inline bool isNumberedHeaderElement(ContainerNode* node)
44 {
45     return node->hasTagName(h1Tag)
46         || node->hasTagName(h2Tag)
47         || node->hasTagName(h3Tag)
48         || node->hasTagName(h4Tag)
49         || node->hasTagName(h5Tag)
50         || node->hasTagName(h6Tag);
51 }
52 
isRootNode(ContainerNode * node)53 inline bool isRootNode(ContainerNode* node)
54 {
55     return node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE
56         || node->hasTagName(htmlTag);
57 }
58 
isScopeMarker(ContainerNode * node)59 inline bool isScopeMarker(ContainerNode* node)
60 {
61     return node->hasTagName(appletTag)
62         || node->hasTagName(captionTag)
63         || node->hasTagName(marqueeTag)
64         || node->hasTagName(objectTag)
65         || node->hasTagName(tableTag)
66         || node->hasTagName(tdTag)
67         || node->hasTagName(thTag)
68         || node->hasTagName(MathMLNames::miTag)
69         || node->hasTagName(MathMLNames::moTag)
70         || node->hasTagName(MathMLNames::mnTag)
71         || node->hasTagName(MathMLNames::msTag)
72         || node->hasTagName(MathMLNames::mtextTag)
73         || node->hasTagName(MathMLNames::annotation_xmlTag)
74         || node->hasTagName(SVGNames::foreignObjectTag)
75         || node->hasTagName(SVGNames::descTag)
76         || node->hasTagName(SVGNames::titleTag)
77         || isRootNode(node);
78 }
79 
isListItemScopeMarker(ContainerNode * node)80 inline bool isListItemScopeMarker(ContainerNode* node)
81 {
82     return isScopeMarker(node)
83         || node->hasTagName(olTag)
84         || node->hasTagName(ulTag);
85 }
86 
isTableScopeMarker(ContainerNode * node)87 inline bool isTableScopeMarker(ContainerNode* node)
88 {
89     return node->hasTagName(tableTag)
90         || isRootNode(node);
91 }
92 
isTableBodyScopeMarker(ContainerNode * node)93 inline bool isTableBodyScopeMarker(ContainerNode* node)
94 {
95     return node->hasTagName(tbodyTag)
96         || node->hasTagName(tfootTag)
97         || node->hasTagName(theadTag)
98         || isRootNode(node);
99 }
100 
isTableRowScopeMarker(ContainerNode * node)101 inline bool isTableRowScopeMarker(ContainerNode* node)
102 {
103     return node->hasTagName(trTag)
104         || isRootNode(node);
105 }
106 
isForeignContentScopeMarker(ContainerNode * node)107 inline bool isForeignContentScopeMarker(ContainerNode* node)
108 {
109     return node->hasTagName(MathMLNames::miTag)
110         || node->hasTagName(MathMLNames::moTag)
111         || node->hasTagName(MathMLNames::mnTag)
112         || node->hasTagName(MathMLNames::msTag)
113         || node->hasTagName(MathMLNames::mtextTag)
114         || node->hasTagName(SVGNames::foreignObjectTag)
115         || node->hasTagName(SVGNames::descTag)
116         || node->hasTagName(SVGNames::titleTag)
117         || isInHTMLNamespace(node);
118 }
119 
isButtonScopeMarker(ContainerNode * node)120 inline bool isButtonScopeMarker(ContainerNode* node)
121 {
122     return isScopeMarker(node)
123         || node->hasTagName(buttonTag);
124 }
125 
isSelectScopeMarker(ContainerNode * node)126 inline bool isSelectScopeMarker(ContainerNode* node)
127 {
128     return !node->hasTagName(optgroupTag)
129         && !node->hasTagName(optionTag);
130 }
131 
132 }
133 
ElementRecord(PassRefPtr<ContainerNode> node,PassOwnPtr<ElementRecord> next)134 HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<ContainerNode> node, PassOwnPtr<ElementRecord> next)
135     : m_node(node)
136     , m_next(next)
137 {
138     ASSERT(m_node);
139 }
140 
~ElementRecord()141 HTMLElementStack::ElementRecord::~ElementRecord()
142 {
143 }
144 
replaceElement(PassRefPtr<Element> element)145 void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<Element> element)
146 {
147     ASSERT(element);
148     ASSERT(!m_node || m_node->isElementNode());
149     // FIXME: Should this call finishParsingChildren?
150     m_node = element;
151 }
152 
isAbove(ElementRecord * other) const153 bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const
154 {
155     for (ElementRecord* below = next(); below; below = below->next()) {
156         if (below == other)
157             return true;
158     }
159     return false;
160 }
161 
HTMLElementStack()162 HTMLElementStack::HTMLElementStack()
163     : m_rootNode(0)
164     , m_headElement(0)
165     , m_bodyElement(0)
166 {
167 }
168 
~HTMLElementStack()169 HTMLElementStack::~HTMLElementStack()
170 {
171 }
172 
hasOnlyOneElement() const173 bool HTMLElementStack::hasOnlyOneElement() const
174 {
175     return !topRecord()->next();
176 }
177 
secondElementIsHTMLBodyElement() const178 bool HTMLElementStack::secondElementIsHTMLBodyElement() const
179 {
180     // This is used the fragment case of <body> and <frameset> in the "in body"
181     // insertion mode.
182     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
183     ASSERT(m_rootNode);
184     // If we have a body element, it must always be the second element on the
185     // stack, as we always start with an html element, and any other element
186     // would cause the implicit creation of a body element.
187     return !!m_bodyElement;
188 }
189 
popHTMLHeadElement()190 void HTMLElementStack::popHTMLHeadElement()
191 {
192     ASSERT(top() == m_headElement);
193     m_headElement = 0;
194     popCommon();
195 }
196 
popHTMLBodyElement()197 void HTMLElementStack::popHTMLBodyElement()
198 {
199     ASSERT(top() == m_bodyElement);
200     m_bodyElement = 0;
201     popCommon();
202 }
203 
popAll()204 void HTMLElementStack::popAll()
205 {
206     m_rootNode = 0;
207     m_headElement = 0;
208     m_bodyElement = 0;
209     while (m_top) {
210         topNode()->finishParsingChildren();
211         m_top = m_top->releaseNext();
212     }
213 }
214 
pop()215 void HTMLElementStack::pop()
216 {
217     ASSERT(!top()->hasTagName(HTMLNames::headTag));
218     popCommon();
219 }
220 
popUntil(const AtomicString & tagName)221 void HTMLElementStack::popUntil(const AtomicString& tagName)
222 {
223     while (!top()->hasLocalName(tagName)) {
224         // pop() will ASSERT at <body> if callers fail to check that there is an
225         // element with localName |tagName| on the stack of open elements.
226         pop();
227     }
228 }
229 
popUntilPopped(const AtomicString & tagName)230 void HTMLElementStack::popUntilPopped(const AtomicString& tagName)
231 {
232     popUntil(tagName);
233     pop();
234 }
235 
popUntilNumberedHeaderElementPopped()236 void HTMLElementStack::popUntilNumberedHeaderElementPopped()
237 {
238     while (!isNumberedHeaderElement(topNode()))
239         pop();
240     pop();
241 }
242 
popUntil(Element * element)243 void HTMLElementStack::popUntil(Element* element)
244 {
245     while (top() != element)
246         pop();
247 }
248 
popUntilPopped(Element * element)249 void HTMLElementStack::popUntilPopped(Element* element)
250 {
251     popUntil(element);
252     pop();
253 }
254 
popUntilTableScopeMarker()255 void HTMLElementStack::popUntilTableScopeMarker()
256 {
257     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context
258     while (!isTableScopeMarker(topNode()))
259         pop();
260 }
261 
popUntilTableBodyScopeMarker()262 void HTMLElementStack::popUntilTableBodyScopeMarker()
263 {
264     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context
265     while (!isTableBodyScopeMarker(topNode()))
266         pop();
267 }
268 
popUntilTableRowScopeMarker()269 void HTMLElementStack::popUntilTableRowScopeMarker()
270 {
271     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-row-context
272     while (!isTableRowScopeMarker(topNode()))
273         pop();
274 }
275 
popUntilForeignContentScopeMarker()276 void HTMLElementStack::popUntilForeignContentScopeMarker()
277 {
278     while (!isForeignContentScopeMarker(topNode()))
279         pop();
280 }
281 
pushRootNode(PassRefPtr<ContainerNode> rootNode)282 void HTMLElementStack::pushRootNode(PassRefPtr<ContainerNode> rootNode)
283 {
284     ASSERT(rootNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE);
285     pushRootNodeCommon(rootNode);
286 }
287 
pushHTMLHtmlElement(PassRefPtr<Element> element)288 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element)
289 {
290     ASSERT(element->hasTagName(HTMLNames::htmlTag));
291     pushRootNodeCommon(element);
292 }
293 
pushRootNodeCommon(PassRefPtr<ContainerNode> rootNode)294 void HTMLElementStack::pushRootNodeCommon(PassRefPtr<ContainerNode> rootNode)
295 {
296     ASSERT(!m_top);
297     ASSERT(!m_rootNode);
298     m_rootNode = rootNode.get();
299     pushCommon(rootNode);
300 }
301 
pushHTMLHeadElement(PassRefPtr<Element> element)302 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element)
303 {
304     ASSERT(element->hasTagName(HTMLNames::headTag));
305     ASSERT(!m_headElement);
306     m_headElement = element.get();
307     pushCommon(element);
308 }
309 
pushHTMLBodyElement(PassRefPtr<Element> element)310 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element)
311 {
312     ASSERT(element->hasTagName(HTMLNames::bodyTag));
313     ASSERT(!m_bodyElement);
314     m_bodyElement = element.get();
315     pushCommon(element);
316 }
317 
push(PassRefPtr<Element> element)318 void HTMLElementStack::push(PassRefPtr<Element> element)
319 {
320     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
321     ASSERT(!element->hasTagName(HTMLNames::headTag));
322     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
323     ASSERT(m_rootNode);
324     pushCommon(element);
325 }
326 
insertAbove(PassRefPtr<Element> element,ElementRecord * recordBelow)327 void HTMLElementStack::insertAbove(PassRefPtr<Element> element, ElementRecord* recordBelow)
328 {
329     ASSERT(element);
330     ASSERT(recordBelow);
331     ASSERT(m_top);
332     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
333     ASSERT(!element->hasTagName(HTMLNames::headTag));
334     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
335     ASSERT(m_rootNode);
336     if (recordBelow == m_top) {
337         push(element);
338         return;
339     }
340 
341     for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) {
342         if (recordAbove->next() != recordBelow)
343             continue;
344 
345         recordAbove->setNext(adoptPtr(new ElementRecord(element, recordAbove->releaseNext())));
346         recordAbove->next()->element()->beginParsingChildren();
347         return;
348     }
349     ASSERT_NOT_REACHED();
350 }
351 
topRecord() const352 HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const
353 {
354     ASSERT(m_top);
355     return m_top.get();
356 }
357 
oneBelowTop() const358 Element* HTMLElementStack::oneBelowTop() const
359 {
360     // We should never be calling this if it could be 0.
361     ASSERT(m_top);
362     ASSERT(m_top->next());
363     return m_top->next()->element();
364 }
365 
bottom() const366 Element* HTMLElementStack::bottom() const
367 {
368     return htmlElement();
369 }
370 
removeHTMLHeadElement(Element * element)371 void HTMLElementStack::removeHTMLHeadElement(Element* element)
372 {
373     ASSERT(m_headElement == element);
374     if (m_top->element() == element) {
375         popHTMLHeadElement();
376         return;
377     }
378     m_headElement = 0;
379     removeNonTopCommon(element);
380 }
381 
remove(Element * element)382 void HTMLElementStack::remove(Element* element)
383 {
384     ASSERT(!element->hasTagName(HTMLNames::headTag));
385     if (m_top->element() == element) {
386         pop();
387         return;
388     }
389     removeNonTopCommon(element);
390 }
391 
find(Element * element) const392 HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const
393 {
394     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
395         if (pos->node() == element)
396             return pos;
397     }
398     return 0;
399 }
400 
topmost(const AtomicString & tagName) const401 HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const
402 {
403     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
404         if (pos->node()->hasLocalName(tagName))
405             return pos;
406     }
407     return 0;
408 }
409 
contains(Element * element) const410 bool HTMLElementStack::contains(Element* element) const
411 {
412     return !!find(element);
413 }
414 
contains(const AtomicString & tagName) const415 bool HTMLElementStack::contains(const AtomicString& tagName) const
416 {
417     return !!topmost(tagName);
418 }
419 
420 template <bool isMarker(ContainerNode*)>
inScopeCommon(HTMLElementStack::ElementRecord * top,const AtomicString & targetTag)421 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
422 {
423     for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
424         ContainerNode* node = pos->node();
425         if (node->hasLocalName(targetTag))
426             return true;
427         if (isMarker(node))
428             return false;
429     }
430     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
431     return false;
432 }
433 
hasOnlyHTMLElementsInScope() const434 bool HTMLElementStack::hasOnlyHTMLElementsInScope() const
435 {
436     for (ElementRecord* record = m_top.get(); record; record = record->next()) {
437         ContainerNode* node = record->node();
438         if (!isInHTMLNamespace(node))
439             return false;
440         if (isScopeMarker(node))
441             return true;
442     }
443     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
444     return true;
445 }
446 
hasNumberedHeaderElementInScope() const447 bool HTMLElementStack::hasNumberedHeaderElementInScope() const
448 {
449     for (ElementRecord* record = m_top.get(); record; record = record->next()) {
450         ContainerNode* node = record->node();
451         if (isNumberedHeaderElement(node))
452             return true;
453         if (isScopeMarker(node))
454             return false;
455     }
456     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
457     return false;
458 }
459 
inScope(Element * targetElement) const460 bool HTMLElementStack::inScope(Element* targetElement) const
461 {
462     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
463         ContainerNode* node = pos->node();
464         if (node == targetElement)
465             return true;
466         if (isScopeMarker(node))
467             return false;
468     }
469     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
470     return false;
471 }
472 
inScope(const AtomicString & targetTag) const473 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
474 {
475     return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
476 }
477 
inScope(const QualifiedName & tagName) const478 bool HTMLElementStack::inScope(const QualifiedName& tagName) const
479 {
480     // FIXME: Is localName() right for non-html elements?
481     return inScope(tagName.localName());
482 }
483 
inListItemScope(const AtomicString & targetTag) const484 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
485 {
486     return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
487 }
488 
inListItemScope(const QualifiedName & tagName) const489 bool HTMLElementStack::inListItemScope(const QualifiedName& tagName) const
490 {
491     // FIXME: Is localName() right for non-html elements?
492     return inListItemScope(tagName.localName());
493 }
494 
inTableScope(const AtomicString & targetTag) const495 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
496 {
497     return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
498 }
499 
inTableScope(const QualifiedName & tagName) const500 bool HTMLElementStack::inTableScope(const QualifiedName& tagName) const
501 {
502     // FIXME: Is localName() right for non-html elements?
503     return inTableScope(tagName.localName());
504 }
505 
inButtonScope(const AtomicString & targetTag) const506 bool HTMLElementStack::inButtonScope(const AtomicString& targetTag) const
507 {
508     return inScopeCommon<isButtonScopeMarker>(m_top.get(), targetTag);
509 }
510 
inButtonScope(const QualifiedName & tagName) const511 bool HTMLElementStack::inButtonScope(const QualifiedName& tagName) const
512 {
513     // FIXME: Is localName() right for non-html elements?
514     return inButtonScope(tagName.localName());
515 }
516 
inSelectScope(const AtomicString & targetTag) const517 bool HTMLElementStack::inSelectScope(const AtomicString& targetTag) const
518 {
519     return inScopeCommon<isSelectScopeMarker>(m_top.get(), targetTag);
520 }
521 
inSelectScope(const QualifiedName & tagName) const522 bool HTMLElementStack::inSelectScope(const QualifiedName& tagName) const
523 {
524     // FIXME: Is localName() right for non-html elements?
525     return inSelectScope(tagName.localName());
526 }
527 
htmlElement() const528 Element* HTMLElementStack::htmlElement() const
529 {
530     ASSERT(m_rootNode);
531     return toElement(m_rootNode);
532 }
533 
headElement() const534 Element* HTMLElementStack::headElement() const
535 {
536     ASSERT(m_headElement);
537     return m_headElement;
538 }
539 
bodyElement() const540 Element* HTMLElementStack::bodyElement() const
541 {
542     ASSERT(m_bodyElement);
543     return m_bodyElement;
544 }
545 
rootNode() const546 ContainerNode* HTMLElementStack::rootNode() const
547 {
548     ASSERT(m_rootNode);
549     return m_rootNode;
550 }
551 
pushCommon(PassRefPtr<ContainerNode> node)552 void HTMLElementStack::pushCommon(PassRefPtr<ContainerNode> node)
553 {
554     ASSERT(m_rootNode);
555     m_top = adoptPtr(new ElementRecord(node, m_top.release()));
556     topNode()->beginParsingChildren();
557 }
558 
popCommon()559 void HTMLElementStack::popCommon()
560 {
561     ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
562     ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement);
563     ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement);
564     top()->finishParsingChildren();
565     m_top = m_top->releaseNext();
566 }
567 
removeNonTopCommon(Element * element)568 void HTMLElementStack::removeNonTopCommon(Element* element)
569 {
570     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
571     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
572     ASSERT(top() != element);
573     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
574         if (pos->next()->element() == element) {
575             // FIXME: Is it OK to call finishParsingChildren()
576             // when the children aren't actually finished?
577             element->finishParsingChildren();
578             pos->setNext(pos->next()->releaseNext());
579             return;
580         }
581     }
582     ASSERT_NOT_REACHED();
583 }
584 
585 #ifndef NDEBUG
586 
show()587 void HTMLElementStack::show()
588 {
589     for (ElementRecord* record = m_top.get(); record; record = record->next())
590         record->element()->showNode();
591 }
592 
593 #endif
594 
595 }
596