@@ -2145,20 +2145,15 @@ private function run_adoption_agency_algorithm() {
21452145
21462146 if (
21472147 // > If the current node is an HTML element whose tag name is subject
2148- $ current_node && $ subject === $ current_node ->node_name &&
2148+ isset ( $ current_node ) && $ subject === $ current_node ->node_name &&
21492149 // > the current node is not in the list of active formatting elements
21502150 ! $ this ->state ->active_formatting_elements ->contains_node ( $ current_node )
21512151 ) {
21522152 $ this ->state ->stack_of_open_elements ->pop ();
21532153 return ;
21542154 }
21552155
2156- $ outer_loop_counter = 0 ;
2157- while ( $ budget -- > 0 ) {
2158- if ( $ outer_loop_counter ++ >= 8 ) {
2159- return ;
2160- }
2161-
2156+ for ( $ outer_loop_counter = 0 ; $ outer_loop_counter < 8 ; $ outer_loop_counter ++ ) {
21622157 /*
21632158 * > Let formatting element be the last element in the list of active formatting elements that:
21642159 * > - is between the end of the list and the last marker in the list,
@@ -2179,8 +2174,35 @@ private function run_adoption_agency_algorithm() {
21792174
21802175 // > If there is no such element, then return and instead act as described in the "any other end tag" entry above.
21812176 if ( null === $ formatting_element ) {
2182- $ this ->last_error = self ::ERROR_UNSUPPORTED ;
2183- throw new WP_HTML_Unsupported_Exception ( 'Cannot run adoption agency when "any other end tag" is required. ' );
2177+ /*
2178+ * > Any other end tag
2179+ */
2180+
2181+ /*
2182+ * Find the corresponding tag opener in the stack of open elements, if
2183+ * it exists before reaching a special element, which provides a kind
2184+ * of boundary in the stack. For example, a `</custom-tag>` should not
2185+ * close anything beyond its containing `P` or `DIV` element.
2186+ */
2187+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up () as $ node ) {
2188+ if ( $ subject === $ node ->node_name ) {
2189+ break ;
2190+ }
2191+
2192+ if ( self ::is_special ( $ node ->node_name ) ) {
2193+ // This is a parse error, ignore the token.
2194+ return ;
2195+ }
2196+ }
2197+
2198+ $ this ->generate_implied_end_tags ( $ subject );
2199+
2200+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up () as $ item ) {
2201+ $ this ->state ->stack_of_open_elements ->pop ();
2202+ if ( $ node === $ item ) {
2203+ return ;
2204+ }
2205+ }
21842206 }
21852207
21862208 // > 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.
@@ -2194,22 +2216,16 @@ private function run_adoption_agency_algorithm() {
21942216 return ;
21952217 }
21962218
2219+ /*
2220+ * > If formatting element is not the current node, this is a parse error. (But do not return.)
2221+ */
2222+
21972223 /*
21982224 * > Let furthest block be the topmost node in the stack of open elements that is lower in the stack
21992225 * > than formatting element, and is an element in the special category. There might not be one.
22002226 */
2201- $ is_above_formatting_element = true ;
2202- $ furthest_block = null ;
2203- foreach ( $ this ->state ->stack_of_open_elements ->walk_down () as $ item ) {
2204- if ( $ is_above_formatting_element && $ formatting_element ->bookmark_name !== $ item ->bookmark_name ) {
2205- continue ;
2206- }
2207-
2208- if ( $ is_above_formatting_element ) {
2209- $ is_above_formatting_element = false ;
2210- continue ;
2211- }
2212-
2227+ $ furthest_block = null ;
2228+ foreach ( $ this ->state ->stack_of_open_elements ->walk_down ( $ formatting_element ) as $ item ) {
22132229 if ( self ::is_special ( $ item ->node_name ) ) {
22142230 $ furthest_block = $ item ;
22152231 break ;
@@ -2225,19 +2241,87 @@ private function run_adoption_agency_algorithm() {
22252241 foreach ( $ this ->state ->stack_of_open_elements ->walk_up () as $ item ) {
22262242 $ this ->state ->stack_of_open_elements ->pop ();
22272243
2228- if ( $ formatting_element-> bookmark_name === $ item-> bookmark_name ) {
2244+ if ( $ formatting_element === $ item ) {
22292245 $ this ->state ->active_formatting_elements ->remove_node ( $ formatting_element );
22302246 return ;
22312247 }
22322248 }
22332249 }
22342250
2251+ /*
2252+ * > Let common ancestor be the element immediately above formatting element in the stack of open elements.
2253+ */
2254+ $ common_ancestor = null ;
2255+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up ( $ formatting_element ) as $ item ) {
2256+ $ common_ancestor = $ item ;
2257+ break ;
2258+ }
2259+
2260+ /*
2261+ * 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.
2262+ */
2263+ $ formatting_element_index = 0 ;
2264+ foreach ( $ this ->state ->active_formatting_elements ->walk_down () as $ item ) {
2265+ if ( $ formatting_element === $ item ) {
2266+ break ;
2267+ }
2268+
2269+ ++$ formatting_element_index ;
2270+ }
2271+
2272+ /*
2273+ * > Let node and last node be furthest block.
2274+ */
2275+ $ node = $ furthest_block ;
2276+ $ last_node = $ furthest_block ;
2277+
2278+ $ inner_loop_counter = 0 ;
2279+ while ( $ budget -- > 0 ) {
2280+ ++$ inner_loop_counter ;
2281+
2282+ if ( $ this ->state ->stack_of_open_elements ->contains_node ( $ node ) ) {
2283+ foreach ( $ this ->state ->stack_of_open_elements ->walk_up ( $ node ) as $ item ) {
2284+ $ node = $ item ;
2285+ break ;
2286+ }
2287+ } else {
2288+ $ this ->last_error = self ::ERROR_UNSUPPORTED ;
2289+ throw new WP_HTML_Unsupported_Exception ( 'Cannot adjust node pointer above removed node. ' );
2290+ }
2291+
2292+ if ( $ formatting_element === $ node ) {
2293+ break ;
2294+ }
2295+
2296+ if ( $ inner_loop_counter > 3 && $ this ->state ->active_formatting_elements ->contains_node ( $ node ) ) {
2297+ $ this ->state ->active_formatting_elements ->remove_node ( $ node );
2298+ }
2299+
2300+ if ( ! $ this ->state ->active_formatting_elements ->contains_node ( $ node ) ) {
2301+ $ this ->state ->stack_of_open_elements ->remove_node ( $ node );
2302+ continue ;
2303+ }
2304+
2305+ /*
2306+ * > Create an element for the token for which the element node was created,
2307+ * in the HTML namespace, with common ancestor as the intended parent;
2308+ * replace the entry for node in the list of active formatting elements
2309+ * with an entry for the new element, replace the entry for node in the
2310+ * stack of open elements with an entry for the new element, and let node
2311+ * be the new element.
2312+ */
2313+ $ this ->last_error = self ::ERROR_UNSUPPORTED ;
2314+ throw new WP_HTML_Unsupported_Exception ( 'Cannot create and reference new element for which no token exists. ' );
2315+ }
2316+
2317+ /*
2318+ * > Insert whatever last node ended up being in the previous step at the appropriate
2319+ * > palce for inserting a node, but using common ancestor as the override target.
2320+ */
2321+
22352322 $ this ->last_error = self ::ERROR_UNSUPPORTED ;
2236- throw new WP_HTML_Unsupported_Exception ( 'Cannot extract common ancestor in adoption agency algorithm . ' );
2323+ throw new WP_HTML_Unsupported_Exception ( 'Cannot create and reference new element for which no token exists . ' );
22372324 }
2238-
2239- $ this ->last_error = self ::ERROR_UNSUPPORTED ;
2240- throw new WP_HTML_Unsupported_Exception ( 'Cannot run adoption agency when looping required. ' );
22412325 }
22422326
22432327 /**
0 commit comments