fix(player): a fullscreen button the browser cannot honour is not shown

Brave on iPadOS switches element fullscreen off, patches requestFullscreen with
a video-only stand-in and fakes fullscreenEnabled, so the Plex fullscreen button
and the YouTube docked bar's did nothing. Support is now read off
Element.prototype and the buttons are left out without it (owner). A stand-in
that returns a boolean no longer throws. A test's bare matchMedia stub no
longer outlives its describe.
This commit is contained in:
2026-10-08 02:50:51 +02:00
parent c3664826fe
commit 5249c41edc
6 changed files with 116 additions and 15 deletions
+17 -2
View File
@@ -8,7 +8,7 @@ import {
// The browser's Fullscreen API, WebKit included.
//
// Safari and iPadOS still ship only the prefixed spelling: no `fullscreenchange`, no
// Safari and iPadOS before 16.4 ship only the prefixed spelling: no `fullscreenchange`, no
// `fullscreenElement`, no `requestFullscreen` on a plain element. Reading only the unprefixed names
// there means the page believes it is NEVER fullscreen — which silently breaks whatever the
// fullscreen state drives (an overlay that must yield, a focus reclaim, an Esc branch that would
@@ -29,11 +29,26 @@ export function fullscreenElement(): Element | null {
return d.fullscreenElement ?? d.webkitFullscreenElement ?? null;
}
/** Can this browser put a plain element in fullscreen at all? Read off `Element.prototype`, never
* `document.fullscreenEnabled`: Brave on iPadOS switches element fullscreen off in its web view, then
* patches `HTMLElement.prototype.requestFullscreen` with a stand-in that only fullscreens a <video>
* and forces `fullscreenEnabled` to true — both claim a support that is not there, and the players'
* fullscreen buttons did nothing. A button that cannot work is not shown (owner). */
export function canFullscreenElements(): boolean {
if (typeof Element === "undefined") return false;
const proto = Element.prototype as Partial<WebkitElement>;
return (
typeof proto.requestFullscreen === "function" ||
typeof proto.webkitRequestFullscreen === "function"
);
}
/** Ask for fullscreen on `el`. Errors are swallowed on purpose: a refused request (no user
* activation, an iOS device that only fullscreens <video>) is not something a caller can fix. */
export function requestFullscreen(el: HTMLElement): void {
const e = el as WebkitElement;
if (e.requestFullscreen) void e.requestFullscreen().catch(() => {});
// Wrapped: a patched stand-in (Brave's, above) may return a boolean, not a promise.
if (e.requestFullscreen) void Promise.resolve(e.requestFullscreen()).catch(() => {});
else e.webkitRequestFullscreen?.();
}