| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. | 2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. |
| 3 * Copyright (C) 2011 Apple Inc. All rights reserved. | 3 * Copyright (C) 2011 Apple Inc. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 if (task.operation == HTMLConstructionSiteTask::Reparent) | 177 if (task.operation == HTMLConstructionSiteTask::Reparent) |
| 178 return executeReparentTask(task); | 178 return executeReparentTask(task); |
| 179 | 179 |
| 180 if (task.operation == HTMLConstructionSiteTask::TakeAllChildren) | 180 if (task.operation == HTMLConstructionSiteTask::TakeAllChildren) |
| 181 return executeTakeAllChildrenTask(task); | 181 return executeTakeAllChildrenTask(task); |
| 182 | 182 |
| 183 ASSERT_NOT_REACHED(); | 183 ASSERT_NOT_REACHED(); |
| 184 } | 184 } |
| 185 | 185 |
| 186 // This is only needed for TextDocuments where we might have text nodes | 186 // This is only needed for TextDocuments where we might have text nodes |
| 187 // approaching the default length limit (~64k) and we don't want to | 187 // approaching the default length limit (~64k) and we don't want to break a text |
| 188 // break a text node in the middle of a combining character. | 188 // node in the middle of a combining character. |
| 189 static unsigned findBreakIndexBetween(const StringBuilder& string, | 189 static unsigned findBreakIndexBetween(const StringBuilder& string, |
| 190 unsigned currentPosition, | 190 unsigned currentPosition, |
| 191 unsigned proposedBreakIndex) { | 191 unsigned proposedBreakIndex) { |
| 192 ASSERT(currentPosition < proposedBreakIndex); | 192 ASSERT(currentPosition < proposedBreakIndex); |
| 193 ASSERT(proposedBreakIndex <= string.length()); | 193 ASSERT(proposedBreakIndex <= string.length()); |
| 194 // The end of the string is always a valid break. | 194 // The end of the string is always a valid break. |
| 195 if (proposedBreakIndex == string.length()) | 195 if (proposedBreakIndex == string.length()) |
| 196 return proposedBreakIndex; | 196 return proposedBreakIndex; |
| 197 | 197 |
| 198 // Latin-1 does not have breakable boundaries. If we ever moved to a differnet
8-bit encoding this could be wrong. | 198 // Latin-1 does not have breakable boundaries. If we ever moved to a different |
| 199 // 8-bit encoding this could be wrong. |
| 199 if (string.is8Bit()) | 200 if (string.is8Bit()) |
| 200 return proposedBreakIndex; | 201 return proposedBreakIndex; |
| 201 | 202 |
| 202 const UChar* breakSearchCharacters = string.characters16() + currentPosition; | 203 const UChar* breakSearchCharacters = string.characters16() + currentPosition; |
| 203 // We need at least two characters look-ahead to account for UTF-16 surrogates
, but can't search off the end of the buffer! | 204 // We need at least two characters look-ahead to account for UTF-16 |
| 205 // surrogates, but can't search off the end of the buffer! |
| 204 unsigned breakSearchLength = | 206 unsigned breakSearchLength = |
| 205 std::min(proposedBreakIndex - currentPosition + 2, | 207 std::min(proposedBreakIndex - currentPosition + 2, |
| 206 string.length() - currentPosition); | 208 string.length() - currentPosition); |
| 207 NonSharedCharacterBreakIterator it(breakSearchCharacters, breakSearchLength); | 209 NonSharedCharacterBreakIterator it(breakSearchCharacters, breakSearchLength); |
| 208 | 210 |
| 209 if (it.isBreak(proposedBreakIndex - currentPosition)) | 211 if (it.isBreak(proposedBreakIndex - currentPosition)) |
| 210 return proposedBreakIndex; | 212 return proposedBreakIndex; |
| 211 | 213 |
| 212 int adjustedBreakIndexInSubstring = | 214 int adjustedBreakIndexInSubstring = |
| 213 it.preceding(proposedBreakIndex - currentPosition); | 215 it.preceding(proposedBreakIndex - currentPosition); |
| 214 if (adjustedBreakIndexInSubstring > 0) | 216 if (adjustedBreakIndexInSubstring > 0) |
| 215 return currentPosition + adjustedBreakIndexInSubstring; | 217 return currentPosition + adjustedBreakIndexInSubstring; |
| 216 // We failed to find a breakable point, let the caller figure out what to do. | 218 // We failed to find a breakable point, let the caller figure out what to do. |
| 217 return 0; | 219 return 0; |
| 218 } | 220 } |
| 219 | 221 |
| 220 static String atomizeIfAllWhitespace(const String& string, | 222 static String atomizeIfAllWhitespace(const String& string, |
| 221 WhitespaceMode whitespaceMode) { | 223 WhitespaceMode whitespaceMode) { |
| 222 // Strings composed entirely of whitespace are likely to be repeated. | 224 // Strings composed entirely of whitespace are likely to be repeated. Turn |
| 223 // Turn them into AtomicString so we share a single string for each. | 225 // them into AtomicString so we share a single string for each. |
| 224 if (whitespaceMode == AllWhitespace || | 226 if (whitespaceMode == AllWhitespace || |
| 225 (whitespaceMode == WhitespaceUnknown && isAllWhitespace(string))) | 227 (whitespaceMode == WhitespaceUnknown && isAllWhitespace(string))) |
| 226 return AtomicString(string).getString(); | 228 return AtomicString(string).getString(); |
| 227 return string; | 229 return string; |
| 228 } | 230 } |
| 229 | 231 |
| 230 void HTMLConstructionSite::flushPendingText(FlushMode mode) { | 232 void HTMLConstructionSite::flushPendingText(FlushMode mode) { |
| 231 if (m_pendingText.isEmpty()) | 233 if (m_pendingText.isEmpty()) |
| 232 return; | 234 return; |
| 233 | 235 |
| 234 if (mode == FlushIfAtTextLimit && | 236 if (mode == FlushIfAtTextLimit && |
| 235 !shouldUseLengthLimit(*m_pendingText.parent)) | 237 !shouldUseLengthLimit(*m_pendingText.parent)) |
| 236 return; | 238 return; |
| 237 | 239 |
| 238 PendingText pendingText; | 240 PendingText pendingText; |
| 239 // Hold onto the current pending text on the stack so that queueTask doesn't r
ecurse infinitely. | 241 // Hold onto the current pending text on the stack so that queueTask doesn't |
| 242 // recurse infinitely. |
| 240 m_pendingText.swap(pendingText); | 243 m_pendingText.swap(pendingText); |
| 241 ASSERT(m_pendingText.isEmpty()); | 244 ASSERT(m_pendingText.isEmpty()); |
| 242 | 245 |
| 243 // Splitting text nodes into smaller chunks contradicts HTML5 spec, but is nec
essary | 246 // Splitting text nodes into smaller chunks contradicts HTML5 spec, but is |
| 244 // for performance, see: https://bugs.webkit.org/show_bug.cgi?id=55898 | 247 // necessary for performance, see: |
| 248 // https://bugs.webkit.org/show_bug.cgi?id=55898 |
| 245 unsigned lengthLimit = textLengthLimitForContainer(*pendingText.parent); | 249 unsigned lengthLimit = textLengthLimitForContainer(*pendingText.parent); |
| 246 | 250 |
| 247 unsigned currentPosition = 0; | 251 unsigned currentPosition = 0; |
| 248 const StringBuilder& string = pendingText.stringBuilder; | 252 const StringBuilder& string = pendingText.stringBuilder; |
| 249 while (currentPosition < string.length()) { | 253 while (currentPosition < string.length()) { |
| 250 unsigned proposedBreakIndex = | 254 unsigned proposedBreakIndex = |
| 251 std::min(currentPosition + lengthLimit, string.length()); | 255 std::min(currentPosition + lengthLimit, string.length()); |
| 252 unsigned breakIndex = | 256 unsigned breakIndex = |
| 253 findBreakIndexBetween(string, currentPosition, proposedBreakIndex); | 257 findBreakIndexBetween(string, currentPosition, proposedBreakIndex); |
| 254 ASSERT(breakIndex <= string.length()); | 258 ASSERT(breakIndex <= string.length()); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 287 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert); | 291 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert); |
| 288 task.parent = parent; | 292 task.parent = parent; |
| 289 task.child = child; | 293 task.child = child; |
| 290 task.selfClosing = selfClosing; | 294 task.selfClosing = selfClosing; |
| 291 | 295 |
| 292 if (shouldFosterParent()) { | 296 if (shouldFosterParent()) { |
| 293 fosterParent(task.child); | 297 fosterParent(task.child); |
| 294 return; | 298 return; |
| 295 } | 299 } |
| 296 | 300 |
| 297 // Add as a sibling of the parent if we have reached the maximum depth allowed
. | 301 // Add as a sibling of the parent if we have reached the maximum depth |
| 302 // allowed. |
| 298 if (m_openElements.stackDepth() > maximumHTMLParserDOMTreeDepth && | 303 if (m_openElements.stackDepth() > maximumHTMLParserDOMTreeDepth && |
| 299 task.parent->parentNode()) | 304 task.parent->parentNode()) |
| 300 task.parent = task.parent->parentNode(); | 305 task.parent = task.parent->parentNode(); |
| 301 | 306 |
| 302 ASSERT(task.parent); | 307 ASSERT(task.parent); |
| 303 queueTask(task); | 308 queueTask(task); |
| 304 } | 309 } |
| 305 | 310 |
| 306 void HTMLConstructionSite::executeQueuedTasks() { | 311 void HTMLConstructionSite::executeQueuedTasks() { |
| 307 // This has no affect on pendingText, and we may have pendingText | 312 // This has no affect on pendingText, and we may have pendingText remaining |
| 308 // remaining after executing all other queued tasks. | 313 // after executing all other queued tasks. |
| 309 const size_t size = m_taskQueue.size(); | 314 const size_t size = m_taskQueue.size(); |
| 310 if (!size) | 315 if (!size) |
| 311 return; | 316 return; |
| 312 | 317 |
| 313 // Copy the task queue into a local variable in case executeTask | 318 // Copy the task queue into a local variable in case executeTask re-enters the |
| 314 // re-enters the parser. | 319 // parser. |
| 315 TaskQueue queue; | 320 TaskQueue queue; |
| 316 queue.swap(m_taskQueue); | 321 queue.swap(m_taskQueue); |
| 317 | 322 |
| 318 for (size_t i = 0; i < size; ++i) | 323 for (size_t i = 0; i < size; ++i) |
| 319 executeTask(queue[i]); | 324 executeTask(queue[i]); |
| 320 | 325 |
| 321 // We might be detached now. | 326 // We might be detached now. |
| 322 } | 327 } |
| 323 | 328 |
| 324 HTMLConstructionSite::HTMLConstructionSite( | 329 HTMLConstructionSite::HTMLConstructionSite( |
| (...skipping 19 matching lines...) Expand all Loading... |
| 344 DCHECK(!m_form); | 349 DCHECK(!m_form); |
| 345 | 350 |
| 346 m_attachmentRoot = fragment; | 351 m_attachmentRoot = fragment; |
| 347 m_isParsingFragment = true; | 352 m_isParsingFragment = true; |
| 348 | 353 |
| 349 if (!contextElement->document().isTemplateDocument()) | 354 if (!contextElement->document().isTemplateDocument()) |
| 350 m_form = Traversal<HTMLFormElement>::firstAncestorOrSelf(*contextElement); | 355 m_form = Traversal<HTMLFormElement>::firstAncestorOrSelf(*contextElement); |
| 351 } | 356 } |
| 352 | 357 |
| 353 HTMLConstructionSite::~HTMLConstructionSite() { | 358 HTMLConstructionSite::~HTMLConstructionSite() { |
| 354 // Depending on why we're being destroyed it might be OK | 359 // Depending on why we're being destroyed it might be OK to forget queued |
| 355 // to forget queued tasks, but currently we don't expect to. | 360 // tasks, but currently we don't expect to. |
| 356 ASSERT(m_taskQueue.isEmpty()); | 361 ASSERT(m_taskQueue.isEmpty()); |
| 357 // Currently we assume that text will never be the last token in the | 362 // Currently we assume that text will never be the last token in the document |
| 358 // document and that we'll always queue some additional task to cause it to fl
ush. | 363 // and that we'll always queue some additional task to cause it to flush. |
| 359 ASSERT(m_pendingText.isEmpty()); | 364 ASSERT(m_pendingText.isEmpty()); |
| 360 } | 365 } |
| 361 | 366 |
| 362 DEFINE_TRACE(HTMLConstructionSite) { | 367 DEFINE_TRACE(HTMLConstructionSite) { |
| 363 visitor->trace(m_document); | 368 visitor->trace(m_document); |
| 364 visitor->trace(m_attachmentRoot); | 369 visitor->trace(m_attachmentRoot); |
| 365 visitor->trace(m_head); | 370 visitor->trace(m_head); |
| 366 visitor->trace(m_form); | 371 visitor->trace(m_form); |
| 367 visitor->trace(m_openElements); | 372 visitor->trace(m_openElements); |
| 368 visitor->trace(m_activeFormattingElements); | 373 visitor->trace(m_activeFormattingElements); |
| 369 visitor->trace(m_taskQueue); | 374 visitor->trace(m_taskQueue); |
| 370 visitor->trace(m_pendingText); | 375 visitor->trace(m_pendingText); |
| 371 } | 376 } |
| 372 | 377 |
| 373 void HTMLConstructionSite::detach() { | 378 void HTMLConstructionSite::detach() { |
| 374 // FIXME: We'd like to ASSERT here that we're canceling and not just discardin
g | 379 // FIXME: We'd like to ASSERT here that we're canceling and not just |
| 375 // text that really should have made it into the DOM earlier, but there | 380 // discarding text that really should have made it into the DOM earlier, but |
| 376 // doesn't seem to be a nice way to do that. | 381 // there doesn't seem to be a nice way to do that. |
| 377 m_pendingText.discard(); | 382 m_pendingText.discard(); |
| 378 m_document = nullptr; | 383 m_document = nullptr; |
| 379 m_attachmentRoot = nullptr; | 384 m_attachmentRoot = nullptr; |
| 380 } | 385 } |
| 381 | 386 |
| 382 HTMLFormElement* HTMLConstructionSite::takeForm() { | 387 HTMLFormElement* HTMLConstructionSite::takeForm() { |
| 383 return m_form.release(); | 388 return m_form.release(); |
| 384 } | 389 } |
| 385 | 390 |
| 386 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML( | 391 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML( |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 434 Document::CompatibilityMode mode) { | 439 Document::CompatibilityMode mode) { |
| 435 m_inQuirksMode = (mode == Document::QuirksMode); | 440 m_inQuirksMode = (mode == Document::QuirksMode); |
| 436 m_document->setCompatibilityMode(mode); | 441 m_document->setCompatibilityMode(mode); |
| 437 } | 442 } |
| 438 | 443 |
| 439 void HTMLConstructionSite::setCompatibilityModeFromDoctype( | 444 void HTMLConstructionSite::setCompatibilityModeFromDoctype( |
| 440 const String& name, | 445 const String& name, |
| 441 const String& publicId, | 446 const String& publicId, |
| 442 const String& systemId) { | 447 const String& systemId) { |
| 443 // There are three possible compatibility modes: | 448 // There are three possible compatibility modes: |
| 444 // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in
this mode, e.g., unit types can | 449 // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in |
| 445 // be omitted from numbers. | 450 // this mode, e.g., unit types can be omitted from numbers. |
| 446 // Limited Quirks - This mode is identical to no-quirks mode except for its tr
eatment of line-height in the inline box model. | 451 // Limited Quirks - This mode is identical to no-quirks mode except for its |
| 447 // No Quirks - no quirks apply. Web pages will obey the specifications to the
letter. | 452 // treatment of line-height in the inline box model. |
| 453 // No Quirks - no quirks apply. Web pages will obey the specifications to the |
| 454 // letter. |
| 448 | 455 |
| 449 // Check for Quirks Mode. | 456 // Check for Quirks Mode. |
| 450 if (name != "html" || | 457 if (name != "html" || |
| 451 publicId.startsWith("+//Silmaril//dtd html Pro v0r11 19970101//", | 458 publicId.startsWith("+//Silmaril//dtd html Pro v0r11 19970101//", |
| 452 TextCaseInsensitive) || | 459 TextCaseInsensitive) || |
| 453 publicId.startsWith( | 460 publicId.startsWith( |
| 454 "-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", | 461 "-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", |
| 455 TextCaseInsensitive) || | 462 TextCaseInsensitive) || |
| 456 publicId.startsWith("-//AS//DTD HTML 3.0 asWedit + extensions//", | 463 publicId.startsWith("-//AS//DTD HTML 3.0 asWedit + extensions//", |
| 457 TextCaseInsensitive) || | 464 TextCaseInsensitive) || |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 586 setCompatibilityMode(Document::NoQuirksMode); | 593 setCompatibilityMode(Document::NoQuirksMode); |
| 587 } | 594 } |
| 588 | 595 |
| 589 void HTMLConstructionSite::processEndOfFile() { | 596 void HTMLConstructionSite::processEndOfFile() { |
| 590 ASSERT(currentNode()); | 597 ASSERT(currentNode()); |
| 591 flush(FlushAlways); | 598 flush(FlushAlways); |
| 592 openElements()->popAll(); | 599 openElements()->popAll(); |
| 593 } | 600 } |
| 594 | 601 |
| 595 void HTMLConstructionSite::finishedParsing() { | 602 void HTMLConstructionSite::finishedParsing() { |
| 596 // We shouldn't have any queued tasks but we might have pending text which we
need to promote to tasks and execute. | 603 // We shouldn't have any queued tasks but we might have pending text which we |
| 604 // need to promote to tasks and execute. |
| 597 ASSERT(m_taskQueue.isEmpty()); | 605 ASSERT(m_taskQueue.isEmpty()); |
| 598 flush(FlushAlways); | 606 flush(FlushAlways); |
| 599 m_document->finishedParsing(); | 607 m_document->finishedParsing(); |
| 600 } | 608 } |
| 601 | 609 |
| 602 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token) { | 610 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token) { |
| 603 ASSERT(token->type() == HTMLToken::DOCTYPE); | 611 ASSERT(token->type() == HTMLToken::DOCTYPE); |
| 604 | 612 |
| 605 const String& publicId = | 613 const String& publicId = |
| 606 StringImpl::create8BitIfPossible(token->publicIdentifier()); | 614 StringImpl::create8BitIfPossible(token->publicIdentifier()); |
| 607 const String& systemId = | 615 const String& systemId = |
| 608 StringImpl::create8BitIfPossible(token->systemIdentifier()); | 616 StringImpl::create8BitIfPossible(token->systemIdentifier()); |
| 609 DocumentType* doctype = | 617 DocumentType* doctype = |
| 610 DocumentType::create(m_document, token->name(), publicId, systemId); | 618 DocumentType::create(m_document, token->name(), publicId, systemId); |
| 611 attachLater(m_attachmentRoot, doctype); | 619 attachLater(m_attachmentRoot, doctype); |
| 612 | 620 |
| 613 // DOCTYPE nodes are only processed when parsing fragments w/o contextElements
, which | 621 // DOCTYPE nodes are only processed when parsing fragments w/o |
| 614 // never occurs. However, if we ever chose to support such, this code is subt
ly wrong, | 622 // contextElements, which never occurs. However, if we ever chose to support |
| 615 // because context-less fragments can determine their own quirks mode, and thu
s change | 623 // such, this code is subtly wrong, because context-less fragments can |
| 616 // parsing rules (like <p> inside <table>). For now we ASSERT that we never h
it this code | 624 // determine their own quirks mode, and thus change parsing rules (like <p> |
| 617 // in a fragment, as changing the owning document's compatibility mode would b
e wrong. | 625 // inside <table>). For now we ASSERT that we never hit this code in a |
| 626 // fragment, as changing the owning document's compatibility mode would be |
| 627 // wrong. |
| 618 ASSERT(!m_isParsingFragment); | 628 ASSERT(!m_isParsingFragment); |
| 619 if (m_isParsingFragment) | 629 if (m_isParsingFragment) |
| 620 return; | 630 return; |
| 621 | 631 |
| 622 if (token->forceQuirks()) | 632 if (token->forceQuirks()) |
| 623 setCompatibilityMode(Document::QuirksMode); | 633 setCompatibilityMode(Document::QuirksMode); |
| 624 else { | 634 else { |
| 625 setCompatibilityModeFromDoctype(token->name(), publicId, systemId); | 635 setCompatibilityModeFromDoctype(token->name(), publicId, systemId); |
| 626 } | 636 } |
| 627 } | 637 } |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 694 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#th
e-stack-of-open-elements | 704 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#th
e-stack-of-open-elements |
| 695 // Possible active formatting elements include: | 705 // Possible active formatting elements include: |
| 696 // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u. | 706 // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u. |
| 697 insertHTMLElement(token); | 707 insertHTMLElement(token); |
| 698 m_activeFormattingElements.append(currentElementRecord()->stackItem()); | 708 m_activeFormattingElements.append(currentElementRecord()->stackItem()); |
| 699 } | 709 } |
| 700 | 710 |
| 701 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken* token) { | 711 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken* token) { |
| 702 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.htm
l#already-started | 712 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.htm
l#already-started |
| 703 // http://html5.org/specs/dom-parsing.html#dom-range-createcontextualfragment | 713 // http://html5.org/specs/dom-parsing.html#dom-range-createcontextualfragment |
| 704 // For createContextualFragment, the specifications say to mark it parser-inse
rted and already-started and later unmark them. | 714 // For createContextualFragment, the specifications say to mark it |
| 705 // However, we short circuit that logic to avoid the subtree traversal to find
script elements since scripts can never see | 715 // parser-inserted and already-started and later unmark them. However, we |
| 706 // those flags or effects thereof. | 716 // short circuit that logic to avoid the subtree traversal to find script |
| 717 // elements since scripts can never see those flags or effects thereof. |
| 707 const bool parserInserted = | 718 const bool parserInserted = |
| 708 m_parserContentPolicy != AllowScriptingContentAndDoNotMarkAlreadyStarted; | 719 m_parserContentPolicy != AllowScriptingContentAndDoNotMarkAlreadyStarted; |
| 709 const bool alreadyStarted = m_isParsingFragment && parserInserted; | 720 const bool alreadyStarted = m_isParsingFragment && parserInserted; |
| 710 // TODO(csharrison): This logic only works if the tokenizer/parser was not | 721 // TODO(csharrison): This logic only works if the tokenizer/parser was not |
| 711 // blocked waiting for scripts when the element was inserted. This usually | 722 // blocked waiting for scripts when the element was inserted. This usually |
| 712 // fails for instance, on second document.write if a script writes twice in | 723 // fails for instance, on second document.write if a script writes twice in a |
| 713 // a row. To fix this, the parser might have to keep track of raw string | 724 // row. To fix this, the parser might have to keep track of raw string |
| 714 // position. | 725 // position. |
| 715 // TODO(csharrison): Refactor this so that the bools that are passed in are | 726 // TODO(csharrison): Refactor this so that the bools that are passed |
| 716 // packed in a bitfield from an enum class. | 727 // in are packed in a bitfield from an enum class. |
| 717 const bool createdDuringDocumentWrite = | 728 const bool createdDuringDocumentWrite = |
| 718 ownerDocumentForCurrentNode().isInDocumentWrite(); | 729 ownerDocumentForCurrentNode().isInDocumentWrite(); |
| 719 HTMLScriptElement* element = | 730 HTMLScriptElement* element = |
| 720 HTMLScriptElement::create(ownerDocumentForCurrentNode(), parserInserted, | 731 HTMLScriptElement::create(ownerDocumentForCurrentNode(), parserInserted, |
| 721 alreadyStarted, createdDuringDocumentWrite); | 732 alreadyStarted, createdDuringDocumentWrite); |
| 722 setAttributes(element, token, m_parserContentPolicy); | 733 setAttributes(element, token, m_parserContentPolicy); |
| 723 if (scriptingContentIsAllowed(m_parserContentPolicy)) | 734 if (scriptingContentIsAllowed(m_parserContentPolicy)) |
| 724 attachLater(currentNode(), element); | 735 attachLater(currentNode(), element); |
| 725 m_openElements.push(HTMLStackItem::create(element, token)); | 736 m_openElements.push(HTMLStackItem::create(element, token)); |
| 726 } | 737 } |
| 727 | 738 |
| 728 void HTMLConstructionSite::insertForeignElement( | 739 void HTMLConstructionSite::insertForeignElement( |
| 729 AtomicHTMLToken* token, | 740 AtomicHTMLToken* token, |
| 730 const AtomicString& namespaceURI) { | 741 const AtomicString& namespaceURI) { |
| 731 ASSERT(token->type() == HTMLToken::StartTag); | 742 ASSERT(token->type() == HTMLToken::StartTag); |
| 732 DVLOG(1) | 743 // parseError when xmlns or xmlns:xlink are wrong. |
| 733 << "Not implemented."; // parseError when xmlns or xmlns:xlink are wrong. | 744 DVLOG(1) << "Not implemented."; |
| 734 | 745 |
| 735 Element* element = createElement(token, namespaceURI); | 746 Element* element = createElement(token, namespaceURI); |
| 736 if (scriptingContentIsAllowed(m_parserContentPolicy) || | 747 if (scriptingContentIsAllowed(m_parserContentPolicy) || |
| 737 !toScriptLoaderIfPossible(element)) | 748 !toScriptLoaderIfPossible(element)) |
| 738 attachLater(currentNode(), element, token->selfClosing()); | 749 attachLater(currentNode(), element, token->selfClosing()); |
| 739 if (!token->selfClosing()) | 750 if (!token->selfClosing()) |
| 740 m_openElements.push(HTMLStackItem::create(element, token, namespaceURI)); | 751 m_openElements.push(HTMLStackItem::create(element, token, namespaceURI)); |
| 741 } | 752 } |
| 742 | 753 |
| 743 void HTMLConstructionSite::insertTextNode(const String& string, | 754 void HTMLConstructionSite::insertTextNode(const String& string, |
| 744 WhitespaceMode whitespaceMode) { | 755 WhitespaceMode whitespaceMode) { |
| 745 HTMLConstructionSiteTask dummyTask(HTMLConstructionSiteTask::Insert); | 756 HTMLConstructionSiteTask dummyTask(HTMLConstructionSiteTask::Insert); |
| 746 dummyTask.parent = currentNode(); | 757 dummyTask.parent = currentNode(); |
| 747 | 758 |
| 748 if (shouldFosterParent()) | 759 if (shouldFosterParent()) |
| 749 findFosterSite(dummyTask); | 760 findFosterSite(dummyTask); |
| 750 | 761 |
| 751 // FIXME: This probably doesn't need to be done both here and in insert(Task). | 762 // FIXME: This probably doesn't need to be done both here and in insert(Task). |
| 752 if (isHTMLTemplateElement(*dummyTask.parent)) | 763 if (isHTMLTemplateElement(*dummyTask.parent)) |
| 753 dummyTask.parent = toHTMLTemplateElement(dummyTask.parent.get())->content(); | 764 dummyTask.parent = toHTMLTemplateElement(dummyTask.parent.get())->content(); |
| 754 | 765 |
| 755 // Unclear when parent != case occurs. Somehow we insert text into two separat
e nodes while processing the same Token. | 766 // Unclear when parent != case occurs. Somehow we insert text into two |
| 756 // The nextChild != dummy.nextChild case occurs whenever foster parenting happ
ened and we hit a new text node "<table>a</table>b" | 767 // separate nodes while processing the same Token. The nextChild != |
| 757 // In either case we have to flush the pending text into the task queue before
making more. | 768 // dummy.nextChild case occurs whenever foster parenting happened and we hit a |
| 769 // new text node "<table>a</table>b" In either case we have to flush the |
| 770 // pending text into the task queue before making more. |
| 758 if (!m_pendingText.isEmpty() && | 771 if (!m_pendingText.isEmpty() && |
| 759 (m_pendingText.parent != dummyTask.parent || | 772 (m_pendingText.parent != dummyTask.parent || |
| 760 m_pendingText.nextChild != dummyTask.nextChild)) | 773 m_pendingText.nextChild != dummyTask.nextChild)) |
| 761 flushPendingText(FlushAlways); | 774 flushPendingText(FlushAlways); |
| 762 m_pendingText.append(dummyTask.parent, dummyTask.nextChild, string, | 775 m_pendingText.append(dummyTask.parent, dummyTask.nextChild, string, |
| 763 whitespaceMode); | 776 whitespaceMode); |
| 764 } | 777 } |
| 765 | 778 |
| 766 void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord* newParent, | 779 void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord* newParent, |
| 767 HTMLElementStack::ElementRecord* child) { | 780 HTMLElementStack::ElementRecord* child) { |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 842 const Attribute* isAttribute = token->getAttributeItem(HTMLNames::isAttr); | 855 const Attribute* isAttribute = token->getAttributeItem(HTMLNames::isAttr); |
| 843 const AtomicString& name = isAttribute ? isAttribute->value() : localName; | 856 const AtomicString& name = isAttribute ? isAttribute->value() : localName; |
| 844 CustomElementDescriptor descriptor(name, localName); | 857 CustomElementDescriptor descriptor(name, localName); |
| 845 | 858 |
| 846 // 4.-6. | 859 // 4.-6. |
| 847 return registry->definitionFor(descriptor); | 860 return registry->definitionFor(descriptor); |
| 848 } | 861 } |
| 849 | 862 |
| 850 // "create an element for a token" | 863 // "create an element for a token" |
| 851 // https://html.spec.whatwg.org/#create-an-element-for-the-token | 864 // https://html.spec.whatwg.org/#create-an-element-for-the-token |
| 852 // TODO(dominicc): When form association is separate from creation, | 865 // TODO(dominicc): When form association is separate from creation, unify this |
| 853 // unify this with foreign element creation. Add a namespace parameter | 866 // with foreign element creation. Add a namespace parameter and check for HTML |
| 854 // and check for HTML namespace to lookupCustomElementDefinition. | 867 // namespace to lookupCustomElementDefinition. |
| 855 HTMLElement* HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token) { | 868 HTMLElement* HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token) { |
| 856 // "1. Let document be intended parent's node document." | 869 // "1. Let document be intended parent's node document." |
| 857 Document& document = ownerDocumentForCurrentNode(); | 870 Document& document = ownerDocumentForCurrentNode(); |
| 858 | 871 |
| 859 // Only associate the element with the current form if we're creating the new
element | 872 // Only associate the element with the current form if we're creating the new |
| 860 // in a document with a browsing context (rather than in <template> contents). | 873 // element in a document with a browsing context (rather than in <template> |
| 874 // contents). |
| 861 // TODO(dominicc): Change form to happen after element creation when | 875 // TODO(dominicc): Change form to happen after element creation when |
| 862 // implementing customized built-in elements. | 876 // implementing customized built-in elements. |
| 863 HTMLFormElement* form = document.frame() ? m_form.get() : nullptr; | 877 HTMLFormElement* form = document.frame() ? m_form.get() : nullptr; |
| 864 | 878 |
| 865 // "2. Let local name be the tag name of the token." | 879 // "2. Let local name be the tag name of the token." |
| 866 // "3. Let is be the value of the "is" attribute in the giev token ..." etc. | 880 // "3. Let is be the value of the "is" attribute in the giev token ..." etc. |
| 867 // "4. Let definition be the result of looking up a custom element ..." etc. | 881 // "4. Let definition be the result of looking up a custom element ..." etc. |
| 868 CustomElementDefinition* definition = | 882 CustomElementDefinition* definition = |
| 869 m_isParsingFragment ? nullptr | 883 m_isParsingFragment ? nullptr |
| 870 : lookUpCustomElementDefinition(document, token); | 884 : lookUpCustomElementDefinition(document, token); |
| 871 // "5. If definition is non-null and the parser was not originally created | 885 // "5. If definition is non-null and the parser was not originally created |
| 872 // for the HTML fragment parsing algorithm, then let will execute script | 886 // for the HTML fragment parsing algorithm, then let will execute script |
| 873 // be true." | 887 // be true." |
| 874 bool willExecuteScript = definition && !m_isParsingFragment; | 888 bool willExecuteScript = definition && !m_isParsingFragment; |
| 875 | 889 |
| 876 HTMLElement* element; | 890 HTMLElement* element; |
| 877 | 891 |
| 878 if (willExecuteScript) { | 892 if (willExecuteScript) { |
| 879 // "6.1 Increment the document's throw-on-dynamic-insertion counter." | 893 // "6.1 Increment the document's throw-on-dynamic-insertion counter." |
| 880 ThrowOnDynamicMarkupInsertionCountIncrementer | 894 ThrowOnDynamicMarkupInsertionCountIncrementer |
| 881 throwOnDynamicMarkupInsertions(&document); | 895 throwOnDynamicMarkupInsertions(&document); |
| 882 | 896 |
| 883 // "6.2 If the JavaScript execution context stack is empty, | 897 // "6.2 If the JavaScript execution context stack is empty, |
| 884 // then perform a microtask checkpoint." | 898 // then perform a microtask checkpoint." |
| 885 | 899 |
| 886 // TODO(dominicc): This is the way the Blink HTML parser | 900 // TODO(dominicc): This is the way the Blink HTML parser performs |
| 887 // performs checkpoints, but note the spec is different--it | 901 // checkpoints, but note the spec is different--it talks about the |
| 888 // talks about the JavaScript stack, not the script nesting | 902 // JavaScript stack, not the script nesting level. |
| 889 // level. | |
| 890 if (0u == m_reentryPermit->scriptNestingLevel()) | 903 if (0u == m_reentryPermit->scriptNestingLevel()) |
| 891 Microtask::performCheckpoint(V8PerIsolateData::mainThreadIsolate()); | 904 Microtask::performCheckpoint(V8PerIsolateData::mainThreadIsolate()); |
| 892 | 905 |
| 893 // "6.3 Push a new element queue onto the custom element | 906 // "6.3 Push a new element queue onto the custom element |
| 894 // reactions stack." | 907 // reactions stack." |
| 895 CEReactionsScope reactions; | 908 CEReactionsScope reactions; |
| 896 | 909 |
| 897 // 7. | 910 // 7. |
| 898 QualifiedName elementQName(nullAtom, token->name(), | 911 QualifiedName elementQName(nullAtom, token->name(), |
| 899 HTMLNames::xhtmlNamespaceURI); | 912 HTMLNames::xhtmlNamespaceURI); |
| 900 element = definition->createElementSync(document, elementQName); | 913 element = definition->createElementSync(document, elementQName); |
| 901 | 914 |
| 902 // "8. Append each attribute in the given token to element." | 915 // "8. Append each attribute in the given token to element." We don't use |
| 903 // We don't use setAttributes here because the custom element | 916 // setAttributes here because the custom element constructor may have |
| 904 // constructor may have manipulated attributes. | 917 // manipulated attributes. |
| 905 for (const auto& attribute : token->attributes()) | 918 for (const auto& attribute : token->attributes()) |
| 906 element->setAttribute(attribute.name(), attribute.value()); | 919 element->setAttribute(attribute.name(), attribute.value()); |
| 907 | 920 |
| 908 // "9. If will execute script is true, then ..." etc. The | 921 // "9. If will execute script is true, then ..." etc. The CEReactionsScope |
| 909 // CEReactionsScope and ThrowOnDynamicMarkupInsertionCountIncrementer | 922 // and ThrowOnDynamicMarkupInsertionCountIncrementer destructors implement |
| 910 // destructors implement steps 9.1-3. | 923 // steps 9.1-3. |
| 911 } else { | 924 } else { |
| 912 // FIXME: This can't use | 925 // FIXME: This can't use HTMLConstructionSite::createElement because we have |
| 913 // HTMLConstructionSite::createElement because we have to | 926 // to pass the current form element. We should rework form association to |
| 914 // pass the current form element. We should rework form | 927 // occur after construction to allow better code sharing here. |
| 915 // association to occur after construction to allow better | |
| 916 // code sharing here. | |
| 917 element = HTMLElementFactory::createHTMLElement( | 928 element = HTMLElementFactory::createHTMLElement( |
| 918 token->name(), document, form, getCreateElementFlags()); | 929 token->name(), document, form, getCreateElementFlags()); |
| 919 // Definition for the created element does not exist here and | 930 // Definition for the created element does not exist here and it cannot be |
| 920 // it cannot be custom or failed. | 931 // custom or failed. |
| 921 DCHECK_NE(element->getCustomElementState(), CustomElementState::Custom); | 932 DCHECK_NE(element->getCustomElementState(), CustomElementState::Custom); |
| 922 DCHECK_NE(element->getCustomElementState(), CustomElementState::Failed); | 933 DCHECK_NE(element->getCustomElementState(), CustomElementState::Failed); |
| 923 | 934 |
| 924 // "8. Append each attribute in the given token to element." | 935 // "8. Append each attribute in the given token to element." |
| 925 setAttributes(element, token, m_parserContentPolicy); | 936 setAttributes(element, token, m_parserContentPolicy); |
| 926 } | 937 } |
| 927 | 938 |
| 928 // TODO(dominicc): Implement steps 10-12 when customized built-in | 939 // TODO(dominicc): Implement steps 10-12 when customized built-in elements are |
| 929 // elements are implemented. | 940 // implemented. |
| 930 | 941 |
| 931 return element; | 942 return element; |
| 932 } | 943 } |
| 933 | 944 |
| 934 HTMLStackItem* HTMLConstructionSite::createElementFromSavedToken( | 945 HTMLStackItem* HTMLConstructionSite::createElementFromSavedToken( |
| 935 HTMLStackItem* item) { | 946 HTMLStackItem* item) { |
| 936 Element* element; | 947 Element* element; |
| 937 // NOTE: Moving from item -> token -> item copies the Attribute vector twice! | 948 // NOTE: Moving from item -> token -> item copies the Attribute vector twice! |
| 938 AtomicHTMLToken fakeToken(HTMLToken::StartTag, item->localName(), | 949 AtomicHTMLToken fakeToken(HTMLToken::StartTag, item->localName(), |
| 939 item->attributes()); | 950 item->attributes()); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 990 | 1001 |
| 991 void HTMLConstructionSite::generateImpliedEndTags() { | 1002 void HTMLConstructionSite::generateImpliedEndTags() { |
| 992 while (hasImpliedEndTag(currentStackItem())) | 1003 while (hasImpliedEndTag(currentStackItem())) |
| 993 m_openElements.pop(); | 1004 m_openElements.pop(); |
| 994 } | 1005 } |
| 995 | 1006 |
| 996 bool HTMLConstructionSite::inQuirksMode() { | 1007 bool HTMLConstructionSite::inQuirksMode() { |
| 997 return m_inQuirksMode; | 1008 return m_inQuirksMode; |
| 998 } | 1009 } |
| 999 | 1010 |
| 1000 // Adjusts |task| to match the "adjusted insertion location" determined by the f
oster parenting algorithm, | 1011 // Adjusts |task| to match the "adjusted insertion location" determined by the |
| 1001 // laid out as the substeps of step 2 of https://html.spec.whatwg.org/#appropria
te-place-for-inserting-a-node | 1012 // foster parenting algorithm, laid out as the substeps of step 2 of |
| 1013 // https://html.spec.whatwg.org/#appropriate-place-for-inserting-a-node |
| 1002 void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task) { | 1014 void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task) { |
| 1003 // 2.1 | 1015 // 2.1 |
| 1004 HTMLElementStack::ElementRecord* lastTemplate = | 1016 HTMLElementStack::ElementRecord* lastTemplate = |
| 1005 m_openElements.topmost(templateTag.localName()); | 1017 m_openElements.topmost(templateTag.localName()); |
| 1006 | 1018 |
| 1007 // 2.2 | 1019 // 2.2 |
| 1008 HTMLElementStack::ElementRecord* lastTable = | 1020 HTMLElementStack::ElementRecord* lastTable = |
| 1009 m_openElements.topmost(tableTag.localName()); | 1021 m_openElements.topmost(tableTag.localName()); |
| 1010 | 1022 |
| 1011 // 2.3 | 1023 // 2.3 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1045 ASSERT(task.parent); | 1057 ASSERT(task.parent); |
| 1046 queueTask(task); | 1058 queueTask(task); |
| 1047 } | 1059 } |
| 1048 | 1060 |
| 1049 DEFINE_TRACE(HTMLConstructionSite::PendingText) { | 1061 DEFINE_TRACE(HTMLConstructionSite::PendingText) { |
| 1050 visitor->trace(parent); | 1062 visitor->trace(parent); |
| 1051 visitor->trace(nextChild); | 1063 visitor->trace(nextChild); |
| 1052 } | 1064 } |
| 1053 | 1065 |
| 1054 } // namespace blink | 1066 } // namespace blink |
| OLD | NEW |