refactor: unfilter unresponsive events (#44667)

refactor: unfilter unresponsive events (#44629)

* feat: internal -unresponsive event

* Reland "refactor: JSify BrowserWindow unresponsive handling"

This reverts commit ef7ae78ed4.

* fix: emit unresponsive if close not prevented

---------
This commit is contained in:
Keeley Hammond 2024-11-14 16:59:22 -08:00 committed by GitHub
parent e95e6fac0d
commit b88e1aba10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 107 additions and 60 deletions

View file

@ -37,6 +37,33 @@ BrowserWindow.prototype._init = function (this: BWT) {
app.emit('browser-window-focus', event, this); app.emit('browser-window-focus', event, this);
}); });
let unresponsiveEvent: NodeJS.Timeout | null = null;
const emitUnresponsiveEvent = () => {
unresponsiveEvent = null;
if (!this.isDestroyed() && this.isEnabled()) { this.emit('unresponsive'); }
};
this.webContents.on('unresponsive', () => {
if (!unresponsiveEvent) { unresponsiveEvent = setTimeout(emitUnresponsiveEvent, 50); }
});
this.webContents.on('responsive', () => {
if (unresponsiveEvent) {
clearTimeout(unresponsiveEvent);
unresponsiveEvent = null;
}
this.emit('responsive');
});
this.on('close', (event) => {
queueMicrotask(() => {
if (!unresponsiveEvent && !event.defaultPrevented) {
unresponsiveEvent = setTimeout(emitUnresponsiveEvent, 5000);
}
});
});
this.webContents.on('destroyed', () => {
if (unresponsiveEvent) clearTimeout(unresponsiveEvent);
unresponsiveEvent = null;
});
// Subscribe to visibilityState changes and pass to renderer process. // Subscribe to visibilityState changes and pass to renderer process.
let isVisible = this.isVisible() && !this.isMinimized(); let isVisible = this.isVisible() && !this.isMinimized();
const visibilityChanged = () => { const visibilityChanged = () => {

View file

@ -909,6 +909,13 @@ WebContents.prototype._init = function () {
openDialogs.clear(); openDialogs.clear();
}); });
this.on('-unresponsive' as any, (event: Electron.Event<any>) => {
const shouldEmit = !event.shouldIgnore && event.visible && event.rendererInitialized;
if (shouldEmit) {
this.emit('unresponsive', event);
}
});
app.emit('web-contents-created', { sender: this, preventDefault () {}, get defaultPrevented () { return false; } }, this); app.emit('web-contents-created', { sender: this, preventDefault () {}, get defaultPrevented () { return false; } }, this);
// Properties // Properties

View file

@ -134,3 +134,4 @@ fix_software_compositing_infinite_loop.patch
ui_add_missing_shortcut_text_for_vkey_command_on_linux.patch ui_add_missing_shortcut_text_for_vkey_command_on_linux.patch
osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch
cherry-pick-923797bac925.patch cherry-pick-923797bac925.patch
refactor_unfilter_unresponsive_events.patch

View file

@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Maddock <smaddock@slack-corp.com>
Date: Tue, 12 Nov 2024 17:30:42 -0500
Subject: refactor: unfilter unresponsive events
RendererUnresponsive events are filtered out when
* DisableHangMonitor switch is set
* WebContents is awaiting a clipboard scan
* Any remote debugger is attached
* Any local debugger is attached
* Visibility state is not visible
* Renderer process is not initialized
This CL removes these filters so the unresponsive event can still be
accessed from our JS event. The filtering is moved into Electron's code.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index ad1f4c8f5dbe0da06955205e5c8a34908c04a0cd..fc720ea3ef4a45da258075f87da239fa9c3b0e82 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -9410,29 +9410,17 @@ void WebContentsImpl::RendererUnresponsive(
base::RepeatingClosure hang_monitor_restarter) {
OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive",
"render_widget_host", render_widget_host);
- if (ShouldIgnoreUnresponsiveRenderer()) {
+ if (IsBeingDestroyed()) {
return;
}
bool visible = GetVisibility() == Visibility::VISIBLE;
base::UmaHistogramBoolean("Renderer.Unresponsive.Visibility", visible);
- // Do not report hangs (to task manager, to hang renderer dialog, etc.) for
- // invisible tabs (like extension background page, background tabs). See
- // https://crbug.com/881812 for rationale and for choosing the visibility
- // (rather than process priority) as the signal here.
- if (!visible) {
- return;
- }
-
base::UmaHistogramBoolean(
"Renderer.Unresponsive.PageVisible.WidgetVisibility",
!render_widget_host->is_hidden());
- if (!render_widget_host->renderer_initialized()) {
- return;
- }
-
CrashRepHandlingOutcome outcome =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kNoErrorDialogs)

View file

@ -108,19 +108,6 @@ BrowserWindow::~BrowserWindow() {
void BrowserWindow::BeforeUnloadDialogCancelled() { void BrowserWindow::BeforeUnloadDialogCancelled() {
WindowList::WindowCloseCancelled(window()); WindowList::WindowCloseCancelled(window());
// Cancel unresponsive event when window close is cancelled.
window_unresponsive_closure_.Cancel();
}
void BrowserWindow::OnRendererUnresponsive(content::RenderProcessHost*) {
// Schedule the unresponsive shortly later, since we may receive the
// responsive event soon. This could happen after the whole application had
// blocked for a while.
// Also notice that when closing this event would be ignored because we have
// explicitly started a close timeout counter. This is on purpose because we
// don't want the unresponsive event to be sent too early when user is closing
// the window.
ScheduleUnresponsiveEvent(50);
} }
void BrowserWindow::WebContentsDestroyed() { void BrowserWindow::WebContentsDestroyed() {
@ -128,11 +115,6 @@ void BrowserWindow::WebContentsDestroyed() {
CloseImmediately(); CloseImmediately();
} }
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
window_unresponsive_closure_.Cancel();
Emit("responsive");
}
void BrowserWindow::OnSetContentBounds(const gfx::Rect& rect) { void BrowserWindow::OnSetContentBounds(const gfx::Rect& rect) {
// window.resizeTo(...) // window.resizeTo(...)
// window.moveTo(...) // window.moveTo(...)
@ -168,13 +150,6 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) {
// first, and when the web page is closed the window will also be closed. // first, and when the web page is closed the window will also be closed.
*prevent_default = true; *prevent_default = true;
// Assume the window is not responding if it doesn't cancel the close and is
// not closed in 5s, in this way we can quickly show the unresponsive
// dialog when the window is busy executing some script without waiting for
// the unresponsive timeout.
if (window_unresponsive_closure_.IsCancelled())
ScheduleUnresponsiveEvent(5000);
// Already closed by renderer. // Already closed by renderer.
if (!web_contents() || !api_web_contents_) if (!web_contents() || !api_web_contents_)
return; return;
@ -241,9 +216,6 @@ void BrowserWindow::CloseImmediately() {
} }
BaseWindow::CloseImmediately(); BaseWindow::CloseImmediately();
// Do not sent "unresponsive" event after window is closed.
window_unresponsive_closure_.Cancel();
} }
void BrowserWindow::Focus() { void BrowserWindow::Focus() {
@ -294,24 +266,6 @@ v8::Local<v8::Value> BrowserWindow::GetWebContents(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, web_contents_); return v8::Local<v8::Value>::New(isolate, web_contents_);
} }
void BrowserWindow::ScheduleUnresponsiveEvent(int ms) {
if (!window_unresponsive_closure_.IsCancelled())
return;
window_unresponsive_closure_.Reset(base::BindRepeating(
&BrowserWindow::NotifyWindowUnresponsive, GetWeakPtr()));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, window_unresponsive_closure_.callback(),
base::Milliseconds(ms));
}
void BrowserWindow::NotifyWindowUnresponsive() {
window_unresponsive_closure_.Cancel();
if (!window_->IsClosed() && window_->IsEnabled()) {
Emit("unresponsive");
}
}
void BrowserWindow::OnWindowShow() { void BrowserWindow::OnWindowShow() {
web_contents()->WasShown(); web_contents()->WasShown();
BaseWindow::OnWindowShow(); BaseWindow::OnWindowShow();

View file

@ -46,9 +46,6 @@ class BrowserWindow : public BaseWindow,
// content::WebContentsObserver: // content::WebContentsObserver:
void BeforeUnloadDialogCancelled() override; void BeforeUnloadDialogCancelled() override;
void OnRendererUnresponsive(content::RenderProcessHost*) override;
void OnRendererResponsive(
content::RenderProcessHost* render_process_host) override;
void WebContentsDestroyed() override; void WebContentsDestroyed() override;
// ExtendedWebContentsObserver: // ExtendedWebContentsObserver:
@ -83,16 +80,6 @@ class BrowserWindow : public BaseWindow,
private: private:
// Helpers. // Helpers.
// Schedule a notification unresponsive event.
void ScheduleUnresponsiveEvent(int ms);
// Dispatch unresponsive event to observers.
void NotifyWindowUnresponsive();
// Closure that would be called when window is unresponsive when closing,
// it should be cancelled when we can prove that the window is responsive.
base::CancelableRepeatingClosure window_unresponsive_closure_;
v8::Global<v8::Value> web_contents_; v8::Global<v8::Value> web_contents_;
v8::Global<v8::Value> web_contents_view_; v8::Global<v8::Value> web_contents_view_;
base::WeakPtr<api::WebContents> api_web_contents_; base::WeakPtr<api::WebContents> api_web_contents_;

View file

@ -43,6 +43,7 @@
#include "content/browser/renderer_host/render_frame_host_manager.h" // nogncheck #include "content/browser/renderer_host/render_frame_host_manager.h" // nogncheck
#include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck #include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck
#include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck #include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck
#include "content/browser/web_contents/web_contents_impl.h" // nogncheck
#include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/context_menu_params.h" #include "content/public/browser/context_menu_params.h"
#include "content/public/browser/desktop_media_id.h" #include "content/public/browser/desktop_media_id.h"
@ -62,6 +63,7 @@
#include "content/public/browser/service_worker_context.h" #include "content/public/browser/service_worker_context.h"
#include "content/public/browser/site_instance.h" #include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_partition.h"
#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/common/referrer_type_converters.h" #include "content/public/common/referrer_type_converters.h"
#include "content/public/common/result_codes.h" #include "content/public/common/result_codes.h"
@ -1418,7 +1420,25 @@ void WebContents::RendererUnresponsive(
content::WebContents* source, content::WebContents* source,
content::RenderWidgetHost* render_widget_host, content::RenderWidgetHost* render_widget_host,
base::RepeatingClosure hang_monitor_restarter) { base::RepeatingClosure hang_monitor_restarter) {
Emit("unresponsive"); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
gin::Handle<gin_helper::internal::Event> event =
gin_helper::internal::Event::New(isolate);
v8::Local<v8::Object> event_object = event.ToV8().As<v8::Object>();
gin::Dictionary dict(isolate, event_object);
auto* web_contents_impl = static_cast<content::WebContentsImpl*>(source);
bool should_ignore = web_contents_impl->ShouldIgnoreUnresponsiveRenderer();
dict.Set("shouldIgnore", should_ignore);
bool visible = source->GetVisibility() == content::Visibility::VISIBLE;
dict.Set("visible", visible);
auto* rwh_impl =
static_cast<content::RenderWidgetHostImpl*>(render_widget_host);
dict.Set("rendererInitialized", rwh_impl->renderer_initialized());
EmitWithoutEvent("-unresponsive", event);
} }
void WebContents::RendererResponsive( void WebContents::RendererResponsive(