feat: WebContents.focusedFrame (#45667)

feat: webContents.focusedFrame
This commit is contained in:
Sam Maddock 2025-03-04 11:38:58 -05:00 committed by GitHub
parent 54136042c6
commit 8c11764800
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 44 additions and 1 deletions

View file

@ -2391,9 +2391,14 @@ A [`WebFrameMain`](web-frame-main.md) property that represents the top frame of
#### `contents.opener` _Readonly_ #### `contents.opener` _Readonly_
A [`WebFrameMain`](web-frame-main.md) property that represents the frame that opened this WebContents, either A [`WebFrameMain | null`](web-frame-main.md) property that represents the frame that opened this WebContents, either
with open(), or by navigating a link with a target attribute. with open(), or by navigating a link with a target attribute.
#### `contents.focusedFrame` _Readonly_
A [`WebFrameMain | null`](web-frame-main.md) property that represents the currently focused frame in this WebContents.
Can be the top frame, an inner `<iframe>`, or `null` if nothing is focused.
[keyboardevent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent [keyboardevent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter [event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
[SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm [SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm

View file

@ -3751,6 +3751,10 @@ content::RenderFrameHost* WebContents::Opener() {
return web_contents()->GetOpener(); return web_contents()->GetOpener();
} }
content::RenderFrameHost* WebContents::FocusedFrame() {
return web_contents()->GetFocusedFrame();
}
void WebContents::NotifyUserActivation() { void WebContents::NotifyUserActivation() {
content::RenderFrameHost* frame = web_contents()->GetPrimaryMainFrame(); content::RenderFrameHost* frame = web_contents()->GetPrimaryMainFrame();
if (frame) if (frame)
@ -4462,6 +4466,7 @@ void WebContents::FillObjectTemplate(v8::Isolate* isolate,
.SetProperty("debugger", &WebContents::Debugger) .SetProperty("debugger", &WebContents::Debugger)
.SetProperty("mainFrame", &WebContents::MainFrame) .SetProperty("mainFrame", &WebContents::MainFrame)
.SetProperty("opener", &WebContents::Opener) .SetProperty("opener", &WebContents::Opener)
.SetProperty("focusedFrame", &WebContents::FocusedFrame)
.SetMethod("_setOwnerWindow", &WebContents::SetOwnerBaseWindow) .SetMethod("_setOwnerWindow", &WebContents::SetOwnerBaseWindow)
.Build(); .Build();
} }

View file

@ -374,6 +374,7 @@ class WebContents final : public ExclusiveAccessContext,
v8::Local<v8::Value> Debugger(v8::Isolate* isolate); v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
content::RenderFrameHost* MainFrame(); content::RenderFrameHost* MainFrame();
content::RenderFrameHost* Opener(); content::RenderFrameHost* Opener();
content::RenderFrameHost* FocusedFrame();
WebContentsZoomController* GetZoomController() { return zoom_controller_; } WebContentsZoomController* GetZoomController() { return zoom_controller_; }

View file

@ -1825,6 +1825,38 @@ describe('webContents module', () => {
}); });
}); });
describe('focusedFrame api', () => {
const focusFrame = (frame: Electron.WebFrameMain) => {
// There has to be a better way to do this...
return frame.executeJavaScript(`(${() => {
const input = document.createElement('input');
document.body.appendChild(input);
input.onfocus = () => input.remove();
input.focus();
}})()`, true);
};
it('is null before a url is committed', () => {
const w = new BrowserWindow({ show: false });
expect(w.webContents.focusedFrame).to.be.null();
});
it('is set when main frame is focused', async () => {
const w = new BrowserWindow({ show: true });
await w.loadURL('about:blank');
w.webContents.focus();
await waitUntil(() => w.webContents.focusedFrame === w.webContents.mainFrame);
});
it('is set to child frame when focused', async () => {
const w = new BrowserWindow({ show: true });
await w.loadFile(path.join(fixturesPath, 'sub-frames', 'frame-with-frame-container.html'));
const childFrame = w.webContents.mainFrame.frames[0];
await focusFrame(childFrame);
await waitUntil(() => w.webContents.focusedFrame === childFrame);
});
});
describe('render view deleted events', () => { describe('render view deleted events', () => {
let server: http.Server; let server: http.Server;
let serverUrl: string; let serverUrl: string;