Repository navigation
fix(desktop): one preview viewer or recording stopping no longer freezes the others #31051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2735171
41a057c
2850dda
d0680a3
b9e9ff3
4aacf47
4848d17
dc9708a
72f3287
f0f768a
566f4aa
dcc264b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,8 +75,8 @@ interface AttachedTab { | |
| */ | ||
| agentUsed: boolean; | ||
| focusReleasing: boolean; | ||
| /** The sessions streaming the guest; one stopping leaves the others' screencasts running. */ | ||
| readonly screencasts: Set<string | undefined>; | ||
| /** The sessions sharing the guest's one Chromium screencast; it stops with the last of them. */ | ||
| readonly screencasts: Map<string | undefined, { started: boolean; pending: number }>; | ||
| readonly onMessage: ( | ||
| event: Electron.Event, | ||
| method: string, | ||
|
|
@@ -289,9 +289,15 @@ export const make = Effect.gen(function* () { | |
| dropFocusHolds(tab); | ||
| }; | ||
|
|
||
| /** An owner that leaves without stopping would otherwise keep the screencast running unseen. */ | ||
| const leaveScreencast = (tab: AttachedTab, owner: string | undefined) => { | ||
| if (!tab.screencasts.delete(owner) || tab.screencasts.size > 0) return; | ||
| void tab.debuggee.debugger.sendCommand("Page.stopScreencast").catch(() => {}); | ||
| }; | ||
|
|
||
| /** A detached session, such as a closed viewer, waits on nothing it sent. */ | ||
| const dropSessionHolds = (tab: AttachedTab, owner: string) => { | ||
| tab.screencasts.delete(owner); | ||
| leaveScreencast(tab, owner); | ||
| for (const [pending, holder] of tab.focusPending) { | ||
| if (holder === owner) tab.focusPending.delete(pending); | ||
| } | ||
|
|
@@ -330,13 +336,19 @@ export const make = Effect.gen(function* () { | |
| { | ||
| send: (method, params, sessionId, owner) => { | ||
| // A child session's screencast is not the page's, such as a late one on a detached session. | ||
| if (sessionId === undefined && method === "Page.startScreencast") { | ||
| tab.screencasts.add(owner); | ||
| const stream = | ||
| sessionId === undefined && method === "Page.startScreencast" | ||
| ? (tab.screencasts.get(owner) ?? { started: false, pending: 0 }) | ||
| : undefined; | ||
| if (stream) { | ||
| stream.pending += 1; | ||
| tab.screencasts.set(owner, stream); | ||
| if (owner !== undefined) streamingOwners.add(owner); | ||
| syncPainting(tab); | ||
| } else if (sessionId === undefined && method === "Page.stopScreencast") { | ||
| tab.screencasts.delete(owner); | ||
| syncPainting(tab); | ||
| if (tab.screencasts.size > 0) return Promise.resolve({}); | ||
| } | ||
| // A person's input through a viewer, or Playwright's own housekeeping, is not the agent's. | ||
| if ( | ||
|
|
@@ -365,19 +377,42 @@ export const make = Effect.gen(function* () { | |
| held ? holdFocus(tab, owner, () => forward(current)) : forward(current), | ||
| ) | ||
| : method === "Page.startScreencast" | ||
| ? holdFocus(tab, owner, forward).catch((cause: unknown) => { | ||
| // A viewer can outlive a refused stream; nothing is streaming for it. | ||
| if (sessionId === undefined && tab.relay === relay) tab.screencasts.delete(owner); | ||
| releaseFocusWhenIdle(tab); | ||
| syncPainting(tab); | ||
| throw cause; | ||
| }) | ||
| ? holdFocus(tab, owner, forward).then( | ||
| (result) => { | ||
| if (stream) { | ||
| stream.pending -= 1; | ||
| stream.started = true; | ||
| // A detach's stop can overtake a start still waiting on focus; nobody owns it now. | ||
| if (tab.screencasts.get(owner) !== stream && tab.screencasts.size === 0) { | ||
| void debuggee.sendCommand("Page.stopScreencast").catch(() => {}); | ||
| } | ||
| } | ||
| return result; | ||
| }, | ||
| (cause: unknown) => { | ||
| if (stream) stream.pending -= 1; | ||
| // A viewer can outlive a refused start; its running or pending stream still holds it. | ||
| if ( | ||
| stream && | ||
| !stream.started && | ||
| stream.pending === 0 && | ||
| tab.relay === relay && | ||
| tab.screencasts.get(owner) === stream | ||
| ) { | ||
| leaveScreencast(tab, owner); | ||
| } | ||
|
Comment on lines
+392
to
+403
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| releaseFocusWhenIdle(tab); | ||
| syncPainting(tab); | ||
| throw cause; | ||
| }, | ||
| ) | ||
| : holdFocus(tab, owner, forward); | ||
| }, | ||
| sessionDetached: (owner) => { | ||
| detachedOwners.add(owner); | ||
| dropSessionHolds(tab, owner); | ||
| }, | ||
| receivesScreencast: (owner) => tab.screencasts.has(owner), | ||
| targetId: () => | ||
| debuggee | ||
| .sendCommand("Target.getTargetInfo") | ||
|
|
@@ -463,7 +498,7 @@ export const make = Effect.gen(function* () { | |
| painting: false, | ||
| agentUsed: false, | ||
| focusReleasing: false, | ||
| screencasts: new Set(), | ||
| screencasts: new Map(), | ||
| onMessage: (_event, method, params, sessionId) => { | ||
| if (method === "Page.downloadWillBegin") { | ||
| const guid = (params as { guid?: unknown } | undefined)?.guid; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not count a pending start as a committed screencast owner.
If session A is streaming and session B’s
Page.startScreencastremains unanswered, B is already intab.screencasts. When A stops, this guard suppresses the native stop. If B’s start never settles, the native stream and painting state remain active without a committed consumer. Track pending starts separately, and base the last-owner stop decision on committed starts. (raw.githubusercontent.com)