Browse Source

Consolidate tree constructor into single loop

ns
J. King 3 years ago
parent
commit
403432bad5
  1. 506
      lib/TreeBuilder.php

506
lib/TreeBuilder.php

@ -294,16 +294,14 @@ class TreeBuilder {
$this->debugLog .= "EMITTED: ".constant(get_class($token)."::NAME")."\n";
return true;
})());
$iterations = 0;
$insertionMode = $this->insertionMode;
// If element name coercison has occurred at some earlier point,
// we must coerce all end tag names to match mangled start tags
if ($token instanceof EndTagToken && $this->DOM->mangledElements) {
$token->name = $this->coerceName($token->name);
}
ProcessToken:
$adjustedCurrentNode = $this->stack->adjustedCurrentNode;
$adjustedCurrentNodeName = $this->stack->adjustedCurrentNodeName;
$adjustedCurrentNodeNamespace = $this->stack->adjustedCurrentNodeNamespace;
# 13.2.6 Tree construction
#
@ -315,49 +313,30 @@ class TreeBuilder {
# If the adjusted current node is an element in the HTML namespace
// DEVIATION: For the purposes of this implementation the HTML namespace is null
// rather than the XHTML namespace
|| $adjustedCurrentNodeNamespace === null
|| $this->stack->adjustedCurrentNodeNamespace === null
# If the adjusted current node is a MathML text integration
# point and the token is a start tag whose tag name is
# neither "mglyph" nor "malignmark"
# If the adjusted current node is a MathML text integration
# point and the token is a character token
|| ($adjustedCurrentNode->isMathMLTextIntegrationPoint() && (($token instanceof StartTagToken && ($token->name !== 'mglyph' && $token->name !== 'malignmark') || $token instanceof CharacterToken)))
|| ($this->stack->adjustedCurrentNode->isMathMLTextIntegrationPoint() && (($token instanceof StartTagToken && ($token->name !== 'mglyph' && $token->name !== 'malignmark') || $token instanceof CharacterToken)))
# If the adjusted current node is an annotation-xml element
# in the MathML namespace and the token is a start tag
# whose tag name is "svg"
|| ($adjustedCurrentNodeNamespace === Parser::MATHML_NAMESPACE && $adjustedCurrentNodeName === 'annotation-xml' && $token instanceof StartTagToken && $token->name === 'svg')
|| ($this->stack->adjustedCurrentNodeNamespace === Parser::MATHML_NAMESPACE && $this->stack->adjustedCurrentNodeName === 'annotation-xml' && $token instanceof StartTagToken && $token->name === 'svg')
# If the adjusted current node is an HTML integration point
# and the token is a start tag
# If the adjusted current node is an HTML integration point
# and the token is a character token
|| ($adjustedCurrentNode->isHTMLIntegrationPoint() && ($token instanceof StartTagToken || $token instanceof CharacterToken))
|| ($this->stack->adjustedCurrentNode->isHTMLIntegrationPoint() && ($token instanceof StartTagToken || $token instanceof CharacterToken))
# If the token is an end-of-file token
|| $token instanceof EOFToken
) {
# Process the token according to the rules given in the section
# corresponding to the current insertion mode in HTML content.
$this->parseTokenInHTMLContent($token);
}
# Otherwise
else {
# Process the token according to the rules given in the section
# for parsing tokens in foreign content.
$this->parseTokenInForeignContent($token);
}
# When a start tag token is emitted with its self-closing flag set, if the flag
# is not acknowledged when it is processed by the tree construction stage, that
# is a non-void-html-element-start-tag-with-trailing-solidus parse error.
if ($token instanceof StartTagToken && $token->selfClosing && !$token->selfClosingAcknowledged) {
$this->error(ParseError::NON_VOID_HTML_ELEMENT_START_TAG_WITH_TRAILING_SOLIDUS, $token->name);
}
}
}
protected function parseTokenInHTMLContent(Token $token, int $insertionMode = null): bool {
$iterations = 0;
ProcessToken:
assert($iterations++ < 50, new LoopException("Probable infinite loop detected in HTML content handling (inner reprocessing)"));
$insertionMode = $insertionMode ?? $this->insertionMode;
assert((function() use ($insertionMode) {
$mode = self::INSERTION_MODE_NAMES[$insertionMode] ?? $insertionMode;
$this->debugLog .= " Mode: $mode (".(string) $this->stack.")\n";
@ -1230,7 +1209,7 @@ class TreeBuilder {
// Character tokens in this implementation can have more than one character in
// them.
if (strlen($nextToken->data) === 1 && $nextToken->data === "\n") {
return true;
continue;
} elseif (strpos($nextToken->data, "\n") === 0) {
$nextToken->data = substr($nextToken->data, 1);
}
@ -1527,7 +1506,7 @@ class TreeBuilder {
// Character tokens in this implementation can have more than one character in
// them.
if (strlen($nextToken->data) === 1 && $nextToken->data === "\n") {
return true;
continue;
} elseif (strpos($nextToken->data, "\n") === 0) {
$nextToken->data = substr($nextToken->data, 1);
}
@ -1760,7 +1739,7 @@ class TreeBuilder {
# scope, then this is a parse error; return and ignore the token.
if (!$node || !$this->stack->hasElementInScope($node)) {
$this->error(ParseError::UNEXPECTED_END_TAG, $token->name);
return true;
continue;
}
# 4. Generate implied end tags.
$this->stack->generateImpliedEndTags();
@ -1778,7 +1757,7 @@ class TreeBuilder {
# this is a parse error; return and ignore the token.
if ($this->stack->hasElementInScope('form')) {
$this->error(ParseError::UNEXPECTED_END_TAG, $token->name);
return true;
continue;
}
# 2. Generate implied end tags.
$this->stack->generateImpliedEndTags();
@ -1929,13 +1908,13 @@ class TreeBuilder {
}
# Pop all the nodes from the current node up to node, including node, then stop these steps.
$this->stack->popUntilSame($node);
return true;
continue 2;
}
# Otherwise, if node is in the special category, then
# this is a parse error; ignore the token, and return.
elseif ($this->isElementSpecial($node)) {
$this->error(ParseError::UNEXPECTED_END_TAG, $token->name);
return true;
continue 2;
}
# Set node to the previous entry in the stack of open elements.
# Return to the step labeled loop.
@ -1961,7 +1940,7 @@ class TreeBuilder {
}
# 2. Stop parsing.
return true;
return;
}
}
# 13.2.6.4.8 The "text" insertion mode
@ -3124,7 +3103,7 @@ class TreeBuilder {
# An end-of-file token
elseif ($token instanceof EOFToken) {
# Stop parsing.
return true;
return;
}
# Anything else
else {
@ -3227,7 +3206,7 @@ class TreeBuilder {
$this->error(ParseError::UNEXPECTED_EOF);
}
# Stop parsing.
return true;
return;
}
# Anything else
else {
@ -3289,7 +3268,7 @@ class TreeBuilder {
# An end-of-file token
elseif ($token instanceof EOFToken) {
# Stop parsing.
return true;
return;
}
# Anything else
else {
@ -3331,7 +3310,7 @@ class TreeBuilder {
# An end-of-file token
elseif ($token instanceof EOFToken) {
# Stop parsing.
return true;
return;
}
# Anything else
else {
@ -3371,7 +3350,7 @@ class TreeBuilder {
# An end-of-file token
elseif ($token instanceof EOFToken) {
# Stop parsing.
return true;
return;
}
# A start tag whose tag name is "noframes"
elseif ($token instanceof StartTagToken && $token->name === "noframes") {
@ -3397,223 +3376,12 @@ class TreeBuilder {
else {
throw new \Exception("UNREACHABLE CODE");
}
return true;
}
protected function adopt(TagToken $token): void {
# The adoption agency algorithm, which takes as its only argument a
# token 'token' for which the algorithm is being run, consists of
# the following steps:
assert((function() {
$this->debugLog .= " Adoption agency (".(string) $this->stack.")\n";
return true;
})());
# Let subject be token's tag name.
$subject = $token->name;
$errorCode = $token instanceof StartTagToken ? ParseError::UNEXPECTED_START_TAG : ParseError::UNEXPECTED_END_TAG;
# If the current node is an HTML element whose tag name is subject,
# and the current node is not in the list of active formatting elements,
# then pop the current node off the stack of open elements, and return.
$currentNode = $this->stack->currentNode;
if (
$currentNode->namespaceURI === null
&& $currentNode->nodeName === $subject
&& $this->activeFormattingElementsList->findSame($currentNode) === -1
) {
$this->stack->pop();
return;
}
# Let outer loop counter be zero.
$outerLoopCounter = 0;
# Outer loop: If outer loop counter is greater than or equal to eight, then return.
OuterLoop:
if ($outerLoopCounter >= 8) {
return;
}
# Increment outer loop counter by one.
$outerLoopCounter++;
# Let formatting element be the last element in the list of active
# formatting elements that:
# 1. is between the end of the list and the last marker in the list,
# if any, or the start of the list otherwise, and
# 2. has the tag name subject.
$formattingElementIndex = $this->activeFormattingElementsList->findToMarker($subject);
if ($formattingElementIndex > -1) {
$formattingElement = $this->activeFormattingElementsList[$formattingElementIndex]['element'];
$formattingToken = $this->activeFormattingElementsList[$formattingElementIndex]['token'];
} else {
$formattingElement = null;
}
# If there is no such element, then return and instead act as
# described in the "any other end tag" entry above.
if (!$formattingElement) {
// NOTE: The "entry above" refers to the "in body" insertion mode
// Changes here should be mirrored there
foreach ($this->stack as $node) {
if ($node->nodeName === $token->name && $node->namespaceURI === null) {
$this->stack->generateImpliedEndTags($token->name);
if (!$node->isSameNode($this->stack->currentNode)) {
$this->error($errorCode, $token->name);
}
$this->stack->popUntilSame($node);
return;
} elseif ($this->isElementSpecial($node)) {
$this->error($errorCode, $token->name);
return;
}
}
}
# If formatting element is not in the stack of open elements,
# then this is a parse error; remove the element from the
# list, and return.
if (($stackIndex = $this->stack->findSame($formattingElement)) === -1) {
$this->error($errorCode, $token->name);
unset($this->activeFormattingElementsList[$formattingElementIndex]);
return;
}
# If formatting element is in the stack of open elements, but
# the element is not in scope, then this is a parse error; return.
if (!$this->stack->hasElementInScope($formattingElement)) {
$this->error($errorCode, $token->name);
return;
}
# If formatting element is not the current node, this is a
# parse error. (But do not return.)
if (!$formattingElement->isSameNode($this->stack->currentNode)) {
$this->error($errorCode, $token->name);
}
# Let furthest block be the topmost node in the stack of open elements that
# is lower in the stack than formatting element, and is an element in the
# special category. There might not be one.
$furthestBlock = null;
for ($k = ($stackIndex + 1); $k < count($this->stack); $k++) {
if ($this->isElementSpecial($this->stack[$k])) {
$furthestBlockIndex = $k;
$furthestBlock = $this->stack[$k];
break;
}
}
# If there is no furthest block, then the UA must first pop all the nodes
# from the bottom of the stack of open elements, from the current node up
# to and including formatting element, then remove formatting element from
# the list of active formatting elements, and finally return.
if (!$furthestBlock) {
$this->stack->popUntilSame($formattingElement);
$this->activeFormattingElementsList->removeSame($formattingElement);
return;
}
# Let common ancestor be the element immediately above formatting element
# in the stack of open elements.
$commonAncestor = $this->stack[$stackIndex - 1] ?? null;
# Let a bookmark note the position of formatting element in the list of
# active formatting elements relative to the elements on either side
# of it in the list.
$bookmark = $formattingElementIndex;
# Let node and last node be furthest block. Follow these steps:
$node = $furthestBlock;
$nodeIndex = $furthestBlockIndex;
$lastNode = $furthestBlock;
# Let inner loop counter be zero.
$innerLoopCounter = 0;
# Inner loop: Increment inner loop counter by one.
InnerLoop:
$innerLoopCounter++;
# Let node be the element immediately above node in the stack of open
# elements, or if node is no longer in the stack of open elements
# (e.g. because it got removed by this algorithm), the element that
# was immediately above node in the stack of open elements before
# node was removed.
$node = $this->stack[--$nodeIndex];
# If node is formatting element, then go to the next step in the
# overall algorithm.
if ($node->isSameNode($formattingElement)) {
$nodeListPos = $formattingElementIndex;
goto AfterInnerLoop;
}
# If inner loop counter is greater than three and node is in the
# list of active formatting elements, then remove node from the
# list of active formatting elements.
$nodeListPos = $this->activeFormattingElementsList->findSame($node);
if ($innerLoopCounter > 3 && $nodeListPos > -1) {
$this->activeFormattingElementsList->removeSame($node);
if ($bookmark > $nodeListPos) {
$bookmark--;
}
$nodeListPos = -1;
}
# If node is not in the list of active formatting elements, then
# remove node from the stack of open elements and then go back to
# the step labeled inner loop.
if ($nodeListPos === -1) {
$this->stack->removeSame($node);
goto InnerLoop;
}
# Create an element for the token for which the element node was
# created, in the HTML namespace, with common ancestor as the
# intended parent; replace the entry for node in the list of
# active formatting elements with an entry for the new element,
# replace the entry for node in the stack of open elements with
# an entry for the new element, and let node be the new element.
$nodeToken = $this->activeFormattingElementsList[$nodeListPos]['token'];
$element = $this->createElementForToken($nodeToken, null, $commonAncestor);
$this->activeFormattingElementsList[$nodeListPos] = ['token' => $nodeToken, 'element' => $element];
$this->stack[$nodeIndex] = $element;
$node = $element;
# If last node is furthest block, then move the aforementioned
# bookmark to be immediately after the new node in the list of
# active formatting elements.
if ($lastNode->isSameNode($furthestBlock)) {
$bookmark = $nodeListPos + 1;
}
# Insert last node into node, first removing it from its previous
# parent node if any.
if ($lastNode->parentNode) {
$lastNode->parentNode->removeChild($lastNode);
}
$node->appendChild($lastNode);
# Let last node be node.
$lastNode = $node;
# Return to the step labeled inner loop.
goto InnerLoop;
# Insert whatever last node ended up being in the previous step
# at the appropriate place for inserting a node, but using
# common ancestor as the override target.
AfterInnerLoop:
$place = $this->appropriatePlaceForInsertingNode($commonAncestor);
if ($place['insert before']) {
$place['node']->parentNode->insertBefore($lastNode, $place['node']);
} else {
$place['node']->appendChild($lastNode);
}
# Create an element for the token for which formatting element was
# created, in the HTML namespace, with furthest block as the
# intended parent.
$element = $this->createElementForToken($formattingToken, null, $furthestBlock);
# Take all of the child nodes of furthest block and append them to
# the element created in the last step.
while ($furthestBlock->hasChildNodes()) {
$element->appendChild($furthestBlock->firstChild);
}
# Append that new element to furthest block.
$furthestBlock->appendChild($element);
# Remove formatting element from the list of active formatting
# elements, and insert the new element into the list of active
# formatting elements at the position of the aforementioned bookmark.
$this->activeFormattingElementsList->insert($formattingToken, $element, $bookmark);
$this->activeFormattingElementsList->removeSame($formattingElement);
# Remove formatting element from the stack of open elements, and
# insert the new element into the stack of open elements
# immediately below the position of furthest block in that stack.
assert($stackIndex > 0, new \Exception("Attempting to delete root element from stack"));
$this->stack->removeSame($formattingElement);
$this->stack->insert($element, $this->stack->findSame($furthestBlock) + 1);
# Jump back to the step labeled outer loop.
goto OuterLoop;
}
# Otherwise
else {
# Process the token according to the rules given in the section
# for parsing tokens in foreign content.
protected function parseTokenInForeignContent(Token $token): bool {
assert((function() {
$this->debugLog .= " Mode: Foreign content (".(string) $this->stack.")\n";
return true;
@ -3729,7 +3497,7 @@ class TreeBuilder {
# "in body" insertion mode.
// DEVIATION: Spec bug
// See https://github.com/whatwg/html/issues/6439
return $this->parseTokenInHTMLContent($token);
goto ProcessToken;
}
# Any other start tag
else {
@ -3812,7 +3580,7 @@ class TreeBuilder {
if (strtolower($this->stack->currentNodeName) !== $token->name) {
$this->error(ParseError::UNEXPECTED_END_TAG, $token->name);
}
return true;
continue 2;
}
# If node's tag name, converted to ASCII lowercase, is the same as the
# tag name of the token, pop elements from the stack of open elements until node
@ -3822,7 +3590,7 @@ class TreeBuilder {
$this->error(ParseError::UNEXPECTED_END_TAG, $token->name);
}
$this->stack->popUntilSame($node);
return true;
continue 2;
}
# Set node to the previous entry in the stack of open elements.
$node = $this->stack[--$pos];
@ -3831,9 +3599,229 @@ class TreeBuilder {
} while ($node->namespaceURI !== null);
# Otherwise, process the token according to the rules given in the section
# corresponding to the current insertion mode in HTML content.
return $this->parseTokenInHTMLContent($token, $this->insertionMode);
goto ProcessToken;
}
}
# When a start tag token is emitted with its self-closing flag set, if the flag
# is not acknowledged when it is processed by the tree construction stage, that
# is a non-void-html-element-start-tag-with-trailing-solidus parse error.
if ($token instanceof StartTagToken && $token->selfClosing && !$token->selfClosingAcknowledged) {
$this->error(ParseError::NON_VOID_HTML_ELEMENT_START_TAG_WITH_TRAILING_SOLIDUS, $token->name);
}
}
}
protected function adopt(TagToken $token): void {
# The adoption agency algorithm, which takes as its only argument a
# token 'token' for which the algorithm is being run, consists of
# the following steps:
assert((function() {
$this->debugLog .= " Adoption agency (".(string) $this->stack.")\n";
return true;
})());
# Let subject be token's tag name.
$subject = $token->name;
$errorCode = $token instanceof StartTagToken ? ParseError::UNEXPECTED_START_TAG : ParseError::UNEXPECTED_END_TAG;
# If the current node is an HTML element whose tag name is subject,
# and the current node is not in the list of active formatting elements,
# then pop the current node off the stack of open elements, and return.
$currentNode = $this->stack->currentNode;
if (
$currentNode->namespaceURI === null
&& $currentNode->nodeName === $subject
&& $this->activeFormattingElementsList->findSame($currentNode) === -1
) {
$this->stack->pop();
return;
}
# Let outer loop counter be zero.
$outerLoopCounter = 0;
# Outer loop: If outer loop counter is greater than or equal to eight, then return.
OuterLoop:
if ($outerLoopCounter >= 8) {
return;
}
# Increment outer loop counter by one.
$outerLoopCounter++;
# Let formatting element be the last element in the list of active
# formatting elements that:
# 1. is between the end of the list and the last marker in the list,
# if any, or the start of the list otherwise, and
# 2. has the tag name subject.
$formattingElementIndex = $this->activeFormattingElementsList->findToMarker($subject);
if ($formattingElementIndex > -1) {
$formattingElement = $this->activeFormattingElementsList[$formattingElementIndex]['element'];
$formattingToken = $this->activeFormattingElementsList[$formattingElementIndex]['token'];
} else {
$formattingElement = null;
}
# If there is no such element, then return and instead act as
# described in the "any other end tag" entry above.
if (!$formattingElement) {
// NOTE: The "entry above" refers to the "in body" insertion mode
// Changes here should be mirrored there
foreach ($this->stack as $node) {
if ($node->nodeName === $token->name && $node->namespaceURI === null) {
$this->stack->generateImpliedEndTags($token->name);
if (!$node->isSameNode($this->stack->currentNode)) {
$this->error($errorCode, $token->name);
}
$this->stack->popUntilSame($node);
return;
} elseif ($this->isElementSpecial($node)) {
$this->error($errorCode, $token->name);
return;
}
}
}
# If formatting element is not in the stack of open elements,
# then this is a parse error; remove the element from the
# list, and return.
if (($stackIndex = $this->stack->findSame($formattingElement)) === -1) {
$this->error($errorCode, $token->name);
unset($this->activeFormattingElementsList[$formattingElementIndex]);
return;
}
# If formatting element is in the stack of open elements, but
# the element is not in scope, then this is a parse error; return.
if (!$this->stack->hasElementInScope($formattingElement)) {
$this->error($errorCode, $token->name);
return;
}
# If formatting element is not the current node, this is a
# parse error. (But do not return.)
if (!$formattingElement->isSameNode($this->stack->currentNode)) {
$this->error($errorCode, $token->name);
}
# Let furthest block be the topmost node in the stack of open elements that
# is lower in the stack than formatting element, and is an element in the
# special category. There might not be one.
$furthestBlock = null;
for ($k = ($stackIndex + 1); $k < count($this->stack); $k++) {
if ($this->isElementSpecial($this->stack[$k])) {
$furthestBlockIndex = $k;
$furthestBlock = $this->stack[$k];
break;
}
}
# If there is no furthest block, then the UA must first pop all the nodes
# from the bottom of the stack of open elements, from the current node up
# to and including formatting element, then remove formatting element from
# the list of active formatting elements, and finally return.
if (!$furthestBlock) {
$this->stack->popUntilSame($formattingElement);
$this->activeFormattingElementsList->removeSame($formattingElement);
return;
}
# Let common ancestor be the element immediately above formatting element
# in the stack of open elements.
$commonAncestor = $this->stack[$stackIndex - 1] ?? null;
# Let a bookmark note the position of formatting element in the list of
# active formatting elements relative to the elements on either side
# of it in the list.
$bookmark = $formattingElementIndex;
# Let node and last node be furthest block. Follow these steps:
$node = $furthestBlock;
$nodeIndex = $furthestBlockIndex;
$lastNode = $furthestBlock;
# Let inner loop counter be zero.
$innerLoopCounter = 0;
# Inner loop: Increment inner loop counter by one.
InnerLoop:
$innerLoopCounter++;
# Let node be the element immediately above node in the stack of open
# elements, or if node is no longer in the stack of open elements
# (e.g. because it got removed by this algorithm), the element that
# was immediately above node in the stack of open elements before
# node was removed.
$node = $this->stack[--$nodeIndex];
# If node is formatting element, then go to the next step in the
# overall algorithm.
if ($node->isSameNode($formattingElement)) {
$nodeListPos = $formattingElementIndex;
goto AfterInnerLoop;
}
# If inner loop counter is greater than three and node is in the
# list of active formatting elements, then remove node from the
# list of active formatting elements.
$nodeListPos = $this->activeFormattingElementsList->findSame($node);
if ($innerLoopCounter > 3 && $nodeListPos > -1) {
$this->activeFormattingElementsList->removeSame($node);
if ($bookmark > $nodeListPos) {
$bookmark--;
}
$nodeListPos = -1;
}
# If node is not in the list of active formatting elements, then
# remove node from the stack of open elements and then go back to
# the step labeled inner loop.
if ($nodeListPos === -1) {
$this->stack->removeSame($node);
goto InnerLoop;
}
# Create an element for the token for which the element node was
# created, in the HTML namespace, with common ancestor as the
# intended parent; replace the entry for node in the list of
# active formatting elements with an entry for the new element,
# replace the entry for node in the stack of open elements with
# an entry for the new element, and let node be the new element.
$nodeToken = $this->activeFormattingElementsList[$nodeListPos]['token'];
$element = $this->createElementForToken($nodeToken, null, $commonAncestor);
$this->activeFormattingElementsList[$nodeListPos] = ['token' => $nodeToken, 'element' => $element];
$this->stack[$nodeIndex] = $element;
$node = $element;
# If last node is furthest block, then move the aforementioned
# bookmark to be immediately after the new node in the list of
# active formatting elements.
if ($lastNode->isSameNode($furthestBlock)) {
$bookmark = $nodeListPos + 1;
}
# Insert last node into node, first removing it from its previous
# parent node if any.
if ($lastNode->parentNode) {
$lastNode->parentNode->removeChild($lastNode);
}
$node->appendChild($lastNode);
# Let last node be node.
$lastNode = $node;
# Return to the step labeled inner loop.
goto InnerLoop;
# Insert whatever last node ended up being in the previous step
# at the appropriate place for inserting a node, but using
# common ancestor as the override target.
AfterInnerLoop:
$place = $this->appropriatePlaceForInsertingNode($commonAncestor);
if ($place['insert before']) {
$place['node']->parentNode->insertBefore($lastNode, $place['node']);
} else {
$place['node']->appendChild($lastNode);
}
# Create an element for the token for which formatting element was
# created, in the HTML namespace, with furthest block as the
# intended parent.
$element = $this->createElementForToken($formattingToken, null, $furthestBlock);
# Take all of the child nodes of furthest block and append them to
# the element created in the last step.
while ($furthestBlock->hasChildNodes()) {
$element->appendChild($furthestBlock->firstChild);
}
# Append that new element to furthest block.
$furthestBlock->appendChild($element);
# Remove formatting element from the list of active formatting
# elements, and insert the new element into the list of active
# formatting elements at the position of the aforementioned bookmark.
$this->activeFormattingElementsList->insert($formattingToken, $element, $bookmark);
$this->activeFormattingElementsList->removeSame($formattingElement);
# Remove formatting element from the stack of open elements, and
# insert the new element into the stack of open elements
# immediately below the position of furthest block in that stack.
assert($stackIndex > 0, new \Exception("Attempting to delete root element from stack"));
$this->stack->removeSame($formattingElement);
$this->stack->insert($element, $this->stack->findSame($furthestBlock) + 1);
# Jump back to the step labeled outer loop.
goto OuterLoop;
}
protected function appropriatePlaceForInsertingNode(\DOMNode $overrideTarget = null): array {

Loading…
Cancel
Save