Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose initiator in navigation events #37085

Merged
merged 35 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2da5a05
feat: expose initiator in navigation events
nornagon Jan 31, 2023
17fb58d
better api
nornagon Jan 31, 2023
971b0a4
methods go on the prototype
nornagon Jan 31, 2023
850ec53
refactor: move gin_helper::Constructible methods to prototype
nornagon Feb 1, 2023
98acc6b
Merge branch 'main' into navigate-initiator
nornagon Feb 1, 2023
681597f
refactor: simplify events
nornagon Feb 1, 2023
2104b3d
more cleanup
nornagon Feb 1, 2023
6bdc04d
drop unneeded headers
nornagon Feb 1, 2023
d7d044b
simplify ui_event
nornagon Feb 1, 2023
fcfc826
remove EmitCustomEvent
nornagon Feb 1, 2023
e484046
correctly initialize PreventableEvent
nornagon Feb 1, 2023
7ace33f
remove _reply and _throw
nornagon Feb 1, 2023
876ce17
make some tests more lax about prototype vs object
nornagon Feb 1, 2023
dff55f0
every PreventableEvent has a sender
nornagon Feb 1, 2023
469fe60
Merge branches 'constructible-prototype' and 'event-refactor' into na…
nornagon Feb 1, 2023
7b94f34
use PreventableEvent from the other PR
nornagon Feb 1, 2023
9d479f4
more
nornagon Feb 2, 2023
2f4909b
build: use new type defs
MarshallOfSound Feb 2, 2023
dbf6b90
fix types
nornagon Feb 2, 2023
7d8b58b
update header
nornagon Feb 2, 2023
4e0d1f1
fix types in test
nornagon Feb 2, 2023
07d8c3a
Merge branch 'event-refactor' into navigate-initiator
nornagon Feb 2, 2023
985d030
Merge branch 'constructible-prototype' into navigate-initiator
nornagon Feb 2, 2023
c0f646e
use the new event stuff
nornagon Feb 2, 2023
a4bfcd4
Merge remote-tracking branch 'origin/main' into navigate-initiator
nornagon Feb 7, 2023
215873d
Merge remote-tracking branch 'origin/main' into navigate-initiator
nornagon Feb 13, 2023
9f1adec
remove constructible2
nornagon Feb 13, 2023
4628059
fix merge
nornagon Feb 14, 2023
80e4e94
remove extra space
nornagon Feb 14, 2023
7ab2fed
use SetGetter
nornagon Feb 21, 2023
f3c942a
Merge remote-tracking branch 'origin/main' into navigate-initiator
nornagon Feb 21, 2023
87f63ff
drop debugging code
nornagon Feb 23, 2023
f18f5cd
fix merge
nornagon Feb 23, 2023
9608a2c
Merge branch 'main' into navigate-initiator
nornagon Feb 23, 2023
5126bff
fix merge
nornagon Feb 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
93 changes: 70 additions & 23 deletions docs/api/web-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,23 @@ See [`window.open()`](window-open.md) for more details and how to use this in co

Returns:

* `event` Event
* `url` string
* `details` Event<>
* `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
the `window.location` object is changed or a user clicks a link in the page.
Expand All @@ -226,26 +241,47 @@ Calling `event.preventDefault()` will prevent the navigation.

Returns:

* `event` Event
* `url` string
* `isInPlace` boolean
* `isMainFrame` boolean
* `frameProcessId` Integer
* `frameRoutingId` Integer

Emitted when any frame (including main) starts navigating. `isInPlace` will be
`true` for in-page navigations.
* `details` Event<>
* `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 any frame (including main) starts navigating.

#### Event: 'will-redirect'

Returns:

* `event` Event
* `url` string
* `isInPlace` boolean
* `isMainFrame` boolean
* `frameProcessId` Integer
* `frameRoutingId` Integer
* `details` Event<>
* `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 server side redirect occurs during navigation. For example a 302
redirect.
Expand All @@ -260,12 +296,23 @@ redirect).

Returns:

* `event` Event
* `url` string
* `isInPlace` boolean
* `isMainFrame` boolean
* `frameProcessId` Integer
* `frameRoutingId` Integer
* `details` Event<>
* `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 after a server side redirect occurs during navigation. For example a 302
redirect.
Expand Down
35 changes: 30 additions & 5 deletions shell/browser/api/electron_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/image_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_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
Expand Down Expand Up @@ -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
// WebFrameMain instance.
auto* web_frame =
WebFrameMain::FromFrameTreeNodeId(new_host->GetFrameTreeNodeId());
auto* web_frame = WebFrameMain::FromRenderFrameHost(new_host);
if (web_frame) {
web_frame->UpdateRenderFrameHost(new_host);
}
Expand Down Expand Up @@ -1761,7 +1761,7 @@ void WebContents::DidStopLoading() {
}

bool WebContents::EmitNavigationEvent(
const std::string& event,
const std::string& event_name,
content::NavigationHandle* navigation_handle) {
bool is_main_frame = navigation_handle->IsInMainFrame();
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
Expand All @@ -1782,8 +1782,33 @@ bool WebContents::EmitNavigationEvent(
}
bool is_same_document = navigation_handle->IsSameDocument();
auto url = navigation_handle->GetURL();
return Emit(event, url, is_same_document, is_main_frame, frame_process_id,
frame_routing_id);
content::RenderFrameHost* initiator_frame_host =
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.Set("initiatorURL", navigation_handle->GetInitiatorBaseUrl());
nornagon marked this conversation as resolved.
Show resolved Hide resolved
dict.Set("hadInitiator",
nornagon marked this conversation as resolved.
Show resolved Hide resolved
navigation_handle->GetInitiatorFrameToken().has_value());
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,
Expand Down
3 changes: 3 additions & 0 deletions shell/browser/api/electron_api_web_frame_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ void WebFrameMain::MarkRenderFrameDisposed() {
void WebFrameMain::UpdateRenderFrameHost(content::RenderFrameHost* rfh) {
// Should only be called when swapping frames.
render_frame_disposed_ = false;
LOG(INFO) << "old rfh token: " << render_frame_->GetFrameToken();
LOG(INFO) << "new rfh token: " << rfh->GetFrameToken();
LOG(INFO) << "rfhs equal " << (render_frame_ == rfh ? "true" : "false");
nornagon marked this conversation as resolved.
Show resolved Hide resolved
render_frame_ = rfh;
TeardownMojoConnection();
MaybeSetupMojoConnection();
Expand Down
38 changes: 10 additions & 28 deletions spec/api-browser-window-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as fs from 'fs';
import * as qs from 'querystring';
import * as http from 'http';
import * as os from 'os';
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main';
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents, WebFrameMain } from 'electron/main';

import { emittedOnce, emittedUntil, emittedNTimes } from './lib/events-helpers';
import { ifit, ifdescribe, defer, delay, listen } from './lib/spec-helpers';
Expand Down Expand Up @@ -551,35 +551,17 @@ describe('BrowserWindow module', () => {
});

it('is triggered when a cross-origin iframe navigates _top', async () => {
await w.loadURL(`data:text/html,<iframe src="${url}/navigate-top"></iframe>`);
await delay(1000);
w.webContents.debugger.attach('1.1');
const targets = await w.webContents.debugger.sendCommand('Target.getTargets');
const iframeTarget = targets.targetInfos.find((t: any) => t.type === 'iframe');
const { sessionId } = await w.webContents.debugger.sendCommand('Target.attachToTarget', {
targetId: iframeTarget.targetId,
flatten: true
});
let willNavigateEmitted = false;
w.webContents.on('will-navigate', () => {
willNavigateEmitted = true;
w.loadURL(`data:text/html,<iframe src="http://127.0.0.1:${(server.address() as AddressInfo).port}/navigate-top"></iframe>`);
await emittedUntil(w.webContents, 'did-frame-finish-load', (e: any, isMainFrame: boolean) => !isMainFrame);
let initiator: WebFrameMain | undefined;
w.webContents.on('will-navigate', (e) => {
initiator = e.initiator;
});
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);
const subframe = w.webContents.mainFrame.frames[0];
subframe.executeJavaScript('document.getElementsByTagName("a")[0].click()', true);
await emittedOnce(w.webContents, 'did-navigate');
expect(willNavigateEmitted).to.be.true();
expect(initiator).not.to.be.undefined();
expect(initiator).to.equal(subframe);
});
});

Expand Down