Project

General

Profile

Actions

Bug #15057

closed

REXML::Text#value returns a double unescaped string in non-raw mode

Added by rna (Ryosuke Nanba) over 6 years ago. Updated over 6 years ago.

Status:
Rejected
Target version:
-
ruby -v:
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
[ruby-dev:50625]

Description

REXML::Text オブジェクトが非rawモードの場合、REXML::Text#value がエスケープ済みのテキストを二重にエスケープ解除された文字列を返します。

例:

require 'rexml/document'
t = REXML::Text.new("&lt; <", false, nil, false)
t.to_s   # => "&amp;lt; &lt;"
t.value  # => "< <" (expected: "&lt; <")

REXML::Text#value のコメントに以下のような記述があるため、上の挙動は期待通りのように見えますが、このコメントそのものが誤りだと思います。

    #   t = Text.new( "< & &s; russell", false, nil, false )
    #   t.value   #-> "< & sean russell"

非rawモードではコンストラクタの第一引数に渡された文字列はテキストノードが表す文字列そのものを意味するはずです。上で渡された文字列中の "&s;" は実体参照ではなく単なる3文字のテキストを意味します。t.value は "< & &s; russell" であるべきだと思います。

Updated by kou (Kouhei Sutou) over 6 years ago

  • Status changed from Open to Feedback
  • Assignee set to kou (Kouhei Sutou)

DOMでもnodeValueの値は実体参照を解決した値になるので今の挙動で問題ないように思うんですが、どんなユースケースで使おうとして今の挙動は間違っていると考えたか教えてもらえますか?

非rawモードではコンストラクタの第一引数に渡された文字列はテキストノードが表す文字列そのものを意味するはずです。

こうするべきだと考えた理由というか。

Updated by rna (Ryosuke Nanba) over 6 years ago

DOMでもnodeValueの値は実体参照を解決した値になるので今の挙動で問題ないように思うんですが、

JavaScript の DOM でも Text のコンストラクタ引数で渡した文字列はパースの対象にならずにそのままテキストノードが表す文字列になります。

<p id="p"></p>
<script>
t = new Text("&lt; <");
console.log(t.nodeValue); // コンソールに「&lt; <」が出力
p.appendChild(t);         // 画面に「&lt; <」と表示。
</script>

実体参照が解決されるのは XML 文書実体(ソースコード)からパースする段階の話で、DOM API としては通常パース済みの文字列しか扱いません。

DOM はともかくとして、REXMLの仕様として(非rawモードで) Text のコンストラクタ引数はパースされることになっているのだ、ということであれば、以下の挙動はバグということになってしまいます。

t = REXML::Text.new("&lt; <", false, nil, false)
t.to_s   # => "&amp;lt; &lt;" (expected?: "&lt; &lt;" (or Error?))

どんなユースケースで使おうとして今の挙動は間違っていると考えたか教えてもらえますか?

atomutil 0.1.4 でテキストノードを要素から要素にコピーするユースケースでエスケープが解除されてしまってコピーにならないという現象が発生していました。

https://github.com/lyokato/ruby-atomutil/blob/954c5d1b387bff63db3462d19d3f7abf196d2b1f/lib/atomutil.rb#L526 (elementvalue.elemREXML::Element です):

        element.text = value.elem.text unless value.elem.text.nil?

Updated by kou (Kouhei Sutou) over 6 years ago

なるほど。
atomutilでそうなるケースを試してみたいので再現するAtomとサンプルコードを提供してもらえませんか?

Updated by rna (Ryosuke Nanba) over 6 years ago

atomutilでそうなるケースを試してみたいので再現するAtomとサンプルコードを提供してもらえませんか?

これでどうでしょう?

require 'atomutil'
entry = Atom::Entry.new
entry.content = "&lt;br&gt;"
puts entry.to_s

entry.to_s は実際に投稿する場合 HTTP の request body になります(参照: Atom::Client#create_resource)。

上のサンプルは「HTMLタグを記述可能なテキスト形式での投稿を受理するブログサービス(はてなブログのはてな記法モード等)で、HTMLタグ
の説明を書くためタグをエスケープして記述した」という想定です。ブログサービス側では atom の content 要素の内容をパースしたものをテキスト形式の投稿データとして扱います。

期待する結果はブログエントリに br タグのソースコードが表示されることですが、実際には改行が表示されます(本物の br タグとして機能してしまう)。

ちなみに本件は元々 HatenaBlogWriter v0.5 https://github.com/rnanba/HatenaBlogWriter/tree/v0.5 に対する不具合報告が発端です(master には現在 monkey patch による回避コードが入っています)。はてなブログで実際に試す場合はこちらを参考にしてください。

Updated by kou (Kouhei Sutou) over 6 years ago

ありがとうございます。確認できました。

現状の動きが変だという気持ちはわかるのですが、Text#valueのドキュメントに

This ignores the 'raw' attribute setting

と書いているので、rawのときもそうじゃないときも期待した挙動にするのは、互換性を維持したままではムリなんですよねぇ。

なので、element1.text = element2.textという使い方がよくないんですよねぇ。ただ、こう書けたほうがうれしいので、これに匹敵する使い勝手のAPIを考えておきます。

互換性は壊したくないので、現状ではatomutilでは次のように使ってもらいたいです。

diff --git a/lib/atomutil.rb b/lib/atomutil.rb
index 9bb9e0c..6d9b985 100644
--- a/lib/atomutil.rb
+++ b/lib/atomutil.rb
@@ -523,7 +523,10 @@ module Atom
             element.add_attribute a
           end
         end
-        element.text = value.elem.text unless value.elem.text.nil?
+        text = value.elem.get_text
+        unless text.nil?
+          element.text = REXML::Text.new(text.to_s, true, nil, true)
+        end
       else
         if value.is_a?(REXML::Element)
           element.add_element value.deep_clone

Updated by rna (Ryosuke Nanba) over 6 years ago

This ignores the 'raw' attribute setting

と書いているので、rawのときもそうじゃないときも期待した挙動にするのは、互換性を維持したままではムリなんですよねぇ。

なので、element1.text = element2.textという使い方がよくないんですよねぇ。ただ、こう書けたほうがうれしいので、これに匹敵する使い勝手のAPIを考えておきます。

なるほど。REXML::Element#text も同様のコメントがあってセマンティクスを変えられないと。残念ですが仕方がないですかね… メジャーバージョンアップの際は再検討していただけると幸いです。

互換性は壊したくないので、現状ではatomutilでは次のように使ってもらいたいです。

了解しました。atomutil の方にはこちらから報告しておきます。

Updated by kou (Kouhei Sutou) over 6 years ago

  • Status changed from Feedback to Rejected

了解しました。atomutil の方にはこちらから報告しておきます。

ありがとうございます。では、このチケットはクローズしておきます。

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0