Skip to content

Commit aeee9cf

Browse files
feat: add focus and blur events for WebContents (electron#25873)
test: add focus and blur WebContents event tests test: confirm that webcontents focus event is fired on browserwindow focus fix: mac focus event test timeout
1 parent e34d7f5 commit aeee9cf

File tree

4 files changed

+75
-3
lines changed

4 files changed

+75
-3
lines changed

docs/api/web-contents.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,14 @@ Returns:
508508

509509
Emitted when the user is requesting to change the zoom level using the mouse wheel.
510510

511+
#### Event: 'blur'
512+
513+
Emitted when the `WebContents` loses focus.
514+
515+
#### Event: 'focus'
516+
517+
Emitted when the `WebContents` gains focus.
518+
511519
#### Event: 'devtools-opened'
512520

513521
Emitted when DevTools is opened.

shell/browser/api/electron_api_web_contents.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,6 +1629,16 @@ void WebContents::DidAcquireFullscreen(content::RenderFrameHost* rfh) {
16291629
set_fullscreen_frame(rfh);
16301630
}
16311631

1632+
void WebContents::OnWebContentsFocused(
1633+
content::RenderWidgetHost* render_widget_host) {
1634+
Emit("focus");
1635+
}
1636+
1637+
void WebContents::OnWebContentsLostFocus(
1638+
content::RenderWidgetHost* render_widget_host) {
1639+
Emit("blur");
1640+
}
1641+
16321642
void WebContents::DOMContentLoaded(
16331643
content::RenderFrameHost* render_frame_host) {
16341644
auto* web_frame = WebFrameMain::FromRenderFrameHost(render_frame_host);

shell/browser/api/electron_api_web_contents.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,10 @@ class WebContents : public ExclusiveAccessContext,
621621
void DidChangeThemeColor() override;
622622
void OnCursorChanged(const content::WebCursor& cursor) override;
623623
void DidAcquireFullscreen(content::RenderFrameHost* rfh) override;
624+
void OnWebContentsFocused(
625+
content::RenderWidgetHost* render_widget_host) override;
626+
void OnWebContentsLostFocus(
627+
content::RenderWidgetHost* render_widget_host) override;
624628

625629
// InspectableWebContentsDelegate:
626630
void DevToolsReloadPage() override;

spec-main/api-web-contents-spec.ts

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -807,10 +807,10 @@ describe('webContents module', () => {
807807
});
808808
});
809809

810-
describe('focus()', () => {
811-
describe('when the web contents is hidden', () => {
810+
describe('focus APIs', () => {
811+
describe('focus()', () => {
812812
afterEach(closeAllWindows);
813-
it('does not blur the focused window', async () => {
813+
it('does not blur the focused window when the web contents is hidden', async () => {
814814
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
815815
w.show();
816816
await w.loadURL('about:blank');
@@ -825,6 +825,56 @@ describe('webContents module', () => {
825825
expect(childFocused).to.be.false();
826826
});
827827
});
828+
829+
describe('focus event', () => {
830+
afterEach(closeAllWindows);
831+
it('is triggered when web contents is focused', async () => {
832+
const w = new BrowserWindow({ show: false });
833+
await w.loadURL('about:blank');
834+
const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened');
835+
w.webContents.openDevTools();
836+
await devToolsOpened;
837+
w.webContents.devToolsWebContents!.focus();
838+
const focusPromise = emittedOnce(w.webContents, 'focus');
839+
w.webContents.focus();
840+
await expect(focusPromise).to.eventually.be.fulfilled();
841+
});
842+
843+
it('is triggered when BrowserWindow is focused', async () => {
844+
const window1 = new BrowserWindow({ show: false });
845+
const window2 = new BrowserWindow({ show: false });
846+
847+
await Promise.all([
848+
window1.loadURL('about:blank'),
849+
window2.loadURL('about:blank')
850+
]);
851+
852+
window1.showInactive();
853+
window2.showInactive();
854+
855+
let focusPromise = emittedOnce(window1.webContents, 'focus');
856+
window1.focus();
857+
await expect(focusPromise).to.eventually.be.fulfilled();
858+
859+
focusPromise = emittedOnce(window2.webContents, 'focus');
860+
window2.focus();
861+
await expect(focusPromise).to.eventually.be.fulfilled();
862+
});
863+
});
864+
865+
describe('blur event', () => {
866+
afterEach(closeAllWindows);
867+
it('is triggered when web contents is blurred', async () => {
868+
const w = new BrowserWindow({ show: true });
869+
await w.loadURL('about:blank');
870+
const blurPromise = emittedOnce(w.webContents, 'blur');
871+
const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened');
872+
w.webContents.openDevTools({ mode: 'detach' });
873+
await devToolsOpened;
874+
w.webContents.devToolsWebContents!.focus();
875+
await expect(blurPromise).to.eventually.be.fulfilled();
876+
});
877+
});
828878
});
829879

830880
describe('getOSProcessId()', () => {

0 commit comments

Comments
 (0)