зеркало из https://github.com/electron/electron.git
feat: add WebContents.opener and webContents.fromFrame() (#35140)
* feat: add WebContents.opener * feat: add webContents.fromFrame(frame) * fix: unknown type name * test: fix and add more fromFrame cases * docs: clarified terminology
This commit is contained in:
Родитель
697a219bcb
Коммит
c09c94fc98
|
@ -45,6 +45,13 @@ returns `null`.
|
|||
Returns `WebContents` | undefined - A WebContents instance with the given ID, or
|
||||
`undefined` if there is no WebContents associated with the given ID.
|
||||
|
||||
### `webContents.fromFrame(frame)`
|
||||
|
||||
* `frame` WebFrameMain
|
||||
|
||||
Returns `WebContents` | undefined - A WebContents instance with the given WebFrameMain, or
|
||||
`undefined` if there is no WebContents associated with the given WebFrameMain.
|
||||
|
||||
### `webContents.fromDevToolsTargetId(targetId)`
|
||||
|
||||
* `targetId` string - The Chrome DevTools Protocol [TargetID](https://chromedevtools.github.io/devtools-protocol/tot/Target/#type-TargetID) associated with the WebContents instance.
|
||||
|
@ -2044,6 +2051,11 @@ when the page becomes backgrounded. This also affects the Page Visibility API.
|
|||
|
||||
A [`WebFrameMain`](web-frame-main.md) property that represents the top frame of the page's frame hierarchy.
|
||||
|
||||
#### `contents.opener` _Readonly_
|
||||
|
||||
A [`WebFrameMain`](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.
|
||||
|
||||
[keyboardevent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
|
||||
[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
|
||||
|
|
|
@ -835,6 +835,10 @@ export function fromId (id: string) {
|
|||
return binding.fromId(id);
|
||||
}
|
||||
|
||||
export function fromFrame (frame: Electron.WebFrameMain) {
|
||||
return binding.fromFrame(frame);
|
||||
}
|
||||
|
||||
export function fromDevToolsTargetId (targetId: string) {
|
||||
return binding.fromDevToolsTargetId(targetId);
|
||||
}
|
||||
|
|
|
@ -3449,6 +3449,10 @@ content::RenderFrameHost* WebContents::MainFrame() {
|
|||
return web_contents()->GetPrimaryMainFrame();
|
||||
}
|
||||
|
||||
content::RenderFrameHost* WebContents::Opener() {
|
||||
return web_contents()->GetOpener();
|
||||
}
|
||||
|
||||
void WebContents::NotifyUserActivation() {
|
||||
content::RenderFrameHost* frame = web_contents()->GetPrimaryMainFrame();
|
||||
if (frame)
|
||||
|
@ -4060,6 +4064,7 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
|
|||
.SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
|
||||
.SetProperty("debugger", &WebContents::Debugger)
|
||||
.SetProperty("mainFrame", &WebContents::MainFrame)
|
||||
.SetProperty("opener", &WebContents::Opener)
|
||||
.Build();
|
||||
}
|
||||
|
||||
|
@ -4174,6 +4179,7 @@ namespace {
|
|||
|
||||
using electron::api::GetAllWebContents;
|
||||
using electron::api::WebContents;
|
||||
using electron::api::WebFrameMain;
|
||||
|
||||
gin::Handle<WebContents> WebContentsFromID(v8::Isolate* isolate, int32_t id) {
|
||||
WebContents* contents = WebContents::FromID(id);
|
||||
|
@ -4181,6 +4187,15 @@ gin::Handle<WebContents> WebContentsFromID(v8::Isolate* isolate, int32_t id) {
|
|||
: gin::Handle<WebContents>();
|
||||
}
|
||||
|
||||
gin::Handle<WebContents> WebContentsFromFrame(v8::Isolate* isolate,
|
||||
WebFrameMain* web_frame) {
|
||||
content::RenderFrameHost* rfh = web_frame->render_frame_host();
|
||||
content::WebContents* source = content::WebContents::FromRenderFrameHost(rfh);
|
||||
WebContents* contents = WebContents::From(source);
|
||||
return contents ? gin::CreateHandle(isolate, contents)
|
||||
: gin::Handle<WebContents>();
|
||||
}
|
||||
|
||||
gin::Handle<WebContents> WebContentsFromDevToolsTargetID(
|
||||
v8::Isolate* isolate,
|
||||
std::string target_id) {
|
||||
|
@ -4209,6 +4224,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
|||
gin_helper::Dictionary dict(isolate, exports);
|
||||
dict.Set("WebContents", WebContents::GetConstructor(context));
|
||||
dict.SetMethod("fromId", &WebContentsFromID);
|
||||
dict.SetMethod("fromFrame", &WebContentsFromFrame);
|
||||
dict.SetMethod("fromDevToolsTargetId", &WebContentsFromDevToolsTargetID);
|
||||
dict.SetMethod("getAllWebContents", &GetAllWebContentsAsV8);
|
||||
}
|
||||
|
|
|
@ -333,6 +333,7 @@ class WebContents : public ExclusiveAccessContext,
|
|||
v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
|
||||
content::RenderFrameHost* MainFrame();
|
||||
content::RenderFrameHost* Opener();
|
||||
|
||||
WebContentsZoomController* GetZoomController() { return zoom_controller_; }
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as http from 'http';
|
|||
import { BrowserWindow, ipcMain, webContents, session, WebContents, app, BrowserView } from 'electron/main';
|
||||
import { emittedOnce } from './events-helpers';
|
||||
import { closeAllWindows } from './window-helpers';
|
||||
import { ifdescribe, delay, defer } from './spec-helpers';
|
||||
import { ifdescribe, delay, defer, waitUntil } from './spec-helpers';
|
||||
|
||||
const pdfjs = require('pdfjs-dist');
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
|
@ -46,6 +46,28 @@ describe('webContents module', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('fromFrame()', () => {
|
||||
it('returns WebContents for mainFrame', () => {
|
||||
const contents = (webContents as any).create() as WebContents;
|
||||
expect(webContents.fromFrame(contents.mainFrame)).to.equal(contents);
|
||||
});
|
||||
it('returns undefined for disposed frame', async () => {
|
||||
const contents = (webContents as any).create() as WebContents;
|
||||
const { mainFrame } = contents;
|
||||
contents.destroy();
|
||||
await waitUntil(() => typeof webContents.fromFrame(mainFrame) === 'undefined');
|
||||
});
|
||||
it('throws when passing invalid argument', async () => {
|
||||
let errored = false;
|
||||
try {
|
||||
webContents.fromFrame({} as any);
|
||||
} catch {
|
||||
errored = true;
|
||||
}
|
||||
expect(errored).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromDevToolsTargetId()', () => {
|
||||
it('returns WebContents for attached DevTools target', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
|
@ -1281,6 +1303,54 @@ describe('webContents module', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('opener api', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('can get opener with window.open()', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } });
|
||||
await w.loadURL('about:blank');
|
||||
const childPromise = emittedOnce(w.webContents, 'did-create-window');
|
||||
w.webContents.executeJavaScript('window.open("about:blank")', true);
|
||||
const [childWindow] = await childPromise;
|
||||
expect(childWindow.webContents.opener).to.equal(w.webContents.mainFrame);
|
||||
});
|
||||
it('has no opener when using "noopener"', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } });
|
||||
await w.loadURL('about:blank');
|
||||
const childPromise = emittedOnce(w.webContents, 'did-create-window');
|
||||
w.webContents.executeJavaScript('window.open("about:blank", undefined, "noopener")', true);
|
||||
const [childWindow] = await childPromise;
|
||||
expect(childWindow.webContents.opener).to.be.null();
|
||||
});
|
||||
it('can get opener with a[target=_blank][rel=opener]', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } });
|
||||
await w.loadURL('about:blank');
|
||||
const childPromise = emittedOnce(w.webContents, 'did-create-window');
|
||||
w.webContents.executeJavaScript(`(function() {
|
||||
const a = document.createElement('a');
|
||||
a.target = '_blank';
|
||||
a.rel = 'opener';
|
||||
a.href = 'about:blank';
|
||||
a.click();
|
||||
}())`, true);
|
||||
const [childWindow] = await childPromise;
|
||||
expect(childWindow.webContents.opener).to.equal(w.webContents.mainFrame);
|
||||
});
|
||||
it('has no opener with a[target=_blank][rel=noopener]', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } });
|
||||
await w.loadURL('about:blank');
|
||||
const childPromise = emittedOnce(w.webContents, 'did-create-window');
|
||||
w.webContents.executeJavaScript(`(function() {
|
||||
const a = document.createElement('a');
|
||||
a.target = '_blank';
|
||||
a.rel = 'noopener';
|
||||
a.href = 'about:blank';
|
||||
a.click();
|
||||
}())`, true);
|
||||
const [childWindow] = await childPromise;
|
||||
expect(childWindow.webContents.opener).to.be.null();
|
||||
});
|
||||
});
|
||||
|
||||
describe('render view deleted events', () => {
|
||||
let server: http.Server;
|
||||
let serverUrl: string;
|
||||
|
|
Загрузка…
Ссылка в новой задаче