Link preloading should keep integrity hashes in the header

When a stylesheet or javascript link tag (or preload link tag) is output
in the view, it also gets sent as a Link header for preloading. This
Link header is missing the integrity attribute even when one is set on
the tag, which prevents the browser from using the preloaded resource.

Co-authored-by: Adrianna Chang <adrianna.chang@shopify.com>
This commit is contained in:
Étienne Barrié 2020-12-14 15:02:57 -05:00
parent 446bf0a91d
commit 4aa91262f0
2 changed files with 16 additions and 1 deletions

View File

@ -90,12 +90,14 @@ module ActionView
nopush = options["nopush"].nil? ? true : options.delete("nopush")
crossorigin = options.delete("crossorigin")
crossorigin = "anonymous" if crossorigin == true
integrity = options["integrity"]
sources_tags = sources.uniq.map { |source|
href = path_to_javascript(source, path_options)
unless options["defer"]
preload_link = "<#{href}>; rel=preload; as=script"
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
preload_link += "; integrity=#{integrity}" unless integrity.nil?
preload_link += "; nopush" if nopush
preload_links << preload_link
end
@ -149,11 +151,13 @@ module ActionView
crossorigin = options.delete("crossorigin")
crossorigin = "anonymous" if crossorigin == true
nopush = options["nopush"].nil? ? true : options.delete("nopush")
integrity = options["integrity"]
sources_tags = sources.uniq.map { |source|
href = path_to_stylesheet(source, path_options)
preload_link = "<#{href}>; rel=preload; as=style"
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
preload_link += "; integrity=#{integrity}" unless integrity.nil?
preload_link += "; nopush" if nopush
preload_links << preload_link
tag_options = {
@ -256,6 +260,7 @@ module ActionView
# * <tt>:as</tt> - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type.
# * <tt>:crossorigin</tt> - Specify the crossorigin attribute, required to load cross-origin resources.
# * <tt>:nopush</tt> - Specify if the use of server push is not desired for the resource. Defaults to +false+.
# * <tt>:integrity</tt> - Specify the integrity attribute.
#
# ==== Examples
#
@ -287,6 +292,7 @@ module ActionView
as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
crossorigin = options.delete(:crossorigin)
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
integrity = options[:integrity]
nopush = options.delete(:nopush) || false
link_tag = tag.link(**{
@ -300,6 +306,7 @@ module ActionView
preload_link = "<#{href}>; rel=preload; as=#{as_type}"
preload_link += "; type=#{mime_type}" if mime_type
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
preload_link += "; integrity=#{integrity}" if integrity
preload_link += "; nopush" if nopush
send_preload_links_header([preload_link])

View File

@ -239,7 +239,8 @@ class AssetTagHelperTest < ActionView::TestCase
%(preload_link_tag '//example.com/map?callback=initMap', as: 'fetch', type: 'application/javascript') => %(<link rel="preload" href="//example.com/map?callback=initMap" as="fetch" type="application/javascript" />),
%(preload_link_tag '//example.com/font.woff2') => %(<link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="anonymous"/>),
%(preload_link_tag '//example.com/font.woff2', crossorigin: 'use-credentials') => %(<link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="use-credentials" />),
%(preload_link_tag '/media/audio.ogg', nopush: true) => %(<link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />)
%(preload_link_tag '/media/audio.ogg', nopush: true) => %(<link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />),
%(preload_link_tag '/style.css', integrity: 'sha256-AbpHGcgLb+kRsJGnwFEktk7uzpZOCcBY74+YBdrKVGs') => %(<link rel="preload" href="/style.css" as="style" type="text/css" integrity="sha256-AbpHGcgLb+kRsJGnwFEktk7uzpZOCcBY74+YBdrKVGs">),
}
VideoPathToTag = {
@ -535,6 +536,13 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal expected, @response.headers["Link"]
end
def test_should_set_preload_links_with_integrity_hashes
stylesheet_link_tag("http://example.com/style.css", integrity: "sha256-AbpHGcgLb+kRsJGnwFEktk7uzpZOCcBY74+YBdrKVGs")
javascript_include_tag("http://example.com/all.js", integrity: "sha256-AbpHGcgLb+kRsJGnwFEktk7uzpZOCcBY74+YBdrKVGs")
expected = "<http://example.com/style.css>; rel=preload; as=style; integrity=sha256-AbpHGcgLb+kRsJGnwFEktk7uzpZOCcBY74+YBdrKVGs; nopush,<http://example.com/all.js>; rel=preload; as=script; integrity=sha256-AbpHGcgLb+kRsJGnwFEktk7uzpZOCcBY74+YBdrKVGs; nopush"
assert_equal expected, @response.headers["Link"]
end
def test_image_path
ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end