Make WordPress Core


Ignore:
Timestamp:
03/11/2024 11:53:07 PM (21 months ago)
Author:
dmsnell
Message:

HTML API: Defer applying attribute updates until necessary.

When making repeated updates to a document, the Tag Processor will end
up copying the entire document once for every update. This can lead to
catastrophic behavior in the worse case.

However, when batch-applying updates it's able to copy chunks of the
document in one thread and only end up copying the entire document once
for the entire batch.

Previously the Tag Processor has been eagerly applying udpates, but in
this patch it defers applying those updates as long as is possible.

Developed in https://github.com/WordPress/wordpress-develop/pull/6120
Discussed in https://core.trac.wordpress.org/ticket/60697

Props: dmsnell, bernhard-reiter, jonsurrell, westonruter.
Fixes #60697.
Follow-up to [55706], [56941], [57348].

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php

    r57508 r57805  
    27282728        $this->assertSame( 'test< /A>', $processor->get_modifiable_text(), 'Did not find complete text node.' );
    27292729    }
     2730
     2731    /**
     2732     * Ensures that updates which are enqueued in front of the cursor
     2733     * are applied before moving forward in the document.
     2734     *
     2735     * @ticket 60697
     2736     */
     2737    public function test_applies_updates_before_proceeding() {
     2738        $html = '<div><img></div><div><img></div>';
     2739
     2740        $subclass = new class( $html ) extends WP_HTML_Tag_Processor {
     2741            /**
     2742             * Inserts raw text after the current token.
     2743             *
     2744             * @param string $new_html Raw text to insert.
     2745             */
     2746            public function insert_after( $new_html ) {
     2747                $this->set_bookmark( 'here' );
     2748                $this->lexical_updates[] = new WP_HTML_Text_Replacement(
     2749                    $this->bookmarks['here']->start + $this->bookmarks['here']->length + 1,
     2750                    0,
     2751                    $new_html
     2752                );
     2753            }
     2754        };
     2755
     2756        $subclass->next_tag( 'img' );
     2757        $subclass->insert_after( '<p>snow-capped</p>' );
     2758
     2759        $subclass->next_tag();
     2760        $this->assertSame(
     2761            'P',
     2762            $subclass->get_tag(),
     2763            'Should have matched inserted HTML as next tag.'
     2764        );
     2765
     2766        $subclass->next_tag( 'img' );
     2767        $subclass->set_attribute( 'alt', 'mountain' );
     2768
     2769        $this->assertSame(
     2770            '<div><img><p>snow-capped</p></div><div><img alt="mountain"></div>',
     2771            $subclass->get_updated_html(),
     2772            'Should have properly applied the update from in front of the cursor.'
     2773        );
     2774    }
    27302775}
Note: See TracChangeset for help on using the changeset viewer.