mirrored from git://develop.git.wordpress.org/
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTML API: Improve implementation of adoption agency algorithm #6983
Open
dmsnell
wants to merge
19
commits into
WordPress:trunk
Choose a base branch
from
dmsnell:html-api/improve-adoption-agency-algorithm
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
38c29c8
HTML API: Simplify breadcrumb accounting.
dmsnell e11f9ee
HTML API: Expand Unsupported class and make it available for debugging.
dmsnell e97f678
HTML API: Support more of the adoption agency algorithm.
dmsnell 2b2d6fe
HTML API: Simplify breadcrumb accounting.
dmsnell 6962fa2
HTML API: Expand Unsupported class and make it available for debugging.
dmsnell ab1096f
HTML API: Implement "reconstruct the active formatting elements" algo…
dmsnell ad82d3a
Merge branch 'trunk' into html-api/improve-active-element-reconstruction
dmsnell 99d2175
Merge branch 'trunk' into html-api/improve-adoption-agency-algorithm
dmsnell 22856d3
More iteration
dmsnell 398ce37
Bail when needing to ignore token during adoption.
dmsnell 374cba3
Continue iterating
dmsnell 9bf12f4
Merge branch 'trunk' into html-api/improve-active-element-reconstruction
sirreal d7dac5e
Goto instead of ignore
dmsnell cabe36e
Remove dead code
dmsnell 5e4bab9
Merge branch 'trunk' into html-api/improve-active-element-reconstruction
dmsnell 301438f
Merge branch 'trunk' into html-api/improve-adoption-agency-algorithm
dmsnell 9856fce
Merge branch 'trunk' into html-api/improve-active-element-reconstruction
dmsnell 6d60b3a
Merge branch 'trunk' into html-api/improve-adoption-agency-algorithm
dmsnell dd0f59f
Merge branch 'html-api/improve-active-element-reconstruction' into ht…
dmsnell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -622,12 +622,23 @@ public function remove_node( WP_HTML_Token $token ): bool { | |||||
* see WP_HTML_Open_Elements::walk_up(). | ||||||
* | ||||||
* @since 6.4.0 | ||||||
* @since 6.7.0 Accepts $below_this_node to start traversal below a given node, if it exists. | ||||||
* | ||||||
* @param ?WP_HTML_Token $below_this_node Start traversing below this node, if provided and if the node exists. | ||||||
*/ | ||||||
public function walk_down() { | ||||||
$count = count( $this->stack ); | ||||||
public function walk_down( $below_this_node = null ) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
$has_found_node = null === $below_this_node; | ||||||
$count = count( $this->stack ); | ||||||
|
||||||
for ( $i = 0; $i < $count; $i++ ) { | ||||||
yield $this->stack[ $i ]; | ||||||
$node = $this->stack[ $i ]; | ||||||
|
||||||
if ( ! $has_found_node ) { | ||||||
$has_found_node = $node === $below_this_node; | ||||||
continue; | ||||||
} | ||||||
|
||||||
yield $node; | ||||||
} | ||||||
} | ||||||
|
||||||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2360,7 +2360,9 @@ private function step_in_body(): bool { | |
break; | ||
|
||
case 'A': | ||
$this->run_adoption_agency_algorithm(); | ||
if ( 'ignore' === $this->run_adoption_agency_algorithm() ) { | ||
$this->bail( 'Cannot ignore token after running adoption agency algorithm.' ); | ||
} | ||
$this->state->active_formatting_elements->remove_node( $item ); | ||
$this->state->stack_of_open_elements->remove_node( $item ); | ||
break; | ||
|
@@ -2401,7 +2403,9 @@ private function step_in_body(): bool { | |
|
||
if ( $this->state->stack_of_open_elements->has_element_in_scope( 'NOBR' ) ) { | ||
// Parse error. | ||
$this->run_adoption_agency_algorithm(); | ||
if ( 'ignore' === $this->run_adoption_agency_algorithm() ) { | ||
$this->bail( 'Cannot ignore token after running adoption agency algorithm.' ); | ||
} | ||
$this->reconstruct_active_formatting_elements(); | ||
} | ||
|
||
|
@@ -2426,7 +2430,9 @@ private function step_in_body(): bool { | |
case '-STRONG': | ||
case '-TT': | ||
case '-U': | ||
$this->run_adoption_agency_algorithm(); | ||
if ( 'ignore' === $this->run_adoption_agency_algorithm() ) { | ||
$this->bail( 'Cannot ignore token after running adoption agency algorithm.' ); | ||
} | ||
return true; | ||
|
||
/* | ||
|
@@ -5312,32 +5318,31 @@ public function reset_insertion_mode(): void { | |
* | ||
* @see https://html.spec.whatwg.org/#adoption-agency-algorithm | ||
*/ | ||
private function run_adoption_agency_algorithm(): void { | ||
private function run_adoption_agency_algorithm(): ?string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would need a |
||
$budget = 1000; | ||
$subject = $this->get_tag(); | ||
$current_node = $this->state->stack_of_open_elements->current_node(); | ||
|
||
/* | ||
* > 2. 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. | ||
*/ | ||
if ( | ||
// > If the current node is an HTML element whose tag name is subject | ||
$current_node && $subject === $current_node->node_name && | ||
// > the current node is not in the list of active formatting elements | ||
'html' === $current_node->namespace && | ||
$subject === $current_node->node_name && | ||
! $this->state->active_formatting_elements->contains_node( $current_node ) | ||
) { | ||
$this->state->stack_of_open_elements->pop(); | ||
return; | ||
return null; | ||
} | ||
|
||
$outer_loop_counter = 0; | ||
while ( $budget-- > 0 ) { | ||
if ( $outer_loop_counter++ >= 8 ) { | ||
return; | ||
} | ||
|
||
for ( $outer_loop_counter = 0; $outer_loop_counter < 8; ++$outer_loop_counter ) { | ||
/* | ||
* > Let formatting element be the last element in the list of active formatting elements that: | ||
* > - is between the end of the list and the last marker in the list, | ||
* > if any, or the start of the list otherwise, | ||
* > - and has the tag name subject. | ||
* > 3. Let formatting element be the last element in the list of active formatting elements that: | ||
* > - is between the end of the list and the last marker in the list, | ||
* > if any, or the start of the list otherwise, | ||
* > - and has the tag name subject. | ||
*/ | ||
$formatting_element = null; | ||
foreach ( $this->state->active_formatting_elements->walk_up() as $item ) { | ||
|
@@ -5351,64 +5356,216 @@ private function run_adoption_agency_algorithm(): void { | |
} | ||
} | ||
|
||
// > If there is no such element, then return and instead act as described in the "any other end tag" entry above. | ||
/* | ||
* > If there is no such element, then return and instead act as | ||
* > described in the "any other end tag" entry above. | ||
*/ | ||
if ( null === $formatting_element ) { | ||
$this->bail( 'Cannot run adoption agency when "any other end tag" is required.' ); | ||
/* | ||
* These steps are copied here from above. This may remove the node | ||
* or ignore it, meaning the following code must respect that. | ||
*/ | ||
foreach ( $this->state->stack_of_open_elements->walk_up() as $node ) { | ||
if ( 'html' === $node->namespace && $subject === $node->node_name ) { | ||
break; | ||
} | ||
|
||
if ( self::is_special( $node ) ) { | ||
return 'ignore'; | ||
} | ||
} | ||
|
||
$this->generate_implied_end_tags( $subject ); | ||
foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) { | ||
$this->state->stack_of_open_elements->pop(); | ||
|
||
if ( $node === $item ) { | ||
return null; | ||
} | ||
} | ||
} | ||
|
||
// > 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. | ||
/* | ||
* > 4. 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 ( ! $this->state->stack_of_open_elements->contains_node( $formatting_element ) ) { | ||
$this->state->active_formatting_elements->remove_node( $formatting_element ); | ||
return; | ||
return null; | ||
} | ||
|
||
// > If formatting element is in the stack of open elements, but the element is not in scope, then this is a parse error; return. | ||
/* | ||
* > 5. 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->state->stack_of_open_elements->has_element_in_scope( $formatting_element->node_name ) ) { | ||
return; | ||
return null; | ||
} | ||
|
||
/* | ||
* > 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. | ||
* > 6. If formatting element is not the current node, this is a parse error. (But do not return.) | ||
*/ | ||
$is_above_formatting_element = true; | ||
$furthest_block = null; | ||
foreach ( $this->state->stack_of_open_elements->walk_down() as $item ) { | ||
if ( $is_above_formatting_element && $formatting_element->bookmark_name !== $item->bookmark_name ) { | ||
continue; | ||
} | ||
|
||
if ( $is_above_formatting_element ) { | ||
$is_above_formatting_element = false; | ||
continue; | ||
} | ||
|
||
/* | ||
* > 7. 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. | ||
*/ | ||
$furthest_block = null; | ||
foreach ( $this->state->stack_of_open_elements->walk_down( $formatting_element ) as $item ) { | ||
if ( self::is_special( $item ) ) { | ||
$furthest_block = $item; | ||
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. | ||
* > 8. 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 ( null === $furthest_block ) { | ||
foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) { | ||
$this->state->stack_of_open_elements->pop(); | ||
|
||
if ( $formatting_element->bookmark_name === $item->bookmark_name ) { | ||
if ( $formatting_element === $item ) { | ||
$this->state->active_formatting_elements->remove_node( $formatting_element ); | ||
return; | ||
return null; | ||
} | ||
} | ||
} | ||
|
||
$this->bail( 'Cannot extract common ancestor in adoption agency algorithm.' ); | ||
} | ||
/* | ||
* > 9. Let common ancestor be the element immediately above | ||
* > formatting element in the stack of open elements. | ||
*/ | ||
$common_ancestor = null; | ||
foreach ( $this->state->stack_of_open_elements->walk_up( $formatting_element ) as $item ) { | ||
$common_ancestor = $item; | ||
break; | ||
} | ||
|
||
/* | ||
* > 10. 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. | ||
*/ | ||
$formatting_element_index = 0; | ||
foreach ( $this->state->active_formatting_elements->walk_down() as $item ) { | ||
if ( $formatting_element === $item ) { | ||
break; | ||
} | ||
|
||
++$formatting_element_index; | ||
} | ||
|
||
/* | ||
* > 11. Let node and last node be furthest block. | ||
*/ | ||
$node = $furthest_block; | ||
$last_node = $furthest_block; | ||
|
||
$inner_loop_counter = 0; | ||
while ( $budget-- > 0 ) { | ||
/* | ||
* > 1. Increment innerLoopCounter by 1. | ||
*/ | ||
++$inner_loop_counter; | ||
|
||
$this->bail( 'Cannot run adoption agency when looping required.' ); | ||
/* | ||
* > 2. 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. | ||
*/ | ||
if ( $this->state->stack_of_open_elements->contains_node( $node ) ) { | ||
foreach ( $this->state->stack_of_open_elements->walk_up( $node ) as $item ) { | ||
$node = $item; | ||
break; | ||
} | ||
} else { | ||
$this->bail( 'Cannot adjust node pointer above removed node.' ); | ||
} | ||
|
||
/* | ||
* > 3. If node is formattingElement, the break. | ||
*/ | ||
if ( $formatting_element === $node ) { | ||
break; | ||
} | ||
|
||
/* | ||
* > 4. If innerLoopCounter is greater than 3 and node is in the list of active formatting | ||
* > elements, then remove node from the list of active formatting elements. | ||
*/ | ||
if ( $inner_loop_counter > 3 && $this->state->active_formatting_elements->contains_node( $node ) ) { | ||
$this->state->active_formatting_elements->remove_node( $node ); | ||
} | ||
|
||
/* | ||
* > 5. If node is not in the list of active formatting elements, then remove node from | ||
* > the stack of open elements and continue. | ||
*/ | ||
if ( ! $this->state->active_formatting_elements->contains_node( $node ) ) { | ||
$this->state->stack_of_open_elements->remove_node( $node ); | ||
continue; | ||
} | ||
|
||
/* | ||
* > 6. 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. | ||
*/ | ||
$this->bail( 'Cannot create and reference new element for which no token exists.' ); | ||
|
||
/* | ||
* > 7. If last node is furthestBlock, then move the aforementioned bookmark to | ||
* > be immediately after the new node in the list of active formatting elements. | ||
*/ | ||
|
||
/* | ||
* > 8. Append lastNode to node. | ||
*/ | ||
|
||
/* | ||
* > 9. Set lastNode to node. | ||
*/ | ||
$last_node = $node; | ||
} | ||
|
||
/* | ||
* > 14. 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. | ||
*/ | ||
$this->bail( 'Cannot create and reference new element for which no token exists.' ); | ||
|
||
/* | ||
* > 15. Create an element for the token for which formattingElement was created, | ||
* > in the HTML namespace, with furthestBlock as the intended parent. | ||
*/ | ||
|
||
/* | ||
* > 16. Take all of the child nodes of furthestBlock and append them to the element | ||
* > created in the last step. | ||
*/ | ||
|
||
/* | ||
* > 17. Append that new element to furthestBlock. | ||
*/ | ||
|
||
/* | ||
* > 18. Remove formattingElement 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. | ||
*/ | ||
|
||
/* | ||
* > 19. Remove formattingElement from the stack of open elements, and insert the | ||
* > new element into the stack of open elements immediately below the position | ||
* > of furthestBlock in that stack. | ||
*/ | ||
} | ||
} | ||
|
||
/** | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix PHPdoc: