@@ -3103,20 +3103,15 @@ private function run_adoption_agency_algorithm() {
31033103
31043104 if (
31053105 // > If the current node is an HTML element whose tag name is subject
3106- $ current_node && $ subject === $ current_node ->node_name &&
3106+ isset ( $ current_node ) && $ subject === $ current_node ->node_name &&
31073107 // > the current node is not in the list of active formatting elements
31083108 ! $ this ->state ->active_formatting_elements ->contains_node ( $ current_node )
31093109 ) {
31103110 $ this ->state ->stack_of_open_elements ->pop ();
31113111 return ;
31123112 }
31133113
3114- $ outer_loop_counter = 0 ;
3115- while ( $ budget -- > 0 ) {
3116- if ( $ outer_loop_counter ++ >= 8 ) {
3117- return ;
3118- }
3119-
3114+ for ( $ outer_loop_counter = 0 ; $ outer_loop_counter < 8 ; $ outer_loop_counter ++ ) {
31203115 /*
31213116 * > Let formatting element be the last element in the list of active formatting elements that:
31223117 * > - is between the end of the list and the last marker in the list,
@@ -3137,8 +3132,35 @@ private function run_adoption_agency_algorithm() {
31373132
31383133 // > If there is no such element, then return and instead act as described in the "any other end tag" entry above.
31393134 if ( null === $ formatting_element ) {
3140- $ this ->last_error = self ::ERROR_UNSUPPORTED ;
3141- throw new WP_HTML_Unsupported_Exception ( 'Cannot run adoption agency when "any other end tag" is required. ' );
3135+ /*
3136+ * > Any other end tag
3137+ */
3138+
3139+ /*
3140+ * Find the corresponding tag opener in the stack of open elements, if
3141+ * it exists before reaching a special element, which provides a kind
3142+ * of boundary in the stack. For example, a `</custom-tag>` should not
3143+ * close anything beyond its containing `P` or `DIV` element.
3144+ */
3145+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up () as $ node ) {
3146+ if ( $ subject === $ node ->node_name ) {
3147+ break ;
3148+ }
3149+
3150+ if ( self ::is_special ( $ node ->node_name ) ) {
3151+ // This is a parse error, ignore the token.
3152+ return ;
3153+ }
3154+ }
3155+
3156+ $ this ->generate_implied_end_tags ( $ subject );
3157+
3158+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up () as $ item ) {
3159+ $ this ->state ->stack_of_open_elements ->pop ();
3160+ if ( $ node === $ item ) {
3161+ return ;
3162+ }
3163+ }
31423164 }
31433165
31443166 // > 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.
@@ -3152,22 +3174,16 @@ private function run_adoption_agency_algorithm() {
31523174 return ;
31533175 }
31543176
3177+ /*
3178+ * > If formatting element is not the current node, this is a parse error. (But do not return.)
3179+ */
3180+
31553181 /*
31563182 * > Let furthest block be the topmost node in the stack of open elements that is lower in the stack
31573183 * > than formatting element, and is an element in the special category. There might not be one.
31583184 */
3159- $ is_above_formatting_element = true ;
3160- $ furthest_block = null ;
3161- foreach ( $ this ->state ->stack_of_open_elements ->walk_down () as $ item ) {
3162- if ( $ is_above_formatting_element && $ formatting_element ->bookmark_name !== $ item ->bookmark_name ) {
3163- continue ;
3164- }
3165-
3166- if ( $ is_above_formatting_element ) {
3167- $ is_above_formatting_element = false ;
3168- continue ;
3169- }
3170-
3185+ $ furthest_block = null ;
3186+ foreach ( $ this ->state ->stack_of_open_elements ->walk_down ( $ formatting_element ) as $ item ) {
31713187 if ( self ::is_special ( $ item ->node_name ) ) {
31723188 $ furthest_block = $ item ;
31733189 break ;
@@ -3183,19 +3199,87 @@ private function run_adoption_agency_algorithm() {
31833199 foreach ( $ this ->state ->stack_of_open_elements ->walk_up () as $ item ) {
31843200 $ this ->state ->stack_of_open_elements ->pop ();
31853201
3186- if ( $ formatting_element-> bookmark_name === $ item-> bookmark_name ) {
3202+ if ( $ formatting_element === $ item ) {
31873203 $ this ->state ->active_formatting_elements ->remove_node ( $ formatting_element );
31883204 return ;
31893205 }
31903206 }
31913207 }
31923208
3209+ /*
3210+ * > Let common ancestor be the element immediately above formatting element in the stack of open elements.
3211+ */
3212+ $ common_ancestor = null ;
3213+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up ( $ formatting_element ) as $ item ) {
3214+ $ common_ancestor = $ item ;
3215+ break ;
3216+ }
3217+
3218+ /*
3219+ * 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.
3220+ */
3221+ $ formatting_element_index = 0 ;
3222+ foreach ( $ this ->state ->active_formatting_elements ->walk_down () as $ item ) {
3223+ if ( $ formatting_element === $ item ) {
3224+ break ;
3225+ }
3226+
3227+ ++$ formatting_element_index ;
3228+ }
3229+
3230+ /*
3231+ * > Let node and last node be furthest block.
3232+ */
3233+ $ node = $ furthest_block ;
3234+ $ last_node = $ furthest_block ;
3235+
3236+ $ inner_loop_counter = 0 ;
3237+ while ( $ budget -- > 0 ) {
3238+ ++$ inner_loop_counter ;
3239+
3240+ if ( $ this ->state ->stack_of_open_elements ->contains_node ( $ node ) ) {
3241+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up ( $ node ) as $ item ) {
3242+ $ node = $ item ;
3243+ break ;
3244+ }
3245+ } else {
3246+ $ this ->last_error = self ::ERROR_UNSUPPORTED ;
3247+ throw new WP_HTML_Unsupported_Exception ( 'Cannot adjust node pointer above removed node. ' );
3248+ }
3249+
3250+ if ( $ formatting_element === $ node ) {
3251+ break ;
3252+ }
3253+
3254+ if ( $ inner_loop_counter > 3 && $ this ->state ->active_formatting_elements ->contains_node ( $ node ) ) {
3255+ $ this ->state ->active_formatting_elements ->remove_node ( $ node );
3256+ }
3257+
3258+ if ( ! $ this ->state ->active_formatting_elements ->contains_node ( $ node ) ) {
3259+ $ this ->state ->stack_of_open_elements ->remove_node ( $ node );
3260+ continue ;
3261+ }
3262+
3263+ /*
3264+ * > Create an element for the token for which the element node was created,
3265+ * in the HTML namespace, with common ancestor as the intended parent;
3266+ * replace the entry for node in the list of active formatting elements
3267+ * with an entry for the new element, replace the entry for node in the
3268+ * stack of open elements with an entry for the new element, and let node
3269+ * be the new element.
3270+ */
3271+ $ this ->last_error = self ::ERROR_UNSUPPORTED ;
3272+ throw new WP_HTML_Unsupported_Exception ( 'Cannot create and reference new element for which no token exists. ' );
3273+ }
3274+
3275+ /*
3276+ * > Insert whatever last node ended up being in the previous step at the appropriate
3277+ * > palce for inserting a node, but using common ancestor as the override target.
3278+ */
3279+
31933280 $ this ->last_error = self ::ERROR_UNSUPPORTED ;
3194- throw new WP_HTML_Unsupported_Exception ( 'Cannot extract common ancestor in adoption agency algorithm . ' );
3281+ throw new WP_HTML_Unsupported_Exception ( 'Cannot create and reference new element for which no token exists . ' );
31953282 }
3196-
3197- $ this ->last_error = self ::ERROR_UNSUPPORTED ;
3198- throw new WP_HTML_Unsupported_Exception ( 'Cannot run adoption agency when looping required. ' );
31993283 }
32003284
32013285 /**
0 commit comments