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 "core/html/parser/HTMLElementStack.h"
29
30 #include "core/HTMLNames.h"
31 #include "core/MathMLNames.h"
32 #include "core/SVGNames.h"
33 #include "core/dom/Element.h"
34 #include "core/html/HTMLElement.h"
35
36 namespace blink {
37
38 using namespace HTMLNames;
39
40
41 namespace {
42
isRootNode(HTMLStackItem * item)43 inline bool isRootNode(HTMLStackItem* item)
44 {
45 return item->isDocumentFragmentNode()
46 || item->hasTagName(htmlTag);
47 }
48
isScopeMarker(HTMLStackItem * item)49 inline bool isScopeMarker(HTMLStackItem* item)
50 {
51 return item->hasTagName(appletTag)
52 || item->hasTagName(captionTag)
53 || item->hasTagName(marqueeTag)
54 || item->hasTagName(objectTag)
55 || item->hasTagName(tableTag)
56 || item->hasTagName(tdTag)
57 || item->hasTagName(thTag)
58 || item->hasTagName(MathMLNames::miTag)
59 || item->hasTagName(MathMLNames::moTag)
60 || item->hasTagName(MathMLNames::mnTag)
61 || item->hasTagName(MathMLNames::msTag)
62 || item->hasTagName(MathMLNames::mtextTag)
63 || item->hasTagName(MathMLNames::annotation_xmlTag)
64 || item->hasTagName(SVGNames::foreignObjectTag)
65 || item->hasTagName(SVGNames::descTag)
66 || item->hasTagName(SVGNames::titleTag)
67 || item->hasTagName(templateTag)
68 || isRootNode(item);
69 }
70
isListItemScopeMarker(HTMLStackItem * item)71 inline bool isListItemScopeMarker(HTMLStackItem* item)
72 {
73 return isScopeMarker(item)
74 || item->hasTagName(olTag)
75 || item->hasTagName(ulTag);
76 }
77
isTableScopeMarker(HTMLStackItem * item)78 inline bool isTableScopeMarker(HTMLStackItem* item)
79 {
80 return item->hasTagName(tableTag)
81 || item->hasTagName(templateTag)
82 || isRootNode(item);
83 }
84
isTableBodyScopeMarker(HTMLStackItem * item)85 inline bool isTableBodyScopeMarker(HTMLStackItem* item)
86 {
87 return item->hasTagName(tbodyTag)
88 || item->hasTagName(tfootTag)
89 || item->hasTagName(theadTag)
90 || item->hasTagName(templateTag)
91 || isRootNode(item);
92 }
93
isTableRowScopeMarker(HTMLStackItem * item)94 inline bool isTableRowScopeMarker(HTMLStackItem* item)
95 {
96 return item->hasTagName(trTag)
97 || item->hasTagName(templateTag)
98 || isRootNode(item);
99 }
100
isForeignContentScopeMarker(HTMLStackItem * item)101 inline bool isForeignContentScopeMarker(HTMLStackItem* item)
102 {
103 return HTMLElementStack::isMathMLTextIntegrationPoint(item)
104 || HTMLElementStack::isHTMLIntegrationPoint(item)
105 || item->isInHTMLNamespace();
106 }
107
isButtonScopeMarker(HTMLStackItem * item)108 inline bool isButtonScopeMarker(HTMLStackItem* item)
109 {
110 return isScopeMarker(item)
111 || item->hasTagName(buttonTag);
112 }
113
isSelectScopeMarker(HTMLStackItem * item)114 inline bool isSelectScopeMarker(HTMLStackItem* item)
115 {
116 return !item->hasTagName(optgroupTag)
117 && !item->hasTagName(optionTag);
118 }
119
120 }
121
ElementRecord(PassRefPtrWillBeRawPtr<HTMLStackItem> item,PassOwnPtrWillBeRawPtr<ElementRecord> next)122 HTMLElementStack::ElementRecord::ElementRecord(PassRefPtrWillBeRawPtr<HTMLStackItem> item, PassOwnPtrWillBeRawPtr<ElementRecord> next)
123 : m_item(item)
124 , m_next(next)
125 {
126 ASSERT(m_item);
127 }
128
129 #if !ENABLE(OILPAN)
~ElementRecord()130 HTMLElementStack::ElementRecord::~ElementRecord()
131 {
132 }
133 #endif
134
replaceElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)135 void HTMLElementStack::ElementRecord::replaceElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)
136 {
137 ASSERT(item);
138 ASSERT(!m_item || m_item->isElementNode());
139 // FIXME: Should this call finishParsingChildren?
140 m_item = item;
141 }
142
isAbove(ElementRecord * other) const143 bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const
144 {
145 for (ElementRecord* below = next(); below; below = below->next()) {
146 if (below == other)
147 return true;
148 }
149 return false;
150 }
151
trace(Visitor * visitor)152 void HTMLElementStack::ElementRecord::trace(Visitor* visitor)
153 {
154 #if ENABLE(OILPAN)
155 visitor->trace(m_item);
156 visitor->trace(m_next);
157 #endif
158 }
159
HTMLElementStack()160 HTMLElementStack::HTMLElementStack()
161 : m_rootNode(nullptr)
162 , m_headElement(nullptr)
163 , m_bodyElement(nullptr)
164 , m_stackDepth(0)
165 {
166 }
167
~HTMLElementStack()168 HTMLElementStack::~HTMLElementStack()
169 {
170 }
171
hasOnlyOneElement() const172 bool HTMLElementStack::hasOnlyOneElement() const
173 {
174 return !topRecord()->next();
175 }
176
secondElementIsHTMLBodyElement() const177 bool HTMLElementStack::secondElementIsHTMLBodyElement() const
178 {
179 // This is used the fragment case of <body> and <frameset> in the "in body"
180 // insertion mode.
181 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
182 ASSERT(m_rootNode);
183 // If we have a body element, it must always be the second element on the
184 // stack, as we always start with an html element, and any other element
185 // would cause the implicit creation of a body element.
186 return !!m_bodyElement;
187 }
188
popHTMLHeadElement()189 void HTMLElementStack::popHTMLHeadElement()
190 {
191 ASSERT(top() == m_headElement);
192 m_headElement = nullptr;
193 popCommon();
194 }
195
popHTMLBodyElement()196 void HTMLElementStack::popHTMLBodyElement()
197 {
198 ASSERT(top() == m_bodyElement);
199 m_bodyElement = nullptr;
200 popCommon();
201 }
202
popAll()203 void HTMLElementStack::popAll()
204 {
205 m_rootNode = nullptr;
206 m_headElement = nullptr;
207 m_bodyElement = nullptr;
208 m_stackDepth = 0;
209 while (m_top) {
210 Node& node = *topNode();
211 if (node.isElementNode())
212 toElement(node).finishParsingChildren();
213 m_top = m_top->releaseNext();
214 }
215 }
216
pop()217 void HTMLElementStack::pop()
218 {
219 ASSERT(!topStackItem()->hasTagName(HTMLNames::headTag));
220 popCommon();
221 }
222
popUntil(const AtomicString & tagName)223 void HTMLElementStack::popUntil(const AtomicString& tagName)
224 {
225 while (!topStackItem()->matchesHTMLTag(tagName)) {
226 // pop() will ASSERT if a <body>, <head> or <html> will be popped.
227 pop();
228 }
229 }
230
popUntilPopped(const AtomicString & tagName)231 void HTMLElementStack::popUntilPopped(const AtomicString& tagName)
232 {
233 popUntil(tagName);
234 pop();
235 }
236
popUntilNumberedHeaderElementPopped()237 void HTMLElementStack::popUntilNumberedHeaderElementPopped()
238 {
239 while (!topStackItem()->isNumberedHeaderElement())
240 pop();
241 pop();
242 }
243
popUntil(Element * element)244 void HTMLElementStack::popUntil(Element* element)
245 {
246 while (top() != element)
247 pop();
248 }
249
popUntilPopped(Element * element)250 void HTMLElementStack::popUntilPopped(Element* element)
251 {
252 popUntil(element);
253 pop();
254 }
255
popUntilTableScopeMarker()256 void HTMLElementStack::popUntilTableScopeMarker()
257 {
258 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context
259 while (!isTableScopeMarker(topStackItem()))
260 pop();
261 }
262
popUntilTableBodyScopeMarker()263 void HTMLElementStack::popUntilTableBodyScopeMarker()
264 {
265 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context
266 while (!isTableBodyScopeMarker(topStackItem()))
267 pop();
268 }
269
popUntilTableRowScopeMarker()270 void HTMLElementStack::popUntilTableRowScopeMarker()
271 {
272 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-row-context
273 while (!isTableRowScopeMarker(topStackItem()))
274 pop();
275 }
276
277 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#mathml-text-integration-point
isMathMLTextIntegrationPoint(HTMLStackItem * item)278 bool HTMLElementStack::isMathMLTextIntegrationPoint(HTMLStackItem* item)
279 {
280 if (!item->isElementNode())
281 return false;
282 return item->hasTagName(MathMLNames::miTag)
283 || item->hasTagName(MathMLNames::moTag)
284 || item->hasTagName(MathMLNames::mnTag)
285 || item->hasTagName(MathMLNames::msTag)
286 || item->hasTagName(MathMLNames::mtextTag);
287 }
288
289 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#html-integration-point
isHTMLIntegrationPoint(HTMLStackItem * item)290 bool HTMLElementStack::isHTMLIntegrationPoint(HTMLStackItem* item)
291 {
292 if (!item->isElementNode())
293 return false;
294 if (item->hasTagName(MathMLNames::annotation_xmlTag)) {
295 Attribute* encodingAttr = item->getAttributeItem(MathMLNames::encodingAttr);
296 if (encodingAttr) {
297 const String& encoding = encodingAttr->value();
298 return equalIgnoringCase(encoding, "text/html")
299 || equalIgnoringCase(encoding, "application/xhtml+xml");
300 }
301 return false;
302 }
303 return item->hasTagName(SVGNames::foreignObjectTag)
304 || item->hasTagName(SVGNames::descTag)
305 || item->hasTagName(SVGNames::titleTag);
306 }
307
popUntilForeignContentScopeMarker()308 void HTMLElementStack::popUntilForeignContentScopeMarker()
309 {
310 while (!isForeignContentScopeMarker(topStackItem()))
311 pop();
312 }
313
pushRootNode(PassRefPtrWillBeRawPtr<HTMLStackItem> rootItem)314 void HTMLElementStack::pushRootNode(PassRefPtrWillBeRawPtr<HTMLStackItem> rootItem)
315 {
316 ASSERT(rootItem->isDocumentFragmentNode());
317 pushRootNodeCommon(rootItem);
318 }
319
pushHTMLHtmlElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)320 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)
321 {
322 ASSERT(item->hasTagName(htmlTag));
323 pushRootNodeCommon(item);
324 }
325
pushRootNodeCommon(PassRefPtrWillBeRawPtr<HTMLStackItem> rootItem)326 void HTMLElementStack::pushRootNodeCommon(PassRefPtrWillBeRawPtr<HTMLStackItem> rootItem)
327 {
328 ASSERT(!m_top);
329 ASSERT(!m_rootNode);
330 m_rootNode = rootItem->node();
331 pushCommon(rootItem);
332 }
333
pushHTMLHeadElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)334 void HTMLElementStack::pushHTMLHeadElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)
335 {
336 ASSERT(item->hasTagName(HTMLNames::headTag));
337 ASSERT(!m_headElement);
338 m_headElement = item->element();
339 pushCommon(item);
340 }
341
pushHTMLBodyElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)342 void HTMLElementStack::pushHTMLBodyElement(PassRefPtrWillBeRawPtr<HTMLStackItem> item)
343 {
344 ASSERT(item->hasTagName(HTMLNames::bodyTag));
345 ASSERT(!m_bodyElement);
346 m_bodyElement = item->element();
347 pushCommon(item);
348 }
349
push(PassRefPtrWillBeRawPtr<HTMLStackItem> item)350 void HTMLElementStack::push(PassRefPtrWillBeRawPtr<HTMLStackItem> item)
351 {
352 ASSERT(!item->hasTagName(htmlTag));
353 ASSERT(!item->hasTagName(headTag));
354 ASSERT(!item->hasTagName(bodyTag));
355 ASSERT(m_rootNode);
356 pushCommon(item);
357 }
358
insertAbove(PassRefPtrWillBeRawPtr<HTMLStackItem> item,ElementRecord * recordBelow)359 void HTMLElementStack::insertAbove(PassRefPtrWillBeRawPtr<HTMLStackItem> item, ElementRecord* recordBelow)
360 {
361 ASSERT(item);
362 ASSERT(recordBelow);
363 ASSERT(m_top);
364 ASSERT(!item->hasTagName(htmlTag));
365 ASSERT(!item->hasTagName(headTag));
366 ASSERT(!item->hasTagName(bodyTag));
367 ASSERT(m_rootNode);
368 if (recordBelow == m_top) {
369 push(item);
370 return;
371 }
372
373 for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) {
374 if (recordAbove->next() != recordBelow)
375 continue;
376
377 m_stackDepth++;
378 recordAbove->setNext(adoptPtrWillBeNoop(new ElementRecord(item, recordAbove->releaseNext())));
379 recordAbove->next()->element()->beginParsingChildren();
380 return;
381 }
382 ASSERT_NOT_REACHED();
383 }
384
topRecord() const385 HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const
386 {
387 ASSERT(m_top);
388 return m_top.get();
389 }
390
oneBelowTop() const391 HTMLStackItem* HTMLElementStack::oneBelowTop() const
392 {
393 // We should never call this if there are fewer than 2 elements on the stack.
394 ASSERT(m_top);
395 ASSERT(m_top->next());
396 if (m_top->next()->stackItem()->isElementNode())
397 return m_top->next()->stackItem().get();
398 return 0;
399 }
400
removeHTMLHeadElement(Element * element)401 void HTMLElementStack::removeHTMLHeadElement(Element* element)
402 {
403 ASSERT(m_headElement == element);
404 if (m_top->element() == element) {
405 popHTMLHeadElement();
406 return;
407 }
408 m_headElement = nullptr;
409 removeNonTopCommon(element);
410 }
411
remove(Element * element)412 void HTMLElementStack::remove(Element* element)
413 {
414 ASSERT(!isHTMLHeadElement(element));
415 if (m_top->element() == element) {
416 pop();
417 return;
418 }
419 removeNonTopCommon(element);
420 }
421
find(Element * element) const422 HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const
423 {
424 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
425 if (pos->node() == element)
426 return pos;
427 }
428 return 0;
429 }
430
topmost(const AtomicString & tagName) const431 HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const
432 {
433 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
434 if (pos->stackItem()->matchesHTMLTag(tagName))
435 return pos;
436 }
437 return 0;
438 }
439
contains(Element * element) const440 bool HTMLElementStack::contains(Element* element) const
441 {
442 return !!find(element);
443 }
444
contains(const AtomicString & tagName) const445 bool HTMLElementStack::contains(const AtomicString& tagName) const
446 {
447 return !!topmost(tagName);
448 }
449
450 template <bool isMarker(HTMLStackItem*)>
inScopeCommon(HTMLElementStack::ElementRecord * top,const AtomicString & targetTag)451 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
452 {
453 for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
454 HTMLStackItem* item = pos->stackItem().get();
455 if (item->matchesHTMLTag(targetTag))
456 return true;
457 if (isMarker(item))
458 return false;
459 }
460 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
461 return false;
462 }
463
hasNumberedHeaderElementInScope() const464 bool HTMLElementStack::hasNumberedHeaderElementInScope() const
465 {
466 for (ElementRecord* record = m_top.get(); record; record = record->next()) {
467 HTMLStackItem* item = record->stackItem().get();
468 if (item->isNumberedHeaderElement())
469 return true;
470 if (isScopeMarker(item))
471 return false;
472 }
473 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
474 return false;
475 }
476
inScope(Element * targetElement) const477 bool HTMLElementStack::inScope(Element* targetElement) const
478 {
479 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
480 HTMLStackItem* item = pos->stackItem().get();
481 if (item->node() == targetElement)
482 return true;
483 if (isScopeMarker(item))
484 return false;
485 }
486 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
487 return false;
488 }
489
inScope(const AtomicString & targetTag) const490 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
491 {
492 return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
493 }
494
inScope(const QualifiedName & tagName) const495 bool HTMLElementStack::inScope(const QualifiedName& tagName) const
496 {
497 return inScope(tagName.localName());
498 }
499
inListItemScope(const AtomicString & targetTag) const500 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
501 {
502 return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
503 }
504
inListItemScope(const QualifiedName & tagName) const505 bool HTMLElementStack::inListItemScope(const QualifiedName& tagName) const
506 {
507 return inListItemScope(tagName.localName());
508 }
509
inTableScope(const AtomicString & targetTag) const510 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
511 {
512 return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
513 }
514
inTableScope(const QualifiedName & tagName) const515 bool HTMLElementStack::inTableScope(const QualifiedName& tagName) const
516 {
517 return inTableScope(tagName.localName());
518 }
519
inButtonScope(const AtomicString & targetTag) const520 bool HTMLElementStack::inButtonScope(const AtomicString& targetTag) const
521 {
522 return inScopeCommon<isButtonScopeMarker>(m_top.get(), targetTag);
523 }
524
inButtonScope(const QualifiedName & tagName) const525 bool HTMLElementStack::inButtonScope(const QualifiedName& tagName) const
526 {
527 return inButtonScope(tagName.localName());
528 }
529
inSelectScope(const AtomicString & targetTag) const530 bool HTMLElementStack::inSelectScope(const AtomicString& targetTag) const
531 {
532 return inScopeCommon<isSelectScopeMarker>(m_top.get(), targetTag);
533 }
534
inSelectScope(const QualifiedName & tagName) const535 bool HTMLElementStack::inSelectScope(const QualifiedName& tagName) const
536 {
537 return inSelectScope(tagName.localName());
538 }
539
hasTemplateInHTMLScope() const540 bool HTMLElementStack::hasTemplateInHTMLScope() const
541 {
542 return inScopeCommon<isRootNode>(m_top.get(), templateTag.localName());
543 }
544
htmlElement() const545 Element* HTMLElementStack::htmlElement() const
546 {
547 ASSERT(m_rootNode);
548 return toElement(m_rootNode);
549 }
550
headElement() const551 Element* HTMLElementStack::headElement() const
552 {
553 ASSERT(m_headElement);
554 return m_headElement;
555 }
556
bodyElement() const557 Element* HTMLElementStack::bodyElement() const
558 {
559 ASSERT(m_bodyElement);
560 return m_bodyElement;
561 }
562
rootNode() const563 ContainerNode* HTMLElementStack::rootNode() const
564 {
565 ASSERT(m_rootNode);
566 return m_rootNode;
567 }
568
pushCommon(PassRefPtrWillBeRawPtr<HTMLStackItem> item)569 void HTMLElementStack::pushCommon(PassRefPtrWillBeRawPtr<HTMLStackItem> item)
570 {
571 ASSERT(m_rootNode);
572
573 m_stackDepth++;
574 m_top = adoptPtrWillBeNoop(new ElementRecord(item, m_top.release()));
575 }
576
popCommon()577 void HTMLElementStack::popCommon()
578 {
579 ASSERT(!topStackItem()->hasTagName(htmlTag));
580 ASSERT(!topStackItem()->hasTagName(headTag) || !m_headElement);
581 ASSERT(!topStackItem()->hasTagName(bodyTag) || !m_bodyElement);
582 top()->finishParsingChildren();
583 m_top = m_top->releaseNext();
584
585 m_stackDepth--;
586 }
587
removeNonTopCommon(Element * element)588 void HTMLElementStack::removeNonTopCommon(Element* element)
589 {
590 ASSERT(!isHTMLHtmlElement(element));
591 ASSERT(!isHTMLBodyElement(element));
592 ASSERT(top() != element);
593 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
594 if (pos->next()->element() == element) {
595 // FIXME: Is it OK to call finishParsingChildren()
596 // when the children aren't actually finished?
597 element->finishParsingChildren();
598 pos->setNext(pos->next()->releaseNext());
599 m_stackDepth--;
600 return;
601 }
602 }
603 ASSERT_NOT_REACHED();
604 }
605
furthestBlockForFormattingElement(Element * formattingElement) const606 HTMLElementStack::ElementRecord* HTMLElementStack::furthestBlockForFormattingElement(Element* formattingElement) const
607 {
608 ElementRecord* furthestBlock = 0;
609 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
610 if (pos->element() == formattingElement)
611 return furthestBlock;
612 if (pos->stackItem()->isSpecialNode())
613 furthestBlock = pos;
614 }
615 ASSERT_NOT_REACHED();
616 return 0;
617 }
618
trace(Visitor * visitor)619 void HTMLElementStack::trace(Visitor* visitor)
620 {
621 visitor->trace(m_top);
622 visitor->trace(m_rootNode);
623 visitor->trace(m_headElement);
624 visitor->trace(m_bodyElement);
625 }
626
627 #ifndef NDEBUG
628
show()629 void HTMLElementStack::show()
630 {
631 for (ElementRecord* record = m_top.get(); record; record = record->next())
632 record->element()->showNode();
633 }
634
635 #endif
636
637 }
638