|
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 "HTMLTreeBuilder.h" |
|
28 |
|
29 #include "Comment.h" |
|
30 #include "DocumentFragment.h" |
|
31 #include "DocumentType.h" |
|
32 #include "Element.h" |
|
33 #include "Frame.h" |
|
34 #include "HTMLDocument.h" |
|
35 #include "HTMLElementFactory.h" |
|
36 #include "HTMLFormElement.h" |
|
37 #include "HTMLHtmlElement.h" |
|
38 #include "HTMLNames.h" |
|
39 #include "HTMLScriptElement.h" |
|
40 #include "HTMLToken.h" |
|
41 #include "HTMLTokenizer.h" |
|
42 #include "LegacyHTMLDocumentParser.h" |
|
43 #include "LegacyHTMLTreeBuilder.h" |
|
44 #include "LocalizedStrings.h" |
|
45 #include "MathMLNames.h" |
|
46 #include "NotImplemented.h" |
|
47 #include "SVGNames.h" |
|
48 #include "ScriptController.h" |
|
49 #include "Settings.h" |
|
50 #include "Text.h" |
|
51 #include "XLinkNames.h" |
|
52 #include "XMLNSNames.h" |
|
53 #include "XMLNames.h" |
|
54 #include <wtf/UnusedParam.h> |
|
55 |
|
56 namespace WebCore { |
|
57 |
|
58 using namespace HTMLNames; |
|
59 |
|
60 static const int uninitializedLineNumberValue = -1; |
|
61 |
|
62 namespace { |
|
63 |
|
64 inline bool isTreeBuilderWhitepace(UChar cc) |
|
65 { |
|
66 return cc == '\t' || cc == '\x0A' || cc == '\x0C' || cc == '\x0D' || cc == ' '; |
|
67 } |
|
68 |
|
69 inline bool hasNonWhitespace(const String& string) |
|
70 { |
|
71 const UChar* characters = string.characters(); |
|
72 const unsigned length = string.length(); |
|
73 for (unsigned i = 0; i < length; ++i) { |
|
74 if (!isTreeBuilderWhitepace(characters[i])) |
|
75 return true; |
|
76 } |
|
77 return false; |
|
78 } |
|
79 |
|
80 bool shouldUseLegacyTreeBuilder(Document* document) |
|
81 { |
|
82 return !document->settings() || !document->settings()->html5TreeBuilderEnabled(); |
|
83 } |
|
84 |
|
85 bool isNumberedHeaderTag(const AtomicString& tagName) |
|
86 { |
|
87 return tagName == h1Tag |
|
88 || tagName == h2Tag |
|
89 || tagName == h3Tag |
|
90 || tagName == h4Tag |
|
91 || tagName == h5Tag |
|
92 || tagName == h6Tag; |
|
93 } |
|
94 |
|
95 bool isCaptionColOrColgroupTag(const AtomicString& tagName) |
|
96 { |
|
97 return tagName == captionTag |
|
98 || tagName == colTag |
|
99 || tagName == colgroupTag; |
|
100 } |
|
101 |
|
102 bool isTableCellContextTag(const AtomicString& tagName) |
|
103 { |
|
104 return tagName == thTag || tagName == tdTag; |
|
105 } |
|
106 |
|
107 bool isTableBodyContextTag(const AtomicString& tagName) |
|
108 { |
|
109 return tagName == tbodyTag |
|
110 || tagName == tfootTag |
|
111 || tagName == theadTag; |
|
112 } |
|
113 |
|
114 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special |
|
115 bool isSpecialTag(const AtomicString& tagName) |
|
116 { |
|
117 return tagName == addressTag |
|
118 || tagName == articleTag |
|
119 || tagName == asideTag |
|
120 || tagName == baseTag |
|
121 || tagName == basefontTag |
|
122 || tagName == "bgsound" |
|
123 || tagName == blockquoteTag |
|
124 || tagName == bodyTag |
|
125 || tagName == brTag |
|
126 || tagName == buttonTag |
|
127 || tagName == centerTag |
|
128 || tagName == colTag |
|
129 || tagName == colgroupTag |
|
130 || tagName == "command" |
|
131 || tagName == ddTag |
|
132 || tagName == "details" |
|
133 || tagName == dirTag |
|
134 || tagName == divTag |
|
135 || tagName == dlTag |
|
136 || tagName == dtTag |
|
137 || tagName == embedTag |
|
138 || tagName == fieldsetTag |
|
139 || tagName == "figure" |
|
140 || tagName == footerTag |
|
141 || tagName == formTag |
|
142 || tagName == frameTag |
|
143 || tagName == framesetTag |
|
144 || isNumberedHeaderTag(tagName) |
|
145 || tagName == headTag |
|
146 || tagName == headerTag |
|
147 || tagName == hgroupTag |
|
148 || tagName == hrTag |
|
149 || tagName == iframeTag |
|
150 || tagName == imgTag |
|
151 || tagName == inputTag |
|
152 || tagName == isindexTag |
|
153 || tagName == liTag |
|
154 || tagName == linkTag |
|
155 || tagName == listingTag |
|
156 || tagName == menuTag |
|
157 || tagName == metaTag |
|
158 || tagName == navTag |
|
159 || tagName == noembedTag |
|
160 || tagName == noframesTag |
|
161 || tagName == noscriptTag |
|
162 || tagName == olTag |
|
163 || tagName == pTag |
|
164 || tagName == paramTag |
|
165 || tagName == plaintextTag |
|
166 || tagName == preTag |
|
167 || tagName == scriptTag |
|
168 || tagName == sectionTag |
|
169 || tagName == selectTag |
|
170 || tagName == styleTag |
|
171 || isTableBodyContextTag(tagName) |
|
172 || tagName == textareaTag |
|
173 || tagName == titleTag |
|
174 || tagName == trTag |
|
175 || tagName == ulTag |
|
176 || tagName == wbrTag |
|
177 || tagName == xmpTag; |
|
178 } |
|
179 |
|
180 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#scoping |
|
181 // Same as isScopingTag in LegacyHTMLTreeBuilder.cpp |
|
182 // and isScopeMarker in HTMLElementStack.cpp |
|
183 bool isScopingTag(const AtomicString& tagName) |
|
184 { |
|
185 return tagName == appletTag |
|
186 || tagName == captionTag |
|
187 || tagName == SVGNames::foreignObjectTag |
|
188 || tagName == htmlTag |
|
189 || tagName == marqueeTag |
|
190 || tagName == objectTag |
|
191 || tagName == tableTag |
|
192 || isTableCellContextTag(tagName); |
|
193 } |
|
194 |
|
195 bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName) |
|
196 { |
|
197 return tagName == bTag |
|
198 || tagName == bigTag |
|
199 || tagName == codeTag |
|
200 || tagName == emTag |
|
201 || tagName == fontTag |
|
202 || tagName == iTag |
|
203 || tagName == sTag |
|
204 || tagName == smallTag |
|
205 || tagName == strikeTag |
|
206 || tagName == strongTag |
|
207 || tagName == ttTag |
|
208 || tagName == uTag; |
|
209 } |
|
210 |
|
211 bool isNonAnchorFormattingTag(const AtomicString& tagName) |
|
212 { |
|
213 return tagName == nobrTag |
|
214 || isNonAnchorNonNobrFormattingTag(tagName); |
|
215 } |
|
216 |
|
217 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting |
|
218 bool isFormattingTag(const AtomicString& tagName) |
|
219 { |
|
220 return tagName == aTag || isNonAnchorFormattingTag(tagName); |
|
221 } |
|
222 |
|
223 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#phrasing |
|
224 bool isPhrasingTag(const AtomicString& tagName) |
|
225 { |
|
226 return !isSpecialTag(tagName) && !isScopingTag(tagName) && !isFormattingTag(tagName); |
|
227 } |
|
228 |
|
229 bool isNotFormattingAndNotPhrasing(const Element* element) |
|
230 { |
|
231 // The spec often says "node is not in the formatting category, and is not |
|
232 // in the phrasing category". !phrasing && !formatting == scoping || special |
|
233 // scoping || special is easier to compute. |
|
234 // FIXME: localName() is wrong for non-html content. |
|
235 const AtomicString& tagName = element->localName(); |
|
236 return isScopingTag(tagName) || isSpecialTag(tagName); |
|
237 } |
|
238 |
|
239 } // namespace |
|
240 |
|
241 class HTMLTreeBuilder::ExternalCharacterTokenBuffer : public Noncopyable { |
|
242 public: |
|
243 explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token) |
|
244 : m_current(token.characters().data()) |
|
245 , m_end(m_current + token.characters().size()) |
|
246 { |
|
247 ASSERT(!isEmpty()); |
|
248 } |
|
249 |
|
250 explicit ExternalCharacterTokenBuffer(const String& string) |
|
251 : m_current(string.characters()) |
|
252 , m_end(m_current + string.length()) |
|
253 { |
|
254 ASSERT(!isEmpty()); |
|
255 } |
|
256 |
|
257 ~ExternalCharacterTokenBuffer() |
|
258 { |
|
259 ASSERT(isEmpty()); |
|
260 } |
|
261 |
|
262 bool isEmpty() const { return m_current == m_end; } |
|
263 |
|
264 void skipLeadingWhitespace() |
|
265 { |
|
266 ASSERT(!isEmpty()); |
|
267 while (isTreeBuilderWhitepace(*m_current)) { |
|
268 if (++m_current == m_end) |
|
269 return; |
|
270 } |
|
271 } |
|
272 |
|
273 String takeLeadingWhitespace() |
|
274 { |
|
275 ASSERT(!isEmpty()); |
|
276 const UChar* start = m_current; |
|
277 skipLeadingWhitespace(); |
|
278 if (start == m_current) |
|
279 return String(); |
|
280 return String(start, m_current - start); |
|
281 } |
|
282 |
|
283 String takeRemaining() |
|
284 { |
|
285 ASSERT(!isEmpty()); |
|
286 const UChar* start = m_current; |
|
287 m_current = m_end; |
|
288 return String(start, m_current - start); |
|
289 } |
|
290 |
|
291 void giveRemainingTo(Vector<UChar>& recipient) |
|
292 { |
|
293 recipient.append(m_current, m_end - m_current); |
|
294 m_current = m_end; |
|
295 } |
|
296 |
|
297 String takeRemainingWhitespace() |
|
298 { |
|
299 ASSERT(!isEmpty()); |
|
300 Vector<UChar> whitespace; |
|
301 do { |
|
302 UChar cc = *m_current++; |
|
303 if (isTreeBuilderWhitepace(cc)) |
|
304 whitespace.append(cc); |
|
305 } while (m_current < m_end); |
|
306 // Returning the null string when there aren't any whitespace |
|
307 // characters is slightly cleaner semantically because we don't want |
|
308 // to insert a text node (as opposed to inserting an empty text node). |
|
309 if (whitespace.isEmpty()) |
|
310 return String(); |
|
311 return String::adopt(whitespace); |
|
312 } |
|
313 |
|
314 private: |
|
315 const UChar* m_current; |
|
316 const UChar* m_end; |
|
317 }; |
|
318 |
|
319 |
|
320 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, HTMLDocument* document, bool reportErrors) |
|
321 : m_framesetOk(true) |
|
322 , m_document(document) |
|
323 , m_tree(document, FragmentScriptingAllowed, false) |
|
324 , m_reportErrors(reportErrors) |
|
325 , m_isPaused(false) |
|
326 , m_insertionMode(InitialMode) |
|
327 , m_originalInsertionMode(InitialMode) |
|
328 , m_secondaryInsertionMode(InitialMode) |
|
329 , m_tokenizer(tokenizer) |
|
330 , m_legacyTreeBuilder(shouldUseLegacyTreeBuilder(document) ? new LegacyHTMLTreeBuilder(document, reportErrors) : 0) |
|
331 , m_lastScriptElementStartLine(uninitializedLineNumberValue) |
|
332 , m_scriptToProcessStartLine(uninitializedLineNumberValue) |
|
333 , m_fragmentScriptingPermission(FragmentScriptingAllowed) |
|
334 , m_isParsingFragment(false) |
|
335 { |
|
336 } |
|
337 |
|
338 // FIXME: Member variables should be grouped into self-initializing structs to |
|
339 // minimize code duplication between these constructors. |
|
340 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission) |
|
341 : m_framesetOk(true) |
|
342 , m_document(fragment->document()) |
|
343 , m_tree(fragment->document(), scriptingPermission, true) |
|
344 , m_reportErrors(false) // FIXME: Why not report errors in fragments? |
|
345 , m_isPaused(false) |
|
346 , m_insertionMode(InitialMode) |
|
347 , m_originalInsertionMode(InitialMode) |
|
348 , m_secondaryInsertionMode(InitialMode) |
|
349 , m_tokenizer(tokenizer) |
|
350 , m_legacyTreeBuilder(new LegacyHTMLTreeBuilder(fragment, scriptingPermission)) |
|
351 , m_lastScriptElementStartLine(uninitializedLineNumberValue) |
|
352 , m_scriptToProcessStartLine(uninitializedLineNumberValue) |
|
353 , m_fragmentScriptingPermission(scriptingPermission) |
|
354 , m_isParsingFragment(true) |
|
355 { |
|
356 } |
|
357 |
|
358 HTMLTreeBuilder::~HTMLTreeBuilder() |
|
359 { |
|
360 } |
|
361 |
|
362 static void convertToOldStyle(AtomicHTMLToken& token, Token& oldStyleToken) |
|
363 { |
|
364 switch (token.type()) { |
|
365 case HTMLToken::Uninitialized: |
|
366 case HTMLToken::DOCTYPE: |
|
367 ASSERT_NOT_REACHED(); |
|
368 break; |
|
369 case HTMLToken::EndOfFile: |
|
370 ASSERT_NOT_REACHED(); |
|
371 notImplemented(); |
|
372 break; |
|
373 case HTMLToken::StartTag: |
|
374 case HTMLToken::EndTag: { |
|
375 oldStyleToken.beginTag = (token.type() == HTMLToken::StartTag); |
|
376 // The LegacyHTMLTreeBuilder seems to work better if we lie here and |
|
377 // say that tags are never self closing. As a wise man once said: |
|
378 // "You can't handle the truth!" |
|
379 oldStyleToken.selfClosingTag = false; |
|
380 oldStyleToken.tagName = token.name(); |
|
381 oldStyleToken.attrs = token.takeAtributes(); |
|
382 break; |
|
383 } |
|
384 case HTMLToken::Comment: |
|
385 oldStyleToken.tagName = commentAtom; |
|
386 oldStyleToken.text = token.comment().impl(); |
|
387 break; |
|
388 case HTMLToken::Character: |
|
389 oldStyleToken.tagName = textAtom; |
|
390 oldStyleToken.text = StringImpl::create(token.characters().data(), token.characters().size()); |
|
391 break; |
|
392 } |
|
393 } |
|
394 |
|
395 void HTMLTreeBuilder::handleScriptStartTag() |
|
396 { |
|
397 notImplemented(); // The HTML frgment case? |
|
398 m_tokenizer->setState(HTMLTokenizer::ScriptDataState); |
|
399 notImplemented(); // Save insertion mode. |
|
400 } |
|
401 |
|
402 void HTMLTreeBuilder::handleScriptEndTag(Element* scriptElement, int scriptStartLine) |
|
403 { |
|
404 ASSERT(!m_scriptToProcess); // Caller never called takeScriptToProcess! |
|
405 ASSERT(m_scriptToProcessStartLine == uninitializedLineNumberValue); // Caller never called takeScriptToProcess! |
|
406 notImplemented(); // Save insertion mode and insertion point? |
|
407 |
|
408 // Pause ourselves so that parsing stops until the script can be processed by the caller. |
|
409 m_isPaused = true; |
|
410 m_scriptToProcess = scriptElement; |
|
411 // Lexer line numbers are 0-based, ScriptSourceCode expects 1-based lines, |
|
412 // so we convert here before passing the line number off to HTMLScriptRunner. |
|
413 m_scriptToProcessStartLine = scriptStartLine + 1; |
|
414 } |
|
415 |
|
416 PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(int& scriptStartLine) |
|
417 { |
|
418 // Unpause ourselves, callers may pause us again when processing the script. |
|
419 // The HTML5 spec is written as though scripts are executed inside the tree |
|
420 // builder. We pause the parser to exit the tree builder, and then resume |
|
421 // before running scripts. |
|
422 m_isPaused = false; |
|
423 scriptStartLine = m_scriptToProcessStartLine; |
|
424 m_scriptToProcessStartLine = uninitializedLineNumberValue; |
|
425 return m_scriptToProcess.release(); |
|
426 } |
|
427 |
|
428 HTMLTokenizer::State HTMLTreeBuilder::adjustedLexerState(HTMLTokenizer::State state, const AtomicString& tagName, Frame* frame) |
|
429 { |
|
430 if (tagName == textareaTag || tagName == titleTag) |
|
431 return HTMLTokenizer::RCDATAState; |
|
432 |
|
433 if (tagName == styleTag |
|
434 || tagName == iframeTag |
|
435 || tagName == xmpTag |
|
436 || tagName == noembedTag |
|
437 || tagName == noframesTag |
|
438 || (tagName == noscriptTag && isScriptingFlagEnabled(frame))) |
|
439 return HTMLTokenizer::RAWTEXTState; |
|
440 |
|
441 if (tagName == plaintextTag) |
|
442 return HTMLTokenizer::PLAINTEXTState; |
|
443 |
|
444 return state; |
|
445 } |
|
446 |
|
447 void HTMLTreeBuilder::passTokenToLegacyParser(HTMLToken& token) |
|
448 { |
|
449 if (token.type() == HTMLToken::DOCTYPE) { |
|
450 DoctypeToken doctypeToken; |
|
451 doctypeToken.m_name.append(token.name().data(), token.name().size()); |
|
452 doctypeToken.m_publicID = token.publicIdentifier(); |
|
453 doctypeToken.m_systemID = token.systemIdentifier(); |
|
454 doctypeToken.m_forceQuirks = token.forceQuirks(); |
|
455 |
|
456 m_legacyTreeBuilder->parseDoctypeToken(&doctypeToken); |
|
457 return; |
|
458 } |
|
459 |
|
460 if (token.type() == HTMLToken::EndOfFile) |
|
461 return; |
|
462 |
|
463 // For now, we translate into an old-style token for testing. |
|
464 Token oldStyleToken; |
|
465 AtomicHTMLToken atomicToken(token); |
|
466 convertToOldStyle(atomicToken, oldStyleToken); |
|
467 |
|
468 RefPtr<Node> result = m_legacyTreeBuilder->parseToken(&oldStyleToken); |
|
469 if (token.type() == HTMLToken::StartTag) { |
|
470 // This work is supposed to be done by the parser, but |
|
471 // when using the old parser for we have to do this manually. |
|
472 if (oldStyleToken.tagName == scriptTag) { |
|
473 handleScriptStartTag(); |
|
474 m_lastScriptElement = static_pointer_cast<Element>(result); |
|
475 m_lastScriptElementStartLine = m_tokenizer->lineNumber(); |
|
476 } else if (oldStyleToken.tagName == preTag || oldStyleToken.tagName == listingTag) |
|
477 m_tokenizer->skipLeadingNewLineForListing(); |
|
478 else |
|
479 m_tokenizer->setState(adjustedLexerState(m_tokenizer->state(), oldStyleToken.tagName, m_document->frame())); |
|
480 } else if (token.type() == HTMLToken::EndTag) { |
|
481 if (oldStyleToken.tagName == scriptTag) { |
|
482 if (m_lastScriptElement) { |
|
483 ASSERT(m_lastScriptElementStartLine != uninitializedLineNumberValue); |
|
484 if (m_fragmentScriptingPermission == FragmentScriptingNotAllowed) { |
|
485 // FIXME: This is a horrible hack for platform/Pasteboard. |
|
486 // Clear the <script> tag when using the Parser to create |
|
487 // a DocumentFragment for pasting so that javascript content |
|
488 // does not show up in pasted HTML. |
|
489 m_lastScriptElement->removeChildren(); |
|
490 } else if (insertionMode() != AfterFramesetMode) |
|
491 handleScriptEndTag(m_lastScriptElement.get(), m_lastScriptElementStartLine); |
|
492 m_lastScriptElement = 0; |
|
493 m_lastScriptElementStartLine = uninitializedLineNumberValue; |
|
494 } |
|
495 } else if (oldStyleToken.tagName == framesetTag) |
|
496 setInsertionMode(AfterFramesetMode); |
|
497 } |
|
498 } |
|
499 |
|
500 void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken) |
|
501 { |
|
502 if (m_legacyTreeBuilder) { |
|
503 passTokenToLegacyParser(rawToken); |
|
504 return; |
|
505 } |
|
506 |
|
507 AtomicHTMLToken token(rawToken); |
|
508 processToken(token); |
|
509 } |
|
510 |
|
511 void HTMLTreeBuilder::processToken(AtomicHTMLToken& token) |
|
512 { |
|
513 switch (token.type()) { |
|
514 case HTMLToken::Uninitialized: |
|
515 ASSERT_NOT_REACHED(); |
|
516 break; |
|
517 case HTMLToken::DOCTYPE: |
|
518 processDoctypeToken(token); |
|
519 break; |
|
520 case HTMLToken::StartTag: |
|
521 processStartTag(token); |
|
522 break; |
|
523 case HTMLToken::EndTag: |
|
524 processEndTag(token); |
|
525 break; |
|
526 case HTMLToken::Comment: |
|
527 processComment(token); |
|
528 return; |
|
529 case HTMLToken::Character: |
|
530 processCharacter(token); |
|
531 break; |
|
532 case HTMLToken::EndOfFile: |
|
533 processEndOfFile(token); |
|
534 break; |
|
535 } |
|
536 } |
|
537 |
|
538 void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token) |
|
539 { |
|
540 ASSERT(token.type() == HTMLToken::DOCTYPE); |
|
541 if (m_insertionMode == InitialMode) { |
|
542 m_tree.insertDoctype(token); |
|
543 return; |
|
544 } |
|
545 if (m_insertionMode == InTableTextMode) { |
|
546 defaultForInTableText(); |
|
547 processDoctypeToken(token); |
|
548 return; |
|
549 } |
|
550 parseError(token); |
|
551 } |
|
552 |
|
553 void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, PassRefPtr<NamedNodeMap> attributes) |
|
554 { |
|
555 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags. |
|
556 AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), attributes); |
|
557 processStartTag(fakeToken); |
|
558 } |
|
559 |
|
560 void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName) |
|
561 { |
|
562 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags. |
|
563 AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName.localName()); |
|
564 processEndTag(fakeToken); |
|
565 } |
|
566 |
|
567 void HTMLTreeBuilder::processFakeCharacters(const String& characters) |
|
568 { |
|
569 ASSERT(!characters.isEmpty()); |
|
570 ExternalCharacterTokenBuffer buffer(characters); |
|
571 processCharacterBuffer(buffer); |
|
572 } |
|
573 |
|
574 void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope() |
|
575 { |
|
576 if (!m_tree.openElements()->inButtonScope(pTag.localName())) |
|
577 return; |
|
578 AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName()); |
|
579 processEndTag(endP); |
|
580 } |
|
581 |
|
582 PassRefPtr<NamedNodeMap> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token) |
|
583 { |
|
584 RefPtr<NamedNodeMap> attributes = token.takeAtributes(); |
|
585 if (!attributes) |
|
586 attributes = NamedNodeMap::create(); |
|
587 else { |
|
588 attributes->removeAttribute(nameAttr); |
|
589 attributes->removeAttribute(actionAttr); |
|
590 attributes->removeAttribute(promptAttr); |
|
591 } |
|
592 |
|
593 RefPtr<Attribute> mappedAttribute = Attribute::createMapped(nameAttr, isindexTag.localName()); |
|
594 attributes->insertAttribute(mappedAttribute.release(), false); |
|
595 return attributes.release(); |
|
596 } |
|
597 |
|
598 void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token) |
|
599 { |
|
600 ASSERT(token.type() == HTMLToken::StartTag); |
|
601 ASSERT(token.name() == isindexTag); |
|
602 parseError(token); |
|
603 if (m_tree.form()) |
|
604 return; |
|
605 notImplemented(); // Acknowledge self-closing flag |
|
606 processFakeStartTag(formTag); |
|
607 Attribute* actionAttribute = token.getAttributeItem(actionAttr); |
|
608 if (actionAttribute) { |
|
609 ASSERT(m_tree.currentElement()->hasTagName(formTag)); |
|
610 m_tree.currentElement()->setAttribute(actionAttr, actionAttribute->value()); |
|
611 } |
|
612 processFakeStartTag(hrTag); |
|
613 processFakeStartTag(labelTag); |
|
614 Attribute* promptAttribute = token.getAttributeItem(promptAttr); |
|
615 if (promptAttribute) |
|
616 processFakeCharacters(promptAttribute->value()); |
|
617 else |
|
618 processFakeCharacters(searchableIndexIntroduction()); |
|
619 processFakeStartTag(inputTag, attributesForIsindexInput(token)); |
|
620 notImplemented(); // This second set of characters may be needed by non-english locales. |
|
621 processFakeEndTag(labelTag); |
|
622 processFakeStartTag(hrTag); |
|
623 processFakeEndTag(formTag); |
|
624 } |
|
625 |
|
626 namespace { |
|
627 |
|
628 bool isLi(const Element* element) |
|
629 { |
|
630 return element->hasTagName(liTag); |
|
631 } |
|
632 |
|
633 bool isDdOrDt(const Element* element) |
|
634 { |
|
635 return element->hasTagName(ddTag) |
|
636 || element->hasTagName(dtTag); |
|
637 } |
|
638 |
|
639 } |
|
640 |
|
641 template <bool shouldClose(const Element*)> |
|
642 void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token) |
|
643 { |
|
644 m_framesetOk = false; |
|
645 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); |
|
646 while (1) { |
|
647 Element* node = nodeRecord->element(); |
|
648 if (shouldClose(node)) { |
|
649 processFakeEndTag(node->tagQName()); |
|
650 break; |
|
651 } |
|
652 if (isNotFormattingAndNotPhrasing(node) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag)) |
|
653 break; |
|
654 nodeRecord = nodeRecord->next(); |
|
655 } |
|
656 processFakePEndTagIfPInButtonScope(); |
|
657 m_tree.insertHTMLElement(token); |
|
658 } |
|
659 |
|
660 namespace { |
|
661 |
|
662 typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap; |
|
663 |
|
664 void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, QualifiedName** names, size_t length) |
|
665 { |
|
666 for (size_t i = 0; i < length; ++i) { |
|
667 const QualifiedName& name = *names[i]; |
|
668 const AtomicString& localName = name.localName(); |
|
669 AtomicString loweredLocalName = localName.lower(); |
|
670 if (loweredLocalName != localName) |
|
671 map->add(loweredLocalName, name); |
|
672 } |
|
673 } |
|
674 |
|
675 void addName(PrefixedNameToQualifiedNameMap* map, const QualifiedName& name) |
|
676 { |
|
677 map->add(name.localName().lower(), name); |
|
678 } |
|
679 |
|
680 void adjustSVGTagNameCase(AtomicHTMLToken& token) |
|
681 { |
|
682 static PrefixedNameToQualifiedNameMap* caseMap = 0; |
|
683 if (!caseMap) { |
|
684 caseMap = new PrefixedNameToQualifiedNameMap; |
|
685 size_t length = 0; |
|
686 QualifiedName** svgTags = SVGNames::getSVGTags(&length); |
|
687 mapLoweredLocalNameToName(caseMap, svgTags, length); |
|
688 } |
|
689 |
|
690 const QualifiedName& casedName = caseMap->get(token.name()); |
|
691 if (casedName.localName().isNull()) |
|
692 return; |
|
693 token.setName(casedName.localName()); |
|
694 } |
|
695 |
|
696 void adjustSVGAttributes(AtomicHTMLToken& token) |
|
697 { |
|
698 static PrefixedNameToQualifiedNameMap* caseMap = 0; |
|
699 if (!caseMap) { |
|
700 caseMap = new PrefixedNameToQualifiedNameMap; |
|
701 size_t length = 0; |
|
702 QualifiedName** svgAttrs = SVGNames::getSVGAttrs(&length); |
|
703 mapLoweredLocalNameToName(caseMap, svgAttrs, length); |
|
704 } |
|
705 |
|
706 NamedNodeMap* attributes = token.attributes(); |
|
707 if (!attributes) |
|
708 return; |
|
709 |
|
710 for (unsigned x = 0; x < attributes->length(); ++x) { |
|
711 Attribute* attribute = attributes->attributeItem(x); |
|
712 const QualifiedName& casedName = caseMap->get(attribute->localName()); |
|
713 if (!casedName.localName().isNull()) |
|
714 attribute->parserSetName(casedName); |
|
715 } |
|
716 } |
|
717 |
|
718 void adjustMathMLAttributes(AtomicHTMLToken&) |
|
719 { |
|
720 notImplemented(); |
|
721 } |
|
722 |
|
723 void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, QualifiedName** names, size_t length) |
|
724 { |
|
725 for (size_t i = 0; i < length; ++i) { |
|
726 QualifiedName* name = names[i]; |
|
727 const AtomicString& localName = name->localName(); |
|
728 AtomicString prefixColonLocalName(prefix + ":" + localName); |
|
729 QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI()); |
|
730 map->add(prefixColonLocalName, nameWithPrefix); |
|
731 } |
|
732 } |
|
733 |
|
734 void adjustForeignAttributes(AtomicHTMLToken& token) |
|
735 { |
|
736 static PrefixedNameToQualifiedNameMap* map = 0; |
|
737 if (!map) { |
|
738 map = new PrefixedNameToQualifiedNameMap; |
|
739 size_t length = 0; |
|
740 QualifiedName** attrs = XLinkNames::getXLinkAttrs(&length); |
|
741 addNamesWithPrefix(map, "xlink", attrs, length); |
|
742 |
|
743 attrs = XMLNames::getXMLAttrs(&length); |
|
744 addNamesWithPrefix(map, "xml", attrs, length); |
|
745 |
|
746 map->add("xmlns", XMLNSNames::xmlnsAttr); |
|
747 map->add("xmlns:xlink", QualifiedName("xmlns", "xlink", XMLNSNames::xmlnsNamespaceURI)); |
|
748 } |
|
749 |
|
750 NamedNodeMap* attributes = token.attributes(); |
|
751 if (!attributes) |
|
752 return; |
|
753 |
|
754 for (unsigned x = 0; x < attributes->length(); ++x) { |
|
755 Attribute* attribute = attributes->attributeItem(x); |
|
756 const QualifiedName& name = map->get(attribute->localName()); |
|
757 if (!name.localName().isNull()) |
|
758 attribute->parserSetName(name); |
|
759 } |
|
760 } |
|
761 |
|
762 } |
|
763 |
|
764 void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token) |
|
765 { |
|
766 ASSERT(token.type() == HTMLToken::StartTag); |
|
767 if (token.name() == htmlTag) { |
|
768 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
769 return; |
|
770 } |
|
771 if (token.name() == baseTag |
|
772 || token.name() == basefontTag |
|
773 || token.name() == "bgsound" |
|
774 || token.name() == "command" |
|
775 || token.name() == linkTag |
|
776 || token.name() == metaTag |
|
777 || token.name() == noframesTag |
|
778 || token.name() == scriptTag |
|
779 || token.name() == styleTag |
|
780 || token.name() == titleTag) { |
|
781 bool didProcess = processStartTagForInHead(token); |
|
782 ASSERT_UNUSED(didProcess, didProcess); |
|
783 return; |
|
784 } |
|
785 if (token.name() == bodyTag) { |
|
786 m_tree.insertHTMLBodyStartTagInBody(token); |
|
787 return; |
|
788 } |
|
789 if (token.name() == framesetTag) { |
|
790 parseError(token); |
|
791 notImplemented(); // fragment case |
|
792 if (!m_framesetOk) |
|
793 return; |
|
794 ExceptionCode ec = 0; |
|
795 m_tree.openElements()->bodyElement()->remove(ec); |
|
796 ASSERT(!ec); |
|
797 m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement()); |
|
798 m_tree.openElements()->popHTMLBodyElement(); |
|
799 ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement()); |
|
800 m_tree.insertHTMLElement(token); |
|
801 setInsertionMode(InFramesetMode); |
|
802 return; |
|
803 } |
|
804 if (token.name() == addressTag |
|
805 || token.name() == articleTag |
|
806 || token.name() == asideTag |
|
807 || token.name() == blockquoteTag |
|
808 || token.name() == centerTag |
|
809 || token.name() == "details" |
|
810 || token.name() == dirTag |
|
811 || token.name() == divTag |
|
812 || token.name() == dlTag |
|
813 || token.name() == fieldsetTag |
|
814 || token.name() == "figcaption" |
|
815 || token.name() == "figure" |
|
816 || token.name() == footerTag |
|
817 || token.name() == headerTag |
|
818 || token.name() == hgroupTag |
|
819 || token.name() == menuTag |
|
820 || token.name() == navTag |
|
821 || token.name() == olTag |
|
822 || token.name() == pTag |
|
823 || token.name() == sectionTag |
|
824 || token.name() == "summary" |
|
825 || token.name() == ulTag) { |
|
826 processFakePEndTagIfPInButtonScope(); |
|
827 m_tree.insertHTMLElement(token); |
|
828 return; |
|
829 } |
|
830 if (isNumberedHeaderTag(token.name())) { |
|
831 processFakePEndTagIfPInButtonScope(); |
|
832 if (isNumberedHeaderTag(m_tree.currentElement()->localName())) { |
|
833 parseError(token); |
|
834 m_tree.openElements()->pop(); |
|
835 } |
|
836 m_tree.insertHTMLElement(token); |
|
837 return; |
|
838 } |
|
839 if (token.name() == preTag || token.name() == listingTag) { |
|
840 processFakePEndTagIfPInButtonScope(); |
|
841 m_tree.insertHTMLElement(token); |
|
842 m_tokenizer->skipLeadingNewLineForListing(); |
|
843 m_framesetOk = false; |
|
844 return; |
|
845 } |
|
846 if (token.name() == formTag) { |
|
847 if (m_tree.form()) { |
|
848 parseError(token); |
|
849 return; |
|
850 } |
|
851 processFakePEndTagIfPInButtonScope(); |
|
852 m_tree.insertHTMLFormElement(token); |
|
853 return; |
|
854 } |
|
855 if (token.name() == liTag) { |
|
856 processCloseWhenNestedTag<isLi>(token); |
|
857 return; |
|
858 } |
|
859 if (token.name() == ddTag || token.name() == dtTag) { |
|
860 processCloseWhenNestedTag<isDdOrDt>(token); |
|
861 return; |
|
862 } |
|
863 if (token.name() == plaintextTag) { |
|
864 processFakePEndTagIfPInButtonScope(); |
|
865 m_tree.insertHTMLElement(token); |
|
866 m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState); |
|
867 return; |
|
868 } |
|
869 if (token.name() == buttonTag) { |
|
870 if (m_tree.openElements()->inScope(buttonTag)) { |
|
871 parseError(token); |
|
872 processFakeEndTag(buttonTag); |
|
873 processStartTag(token); // FIXME: Could we just fall through here? |
|
874 return; |
|
875 } |
|
876 m_tree.reconstructTheActiveFormattingElements(); |
|
877 m_tree.insertHTMLElement(token); |
|
878 m_framesetOk = false; |
|
879 return; |
|
880 } |
|
881 if (token.name() == aTag) { |
|
882 Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName()); |
|
883 if (activeATag) { |
|
884 parseError(token); |
|
885 processFakeEndTag(aTag); |
|
886 m_tree.activeFormattingElements()->remove(activeATag); |
|
887 if (m_tree.openElements()->contains(activeATag)) |
|
888 m_tree.openElements()->remove(activeATag); |
|
889 } |
|
890 m_tree.reconstructTheActiveFormattingElements(); |
|
891 m_tree.insertFormattingElement(token); |
|
892 return; |
|
893 } |
|
894 if (isNonAnchorNonNobrFormattingTag(token.name())) { |
|
895 m_tree.reconstructTheActiveFormattingElements(); |
|
896 m_tree.insertFormattingElement(token); |
|
897 return; |
|
898 } |
|
899 if (token.name() == nobrTag) { |
|
900 m_tree.reconstructTheActiveFormattingElements(); |
|
901 if (m_tree.openElements()->inScope(nobrTag)) { |
|
902 parseError(token); |
|
903 processFakeEndTag(nobrTag); |
|
904 m_tree.reconstructTheActiveFormattingElements(); |
|
905 } |
|
906 m_tree.insertFormattingElement(token); |
|
907 return; |
|
908 } |
|
909 if (token.name() == appletTag |
|
910 || token.name() == marqueeTag |
|
911 || token.name() == objectTag) { |
|
912 m_tree.reconstructTheActiveFormattingElements(); |
|
913 m_tree.insertHTMLElement(token); |
|
914 m_tree.activeFormattingElements()->appendMarker(); |
|
915 m_framesetOk = false; |
|
916 return; |
|
917 } |
|
918 if (token.name() == tableTag) { |
|
919 if (m_document->parseMode() != Document::Compat && m_tree.openElements()->inScope(pTag)) |
|
920 processFakeEndTag(pTag); |
|
921 m_tree.insertHTMLElement(token); |
|
922 m_framesetOk = false; |
|
923 setInsertionMode(InTableMode); |
|
924 return; |
|
925 } |
|
926 if (token.name() == imageTag) { |
|
927 parseError(token); |
|
928 // Apparently we're not supposed to ask. |
|
929 token.setName(imgTag.localName()); |
|
930 // Note the fall through to the imgTag handling below! |
|
931 } |
|
932 if (token.name() == areaTag |
|
933 || token.name() == brTag |
|
934 || token.name() == embedTag |
|
935 || token.name() == imgTag |
|
936 || token.name() == inputTag |
|
937 || token.name() == keygenTag |
|
938 || token.name() == wbrTag) { |
|
939 m_tree.reconstructTheActiveFormattingElements(); |
|
940 m_tree.insertSelfClosingHTMLElement(token); |
|
941 m_framesetOk = false; |
|
942 return; |
|
943 } |
|
944 if (token.name() == paramTag |
|
945 || token.name() == sourceTag |
|
946 || token.name() == "track") { |
|
947 m_tree.insertSelfClosingHTMLElement(token); |
|
948 return; |
|
949 } |
|
950 if (token.name() == hrTag) { |
|
951 processFakePEndTagIfPInButtonScope(); |
|
952 m_tree.insertSelfClosingHTMLElement(token); |
|
953 m_framesetOk = false; |
|
954 return; |
|
955 } |
|
956 if (token.name() == isindexTag) { |
|
957 processIsindexStartTagForInBody(token); |
|
958 return; |
|
959 } |
|
960 if (token.name() == textareaTag) { |
|
961 m_tree.insertHTMLElement(token); |
|
962 m_tokenizer->skipLeadingNewLineForListing(); |
|
963 m_tokenizer->setState(HTMLTokenizer::RCDATAState); |
|
964 m_originalInsertionMode = m_insertionMode; |
|
965 m_framesetOk = false; |
|
966 setInsertionMode(TextMode); |
|
967 return; |
|
968 } |
|
969 if (token.name() == xmpTag) { |
|
970 processFakePEndTagIfPInButtonScope(); |
|
971 m_tree.reconstructTheActiveFormattingElements(); |
|
972 m_framesetOk = false; |
|
973 processGenericRawTextStartTag(token); |
|
974 return; |
|
975 } |
|
976 if (token.name() == iframeTag) { |
|
977 m_framesetOk = false; |
|
978 processGenericRawTextStartTag(token); |
|
979 return; |
|
980 } |
|
981 if (token.name() == noembedTag) { |
|
982 processGenericRawTextStartTag(token); |
|
983 return; |
|
984 } |
|
985 if (token.name() == noscriptTag && isScriptingFlagEnabled(m_document->frame())) { |
|
986 processGenericRawTextStartTag(token); |
|
987 return; |
|
988 } |
|
989 if (token.name() == selectTag) { |
|
990 m_tree.reconstructTheActiveFormattingElements(); |
|
991 m_tree.insertHTMLElement(token); |
|
992 m_framesetOk = false; |
|
993 if (m_insertionMode == InTableMode |
|
994 || m_insertionMode == InCaptionMode |
|
995 || m_insertionMode == InColumnGroupMode |
|
996 || m_insertionMode == InTableBodyMode |
|
997 || m_insertionMode == InRowMode |
|
998 || m_insertionMode == InCellMode) |
|
999 setInsertionMode(InSelectInTableMode); |
|
1000 else |
|
1001 setInsertionMode(InSelectMode); |
|
1002 return; |
|
1003 } |
|
1004 if (token.name() == optgroupTag || token.name() == optionTag) { |
|
1005 if (m_tree.openElements()->inScope(optionTag.localName())) { |
|
1006 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); |
|
1007 processEndTag(endOption); |
|
1008 } |
|
1009 m_tree.reconstructTheActiveFormattingElements(); |
|
1010 m_tree.insertHTMLElement(token); |
|
1011 return; |
|
1012 } |
|
1013 if (token.name() == rpTag || token.name() == rtTag) { |
|
1014 if (m_tree.openElements()->inScope(rubyTag.localName())) { |
|
1015 m_tree.generateImpliedEndTags(); |
|
1016 if (!m_tree.currentElement()->hasTagName(rubyTag)) { |
|
1017 parseError(token); |
|
1018 m_tree.openElements()->popUntil(rubyTag.localName()); |
|
1019 } |
|
1020 } |
|
1021 m_tree.insertHTMLElement(token); |
|
1022 return; |
|
1023 } |
|
1024 if (token.name() == MathMLNames::mathTag.localName()) { |
|
1025 m_tree.reconstructTheActiveFormattingElements(); |
|
1026 adjustMathMLAttributes(token); |
|
1027 adjustForeignAttributes(token); |
|
1028 m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI); |
|
1029 if (m_insertionMode != InForeignContentMode) { |
|
1030 setSecondaryInsertionMode(m_insertionMode); |
|
1031 setInsertionMode(InForeignContentMode); |
|
1032 } |
|
1033 return; |
|
1034 } |
|
1035 if (token.name() == SVGNames::svgTag.localName()) { |
|
1036 m_tree.reconstructTheActiveFormattingElements(); |
|
1037 adjustSVGAttributes(token); |
|
1038 adjustForeignAttributes(token); |
|
1039 m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI); |
|
1040 if (m_insertionMode != InForeignContentMode) { |
|
1041 setSecondaryInsertionMode(m_insertionMode); |
|
1042 setInsertionMode(InForeignContentMode); |
|
1043 } |
|
1044 return; |
|
1045 } |
|
1046 if (isCaptionColOrColgroupTag(token.name()) |
|
1047 || token.name() == frameTag |
|
1048 || token.name() == headTag |
|
1049 || isTableBodyContextTag(token.name()) |
|
1050 || isTableCellContextTag(token.name()) |
|
1051 || token.name() == trTag) { |
|
1052 parseError(token); |
|
1053 return; |
|
1054 } |
|
1055 m_tree.reconstructTheActiveFormattingElements(); |
|
1056 m_tree.insertHTMLElement(token); |
|
1057 } |
|
1058 |
|
1059 bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup() |
|
1060 { |
|
1061 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { |
|
1062 ASSERT(m_isParsingFragment); |
|
1063 // FIXME: parse error |
|
1064 return false; |
|
1065 } |
|
1066 m_tree.openElements()->pop(); |
|
1067 setInsertionMode(InTableMode); |
|
1068 return true; |
|
1069 } |
|
1070 |
|
1071 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell |
|
1072 void HTMLTreeBuilder::closeTheCell() |
|
1073 { |
|
1074 ASSERT(insertionMode() == InCellMode); |
|
1075 if (m_tree.openElements()->inTableScope(tdTag)) { |
|
1076 ASSERT(!m_tree.openElements()->inTableScope(thTag)); |
|
1077 processFakeEndTag(tdTag); |
|
1078 return; |
|
1079 } |
|
1080 ASSERT(m_tree.openElements()->inTableScope(thTag)); |
|
1081 processFakeEndTag(thTag); |
|
1082 ASSERT(insertionMode() == InRowMode); |
|
1083 } |
|
1084 |
|
1085 void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token) |
|
1086 { |
|
1087 ASSERT(token.type() == HTMLToken::StartTag); |
|
1088 if (token.name() == captionTag) { |
|
1089 m_tree.openElements()->popUntilTableScopeMarker(); |
|
1090 m_tree.activeFormattingElements()->appendMarker(); |
|
1091 m_tree.insertHTMLElement(token); |
|
1092 setInsertionMode(InCaptionMode); |
|
1093 return; |
|
1094 } |
|
1095 if (token.name() == colgroupTag) { |
|
1096 m_tree.openElements()->popUntilTableScopeMarker(); |
|
1097 m_tree.insertHTMLElement(token); |
|
1098 setInsertionMode(InColumnGroupMode); |
|
1099 return; |
|
1100 } |
|
1101 if (token.name() == colTag) { |
|
1102 processFakeStartTag(colgroupTag); |
|
1103 ASSERT(InColumnGroupMode); |
|
1104 processStartTag(token); |
|
1105 return; |
|
1106 } |
|
1107 if (isTableBodyContextTag(token.name())) { |
|
1108 m_tree.openElements()->popUntilTableScopeMarker(); |
|
1109 m_tree.insertHTMLElement(token); |
|
1110 setInsertionMode(InTableBodyMode); |
|
1111 return; |
|
1112 } |
|
1113 if (isTableCellContextTag(token.name()) |
|
1114 || token.name() == trTag) { |
|
1115 processFakeStartTag(tbodyTag); |
|
1116 ASSERT(insertionMode() == InTableBodyMode); |
|
1117 processStartTag(token); |
|
1118 return; |
|
1119 } |
|
1120 if (token.name() == tableTag) { |
|
1121 parseError(token); |
|
1122 if (!processTableEndTagForInTable()) { |
|
1123 ASSERT(m_isParsingFragment); |
|
1124 return; |
|
1125 } |
|
1126 processStartTag(token); |
|
1127 return; |
|
1128 } |
|
1129 if (token.name() == styleTag || token.name() == scriptTag) { |
|
1130 processStartTagForInHead(token); |
|
1131 return; |
|
1132 } |
|
1133 if (token.name() == inputTag) { |
|
1134 Attribute* typeAttribute = token.getAttributeItem(typeAttr); |
|
1135 if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) { |
|
1136 parseError(token); |
|
1137 m_tree.insertSelfClosingHTMLElement(token); |
|
1138 return; |
|
1139 } |
|
1140 // Fall through to "anything else" case. |
|
1141 } |
|
1142 if (token.name() == formTag) { |
|
1143 parseError(token); |
|
1144 if (m_tree.form()) |
|
1145 return; |
|
1146 // FIXME: This deviates from the spec: |
|
1147 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10216 |
|
1148 m_tree.insertHTMLFormElement(token); |
|
1149 m_tree.openElements()->pop(); |
|
1150 return; |
|
1151 } |
|
1152 parseError(token); |
|
1153 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); |
|
1154 processStartTagForInBody(token); |
|
1155 } |
|
1156 |
|
1157 namespace { |
|
1158 |
|
1159 bool shouldProcessUsingSecondaryInsertionMode(AtomicHTMLToken& token, Element* currentElement) |
|
1160 { |
|
1161 ASSERT(token.type() == HTMLToken::StartTag); |
|
1162 if (currentElement->hasTagName(MathMLNames::miTag) |
|
1163 || currentElement->hasTagName(MathMLNames::moTag) |
|
1164 || currentElement->hasTagName(MathMLNames::mnTag) |
|
1165 || currentElement->hasTagName(MathMLNames::msTag) |
|
1166 || currentElement->hasTagName(MathMLNames::mtextTag)) { |
|
1167 return token.name() != MathMLNames::mglyphTag |
|
1168 && token.name() != MathMLNames::malignmarkTag; |
|
1169 } |
|
1170 if (currentElement->hasTagName(MathMLNames::annotation_xmlTag)) |
|
1171 return token.name() == SVGNames::svgTag; |
|
1172 if (currentElement->hasTagName(SVGNames::foreignObjectTag) |
|
1173 || currentElement->hasTagName(SVGNames::descTag) |
|
1174 || currentElement->hasTagName(SVGNames::titleTag)) |
|
1175 return true; |
|
1176 return currentElement->namespaceURI() == HTMLNames::xhtmlNamespaceURI; |
|
1177 } |
|
1178 |
|
1179 } |
|
1180 |
|
1181 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) |
|
1182 { |
|
1183 ASSERT(token.type() == HTMLToken::StartTag); |
|
1184 switch (insertionMode()) { |
|
1185 case InitialMode: |
|
1186 ASSERT(insertionMode() == InitialMode); |
|
1187 defaultForInitial(); |
|
1188 // Fall through. |
|
1189 case BeforeHTMLMode: |
|
1190 ASSERT(insertionMode() == BeforeHTMLMode); |
|
1191 if (token.name() == htmlTag) { |
|
1192 m_tree.insertHTMLHtmlStartTagBeforeHTML(token); |
|
1193 setInsertionMode(BeforeHeadMode); |
|
1194 return; |
|
1195 } |
|
1196 defaultForBeforeHTML(); |
|
1197 // Fall through. |
|
1198 case BeforeHeadMode: |
|
1199 ASSERT(insertionMode() == BeforeHeadMode); |
|
1200 if (token.name() == htmlTag) { |
|
1201 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1202 return; |
|
1203 } |
|
1204 if (token.name() == headTag) { |
|
1205 m_tree.insertHTMLHeadElement(token); |
|
1206 setInsertionMode(InHeadMode); |
|
1207 return; |
|
1208 } |
|
1209 defaultForBeforeHead(); |
|
1210 // Fall through. |
|
1211 case InHeadMode: |
|
1212 ASSERT(insertionMode() == InHeadMode); |
|
1213 if (processStartTagForInHead(token)) |
|
1214 return; |
|
1215 defaultForInHead(); |
|
1216 // Fall through. |
|
1217 case AfterHeadMode: |
|
1218 ASSERT(insertionMode() == AfterHeadMode); |
|
1219 if (token.name() == htmlTag) { |
|
1220 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1221 return; |
|
1222 } |
|
1223 if (token.name() == bodyTag) { |
|
1224 m_framesetOk = false; |
|
1225 m_tree.insertHTMLBodyElement(token); |
|
1226 setInsertionMode(InBodyMode); |
|
1227 return; |
|
1228 } |
|
1229 if (token.name() == framesetTag) { |
|
1230 m_tree.insertHTMLElement(token); |
|
1231 setInsertionMode(InFramesetMode); |
|
1232 return; |
|
1233 } |
|
1234 if (token.name() == baseTag |
|
1235 || token.name() == basefontTag |
|
1236 || token.name() == "bgsound" |
|
1237 || token.name() == linkTag |
|
1238 || token.name() == metaTag |
|
1239 || token.name() == noframesTag |
|
1240 || token.name() == scriptTag |
|
1241 || token.name() == styleTag |
|
1242 || token.name() == titleTag) { |
|
1243 parseError(token); |
|
1244 ASSERT(m_tree.head()); |
|
1245 m_tree.openElements()->pushHTMLHeadElement(m_tree.head()); |
|
1246 processStartTagForInHead(token); |
|
1247 m_tree.openElements()->removeHTMLHeadElement(m_tree.head()); |
|
1248 return; |
|
1249 } |
|
1250 if (token.name() == headTag) { |
|
1251 parseError(token); |
|
1252 return; |
|
1253 } |
|
1254 defaultForAfterHead(); |
|
1255 // Fall through |
|
1256 case InBodyMode: |
|
1257 ASSERT(insertionMode() == InBodyMode); |
|
1258 processStartTagForInBody(token); |
|
1259 break; |
|
1260 case InTableMode: |
|
1261 ASSERT(insertionMode() == InTableMode); |
|
1262 processStartTagForInTable(token); |
|
1263 break; |
|
1264 case InCaptionMode: |
|
1265 ASSERT(insertionMode() == InCaptionMode); |
|
1266 if (isCaptionColOrColgroupTag(token.name()) |
|
1267 || isTableBodyContextTag(token.name()) |
|
1268 || isTableCellContextTag(token.name()) |
|
1269 || token.name() == trTag) { |
|
1270 parseError(token); |
|
1271 if (!processCaptionEndTagForInCaption()) { |
|
1272 ASSERT(m_isParsingFragment); |
|
1273 return; |
|
1274 } |
|
1275 processStartTag(token); |
|
1276 return; |
|
1277 } |
|
1278 processStartTagForInBody(token); |
|
1279 break; |
|
1280 case InColumnGroupMode: |
|
1281 ASSERT(insertionMode() == InColumnGroupMode); |
|
1282 if (token.name() == htmlTag) { |
|
1283 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1284 return; |
|
1285 } |
|
1286 if (token.name() == colTag) { |
|
1287 m_tree.insertSelfClosingHTMLElement(token); |
|
1288 return; |
|
1289 } |
|
1290 if (!processColgroupEndTagForInColumnGroup()) { |
|
1291 ASSERT(m_isParsingFragment); |
|
1292 return; |
|
1293 } |
|
1294 processStartTag(token); |
|
1295 break; |
|
1296 case InTableBodyMode: |
|
1297 ASSERT(insertionMode() == InTableBodyMode); |
|
1298 if (token.name() == trTag) { |
|
1299 m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop? |
|
1300 m_tree.insertHTMLElement(token); |
|
1301 setInsertionMode(InRowMode); |
|
1302 return; |
|
1303 } |
|
1304 if (isTableCellContextTag(token.name())) { |
|
1305 parseError(token); |
|
1306 processFakeStartTag(trTag); |
|
1307 ASSERT(insertionMode() == InRowMode); |
|
1308 processStartTag(token); |
|
1309 return; |
|
1310 } |
|
1311 if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) { |
|
1312 // FIXME: This is slow. |
|
1313 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) { |
|
1314 ASSERT(m_isParsingFragment); |
|
1315 parseError(token); |
|
1316 return; |
|
1317 } |
|
1318 m_tree.openElements()->popUntilTableBodyScopeMarker(); |
|
1319 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName())); |
|
1320 processFakeEndTag(m_tree.currentElement()->tagQName()); |
|
1321 processStartTag(token); |
|
1322 return; |
|
1323 } |
|
1324 processStartTagForInTable(token); |
|
1325 break; |
|
1326 case InRowMode: |
|
1327 ASSERT(insertionMode() == InRowMode); |
|
1328 if (isTableCellContextTag(token.name())) { |
|
1329 m_tree.openElements()->popUntilTableRowScopeMarker(); |
|
1330 m_tree.insertHTMLElement(token); |
|
1331 setInsertionMode(InCellMode); |
|
1332 m_tree.activeFormattingElements()->appendMarker(); |
|
1333 return; |
|
1334 } |
|
1335 if (token.name() == trTag |
|
1336 || isCaptionColOrColgroupTag(token.name()) |
|
1337 || isTableBodyContextTag(token.name())) { |
|
1338 if (!processTrEndTagForInRow()) { |
|
1339 ASSERT(m_isParsingFragment); |
|
1340 return; |
|
1341 } |
|
1342 ASSERT(insertionMode() == InTableBodyMode); |
|
1343 processStartTag(token); |
|
1344 return; |
|
1345 } |
|
1346 processStartTagForInTable(token); |
|
1347 break; |
|
1348 case InCellMode: |
|
1349 ASSERT(insertionMode() == InCellMode); |
|
1350 if (isCaptionColOrColgroupTag(token.name()) |
|
1351 || isTableCellContextTag(token.name()) |
|
1352 || token.name() == trTag |
|
1353 || isTableBodyContextTag(token.name())) { |
|
1354 // FIXME: This could be more efficient. |
|
1355 if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) { |
|
1356 ASSERT(m_isParsingFragment); |
|
1357 parseError(token); |
|
1358 return; |
|
1359 } |
|
1360 closeTheCell(); |
|
1361 processStartTag(token); |
|
1362 return; |
|
1363 } |
|
1364 processStartTagForInBody(token); |
|
1365 break; |
|
1366 case AfterBodyMode: |
|
1367 case AfterAfterBodyMode: |
|
1368 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); |
|
1369 if (token.name() == htmlTag) { |
|
1370 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1371 return; |
|
1372 } |
|
1373 setInsertionMode(InBodyMode); |
|
1374 processStartTag(token); |
|
1375 break; |
|
1376 case InHeadNoscriptMode: |
|
1377 ASSERT(insertionMode() == InHeadNoscriptMode); |
|
1378 if (token.name() == htmlTag) { |
|
1379 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1380 return; |
|
1381 } |
|
1382 if (token.name() == basefontTag |
|
1383 || token.name() == "bgsound" |
|
1384 || token.name() == linkTag |
|
1385 || token.name() == metaTag |
|
1386 || token.name() == noframesTag |
|
1387 || token.name() == styleTag) { |
|
1388 bool didProcess = processStartTagForInHead(token); |
|
1389 ASSERT_UNUSED(didProcess, didProcess); |
|
1390 return; |
|
1391 } |
|
1392 if (token.name() == htmlTag || token.name() == noscriptTag) { |
|
1393 parseError(token); |
|
1394 return; |
|
1395 } |
|
1396 defaultForInHeadNoscript(); |
|
1397 processToken(token); |
|
1398 break; |
|
1399 case InFramesetMode: |
|
1400 ASSERT(insertionMode() == InFramesetMode); |
|
1401 if (token.name() == htmlTag) { |
|
1402 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1403 return; |
|
1404 } |
|
1405 if (token.name() == framesetTag) { |
|
1406 m_tree.insertHTMLElement(token); |
|
1407 return; |
|
1408 } |
|
1409 if (token.name() == frameTag) { |
|
1410 m_tree.insertSelfClosingHTMLElement(token); |
|
1411 return; |
|
1412 } |
|
1413 if (token.name() == noframesTag) { |
|
1414 processStartTagForInHead(token); |
|
1415 return; |
|
1416 } |
|
1417 parseError(token); |
|
1418 break; |
|
1419 case AfterFramesetMode: |
|
1420 case AfterAfterFramesetMode: |
|
1421 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); |
|
1422 if (token.name() == htmlTag) { |
|
1423 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1424 return; |
|
1425 } |
|
1426 if (token.name() == noframesTag) { |
|
1427 processStartTagForInHead(token); |
|
1428 return; |
|
1429 } |
|
1430 parseError(token); |
|
1431 break; |
|
1432 case InSelectInTableMode: |
|
1433 ASSERT(insertionMode() == InSelectInTableMode); |
|
1434 if (token.name() == captionTag |
|
1435 || token.name() == tableTag |
|
1436 || isTableBodyContextTag(token.name()) |
|
1437 || token.name() == trTag |
|
1438 || isTableCellContextTag(token.name())) { |
|
1439 parseError(token); |
|
1440 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); |
|
1441 processEndTag(endSelect); |
|
1442 processStartTag(token); |
|
1443 return; |
|
1444 } |
|
1445 // Fall through |
|
1446 case InSelectMode: |
|
1447 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); |
|
1448 if (token.name() == htmlTag) { |
|
1449 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
1450 return; |
|
1451 } |
|
1452 if (token.name() == optionTag) { |
|
1453 if (m_tree.currentElement()->hasTagName(optionTag)) { |
|
1454 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); |
|
1455 processEndTag(endOption); |
|
1456 } |
|
1457 m_tree.insertHTMLElement(token); |
|
1458 return; |
|
1459 } |
|
1460 if (token.name() == optgroupTag) { |
|
1461 if (m_tree.currentElement()->hasTagName(optionTag)) { |
|
1462 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); |
|
1463 processEndTag(endOption); |
|
1464 } |
|
1465 if (m_tree.currentElement()->hasTagName(optgroupTag)) { |
|
1466 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName()); |
|
1467 processEndTag(endOptgroup); |
|
1468 } |
|
1469 m_tree.insertHTMLElement(token); |
|
1470 return; |
|
1471 } |
|
1472 if (token.name() == selectTag) { |
|
1473 parseError(token); |
|
1474 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); |
|
1475 processEndTag(endSelect); |
|
1476 return; |
|
1477 } |
|
1478 if (token.name() == inputTag |
|
1479 || token.name() == keygenTag |
|
1480 || token.name() == textareaTag) { |
|
1481 parseError(token); |
|
1482 notImplemented(); // fragment case |
|
1483 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); |
|
1484 processEndTag(endSelect); |
|
1485 processStartTag(token); |
|
1486 return; |
|
1487 } |
|
1488 if (token.name() == scriptTag) { |
|
1489 bool didProcess = processStartTagForInHead(token); |
|
1490 ASSERT_UNUSED(didProcess, didProcess); |
|
1491 return; |
|
1492 } |
|
1493 break; |
|
1494 case InTableTextMode: |
|
1495 defaultForInTableText(); |
|
1496 processStartTag(token); |
|
1497 break; |
|
1498 case InForeignContentMode: { |
|
1499 // FIXME: We're missing a bunch of if branches here. |
|
1500 if (shouldProcessUsingSecondaryInsertionMode(token, m_tree.currentElement())) { |
|
1501 processUsingSecondaryInsertionModeAndAdjustInsertionMode(token); |
|
1502 return; |
|
1503 } |
|
1504 if (token.name() == bTag |
|
1505 || token.name() == bigTag |
|
1506 || token.name() == blockquoteTag |
|
1507 || token.name() == bodyTag |
|
1508 || token.name() == brTag |
|
1509 || token.name() == centerTag |
|
1510 || token.name() == codeTag |
|
1511 || token.name() == ddTag |
|
1512 || token.name() == divTag |
|
1513 || token.name() == dlTag |
|
1514 || token.name() == dtTag |
|
1515 || token.name() == emTag |
|
1516 || token.name() == embedTag |
|
1517 || isNumberedHeaderTag(token.name()) |
|
1518 || token.name() == headTag |
|
1519 || token.name() == hrTag |
|
1520 || token.name() == iTag |
|
1521 || token.name() == imgTag |
|
1522 || token.name() == liTag |
|
1523 || token.name() == listingTag |
|
1524 || token.name() == menuTag |
|
1525 || token.name() == metaTag |
|
1526 || token.name() == nobrTag |
|
1527 || token.name() == olTag |
|
1528 || token.name() == pTag |
|
1529 || token.name() == preTag |
|
1530 || token.name() == rubyTag |
|
1531 || token.name() == sTag |
|
1532 || token.name() == smallTag |
|
1533 || token.name() == spanTag |
|
1534 || token.name() == strongTag |
|
1535 || token.name() == strikeTag |
|
1536 || token.name() == subTag |
|
1537 || token.name() == supTag |
|
1538 || token.name() == tableTag |
|
1539 || token.name() == ttTag |
|
1540 || token.name() == uTag |
|
1541 || token.name() == ulTag |
|
1542 || token.name() == varTag |
|
1543 || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) { |
|
1544 m_tree.openElements()->popUntilElementWithNamespace(xhtmlNamespaceURI); |
|
1545 setInsertionMode(m_secondaryInsertionMode); |
|
1546 processStartTag(token); |
|
1547 return; |
|
1548 } |
|
1549 const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI(); |
|
1550 if (currentNamespace == MathMLNames::mathmlNamespaceURI) |
|
1551 adjustMathMLAttributes(token); |
|
1552 if (currentNamespace == SVGNames::svgNamespaceURI) { |
|
1553 adjustSVGTagNameCase(token); |
|
1554 adjustSVGAttributes(token); |
|
1555 } |
|
1556 adjustForeignAttributes(token); |
|
1557 m_tree.insertForeignElement(token, currentNamespace); |
|
1558 break; |
|
1559 } |
|
1560 case TextMode: |
|
1561 notImplemented(); |
|
1562 break; |
|
1563 } |
|
1564 } |
|
1565 |
|
1566 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token) |
|
1567 { |
|
1568 ASSERT(token.type() == HTMLToken::EndTag); |
|
1569 ASSERT(token.name() == bodyTag); |
|
1570 if (!m_tree.openElements()->inScope(bodyTag.localName())) { |
|
1571 parseError(token); |
|
1572 return false; |
|
1573 } |
|
1574 notImplemented(); // Emit a more specific parse error based on stack contents. |
|
1575 setInsertionMode(AfterBodyMode); |
|
1576 return true; |
|
1577 } |
|
1578 |
|
1579 void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token) |
|
1580 { |
|
1581 ASSERT(token.type() == HTMLToken::EndTag); |
|
1582 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); |
|
1583 while (1) { |
|
1584 Element* node = record->element(); |
|
1585 if (node->hasLocalName(token.name())) { |
|
1586 m_tree.generateImpliedEndTags(); |
|
1587 if (!m_tree.currentElement()->hasLocalName(token.name())) { |
|
1588 parseError(token); |
|
1589 // FIXME: This is either a bug in the spec, or a bug in our |
|
1590 // implementation. Filed a bug with HTML5: |
|
1591 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080 |
|
1592 // We might have already popped the node for the token in |
|
1593 // generateImpliedEndTags, just abort. |
|
1594 if (!m_tree.openElements()->contains(node)) |
|
1595 return; |
|
1596 } |
|
1597 m_tree.openElements()->popUntilPopped(node); |
|
1598 return; |
|
1599 } |
|
1600 if (isNotFormattingAndNotPhrasing(node)) { |
|
1601 parseError(token); |
|
1602 return; |
|
1603 } |
|
1604 record = record->next(); |
|
1605 } |
|
1606 } |
|
1607 |
|
1608 // FIXME: This probably belongs on HTMLElementStack. |
|
1609 HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement) |
|
1610 { |
|
1611 HTMLElementStack::ElementRecord* furthestBlock = 0; |
|
1612 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); |
|
1613 for (; record; record = record->next()) { |
|
1614 if (record->element() == formattingElement) |
|
1615 return furthestBlock; |
|
1616 if (isNotFormattingAndNotPhrasing(record->element())) |
|
1617 furthestBlock = record; |
|
1618 } |
|
1619 ASSERT_NOT_REACHED(); |
|
1620 return 0; |
|
1621 } |
|
1622 |
|
1623 // FIXME: This should have a whitty name. |
|
1624 // FIXME: This must be implemented in many other places in WebCore. |
|
1625 void HTMLTreeBuilder::reparentChildren(Element* oldParent, Element* newParent) |
|
1626 { |
|
1627 Node* child = oldParent->firstChild(); |
|
1628 while (child) { |
|
1629 Node* nextChild = child->nextSibling(); |
|
1630 ExceptionCode ec; |
|
1631 newParent->appendChild(child, ec); |
|
1632 ASSERT(!ec); |
|
1633 child = nextChild; |
|
1634 } |
|
1635 } |
|
1636 |
|
1637 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody |
|
1638 void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) |
|
1639 { |
|
1640 while (1) { |
|
1641 // 1. |
|
1642 Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name()); |
|
1643 if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) { |
|
1644 parseError(token); |
|
1645 notImplemented(); // Check the stack of open elements for a more specific parse error. |
|
1646 return; |
|
1647 } |
|
1648 HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement); |
|
1649 if (!formattingElementRecord) { |
|
1650 parseError(token); |
|
1651 m_tree.activeFormattingElements()->remove(formattingElement); |
|
1652 return; |
|
1653 } |
|
1654 if (formattingElement != m_tree.currentElement()) |
|
1655 parseError(token); |
|
1656 // 2. |
|
1657 HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement); |
|
1658 // 3. |
|
1659 if (!furthestBlock) { |
|
1660 m_tree.openElements()->popUntilPopped(formattingElement); |
|
1661 m_tree.activeFormattingElements()->remove(formattingElement); |
|
1662 return; |
|
1663 } |
|
1664 // 4. |
|
1665 ASSERT(furthestBlock->isAbove(formattingElementRecord)); |
|
1666 Element* commonAncestor = formattingElementRecord->next()->element(); |
|
1667 // 5. |
|
1668 HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement); |
|
1669 // 6. |
|
1670 HTMLElementStack::ElementRecord* node = furthestBlock; |
|
1671 HTMLElementStack::ElementRecord* nextNode = node->next(); |
|
1672 HTMLElementStack::ElementRecord* lastNode = furthestBlock; |
|
1673 while (1) { |
|
1674 // 6.1 |
|
1675 node = nextNode; |
|
1676 ASSERT(node); |
|
1677 nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2. |
|
1678 // 6.2 |
|
1679 if (!m_tree.activeFormattingElements()->contains(node->element())) { |
|
1680 m_tree.openElements()->remove(node->element()); |
|
1681 node = 0; |
|
1682 continue; |
|
1683 } |
|
1684 // 6.3 |
|
1685 if (node == formattingElementRecord) |
|
1686 break; |
|
1687 // 6.5 |
|
1688 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node); |
|
1689 HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element()); |
|
1690 nodeEntry->replaceElement(newElement.get()); |
|
1691 node->replaceElement(newElement.release()); |
|
1692 // 6.4 -- Intentionally out of order to handle the case where node |
|
1693 // was replaced in 6.5. |
|
1694 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096 |
|
1695 if (lastNode == furthestBlock) |
|
1696 bookmark.moveToAfter(nodeEntry); |
|
1697 // 6.6 |
|
1698 // Use appendChild instead of parserAddChild to handle possible reparenting. |
|
1699 ExceptionCode ec; |
|
1700 node->element()->appendChild(lastNode->element(), ec); |
|
1701 ASSERT(!ec); |
|
1702 // 6.7 |
|
1703 lastNode = node; |
|
1704 } |
|
1705 // 7 |
|
1706 const AtomicString& commonAncestorTag = commonAncestor->localName(); |
|
1707 // FIXME: If this moves to HTMLConstructionSite, this check should use |
|
1708 // causesFosterParenting(tagName) instead. |
|
1709 if (commonAncestorTag == tableTag |
|
1710 || commonAncestorTag == trTag |
|
1711 || isTableBodyContextTag(commonAncestorTag)) |
|
1712 m_tree.fosterParent(lastNode->element()); |
|
1713 else { |
|
1714 ExceptionCode ec; |
|
1715 commonAncestor->appendChild(lastNode->element(), ec); |
|
1716 ASSERT(!ec); |
|
1717 } |
|
1718 // 8 |
|
1719 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord); |
|
1720 // 9 |
|
1721 reparentChildren(furthestBlock->element(), newElement.get()); |
|
1722 // 10 |
|
1723 Element* furthestBlockElement = furthestBlock->element(); |
|
1724 // FIXME: All this creation / parserAddChild / attach business should |
|
1725 // be in HTMLConstructionSite. My guess is that steps 8--12 |
|
1726 // should all be in some HTMLConstructionSite function. |
|
1727 furthestBlockElement->parserAddChild(newElement); |
|
1728 if (furthestBlockElement->attached()) { |
|
1729 ASSERT(!newElement->attached()); |
|
1730 newElement->attach(); |
|
1731 } |
|
1732 // 11 |
|
1733 m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark); |
|
1734 // 12 |
|
1735 m_tree.openElements()->remove(formattingElement); |
|
1736 m_tree.openElements()->insertAbove(newElement, furthestBlock); |
|
1737 } |
|
1738 } |
|
1739 |
|
1740 void HTMLTreeBuilder::setSecondaryInsertionMode(InsertionMode mode) |
|
1741 { |
|
1742 ASSERT(mode != InForeignContentMode); |
|
1743 m_secondaryInsertionMode = mode; |
|
1744 } |
|
1745 |
|
1746 void HTMLTreeBuilder::setInsertionModeAndEnd(InsertionMode newInsertionMode, bool foreign) |
|
1747 { |
|
1748 setInsertionMode(newInsertionMode); |
|
1749 if (foreign) { |
|
1750 setSecondaryInsertionMode(m_insertionMode); |
|
1751 setInsertionMode(InForeignContentMode); |
|
1752 } |
|
1753 } |
|
1754 |
|
1755 void HTMLTreeBuilder::resetInsertionModeAppropriately() |
|
1756 { |
|
1757 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately |
|
1758 bool last = false; |
|
1759 bool foreign = false; |
|
1760 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); |
|
1761 while (1) { |
|
1762 Element* node = nodeRecord->element(); |
|
1763 if (node == m_tree.openElements()->bottom()) { |
|
1764 ASSERT(m_isParsingFragment); |
|
1765 last = true; |
|
1766 notImplemented(); // node = m_contextElement; |
|
1767 } |
|
1768 if (node->hasTagName(selectTag)) { |
|
1769 ASSERT(m_isParsingFragment); |
|
1770 return setInsertionModeAndEnd(InSelectMode, foreign); |
|
1771 } |
|
1772 if (node->hasTagName(tdTag) || node->hasTagName(thTag)) |
|
1773 return setInsertionModeAndEnd(InCellMode, foreign); |
|
1774 if (node->hasTagName(trTag)) |
|
1775 return setInsertionModeAndEnd(InRowMode, foreign); |
|
1776 if (isTableBodyContextTag(node->localName())) |
|
1777 return setInsertionModeAndEnd(InTableBodyMode, foreign); |
|
1778 if (node->hasTagName(captionTag)) |
|
1779 return setInsertionModeAndEnd(InCaptionMode, foreign); |
|
1780 if (node->hasTagName(colgroupTag)) { |
|
1781 ASSERT(m_isParsingFragment); |
|
1782 return setInsertionModeAndEnd(InColumnGroupMode, foreign); |
|
1783 } |
|
1784 if (node->hasTagName(tableTag)) |
|
1785 return setInsertionModeAndEnd(InTableMode, foreign); |
|
1786 if (node->hasTagName(headTag)) { |
|
1787 ASSERT(m_isParsingFragment); |
|
1788 return setInsertionModeAndEnd(InBodyMode, foreign); |
|
1789 } |
|
1790 if (node->hasTagName(bodyTag)) |
|
1791 return setInsertionModeAndEnd(InBodyMode, foreign); |
|
1792 if (node->hasTagName(framesetTag)) { |
|
1793 ASSERT(m_isParsingFragment); |
|
1794 return setInsertionModeAndEnd(InFramesetMode, foreign); |
|
1795 } |
|
1796 if (node->hasTagName(htmlTag)) { |
|
1797 ASSERT(m_isParsingFragment); |
|
1798 return setInsertionModeAndEnd(BeforeHeadMode, foreign); |
|
1799 } |
|
1800 if (node->namespaceURI() == SVGNames::svgNamespaceURI |
|
1801 || node->namespaceURI() == MathMLNames::mathmlNamespaceURI) |
|
1802 foreign = true; |
|
1803 if (last) { |
|
1804 ASSERT(m_isParsingFragment); |
|
1805 return setInsertionModeAndEnd(InBodyMode, foreign); |
|
1806 } |
|
1807 nodeRecord = nodeRecord->next(); |
|
1808 } |
|
1809 } |
|
1810 |
|
1811 void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token) |
|
1812 { |
|
1813 ASSERT(token.type() == HTMLToken::EndTag); |
|
1814 if (isTableBodyContextTag(token.name())) { |
|
1815 if (!m_tree.openElements()->inTableScope(token.name())) { |
|
1816 parseError(token); |
|
1817 return; |
|
1818 } |
|
1819 m_tree.openElements()->popUntilTableBodyScopeMarker(); |
|
1820 m_tree.openElements()->pop(); |
|
1821 setInsertionMode(InTableMode); |
|
1822 return; |
|
1823 } |
|
1824 if (token.name() == tableTag) { |
|
1825 // FIXME: This is slow. |
|
1826 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) { |
|
1827 ASSERT(m_isParsingFragment); |
|
1828 parseError(token); |
|
1829 return; |
|
1830 } |
|
1831 m_tree.openElements()->popUntilTableBodyScopeMarker(); |
|
1832 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName())); |
|
1833 processFakeEndTag(m_tree.currentElement()->tagQName()); |
|
1834 processEndTag(token); |
|
1835 return; |
|
1836 } |
|
1837 if (token.name() == bodyTag |
|
1838 || isCaptionColOrColgroupTag(token.name()) |
|
1839 || token.name() == htmlTag |
|
1840 || isTableCellContextTag(token.name()) |
|
1841 || token.name() == trTag) { |
|
1842 parseError(token); |
|
1843 return; |
|
1844 } |
|
1845 processEndTagForInTable(token); |
|
1846 } |
|
1847 |
|
1848 void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token) |
|
1849 { |
|
1850 ASSERT(token.type() == HTMLToken::EndTag); |
|
1851 if (token.name() == trTag) { |
|
1852 processTrEndTagForInRow(); |
|
1853 return; |
|
1854 } |
|
1855 if (token.name() == tableTag) { |
|
1856 if (!processTrEndTagForInRow()) { |
|
1857 ASSERT(m_isParsingFragment); |
|
1858 return; |
|
1859 } |
|
1860 ASSERT(insertionMode() == InTableBodyMode); |
|
1861 processEndTag(token); |
|
1862 return; |
|
1863 } |
|
1864 if (isTableBodyContextTag(token.name())) { |
|
1865 if (!m_tree.openElements()->inTableScope(token.name())) { |
|
1866 parseError(token); |
|
1867 return; |
|
1868 } |
|
1869 processFakeEndTag(trTag); |
|
1870 ASSERT(insertionMode() == InTableBodyMode); |
|
1871 processEndTag(token); |
|
1872 return; |
|
1873 } |
|
1874 if (token.name() == bodyTag |
|
1875 || isCaptionColOrColgroupTag(token.name()) |
|
1876 || token.name() == htmlTag |
|
1877 || isTableCellContextTag(token.name())) { |
|
1878 parseError(token); |
|
1879 return; |
|
1880 } |
|
1881 processEndTagForInTable(token); |
|
1882 } |
|
1883 |
|
1884 void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token) |
|
1885 { |
|
1886 ASSERT(token.type() == HTMLToken::EndTag); |
|
1887 if (isTableCellContextTag(token.name())) { |
|
1888 if (!m_tree.openElements()->inTableScope(token.name())) { |
|
1889 parseError(token); |
|
1890 return; |
|
1891 } |
|
1892 m_tree.generateImpliedEndTags(); |
|
1893 if (!m_tree.currentElement()->hasLocalName(token.name())) |
|
1894 parseError(token); |
|
1895 m_tree.openElements()->popUntilPopped(token.name()); |
|
1896 m_tree.activeFormattingElements()->clearToLastMarker(); |
|
1897 setInsertionMode(InRowMode); |
|
1898 ASSERT(m_tree.currentElement()->hasTagName(trTag)); |
|
1899 return; |
|
1900 } |
|
1901 if (token.name() == bodyTag |
|
1902 || isCaptionColOrColgroupTag(token.name()) |
|
1903 || token.name() == htmlTag) { |
|
1904 parseError(token); |
|
1905 return; |
|
1906 } |
|
1907 if (token.name() == tableTag |
|
1908 || token.name() == trTag |
|
1909 || isTableBodyContextTag(token.name())) { |
|
1910 if (!m_tree.openElements()->inTableScope(token.name())) { |
|
1911 ASSERT(m_isParsingFragment); |
|
1912 // FIXME: It is unclear what the exact ASSERT should be. |
|
1913 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10098 |
|
1914 parseError(token); |
|
1915 return; |
|
1916 } |
|
1917 closeTheCell(); |
|
1918 processEndTag(token); |
|
1919 return; |
|
1920 } |
|
1921 processEndTagForInBody(token); |
|
1922 } |
|
1923 |
|
1924 void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) |
|
1925 { |
|
1926 ASSERT(token.type() == HTMLToken::EndTag); |
|
1927 if (token.name() == bodyTag) { |
|
1928 processBodyEndTagForInBody(token); |
|
1929 return; |
|
1930 } |
|
1931 if (token.name() == htmlTag) { |
|
1932 AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName()); |
|
1933 if (processBodyEndTagForInBody(endBody)) |
|
1934 processEndTag(token); |
|
1935 return; |
|
1936 } |
|
1937 if (token.name() == addressTag |
|
1938 || token.name() == articleTag |
|
1939 || token.name() == asideTag |
|
1940 || token.name() == blockquoteTag |
|
1941 || token.name() == buttonTag |
|
1942 || token.name() == centerTag |
|
1943 || token.name() == "details" |
|
1944 || token.name() == dirTag |
|
1945 || token.name() == divTag |
|
1946 || token.name() == dlTag |
|
1947 || token.name() == fieldsetTag |
|
1948 || token.name() == "figure" |
|
1949 || token.name() == footerTag |
|
1950 || token.name() == headerTag |
|
1951 || token.name() == hgroupTag |
|
1952 || token.name() == listingTag |
|
1953 || token.name() == menuTag |
|
1954 || token.name() == navTag |
|
1955 || token.name() == olTag |
|
1956 || token.name() == preTag |
|
1957 || token.name() == sectionTag |
|
1958 || token.name() == ulTag) { |
|
1959 if (!m_tree.openElements()->inScope(token.name())) { |
|
1960 parseError(token); |
|
1961 return; |
|
1962 } |
|
1963 m_tree.generateImpliedEndTags(); |
|
1964 if (!m_tree.currentElement()->hasLocalName(token.name())) |
|
1965 parseError(token); |
|
1966 m_tree.openElements()->popUntilPopped(token.name()); |
|
1967 return; |
|
1968 } |
|
1969 if (token.name() == formTag) { |
|
1970 RefPtr<Element> node = m_tree.takeForm(); |
|
1971 if (!node || !m_tree.openElements()->inScope(node.get())) { |
|
1972 parseError(token); |
|
1973 return; |
|
1974 } |
|
1975 m_tree.generateImpliedEndTags(); |
|
1976 if (m_tree.currentElement() != node.get()) |
|
1977 parseError(token); |
|
1978 m_tree.openElements()->remove(node.get()); |
|
1979 } |
|
1980 if (token.name() == pTag) { |
|
1981 if (!m_tree.openElements()->inButtonScope(token.name())) { |
|
1982 parseError(token); |
|
1983 processFakeStartTag(pTag); |
|
1984 ASSERT(m_tree.openElements()->inScope(token.name())); |
|
1985 processEndTag(token); |
|
1986 return; |
|
1987 } |
|
1988 m_tree.generateImpliedEndTagsWithExclusion(token.name()); |
|
1989 if (!m_tree.currentElement()->hasLocalName(token.name())) |
|
1990 parseError(token); |
|
1991 m_tree.openElements()->popUntilPopped(token.name()); |
|
1992 return; |
|
1993 } |
|
1994 if (token.name() == liTag) { |
|
1995 if (!m_tree.openElements()->inListItemScope(token.name())) { |
|
1996 parseError(token); |
|
1997 return; |
|
1998 } |
|
1999 m_tree.generateImpliedEndTagsWithExclusion(token.name()); |
|
2000 if (!m_tree.currentElement()->hasLocalName(token.name())) |
|
2001 parseError(token); |
|
2002 m_tree.openElements()->popUntilPopped(token.name()); |
|
2003 return; |
|
2004 } |
|
2005 if (token.name() == ddTag |
|
2006 || token.name() == dtTag) { |
|
2007 if (!m_tree.openElements()->inScope(token.name())) { |
|
2008 parseError(token); |
|
2009 return; |
|
2010 } |
|
2011 m_tree.generateImpliedEndTagsWithExclusion(token.name()); |
|
2012 if (!m_tree.currentElement()->hasLocalName(token.name())) |
|
2013 parseError(token); |
|
2014 m_tree.openElements()->popUntilPopped(token.name()); |
|
2015 return; |
|
2016 } |
|
2017 if (isNumberedHeaderTag(token.name())) { |
|
2018 if (!m_tree.openElements()->inScope(token.name())) { |
|
2019 parseError(token); |
|
2020 return; |
|
2021 } |
|
2022 m_tree.generateImpliedEndTags(); |
|
2023 if (!m_tree.currentElement()->hasLocalName(token.name())) |
|
2024 parseError(token); |
|
2025 m_tree.openElements()->popUntilPopped(token.name()); |
|
2026 return; |
|
2027 } |
|
2028 if (token.name() == "sarcasm") { |
|
2029 notImplemented(); // Take a deep breath. |
|
2030 return; |
|
2031 } |
|
2032 if (isFormattingTag(token.name())) { |
|
2033 callTheAdoptionAgency(token); |
|
2034 return; |
|
2035 } |
|
2036 if (token.name() == appletTag |
|
2037 || token.name() == marqueeTag |
|
2038 || token.name() == objectTag) { |
|
2039 if (!m_tree.openElements()->inScope(token.name())) { |
|
2040 parseError(token); |
|
2041 return; |
|
2042 } |
|
2043 m_tree.generateImpliedEndTags(); |
|
2044 if (!m_tree.currentElement()->hasLocalName(token.name())) |
|
2045 parseError(token); |
|
2046 m_tree.openElements()->popUntilPopped(token.name()); |
|
2047 m_tree.activeFormattingElements()->clearToLastMarker(); |
|
2048 return; |
|
2049 } |
|
2050 if (token.name() == brTag) { |
|
2051 parseError(token); |
|
2052 processFakeStartTag(brTag); |
|
2053 return; |
|
2054 } |
|
2055 processAnyOtherEndTagForInBody(token); |
|
2056 } |
|
2057 |
|
2058 bool HTMLTreeBuilder::processCaptionEndTagForInCaption() |
|
2059 { |
|
2060 if (!m_tree.openElements()->inTableScope(captionTag.localName())) { |
|
2061 ASSERT(m_isParsingFragment); |
|
2062 // FIXME: parse error |
|
2063 return false; |
|
2064 } |
|
2065 m_tree.generateImpliedEndTags(); |
|
2066 // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag)) |
|
2067 m_tree.openElements()->popUntilPopped(captionTag.localName()); |
|
2068 m_tree.activeFormattingElements()->clearToLastMarker(); |
|
2069 setInsertionMode(InTableMode); |
|
2070 return true; |
|
2071 } |
|
2072 |
|
2073 bool HTMLTreeBuilder::processTrEndTagForInRow() |
|
2074 { |
|
2075 if (!m_tree.openElements()->inTableScope(trTag.localName())) { |
|
2076 ASSERT(m_isParsingFragment); |
|
2077 // FIXME: parse error |
|
2078 return false; |
|
2079 } |
|
2080 m_tree.openElements()->popUntilTableRowScopeMarker(); |
|
2081 ASSERT(m_tree.currentElement()->hasTagName(trTag)); |
|
2082 m_tree.openElements()->pop(); |
|
2083 setInsertionMode(InTableBodyMode); |
|
2084 return true; |
|
2085 } |
|
2086 |
|
2087 bool HTMLTreeBuilder::processTableEndTagForInTable() |
|
2088 { |
|
2089 if (!m_tree.openElements()->inTableScope(tableTag)) { |
|
2090 ASSERT(m_isParsingFragment); |
|
2091 // FIXME: parse error. |
|
2092 return false; |
|
2093 } |
|
2094 m_tree.openElements()->popUntilPopped(tableTag.localName()); |
|
2095 resetInsertionModeAppropriately(); |
|
2096 return true; |
|
2097 } |
|
2098 |
|
2099 void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token) |
|
2100 { |
|
2101 ASSERT(token.type() == HTMLToken::EndTag); |
|
2102 if (token.name() == tableTag) { |
|
2103 processTableEndTagForInTable(); |
|
2104 return; |
|
2105 } |
|
2106 if (token.name() == bodyTag |
|
2107 || isCaptionColOrColgroupTag(token.name()) |
|
2108 || token.name() == htmlTag |
|
2109 || isTableBodyContextTag(token.name()) |
|
2110 || isTableCellContextTag(token.name()) |
|
2111 || token.name() == trTag) { |
|
2112 parseError(token); |
|
2113 return; |
|
2114 } |
|
2115 // Is this redirection necessary here? |
|
2116 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); |
|
2117 processEndTagForInBody(token); |
|
2118 } |
|
2119 |
|
2120 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) |
|
2121 { |
|
2122 ASSERT(token.type() == HTMLToken::EndTag); |
|
2123 switch (insertionMode()) { |
|
2124 case InitialMode: |
|
2125 ASSERT(insertionMode() == InitialMode); |
|
2126 defaultForInitial(); |
|
2127 // Fall through. |
|
2128 case BeforeHTMLMode: |
|
2129 ASSERT(insertionMode() == BeforeHTMLMode); |
|
2130 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { |
|
2131 parseError(token); |
|
2132 return; |
|
2133 } |
|
2134 defaultForBeforeHTML(); |
|
2135 // Fall through. |
|
2136 case BeforeHeadMode: |
|
2137 ASSERT(insertionMode() == BeforeHeadMode); |
|
2138 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { |
|
2139 parseError(token); |
|
2140 return; |
|
2141 } |
|
2142 defaultForBeforeHead(); |
|
2143 // Fall through. |
|
2144 case InHeadMode: |
|
2145 ASSERT(insertionMode() == InHeadMode); |
|
2146 if (token.name() == headTag) { |
|
2147 m_tree.openElements()->popHTMLHeadElement(); |
|
2148 setInsertionMode(AfterHeadMode); |
|
2149 return; |
|
2150 } |
|
2151 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { |
|
2152 parseError(token); |
|
2153 return; |
|
2154 } |
|
2155 defaultForInHead(); |
|
2156 // Fall through. |
|
2157 case AfterHeadMode: |
|
2158 ASSERT(insertionMode() == AfterHeadMode); |
|
2159 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { |
|
2160 parseError(token); |
|
2161 return; |
|
2162 } |
|
2163 defaultForAfterHead(); |
|
2164 // Fall through |
|
2165 case InBodyMode: |
|
2166 ASSERT(insertionMode() == InBodyMode); |
|
2167 processEndTagForInBody(token); |
|
2168 break; |
|
2169 case InTableMode: |
|
2170 ASSERT(insertionMode() == InTableMode); |
|
2171 processEndTagForInTable(token); |
|
2172 break; |
|
2173 case InCaptionMode: |
|
2174 ASSERT(insertionMode() == InCaptionMode); |
|
2175 if (token.name() == captionTag) { |
|
2176 processCaptionEndTagForInCaption(); |
|
2177 return; |
|
2178 } |
|
2179 if (token.name() == tableTag) { |
|
2180 parseError(token); |
|
2181 if (!processCaptionEndTagForInCaption()) { |
|
2182 ASSERT(m_isParsingFragment); |
|
2183 return; |
|
2184 } |
|
2185 processEndTag(token); |
|
2186 return; |
|
2187 } |
|
2188 if (token.name() == bodyTag |
|
2189 || token.name() == colTag |
|
2190 || token.name() == colgroupTag |
|
2191 || token.name() == htmlTag |
|
2192 || isTableBodyContextTag(token.name()) |
|
2193 || isTableCellContextTag(token.name()) |
|
2194 || token.name() == trTag) { |
|
2195 parseError(token); |
|
2196 return; |
|
2197 } |
|
2198 processEndTagForInBody(token); |
|
2199 break; |
|
2200 case InColumnGroupMode: |
|
2201 ASSERT(insertionMode() == InColumnGroupMode); |
|
2202 if (token.name() == colgroupTag) { |
|
2203 processColgroupEndTagForInColumnGroup(); |
|
2204 return; |
|
2205 } |
|
2206 if (token.name() == colTag) { |
|
2207 parseError(token); |
|
2208 return; |
|
2209 } |
|
2210 if (!processColgroupEndTagForInColumnGroup()) { |
|
2211 ASSERT(m_isParsingFragment); |
|
2212 return; |
|
2213 } |
|
2214 processEndTag(token); |
|
2215 break; |
|
2216 case InRowMode: |
|
2217 ASSERT(insertionMode() == InRowMode); |
|
2218 processEndTagForInRow(token); |
|
2219 break; |
|
2220 case InCellMode: |
|
2221 ASSERT(insertionMode() == InCellMode); |
|
2222 processEndTagForInCell(token); |
|
2223 break; |
|
2224 case InTableBodyMode: |
|
2225 ASSERT(insertionMode() == InTableBodyMode); |
|
2226 processEndTagForInTableBody(token); |
|
2227 break; |
|
2228 case AfterBodyMode: |
|
2229 ASSERT(insertionMode() == AfterBodyMode); |
|
2230 if (token.name() == htmlTag) { |
|
2231 if (m_isParsingFragment) { |
|
2232 parseError(token); |
|
2233 return; |
|
2234 } |
|
2235 setInsertionMode(AfterAfterBodyMode); |
|
2236 return; |
|
2237 } |
|
2238 // Fall through. |
|
2239 case AfterAfterBodyMode: |
|
2240 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); |
|
2241 parseError(token); |
|
2242 setInsertionMode(InBodyMode); |
|
2243 processEndTag(token); |
|
2244 break; |
|
2245 case InHeadNoscriptMode: |
|
2246 ASSERT(insertionMode() == InHeadNoscriptMode); |
|
2247 if (token.name() == noscriptTag) { |
|
2248 ASSERT(m_tree.currentElement()->hasTagName(noscriptTag)); |
|
2249 m_tree.openElements()->pop(); |
|
2250 ASSERT(m_tree.currentElement()->hasTagName(headTag)); |
|
2251 setInsertionMode(InHeadMode); |
|
2252 return; |
|
2253 } |
|
2254 if (token.name() != brTag) { |
|
2255 parseError(token); |
|
2256 return; |
|
2257 } |
|
2258 defaultForInHeadNoscript(); |
|
2259 processToken(token); |
|
2260 break; |
|
2261 case TextMode: |
|
2262 if (token.name() == scriptTag) { |
|
2263 // Pause ourselves so that parsing stops until the script can be processed by the caller. |
|
2264 m_isPaused = true; |
|
2265 ASSERT(m_tree.currentElement()->hasTagName(scriptTag)); |
|
2266 m_scriptToProcess = m_tree.currentElement(); |
|
2267 m_scriptToProcessStartLine = m_lastScriptElementStartLine + 1; |
|
2268 m_tree.openElements()->pop(); |
|
2269 setInsertionMode(m_originalInsertionMode); |
|
2270 return; |
|
2271 } |
|
2272 m_tree.openElements()->pop(); |
|
2273 setInsertionMode(m_originalInsertionMode); |
|
2274 break; |
|
2275 case InFramesetMode: |
|
2276 ASSERT(insertionMode() == InFramesetMode); |
|
2277 if (token.name() == framesetTag) { |
|
2278 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { |
|
2279 parseError(token); |
|
2280 return; |
|
2281 } |
|
2282 m_tree.openElements()->pop(); |
|
2283 if (!m_isParsingFragment && !m_tree.currentElement()->hasTagName(framesetTag)) |
|
2284 setInsertionMode(AfterFramesetMode); |
|
2285 return; |
|
2286 } |
|
2287 break; |
|
2288 case AfterFramesetMode: |
|
2289 ASSERT(insertionMode() == AfterFramesetMode); |
|
2290 if (token.name() == htmlTag) { |
|
2291 setInsertionMode(AfterAfterFramesetMode); |
|
2292 return; |
|
2293 } |
|
2294 // Fall through. |
|
2295 case AfterAfterFramesetMode: |
|
2296 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); |
|
2297 parseError(token); |
|
2298 break; |
|
2299 case InSelectInTableMode: |
|
2300 ASSERT(insertionMode() == InSelectInTableMode); |
|
2301 if (token.name() == captionTag |
|
2302 || token.name() == tableTag |
|
2303 || isTableBodyContextTag(token.name()) |
|
2304 || token.name() == trTag |
|
2305 || isTableCellContextTag(token.name())) { |
|
2306 parseError(token); |
|
2307 if (m_tree.openElements()->inTableScope(token.name())) { |
|
2308 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); |
|
2309 processEndTag(endSelect); |
|
2310 processEndTag(token); |
|
2311 } |
|
2312 return; |
|
2313 } |
|
2314 // Fall through. |
|
2315 case InSelectMode: |
|
2316 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); |
|
2317 if (token.name() == optgroupTag) { |
|
2318 if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag)) |
|
2319 processFakeEndTag(optionTag); |
|
2320 if (m_tree.currentElement()->hasTagName(optgroupTag)) { |
|
2321 m_tree.openElements()->pop(); |
|
2322 return; |
|
2323 } |
|
2324 parseError(token); |
|
2325 return; |
|
2326 } |
|
2327 if (token.name() == optionTag) { |
|
2328 if (m_tree.currentElement()->hasTagName(optionTag)) { |
|
2329 m_tree.openElements()->pop(); |
|
2330 return; |
|
2331 } |
|
2332 parseError(token); |
|
2333 return; |
|
2334 } |
|
2335 if (token.name() == selectTag) { |
|
2336 notImplemented(); // fragment case |
|
2337 m_tree.openElements()->popUntilPopped(selectTag.localName()); |
|
2338 resetInsertionModeAppropriately(); |
|
2339 return; |
|
2340 } |
|
2341 break; |
|
2342 case InTableTextMode: |
|
2343 defaultForInTableText(); |
|
2344 processEndTag(token); |
|
2345 break; |
|
2346 case InForeignContentMode: |
|
2347 if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) { |
|
2348 notImplemented(); |
|
2349 return; |
|
2350 } |
|
2351 if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) { |
|
2352 // FIXME: This code just wants an Element* iterator, instead of an ElementRecord* |
|
2353 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); |
|
2354 if (!nodeRecord->element()->hasLocalName(token.name())) { |
|
2355 parseError(token); |
|
2356 // FIXME: This return is not in the spec but it needed for now |
|
2357 // to prevent walking off the bottom of the stack. |
|
2358 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10118 |
|
2359 if (!m_tree.openElements()->contains(token.name())) |
|
2360 return; |
|
2361 } |
|
2362 while (1) { |
|
2363 if (nodeRecord->element()->hasLocalName(token.name())) { |
|
2364 m_tree.openElements()->popUntilPopped(nodeRecord->element()); |
|
2365 return; |
|
2366 } |
|
2367 nodeRecord = nodeRecord->next(); |
|
2368 if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI) { |
|
2369 processUsingSecondaryInsertionModeAndAdjustInsertionMode(token); |
|
2370 // FIXME: This is a hack around a spec bug and is likely wrong. |
|
2371 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=9581 |
|
2372 if (nodeRecord != m_tree.openElements()->topRecord()) |
|
2373 return; |
|
2374 } |
|
2375 } |
|
2376 return; |
|
2377 } |
|
2378 processUsingSecondaryInsertionModeAndAdjustInsertionMode(token); |
|
2379 break; |
|
2380 } |
|
2381 } |
|
2382 |
|
2383 class HTMLTreeBuilder::FakeInsertionMode : public Noncopyable { |
|
2384 public: |
|
2385 FakeInsertionMode(HTMLTreeBuilder* treeBuilder, InsertionMode mode) |
|
2386 : m_treeBuilder(treeBuilder) |
|
2387 , m_originalMode(treeBuilder->insertionMode()) |
|
2388 { |
|
2389 m_treeBuilder->setFakeInsertionMode(mode); |
|
2390 } |
|
2391 |
|
2392 ~FakeInsertionMode() |
|
2393 { |
|
2394 if (m_treeBuilder->isFakeInsertionMode()) |
|
2395 m_treeBuilder->setInsertionMode(m_originalMode); |
|
2396 } |
|
2397 |
|
2398 private: |
|
2399 HTMLTreeBuilder* m_treeBuilder; |
|
2400 InsertionMode m_originalMode; |
|
2401 }; |
|
2402 |
|
2403 // This handles both secondary insertion mode processing, as well as updating |
|
2404 // the insertion mode. These are separate steps in the spec, but always occur |
|
2405 // right after one another. |
|
2406 void HTMLTreeBuilder::processUsingSecondaryInsertionModeAndAdjustInsertionMode(AtomicHTMLToken& token) |
|
2407 { |
|
2408 ASSERT(token.type() == HTMLToken::StartTag || token.type() == HTMLToken::EndTag); |
|
2409 { |
|
2410 FakeInsertionMode fakeMode(this, m_secondaryInsertionMode); |
|
2411 processToken(token); |
|
2412 } |
|
2413 if (insertionMode() == InForeignContentMode && m_tree.openElements()->hasOnlyHTMLElementsInScope()) |
|
2414 setInsertionMode(m_secondaryInsertionMode); |
|
2415 } |
|
2416 |
|
2417 void HTMLTreeBuilder::processComment(AtomicHTMLToken& token) |
|
2418 { |
|
2419 ASSERT(token.type() == HTMLToken::Comment); |
|
2420 if (m_insertionMode == InitialMode |
|
2421 || m_insertionMode == BeforeHTMLMode |
|
2422 || m_insertionMode == AfterAfterBodyMode |
|
2423 || m_insertionMode == AfterAfterFramesetMode) { |
|
2424 m_tree.insertCommentOnDocument(token); |
|
2425 return; |
|
2426 } |
|
2427 if (m_insertionMode == AfterBodyMode) { |
|
2428 m_tree.insertCommentOnHTMLHtmlElement(token); |
|
2429 return; |
|
2430 } |
|
2431 if (m_insertionMode == InTableTextMode) { |
|
2432 defaultForInTableText(); |
|
2433 processComment(token); |
|
2434 return; |
|
2435 } |
|
2436 m_tree.insertComment(token); |
|
2437 } |
|
2438 |
|
2439 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token) |
|
2440 { |
|
2441 ASSERT(token.type() == HTMLToken::Character); |
|
2442 // FIXME: Currently this design has an extra memcpy because we copy the |
|
2443 // characters out of the HTMLTokenizer's buffer into the AtomicHTMLToken |
|
2444 // and then into the text node. What we'd really like is to copy directly |
|
2445 // from the HTMLTokenizer's buffer into the text node. |
|
2446 ExternalCharacterTokenBuffer buffer(token); |
|
2447 processCharacterBuffer(buffer); |
|
2448 } |
|
2449 |
|
2450 void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer) |
|
2451 { |
|
2452 ReprocessBuffer: |
|
2453 switch (insertionMode()) { |
|
2454 case InitialMode: { |
|
2455 ASSERT(insertionMode() == InitialMode); |
|
2456 buffer.skipLeadingWhitespace(); |
|
2457 if (buffer.isEmpty()) |
|
2458 return; |
|
2459 defaultForInitial(); |
|
2460 // Fall through. |
|
2461 } |
|
2462 case BeforeHTMLMode: { |
|
2463 ASSERT(insertionMode() == BeforeHTMLMode); |
|
2464 buffer.skipLeadingWhitespace(); |
|
2465 if (buffer.isEmpty()) |
|
2466 return; |
|
2467 defaultForBeforeHTML(); |
|
2468 // Fall through. |
|
2469 } |
|
2470 case BeforeHeadMode: { |
|
2471 ASSERT(insertionMode() == BeforeHeadMode); |
|
2472 buffer.skipLeadingWhitespace(); |
|
2473 if (buffer.isEmpty()) |
|
2474 return; |
|
2475 defaultForBeforeHead(); |
|
2476 // Fall through. |
|
2477 } |
|
2478 case InHeadMode: { |
|
2479 ASSERT(insertionMode() == InHeadMode); |
|
2480 String leadingWhitespace = buffer.takeLeadingWhitespace(); |
|
2481 if (!leadingWhitespace.isEmpty()) |
|
2482 m_tree.insertTextNode(leadingWhitespace); |
|
2483 if (buffer.isEmpty()) |
|
2484 return; |
|
2485 defaultForInHead(); |
|
2486 // Fall through. |
|
2487 } |
|
2488 case AfterHeadMode: { |
|
2489 ASSERT(insertionMode() == AfterHeadMode); |
|
2490 String leadingWhitespace = buffer.takeLeadingWhitespace(); |
|
2491 if (!leadingWhitespace.isEmpty()) |
|
2492 m_tree.insertTextNode(leadingWhitespace); |
|
2493 if (buffer.isEmpty()) |
|
2494 return; |
|
2495 defaultForAfterHead(); |
|
2496 // Fall through. |
|
2497 } |
|
2498 case InBodyMode: |
|
2499 case InCaptionMode: |
|
2500 case InCellMode: { |
|
2501 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode); |
|
2502 m_tree.reconstructTheActiveFormattingElements(); |
|
2503 String characters = buffer.takeRemaining(); |
|
2504 m_tree.insertTextNode(characters); |
|
2505 if (m_framesetOk && hasNonWhitespace(characters)) |
|
2506 m_framesetOk = false; |
|
2507 break; |
|
2508 } |
|
2509 case InTableMode: |
|
2510 case InTableBodyMode: |
|
2511 case InRowMode: { |
|
2512 ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode); |
|
2513 ASSERT(m_pendingTableCharacters.isEmpty()); |
|
2514 m_originalInsertionMode = m_insertionMode; |
|
2515 setInsertionMode(InTableTextMode); |
|
2516 // Fall through. |
|
2517 } |
|
2518 case InTableTextMode: { |
|
2519 buffer.giveRemainingTo(m_pendingTableCharacters); |
|
2520 break; |
|
2521 } |
|
2522 case InColumnGroupMode: { |
|
2523 ASSERT(insertionMode() == InColumnGroupMode); |
|
2524 String leadingWhitespace = buffer.takeLeadingWhitespace(); |
|
2525 if (!leadingWhitespace.isEmpty()) |
|
2526 m_tree.insertTextNode(leadingWhitespace); |
|
2527 if (buffer.isEmpty()) |
|
2528 return; |
|
2529 if (!processColgroupEndTagForInColumnGroup()) { |
|
2530 ASSERT(m_isParsingFragment); |
|
2531 return; |
|
2532 } |
|
2533 goto ReprocessBuffer; |
|
2534 } |
|
2535 case AfterBodyMode: |
|
2536 case AfterAfterBodyMode: { |
|
2537 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); |
|
2538 // FIXME: parse error |
|
2539 setInsertionMode(InBodyMode); |
|
2540 goto ReprocessBuffer; |
|
2541 break; |
|
2542 } |
|
2543 case TextMode: { |
|
2544 ASSERT(insertionMode() == TextMode); |
|
2545 m_tree.insertTextNode(buffer.takeRemaining()); |
|
2546 break; |
|
2547 } |
|
2548 case InHeadNoscriptMode: { |
|
2549 ASSERT(insertionMode() == InHeadNoscriptMode); |
|
2550 String leadingWhitespace = buffer.takeLeadingWhitespace(); |
|
2551 if (!leadingWhitespace.isEmpty()) |
|
2552 m_tree.insertTextNode(leadingWhitespace); |
|
2553 if (buffer.isEmpty()) |
|
2554 return; |
|
2555 defaultForInHeadNoscript(); |
|
2556 goto ReprocessBuffer; |
|
2557 break; |
|
2558 } |
|
2559 case InFramesetMode: |
|
2560 case AfterFramesetMode: { |
|
2561 ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); |
|
2562 String leadingWhitespace = buffer.takeRemainingWhitespace(); |
|
2563 if (!leadingWhitespace.isEmpty()) |
|
2564 m_tree.insertTextNode(leadingWhitespace); |
|
2565 // FIXME: We should generate a parse error if we skipped over any |
|
2566 // non-whitespace characters. |
|
2567 break; |
|
2568 } |
|
2569 case InSelectInTableMode: |
|
2570 case InSelectMode: { |
|
2571 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); |
|
2572 m_tree.insertTextNode(buffer.takeRemaining()); |
|
2573 break; |
|
2574 } |
|
2575 case InForeignContentMode: { |
|
2576 ASSERT(insertionMode() == InForeignContentMode); |
|
2577 String characters = buffer.takeRemaining(); |
|
2578 m_tree.insertTextNode(characters); |
|
2579 if (m_framesetOk && hasNonWhitespace(characters)) |
|
2580 m_framesetOk = false; |
|
2581 break; |
|
2582 } |
|
2583 case AfterAfterFramesetMode: { |
|
2584 String leadingWhitespace = buffer.takeRemainingWhitespace(); |
|
2585 if (!leadingWhitespace.isEmpty()) { |
|
2586 m_tree.reconstructTheActiveFormattingElements(); |
|
2587 m_tree.insertTextNode(leadingWhitespace); |
|
2588 } |
|
2589 // FIXME: We should generate a parse error if we skipped over any |
|
2590 // non-whitespace characters. |
|
2591 break; |
|
2592 } |
|
2593 } |
|
2594 } |
|
2595 |
|
2596 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) |
|
2597 { |
|
2598 ASSERT(token.type() == HTMLToken::EndOfFile); |
|
2599 switch (insertionMode()) { |
|
2600 case InitialMode: |
|
2601 ASSERT(insertionMode() == InitialMode); |
|
2602 defaultForInitial(); |
|
2603 // Fall through. |
|
2604 case BeforeHTMLMode: |
|
2605 ASSERT(insertionMode() == BeforeHTMLMode); |
|
2606 defaultForBeforeHTML(); |
|
2607 // Fall through. |
|
2608 case BeforeHeadMode: |
|
2609 ASSERT(insertionMode() == BeforeHeadMode); |
|
2610 defaultForBeforeHead(); |
|
2611 // Fall through. |
|
2612 case InHeadMode: |
|
2613 ASSERT(insertionMode() == InHeadMode); |
|
2614 defaultForInHead(); |
|
2615 // Fall through. |
|
2616 case AfterHeadMode: |
|
2617 ASSERT(insertionMode() == AfterHeadMode); |
|
2618 defaultForAfterHead(); |
|
2619 // Fall through |
|
2620 case InBodyMode: |
|
2621 case InCellMode: |
|
2622 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode); |
|
2623 notImplemented(); // Emit parse error based on what elemtns are still open. |
|
2624 break; |
|
2625 case AfterBodyMode: |
|
2626 case AfterAfterBodyMode: |
|
2627 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); |
|
2628 notImplemented(); |
|
2629 break; |
|
2630 case InHeadNoscriptMode: |
|
2631 ASSERT(insertionMode() == InHeadNoscriptMode); |
|
2632 defaultForInHeadNoscript(); |
|
2633 processEndOfFile(token); |
|
2634 return; |
|
2635 case AfterFramesetMode: |
|
2636 case AfterAfterFramesetMode: |
|
2637 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); |
|
2638 break; |
|
2639 case InFramesetMode: |
|
2640 case InTableMode: |
|
2641 case InTableBodyMode: |
|
2642 case InSelectInTableMode: |
|
2643 case InSelectMode: |
|
2644 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode); |
|
2645 if (m_tree.currentElement() != m_tree.openElements()->htmlElement()) |
|
2646 parseError(token); |
|
2647 break; |
|
2648 case InColumnGroupMode: |
|
2649 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { |
|
2650 ASSERT(m_isParsingFragment); |
|
2651 return; |
|
2652 } |
|
2653 if (!processColgroupEndTagForInColumnGroup()) { |
|
2654 ASSERT(m_isParsingFragment); |
|
2655 return; |
|
2656 } |
|
2657 processEndOfFile(token); |
|
2658 return; |
|
2659 case InForeignContentMode: |
|
2660 parseError(token); |
|
2661 // FIXME: Following the spec would infinitely recurse on <svg><svg> |
|
2662 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10115 |
|
2663 m_tree.openElements()->popUntilElementWithNamespace(xhtmlNamespaceURI); |
|
2664 setInsertionMode(m_secondaryInsertionMode); |
|
2665 processEndOfFile(token); |
|
2666 return; |
|
2667 case InTableTextMode: |
|
2668 defaultForInTableText(); |
|
2669 processEndOfFile(token); |
|
2670 return; |
|
2671 case TextMode: |
|
2672 case InCaptionMode: |
|
2673 case InRowMode: |
|
2674 notImplemented(); |
|
2675 break; |
|
2676 } |
|
2677 ASSERT(m_tree.openElements()->top()); |
|
2678 m_tree.openElements()->popAll(); |
|
2679 } |
|
2680 |
|
2681 void HTMLTreeBuilder::defaultForInitial() |
|
2682 { |
|
2683 notImplemented(); |
|
2684 // FIXME: parse error |
|
2685 setInsertionMode(BeforeHTMLMode); |
|
2686 } |
|
2687 |
|
2688 void HTMLTreeBuilder::defaultForBeforeHTML() |
|
2689 { |
|
2690 AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName()); |
|
2691 m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML); |
|
2692 setInsertionMode(BeforeHeadMode); |
|
2693 } |
|
2694 |
|
2695 void HTMLTreeBuilder::defaultForBeforeHead() |
|
2696 { |
|
2697 AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName()); |
|
2698 processStartTag(startHead); |
|
2699 } |
|
2700 |
|
2701 void HTMLTreeBuilder::defaultForInHead() |
|
2702 { |
|
2703 AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName()); |
|
2704 processEndTag(endHead); |
|
2705 } |
|
2706 |
|
2707 void HTMLTreeBuilder::defaultForInHeadNoscript() |
|
2708 { |
|
2709 AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName()); |
|
2710 processEndTag(endNoscript); |
|
2711 } |
|
2712 |
|
2713 void HTMLTreeBuilder::defaultForAfterHead() |
|
2714 { |
|
2715 AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName()); |
|
2716 processStartTag(startBody); |
|
2717 m_framesetOk = true; |
|
2718 } |
|
2719 |
|
2720 void HTMLTreeBuilder::defaultForInTableText() |
|
2721 { |
|
2722 String characters = String::adopt(m_pendingTableCharacters); |
|
2723 if (hasNonWhitespace(characters)) { |
|
2724 // FIXME: parse error |
|
2725 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); |
|
2726 m_tree.reconstructTheActiveFormattingElements(); |
|
2727 m_tree.insertTextNode(characters); |
|
2728 m_framesetOk = false; |
|
2729 setInsertionMode(m_originalInsertionMode); |
|
2730 return; |
|
2731 } |
|
2732 m_tree.insertTextNode(characters); |
|
2733 setInsertionMode(m_originalInsertionMode); |
|
2734 } |
|
2735 |
|
2736 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token) |
|
2737 { |
|
2738 ASSERT(token.type() == HTMLToken::StartTag); |
|
2739 if (token.name() == htmlTag) { |
|
2740 m_tree.insertHTMLHtmlStartTagInBody(token); |
|
2741 return true; |
|
2742 } |
|
2743 // FIXME: Atomize "command". |
|
2744 if (token.name() == baseTag |
|
2745 || token.name() == basefontTag |
|
2746 || token.name() == "bgsound" |
|
2747 || token.name() == "command" |
|
2748 || token.name() == linkTag |
|
2749 || token.name() == metaTag) { |
|
2750 m_tree.insertSelfClosingHTMLElement(token); |
|
2751 // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process(). |
|
2752 return true; |
|
2753 } |
|
2754 if (token.name() == titleTag) { |
|
2755 processGenericRCDATAStartTag(token); |
|
2756 return true; |
|
2757 } |
|
2758 if (token.name() == noscriptTag) { |
|
2759 if (isScriptingFlagEnabled(m_document->frame())) { |
|
2760 processGenericRawTextStartTag(token); |
|
2761 return true; |
|
2762 } |
|
2763 m_tree.insertHTMLElement(token); |
|
2764 setInsertionMode(InHeadNoscriptMode); |
|
2765 return true; |
|
2766 } |
|
2767 if (token.name() == noframesTag || token.name() == styleTag) { |
|
2768 processGenericRawTextStartTag(token); |
|
2769 return true; |
|
2770 } |
|
2771 if (token.name() == scriptTag) { |
|
2772 processScriptStartTag(token); |
|
2773 return true; |
|
2774 } |
|
2775 if (token.name() == headTag) { |
|
2776 parseError(token); |
|
2777 return true; |
|
2778 } |
|
2779 return false; |
|
2780 } |
|
2781 |
|
2782 void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token) |
|
2783 { |
|
2784 ASSERT(token.type() == HTMLToken::StartTag); |
|
2785 m_tree.insertHTMLElement(token); |
|
2786 m_tokenizer->setState(HTMLTokenizer::RCDATAState); |
|
2787 m_originalInsertionMode = m_insertionMode; |
|
2788 setInsertionMode(TextMode); |
|
2789 } |
|
2790 |
|
2791 void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token) |
|
2792 { |
|
2793 ASSERT(token.type() == HTMLToken::StartTag); |
|
2794 m_tree.insertHTMLElement(token); |
|
2795 m_tokenizer->setState(HTMLTokenizer::RAWTEXTState); |
|
2796 m_originalInsertionMode = m_insertionMode; |
|
2797 setInsertionMode(TextMode); |
|
2798 } |
|
2799 |
|
2800 void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token) |
|
2801 { |
|
2802 ASSERT(token.type() == HTMLToken::StartTag); |
|
2803 m_tree.insertScriptElement(token); |
|
2804 m_tokenizer->setState(HTMLTokenizer::ScriptDataState); |
|
2805 m_originalInsertionMode = m_insertionMode; |
|
2806 m_lastScriptElementStartLine = m_tokenizer->lineNumber(); |
|
2807 setInsertionMode(TextMode); |
|
2808 } |
|
2809 |
|
2810 void HTMLTreeBuilder::finished() |
|
2811 { |
|
2812 // We should call m_document->finishedParsing() here, except |
|
2813 // m_legacyTreeBuilder->finished() does it for us. |
|
2814 if (m_legacyTreeBuilder) { |
|
2815 m_legacyTreeBuilder->finished(); |
|
2816 return; |
|
2817 } |
|
2818 |
|
2819 // Warning, this may delete the parser, so don't try to do anything else after this. |
|
2820 if (!m_isParsingFragment) |
|
2821 m_document->finishedParsing(); |
|
2822 } |
|
2823 |
|
2824 bool HTMLTreeBuilder::isScriptingFlagEnabled(Frame* frame) |
|
2825 { |
|
2826 if (!frame) |
|
2827 return false; |
|
2828 if (ScriptController* scriptController = frame->script()) |
|
2829 return scriptController->canExecuteScripts(NotAboutToExecuteScript); |
|
2830 return false; |
|
2831 } |
|
2832 |
|
2833 } |