--- a/actionmailer/test/fixtures/helpers/example_helper.rb
+++ b/actionmailer/test/fixtures/helpers/example_helper.rb
@@ -1,5 +1,5 @@
 module ExampleHelper
   def example_format(text)
-    "<em><strong><small>#{h(text)}</small></strong></em>".html_safe!
+    "<em><strong><small>#{h(text)}</small></strong></em>".html_safe
   end
 end
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -37,7 +37,7 @@ module ActionController #:nodoc:
       def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
         if perform_caching
           if cache = read_fragment(name, options)
-            buffer.concat(cache)
+            buffer.safe_concat(cache.html_safe)
           else
             pos = buffer.length
             block.call
--- a/actionpack/lib/action_controller/rack_lint_patch.rb
+++ b/actionpack/lib/action_controller/rack_lint_patch.rb
@@ -1,4 +1,4 @@
-# Rack 1.0 does not allow string subclass body. This does not play well with our ActionView::SafeBuffer.
+# Rack 1.0 does not allow string subclass body. This does not play well with our ActiveSupport::SafeBuffer.
 # The next release of Rack will be allowing string subclass body - http://github.com/rack/rack/commit/de668df02802a0335376a81ba709270e43ba9d55
 # TODO : Remove this monkey patch after the next release of Rack
 
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -49,10 +49,9 @@ module ActionView
   autoload :TemplateHandler, 'action_view/template_handler'
   autoload :TemplateHandlers, 'action_view/template_handlers'
   autoload :Helpers, 'action_view/helpers'
-  autoload :SafeBuffer, 'action_view/safe_buffer'
 end
 
-require 'action_view/erb/util'
+require 'active_support/core_ext/string/output_safety'
 
 
 I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
--- a/actionpack/lib/action_view/erb/util.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'erb'
-
-class ERB
-  module Util
-    HTML_ESCAPE = { '&' => '&amp;',  '>' => '&gt;',   '<' => '&lt;', '"' => '&quot;' }
-    JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
-
-    # A utility method for escaping HTML tag characters.
-    # This method is also aliased as <tt>h</tt>.
-    #
-    # In your ERb templates, use this method to escape any unsafe content. For example:
-    #   <%=h @person.name %>
-    #
-    # ==== Example:
-    #   puts html_escape("is a > 0 & a < 10?")
-    #   # => is a &gt; 0 &amp; a &lt; 10?
-    def html_escape(s)
-      s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
-    end
-
-    undef :h
-    alias h html_escape
-
-    module_function :html_escape
-    module_function :h
-
-    # A utility method for escaping HTML entities in JSON strings.
-    # This method is also aliased as <tt>j</tt>.
-    #
-    # In your ERb templates, use this method to escape any HTML entities:
-    #   <%=j @person.to_json %>
-    #
-    # ==== Example:
-    #   puts json_escape("is a > 0 & a < 10?")
-    #   # => is a \u003E 0 \u0026 a \u003C 10?
-    def json_escape(s)
-      s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
-    end
-
-    alias j json_escape
-    module_function :j
-    module_function :json_escape
-  end
-end
--- a/actionpack/lib/action_view/helpers/active_record_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_record_helper.rb
@@ -3,7 +3,7 @@ require 'action_view/helpers/form_helper
 
 module ActionView
   class Base
-    @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe! }
+    @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
     cattr_accessor :field_error_proc
   end
 
@@ -82,11 +82,11 @@ module ActionView
         submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
 
         contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
-        contents << hidden_field(record_name, :id) unless record.new_record?
-        contents << all_input_tags(record, record_name, options)
+        contents.safe_concat hidden_field(record_name, :id) unless record.new_record?
+        contents.safe_concat all_input_tags(record, record_name, options)
         yield contents if block_given?
-        contents << submit_tag(submit_value)
-        contents << '</form>'
+        contents.safe_concat submit_tag(submit_value)
+        contents.safe_concat '</form>'
       end
 
       # Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
@@ -290,7 +290,7 @@ module ActionView
       end
 
       def error_wrapping(html_tag, has_error)
-        has_error ? Base.field_error_proc.call(html_tag, self).html_safe! : html_tag
+        has_error ? Base.field_error_proc.call(html_tag, self) : html_tag
       end
 
       def error_message
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -285,7 +285,7 @@ module ActionView
           end
           javascript_src_tag(joined_javascript_name, options)
         else
-          expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe!
+          expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe
         end
       end
 
@@ -434,7 +434,7 @@ module ActionView
           end
           stylesheet_tag(joined_stylesheet_name, options)
         else
-          expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe!
+          expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe
         end
       end
 
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -118,13 +118,13 @@ module ActionView
       def content_for(name, content = nil, &block)
         ivar = "@content_for_#{name}"
         content = capture(&block) if block_given?
-        instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}".html_safe!)
+        instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}".html_safe)
         nil
       end
 
       # Use an alternate output buffer for the duration of the block.
       # Defaults to a new empty string.
-      def with_output_buffer(buf = "") #:nodoc:
+      def with_output_buffer(buf = ActiveSupport::SafeBuffer.new) #:nodoc:
         self.output_buffer, old_buffer = buf, output_buffer
         yield
         output_buffer
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -616,7 +616,7 @@ module ActionView
 
           build_selects_from_types(order)
         else
-          "#{select_date}#{@options[:datetime_separator]}#{select_time}"
+          "#{select_date}#{@options[:datetime_separator]}#{select_time}".html_safe
         end
       end
 
@@ -835,7 +835,7 @@ module ActionView
           select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
           select_html << select_options_as_html.to_s
 
-          content_tag(:select, select_html, select_options) + "\n"
+          (content_tag(:select, select_html, select_options) + "\n").html_safe
         end
 
         # Builds a prompt option tag with supplied options or from default options
@@ -860,12 +860,12 @@ module ActionView
         #  build_hidden(:year, 2008)
         #  => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
         def build_hidden(type, value)
-          tag(:input, {
+          (tag(:input, {
             :type => "hidden",
             :id => input_id_from_type(type),
             :name => input_name_from_type(type),
             :value => value
-          }) + "\n"
+          }) + "\n").html_safe
         end
 
         # Returns the name attribute for the input tag
@@ -896,7 +896,7 @@ module ActionView
             separator = separator(type) unless type == order.first # don't add on last field
             select.insert(0, separator.to_s + send("select_#{type}").to_s)
           end
-          select
+          select.html_safe
         end
 
         # Returns the separator for a given datetime component
@@ -916,15 +916,15 @@ module ActionView
 
     class InstanceTag #:nodoc:
       def to_date_select_tag(options = {}, html_options = {})
-        datetime_selector(options, html_options).select_date.html_safe!
+        datetime_selector(options, html_options).select_date.html_safe
       end
 
       def to_time_select_tag(options = {}, html_options = {})
-        datetime_selector(options, html_options).select_time.html_safe!
+        datetime_selector(options, html_options).select_time.html_safe
       end
 
       def to_datetime_select_tag(options = {}, html_options = {})
-        datetime_selector(options, html_options).select_datetime.html_safe!
+        datetime_selector(options, html_options).select_datetime.html_safe
       end
 
       private
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -27,10 +27,10 @@ module ActionView
       def debug(object)
         begin
           Marshal::dump(object)
-          "<pre class='debug_dump'>#{h(object.to_yaml).gsub("  ", "&nbsp; ")}</pre>"
+          "<pre class='debug_dump'>#{h(object.to_yaml).gsub("  ", "&nbsp; ")}</pre>".html_safe
         rescue Exception => e  # errors from Marshal or YAML
           # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
-          "<code class='debug_dump'>#{h(object.inspect)}</code>"
+          "<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe
         end
       end
     end
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -280,7 +280,7 @@ module ActionView
 
         concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
         fields_for(object_name, *(args << options), &proc)
-        concat('</form>'.html_safe!)
+        concat('</form>')
       end
 
       def apply_form_for_options!(object_or_array, options) #:nodoc:
@@ -797,7 +797,7 @@ module ActionView
         add_default_name_and_id(options)
         hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
         checkbox = tag("input", options)
-        (hidden + checkbox).html_safe!
+        (hidden + checkbox).html_safe
       end
 
       def to_boolean_select_tag(options = {})
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -297,7 +297,7 @@ module ActionView
           options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}>#{html_escape(text.to_s)}</option>)
         end
 
-        options_for_select.join("\n").html_safe!
+        options_for_select.join("\n").html_safe
       end
 
       # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -432,7 +432,7 @@ module ActionView
         concat(tag(:fieldset, options, true))
         concat(content_tag(:legend, legend)) unless legend.blank?
         concat(content)
-        concat("</fieldset>".html_safe!)
+        concat("</fieldset>")
       end
 
       private
@@ -459,14 +459,14 @@ module ActionView
 
         def form_tag_html(html_options)
           extra_tags = extra_tags_for_form(html_options)
-          (tag(:form, html_options, true) + extra_tags).html_safe!
+          (tag(:form, html_options, true) + extra_tags).html_safe
         end
 
         def form_tag_in_block(html_options, &block)
           content = capture(&block)
           concat(form_tag_html(html_options))
           concat(content)
-          concat("</form>".html_safe!)
+          concat("</form>")
         end
 
         def token_tag
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -89,7 +89,7 @@ module ActionView
             :precision => precision,
             :delimiter => delimiter,
             :separator => separator)
-          ).gsub(/%u/, unit)
+          ).gsub(/%u/, unit).html_safe
         rescue
           number
         end
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -393,7 +393,7 @@ module ActionView
 
         concat(form_remote_tag(options))
         fields_for(object_name, *(args << options), &proc)
-        concat('</form>'.html_safe!)
+        concat('</form>')
       end
       alias_method :form_remote_for, :remote_form_for
 
@@ -1026,7 +1026,7 @@ module ActionView
       #     page.hide 'spinner'
       #   end
       def update_page(&block)
-        JavaScriptGenerator.new(@template, &block).to_s
+        JavaScriptGenerator.new(@template, &block).to_s.html_safe
       end
 
       # Works like update_page but wraps the generated JavaScript in a <script>
--- a/actionpack/lib/action_view/helpers/raw_output_helper.rb
+++ b/actionpack/lib/action_view/helpers/raw_output_helper.rb
@@ -2,8 +2,8 @@ module ActionView #:nodoc:
   module Helpers #:nodoc:
     module RawOutputHelper
       def raw(stringish)
-        stringish.to_s.html_safe!
+        stringish.to_s.html_safe
       end
     end
   end
-end
\ No newline at end of file
+end
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -49,11 +49,7 @@ module ActionView
       # confuse browsers.
       #
       def sanitize(html, options = {})
-        returning self.class.white_list_sanitizer.sanitize(html, options) do |sanitized|
-          if sanitized
-            sanitized.html_safe!
-          end
-        end
+        self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe)
       end
 
       # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
@@ -76,11 +72,7 @@ module ActionView
       #   strip_tags("<div id='top-bar'>Welcome to my website!</div>")
       #   # => Welcome to my website!
       def strip_tags(html)
-        returning self.class.full_sanitizer.sanitize(html) do |sanitized|
-          if sanitized
-            sanitized.html_safe!
-          end
-        end
+        self.class.full_sanitizer.sanitize(html).try(:html_safe)
       end
 
       # Strips all link tags from +text+ leaving just the link text.
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -1,4 +1,4 @@
-require 'action_view/erb/util'
+require 'active_support/core_ext/string/output_safety'
 require 'set'
 
 module ActionView
@@ -38,7 +38,7 @@ module ActionView
       #   tag("img", { :src => "open &amp; shut.png" }, false, false)
       #   # => <img src="open &amp; shut.png" />
       def tag(name, options = nil, open = false, escape = true)
-        "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe!
+        "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
       end
 
       # Returns an HTML block tag of type +name+ surrounding the +content+. Add
@@ -91,7 +91,7 @@ module ActionView
       #   cdata_section(File.read("hello_world.txt"))
       #   # => <![CDATA[<hello from a text file]]>
       def cdata_section(content)
-        "<![CDATA[#{content}]]>".html_safe!
+        "<![CDATA[#{content}]]>".html_safe
       end
 
       # Returns an escaped version of +html+ without affecting existing escaped entities.
@@ -125,7 +125,7 @@ module ActionView
 
         def content_tag_string(name, content, options, escape = true)
           tag_options = tag_options(options, escape) if options
-          "<#{name}#{tag_options}>#{content}</#{name}>".html_safe!
+          "<#{name}#{tag_options}>#{content}</#{name}>".html_safe
         end
 
         def tag_options(options, escape = true)
@@ -142,7 +142,7 @@ module ActionView
             else
               attrs = options.map { |key, value| %(#{key}="#{value}") }
             end
-            " #{attrs.sort * ' '}".html_safe! unless attrs.empty?
+            " #{attrs.sort * ' '}".html_safe unless attrs.empty?
           end
         end
     end
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -29,7 +29,7 @@ module ActionView
           ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed.  Please remove it from your views and helpers.", caller)
         end
 
-        output_buffer << string
+        output_buffer.safe_concat(string)
       end
 
       # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -12,7 +12,7 @@ module ActionView
       # prepend the key with a period, nothing is converted.
       def translate(key, options = {})
         options[:raise] = true
-        I18n.translate(scope_key_by_partial(key), options)
+        I18n.translate(scope_key_by_partial(key), options).html_safe
       rescue I18n::MissingTranslationData => e
         keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
         content_tag('span', keys.join(', '), :class => 'translation_missing')
@@ -36,4 +36,4 @@ module ActionView
         end
     end
   end
-end
\ No newline at end of file
+end
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -91,7 +91,7 @@ module ActionView
           polymorphic_path(options)
         end
 
-        escape ? escape_once(url) : url
+        escape ? escape_once(url).html_safe : url
       end
 
       # Creates a link tag of the given +name+ using a URL created by the set
@@ -219,7 +219,7 @@ module ActionView
         if block_given?
           options      = args.first || {}
           html_options = args.second
-          concat(link_to(capture(&block), options, html_options).html_safe!)
+          concat(link_to(capture(&block), options, html_options))
         else
           name         = args.first
           options      = args.second || {}
@@ -237,7 +237,7 @@ module ActionView
           end
 
           href_attr = "href=\"#{url}\"" unless href
-          "<a #{href_attr}#{tag_options}>#{name || url}</a>".html_safe!
+          "<a #{href_attr}#{tag_options}>#{name || url}</a>".html_safe
         end
       end
 
@@ -309,7 +309,7 @@ module ActionView
         html_options.merge!("type" => "submit", "value" => name)
 
         "<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" +
-          method_tag + tag("input", html_options) + request_token_tag + "</div></form>".html_safe!
+          method_tag + tag("input", html_options) + request_token_tag + "</div></form>".html_safe
       end
 
 
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -221,7 +221,7 @@ module ActionView
           result = template.render_partial(self, object, local_assigns.dup, as)
           index += 1
           result
-        end.join(spacer).html_safe!
+        end.join(spacer).html_safe
       end
 
       def _pick_partial_template(partial_path) #:nodoc:
--- a/actionpack/lib/action_view/safe_buffer.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-
-module ActionView #:nodoc:
-  class SafeBuffer < String
-    def <<(value)
-      if value.html_safe?
-        super(value)
-      else
-        super(ERB::Util.h(value))
-      end
-    end
-
-    def concat(value)
-      self << value
-    end
-
-    def html_safe?
-      true
-    end
-
-    def html_safe!
-      self
-    end
-
-    def to_s
-      self
-    end
-  end
-end
\ No newline at end of file
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -53,7 +53,7 @@ module ActionView
     setup :setup_with_controller
     def setup_with_controller
       @controller = TestController.new
-      @output_buffer = ''
+      @output_buffer = ActiveSupport::SafeBuffer.new
       @rendered = ''
 
       self.class.send(:include_helper_modules!)
@@ -159,4 +159,4 @@ module ActionView
         end
       end
   end
-end
\ No newline at end of file
+end
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -604,7 +604,7 @@ class FragmentCachingTest < ActionContro
     @store.write('views/expensive', 'fragment content')
     fragment_computed = false
 
-    buffer = 'generated till now -> '
+    buffer = 'generated till now -> '.html_safe
     @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
 
     assert fragment_computed
@@ -615,7 +615,7 @@ class FragmentCachingTest < ActionContro
     @store.write('views/expensive', 'fragment content')
     fragment_computed = false
 
-    buffer = 'generated till now -> '
+    buffer = 'generated till now -> '.html_safe
     @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
 
     assert !fragment_computed
--- /dev/null
+++ b/actionpack/test/controller/output_escaping_test.rb
@@ -0,0 +1,19 @@
+require 'abstract_unit'
+
+class OutputEscapingTest < ActiveSupport::TestCase
+
+  test "escape_html shouldn't die when passed nil" do
+    assert ERB::Util.h(nil).blank?
+  end
+
+  test "escapeHTML should escape strings" do
+    assert_equal "&lt;&gt;&quot;", ERB::Util.h("<>\"")
+  end
+
+  test "escapeHTML shouldn't touch explicitly safe strings" do
+    # TODO this seems easier to compose and reason about, but
+    # this should be verified
+    assert_equal "<", ERB::Util.h("<".html_safe)
+  end
+
+end
--- a/actionpack/test/template/erb_util_test.rb
+++ b/actionpack/test/template/erb_util_test.rb
@@ -14,6 +14,18 @@ class ErbUtilTest < Test::Unit::TestCase
       end
     end
   end
+
+  def test_html_escape_is_html_safe
+    escaped = h("<p>")
+    assert_equal "&lt;p&gt;", escaped
+    assert escaped.html_safe?
+  end
+
+  def test_html_escape_passes_html_escpe_unmodified
+    escaped = h("<p>".html_safe)
+    assert_equal "<p>", escaped
+    assert escaped.html_safe?
+  end
   
   def test_rest_in_ascii
     (0..127).to_a.map(&:chr).each do |chr|
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -1104,7 +1104,7 @@ class FormHelperTest < ActionView::TestC
     (field_helpers - %w(hidden_field)).each do |selector|
       src = <<-END_SRC
         def #{selector}(field, *args, &proc)
-          ("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe!
+          ("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe
         end
       END_SRC
       class_eval src, __FILE__, __LINE__
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -313,19 +313,19 @@ class FormTagHelperTest < ActionView::Te
     expected = %(<fieldset><legend>Your details</legend>Hello world!</fieldset>)
     assert_dom_equal expected, output_buffer
 
-    self.output_buffer = ''
+    self.output_buffer = ''.html_safe
     field_set_tag { concat "Hello world!" }
 
     expected = %(<fieldset>Hello world!</fieldset>)
     assert_dom_equal expected, output_buffer
 
-    self.output_buffer = ''
+    self.output_buffer = ''.html_safe
     field_set_tag('') { concat "Hello world!" }
 
     expected = %(<fieldset>Hello world!</fieldset>)
     assert_dom_equal expected, output_buffer
 
-    self.output_buffer = ''
+    self.output_buffer = ''.html_safe
     field_set_tag('', :class => 'format') { concat "Hello world!" }
 
     expected = %(<fieldset class="format">Hello world!</fieldset>)
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -17,7 +17,7 @@ class TextHelperTest < ActionView::TestC
   end
 
   def test_concat
-    self.output_buffer = 'foo'
+    self.output_buffer = ActiveSupport::SafeBuffer.new('foo')
     assert_equal 'foobar', concat('bar')
     assert_equal 'foobar', output_buffer
   end
--- a/actionpack/test/view/safe_buffer_test.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require 'abstract_unit'
-
-class SafeBufferTest < ActionView::TestCase
-  def setup
-    @buffer = ActionView::SafeBuffer.new
-  end
-
-  test "Should look like a string" do
-    assert @buffer.is_a?(String)
-    assert_equal "", @buffer
-  end
-
-  test "Should escape a raw string which is passed to them" do
-    @buffer << "<script>"
-    assert_equal "&lt;script&gt;", @buffer
-  end
-
-  test "Should NOT escape a safe value passed to it" do
-    @buffer << "<script>".html_safe!
-    assert_equal "<script>", @buffer
-  end
-
-  test "Should not mess with an innocuous string" do
-    @buffer << "Hello"
-    assert_equal "Hello", @buffer
-  end
-
-  test "Should be considered safe" do
-    assert @buffer.html_safe?
-  end
-
-  test "Should return a safe buffer when calling to_s" do
-    new_buffer = @buffer.to_s
-    assert_equal ActionView::SafeBuffer, new_buffer.class
-  end
-end
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -44,6 +44,7 @@ module ActiveSupport
   autoload :OrderedHash, 'active_support/ordered_hash'
   autoload :OrderedOptions, 'active_support/ordered_options'
   autoload :Rescuable, 'active_support/rescuable'
+  autoload :SafeBuffer, 'active_support/core_ext/string/output_safety'
   autoload :SecureRandom, 'active_support/secure_random'
   autoload :StringInquirer, 'active_support/string_inquirer'
   autoload :TimeWithZone, 'active_support/time_with_zone'
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -21,5 +21,4 @@ class String #:nodoc:
   include ActiveSupport::CoreExtensions::String::Iterators
   include ActiveSupport::CoreExtensions::String::Behavior
   include ActiveSupport::CoreExtensions::String::Multibyte
-  include ActiveSupport::CoreExtensions::String::OutputSafety
 end
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -1,48 +1,121 @@
+require 'erb'
+
+class ERB
+  undef :set_eoutvar
+  def set_eoutvar(compiler, eoutvar = '_erbout')
+    compiler.put_cmd = "#{eoutvar}.safe_concat"
+    compiler.insert_cmd = "#{eoutvar}.safe_concat"
+
+    cmd = []
+    cmd.push "#{eoutvar} = ActiveSupport::SafeBuffer.new"
+
+    compiler.pre_cmd = cmd
+
+    cmd = []
+    cmd.push(eoutvar)
+
+    compiler.post_cmd = cmd
+  end
+
+  module Util
+    HTML_ESCAPE = { '&' => '&amp;',  '>' => '&gt;',   '<' => '&lt;', '"' => '&quot;' }
+    JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
+
+    # A utility method for escaping HTML tag characters.
+    # This method is also aliased as <tt>h</tt>.
+    #
+    # In your ERb templates, use this method to escape any unsafe content. For example:
+    #   <%=h @person.name %>
+    #
+    # ==== Example:
+    #   puts html_escape("is a > 0 & a < 10?")
+    #   # => is a &gt; 0 &amp; a &lt; 10?
+    def html_escape(s)
+      s = s.to_s
+      if s.html_safe?
+        s
+      else
+        s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe
+      end
+    end
+
+    undef :h
+    alias h html_escape
+
+    module_function :html_escape
+    module_function :h
+
+    # A utility method for escaping HTML entities in JSON strings.
+    # This method is also aliased as <tt>j</tt>.
+    #
+    # In your ERb templates, use this method to escape any HTML entities:
+    #   <%=j @person.to_json %>
+    #
+    # ==== Example:
+    #   puts json_escape("is a > 0 & a < 10?")
+    #   # => is a \u003E 0 \u0026 a \u003C 10?
+    def json_escape(s)
+      s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
+    end
+
+    alias j json_escape
+    module_function :j
+    module_function :json_escape
+  end
+end
+
+class Object
+  def html_safe?
+    false
+  end
+end
+
+class Fixnum
+  def html_safe?
+    true
+  end
+end
+
 module ActiveSupport #:nodoc:
-  module CoreExtensions #:nodoc:
-    module String #:nodoc:
-      module OutputSafety
-        def self.included(base)
-          base.class_eval do
-            alias_method :add_without_safety, :+
-            alias_method :+, :add_with_safety
-            alias_method_chain :concat, :safety
-            undef_method :<<
-            alias_method :<<, :concat_with_safety
-          end
-        end
-
-        def html_safe?
-          defined?(@_rails_html_safe) && @_rails_html_safe
-        end
-
-        def html_safe!
-          @_rails_html_safe = true
-          self
-        end
-
-        def add_with_safety(other)
-          result = add_without_safety(other)
-          if html_safe? && also_html_safe?(other)
-            result.html_safe!
-          else
-            result
-          end
-        end
-
-        def concat_with_safety(other_or_fixnum)
-          result = concat_without_safety(other_or_fixnum)
-          unless html_safe? && also_html_safe?(other_or_fixnum)
-            @_rails_html_safe = false
-          end
-          result
-        end
-
-        private
-          def also_html_safe?(other)
-            other.respond_to?(:html_safe?) && other.html_safe?
-          end
+  class SafeBuffer < String
+    alias safe_concat concat
+
+    def concat(value)
+      if value.html_safe?
+        super(value)
+      else
+        super(ERB::Util.h(value))
       end
     end
+
+    def +(other)
+      dup.concat(other)
+    end
+
+    def <<(value)
+      self.concat(value)
+    end
+
+    def html_safe?
+      true
+    end
+
+    def html_safe
+      self
+    end
+
+    def to_s
+      self
+    end
+  end
+end
+
+class String
+  def html_safe!
+    raise "Use html_safe with your strings instead of html_safe! See http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/ for the full story."
+  end
+
+  def html_safe
+    ActiveSupport::SafeBuffer.new(self)
   end
-end
\ No newline at end of file
+end
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -292,53 +292,53 @@ class OutputSafetyTest < ActiveSupport::
   end
 
   test "A string can be marked safe" do
-    @string.html_safe!
-    assert @string.html_safe?
+    string = @string.html_safe
+    assert string.html_safe?
   end
 
   test "Marking a string safe returns the string" do
-    assert_equal @string, @string.html_safe!
+    assert_equal @string, @string.html_safe
   end
 
   test "Adding a safe string to another safe string returns a safe string" do
-    @other_string = "other".html_safe!
-    @string.html_safe!
-    @combination = @other_string + @string
+    @other_string = "other".html_safe
+    string = @string.html_safe
+    @combination = @other_string + string
 
     assert_equal "otherhello", @combination
     assert @combination.html_safe?
   end
 
-  test "Adding an unsafe string to a safe string returns an unsafe string" do
-    @other_string = "other".html_safe!
-    @combination = @other_string + @string
-    @other_combination = @string + @other_string
+  test "Adding an unsafe string to a safe string escapes it and returns a safe string" do
+    @other_string = "other".html_safe
+    @combination = @other_string + "<foo>"
+    @other_combination = @string + "<foo>"
 
-    assert_equal "otherhello", @combination
-    assert_equal "helloother", @other_combination
+    assert_equal "other&lt;foo&gt;", @combination
+    assert_equal "hello<foo>", @other_combination
 
-    assert !@combination.html_safe?
+    assert @combination.html_safe?
     assert !@other_combination.html_safe?
   end
 
   test "Concatting safe onto unsafe yields unsafe" do
     @other_string = "other"
-    @string.html_safe!
+    @string.html_safe
 
     @other_string.concat(@string)
     assert !@other_string.html_safe?
   end
 
-  test "Concatting unsafe onto safe yields unsafe" do
-    @other_string = "other".html_safe!
-
-    @other_string.concat(@string)
-    assert !@other_string.html_safe?
+  test "Concatting unsafe onto safe yields escaped safe" do
+    @other_string = "other".html_safe
+    string = @other_string.concat("<foo>")
+    assert_equal "other&lt;foo&gt;", string
+    assert string.html_safe?
   end
 
   test "Concatting safe onto safe yields safe" do
-    @other_string = "other".html_safe!
-    @string.html_safe!
+    @other_string = "other".html_safe
+    @string.html_safe
 
     @other_string.concat(@string)
     assert @other_string.html_safe?
@@ -346,24 +346,31 @@ class OutputSafetyTest < ActiveSupport::
 
   test "Concatting safe onto unsafe with << yields unsafe" do
     @other_string = "other"
-    @string.html_safe!
+    @string.html_safe
 
     @other_string << @string
     assert !@other_string.html_safe?
   end
 
-  test "Concatting unsafe onto safe with << yields unsafe" do
-    @other_string = "other".html_safe!
-
-    @other_string << @string
-    assert !@other_string.html_safe?
+  test "Concatting unsafe onto safe with << yields escaped safe" do
+    @other_string = "other".html_safe
+    string = @other_string << "<foo>"
+    assert_equal "other&lt;foo&gt;", string
+    assert string.html_safe?
   end
 
   test "Concatting safe onto safe with << yields safe" do
-    @other_string = "other".html_safe!
-    @string.html_safe!
+    @other_string = "other".html_safe
+    @string.html_safe
 
     @other_string << @string
     assert @other_string.html_safe?
   end
+
+  test "Concatting a fixnum to safe always yields safe" do
+    string = @string.html_safe
+    string = string.concat(13)
+    assert_equal "hello".concat(13), string
+    assert string.html_safe?
+  end
 end
--- /dev/null
+++ b/activesupport/test/safe_buffer_test.rb
@@ -0,0 +1,36 @@
+require 'abstract_unit'
+
+class SafeBufferTest < ActiveSupport::TestCase
+  def setup
+    @buffer = ActiveSupport::SafeBuffer.new
+  end
+
+  test "Should look like a string" do
+    assert @buffer.is_a?(String)
+    assert_equal "", @buffer
+  end
+
+  test "Should escape a raw string which is passed to them" do
+    @buffer << "<script>"
+    assert_equal "&lt;script&gt;", @buffer
+  end
+
+  test "Should NOT escape a safe value passed to it" do
+    @buffer << "<script>".html_safe
+    assert_equal "<script>", @buffer
+  end
+
+  test "Should not mess with an innocuous string" do
+    @buffer << "Hello"
+    assert_equal "Hello", @buffer
+  end
+
+  test "Should be considered safe" do
+    assert @buffer.html_safe?
+  end
+
+  test "Should return a safe buffer when calling to_s" do
+    new_buffer = @buffer.to_s
+    assert_equal ActiveSupport::SafeBuffer, new_buffer.class
+  end
+end
