Commit 71490b74 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Fix VideoLinkFilter for uppercase file extensions

Also fixed to handle images with blank src
attributes gracefully
parent 9af1b6d2
---
title: Fix inline rendering of videos for uploads with uppercase file extensions
merge_request: 17924
author:
type: fixed
...@@ -8,8 +8,8 @@ module Banzai ...@@ -8,8 +8,8 @@ module Banzai
# a "Download" link in the case the video cannot be played. # a "Download" link in the case the video cannot be played.
class VideoLinkFilter < HTML::Pipeline::Filter class VideoLinkFilter < HTML::Pipeline::Filter
def call def call
doc.xpath(query).each do |el| doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |el|
el.replace(video_node(doc, el)) el.replace(video_node(doc, el)) if has_video_extension?(el)
end end
doc doc
...@@ -17,22 +17,13 @@ module Banzai ...@@ -17,22 +17,13 @@ module Banzai
private private
def query def has_video_extension?(element)
@query ||= begin src = element.attr('data-canonical-src').presence || element.attr('src')
src_query = UploaderHelper::SAFE_VIDEO_EXT.map do |ext|
"'.#{ext}' = substring(@src, string-length(@src) - #{ext.size})"
end
if context[:asset_proxy_enabled].present? return unless src.present?
src_query.concat(
UploaderHelper::SAFE_VIDEO_EXT.map do |ext|
"'.#{ext}' = substring(@data-canonical-src, string-length(@data-canonical-src) - #{ext.size})"
end
)
end
"descendant-or-self::img[not(ancestor::a) and (#{src_query.join(' or ')})]" src_ext = File.extname(src).sub('.', '').downcase
end Gitlab::FileTypeDetection::SAFE_VIDEO_EXT.include?(src_ext)
end end
def video_node(doc, element) def video_node(doc, element)
......
...@@ -12,15 +12,18 @@ describe Banzai::Filter::VideoLinkFilter do ...@@ -12,15 +12,18 @@ describe Banzai::Filter::VideoLinkFilter do
end end
def link_to_image(path) def link_to_image(path)
%(<img src="#{path}" />) return '<img/>' if path.nil?
%(<img src="#{path}"/>)
end end
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
context 'when the element src has a video extension' do shared_examples 'a video element' do
UploaderHelper::SAFE_VIDEO_EXT.each do |ext| let(:image) { link_to_image(src) }
it "replaces the image tag 'path/video.#{ext}' with a video tag" do
container = filter(link_to_image("/path/video.#{ext}")).children.first it 'replaces the image tag with a video tag' do
container = filter(image).children.first
expect(container.name).to eq 'div' expect(container.name).to eq 'div'
expect(container['class']).to eq 'video-container' expect(container['class']).to eq 'video-container'
...@@ -28,36 +31,79 @@ describe Banzai::Filter::VideoLinkFilter do ...@@ -28,36 +31,79 @@ describe Banzai::Filter::VideoLinkFilter do
video, paragraph = container.children video, paragraph = container.children
expect(video.name).to eq 'video' expect(video.name).to eq 'video'
expect(video['src']).to eq "/path/video.#{ext}" expect(video['src']).to eq src
expect(paragraph.name).to eq 'p' expect(paragraph.name).to eq 'p'
link = paragraph.children.first link = paragraph.children.first
expect(link.name).to eq 'a' expect(link.name).to eq 'a'
expect(link['href']).to eq "/path/video.#{ext}" expect(link['href']).to eq src
expect(link['target']).to eq '_blank' expect(link['target']).to eq '_blank'
end end
end end
end
context 'when the element src is an image' do shared_examples 'an unchanged element' do |ext|
it 'leaves the document unchanged' do it 'leaves the document unchanged' do
element = filter(link_to_image('/path/my_image.jpg')).children.first element = filter(link_to_image(src)).children.first
expect(element.name).to eq 'img' expect(element.name).to eq 'img'
expect(element['src']).to eq '/path/my_image.jpg' expect(element['src']).to eq src
end end
end end
context 'when asset proxy is enabled' do context 'when the element src has a video extension' do
it 'uses the correct src' do Gitlab::FileTypeDetection::SAFE_VIDEO_EXT.each do |ext|
stub_asset_proxy_setting(enabled: true) it_behaves_like 'a video element' do
let(:src) { "/path/video.#{ext}" }
end
it_behaves_like 'a video element' do
let(:src) { "/path/video.#{ext.upcase}" }
end
end
end
context 'when the element has no src attribute' do
let(:src) { nil }
it_behaves_like 'an unchanged element'
end
context 'when the element src is an image' do
let(:src) { '/path/my_image.jpg' }
it_behaves_like 'an unchanged element'
end
context 'when the element src has an invalid file extension' do
let(:src) { '/path/my_video.somemp4' }
it_behaves_like 'an unchanged element'
end
context 'when data-canonical-src is empty' do
let(:image) { %(<img src="#{src}" data-canonical-src=""/>) }
context 'and src is a video' do
let(:src) { '/path/video.mp4' }
it_behaves_like 'a video element'
end
context 'and src is an image' do
let(:src) { '/path/my_image.jpg' }
it_behaves_like 'an unchanged element'
end
end
context 'when data-canonical-src is set' do
it 'uses the correct src' do
proxy_src = 'https://assets.example.com/6d8b63' proxy_src = 'https://assets.example.com/6d8b63'
canonical_src = 'http://example.com/test.mp4' canonical_src = 'http://example.com/test.mp4'
image = %(<img src="#{proxy_src}" data-canonical-src="#{canonical_src}" />) image = %(<img src="#{proxy_src}" data-canonical-src="#{canonical_src}"/>)
container = filter(image, asset_proxy_enabled: true).children.first container = filter(image).children.first
expect(container['class']).to eq 'video-container' expect(container['class']).to eq 'video-container'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment