NAME
Developer::Dashboard::HtmlEscape - shared HTML-escaping helpers
SYNOPSIS
use Developer::Dashboard::HtmlEscape qw(_escape_html _escape_html_attr);
my $body = _escape_html($raw_text); # for HTML body content
my $attr = _escape_html_attr($raw_value); # for a quoted attribute
DESCRIPTION
Provides _escape_html and _escape_html_attr, the single home for two HTML-escaping helpers that used to be written out identically in Developer::Dashboard::Web::App and Developer::Dashboard::Zipper (DD-898) - the same "small helper reimplemented per file instead of shared" pattern this project already fixed several times (DD-762, DD-785, DD-888, DD-891, DD-894). Notably, this specific duplication was created deliberately: DD-892 mirrored Web::App's existing pair into Zipper as its own private copy rather than extracting a shared module at the time - a duplication being intentional when created does not mean it should stay duplicated.
When the distribution's XS extension is built, body strings with no escapable bytes are returned unchanged. Inputs of at most 64 characters with an escape, or longer inputs with at least four escapable bytes in the first 16 characters, use the direct compiled C loop. Longer strings with fewer early escapes use a native chunk-copy pass; this avoids sending long, early-but-rare input through the direct loop. A source checkout without the compiled extension applies the equivalent Perl substitutions directly in _escape_html, avoiding a second helper dispatch on that path. Overloaded references use Perl stringification so native scalar conversion does not change their behavior. A missing extension is the only load condition that selects the documented Perl path; other XS load errors remain visible. Attribute values use a separate single-pass XS loop that handles body metacharacters and both quote characters together. All XS paths retain the Perl UTF-8 flag and taint state.
PURPOSE
Give every module that needs to escape text for HTML output one canonical pair of implementations to call, rather than each maintaining its own private copy that can silently drift.
WHY IT EXISTS
Web::App's and Zipper's escaping pairs were byte-for-byte identical, with no reason for the two copies to ever diverge and no mechanism to notice if one changed and the other did not. Extracting the shared behavior removes that latent-drift risk before an edit to one copy silently stops matching the other - the same reasoning that produced TextUtils.pm (DD-891) and TimeUtils.pm (DD-894).
WHEN TO USE
Any module in this codebase that needs to escape text for HTML body content or a quoted HTML attribute should use this module rather than writing private _escape_html/_escape_html_attr subs.
HOW TO USE
use Developer::Dashboard::HtmlEscape qw(_escape_html _escape_html_attr);
my $safe_body = _escape_html($raw_text);
my $safe_attr = _escape_html_attr($raw_value);
_escape_html_attr uses the single-pass native implementation for scalar inputs when XS is loaded, and the Perl implementation for overloaded references or source-only operation. In the Perl path, plain scalar attribute strings of at least twelve characters return after a metacharacter scan when no escaping is needed; shorter values keep the substitution path. Use it for anything placed inside a quoted HTML attribute, and _escape_html alone for plain body content.
WHAT USES IT
Developer::Dashboard::Web::App and Developer::Dashboard::Zipper, at the call sites the DD-898 extraction migrated.
EXAMPLES
_escape_html('<x>') # '<x>'
_escape_html_attr('"quoted"') # '"quoted"'