Merge pull request #1646 from atom/navigation-in-page

Fix handling in-page navigations
This commit is contained in:
Cheng Zhao 2015-05-11 19:47:15 +08:00
commit 4ca8039104
10 changed files with 103 additions and 15 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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();

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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