Compare commits

...
Author SHA1 Message Date
joshpatra 7e3054f6fa v6.0.7.0-beta.1 2026-08-02 10:09:10 -04:00
joshpatra 98546adf41 Bump version to 6.0.7.0 2026-08-02 10:09:09 -04:00
joshpatra daeff63d2a fix(controller): avoid popovers over fullscreen players 2026-08-02 10:07:32 -04:00
joshpatra 80aa00670c chore(release): prepare v6.0.6.0 beta.2 2026-08-02 00:08:01 -04:00
joshpatra 5af0200008 chore(release): merge v6.0.6.0 beta 2026-08-02 00:05:56 -04:00
joshpatra 13350e5cea v6.0.4.1-beta.1 2026-07-26 11:46:49 -04:00
joshpatra 461c8c448f v6.0.4-beta.1 2026-07-26 11:15:22 -04:00
joshpatra 39c1c76db1 v6.0.3-beta.1 2026-07-26 10:19:31 -04:00
joshpatra f8cbcd4368 chore(release): merge v6.0.2-beta.1 2026-07-23 00:09:01 -04:00
joshpatra e5ae1ed4ba v6.0.1-beta.1 2026-07-16 23:58:33 -04:00
joshpatra 1ae7bbdee3 v6.0.0-beta.1 2026-07-16 17:42:12 -04:00
joshpatra f1e8f44f42 v5.3.6.1-beta.1 2026-07-16 02:12:13 -04:00
joshpatra 7fd8e5d92d v5.3.6.0-beta.1 2026-07-16 01:59:48 -04:00
joshpatra fd9911855a v5.3.5.0-beta.1 2026-07-14 21:07:29 -04:00
joshpatra 5b8f2422ab v5.3.4.0-beta.1 2026-07-12 16:01:33 -04:00
joshpatra c429215626 v5.3.3.0-beta.1 2026-07-12 15:51:14 -04:00
joshpatra 432ed37f45 v5.3.2.0-beta.1 2026-06-01 01:24:52 -04:00
joshpatra 51caae3b7c v5.3.1.0-beta.1 2026-05-29 15:24:02 -04:00
joshpatra 5df0bbc551 v5.3.0.0-beta.1 2026-05-22 13:37:49 -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);