diff --git a/.changeset/busy-icons-notice.md b/.changeset/busy-icons-notice.md new file mode 100644 index 0000000000..a50b1c9b5a --- /dev/null +++ b/.changeset/busy-icons-notice.md @@ -0,0 +1,5 @@ +--- +'livekit-client': patch +--- + +Defer `onEnterPiP` visibility update until after the next microtask and animation frame so Document Picture-in-Picture embedders can append DOM into the PiP window before `isElementInPiP` runs. diff --git a/src/room/track/RemoteVideoTrack.ts b/src/room/track/RemoteVideoTrack.ts index b3d585db04..d7d1c9436f 100644 --- a/src/room/track/RemoteVideoTrack.ts +++ b/src/room/track/RemoteVideoTrack.ts @@ -384,8 +384,14 @@ class HTMLElementInfo implements ElementInfo { private onEnterPiP = () => { window.documentPictureInPicture?.window?.addEventListener('pagehide', this.onLeavePiP); - this.isPiP = isElementInPiP(this.element); - this.handleVisibilityChanged?.(); + // Document PiP: the browser may fire 'enter' before the app has appended its subtree into + // documentPictureInPicture.window. Defer so pipWin.document.contains(video) is reliable. + queueMicrotask(() => { + requestAnimationFrame(() => { + this.isPiP = isElementInPiP(this.element); + this.handleVisibilityChanged?.(); + }); + }); }; private onLeavePiP = () => {