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: add will-redirect #13866

Merged
merged 8 commits into from Sep 15, 2018
18 changes: 15 additions & 3 deletions atom/browser/api/atom_api_web_contents.cc
Expand Up @@ -16,6 +16,7 @@
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/atom_javascript_dialog_manager.h"
#include "atom/browser/atom_navigation_throttle.h"
#include "atom/browser/child_web_contents_tracker.h"
#include "atom/browser/lib/bluetooth_chooser.h"
#include "atom/browser/native_window.h"
Expand Down Expand Up @@ -845,7 +846,8 @@ void WebContents::DidStopLoading() {
Emit("did-stop-loading");
}

void WebContents::DidStartNavigation(
bool WebContents::EmitNavigationEvent(
const std::string& event,
content::NavigationHandle* navigation_handle) {
bool is_main_frame = navigation_handle->IsInMainFrame();
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
Expand All @@ -866,8 +868,18 @@ void WebContents::DidStartNavigation(
}
bool is_same_document = navigation_handle->IsSameDocument();
auto url = navigation_handle->GetURL();
Emit("did-start-navigation", url, is_same_document, is_main_frame,
frame_process_id, frame_routing_id);
return Emit(event, url, is_same_document, is_main_frame, frame_process_id,
frame_routing_id);
}

void WebContents::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
EmitNavigationEvent("did-start-navigation", navigation_handle);
}

void WebContents::DidRedirectNavigation(
content::NavigationHandle* navigation_handle) {
EmitNavigationEvent("did-redirect-navigation", navigation_handle);
}

void WebContents::DidFinishNavigation(
Expand Down
5 changes: 5 additions & 0 deletions atom/browser/api/atom_api_web_contents.h
Expand Up @@ -266,6 +266,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
observers_.RemoveObserver(obs);
}

bool EmitNavigationEvent(const std::string& event,
content::NavigationHandle* navigation_handle);

protected:
WebContents(v8::Isolate* isolate,
content::WebContents* web_contents,
Expand Down Expand Up @@ -366,6 +369,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
void DidStopLoading() override;
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
void DidRedirectNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
bool OnMessageReceived(const IPC::Message& message) override;
Expand Down
9 changes: 9 additions & 0 deletions atom/browser/atom_browser_client.cc
Expand Up @@ -16,6 +16,7 @@
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/atom_navigation_throttle.h"
#include "atom/browser/atom_quota_permission_context.h"
#include "atom/browser/atom_resource_dispatcher_host_delegate.h"
#include "atom/browser/atom_speech_recognition_manager_delegate.h"
Expand Down Expand Up @@ -613,4 +614,12 @@ bool AtomBrowserClient::HandleExternalProtocol(
return true;
}

std::vector<std::unique_ptr<content::NavigationThrottle>>
AtomBrowserClient::CreateThrottlesForNavigation(
content::NavigationHandle* handle) {
std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
throttles.push_back(std::make_unique<AtomNavigationThrottle>(handle));
return throttles;
}

} // namespace atom
3 changes: 3 additions & 0 deletions atom/browser/atom_browser_client.h
Expand Up @@ -47,6 +47,9 @@ class AtomBrowserClient : public brightray::BrowserClient,
static void SetCustomServiceWorkerSchemes(
const std::vector<std::string>& schemes);

std::vector<std::unique_ptr<content::NavigationThrottle>>
CreateThrottlesForNavigation(content::NavigationHandle* handle) override;

protected:
// content::ContentBrowserClient:
void RenderProcessWillLaunch(
Expand Down
44 changes: 44 additions & 0 deletions atom/browser/atom_navigation_throttle.cc
@@ -0,0 +1,44 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/atom_navigation_throttle.h"

#include "atom/browser/api/atom_api_web_contents.h"
#include "content/public/browser/navigation_handle.h"

namespace atom {

AtomNavigationThrottle::AtomNavigationThrottle(
content::NavigationHandle* navigation_handle)
: content::NavigationThrottle(navigation_handle) {}

AtomNavigationThrottle::~AtomNavigationThrottle() {}

const char* AtomNavigationThrottle::GetNameForLogging() {
return "AtomNavigationThrottle";
}

content::NavigationThrottle::ThrottleCheckResult
AtomNavigationThrottle::WillRedirectRequest() {
auto* handle = navigation_handle();
auto* contents = handle->GetWebContents();
if (!contents) {
NOTREACHED();
return PROCEED;
}

auto api_contents =
atom::api::WebContents::CreateFrom(v8::Isolate::GetCurrent(), contents);
if (api_contents.IsEmpty()) {
NOTREACHED();
return PROCEED;
}

if (api_contents->EmitNavigationEvent("will-redirect", handle)) {
return CANCEL;
}
return PROCEED;
}

} // namespace atom
27 changes: 27 additions & 0 deletions atom/browser/atom_navigation_throttle.h
@@ -0,0 +1,27 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef ATOM_BROWSER_ATOM_NAVIGATION_THROTTLE_H_
#define ATOM_BROWSER_ATOM_NAVIGATION_THROTTLE_H_

#include "content/public/browser/navigation_throttle.h"

namespace atom {

class AtomNavigationThrottle : public content::NavigationThrottle {
public:
explicit AtomNavigationThrottle(content::NavigationHandle* handle);
~AtomNavigationThrottle() override;

AtomNavigationThrottle::ThrottleCheckResult WillRedirectRequest() override;

const char* GetNameForLogging() override;

private:
DISALLOW_COPY_AND_ASSIGN(AtomNavigationThrottle);
};

} // namespace atom

#endif // ATOM_BROWSER_ATOM_NAVIGATION_THROTTLE_H_
38 changes: 38 additions & 0 deletions docs/api/web-contents.md
Expand Up @@ -170,6 +170,7 @@ Calling `event.preventDefault()` will prevent the navigation.

Returns:

* `event` Event
* `url` String
* `isInPlace` Boolean
* `isMainFrame` Boolean
Expand All @@ -179,6 +180,43 @@ Returns:
Emitted when any frame (including main) starts navigating. `isInplace` will be
`true` for in-page navigations.

#### Event: 'will-redirect'

Returns:

* `event` Event
* `url` String
* `isInPlace` Boolean
* `isMainFrame` Boolean
* `frameProcessId` Integer
* `frameRoutingId` Integer

Emitted as a server side redirect occurs during navigation. For example a 302
redirect.

This event will be emitted after `did-start-navigation` and always before the
`did-redirect-navigation` event for the same navigation.

Calling `event.preventDefault()` will prevent the navigation (not just the
redirect).

#### Event: 'did-redirect-navigation'

Returns:

* `event` Event
* `url` String
* `isInPlace` Boolean
* `isMainFrame` Boolean
* `frameProcessId` Integer
* `frameRoutingId` Integer

Emitted after a server side redirect occurs during navigation. For example a 302
redirect.

This event can not be prevented, if you want to prevent redirects you should
checkout out the `will-redirect` event above.

#### Event: 'did-navigate'

Returns:
Expand Down
2 changes: 2 additions & 0 deletions filenames.gni
Expand Up @@ -209,6 +209,8 @@ filenames = {
"atom/browser/atom_browser_main_parts_posix.cc",
"atom/browser/atom_javascript_dialog_manager.cc",
"atom/browser/atom_javascript_dialog_manager.h",
"atom/browser/atom_navigation_throttle.h",
"atom/browser/atom_navigation_throttle.cc",
"atom/browser/atom_permission_manager.cc",
"atom/browser/atom_permission_manager.h",
"atom/browser/atom_quota_permission_context.cc",
Expand Down
63 changes: 63 additions & 0 deletions spec/api-browser-window-spec.js
Expand Up @@ -84,6 +84,12 @@ describe('BrowserWindow module', () => {
}
})
})
} else if (req.url === '/302') {
res.setHeader('Location', '/200')
res.statusCode = 302
res.end()
} else if (req.url === '/navigate-302') {
res.end(`<html><body><script>window.location='${server.url}/302'</script></body></html>`)
} else {
res.end()
}
Expand Down Expand Up @@ -344,6 +350,63 @@ describe('BrowserWindow module', () => {
})
})

describe('will-redirect event', () => {
it('is emitted on redirects', (done) => {
w.webContents.on('will-redirect', (event, url) => {
done()
})
w.loadURL(`${server.url}/302`)
})

it('is emitted after will-navigate on redirects', (done) => {
let navigateCalled = false
w.loadURL(`${server.url}/navigate-302`)
w.webContents.on('will-navigate', () => {
navigateCalled = true
})
w.webContents.on('will-redirect', (event, url) => {
expect(navigateCalled).to.equal(true, 'should have called will-navigate first')
done()
})
})

it('is emitted before did-stop-loading on redirects', (done) => {
let stopCalled = false
w.webContents.on('did-stop-loading', () => {
stopCalled = true
})
w.webContents.on('will-redirect', (event, url) => {
expect(stopCalled).to.equal(false, 'should not have called did-stop-loading first')
done()
})
w.loadURL(`${server.url}/302`)
})

it('allows the window to be closed from the event listener', (done) => {
ipcRenderer.send('close-on-will-redirect', w.id)
ipcRenderer.once('closed-on-will-redirect', () => { done() })
w.loadURL(`${server.url}/302`)
})

it('can be prevented', (done) => {
ipcRenderer.send('prevent-will-redirect', w.id)
w.webContents.on('will-navigate', (e, url) => {
expect(url).to.equal(`${server.url}/302`)
})
w.webContents.on('did-stop-loading', () => {
expect(w.webContents.getURL()).to.equal(
`${server.url}/navigate-302`,
'url should not have changed after navigation event'
)
done()
})
w.webContents.on('will-redirect', (e, url) => {
expect(url).to.equal(`${server.url}/200`)
})
w.loadURL(`${server.url}/navigate-302`)
})
})

describe('BrowserWindow.show()', () => {
before(function () {
if (isCI) {
Expand Down
16 changes: 16 additions & 0 deletions spec/static/main.js
Expand Up @@ -265,6 +265,22 @@ ipcMain.on('close-on-will-navigate', (event, id) => {
})
})

ipcMain.on('close-on-will-redirect', (event, id) => {
const contents = event.sender
const window = BrowserWindow.fromId(id)
window.webContents.once('will-redirect', (event, input) => {
window.close()
contents.send('closed-on-will-redirect')
})
})

ipcMain.on('prevent-will-redirect', (event, id) => {
const window = BrowserWindow.fromId(id)
window.webContents.once('will-redirect', (event) => {
event.preventDefault()
})
})

ipcMain.on('create-window-with-options-cycle', (event) => {
// This can't be done over remote since cycles are already
// nulled out at the IPC layer
Expand Down