Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Draft
- Fixed YouTube video playback in Safari by adding widget_referrer and origin parameters [#2598](https://github.com/bigcommerce/cornerstone/pull/2598)

## 6.18.2 (01-08-2026)
- Fixed validation removal on form fields [#2593](https://github.com/bigcommerce/cornerstone/pull/2593)
Expand Down
74 changes: 73 additions & 1 deletion assets/js/theme/product/video-gallery.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,78 @@ export class VideoGallery {
this.$player = $element.find('[data-video-player]');
this.$videos = $element.find('[data-video-item]');
this.currentVideo = {};
this.referrer = window.location.origin;
this.isSafari = this.detectSafari();
this.bindEvents();
this.initializePlayer();
}

detectSafari() {
const userAgent = navigator.userAgent.toLowerCase();

return /safari/.test(userAgent) && !/chrome|chromium|crios|fxios/.test(userAgent);
}

/**
* Build YouTube embed URL with widget_referrer and origin parameters
* @param {string} videoId - YouTube video ID
* @param {string} existingParams - Existing URL parameters (e.g., "rel=0")
* @returns {string} Complete YouTube embed URL
*/
buildEmbedUrl(videoId, existingParams = '') {
const params = new URLSearchParams(existingParams);

// helps with Safari compatibility
params.set('widget_referrer', this.referrer);
params.set('origin', this.referrer);

return `//www.youtube.com/embed/${videoId}?${params.toString()}`;
}

/**
* Set iframe permissions for enhanced YouTube player functionality
*/
setIframePermissions() {
const allowPermissions = [
'autoplay',
'clipboard-write',
// allows the iframe to use Encrypted Media Extensions and play protected content
'encrypted-media',
'picture-in-picture',
'web-share',
];
const currentPermissions = (this.$player.attr('allow') || '').split(';').map(s => s.trim()).filter(Boolean);
const allowSet = new Set([...currentPermissions, ...allowPermissions]);
this.$player.attr('allow', Array.from(allowSet).join('; '));
}

initializePlayer() {
// Set iframe referrer policy to ensure Referer header is sent
this.$player.attr('referrerpolicy', 'strict-origin-when-cross-origin');

// Set extra permissions for better YouTube functionality
// this.setIframePermissions();

const currentSrc = this.$player.attr('src') || this.$player.attr('data-src');
if (currentSrc) {
// Extract video ID and existing parameters from the URL
const match = currentSrc.match(/\/embed\/([^?]+)(?:\?(.+))?/);
if (match) {
const videoId = match[1];
const existingParams = match[2] || '';
// Rebuild URL to ensure widget_referrer and origin are present for Safari compatibility
const newSrc = this.buildEmbedUrl(videoId, existingParams);

if (this.isSafari) {
// Removing lazyload and setting src directly ensures referrerpolicy is respected before the first request in Safari
this.$player.removeClass('lazyload');
this.$player.attr('src', newSrc);
} else {
// Keep lazyload class and only update data-src for rest browsers
this.$player.attr('data-src', newSrc);
}
}
}
}

selectNewVideo(e) {
Expand All @@ -21,7 +92,8 @@ export class VideoGallery {
}

setMainVideo() {
this.$player.attr('src', `//www.youtube.com/embed/${this.currentVideo.id}`);
const embedUrl = this.buildEmbedUrl(this.currentVideo.id);
this.$player.attr('src', embedUrl);
}

setActiveThumb() {
Expand Down