Skip to content
Permalink
Browse files
HTML foster-parenting algorithm no longer requires foster parents to …
…be elements

https://bugs.webkit.org/show_bug.cgi?id=137501

Reviewed by Chris Dumez.

Merge https://src.chromium.org/viewvc/blink?revision=183370&view=revision

This patch updates the foster parenting algorithm to match the spec and behavior of Blink.

* LayoutTests/fast/parser/foster-parent-expected.txt:
* LayoutTests/fast/parser/foster-parent.html:
* LayoutTests/html5lib/resources/template.dat:

* Source/WebCore/html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::findFosterSite):

Canonical link: https://commits.webkit.org/253504@main
  • Loading branch information
rniwa committed Aug 17, 2022
1 parent 6fbaa3f commit 3363325609a1e42490d723fb454977c18e5009e1
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 32 deletions.
@@ -1,3 +1,6 @@
Otherwise, if there is a table element in the stack of open elements, but the last table element in the stack of open elements has no parent, or its parent node is not an element, then the foster parent element is the element before the last table element in the stack of open elements.
PASS div.children.length is 0
PASS docFragment.firstElementChild.tagName is "H1"
PASS successfullyParsed is true

TEST COMPLETE

success
@@ -1,10 +1,6 @@
<html>
<body>
<script>
if (window.testRunner)
testRunner.dumpAsText();
</script>

<script src="../../resources/js-test.js"></script>
<div id="div">
<table id="table">
<script>
@@ -13,18 +9,14 @@
var docFragment = document.createDocumentFragment();
docFragment.appendChild(table);
</script>
<h1 id="h1">Otherwise, if there is a table element in the stack of open elements, but the last table element in the stack of open elements has no parent, or its parent node is not an element, then the foster parent element is the element before the last table element in the stack of open elements.</h1>
<h1></h1>
</table>
</div>

<script>
var div = document.getElementById('div');
var h1 = document.getElementById('h1');

if (h1 && h1 === div.firstElementChild)
document.write("success");
else
document.write("failure");
shouldBe("div.children.length", "0");
shouldBeEqualToString("docFragment.firstElementChild.tagName", "H1");
</script>
</body>
</html>
@@ -1338,3 +1338,16 @@
| <body>
| <template>
| content

#data
<template><a><table><a>
#errors
#document
| <html>
| <head>
| <template>
| content
| <a>
| <a>
| <table>
| <body>
@@ -779,32 +779,31 @@ void HTMLConstructionSite::generateImpliedEndTags()
m_openElements.pop();
}

// Adjusts |task| to match the "adjusted insertion location" determined by the foster parenting algorithm,
// laid out as the substeps of step 2 of https://html.spec.whatwg.org/#appropriate-place-for-inserting-a-node
void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task)
{
// When a node is to be foster parented, the last template element with no table element is below it in the stack of open elements is the foster parent element (NOT the template's parent!)
auto* lastTemplateElement = m_openElements.topmost(templateTag->localName());
if (lastTemplateElement && !m_openElements.inTableScope(tableTag)) {
task.parent = &lastTemplateElement->element();
auto* lastTemplate = m_openElements.topmost(templateTag->localName());
auto* lastTable = m_openElements.topmost(tableTag->localName());
if (lastTemplate && (!lastTable || lastTemplate->isAbove(*lastTable))) {
task.parent = &lastTemplate->element();
return;
}

if (auto* lastTableElementRecord = m_openElements.topmost(tableTag->localName())) {
auto& lastTableElement = lastTableElementRecord->element();
RefPtr parent = lastTableElement.parentNode();
// When parsing HTML fragments, we skip step 4.2 ("Let root be a new html element with no attributes") for efficiency,
// and instead use the DocumentFragment as a root node. So we must treat the root node (DocumentFragment) as if it is a html element here.
bool parentCanBeFosterParent = parent && (parent->isElementNode() || (m_isParsingFragment && parent == &m_openElements.rootNode()));
parentCanBeFosterParent = parentCanBeFosterParent || (is<DocumentFragment>(parent) && downcast<DocumentFragment>(parent.get())->isTemplateContent());
if (parentCanBeFosterParent) {
task.parent = parent;
task.nextChild = &lastTableElement;
return;
}
task.parent = &lastTableElementRecord->next()->element();
if (!lastTable) {
// Fragment case
task.parent = &m_openElements.rootNode();
return;
}
// Fragment case
task.parent = &m_openElements.rootNode(); // DocumentFragment

if (auto* parent = lastTable->element().parentNode()) {
task.parent = parent;
task.nextChild = &lastTable->element();
return;
}

task.parent = &lastTable->next()->element();
}

bool HTMLConstructionSite::shouldFosterParent() const

0 comments on commit 3363325

Please sign in to comment.