fix: zombie windows when fullscreening and closing (#34378)

This commit is contained in:
Shelley Vohr 2022-05-31 10:43:42 +02:00 коммит произвёл GitHub
Родитель 93b39b92b5
Коммит 2cb53c5db1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 46 добавлений и 5 удалений

Просмотреть файл

@ -176,6 +176,10 @@ class NativeWindowMac : public NativeWindow,
// Handle fullscreen transitions. // Handle fullscreen transitions.
void SetFullScreenTransitionState(FullScreenTransitionState state); void SetFullScreenTransitionState(FullScreenTransitionState state);
void HandlePendingFullscreenTransitions(); void HandlePendingFullscreenTransitions();
bool HandleDeferredClose();
void SetHasDeferredWindowClose(bool defer_close) {
has_deferred_window_close_ = defer_close;
}
enum class VisualEffectState { enum class VisualEffectState {
kFollowWindow, kFollowWindow,
@ -249,6 +253,12 @@ class NativeWindowMac : public NativeWindow,
FullScreenTransitionState fullscreen_transition_state_ = FullScreenTransitionState fullscreen_transition_state_ =
FullScreenTransitionState::NONE; FullScreenTransitionState::NONE;
// Trying to close an NSWindow during a fullscreen transition will cause the
// window to lock up. Use this to track if CloseWindow was called during a
// fullscreen transition, to defer the -[NSWindow close] call until the
// transition is complete.
bool has_deferred_window_close_ = false;
NSInteger attention_request_id_ = 0; // identifier from requestUserAttention NSInteger attention_request_id_ = 0; // identifier from requestUserAttention
// The presentation options before entering kiosk mode. // The presentation options before entering kiosk mode.

Просмотреть файл

@ -474,6 +474,11 @@ void NativeWindowMac::Close() {
return; return;
} }
if (fullscreen_transition_state() != FullScreenTransitionState::NONE) {
SetHasDeferredWindowClose(true);
return;
}
// If a sheet is attached to the window when we call // If a sheet is attached to the window when we call
// [window_ performClose:nil], the window won't close properly // [window_ performClose:nil], the window won't close properly
// even after the user has ended the sheet. // even after the user has ended the sheet.
@ -678,6 +683,15 @@ void NativeWindowMac::HandlePendingFullscreenTransitions() {
SetFullScreen(next_transition); SetFullScreen(next_transition);
} }
bool NativeWindowMac::HandleDeferredClose() {
if (has_deferred_window_close_) {
SetHasDeferredWindowClose(false);
Close();
return true;
}
return false;
}
void NativeWindowMac::SetFullScreen(bool fullscreen) { void NativeWindowMac::SetFullScreen(bool fullscreen) {
// [NSWindow -toggleFullScreen] is an asynchronous operation, which means // [NSWindow -toggleFullScreen] is an asynchronous operation, which means
// that it's possible to call it while a fullscreen transition is currently // that it's possible to call it while a fullscreen transition is currently

Просмотреть файл

@ -248,6 +248,9 @@ using FullScreenTransitionState =
shell_->NotifyWindowEnterFullScreen(); shell_->NotifyWindowEnterFullScreen();
if (shell_->HandleDeferredClose())
return;
shell_->HandlePendingFullscreenTransitions(); shell_->HandlePendingFullscreenTransitions();
} }
@ -263,6 +266,9 @@ using FullScreenTransitionState =
shell_->SetResizable(is_resizable_); shell_->SetResizable(is_resizable_);
shell_->NotifyWindowLeaveFullScreen(); shell_->NotifyWindowLeaveFullScreen();
if (shell_->HandleDeferredClose())
return;
shell_->HandlePendingFullscreenTransitions(); shell_->HandlePendingFullscreenTransitions();
} }

Просмотреть файл

@ -434,15 +434,15 @@ describe('<webview> tag', function () {
return [w, webview]; return [w, webview];
}; };
afterEach(closeAllWindows);
afterEach(async () => { afterEach(async () => {
// The leaving animation is un-observable but can interfere with future tests // The leaving animation is un-observable but can interfere with future tests
// Specifically this is async on macOS but can be on other platforms too // Specifically this is async on macOS but can be on other platforms too
await delay(1000); await delay(1000);
closeAllWindows();
}); });
// TODO(jkleinsc) fix this test on arm64 macOS. It causes the tests following it to fail/be flaky it('should make parent frame element fullscreen too', async () => {
ifit(process.platform !== 'darwin' || process.arch !== 'arm64')('should make parent frame element fullscreen too', async () => {
const [w, webview] = await loadWebViewWindow(); const [w, webview] = await loadWebViewWindow();
expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.false(); expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.false();
@ -450,11 +450,13 @@ describe('<webview> tag', function () {
await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
await parentFullscreen; await parentFullscreen;
expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true(); expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true();
w.close();
await emittedOnce(w, 'closed');
}); });
// FIXME(zcbenz): Fullscreen events do not work on Linux. // FIXME(zcbenz): Fullscreen events do not work on Linux.
// This test is flaky on arm64 macOS. ifit(process.platform !== 'linux')('exiting fullscreen should unfullscreen window', async () => {
ifit(process.platform !== 'linux' && process.arch !== 'arm64')('exiting fullscreen should unfullscreen window', async () => {
const [w, webview] = await loadWebViewWindow(); const [w, webview] = await loadWebViewWindow();
const enterFullScreen = emittedOnce(w, 'enter-full-screen'); const enterFullScreen = emittedOnce(w, 'enter-full-screen');
await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
@ -465,6 +467,9 @@ describe('<webview> tag', function () {
await leaveFullScreen; await leaveFullScreen;
await delay(0); await delay(0);
expect(w.isFullScreen()).to.be.false(); expect(w.isFullScreen()).to.be.false();
w.close();
await emittedOnce(w, 'closed');
}); });
// Sending ESC via sendInputEvent only works on Windows. // Sending ESC via sendInputEvent only works on Windows.
@ -479,6 +484,9 @@ describe('<webview> tag', function () {
await leaveFullScreen; await leaveFullScreen;
await delay(0); await delay(0);
expect(w.isFullScreen()).to.be.false(); expect(w.isFullScreen()).to.be.false();
w.close();
await emittedOnce(w, 'closed');
}); });
it('pressing ESC should emit the leave-html-full-screen event', async () => { it('pressing ESC should emit the leave-html-full-screen event', async () => {
@ -507,6 +515,9 @@ describe('<webview> tag', function () {
webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Escape' }); webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Escape' });
await leaveFSWindow; await leaveFSWindow;
await leaveFSWebview; await leaveFSWebview;
w.close();
await emittedOnce(w, 'closed');
}); });
}); });