@@ -467,22 +467,10 @@ export class Parser implements Callbacks {
467467 if ( ! this . isVoidElement ( name ) ) {
468468 const pos = this . stack . indexOf ( name ) ;
469469 if ( pos !== - 1 ) {
470- // Only shift foreignContext when the element is actually on
471- // the stack — a stray </svg> must not underflow the stack.
472- if (
473- this . htmlMode &&
474- ( foreignContextElements . has ( name ) ||
475- htmlIntegrationElements . has ( name ) )
476- ) {
477- this . foreignContext . shift ( ) ;
478- }
479- for ( let index = 0 ; index <= pos ; index ++ ) {
480- const element = this . stack . shift ( ) ;
481- if ( element === undefined ) {
482- break ;
483- }
484- this . cbs . onclosetag ?.( element , index !== pos ) ;
470+ for ( let index = 0 ; index < pos ; index ++ ) {
471+ this . popElement ( true ) ;
485472 }
473+ this . popElement ( false ) ;
486474 } else if ( this . htmlMode && name === "p" ) {
487475 // Implicit open before close
488476 this . emitOpenTag ( "p" ) ;
@@ -516,15 +504,31 @@ export class Parser implements Callbacks {
516504 }
517505 }
518506
507+ /**
508+ * Pop the top element off the stack, emit a close event, and maintain
509+ * the foreign context stack.
510+ * @param implied Whether this close is implied (not from an explicit end tag).
511+ */
512+ private popElement ( implied : boolean ) : void {
513+ // biome-ignore lint/style/noNonNullAssertion: The element is guaranteed to exist.
514+ const element = this . stack . shift ( ) ! ;
515+ if (
516+ this . htmlMode &&
517+ ( foreignContextElements . has ( element ) ||
518+ htmlIntegrationElements . has ( element ) )
519+ ) {
520+ this . foreignContext . shift ( ) ;
521+ }
522+ this . cbs . onclosetag ?.( element , implied ) ;
523+ }
524+
519525 private closeCurrentTag ( isOpenImplied : boolean ) {
520526 const name = this . tagname ;
521527 this . endOpenTag ( isOpenImplied ) ;
522528
523529 // Self-closing tags will be on the top of the stack
524530 if ( this . stack [ 0 ] === name ) {
525- // If the opening tag isn't implied, the closing tag has to be implied.
526- this . cbs . onclosetag ?.( name , ! isOpenImplied ) ;
527- this . stack . shift ( ) ;
531+ this . popElement ( ! isOpenImplied ) ;
528532 }
529533 }
530534
0 commit comments