diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 27b0c91d0e..8a6657b1b8 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -126,7 +126,8 @@ module ActionView def content_tag_string(name, content, options, escape = true) tag_options = tag_options(options, escape) if options - "<#{name}#{tag_options}>#{escape ? ERB::Util.html_escape(content) : content}".html_safe + content = ERB::Util.unwrapped_html_escape(content) if escape + "<#{name}#{tag_options}>#{content}".html_safe end def tag_options(options, escape = true) @@ -137,7 +138,7 @@ module ActionView attrs << %(#{key}="#{key}") if value elsif !value.nil? final_value = value.is_a?(Array) ? value.join(" ") : value - final_value = html_escape(final_value) if escape + final_value = ERB::Util.unwrapped_html_escape(final_value) if escape attrs << %(#{key}="#{final_value}") end end diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index a4557e851d..a89d3ae6a0 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/kernel/singleton_class' class ERB module Util HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' } + HTML_ESCAPE_REGEXP = /[&"'><]/ JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' } HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/ JSON_ESCAPE_REGEXP = /[&"><]/ @@ -18,12 +19,7 @@ class ERB # # => is a > 0 & a < 10? if RUBY_VERSION > '1.9' def html_escape(s) - s = s.to_s - if s.html_safe? - s - else - s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe - end + unwrapped_html_escape(s).html_safe end else def html_escape(s) @@ -31,7 +27,7 @@ class ERB if s.html_safe? s else - s.gsub(/[&"'><]/){ |x| HTML_ESCAPE[x] }.html_safe + s.gsub(HTML_ESCAPE_REGEXP){ |x| HTML_ESCAPE[x] }.html_safe end end end @@ -45,6 +41,18 @@ class ERB singleton_class.send(:remove_method, :html_escape) module_function :html_escape + # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer. + # This method is not for public consumption! Seriously! + def unwrapped_html_escape(s) # :nodoc: + s = s.to_s + if s.html_safe? + s + else + s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE) + end + end + module_function :unwrapped_html_escape + # A utility method for escaping HTML without affecting existing escaped entities. # # html_escape_once('1 < 2 & 3')