From 7d9d6e65101ec220450c8939184102c1beaec8ae Mon Sep 17 00:00:00 2001 From: Giovanni Date: Tue, 21 Jul 2026 15:41:30 +0200 Subject: [PATCH] Defer visible/invisible HTML rendering until actually requested rendered() unconditionally computed get_rendered_text() three times per HTML part (full, visible-only, invisible-only), each doing a full mask-and-whitespace-cleanup pass over the text. Keep the HTML parser object around instead and only compute the visible/invisible variants inside visible_rendered()/invisible_rendered() when something actually calls them, since not every ruleset uses invisible-text rules. Co-Authored-By: Claude Sonnet 5 --- lib/Mail/SpamAssassin/Message.pm | 1 + lib/Mail/SpamAssassin/Message/Node.pm | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Mail/SpamAssassin/Message.pm b/lib/Mail/SpamAssassin/Message.pm index c830c9f7d1..84137a0524 100644 --- a/lib/Mail/SpamAssassin/Message.pm +++ b/lib/Mail/SpamAssassin/Message.pm @@ -772,6 +772,7 @@ sub finish { delete $part->{'rendered'}; delete $part->{'visible_rendered'}; delete $part->{'invisible_rendered'}; + delete $part->{'html_obj'}; delete $part->{'type'}; delete $part->{'rendered_type'}; delete $part->{'effective_type'}; diff --git a/lib/Mail/SpamAssassin/Message/Node.pm b/lib/Mail/SpamAssassin/Message/Node.pm index c5ee696a7b..7f0b205864 100644 --- a/lib/Mail/SpamAssassin/Message/Node.pm +++ b/lib/Mail/SpamAssassin/Message/Node.pm @@ -860,8 +860,11 @@ sub rendered { # resulting HTML-decoded text is in perl characters (utf8 flag on) $self->{rendered} = $html->get_rendered_text(); - $self->{visible_rendered} = $html->get_rendered_text(invisible => 0); - $self->{invisible_rendered} = $html->get_rendered_text(invisible => 1); + # visible_rendered/invisible_rendered are each a further full pass over + # the rendered text (mask + whitespace cleanup); defer computing them + # until visible_rendered()/invisible_rendered() are actually called, + # which many rulesets never need for a given part. + $self->{html_obj} = $html; $self->{html_results} = $html->get_results(); # end-of-document result values that require looking at the text @@ -950,6 +953,10 @@ Render and return the visible text in this part. sub visible_rendered { my ($self) = @_; $self->rendered(); # ignore return, we want just this: + if (!exists $self->{visible_rendered}) { + $self->{visible_rendered} = $self->{html_obj} + ? $self->{html_obj}->get_rendered_text(invisible => 0) : $self->{rendered}; + } return ($self->{rendered_type}, $self->{visible_rendered}); } @@ -962,6 +969,10 @@ Render and return the invisible text in this part. sub invisible_rendered { my ($self) = @_; $self->rendered(); # ignore return, we want just this: + if (!exists $self->{invisible_rendered}) { + $self->{invisible_rendered} = $self->{html_obj} + ? $self->{html_obj}->get_rendered_text(invisible => 1) : ''; + } return ($self->{rendered_type}, $self->{invisible_rendered}); }