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