From 074cedd5611c29f3b2b0dbd7ce5f8747cb5d8533 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:41:46 -0500 Subject: [PATCH] feat: Focus DevTools when breakpoint is triggered (#48701) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `bringToFront` DevTools message is sent when breakpoint is triggered or inspect is called and Chromium upon this message activates DevTools via `DevToolsUIBindings::Delegate::ActivateWindow`: ``` void DevToolsWindow::ActivateWindow() { if (life_stage_ != kLoadCompleted) return; \#if BUILDFLAG(IS_ANDROID) NOTIMPLEMENTED(); \#else if (is_docked_ && GetInspectedBrowserWindow()) main_web_contents_->Focus(); else if (!is_docked_ && browser_ && !browser_->window()->IsActive()) browser_->window()->Activate(); \#endif } ``` which implements: `DevToolsUIBindings::Delegate::ActivateWindow`. Electron also implements this interface in: `electron::InspectableWebContents`. However it was only setting a zoom level, therefore this commit extends it with activation of the DevTools. Only supported for DevTools manged by `electron::InspectableWebContents`. Closes: #37388 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Michał Pichliński --- shell/browser/ui/inspectable_web_contents.cc | 6 +++ .../ui/inspectable_web_contents_view.cc | 17 +++++++++ .../ui/inspectable_web_contents_view.h | 1 + spec/api-web-contents-spec.ts | 37 ++++++++++++++++++- 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/shell/browser/ui/inspectable_web_contents.cc b/shell/browser/ui/inspectable_web_contents.cc index f05476527b..50abf0fe4d 100644 --- a/shell/browser/ui/inspectable_web_contents.cc +++ b/shell/browser/ui/inspectable_web_contents.cc @@ -519,6 +519,12 @@ void InspectableWebContents::UpdateDevToolsZoomLevel(double level) { } void InspectableWebContents::ActivateWindow() { + if (embedder_message_dispatcher_) { + if (managed_devtools_web_contents_ && view_) { + view_->ActivateDevTools(); + } + } + // Set the zoom level. SetZoomLevelForWebContents(GetDevToolsWebContents(), GetDevToolsZoomLevel()); } diff --git a/shell/browser/ui/inspectable_web_contents_view.cc b/shell/browser/ui/inspectable_web_contents_view.cc index a71f544aae..88432df143 100644 --- a/shell/browser/ui/inspectable_web_contents_view.cc +++ b/shell/browser/ui/inspectable_web_contents_view.cc @@ -132,6 +132,23 @@ void InspectableWebContentsView::ShowDevTools(bool activate) { } } +void InspectableWebContentsView::ActivateDevTools() { + if (!devtools_visible_) { + return; + } + if (devtools_window_) { + if (!devtools_window_->IsActive()) { + devtools_window_->Activate(); + } + return; + } + if (devtools_web_view_) { + if (!devtools_web_view_->HasFocus()) { + devtools_web_view_->RequestFocus(); + } + } +} + void InspectableWebContentsView::CloseDevTools() { if (!devtools_visible_) return; diff --git a/shell/browser/ui/inspectable_web_contents_view.h b/shell/browser/ui/inspectable_web_contents_view.h index 282d3bf805..9fe35e4c44 100644 --- a/shell/browser/ui/inspectable_web_contents_view.h +++ b/shell/browser/ui/inspectable_web_contents_view.h @@ -49,6 +49,7 @@ class InspectableWebContentsView : public views::View { void SetCornerRadii(const gfx::RoundedCornersF& corner_radii); void ShowDevTools(bool activate); + void ActivateDevTools(); void CloseDevTools(); bool IsDevToolsViewShowing(); bool IsDevToolsViewFocused(); diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index fc4b3314fb..2005409364 100644 --- a/spec/api-web-contents-spec.ts +++ b/spec/api-web-contents-spec.ts @@ -1,6 +1,6 @@ import { BrowserWindow, ipcMain, webContents, session, app, BrowserView, WebContents, BaseWindow, WebContentsView } from 'electron/main'; -import { expect } from 'chai'; +import { assert, expect } from 'chai'; import * as cp from 'node:child_process'; import { once } from 'node:events'; @@ -995,6 +995,41 @@ describe('webContents module', () => { await devToolsClosed; expect(() => { webContents.getFocusedWebContents(); }).to.not.throw(); }); + + it('Inspect activates detached devtools window', async () => { + const window = new BrowserWindow({ show: true }); + await window.loadURL('about:blank'); + const webContentsBeforeOpenedDevtools = webContents.getAllWebContents(); + + const windowWasBlurred = once(window, 'blur'); + window.webContents.openDevTools({ mode: 'detach' }); + await windowWasBlurred; + + let devToolsWebContents = null; + for (const newWebContents of webContents.getAllWebContents()) { + const oldWebContents = webContentsBeforeOpenedDevtools.find( + oldWebContents => { + return newWebContents.id === oldWebContents.id; + }); + if (oldWebContents !== null) { + devToolsWebContents = newWebContents; + break; + } + } + assert(devToolsWebContents !== null); + + const windowFocused = once(window, 'focus'); + window.focus(); + await windowFocused; + + expect(devToolsWebContents.isFocused()).to.be.false(); + const devToolsWebContentsFocused = once(devToolsWebContents, 'focus'); + window.webContents.inspectElement(100, 100); + await devToolsWebContentsFocused; + + expect(devToolsWebContents.isFocused()).to.be.true(); + expect(window.isFocused()).to.be.false(); + }); }); describe('setDevToolsWebContents() API', () => {