diff --git a/docs/api/web-frame-main.md b/docs/api/web-frame-main.md index b1c9f7791443..f72a935016f1 100644 --- a/docs/api/web-frame-main.md +++ b/docs/api/web-frame-main.md @@ -66,6 +66,16 @@ These methods can be accessed from the `webFrameMain` module: Returns `WebFrameMain | undefined` - A frame with the given process and routing IDs, or `undefined` if there is no WebFrameMain associated with the given IDs. +### `webFrameMain.fromFrameToken(processId, frameToken)` + +* `processId` Integer - An `Integer` representing the internal ID of the process which owns the frame. +* `frameToken` string - A `string` token identifying the unique frame. Can also + be retrieved in the renderer process via + [`webFrame.frameToken`](web-frame.md#webframeframetoken-readonly). + +Returns `WebFrameMain | null` - A frame with the given process and frame token, +or `null` if there is no WebFrameMain associated with the given IDs. + ## Class: WebFrameMain Process: [Main](../glossary.md#main-process)
diff --git a/lib/browser/api/web-frame-main.ts b/lib/browser/api/web-frame-main.ts index ce6023f843fd..de4e74b0dd13 100644 --- a/lib/browser/api/web-frame-main.ts +++ b/lib/browser/api/web-frame-main.ts @@ -1,7 +1,7 @@ import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'; import { MessagePortMain } from '@electron/internal/browser/message-port-main'; -const { WebFrameMain, fromId } = process._linkedBinding('electron_browser_web_frame_main'); +const { WebFrameMain, fromId, fromFrameToken } = process._linkedBinding('electron_browser_web_frame_main'); Object.defineProperty(WebFrameMain.prototype, 'ipc', { get () { @@ -43,5 +43,6 @@ WebFrameMain.prototype.postMessage = function (...args) { }; export default { - fromId + fromId, + fromFrameToken }; diff --git a/shell/browser/api/electron_api_web_frame_main.cc b/shell/browser/api/electron_api_web_frame_main.cc index 5422e3204d42..2888ebaa09da 100644 --- a/shell/browser/api/electron_api_web_frame_main.cc +++ b/shell/browser/api/electron_api_web_frame_main.cc @@ -641,6 +641,31 @@ v8::Local FromID(gin_helper::ErrorThrower thrower, return WebFrameMain::From(thrower.isolate(), rfh).ToV8(); } +v8::Local FromFrameToken(gin_helper::ErrorThrower thrower, + int render_process_id, + std::string render_frame_token) { + if (!electron::Browser::Get()->is_ready()) { + thrower.ThrowError("WebFrameMain is available only after app ready"); + return v8::Null(thrower.isolate()); + } + + auto token = base::Token::FromString(render_frame_token); + if (!token) + return v8::Null(thrower.isolate()); + auto unguessable_token = + base::UnguessableToken::Deserialize(token->high(), token->low()); + if (!unguessable_token) + return v8::Null(thrower.isolate()); + auto frame_token = blink::LocalFrameToken(unguessable_token.value()); + + auto* rfh = content::RenderFrameHost::FromFrameToken( + content::GlobalRenderFrameHostToken(render_process_id, frame_token)); + if (!rfh) + return v8::Null(thrower.isolate()); + + return WebFrameMain::From(thrower.isolate(), rfh).ToV8(); +} + v8::Local FromIdIfExists(gin_helper::ErrorThrower thrower, int render_process_id, int render_frame_id) { @@ -677,6 +702,7 @@ void Initialize(v8::Local exports, gin_helper::Dictionary dict{isolate, exports}; dict.Set("WebFrameMain", WebFrameMain::GetConstructor(isolate, context)); dict.SetMethod("fromId", &FromID); + dict.SetMethod("fromFrameToken", &FromFrameToken); dict.SetMethod("_fromIdIfExists", &FromIdIfExists); dict.SetMethod("_fromFtnIdIfExists", &FromFtnIdIfExists); } diff --git a/spec/api-web-frame-main-spec.ts b/spec/api-web-frame-main-spec.ts index df9c2fe87f96..ccb109beec28 100644 --- a/spec/api-web-frame-main-spec.ts +++ b/spec/api-web-frame-main-spec.ts @@ -496,6 +496,19 @@ describe('webFrameMain module', () => { }); }); + describe('webFrameMain.fromFrameToken', () => { + it('returns null for unknown IDs', () => { + expect(webFrameMain.fromFrameToken(0, '')).to.be.null(); + }); + + it('can find existing frame', async () => { + const w = new BrowserWindow({ show: false }); + const { mainFrame } = w.webContents; + const frame = webFrameMain.fromFrameToken(mainFrame.processId, mainFrame.frameToken); + expect(frame).to.equal(mainFrame); + }); + }); + describe('webFrameMain.collectJavaScriptCallStack', () => { let server: Server; before(async () => { diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 7d612cf3769d..40ba2511682d 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -133,7 +133,8 @@ declare namespace NodeJS { interface WebFrameMainBinding { WebFrameMain: typeof Electron.WebFrameMain; - fromId(processId: number, routingId: number): Electron.WebFrameMain; + fromId(processId: number, routingId: number): Electron.WebFrameMain | undefined; + fromFrameToken(processId: number, frameToken: string): Electron.WebFrameMain | null; _fromIdIfExists(processId: number, routingId: number): Electron.WebFrameMain | null; _fromFtnIdIfExists(frameTreeNodeId: number): Electron.WebFrameMain | null; } @@ -153,6 +154,7 @@ declare namespace NodeJS { interface WebFrameBinding { mainFrame: InternalWebFrame; + WebFrame: Electron.WebFrame; } type DataPipe = {