Skip to content

Commit

Permalink
feat: add will-frame-navigate event (#34418)
Browse files Browse the repository at this point in the history
* feat: add will-navigate-in-frame event to webContents

* docs: add documentation for webview will-frame-navigate event

* feat: Eliminate isInPlace argument from will-frame-navigate event

* fix: Fire will-frame-navigate before will-navigate

* feat: send will-frame-navigate with a WebFrameMain in the event details

* docs: Update WebContents docs for new API signature

* feat: Add custom event forwarding for <webview> will-frame-navigate

* fix: wrap WebFrameMain so it can be sent as an event

* test: update webContents and <webview> tests to match new signatures

* chore: undo unnecessary change

* fix: don't switch will-navigate to use EmitNavigationEventDetails

* test: clean up will-navigate and will-frame-navigate tests for <webview>

* chore: apply lint fixes

* chore: move GetRenderFrameHost helper into anonymous namespace

* docs: auto-generate WillFrameNavigateDetails rather than defining it manually

* test: Update <webview> tests to actually pass under new spec runner

* docs: Add section explaining relationship between various nav events

* test: Add some tests to ensure navigation event order doesn't silently change

* test: Always monitor all nav events to ensure unexpected ones don't fire

* test: Add test to verify in-page navigation event order

* feat: Change to new style where extra params are exposed as event props

* fix: Remove unused EmitNavigationEventDetails

* fix: Update tests to use new async helpers

* docs: Rename and reorder sections documenting navigation events

---------

Co-authored-by: Milan Burda <milan.burda@gmail.com>
  • Loading branch information
itsananderson and miniak committed Mar 28, 2023
1 parent 2e1f803 commit 2b9dae4
Show file tree
Hide file tree
Showing 8 changed files with 540 additions and 14 deletions.
60 changes: 59 additions & 1 deletion docs/api/web-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,36 @@ const contents = win.webContents
console.log(contents)
```

## Navigation Events

Several events can be used to monitor navigations as they occur within a `webContents`.

### Document Navigations

When a `webContents` navigates to another page (as opposed to an [in-page navigation](web-contents.md#in-page-navigation)), the following events will be fired.

* [`did-start-navigation`](web-contents.md#event-did-start-navigation)
* [`will-frame-navigate`](web-contents.md#event-will-frame-navigate)
* [`will-navigate`](web-contents.md#event-will-navigate) (only fired when main frame navigates)
* [`will-redirect`](web-contents.md#event-will-redirect) (only fired when a redirect happens during navigation)
* [`did-redirect-navigation`](web-contents.md#event-did-redirect-navigation) (only fired when a redirect happens during navigation)
* [`did-frame-navigate`](web-contents.md#event-did-frame-navigate)
* [`did-navigate`](web-contents.md#event-did-navigate) (only fired when main frame navigates)

Subsequent events will not fire if `event.preventDefault()` is called on any of the cancellable events.

### In-page Navigation

In-page navigations don't cause the page to reload, but instead navigate to a location within the current page. These events are not cancellable. For an in-page navigations, the following events will fire in this order:

* [`did-start-navigation`](web-contents.md#event-did-start-navigation)
* [`did-navigate-in-page`](web-contents.md#event-did-navigate-in-page)

### Frame Navigation

The [`will-navigate`](web-contents.md#event-will-navigate) and [`did-navigate`](web-contents.md#event-did-navigate) events only fire when the [mainFrame](web-contents.md#contentsmainframe-readonly) navigates.
If you want to also observe navigations in `<iframe>`s, use [`will-frame-navigate`](web-contents.md#event-will-frame-navigate) and [`did-frame-navigate`](web-contents.md#event-did-frame-navigate) events.

## Methods

These methods can be accessed from the `webContents` module:
Expand Down Expand Up @@ -225,9 +255,37 @@ Returns:
* `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 on the main frame. It can happen when
the `window.location` object is changed or a user clicks a link in the page.

This event will not emit when the navigation is started programmatically with
APIs like `webContents.loadURL` and `webContents.back`.

It is also not emitted for in-page navigations, such as clicking anchor links
or updating the `window.location.hash`. Use `did-navigate-in-page` event for
this purpose.

Calling `event.preventDefault()` will prevent the navigation.

#### Event: 'will-frame-navigate'

Returns:

* `details` Event<>
* `url` string - The URL the frame is navigating to.
* `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.

Emitted when a user or the page wants to start navigation in any frame. It can happen when
the `window.location` object is changed or a user clicks a link in the page.

Unlike `will-navigate`, `will-frame-navigate` is fired when the main frame or any of its subframes attempts to navigate. When the navigation event comes from the main frame, `isMainFrame` will be `true`.

This event will not emit when the navigation is started programmatically with
APIs like `webContents.loadURL` and `webContents.back`.

Expand Down
22 changes: 22 additions & 0 deletions docs/api/webview-tag.md
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,28 @@ this purpose.

Calling `event.preventDefault()` does __NOT__ have any effect.

### Event: 'will-frame-navigate'

Returns:

* `url` string
* `isMainFrame` boolean
* `frameProcessId` Integer
* `frameRoutingId` Integer

Emitted when a user or the page wants to start navigation anywhere in the `<webview>`
or any frames embedded within. It can happen when the `window.location` object is
changed or a user clicks a link in the page.

This event will not emit when the navigation is started programmatically with
APIs like `<webview>.loadURL` and `<webview>.back`.

It is also not emitted during in-page navigation, such as clicking anchor links
or updating the `window.location.hash`. Use `did-navigate-in-page` event for
this purpose.

Calling `event.preventDefault()` does __NOT__ have any effect.

### Event: 'did-start-navigation'

Returns:
Expand Down
10 changes: 10 additions & 0 deletions lib/browser/guest-view-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ const createGuest = function (embedder: Electron.WebContents, embedderFrameId: n
});
});

// Dispatch guest's frame navigation event to embedder.
guest.on('will-frame-navigate', function (event: Electron.WebContentsWillFrameNavigateEventParams) {
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'will-frame-navigate', {
url: event.url,
isMainFrame: event.isMainFrame,
frameProcessId: event.frame.processId,
frameRoutingId: event.frame.routingId
});
});

// Notify guest of embedder window visibility when it is ready
// FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed
guest.on('dom-ready', function () {
Expand Down
29 changes: 18 additions & 11 deletions shell/browser/api/electron_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,23 @@ void SetBackgroundColor(content::RenderWidgetHostView* rwhv, SkColor color) {
->SetContentBackgroundColor(color);
}

content::RenderFrameHost* GetRenderFrameHost(
content::NavigationHandle* navigation_handle) {
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
content::FrameTreeNode* frame_tree_node =
content::FrameTreeNode::GloballyFindByID(frame_tree_node_id);
content::RenderFrameHostManager* render_manager =
frame_tree_node->render_manager();
content::RenderFrameHost* frame_host = nullptr;
if (render_manager) {
frame_host = render_manager->speculative_frame_host();
if (!frame_host)
frame_host = render_manager->current_frame_host();
}

return frame_host;
}

} // namespace

#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
Expand Down Expand Up @@ -1768,18 +1785,8 @@ bool WebContents::EmitNavigationEvent(
const std::string& event_name,
content::NavigationHandle* navigation_handle) {
bool is_main_frame = navigation_handle->IsInMainFrame();
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
content::FrameTreeNode* frame_tree_node =
content::FrameTreeNode::GloballyFindByID(frame_tree_node_id);
content::RenderFrameHostManager* render_manager =
frame_tree_node->render_manager();
content::RenderFrameHost* frame_host = nullptr;
if (render_manager) {
frame_host = render_manager->speculative_frame_host();
if (!frame_host)
frame_host = render_manager->current_frame_host();
}
int frame_process_id = -1, frame_routing_id = -1;
content::RenderFrameHost* frame_host = GetRenderFrameHost(navigation_handle);
if (frame_host) {
frame_process_id = frame_host->GetProcess()->GetID();
frame_routing_id = frame_host->GetRoutingID();
Expand Down
4 changes: 4 additions & 0 deletions shell/browser/electron_navigation_throttle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ ElectronNavigationThrottle::WillStartRequest() {
return PROCEED;
}

if (handle->IsRendererInitiated() &&
api_contents->EmitNavigationEvent("will-frame-navigate", handle)) {
return CANCEL;
}
if (handle->IsRendererInitiated() && handle->IsInMainFrame() &&
api_contents->EmitNavigationEvent("will-navigate", handle)) {
return CANCEL;
Expand Down
Loading

0 comments on commit 2b9dae4

Please sign in to comment.