diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 6bf1cf2207d6..35b878d7896b 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -1384,7 +1384,9 @@ void WebContents::HandleNewRenderFrame( if (rwh_impl) rwh_impl->disable_hidden_ = !background_throttling_; - WebFrameMain::RenderFrameCreated(render_frame_host); + auto* web_frame = WebFrameMain::FromRenderFrameHost(render_frame_host); + if (web_frame) + web_frame->Connect(); } void WebContents::RenderFrameCreated( @@ -1392,6 +1394,46 @@ void WebContents::RenderFrameCreated( HandleNewRenderFrame(render_frame_host); } +void WebContents::RenderFrameDeleted( + content::RenderFrameHost* render_frame_host) { + // A RenderFrameHost can be deleted when: + // - A WebContents is removed and its containing frames are disposed. + // - An `); + } else { + res.end(''); + } + }); + server.listen(0, '127.0.0.1', () => { + const url = `http://127.0.0.1:${(server.address() as AddressInfo).port}/`; + resolve({ server, url }); + }); + }); + afterEach(closeAllWindows); describe('WebFrame traversal APIs', () => { @@ -70,24 +88,6 @@ describe('webFrameMain module', () => { }); describe('cross-origin', () => { - type Server = { server: http.Server, url: string } - - /** Creates an HTTP server whose handler embeds the given iframe src. */ - const createServer = () => new Promise(resolve => { - const server = http.createServer((req, res) => { - const params = new URLSearchParams(url.parse(req.url || '').search || ''); - if (params.has('frameSrc')) { - res.end(``); - } else { - res.end(''); - } - }); - server.listen(0, '127.0.0.1', () => { - const url = `http://127.0.0.1:${(server.address() as AddressInfo).port}/`; - resolve({ server, url }); - }); - }); - let serverA = null as unknown as Server; let serverB = null as unknown as Server; @@ -194,21 +194,32 @@ describe('webFrameMain module', () => { }); }); - describe('disposed WebFrames', () => { + describe('RenderFrame lifespan', () => { let w: BrowserWindow; - let webFrame: WebFrameMain; - before(async () => { + beforeEach(async () => { w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } }); + }); + + it('throws upon accessing properties when disposed', async () => { await w.loadFile(path.join(subframesPath, 'frame-with-frame-container.html')); - webFrame = w.webContents.mainFrame; + const { mainFrame } = w.webContents; w.destroy(); // Wait for WebContents, and thus RenderFrameHost, to be destroyed. await new Promise(resolve => setTimeout(resolve, 0)); + expect(() => mainFrame.url).to.throw(); }); - it('throws upon accessing properties', () => { - expect(() => webFrame.url).to.throw(); + it('persists through cross-origin navigation', async () => { + const server = await createServer(); + // 'localhost' is treated as a separate origin. + const crossOriginUrl = server.url.replace('127.0.0.1', 'localhost'); + await w.loadURL(server.url); + const { mainFrame } = w.webContents; + expect(mainFrame.url).to.equal(server.url); + await w.loadURL(crossOriginUrl); + expect(w.webContents.mainFrame).to.equal(mainFrame); + expect(mainFrame.url).to.equal(crossOriginUrl); }); });