v6.0.7.0-beta.1

This commit is contained in:
2026-08-02 10:09:10 -04:00
3 changed files with 12 additions and 58 deletions
+6 -9
View File
@@ -3168,14 +3168,14 @@ function disableDirectFullscreenPopover(videoController) {
wrapper.removeAttribute("popover");
}
function enableFullscreenPopover(videoController, preferredMount) {
function enableDirectFullscreenPopover(videoController) {
if (!videoController || !videoController.video || !videoController.div) {
return false;
}
var wrapper = videoController.div;
if (typeof wrapper.showPopover !== "function") return false;
var normalMount = preferredMount || videoController.normalControllerMount;
var normalMount = videoController.normalControllerMount;
var normalMountIsConnected = Boolean(
normalMount &&
(normalMount.isConnected ||
@@ -3230,13 +3230,10 @@ function syncControllerFullscreenMount(videoController) {
if (!targetMount) return false;
if (ownsFullscreen) {
// Fullscreen elements and popovers both participate in the browser's top
// layer. Showing Speeder's host after the player enters fullscreen keeps it
// above provider-owned surfaces even when the provider clips descendants or
// creates a new fullscreen stacking context (notably Firefox + YouTube).
// Browsers without the Popover API retain the player-local remount fallback.
if (enableFullscreenPopover(videoController, targetMount)) return true;
if (fullscreenElement === video) {
// A replaced <video> cannot paint author children, so direct-media
// fullscreen is the only case that needs a separate top-layer popover.
if (enableDirectFullscreenPopover(videoController)) return true;
} else {
disableDirectFullscreenPopover(videoController);
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Speeder",
"short_name": "Speeder",
"version": "6.0.6.0",
"version": "6.0.7.0",
"manifest_version": 2,
"description": "Speed up, slow down, advance and rewind HTML5 audio/video with shortcuts (New and improved version of \"Video Speed Controller\")",
"homepage_url": "https://github.com/SoPat712/speeder",
+5 -48
View File
@@ -590,6 +590,7 @@ describe("inject.js media/controller lifecycle regressions", () => {
div: wrapper,
normalControllerMount: normalMount
};
wrapper.showPopover = vi.fn();
window.setupControllerHostTracking(controller, wrapper, normalMount);
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
@@ -598,6 +599,8 @@ describe("inject.js media/controller lifecycle regressions", () => {
window.syncControllerFullscreenMount(controller);
expect(fullscreenPlayer.contains(wrapper)).toBe(true);
expect(wrapper.showPopover).not.toHaveBeenCalled();
expect(wrapper.hasAttribute("popover")).toBe(false);
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
@@ -610,53 +613,7 @@ describe("inject.js media/controller lifecycle regressions", () => {
controller.controllerHostCleanup();
});
it("promotes an ancestor-fullscreen controller into the browser top layer", async () => {
bootInject();
await settleLifecycle();
const fullscreenPlayer = document.createElement("div");
const video = document.createElement("video");
const wrapper = document.createElement("div");
const rect = makeRect(0, 0, 1280, 720);
fullscreenPlayer.append(video, wrapper);
document.body.appendChild(fullscreenPlayer);
[fullscreenPlayer, video].forEach((element) => {
setRect(element, rect);
setBoxMetrics(element, rect.width, rect.height);
});
wrapper.showPopover = vi.fn();
wrapper.hidePopover = vi.fn();
const controller = {
video,
div: wrapper,
normalControllerMount: fullscreenPlayer
};
window.setupControllerHostTracking(controller, wrapper, fullscreenPlayer);
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
value: fullscreenPlayer
});
expect(window.syncControllerFullscreenMount(controller)).toBe(true);
expect(wrapper.parentNode).toBe(fullscreenPlayer);
expect(wrapper.showPopover).toHaveBeenCalledOnce();
expect(wrapper.getAttribute("popover")).toBe("manual");
expect(wrapper.classList.contains("vsc-fullscreen-popover")).toBe(true);
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
value: null
});
window.syncControllerFullscreenMount(controller);
expect(wrapper.hidePopover).toHaveBeenCalledOnce();
wrapper.remove();
controller.controllerHostCleanup();
});
it("only promotes the controller owned by the fullscreen player", async () => {
it("only promotes the directly-fullscreen video's controller", async () => {
bootInject();
await settleLifecycle();
@@ -680,7 +637,7 @@ describe("inject.js media/controller lifecycle regressions", () => {
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
value: fullscreenPlayer
value: fullscreenVideo
});
window.syncControllerFullscreenMount(fullscreenVideo.vsc);
window.syncControllerFullscreenMount(otherVideo.vsc);