diff --git a/packages/sync/src/quill-delta/Delta.ts b/packages/sync/src/quill-delta/Delta.ts index 5be4e15c545a30..6e32dca922013f 100644 --- a/packages/sync/src/quill-delta/Delta.ts +++ b/packages/sync/src/quill-delta/Delta.ts @@ -8,7 +8,7 @@ * External dependencies */ import type { Change } from 'diff'; -import { diffChars } from 'diff'; +import { diffChars, diffLines } from 'diff'; import { default as isEqual } from 'fast-deep-equal/es6'; /** @@ -23,6 +23,7 @@ function cloneDeep< T >( value: T ): T { } const NULL_CHARACTER = String.fromCharCode( 0 ); // Placeholder char for embed in diff() +const STRING_TOO_LARGE_THRESHOLD = 10000; // If either string is larger than this, use a less precise diff algorithm optimized for large strings /** * Normalize diff changes so that `count` reflects UTF-16 code-unit length @@ -623,12 +624,39 @@ class Delta { diffWithCursor( other: Delta, cursorAfterChange: number | null ): Delta { if ( this.ops === other.ops ) { return new Delta(); + } + + const strings = this.deltasToStrings( other ); + + // When large changes are pasted into the code editor, this can + // result in very long strings as a result of comparing the + // `content` of the code editor before and after the change. + // In this case, diffChars() can take a very long time to complete, + // which causes the editor to freeze. + // When we detect strings that are too long, use a less-precise diff + // method that is optimized for large inputs, at the cost of less + // accurate diffing and cursor placement. + const maxStringLength = Math.max( + ...strings.map( ( str ) => str.length ) + ); + + if ( maxStringLength > STRING_TOO_LARGE_THRESHOLD ) { + const diffResult = normalizeChangeCounts( + diffLines( strings[ 0 ], strings[ 1 ] ) + ); + const thisIterLarge = new OpIterator( this.ops ); + const otherIterLarge = new OpIterator( other.ops ); + return this.convertChangesToDelta( + diffResult, + thisIterLarge, + otherIterLarge + ).chop(); } else if ( cursorAfterChange === null ) { - // If no cursor position is provided, do a regular diff. + // If no cursor position is provided and the string + // is a reasonable length, do a regular diff. return this.diff( other ); } - const strings = this.deltasToStrings( other ); let diffs = normalizeChangeCounts( diffChars( strings[ 0 ], strings[ 1 ] ) ); diff --git a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts new file mode 100644 index 00000000000000..0f48429f552168 --- /dev/null +++ b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts @@ -0,0 +1,131 @@ +/** + * External dependencies + */ +import { readFileSync } from 'fs'; +import { join } from 'path'; + +/** + * WordPress dependencies + */ +import { test as base, expect } from '@wordpress/e2e-test-utils-playwright'; + +/** + * Internal dependencies + */ +import { setCollaboration } from './fixtures/collaboration-utils'; + +const STEP1_CONTENT = readFileSync( + join( __dirname, 'data', 'code-editor-perf-step1.md' ), + 'utf-8' +); + +const STEP2_CONTENT = readFileSync( + join( __dirname, 'data', 'code-editor-perf-step2.md' ), + 'utf-8' +); + +// Maximum time (ms) the editor should take to become responsive after +// pasting new content into the code editor. If this threshold is exceeded +// the test fails, indicating a performance regression. +const RESPONSE_TIMEOUT_MS = 3_000; + +type Fixtures = { + collaborationEnabled: boolean; +}; + +// This test only needs collaboration enabled, not a second user. +const test = base.extend< Fixtures >( { + collaborationEnabled: [ + async ( { requestUtils }, use ) => { + await setCollaboration( requestUtils, true ); + await use( true ); + await setCollaboration( requestUtils, false ); + }, + { auto: true }, + ], +} ); + +test.describe( 'Collaboration - Code editor performance', () => { + test( 'should not freeze when replacing content with large HTML in code editor', async ( { + admin, + context, + editor, + page, + pageUtils, + } ) => { + await context.grantPermissions( [ + 'clipboard-read', + 'clipboard-write', + ] ); + await admin.createNewPost(); + + const codeEditor = page.getByRole( 'textbox', { + name: 'Type text or HTML', + } ); + + // Step 1: Open the code editor and paste step 1 content. + await pageUtils.pressKeys( 'secondary+M' ); + await codeEditor.fill( STEP1_CONTENT ); + + // Step 2: Save the draft. + await pageUtils.pressKeys( 'primary+s' ); + await expect( + page + .getByRole( 'button', { name: 'Dismiss this notice' } ) + .filter( { hasText: 'Draft saved' } ) + ).toBeVisible(); + + // Step 3: Replace with step 2 content using clipboard paste, which + // triggers the same input event processing as a real user paste. + // This is the operation that causes the editor to freeze. + await codeEditor.focus(); + + // Write step 2 content to the clipboard. + await page.evaluate( + ( text: string ) => window.navigator.clipboard.writeText( text ), + STEP2_CONTENT + ); + + // Select all existing content and paste over it. Measure wall-clock + // time from the paste through switching to visual mode and blocks + // being fully parsed. This captures any main-thread freeze. + const startTime = performance.now(); + + // Use pressKeys for select-all (maps to correct modifier per + // platform). We can't use pressKeys for paste because it emulates + // the ClipboardEvent instead of doing a real keyboard paste, so we + // use page.keyboard.press directly with the platform modifier. + await pageUtils.pressKeys( 'primary+a' ); + const pasteModifier = + process.platform === 'darwin' ? 'Meta' : 'Control'; + await page.keyboard.press( `${ pasteModifier }+v` ); + + // Step 4: Switch to visual mode. The editor must process the + // pasted content before it can handle this shortcut. + await pageUtils.pressKeys( 'secondary+M' ); + + // Wait for blocks to be parsed (generous timeout so the test + // doesn't fail from the Playwright timeout itself). + await expect + .poll( + async () => { + const blocks = await editor.getBlocks(); + return blocks.some( + ( block ) => + block.name === 'core/heading' && + ( block.attributes?.content as string )?.includes( + 'Planned Versions' + ) + ); + }, + { timeout: 20_000 } + ) + .toBe( true ); + + const elapsed = performance.now() - startTime; + + // The actual performance assertion: the entire paste-to-blocks + // cycle must complete within the threshold. + expect( elapsed ).toBeLessThan( RESPONSE_TIMEOUT_MS ); + } ); +} ); diff --git a/test/e2e/specs/editor/collaboration/data/code-editor-perf-step1.md b/test/e2e/specs/editor/collaboration/data/code-editor-perf-step1.md new file mode 100644 index 00000000000000..9f7c1e8cbbf43c --- /dev/null +++ b/test/e2e/specs/editor/collaboration/data/code-editor-perf-step1.md @@ -0,0 +1,132 @@ + +
The following are the various versions of WordPress, listed chronologically from the oldest to newest, along with the Change Log information on new features and improvements in each version.
+ + + +NOTE: WordPress core developers share a love of jazz music, and since WordPress 1.0, all major releases are named in honor of jazz musicians they admire.
+ + + +Version 0.7
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
|---|---|---|---|---|---|
| 0.70 | May 27, 2003 | Changelog | Blog | ||
| 0.71 | June 9, 2003 | Changelog | Blog | ||
| 0.711 | June 25, 2003 | Changelog | Blog | ||
| 0.72 | October 11, 2003 | Changelog | Blog |
Version 1.0
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
|---|---|---|---|---|---|
| 1.0 | January 3, 2004 | Miles Davis | Changelog | Blog | |
| 1.0.1 | January 25, 2004 | Changelog | Blog | ||
| 1.0.2 | March 11, 2004 | Art Blakey | Changelog | Blog |
Version 1.2
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 1.2 | May 22, 2004 | Charles Mingus | Changelog | Blog | |
| 1.2.1 | October 6, 2004 | Changelog | Blog | ||
| 1.2.2 | December 15, 2004 | Changelog | Blog | 2540 |
Version 1.5
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 1.5 | February 17, 2005 | Billy Strayhorn | Changelog | Blog | 2541 |
| 1.5.1 | May 9, 2005 | Changelog | Blog | 2541 | |
| 1.5.1.1 | May 9, 2005 | Changelog + | Blog | 2541 | |
| 1.5.1.2 | May 27, 2005 | Changelog | Blog | 2541 | |
| 1.5.1.3 | June 29, 2005 | Changelog | Blog | 2541 | |
| 1.5.2 | August 14, 2005 | Changelog | Blog | 2541 |
Version 2.0
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.0 | December 26, 2005 | Duke Ellington | Changelog | Blog | 3441 |
| 2.0.1 | January 31, 2006 | Changelog | Blog | 3441 | |
| 2.0.2 | March 10, 2006 | Changelog | Blog | 3441 | |
| 2.0.3 | June 1, 2006 | Changelog | Blog | 3441 | |
| 2.0.4 | July 29, 2006 | Changelog | Blog | 3441 | |
| 2.0.5 | October 27, 2006 | Ronan Boren | Changelog | Blog | 3441 |
| 2.0.6 | January 5, 2007 | Changelog | Blog | 3441 | |
| 2.0.7 | January 15, 2007 | Changelog | Blog | 3441 | |
| 2.0.8 | February 8, 2007 | Changelog | No blog | 3441 | |
| 2.0.9 | February 21, 2007 | Changelog | Blog | 3441 | |
| 2.0.10 | April 3, 2007 | Changelog | Blog | 3441 | |
| 2.0.11 | August 5, 2007 | Changelog | Blog | 3441 |
Version 2.1
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.1 | January 22, 2007 | Ella Fitzgerald | Changelog | Blog | 4772 |
| 2.1.1 | February 21, 2007 | Changelog | Blog | 4773 | |
| 2.1.2 | March 2, 2007 | Changelog | Blog | 4773 | |
| 2.1.3 | April 3, 2007 | Changelog | Blog | 4773 |
Version 2.2
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.2 | May 16, 2007 | Stan Getz | Changelog | Blog | 5183 |
| 2.2.1 | June 21, 2007 | Changelog | Blog | 5183 | |
| 2.2.2 | August 5, 2007 | Changelog | Blog | 5183 | |
| 2.2.3 | September 8, 2007 | Changelog | Blog | 5183 |
Version 2.3
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.3 | September 24, 2007 | Dexter Gordon | Changelog | Blog | 6124 |
| 2.3.1 | October 26, 2007 | Changelog | Blog | 6124 | |
| 2.3.2 | December 29, 2007 | Changelog | Blog | 6124 | |
| 2.3.3 | February 5, 2008 | Changelog | Blog | 6124 |
Version 2.5
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.5 | March 29, 2008 | Michael Brecker | Changelog | Blog | 7558 |
| 2.5.1 | April 25, 2008 | Changelog | Blog | 7796 |
Version 2.6
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.6 | July 15, 2008 | McCoy Tyner | Changelog | Blog | 8201 |
| 2.6.1 | August 15, 2008 | Changelog | Blog | 8204 | |
| 2.6.2 | September 8, 2008 | Changelog | Blog | 8204 | |
| 2.6.3 | October 23, 2008 | Changelog | Blog | 8204 | |
| 2.6.5 | November 25, 2008 | Changelog | Blog | 8204 |
Version 2.7
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.7 | December 10, 2008 | John Coltrane | Changelog | Blog | 9872 |
| 2.7.1 | February 10, 2009 | Changelog | Blog | 9872 |
Version 2.8
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.8 | June 10, 2009 | Chet Baker | Changelog | Blog | 11548 |
| 2.8.1 | July 9, 2009 | Changelog | Blog | 11548 | |
| 2.8.2 | July 20, 2009 | Changelog | Blog | 11548 | |
| 2.8.3 | August 3, 2009 | Changelog | Blog | 11548 | |
| 2.8.4 | August 12, 2009 | Changelog | Blog | 11548 | |
| 2.8.5 | October 20, 2009 | Changelog | Blog | 11548 | |
| 2.8.6 | November 12, 2009 | Changelog | Blog | 11548 |
Version 2.9
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
| 2.9 | December 18, 2009 | Carmen McRae | Changelog | Blog | 12329 |
| 2.9.1 | January 4, 2010 | Changelog | Blog | 12329 | |
| 2.9.2 | February 15, 2010 | Changelog | Blog | 12329 |
Version 3.0
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
|---|---|---|---|---|---|
| 3.0 | June 17, 2010 | Thelonious Monk | Changelog | Blog | 15260 |
| 3.0.1 | July 29, 2010 | Changelog | Blog | 15477 | |
| 3.0.2 | November 30, 2010 | Changelog | Blog | 15477 | |
| 3.0.3 | December 8, 2010 | Changelog | Blog | 15477 | |
| 3.0.4 | December 29, 2010 | Changelog | Blog | 15477 | |
| 3.0.5 | February 7, 2011 | Changelog | Blog | 15477 | |
| 3.0.6 | April 26, 2011 | Changelog | No blog | 15477 |
Version 3.1
+ + + +| Version | Release Date | Musician | Change Log | Announcement | DB Version |
|---|---|---|---|---|---|
| 3.1 | February 23, 2011 | Django Reinhardt | Changelog | Blog | 17056 |
| 3.1.1 | April 5, 2011 | Changelog | Blog | 17516 | |
| 3.1.2 | April 26, 2011 | Changelog | Blog | 17516 | |
| 3.1.3 | May 25, 2011 | Changelog | Blog | 17516 | |
| 3.1.4 | June 29, 2011 | Changelog | Blog | 17516 |
The following are the various versions of WordPress, listed chronologically from newest to oldest, along with the Change Log information on new features and improvements in each version.
+ + + +NOTE: WordPress core developers share a love of jazz music, and since WordPress 1.0, all major releases are named in honor of jazz musicians they admire.
+ + + +The schedule for 2026 will be published as soon as it is available. Follow changes in progress for the next major version in our bug tracker.
+ + + +| Version | Planned Release Date | Remarks |
|---|---|---|
| 7.0 | Tentatively 2026 | Roadmap |
Version 6.9 - Gene Harris
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.9.1 | February 3, 2026 | Changelog | Blog | 60717 |
| 6.9 | December 2, 2025 | Changelog | Blog | 60717 |
Version 6.8 - Cecil
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.8.3 | September 30, 2025 | Changelog | Blog | 60717 |
| 6.8.2 | July 15, 2025 | Changelog | Blog | 60421 |
| 6.8.1 | April 30, 2025 | Changelog | Blog | 58975 |
| 6.8 | April 15, 2025 | Changelog | Blog | 58975 |
Version 6.7 - Rollins
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.7.4 | September 30, 2025 | Changelog | No blog | 58975 |
| 6.7.3 | August 5, 2025 | Changelog | Blog | 58975 |
| 6.7.2 | February 11, 2025 | Changelog | Blog | 58975 |
| 6.7.1 | November 21, 2024 | Changelog | Blog | 58975 |
| 6.7 | November 12, 2024 | Changelog | Blog | 58975 |
Version 6.6 - Dorsey
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.6.4 | September 30, 2025 | Changelog | No blog | 57155 |
| 6.6.3 | August 5, 2025 | Changelog | Blog | 57155 |
| 6.6.2 | September 10, 2024 | Changelog | Blog | 57155 |
| 6.6.1 | July 23, 2024 | Changelog | Blog | 57155 |
| 6.6 | July 16, 2024 | Changelog | Blog | 57155 |
Version 6.5 - Regina
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.5.7 | September 30, 2025 | Changelog | No blog | 57155 |
| 6.5.6 | August 5, 2025 | Changelog | Blog | 57155 |
| 6.5.5 | June 24, 2024 | Changelog | Blog | 57155 |
| 6.5.4 | June 5, 2024 | Changelog | Blog | 57155 |
| 6.5.3 | May 7, 2024 | Changelog | Blog | 57155 |
| 6.5.2 | April 9, 2024 | Changelog | Blog | 57155 |
| 6.5.1 | Changelog | Blog | 57155 | |
| 6.5 | April 2, 2024 | Changelog | Blog | 57155 |
Version 6.4 - Shirley
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.4.7 | September 30, 2025 | Changelog | No blog | 56657 |
| 6.4.6 | August 5, 2025 | Changelog | Blog | 56657 |
| 6.4.5 | June 24, 2024 | Changelog | Blog | 56657 |
| 6.4.4 | April 9, 2024 | Changelog | Blog | 56657 |
| 6.4.3 | January 30, 2024 | Changelog | Blog | 56657 |
| 6.4.2 | December 6, 2023 | Changelog | Blog | 56657 |
| 6.4.1 | November 9, 2023 | Changelog | Blog | 56657 |
| 6.4 | November 7, 2023 | Changelog | Blog | 56657 |
Version 6.3 - Lionel
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.3.7 | September 30, 2025 | Changelog | No blog | 55853 |
| 6.3.6 | August 5, 2025 | Changelog | Blog | 55853 |
| 6.3.5 | June 24, 2024 | Changelog | No blog | 55853 |
| 6.3.4 | April 9, 2024 | Changelog | No blog | 55853 |
| 6.3.3 | January 30, 2024 | Changelog | No blog | 55853 |
| 6.3.2 | October 12, 2023 | Changelog | Blog | 55853 |
| 6.3.1 | August 29, 2023 | Changelog | Blog | 55853 |
| 6.3 | August 8, 2023 | Changelog | Blog | 55853 |
Version 6.2 - Dolphy
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.2.8 | September 30, 2025 | Changelog | No blog | 53496 |
| 6.2.7 | August 5, 2025 | Changelog | Blog | 53496 |
| 6.2.6 | June 24, 2024 | No blog | 53496 | |
| 6.2.5 | April 9, 2024 | No blog | 53496 | |
| 6.2.4 | January 30, 2024 | No blog | 53496 | |
| 6.2.3 | October 12, 2023 | No blog | 53496 | |
| 6.2.2 | May 20, 2023 | Changelog | Blog | 53496 |
| 6.2.1 | May 16, 2023 | Changelog | Blog | 53496 |
| 6.2 | March 29, 2023 | Changelog | Blog | 53496 |
Version 6.1 - Mikhail "Misha" Alperin
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.1.9 | September 30, 2025 | Changelog | No blog | 53496 |
| 6.1.8 | August 5, 2025 | Changelog | Blog | 53496 |
| 6.1.7 | June 24, 2024 | No blog | 53496 | |
| 6.1.6 | April 9, 2024 | No blog | 53496 | |
| 6.1.5 | January 30, 2024 | No blog | 53496 | |
| 6.1.4 | October 12, 2023 | No blog | 53496 | |
| 6.1.3 | May 20, 2023 | No Changelog | No Blog | 53496 |
| 6.1.2 | May 16, 2023 | Changelog | No Blog | 53496 |
| 6.1.1 | November 15, 2022 | Changelog | Blog | 53496 |
| 6.1 | November 1, 2022 | Changelog | Blog | 53496 |
Version 6.0 - Arturo O’Farrill
+ + + +| Version | Release Date | Change Log | Announcement | DB Version |
|---|---|---|---|---|
| 6.0.11 | September 30, 2025 | Changelog | No blog | 53496 |
| 6.0.10 | August 5, 2025 | Changelog | Blog | 53496 |
| 6.0.9 | June 24, 2024 | No Blog | 53496 | |
| 6.0.8 | April 10, 2024 | No Blog | 53496 | |
| 6.0.7 | January 30, 2024 | No Blog | 53496 | |
| 6.0.6 | October 12, 2023 | No Blog | 53496 | |
| 6.0.5 | May 20, 2023 | No Blog | 53496 | |
| 6.0.4 | May 16, 2023 | No Blog | 53496 | |
| 6.0.3 + | October 17, 2022 | Changelog + | Blog | 53496 |
| 6.0.2 | August 30, 2022 | Changelog + | Blog + | 53496 |
| 6.0.1 | July 12, 2022 | Changelog + | Blog + | 53496 |
| 6.0 | May 24, 2022 | Changelog + | Blog | 53496 |