HTML コードブロック内のエンティティがエディタ読み込み時に破損する問題を修正
Rails の content_tag における HTML エンティティの扱いが原因で、<pre> ブロック内の < などのエンティティがエディタ読み込み時に実際の HTML タグとして解釈されてしまう問題が修正されました。html_safe 属性を除去することで、属性値のエスケープ処理を正常化しています。
背景
Lexxy エディタでは、HTML コードサンプルを <pre> ブロック内に表示する際、<div> のような HTML エンティティを使用します。この値が lexxy_rich_textarea_tag に渡される際、Rails の render() が返す html_safe 文字列として処理されていました。
content_tag は html_safe な値を属性値として扱う場合、HTML エスケープを行いません。そのため、< が < のまま HTML 属性に書き込まれ、ブラウザが属性値を読み取る際に < として解釈されてしまいます。これにより、コードサンプルとして表示すべき文字列が実際の HTML タグとして認識され、エディタの表示が破損していました。
技術的な変更
lib/lexxy/rich_text_area_tag.rb の lexxy_rich_textarea_tag メソッドに、html_safe 属性を除去する処理が追加されました。
変更後:
value = render_custom_attachments_in(value)
# remove the html_safe attribute to preserve attribute escape
value = value.to_str if value.respond_to? :to_str
options[:name] ||= name
options[:value] ||= value
to_str メソッドを呼び出すことで、ActiveSupport::SafeBuffer から通常の String に変換されます。これにより、後続の content_tag 呼び出し時に < が &lt; として二重エスケープされ、ブラウザが属性値を読み取る際に < として正しく保持されるようになります。
テストでは以下のケースが追加され、HTML エンティティが保持されることを検証しています:
test "#lexxy_rich_textarea_tag preserves HTML entities in code blocks" do
code_html = '<pre data-language="html"><div>test</div></pre>'.html_safe
render inline: <<~ERB, locals: { code_html: code_html }
<%= lexxy_rich_textarea_tag :body, code_html %>
ERB
assert_dom "lexxy-editor", count: 1 do |lexxy_editor, *|
value = lexxy_editor["value"]
assert_includes value, "<div>"
assert_not_includes value, "<div>"
end
end
設計判断
html_safe 属性の除去 という最小限の変更で問題を解決しています。
PR の調査によると、以前のコードでは value = "<div>#{value}</div>" のような文字列補間が行われており、これが html_safe を解除する副作用を持っていました。この処理が削除された際、html_safe な値がそのまま content_tag に渡されるようになり、エスケープ動作が変化したことが問題の原因です。
to_str による変換は、Rails の content_tag のエスケープロジックを意図的に活用する設計です。通常の文字列として扱うことで、Rails の標準的な HTML エスケープ処理が適用され、属性値内のエンティティが正しく二重エスケープされます。respond_to? チェックにより、html_safe でない値に対しても安全に動作します。
まとめ
本 PR は、Rails の content_tag における html_safe 文字列の扱いに起因する HTML エンティティの破損問題を修正しています。to_str による html_safe 属性の除去という 2 行の追加により、エディタ内のコードブロックが正しく表示されるようになりました。Rails のエスケープメカニズムを活用した、副作用の少ない解決策といえます。