Merge pull request #1646 from atom/navigation-in-page
Fix handling in-page navigations
This commit is contained in:
commit
4ca8039104
10 changed files with 103 additions and 15 deletions
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include "atom/browser/atom_browser_client.h"
|
||||||
#include "atom/browser/atom_browser_context.h"
|
#include "atom/browser/atom_browser_context.h"
|
||||||
#include "atom/browser/atom_javascript_dialog_manager.h"
|
#include "atom/browser/atom_javascript_dialog_manager.h"
|
||||||
#include "atom/browser/native_window.h"
|
#include "atom/browser/native_window.h"
|
||||||
|
@ -348,8 +349,9 @@ void WebContents::WebContentsDestroyed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::NavigationEntryCommitted(
|
void WebContents::NavigationEntryCommitted(
|
||||||
const content::LoadCommittedDetails& load_details) {
|
const content::LoadCommittedDetails& details) {
|
||||||
Emit("navigation-entry-commited", load_details.entry->GetURL());
|
Emit("navigation-entry-commited", details.entry->GetURL(),
|
||||||
|
details.is_in_page, details.did_replace_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::DidAttach(int guest_proxy_routing_id) {
|
void WebContents::DidAttach(int guest_proxy_routing_id) {
|
||||||
|
@ -442,6 +444,21 @@ void WebContents::ReloadIgnoringCache() {
|
||||||
web_contents()->GetController().ReloadIgnoringCache(false);
|
web_contents()->GetController().ReloadIgnoringCache(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContents::GoBack() {
|
||||||
|
atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
|
||||||
|
web_contents()->GetController().GoBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContents::GoForward() {
|
||||||
|
atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
|
||||||
|
web_contents()->GetController().GoForward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContents::GoToOffset(int offset) {
|
||||||
|
atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
|
||||||
|
web_contents()->GetController().GoToOffset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
int WebContents::GetRoutingID() const {
|
int WebContents::GetRoutingID() const {
|
||||||
return web_contents()->GetRoutingID();
|
return web_contents()->GetRoutingID();
|
||||||
}
|
}
|
||||||
|
@ -610,6 +627,9 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
|
||||||
.SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
|
.SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
|
||||||
.SetMethod("_stop", &WebContents::Stop)
|
.SetMethod("_stop", &WebContents::Stop)
|
||||||
.SetMethod("_reloadIgnoringCache", &WebContents::ReloadIgnoringCache)
|
.SetMethod("_reloadIgnoringCache", &WebContents::ReloadIgnoringCache)
|
||||||
|
.SetMethod("_goBack", &WebContents::GoBack)
|
||||||
|
.SetMethod("_goForward", &WebContents::GoForward)
|
||||||
|
.SetMethod("_goToOffset", &WebContents::GoToOffset)
|
||||||
.SetMethod("getRoutingId", &WebContents::GetRoutingID)
|
.SetMethod("getRoutingId", &WebContents::GetRoutingID)
|
||||||
.SetMethod("getProcessId", &WebContents::GetProcessID)
|
.SetMethod("getProcessId", &WebContents::GetProcessID)
|
||||||
.SetMethod("isCrashed", &WebContents::IsCrashed)
|
.SetMethod("isCrashed", &WebContents::IsCrashed)
|
||||||
|
|
|
@ -53,6 +53,9 @@ class WebContents : public mate::EventEmitter,
|
||||||
bool IsWaitingForResponse() const;
|
bool IsWaitingForResponse() const;
|
||||||
void Stop();
|
void Stop();
|
||||||
void ReloadIgnoringCache();
|
void ReloadIgnoringCache();
|
||||||
|
void GoBack();
|
||||||
|
void GoForward();
|
||||||
|
void GoToOffset(int offset);
|
||||||
int GetRoutingID() const;
|
int GetRoutingID() const;
|
||||||
int GetProcessID() const;
|
int GetProcessID() const;
|
||||||
bool IsCrashed() const;
|
bool IsCrashed() const;
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
ipc = require 'ipc'
|
||||||
|
|
||||||
|
# The history operation in renderer is redirected to browser.
|
||||||
|
ipc.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
|
||||||
|
event.sender[method] args...
|
||||||
|
|
||||||
# JavaScript implementation of Chromium's NavigationController.
|
# JavaScript implementation of Chromium's NavigationController.
|
||||||
# Instead of relying on Chromium for history control, we compeletely do history
|
# Instead of relying on Chromium for history control, we compeletely do history
|
||||||
# control on user land, and only rely on WebContents.loadUrl for navigation.
|
# control on user land, and only rely on WebContents.loadUrl for navigation.
|
||||||
|
@ -8,17 +14,28 @@ class NavigationController
|
||||||
@history = []
|
@history = []
|
||||||
@currentIndex = -1
|
@currentIndex = -1
|
||||||
@pendingIndex = -1
|
@pendingIndex = -1
|
||||||
|
@inPageIndex = -1
|
||||||
|
|
||||||
@webContents.on 'navigation-entry-commited', (event, url) =>
|
@webContents.on 'navigation-entry-commited', (event, url, inPage, replaceEntry) =>
|
||||||
if @pendingIndex is -1 # Normal navigation.
|
if @inPageIndex > -1 and not inPage
|
||||||
@history = @history.slice 0, @currentIndex + 1 # Clear history.
|
# Navigated to a new page, clear in-page mark.
|
||||||
if @history[@currentIndex] isnt url
|
@inPageIndex = -1
|
||||||
@currentIndex++
|
else if @inPageIndex is -1 and inPage
|
||||||
@history.push url
|
# Started in-page navigations.
|
||||||
else # Go to index.
|
@inPageIndex = @currentIndex
|
||||||
|
|
||||||
|
if @pendingIndex >= 0 # Go to index.
|
||||||
@currentIndex = @pendingIndex
|
@currentIndex = @pendingIndex
|
||||||
@pendingIndex = -1
|
@pendingIndex = -1
|
||||||
@history[@currentIndex] = url
|
@history[@currentIndex] = url
|
||||||
|
else if replaceEntry # Non-user initialized navigation.
|
||||||
|
@history[@currentIndex] = url
|
||||||
|
else # Normal navigation.
|
||||||
|
@history = @history.slice 0, @currentIndex + 1 # Clear history.
|
||||||
|
currentEntry = @history[@currentIndex]
|
||||||
|
if currentEntry?.url isnt url
|
||||||
|
@currentIndex++
|
||||||
|
@history.push url
|
||||||
|
|
||||||
loadUrl: (url, options={}) ->
|
loadUrl: (url, options={}) ->
|
||||||
@pendingIndex = -1
|
@pendingIndex = -1
|
||||||
|
@ -57,21 +74,32 @@ class NavigationController
|
||||||
goBack: ->
|
goBack: ->
|
||||||
return unless @canGoBack()
|
return unless @canGoBack()
|
||||||
@pendingIndex = @getActiveIndex() - 1
|
@pendingIndex = @getActiveIndex() - 1
|
||||||
|
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
|
||||||
|
@webContents._goBack()
|
||||||
|
else
|
||||||
@webContents._loadUrl @history[@pendingIndex], {}
|
@webContents._loadUrl @history[@pendingIndex], {}
|
||||||
|
|
||||||
goForward: ->
|
goForward: ->
|
||||||
return unless @canGoForward()
|
return unless @canGoForward()
|
||||||
@pendingIndex = @getActiveIndex() + 1
|
@pendingIndex = @getActiveIndex() + 1
|
||||||
|
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
|
||||||
|
@webContents._goForward()
|
||||||
|
else
|
||||||
@webContents._loadUrl @history[@pendingIndex], {}
|
@webContents._loadUrl @history[@pendingIndex], {}
|
||||||
|
|
||||||
goToIndex: (index) ->
|
goToIndex: (index) ->
|
||||||
return unless @canGoToIndex index
|
return unless @canGoToIndex index
|
||||||
@pendingIndex = index
|
@pendingIndex = index
|
||||||
@webContents._loadUrl @history[@pendingIndex], {}
|
@webContents._loadUrl @history[@pendingIndex].url, {}
|
||||||
|
|
||||||
goToOffset: (offset) ->
|
goToOffset: (offset) ->
|
||||||
return unless @canGoToOffset offset
|
return unless @canGoToOffset offset
|
||||||
@goToIndex @currentIndex + offset
|
pendingIndex = @currentIndex + offset
|
||||||
|
if @inPageIndex > -1 and pendingIndex >= @inPageIndex
|
||||||
|
@pendingIndex = pendingIndex
|
||||||
|
@webContents._goToOffset offset
|
||||||
|
else
|
||||||
|
@goToIndex pendingIndex
|
||||||
|
|
||||||
getActiveIndex: ->
|
getActiveIndex: ->
|
||||||
if @pendingIndex is -1 then @currentIndex else @pendingIndex
|
if @pendingIndex is -1 then @currentIndex else @pendingIndex
|
||||||
|
|
|
@ -29,7 +29,6 @@ module.exports.wrap = (webContents) ->
|
||||||
|
|
||||||
# The navigation controller.
|
# The navigation controller.
|
||||||
controller = new NavigationController(webContents)
|
controller = new NavigationController(webContents)
|
||||||
webContents.controller = controller
|
|
||||||
for name, method of NavigationController.prototype when method instanceof Function
|
for name, method of NavigationController.prototype when method instanceof Function
|
||||||
do (name, method) ->
|
do (name, method) ->
|
||||||
webContents[name] = -> method.apply controller, arguments
|
webContents[name] = -> method.apply controller, arguments
|
||||||
|
|
|
@ -32,6 +32,9 @@ namespace atom {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Next navigation should not restart renderer process.
|
||||||
|
bool g_suppress_renderer_process_restart = false;
|
||||||
|
|
||||||
struct FindByProcessId {
|
struct FindByProcessId {
|
||||||
explicit FindByProcessId(int child_process_id)
|
explicit FindByProcessId(int child_process_id)
|
||||||
: child_process_id_(child_process_id) {
|
: child_process_id_(child_process_id) {
|
||||||
|
@ -51,6 +54,11 @@ struct FindByProcessId {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// static
|
||||||
|
void AtomBrowserClient::SuppressRendererProcessRestartForOnce() {
|
||||||
|
g_suppress_renderer_process_restart = true;
|
||||||
|
}
|
||||||
|
|
||||||
AtomBrowserClient::AtomBrowserClient()
|
AtomBrowserClient::AtomBrowserClient()
|
||||||
: dying_render_process_(nullptr) {
|
: dying_render_process_(nullptr) {
|
||||||
}
|
}
|
||||||
|
@ -131,6 +139,11 @@ void AtomBrowserClient::OverrideSiteInstanceForNavigation(
|
||||||
content::SiteInstance* current_instance,
|
content::SiteInstance* current_instance,
|
||||||
const GURL& url,
|
const GURL& url,
|
||||||
content::SiteInstance** new_instance) {
|
content::SiteInstance** new_instance) {
|
||||||
|
if (g_suppress_renderer_process_restart) {
|
||||||
|
g_suppress_renderer_process_restart = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (current_instance->HasProcess())
|
if (current_instance->HasProcess())
|
||||||
dying_render_process_ = current_instance->GetProcess();
|
dying_render_process_ = current_instance->GetProcess();
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
||||||
AtomBrowserClient();
|
AtomBrowserClient();
|
||||||
virtual ~AtomBrowserClient();
|
virtual ~AtomBrowserClient();
|
||||||
|
|
||||||
|
// Don't force renderer process to restart for once.
|
||||||
|
static void SuppressRendererProcessRestartForOnce();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// content::ContentBrowserClient:
|
// content::ContentBrowserClient:
|
||||||
void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
|
void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
|
||||||
|
|
|
@ -107,6 +107,9 @@ ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, guestInstanceId) ->
|
||||||
catch e
|
catch e
|
||||||
event.returnValue = errorToMeta e
|
event.returnValue = errorToMeta e
|
||||||
|
|
||||||
|
ipc.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) ->
|
||||||
|
event.returnValue = valueToMeta event.sender, event.sender
|
||||||
|
|
||||||
ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
|
ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
|
||||||
try
|
try
|
||||||
args = unwrapArgs event.sender, args
|
args = unwrapArgs event.sender, args
|
||||||
|
|
|
@ -110,13 +110,20 @@ exports.require = (module) ->
|
||||||
meta = ipc.sendSync 'ATOM_BROWSER_REQUIRE', module
|
meta = ipc.sendSync 'ATOM_BROWSER_REQUIRE', module
|
||||||
moduleCache[module] = metaToValue meta
|
moduleCache[module] = metaToValue meta
|
||||||
|
|
||||||
# Get current window object.
|
# Get current BrowserWindow object.
|
||||||
windowCache = null
|
windowCache = null
|
||||||
exports.getCurrentWindow = ->
|
exports.getCurrentWindow = ->
|
||||||
return windowCache if windowCache?
|
return windowCache if windowCache?
|
||||||
meta = ipc.sendSync 'ATOM_BROWSER_CURRENT_WINDOW', process.guestInstanceId
|
meta = ipc.sendSync 'ATOM_BROWSER_CURRENT_WINDOW', process.guestInstanceId
|
||||||
windowCache = metaToValue meta
|
windowCache = metaToValue meta
|
||||||
|
|
||||||
|
# Get current WebContents object.
|
||||||
|
webContentsCache = null
|
||||||
|
exports.getCurrentWebContents = ->
|
||||||
|
return webContentsCache if webContentsCache?
|
||||||
|
meta = ipc.sendSync 'ATOM_BROWSER_CURRENT_WEB_CONTENTS'
|
||||||
|
webContentsCache = metaToValue meta
|
||||||
|
|
||||||
# Get a global object in browser.
|
# Get a global object in browser.
|
||||||
exports.getGlobal = (name) ->
|
exports.getGlobal = (name) ->
|
||||||
meta = ipc.sendSync 'ATOM_BROWSER_GLOBAL', name
|
meta = ipc.sendSync 'ATOM_BROWSER_GLOBAL', name
|
||||||
|
|
|
@ -74,9 +74,17 @@ window.confirm = (message, title='') ->
|
||||||
window.prompt = ->
|
window.prompt = ->
|
||||||
throw new Error('prompt() is and will not be supported.')
|
throw new Error('prompt() is and will not be supported.')
|
||||||
|
|
||||||
|
# Simple implementation of postMessage.
|
||||||
window.opener =
|
window.opener =
|
||||||
postMessage: (message, targetOrigin='*') ->
|
postMessage: (message, targetOrigin='*') ->
|
||||||
ipc.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPENER_POSTMESSAGE', message, targetOrigin
|
ipc.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPENER_POSTMESSAGE', message, targetOrigin
|
||||||
|
|
||||||
ipc.on 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', (message, targetOrigin) ->
|
ipc.on 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', (message, targetOrigin) ->
|
||||||
window.postMessage message, targetOrigin
|
window.postMessage message, targetOrigin
|
||||||
|
|
||||||
|
# Forward history operations to browser.
|
||||||
|
sendHistoryOperation = (args...) ->
|
||||||
|
ipc.send 'ATOM_SHELL_NAVIGATION_CONTROLLER', args...
|
||||||
|
window.history.back = -> sendHistoryOperation 'goBack'
|
||||||
|
window.history.forward = -> sendHistoryOperation 'goForward'
|
||||||
|
window.history.go = (offset) -> sendHistoryOperation 'goToOffset', offset
|
||||||
|
|
|
@ -138,6 +138,10 @@ Returns the object returned by `require(module)` in the main process.
|
||||||
Returns the [BrowserWindow](browser-window.md) object which this web page
|
Returns the [BrowserWindow](browser-window.md) object which this web page
|
||||||
belongs to.
|
belongs to.
|
||||||
|
|
||||||
|
## remote.getCurrentWebContent()
|
||||||
|
|
||||||
|
Returns the WebContents object of this web page.
|
||||||
|
|
||||||
## remote.getGlobal(name)
|
## remote.getGlobal(name)
|
||||||
|
|
||||||
* `name` String
|
* `name` String
|
||||||
|
|
Loading…
Reference in a new issue