-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Block Editor: Fix duplicate format nesting in addActiveFormats during paste #73912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Conversation
Implement logic in createFromElement to check if parent format is identical to child's outermost format before nesting. This prevents redundant mark element nesting when copy/pasting highlighted text. Co-authored-by: ellatrix <[email protected]>
Co-authored-by: ellatrix <[email protected]>
packages/rich-text/src/create.js
Outdated
| ) { | ||
| mergeFormats.formats = formats; | ||
| mergeFormats.newFormats = formats; | ||
| return formats; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot But what is the root cause?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The root cause is that createFromElement recursively processes nested HTML elements and always prepends parent formats to child formats without checking for duplicates.
The flow:
- When parsing
<mark class="A"><mark class="A">text</mark></mark>, the inner mark is processed first, creating format array[markA] - Then the outer mark calls
mergeFormatswhich always prepends the parent format:[markA, markA] - This happens during copy/paste because the copied HTML contains
<mark>and the destination also wraps content in<mark>
Why it compounds:
Each paste operation adds another layer. After 3 paste cycles: <mark><mark><mark>text</mark></mark></mark>
The fix checks if formats[0] (outermost format) equals the parent format before nesting, preventing the duplication while preserving normal nesting behavior for different formats.
Added detailed explanation in commit 6dadb81.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot I meant, what is adding the additional mark element? Where is it introduced? We should fix the root cause, I don't like that we're changing create.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right! The root cause is in addActiveFormats (packages/block-editor/src/components/rich-text/utils.js), not in create.
What adds the additional mark:
When you paste into a location with active formats (e.g., inside a highlighted region), paste-handler.js line 75 calls addActiveFormats(valueToInsert, value.activeFormats). This function blindly prepends all active formats to the pasted content without checking for duplicates.
The flow:
- Copy
<mark class="A">text</mark> - Paste while cursor is in another
<mark class="A">region (activeFormats = [markA]) addActiveFormatsprepends markA to pasted content that already has markA- Result:
[markA, markA]= nested marks
Fixed in commit 97b4c6e by filtering out active formats that already exist in the pasted content before adding them. Reverted all changes to create.js and added proper unit tests for addActiveFormats.
Explain that the nesting occurs because createFromElement recursively processes nested elements and always prepends parent formats without checking for duplicates. This happens during copy/paste when HTML already contains mark elements and the destination also applies mark formatting. Co-authored-by: ellatrix <[email protected]>
The issue was in addActiveFormats (paste-handler.js calls this), not in create.js. When pasting content into a location with active formats, addActiveFormats was blindly prepending all active formats without checking for duplicates. Now filters out active formats that already exist in the pasted content, preventing nested identical mark elements. - Revert changes to packages/rich-text/src/create.js - Fix addActiveFormats in packages/block-editor/src/components/rich-text/utils.js - Add comprehensive unit tests for addActiveFormats Co-authored-by: ellatrix <[email protected]>
Co-authored-by: ellatrix <[email protected]>
|
Moved this comment to the issue #73909 (comment) |
What?
Fixes #58806
Prevents redundant nesting of identical format elements (like
<mark>tags) when copy/pasting highlighted text in the block editor.Why?
Copy/pasting highlighted text was creating arbitrarily nested identical
<mark>elements, degrading to 100+ levels in production sites. The root cause was in theaddActiveFormatsfunction inpackages/block-editor/src/components/rich-text/utils.js.When pasting content into a location with active formats (e.g., inside a highlighted region),
addActiveFormatsblindly prepended all active formats to the pasted content without checking for duplicates. This caused:The problematic flow:
<mark class="A">text</mark><mark class="A">region (where markA is inactiveFormats)paste-handler.jscallsaddActiveFormats(valueToInsert, value.activeFormats)addActiveFormatsprepends markA to all formats in pasted content[markA, markA]= nested marksHow?
Modified
addActiveFormatsto filter out active formats that already exist in the pasted content before adding them. The function now usesisFormatEqualfrom@wordpress/rich-textto compare formats and only adds formats that don't already exist.Key changes:
packages/block-editor/src/components/rich-text/utils.js: Added duplicate format filtering inaddActiveFormatspackages/block-editor/src/components/rich-text/test/utils.js: Added comprehensive unit tests foraddActiveFormatstest/e2e/specs/editor/various/format-library/text-color.spec.js: Added E2E test for copy/paste workflowTesting Instructions
<mark>element, not nested marksTesting Instructions for Keyboard
All operations above use standard keyboard shortcuts (Cmd/Ctrl+A for select all, Cmd/Ctrl+C/V for copy/paste). No mouse interaction required.
Screenshots or screencast
<mark>element with preserved styling, no nestingOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.