fix: preserve native media layout and fullscreen controls; exclude challenge frames

This commit is contained in:
joshpatra committed 2026-09-27 16:58:47 -04:00
1 parent 71851de849
commit 2c3ad3ac06
7 files changed
+522 -30

No files matched your search

+99 -29
View File
@@ -1,3 +1,40 @@
// Keep in sync with the page-world bridge guard. These checks run before any
// startup hooks because verification frames inspect native DOM/history APIs.
function isChallengeUrl(value) {
try {
var url = new URL(value);
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
var hostname = url.hostname.toLowerCase().replace(/\.$/, "");
return (
hostname === "challenges.cloudflare.com" ||
hostname.endsWith(".challenges.cloudflare.com") ||
url.pathname === "/cdn-cgi/challenge-platform" ||
url.pathname.indexOf("/cdn-cgi/challenge-platform/") === 0
);
} catch (_error) {
return false;
}
}
function isChallengeDocument(win) {
try {
if (isChallengeUrl(win.location.href)) return true;
if (!/^about:(blank|srcdoc)(?:[?#]|$)/i.test(win.location.href)) {
return false;
}
if (
isChallengeUrl(win.document.baseURI) ||
isChallengeUrl(win.document.referrer)
) {
return true;
}
return win.parent !== win && isChallengeDocument(win.parent);
} catch (_error) {
return false;
}
}
var vscIsChallengeDocument = isChallengeDocument(window);
var lastToggleSpeed = {}; // Store last toggle speeds per video
var speederShared =
typeof SpeederShared === "object" && SpeederShared ? SpeederShared : {};
@@ -1788,7 +1825,7 @@ function ensureController(node, parent) {
var fullscreenElement = getFullscreenElement(node.ownerDocument);
var normalMount = getControllerMount(node) || parent || node.parentNode;
if (normalMount) existingController.normalControllerMount = normalMount;
if (normalMount) setNormalControllerMount(existingController, normalMount);
if (
fullscreenElement &&
(fullscreenElement === node ||
@@ -2870,8 +2907,10 @@ function finishStartupStorageWatch() {
// catch shadow roots created while chrome.storage.sync.get is pending.
// Sites like archive.org create Lit/LitElement shadow DOMs during page load;
// waiting for the storage callback would miss them entirely.
patchAttachShadow();
installPageShadowBridge();
if (!vscIsChallengeDocument) {
patchAttachShadow();
installPageShadowBridge();
}
function loadInitialRuntimeSettings(attempt) {
chrome.storage.sync.get(null, function(rawStorage) {
@@ -3172,9 +3211,11 @@ function loadInitialRuntimeSettings(attempt) {
// Install before async settings hydration so SPA-owned window capture handlers
// cannot hide later key events from Speeder.
attachKeydownListeners(document);
beginStartupStorageWatch();
loadInitialRuntimeSettings(0);
if (!vscIsChallengeDocument) {
attachKeydownListeners(document);
beginStartupStorageWatch();
loadInitialRuntimeSettings(0);
}
function getKeyBindings(action, what = "value") {
try {
@@ -3329,6 +3370,24 @@ function getControllerGeometryMount(mount) {
return isShadowRootNode(mount) ? mount.host : mount;
}
function setNormalControllerMount(videoController, mount) {
videoController.normalControllerMount = mount;
// Some site integrations deliberately use a sibling overlay (e.g. Apple
// TV's scrim). Only invalidate containment for mounts that owned the media.
videoController.normalMountContainedMedia = isComposedDescendant(
videoController.video,
getControllerGeometryMount(mount)
);
}
function isNativeMediaDocument(doc) {
return Boolean(
doc &&
(doc.mozSyntheticDocument === true ||
/^(?:video\/|audio\/|application\/ogg(?:;|$))/i.test(doc.contentType || ""))
);
}
function getControllerMount(video, boundary) {
if (!video) return null;
@@ -3357,6 +3416,10 @@ function getControllerMount(video, boundary) {
return boundary;
}
if (isNativeMediaDocument(video.ownerDocument)) {
return video.ownerDocument.body;
}
// YouTube's .html5-video-container is often absolutely laid out and can
// report a zero-height box even while its video is visibly playing. The
// surrounding .html5-video-player is the real player/overlay boundary for
@@ -3444,7 +3507,10 @@ function positionControllerHost(wrapper, video, mount) {
}
var videoRect = video.getBoundingClientRect();
cacheVideoRect(wrapper, video, videoRect);
if (wrapper.classList.contains("vsc-fullscreen-popover")) {
if (
wrapper.classList.contains("vsc-fullscreen-popover") ||
wrapper.classList.contains("vsc-media-document")
) {
if (videoRect.width <= 0 || videoRect.height <= 0) {
wrapper.classList.add("vsc-geometry-hidden");
wrapper.style.setProperty("display", "none", "important");
@@ -3596,6 +3662,11 @@ function setupControllerHostTracking(videoController, wrapper, mount) {
var win = doc.defaultView || window;
var geometryMount = getControllerGeometryMount(mount);
if (!geometryMount || !geometryMount.style) return;
// Firefox's standalone media body can be 0px tall. Making it positioned
// replaces the viewport containing block of its absolutely positioned video
// and collapses playback. Anchor our own host to the viewport instead.
var nativeMediaDocument = isNativeMediaDocument(doc);
wrapper.classList.toggle("vsc-media-document", nativeMediaDocument);
var frameId = null;
var geometryRetryTimer = null;
var geometryRetryAttempts = 0;
@@ -3636,7 +3707,7 @@ function setupControllerHostTracking(videoController, wrapper, mount) {
frameId = win.requestAnimationFrame(update);
};
if (win.getComputedStyle(geometryMount).position === "static") {
if (!nativeMediaDocument && win.getComputedStyle(geometryMount).position === "static") {
geometryMount.dataset.vscPositionOwner = "true";
geometryMount.dataset.vscOriginalPosition =
geometryMount.style.getPropertyValue("position");
@@ -3646,7 +3717,7 @@ function setupControllerHostTracking(videoController, wrapper, mount) {
geometryMount.style.setProperty("position", "relative");
}
if (!createsControllerStackingContext(geometryMount)) {
if (!nativeMediaDocument && !createsControllerStackingContext(geometryMount)) {
geometryMount.dataset.vscIsolationOwner = "true";
geometryMount.dataset.vscOriginalIsolation =
geometryMount.style.getPropertyValue("isolation");
@@ -3844,7 +3915,7 @@ function enableDirectFullscreenPopover(videoController) {
);
if (!normalMountIsConnected) {
normalMount = getControllerMount(videoController.video);
videoController.normalControllerMount = normalMount;
setNormalControllerMount(videoController, normalMount);
}
if (!normalMount) return false;
if (wrapper.parentNode !== normalMount) {
@@ -3876,12 +3947,22 @@ function syncControllerFullscreenMount(videoController) {
var doc = video.ownerDocument;
var fullscreenElement = getFullscreenElement(doc);
var targetMount = videoController.normalControllerMount;
var normalGeometryMount = getControllerGeometryMount(targetMount);
if (
!normalGeometryMount ||
!normalGeometryMount.isConnected ||
(videoController.normalMountContainedMedia === true &&
!isComposedDescendant(video, normalGeometryMount))
) {
targetMount = getControllerMount(video);
setNormalControllerMount(videoController, targetMount);
normalGeometryMount = getControllerGeometryMount(targetMount);
}
var ownsFullscreen = Boolean(
fullscreenElement &&
(fullscreenElement === video ||
isComposedDescendant(video, fullscreenElement))
);
var normalGeometryMount = getControllerGeometryMount(targetMount);
var normalMountIsAlreadyFullscreenVisible = Boolean(
fullscreenElement &&
fullscreenElement !== video &&
@@ -3891,9 +3972,6 @@ function syncControllerFullscreenMount(videoController) {
if (ownsFullscreen && !normalMountIsAlreadyFullscreenVisible) {
targetMount = getControllerMount(video, fullscreenElement);
} else if (!fullscreenElement && (!targetMount || !targetMount.isConnected)) {
targetMount = getControllerMount(video);
videoController.normalControllerMount = targetMount;
}
if (!targetMount) return false;
@@ -4681,7 +4759,7 @@ function defineVideoController() {
) {
log("No suitable parent found, appending to body", 4);
doc.body.appendChild(wrapper);
this.normalControllerMount = doc.body;
setNormalControllerMount(this, doc.body);
setupControllerHostTracking(this, wrapper, doc.body);
return wrapper;
}
@@ -4718,14 +4796,14 @@ function defineVideoController() {
break;
}
mountEl.appendChild(wrapper);
this.normalControllerMount = mountEl;
setNormalControllerMount(this, mountEl);
setupControllerHostTracking(this, wrapper, mountEl);
log("Controller successfully inserted into DOM", 4);
} catch (error) {
log(`Error inserting controller: ${error.message}`, 2);
// Fallback to body insertion
doc.body.appendChild(wrapper);
this.normalControllerMount = doc.body;
setNormalControllerMount(this, doc.body);
setupControllerHostTracking(this, wrapper, doc.body);
}
@@ -5342,18 +5420,10 @@ function scheduleMediaLifecycleReconcile(media, videoController) {
}
var nextMount = getControllerMount(media);
if (
nextMount &&
(nextMount !== videoController.controllerHostMount ||
videoController.div.parentNode !== nextMount ||
!videoController.div.isConnected)
) {
remountControllerHost(videoController, nextMount);
return;
}
if (typeof videoController.controllerHostSchedule === "function") {
videoController.controllerHostSchedule();
}
if (nextMount) setNormalControllerMount(videoController, nextMount);
// A play/metadata event can arrive after fullscreenchange. Resolve the
// active fullscreen boundary again instead of restoring the normal host.
syncControllerFullscreenMount(videoController);
}
);
}