feat: expose initiator in navigation events (#37085)
This commit is contained in:
parent
ed7b5c44a2
commit
3a5ae28c95
3 changed files with 107 additions and 55 deletions
|
@ -207,8 +207,23 @@ See [`window.open()`](window-open.md) for more details and how to use this in co
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
* `event` Event
|
* `details` Event<>
|
||||||
* `url` string
|
* `url` string - The URL the frame is navigating to.
|
||||||
|
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||||
|
document. Examples of same document navigations are reference fragment
|
||||||
|
navigations, pushState/replaceState, and same page history navigation.
|
||||||
|
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||||
|
* `frame` WebFrameMain - The frame to be navigated.
|
||||||
|
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||||
|
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||||
|
frame's name), or null if the navigation was not initiated by a frame. This
|
||||||
|
can also be null if the initiating frame was deleted before the event was
|
||||||
|
emitted.
|
||||||
|
* `url` string _Deprecated_
|
||||||
|
* `isInPlace` boolean _Deprecated_
|
||||||
|
* `isMainFrame` boolean _Deprecated_
|
||||||
|
* `frameProcessId` Integer _Deprecated_
|
||||||
|
* `frameRoutingId` Integer _Deprecated_
|
||||||
|
|
||||||
Emitted when a user or the page wants to start navigation. It can happen when
|
Emitted when a user or the page wants to start navigation. It can happen when
|
||||||
the `window.location` object is changed or a user clicks a link in the page.
|
the `window.location` object is changed or a user clicks a link in the page.
|
||||||
|
@ -226,26 +241,47 @@ Calling `event.preventDefault()` will prevent the navigation.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
* `event` Event
|
* `details` Event<>
|
||||||
* `url` string
|
* `url` string - The URL the frame is navigating to.
|
||||||
* `isInPlace` boolean
|
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||||
* `isMainFrame` boolean
|
document. Examples of same document navigations are reference fragment
|
||||||
* `frameProcessId` Integer
|
navigations, pushState/replaceState, and same page history navigation.
|
||||||
* `frameRoutingId` Integer
|
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||||
|
* `frame` WebFrameMain - The frame to be navigated.
|
||||||
|
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||||
|
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||||
|
frame's name), or null if the navigation was not initiated by a frame. This
|
||||||
|
can also be null if the initiating frame was deleted before the event was
|
||||||
|
emitted.
|
||||||
|
* `url` string _Deprecated_
|
||||||
|
* `isInPlace` boolean _Deprecated_
|
||||||
|
* `isMainFrame` boolean _Deprecated_
|
||||||
|
* `frameProcessId` Integer _Deprecated_
|
||||||
|
* `frameRoutingId` Integer _Deprecated_
|
||||||
|
|
||||||
Emitted when any frame (including main) starts navigating. `isInPlace` will be
|
Emitted when any frame (including main) starts navigating.
|
||||||
`true` for in-page navigations.
|
|
||||||
|
|
||||||
#### Event: 'will-redirect'
|
#### Event: 'will-redirect'
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
* `event` Event
|
* `details` Event<>
|
||||||
* `url` string
|
* `url` string - The URL the frame is navigating to.
|
||||||
* `isInPlace` boolean
|
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||||
* `isMainFrame` boolean
|
document. Examples of same document navigations are reference fragment
|
||||||
* `frameProcessId` Integer
|
navigations, pushState/replaceState, and same page history navigation.
|
||||||
* `frameRoutingId` Integer
|
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||||
|
* `frame` WebFrameMain - The frame to be navigated.
|
||||||
|
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||||
|
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||||
|
frame's name), or null if the navigation was not initiated by a frame. This
|
||||||
|
can also be null if the initiating frame was deleted before the event was
|
||||||
|
emitted.
|
||||||
|
* `url` string _Deprecated_
|
||||||
|
* `isInPlace` boolean _Deprecated_
|
||||||
|
* `isMainFrame` boolean _Deprecated_
|
||||||
|
* `frameProcessId` Integer _Deprecated_
|
||||||
|
* `frameRoutingId` Integer _Deprecated_
|
||||||
|
|
||||||
Emitted when a server side redirect occurs during navigation. For example a 302
|
Emitted when a server side redirect occurs during navigation. For example a 302
|
||||||
redirect.
|
redirect.
|
||||||
|
@ -260,12 +296,23 @@ redirect).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
* `event` Event
|
* `details` Event<>
|
||||||
* `url` string
|
* `url` string - The URL the frame is navigating to.
|
||||||
* `isInPlace` boolean
|
* `isSameDocument` boolean - Whether the navigation happened without changing
|
||||||
* `isMainFrame` boolean
|
document. Examples of same document navigations are reference fragment
|
||||||
* `frameProcessId` Integer
|
navigations, pushState/replaceState, and same page history navigation.
|
||||||
* `frameRoutingId` Integer
|
* `isMainFrame` boolean - True if the navigation is taking place in a main frame.
|
||||||
|
* `frame` WebFrameMain - The frame to be navigated.
|
||||||
|
* `initiator` WebFrameMain (optional) - The frame which initiated the
|
||||||
|
navigation, which can be a parent frame (e.g. via `window.open` with a
|
||||||
|
frame's name), or null if the navigation was not initiated by a frame. This
|
||||||
|
can also be null if the initiating frame was deleted before the event was
|
||||||
|
emitted.
|
||||||
|
* `url` string _Deprecated_
|
||||||
|
* `isInPlace` boolean _Deprecated_
|
||||||
|
* `isMainFrame` boolean _Deprecated_
|
||||||
|
* `frameProcessId` Integer _Deprecated_
|
||||||
|
* `frameRoutingId` Integer _Deprecated_
|
||||||
|
|
||||||
Emitted after a server side redirect occurs during navigation. For example a 302
|
Emitted after a server side redirect occurs during navigation. For example a 302
|
||||||
redirect.
|
redirect.
|
||||||
|
|
|
@ -112,6 +112,7 @@
|
||||||
#include "shell/common/gin_converters/gurl_converter.h"
|
#include "shell/common/gin_converters/gurl_converter.h"
|
||||||
#include "shell/common/gin_converters/image_converter.h"
|
#include "shell/common/gin_converters/image_converter.h"
|
||||||
#include "shell/common/gin_converters/net_converter.h"
|
#include "shell/common/gin_converters/net_converter.h"
|
||||||
|
#include "shell/common/gin_converters/optional_converter.h"
|
||||||
#include "shell/common/gin_converters/value_converter.h"
|
#include "shell/common/gin_converters/value_converter.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "shell/common/gin_helper/object_template_builder.h"
|
#include "shell/common/gin_helper/object_template_builder.h"
|
||||||
|
@ -1621,8 +1622,7 @@ void WebContents::RenderFrameHostChanged(content::RenderFrameHost* old_host,
|
||||||
//
|
//
|
||||||
// |old_host| can be a nullptr so we use |new_host| for looking up the
|
// |old_host| can be a nullptr so we use |new_host| for looking up the
|
||||||
// WebFrameMain instance.
|
// WebFrameMain instance.
|
||||||
auto* web_frame =
|
auto* web_frame = WebFrameMain::FromRenderFrameHost(new_host);
|
||||||
WebFrameMain::FromFrameTreeNodeId(new_host->GetFrameTreeNodeId());
|
|
||||||
if (web_frame) {
|
if (web_frame) {
|
||||||
web_frame->UpdateRenderFrameHost(new_host);
|
web_frame->UpdateRenderFrameHost(new_host);
|
||||||
}
|
}
|
||||||
|
@ -1761,7 +1761,7 @@ void WebContents::DidStopLoading() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebContents::EmitNavigationEvent(
|
bool WebContents::EmitNavigationEvent(
|
||||||
const std::string& event,
|
const std::string& event_name,
|
||||||
content::NavigationHandle* navigation_handle) {
|
content::NavigationHandle* navigation_handle) {
|
||||||
bool is_main_frame = navigation_handle->IsInMainFrame();
|
bool is_main_frame = navigation_handle->IsInMainFrame();
|
||||||
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
|
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
|
||||||
|
@ -1782,8 +1782,30 @@ bool WebContents::EmitNavigationEvent(
|
||||||
}
|
}
|
||||||
bool is_same_document = navigation_handle->IsSameDocument();
|
bool is_same_document = navigation_handle->IsSameDocument();
|
||||||
auto url = navigation_handle->GetURL();
|
auto url = navigation_handle->GetURL();
|
||||||
return Emit(event, url, is_same_document, is_main_frame, frame_process_id,
|
content::RenderFrameHost* initiator_frame_host =
|
||||||
frame_routing_id);
|
navigation_handle->GetInitiatorFrameToken().has_value()
|
||||||
|
? content::RenderFrameHost::FromFrameToken(
|
||||||
|
navigation_handle->GetInitiatorProcessID(),
|
||||||
|
navigation_handle->GetInitiatorFrameToken().value())
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
|
||||||
|
gin::Handle<gin_helper::internal::Event> event =
|
||||||
|
gin_helper::internal::Event::New(isolate);
|
||||||
|
v8::Local<v8::Object> event_object = event.ToV8().As<v8::Object>();
|
||||||
|
|
||||||
|
gin_helper::Dictionary dict(isolate, event_object);
|
||||||
|
dict.Set("url", url);
|
||||||
|
dict.Set("isSameDocument", is_same_document);
|
||||||
|
dict.Set("isMainFrame", is_main_frame);
|
||||||
|
dict.Set("frame", frame_host);
|
||||||
|
dict.SetGetter("initiator", initiator_frame_host);
|
||||||
|
|
||||||
|
EmitWithoutEvent(event_name, event, url, is_same_document, is_main_frame,
|
||||||
|
frame_process_id, frame_routing_id);
|
||||||
|
return event->GetDefaultPrevented();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::Message(bool internal,
|
void WebContents::Message(bool internal,
|
||||||
|
|
|
@ -5,7 +5,8 @@ import * as fs from 'fs';
|
||||||
import * as qs from 'querystring';
|
import * as qs from 'querystring';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main';
|
import { AddressInfo } from 'net';
|
||||||
|
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents, WebFrameMain } from 'electron/main';
|
||||||
|
|
||||||
import { emittedUntil, emittedNTimes } from './lib/events-helpers';
|
import { emittedUntil, emittedNTimes } from './lib/events-helpers';
|
||||||
import { ifit, ifdescribe, defer, listen } from './lib/spec-helpers';
|
import { ifit, ifdescribe, defer, listen } from './lib/spec-helpers';
|
||||||
|
@ -553,35 +554,17 @@ describe('BrowserWindow module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is triggered when a cross-origin iframe navigates _top', async () => {
|
it('is triggered when a cross-origin iframe navigates _top', async () => {
|
||||||
await w.loadURL(`data:text/html,<iframe src="${url}/navigate-top"></iframe>`);
|
w.loadURL(`data:text/html,<iframe src="http://127.0.0.1:${(server.address() as AddressInfo).port}/navigate-top"></iframe>`);
|
||||||
await setTimeout(1000);
|
await emittedUntil(w.webContents, 'did-frame-finish-load', (e: any, isMainFrame: boolean) => !isMainFrame);
|
||||||
w.webContents.debugger.attach('1.1');
|
let initiator: WebFrameMain | undefined;
|
||||||
const targets = await w.webContents.debugger.sendCommand('Target.getTargets');
|
w.webContents.on('will-navigate', (e) => {
|
||||||
const iframeTarget = targets.targetInfos.find((t: any) => t.type === 'iframe');
|
initiator = e.initiator;
|
||||||
const { sessionId } = await w.webContents.debugger.sendCommand('Target.attachToTarget', {
|
|
||||||
targetId: iframeTarget.targetId,
|
|
||||||
flatten: true
|
|
||||||
});
|
});
|
||||||
let willNavigateEmitted = false;
|
const subframe = w.webContents.mainFrame.frames[0];
|
||||||
w.webContents.on('will-navigate', () => {
|
subframe.executeJavaScript('document.getElementsByTagName("a")[0].click()', true);
|
||||||
willNavigateEmitted = true;
|
|
||||||
});
|
|
||||||
await w.webContents.debugger.sendCommand('Input.dispatchMouseEvent', {
|
|
||||||
type: 'mousePressed',
|
|
||||||
x: 10,
|
|
||||||
y: 10,
|
|
||||||
clickCount: 1,
|
|
||||||
button: 'left'
|
|
||||||
}, sessionId);
|
|
||||||
await w.webContents.debugger.sendCommand('Input.dispatchMouseEvent', {
|
|
||||||
type: 'mouseReleased',
|
|
||||||
x: 10,
|
|
||||||
y: 10,
|
|
||||||
clickCount: 1,
|
|
||||||
button: 'left'
|
|
||||||
}, sessionId);
|
|
||||||
await once(w.webContents, 'did-navigate');
|
await once(w.webContents, 'did-navigate');
|
||||||
expect(willNavigateEmitted).to.be.true();
|
expect(initiator).not.to.be.undefined();
|
||||||
|
expect(initiator).to.equal(subframe);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue