Skip to content

Fix extra <div> tags accumulating on 'Edit as New' for HTML messages - #10283

Open
dodjango wants to merge 3 commits into
roundcube:masterfrom
dodjango:fix/edit-as-new-html-div-buildup
Open

Fix extra <div> tags accumulating on 'Edit as New' for HTML messages#10283
dodjango wants to merge 3 commits into
roundcube:masterfrom
dodjango:fix/edit-as-new-html-div-buildup

Conversation

@dodjango

@dodjango dodjango commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #9919 — repeatedly using "Edit as New" on an HTML message caused extra <div> wrappers to accumulate in the message body on every cycle. This turned out to have two independent causes, both fixed here:

  1. prepare_html_body() only skipped the container-id wrapper (<div id="editbody...">) for MODE_DRAFT, not MODE_EDIT ("Edit as New"). Since Edit-as-New reloads/resends the message like a draft, the wrapper was saved and re-added on every cycle.
  2. Setting ignore_elements => ['body'] for MODE_DRAFT/MODE_EDIT had no effect, because wash_html() unconditionally registers a callback for the body tag, and a registered callback takes precedence over ignore_elements in rcube_washtml::dumpHtml(). That callback unconditionally wraps the body (and its style attribute, e.g. the font-size/font-family added by send.php on every send) in a new <div> — so the accumulation continued even after fix Change version text to reflect new versioning system #1, just without an id. Added a skip_washer_body_callback option (mirroring the existing skip_washer_form_callback/skip_washer_style_callback pattern) and set it for MODE_DRAFT/MODE_EDIT so the body tag and its attributes are dropped entirely, keeping only its content.

Test plan

  • Unit tests in tests/Actions/Mail/ComposeTest.php covering MODE_EDIT, MODE_DRAFT (both must not wrap the body, including when the stored <body> tag carries a style attribute), and regression coverage for MODE_REPLY/MODE_FORWARD (must still wrap in a container)
  • Full PHPUnit suite run against this branch; the only remaining failures/errors (ImapTest::test_sort_folder_list, ImageTest, SearchTest, InstallTest::test_check_mime_extensions) were confirmed to pre-exist identically on unpatched upstream/master in a side-by-side worktree comparison — no regressions introduced
  • Manual side-by-side comparison of two live instances (pre-fix vs. patched), repeating "Edit as New" + Send 3-4x on an HTML message: pre-fix accumulates nested <div> wrappers (with growing ids, then style-only once fix Change version text to reflect new versioning system #1 alone was applied); patched instance shows no wrapper at all, regardless of cycle count
  • Manual regression check of adjacent compose flows on the patched instance: save draft → reopen (no wrapper, as intended), Reply (still wraps in <div id="replybody...">, unaffected), Forward (still wraps in <div id="forwardbody...">, unaffected)

@dodjango
dodjango marked this pull request as ready for review July 21, 2026 11:03
dodjango and others added 2 commits August 18, 2026 23:57
…oundcube#9919)

prepare_html_body() only skipped the body container wrapper for
MODE_DRAFT. MODE_EDIT ('Edit as New') fell through to the else branch
and got wrapped in a <div id="editbody..."> container by wash_html().
Since Edit-as-New reloads a full message for re-sending (like a draft),
that wrapper was saved on send and a new one was added on every
edit/send cycle, producing nested <div> buildup.

Treat MODE_EDIT like MODE_DRAFT so no container wrapper is added,
matching how the two modes are already grouped throughout compose.php.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ibute (roundcube#9919)

The previous fix set ignore_elements => ['body'] for MODE_DRAFT/MODE_EDIT
in prepare_html_body(), but wash_html() unconditionally registers a
callback for the 'body' tag. A registered callback takes precedence over
ignore_elements in rcube_washtml::dumpHtml(), so the callback still ran
and wrapped the body's style attribute (added by send.php's
default_font/default_font_size wrapping on every send) into a new <div>
on every edit/send cycle - just without an id this time.

Add a skip_washer_body_callback option, mirroring the existing
skip_washer_form_callback/skip_washer_style_callback pattern, and set it
for MODE_DRAFT/MODE_EDIT so ignore_elements actually takes effect and the
<body> tag (and its style attribute) is dropped entirely, keeping only
its content.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@dodjango
dodjango force-pushed the fix/edit-as-new-html-div-buildup branch from 02c95ef to 928760d Compare August 18, 2026 21:58
…oundcube#9919)

Draft mode shares the same no-container code path as edit mode, so a saved
draft that is reopened repeatedly is susceptible to the same <div> buildup.
Forward mode must keep wrapping the body in a container, so guard that the
fix does not regress style isolation for forwards.
@dodjango

Copy link
Copy Markdown
Contributor Author

Friendly ping — this PR is ready for review whenever a maintainer has a moment. 🙏

Two updates since it was opened:

Rebased onto current master to clear a merge conflict that had appeared in the meantime. The only conflict was in CHANGELOG.md: since 1.7.3 has been released, I moved the entry from that (now released) section into the new ## Unreleased section. program/actions/mail/compose.php merged cleanly — the code change itself is unchanged from its original state.

Added two test cases so the coverage matches what the test plan above describes: MODE_DRAFT (a saved draft shares the same no-container path as Edit-as-New and can be reopened repeatedly, so it is susceptible to the same buildup) and MODE_FORWARD (guards that the fix does not regress container-based style isolation for forwards). ComposeTest is at 7 tests / 15 assertions, all green.

A note on the previously red check: the single failing job (Linux / PHP 8.3, Message Rendering workflow) was a transient infrastructure failure unrelated to this change — composer got an HTTP/2 504 from the GitHub API while downloading nette/utils. Everything substantive was green, and the full matrix has re-run since.

Happy to address any feedback. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extra <div> tags added when using "Edit as New" on HTML emails

1 participant