fix(controller): restore fullscreen overlay visibility

This commit is contained in:
2026-07-31 22:25:38 -04:00
parent ed21281fa3
commit 8d7d31a4db
4 changed files with 62 additions and 7 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ Constraints: local commits only; no push; no browser testing; one commit per fea
## Controller and targeting
- [ ] Keep the controller visible for its own video in element and ancestor fullscreen.
- [x] Keep the controller visible for its own video in element and ancestor fullscreen.
- [ ] Target popup actions at the frame represented by the displayed speed; keep “all videos” intentional.
- [ ] Ignore shortcuts originating from editable controls, including shadow-DOM inputs.
+7 -6
View File
@@ -3196,12 +3196,13 @@ function syncControllerFullscreenMount(videoController) {
var doc = video.ownerDocument;
var fullscreenElement = getFullscreenElement(doc);
var targetMount = videoController.normalControllerMount;
if (
var ownsFullscreen = Boolean(
fullscreenElement &&
(fullscreenElement === video ||
isComposedDescendant(video, fullscreenElement))
) {
(fullscreenElement === video ||
isComposedDescendant(video, fullscreenElement))
);
if (ownsFullscreen) {
targetMount = getControllerMount(video, fullscreenElement);
} else if (!fullscreenElement && (!targetMount || !targetMount.isConnected)) {
targetMount = getControllerMount(video);
@@ -3210,7 +3211,7 @@ function syncControllerFullscreenMount(videoController) {
if (!targetMount) return false;
if (fullscreenElement) {
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
+18
View File
@@ -10,6 +10,24 @@
overflow: visible !important;
}
/* Important declarations inside a shadow tree outrank important page styles.
Repeat the top-layer positioning here so fullscreen cannot fall back to the
base absolute host rule. */
:host(.vsc-fullscreen-popover) {
position: fixed !important;
inset: auto !important;
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
background: transparent !important;
overflow: visible !important;
}
:host(.vsc-fullscreen-popover)::backdrop {
pointer-events: none !important;
background: transparent !important;
}
:host(.vsc-nosource),
:host(.vsc-hidden) {
display: none !important;
+36
View File
@@ -612,6 +612,42 @@ describe("inject.js media/controller lifecycle regressions", () => {
controller.controllerHostCleanup();
});
it("only promotes the controller owned by the fullscreen player", async () => {
bootInject();
await settleLifecycle();
const fullscreenPlayer = document.createElement("div");
const fullscreenVideo = document.createElement("video");
const otherVideo = document.createElement("video");
const rect = makeRect(0, 0, 640, 360);
fullscreenVideo.src = "https://example.org/fullscreen.mp4";
otherVideo.src = "https://example.org/other.mp4";
fullscreenPlayer.appendChild(fullscreenVideo);
document.body.append(fullscreenPlayer, otherVideo);
[fullscreenPlayer, fullscreenVideo, otherVideo].forEach((element) => {
setRect(element, rect);
setBoxMetrics(element, rect.width, rect.height);
});
window.ensureController(fullscreenVideo, fullscreenPlayer);
window.ensureController(otherVideo, document.body);
fullscreenVideo.vsc.div.showPopover = vi.fn();
otherVideo.vsc.div.showPopover = vi.fn();
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
value: fullscreenPlayer
});
window.syncControllerFullscreenMount(fullscreenVideo.vsc);
window.syncControllerFullscreenMount(otherVideo.vsc);
expect(fullscreenVideo.vsc.div.showPopover).toHaveBeenCalledOnce();
expect(otherVideo.vsc.div.showPopover).not.toHaveBeenCalled();
expect(
otherVideo.vsc.div.classList.contains("vsc-fullscreen-popover")
).toBe(false);
});
it("preserves direct-video requestFullscreen semantics and overlays with a popover", async () => {
bootInject();
await settleLifecycle();