Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(electron): add timeout option to electronApp.close() for force-kill escalation by Copilot · Pull Request #40613 · microsoft/playwright · GitHub
Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(electron): add timeout option to electronApp.close() for force-kill escalation by Copilot · Pull Request #40613 · microsoft/playwright · GitHub
Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(electron): add timeout option to electronApp.close() for force-kill escalation by Copilot · Pull Request #40613 · microsoft/playwright · GitHub
Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(electron): add timeout option to electronApp.close() for force-kill escalation by Copilot · Pull Request #40613 · microsoft/playwright · GitHub
Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(electron): add timeout option to electronApp.close() for force-kill escalation by Copilot · Pull Request #40613 · microsoft/playwright · GitHub
Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(electron): add timeout option to electronApp.close() for force-kill escalation by Copilot · Pull Request #40613 · microsoft/playwright · GitHub
Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(electron): add timeout option to electronApp.close() for force-kill escalation by Copilot · Pull Request #40613 · microsoft/playwright · GitHub
Skip to content

feat(electron): add timeout option to electronApp.close() for force-kill escalation - #40613

Merged
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close
May 4, 2026
Merged

feat(electron): add timeout option to electronApp.close() for force-kill escalation#40613
Pavel Feldman (pavelfeldman) merged 3 commits into
mainfrom
copilot/add-timeout-option-to-electronapp-close

Conversation

CopilotAI commented May 4, 2026

Copy link
Copy Markdown
Contributor

electronApp.close() waits indefinitely for the Electron process to exit. Apps with before-quit handlers, leaky IPC handlers, or stray child processes can hang teardown until the test-level timeout kills everything. The force-kill path already exists in processLauncher but is only reachable via OS signals.

Changes

  • packages/playwright-core/src/electron/electron.ts — thread the existing kill from launchProcess into ElectronApplication. Add an opt-in { timeout?: number } to close(): after the existing graceful shutdown (browser.close()app.quit()worker._disconnect()), race _closedPromise against a timer; if the timer wins, invoke kill() (SIGKILL / taskkill /T /F) and await full exit.
  • Backward compatible — without timeout, behavior is unchanged (waits forever, no force-kill).
  • docs/src/electron-api/class-electronapplication.md — document the new option; types.d.ts regenerated.
  • Tests — new electron-app-hang-on-close.js fixture (preventDefaults before-quit + keeps the loop busy). Two specs: one asserts force-kill fires near the timeout, one asserts a cooperating app still exits with code 0 (no force-kill).

Usage

// Unchanged: wait forever.awaitelectronApp.close();// New: force-kill if the app does not exit within 10s.awaitelectronApp.close({timeout: 10_000});

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=856dbc92-6ec9-48ff-ad01-9585c6f246ca,no_channel --user-data-dir=/tmp/playwright-test-UD55eR/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,11195650865713303302,599583126570378049,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json /usr/bin/dirname (dns block)
    • Triggering command: /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron /home/REDACTED/work/playwright/playwright/node_modules/electron/dist/electron -r /home/REDACTED/work/playwright/playwright/packages/playwright-core/lib/electron/loader.js --no-sandbox --inspect=0 --remote-debugging-port=0 /home/REDACTED/work/playwright/playwright/tests/electron/electron-app.js 2/x64/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/sh (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --enable-crash-reporter=2769063e-04a3-4f49-884c-c9d911de6ba5,no_channel --user-data-dir=/tmp/playwright-test-QcPS47/Electron --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,275575950628164391,10456952859755863149,262144 --enable-features=CDPScreenshotNewSurface,PdfUseShowSaveFilePicker --disable-features=AutoDeElevate,AvoidUnnecessaryBeforeUnloadCheckSync,BoundaryEventDispatchTracksNodeRemoval,DestroyProfileOnBrowserClose,DialMediaRouteProvider,GlobalMediaControls,HttpsUpgrades,LensOverlay,LocalNetworkAccessChecks,MediaRouter,Optimizati right/playwright--clearScreen=false ypes/test/tsconfig.json tnet/tools/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAIand others added 2 commits May 4, 2026 22:29
CopilotAI changed the title [WIP] Add timeout option to electronApp.close() for force-kill escalationfeat(electron): add timeout option to electronApp.close() for force-kill escalationMay 4, 2026
@pavelfeldman
Pavel Feldman (pavelfeldman) marked this pull request as ready for review May 4, 2026 22:45
@pavelfeldman
Pavel Feldman (pavelfeldman) merged commit 5263a48 into mainMay 4, 2026
41 of 43 checks passed
@pavelfeldman
Pavel Feldman (pavelfeldman) deleted the copilot/add-timeout-option-to-electronapp-close branch May 4, 2026 23:39
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright that referenced this pull request May 8, 2026
Follow-up to the revert of PR microsoft#40184 that brings the tree to a working
state on top of the main-branch changes that landed after the PR:
- Re-add Electron protocol entries to packages/protocol/spec/electron.yml
and the Electron initializer to packages/protocol/spec/playwright.yml
(protocol.yml was split into per-interface spec/*.yml files in microsoft#40645).
- Keep Electron docs in docs/src/electron-api/ but restore their pre-PR
full content (the PR had truncated docs to match the trimmed-down
CDP-only API). doclint cli.js merges electron-api docs.
- Drop the leftover docs/src/electron/loader bundling step from
utils/build/build.js (loader is back at server/electron/loader.ts).
- Update the restored server/electron/electron.ts import after the
toConsoleMessageLocation -> stackTraceToLocation rename in microsoft#39767.
- Drop the explicit _electron registration from inprocess.ts; with the
server-side dispatcher restored, _electron is wired through the
Playwright initializer again.
- Remove duplicate _electron exports in index.mjs and overrides.d.ts.
- Restore the pre-PR ElectronApplication evaluate/evaluateHandle
override signatures so the generated types are consistent.
Also re-applies the timeout option from microsoft#40613 (electronApp.close({
timeout })) on top of the restored server-side architecture: a new
ElectronApplication.close protocol command takes an optional timeout
and force-kills the underlying browser process via
browserProcess.kill() when graceful close exceeds the deadline.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(electron): add timeout option to electronApp.close() for force-kill escalation

3 participants

@pavelfeldman@yury-s