From 43b0d422424873a902287da12b79bd2dbaf5a646 Mon Sep 17 00:00:00 2001 From: bridiver Date: Tue, 31 May 2016 23:23:14 -0700 Subject: [PATCH] let the navigation controller manage history --- atom/browser/api/atom_api_extension.cc | 9 +- atom/browser/api/atom_api_extension.h | 1 + atom/browser/api/atom_api_web_contents.cc | 81 ++++++++-- atom/browser/api/atom_api_web_contents.h | 9 ++ atom/browser/web_contents_preferences.h | 9 +- atom/common/api/resources/ipc_bindings.js | 21 --- lib/browser/api/navigation-controller.js | 147 ------------------- lib/browser/guest-view-manager.js | 3 +- lib/renderer/override.js | 27 ---- lib/renderer/web-view/guest-view-internal.js | 3 +- 10 files changed, 95 insertions(+), 215 deletions(-) diff --git a/atom/browser/api/atom_api_extension.cc b/atom/browser/api/atom_api_extension.cc index 3f30253af9..559f9c3b7f 100644 --- a/atom/browser/api/atom_api_extension.cc +++ b/atom/browser/api/atom_api_extension.cc @@ -233,13 +233,18 @@ bool Extension::IsBackgroundPageUrl(GURL url, } // static -bool Extension::IsBackgroundPage(WebContents* web_contents) { - auto browser_context = web_contents->web_contents()->GetBrowserContext(); +bool Extension::IsBackgroundPageWebContents(content::WebContents* web_contents) { + auto browser_context = web_contents->GetBrowserContext(); auto url = web_contents->GetURL(); return IsBackgroundPageUrl(url, browser_context); } +// static +bool Extension::IsBackgroundPage(WebContents* web_contents) { + return IsBackgroundPageWebContents(web_contents->web_contents()); +} + // static v8::Local Extension::TabValue(v8::Isolate* isolate, WebContents* web_contents) { diff --git a/atom/browser/api/atom_api_extension.h b/atom/browser/api/atom_api_extension.h index 288e1b873c..836db2ec8e 100644 --- a/atom/browser/api/atom_api_extension.h +++ b/atom/browser/api/atom_api_extension.h @@ -53,6 +53,7 @@ class Extension : public content::NotificationObserver { static bool IsBackgroundPageUrl(GURL url, content::BrowserContext* browser_context); + static bool IsBackgroundPageWebContents(content::WebContents* web_contents); static bool IsBackgroundPage(WebContents* web_contents); static v8::Local TabValue(v8::Isolate* isolate, diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 00a2294611..d43b1d90a0 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -259,6 +259,9 @@ WebContents::WebContents(v8::Isolate* isolate, if (is_guest) { guest_delegate_.reset(new WebViewGuestDelegate); create_params.guest_delegate = guest_delegate_.get(); + if (!params) + create_params.site_instance = content::SiteInstance::CreateForURL( + session->browser_context(), GURL("chrome-guest://fake-host")); } web_contents = content::WebContents::Create(create_params); @@ -988,18 +991,67 @@ void WebContents::Stop() { } void WebContents::GoBack() { - atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); - web_contents()->GetController().GoBack(); + if (web_contents()->GetController().CanGoBack()) { + atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); + web_contents()->GetController().GoBack(); + } } void WebContents::GoForward() { - atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); - web_contents()->GetController().GoForward(); + if (web_contents()->GetController().CanGoForward()) { + atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); + web_contents()->GetController().GoForward(); + } } void WebContents::GoToOffset(int offset) { + if (web_contents()->GetController().CanGoToOffset(offset)) { + atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); + web_contents()->GetController().GoToOffset(offset); + } +} + +void WebContents::GoToIndex(int index) { atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); - web_contents()->GetController().GoToOffset(offset); + web_contents()->GetController().GoToIndex(index); +} + +bool WebContents::CanGoToOffset(int offset) const { + return web_contents()->GetController().CanGoToOffset(offset); +} + +bool WebContents::CanGoBack() const { + return web_contents()->GetController().CanGoBack(); +} + +bool WebContents::CanGoForward() const { + return web_contents()->GetController().CanGoForward(); +} + +int WebContents::GetCurrentEntryIndex() const { + return web_contents()->GetController().GetCurrentEntryIndex(); +} + +int WebContents::GetLastCommittedEntryIndex() const { + return web_contents()->GetController().GetLastCommittedEntryIndex(); +} + +int WebContents::GetEntryCount() const { + return web_contents()->GetController().GetEntryCount(); +} + +const GURL& WebContents::GetURLAtIndex(int index) const { + auto entry = web_contents()->GetController().GetEntryAtIndex(index); + if (entry) + return entry->GetURL(); + else return GURL::EmptyGURL(); +} + +void WebContents::ShowRepostFormWarningDialog(content::WebContents* source) { + if (Emit("repost-form-warning")) + source->GetController().CancelPendingReload(); + else + source->GetController().ContinuePendingReload(); } bool WebContents::IsCrashed() const { @@ -1402,15 +1454,24 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("_loadURL", &WebContents::LoadURL) .SetMethod("_reload", &WebContents::Reload) .SetMethod("downloadURL", &WebContents::DownloadURL) - .SetMethod("_getURL", &WebContents::GetURL) + .SetMethod("getURL", &WebContents::GetURL) .SetMethod("getTitle", &WebContents::GetTitle) .SetMethod("isLoading", &WebContents::IsLoading) .SetMethod("isLoadingMainFrame", &WebContents::IsLoadingMainFrame) .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) - .SetMethod("_stop", &WebContents::Stop) - .SetMethod("_goBack", &WebContents::GoBack) - .SetMethod("_goForward", &WebContents::GoForward) - .SetMethod("_goToOffset", &WebContents::GoToOffset) + .SetMethod("stop", &WebContents::Stop) + .SetMethod("goBack", &WebContents::GoBack) + .SetMethod("goForward", &WebContents::GoForward) + .SetMethod("goToOffset", &WebContents::GoToOffset) + .SetMethod("goToIndex", &WebContents::GoToIndex) + .SetMethod("canGoBack", &WebContents::CanGoBack) + .SetMethod("canGoForward", &WebContents::CanGoForward) + .SetMethod("canGoToOffset", &WebContents::CanGoToOffset) + .SetMethod("getURLAtIndex", &WebContents::GetURLAtIndex) + .SetMethod("getEntryCount", &WebContents::GetEntryCount) + .SetMethod("getCurrentEntryIndex", &WebContents::GetCurrentEntryIndex) + .SetMethod("getLastCommittedEntryIndex", + &WebContents::GetLastCommittedEntryIndex) .SetMethod("isCrashed", &WebContents::IsCrashed) .SetMethod("setUserAgent", &WebContents::SetUserAgent) .SetMethod("getUserAgent", &WebContents::GetUserAgent) diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index a51fdcd36a..b1d3f86008 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -99,6 +99,15 @@ class WebContents : public mate::TrackableObject, void GoBack(); void GoForward(); void GoToOffset(int offset); + bool CanGoToOffset(int offset) const; + bool CanGoBack() const; + bool CanGoForward() const; + void GoToIndex(int index); + const GURL& GetURLAtIndex(int index) const; + int GetCurrentEntryIndex() const; + int GetLastCommittedEntryIndex() const; + int GetEntryCount() const; + void ShowRepostFormWarningDialog(content::WebContents* source) override; bool IsCrashed() const; void SetUserAgent(const std::string& user_agent); std::string GetUserAgent(); diff --git a/atom/browser/web_contents_preferences.h b/atom/browser/web_contents_preferences.h index 899d178d2d..fc6756990c 100644 --- a/atom/browser/web_contents_preferences.h +++ b/atom/browser/web_contents_preferences.h @@ -7,6 +7,7 @@ #include +#include "atom/browser/api/atom_api_extension.h" #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/values.h" @@ -52,14 +53,10 @@ class WebContentsPreferences #endif return !( // node integration is disabled - cmd_line->GetSwitchValueASCII(switches::kNodeIntegration) == "false" && + cmd_line->GetSwitchValueASCII(switches::kNodeIntegration) != "true" && // and there is no preload script !cmd_line->HasSwitch(switches::kPreloadScript) && - !cmd_line->HasSwitch(switches::kPreloadURL) && - // and this is a guest renderer process - (cmd_line->GetSwitchValueASCII(::switches::kProcessType) == - ::switches::kRendererProcess && - cmd_line->HasSwitch(switches::kGuestInstanceID))); + !cmd_line->HasSwitch(switches::kPreloadURL)); } static bool run_node() { diff --git a/atom/common/api/resources/ipc_bindings.js b/atom/common/api/resources/ipc_bindings.js index 973f7472dc..d7e2271889 100644 --- a/atom/common/api/resources/ipc_bindings.js +++ b/atom/common/api/resources/ipc_bindings.js @@ -14,27 +14,6 @@ exports.didCreateDocumentElement = function() { window.confirm = function(message, title) { return ipc.sendSync('window-confirm', message, title); }; - - // copied from override.js - // TODO(bridiver) is this even necessary? I don't think we need - // or want these navigation controller hacks for nodeless renderers - var sendHistoryOperation = function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return ipc.send.apply(ipc, ['ELECTRON_NAVIGATION_CONTROLLER'].concat(slice.call(args))); - }; - - window.history.back = function() { - return sendHistoryOperation('goBack'); - }; - - window.history.forward = function() { - return sendHistoryOperation('goForward'); - }; - - window.history.go = function(offset) { - return sendHistoryOperation('goToOffset', offset); - }; }; exports.binding = ipc; diff --git a/lib/browser/api/navigation-controller.js b/lib/browser/api/navigation-controller.js index e2cb57132b..12d983fced 100644 --- a/lib/browser/api/navigation-controller.js +++ b/lib/browser/api/navigation-controller.js @@ -2,174 +2,27 @@ const {ipcMain} = require('electron') -// The history operation in renderer is redirected to browser. -ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER', function (event, method, ...args) { - var ref - (ref = event.sender)[method].apply(ref, args) -}) - -ipcMain.on('ELECTRON_SYNC_NAVIGATION_CONTROLLER', function (event, method, ...args) { - var ref - event.returnValue = (ref = event.sender)[method].apply(ref, args) -}) - -// JavaScript implementation of Chromium's NavigationController. -// Instead of relying on Chromium for history control, we compeletely do history -// control on user land, and only rely on WebContents.loadURL for navigation. -// This helps us avoid Chromium's various optimizations so we can ensure renderer -// process is restarted everytime. var NavigationController = (function () { function NavigationController (webContents) { this.webContents = webContents - this.clearHistory() - - // webContents may have already navigated to a page. - if (this.webContents._getURL()) { - this.currentIndex++ - this.history.push(this.webContents._getURL()) - } - this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => { - var currentEntry - if (this.inPageIndex > -1 && !inPage) { - // Navigated to a new page, clear in-page mark. - this.inPageIndex = -1 - } else if (this.inPageIndex === -1 && inPage) { - // Started in-page navigations. - this.inPageIndex = this.currentIndex - } - if (this.pendingIndex >= 0) { - // Go to index. - this.currentIndex = this.pendingIndex - this.pendingIndex = -1 - this.history[this.currentIndex] = url - } else if (replaceEntry) { - // Non-user initialized navigation. - this.history[this.currentIndex] = url - } else { - // Normal navigation. Clear history. - this.history = this.history.slice(0, this.currentIndex + 1) - currentEntry = this.history[this.currentIndex] - if ((currentEntry != null ? currentEntry.url : void 0) !== url) { - this.currentIndex++ - return this.history.push(url) - } - } - }) } NavigationController.prototype.loadURL = function (url, options) { if (options == null) { options = {} } - this.pendingIndex = -1 this.webContents._loadURL(url, options) return this.webContents.emit('load-url', url, options) } - NavigationController.prototype.getURL = function () { - if (this.currentIndex === -1) { - return '' - } else { - return this.history[this.currentIndex] - } - } - - NavigationController.prototype.stop = function () { - this.pendingIndex = -1 - return this.webContents._stop() - } - NavigationController.prototype.reload = function () { - this.pendingIndex = this.currentIndex return this.webContents._reload(false) } NavigationController.prototype.reloadIgnoringCache = function () { - this.pendingIndex = this.currentIndex return this.webContents._reload(true) } - NavigationController.prototype.canGoBack = function () { - return this.getActiveIndex() > 0 - } - - NavigationController.prototype.canGoForward = function () { - return this.getActiveIndex() < this.history.length - 1 - } - - NavigationController.prototype.canGoToIndex = function (index) { - return index >= 0 && index < this.history.length - } - - NavigationController.prototype.canGoToOffset = function (offset) { - return this.canGoToIndex(this.currentIndex + offset) - } - - NavigationController.prototype.clearHistory = function () { - this.history = [] - this.currentIndex = -1 - this.pendingIndex = -1 - this.inPageIndex = -1 - } - - NavigationController.prototype.goBack = function () { - if (!this.canGoBack()) { - return - } - this.pendingIndex = this.getActiveIndex() - 1 - if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) { - return this.webContents._goBack() - } else { - return this.webContents._loadURL(this.history[this.pendingIndex], {}) - } - } - - NavigationController.prototype.goForward = function () { - if (!this.canGoForward()) { - return - } - this.pendingIndex = this.getActiveIndex() + 1 - if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) { - return this.webContents._goForward() - } else { - return this.webContents._loadURL(this.history[this.pendingIndex], {}) - } - } - - NavigationController.prototype.goToIndex = function (index) { - if (!this.canGoToIndex(index)) { - return - } - this.pendingIndex = index - return this.webContents._loadURL(this.history[this.pendingIndex], {}) - } - - NavigationController.prototype.goToOffset = function (offset) { - var pendingIndex - if (!this.canGoToOffset(offset)) { - return - } - pendingIndex = this.currentIndex + offset - if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) { - this.pendingIndex = pendingIndex - return this.webContents._goToOffset(offset) - } else { - return this.goToIndex(pendingIndex) - } - } - - NavigationController.prototype.getActiveIndex = function () { - if (this.pendingIndex === -1) { - return this.currentIndex - } else { - return this.pendingIndex - } - } - - NavigationController.prototype.length = function () { - return this.history.length - } - return NavigationController })() diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index 4cbce9825d..9f770f98e3 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -47,7 +47,8 @@ let supportedWebViewEvents = [ 'update-target-url', 'load-progress-changed', 'set-active', - 'context-menu' + 'context-menu', + 'repost-form-warning' ] let nextInstanceId = 0 diff --git a/lib/renderer/override.js b/lib/renderer/override.js index 5667e5e16f..dad59bd7be 100644 --- a/lib/renderer/override.js +++ b/lib/renderer/override.js @@ -201,33 +201,6 @@ ipcRenderer.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (event, sourceId, m window.dispatchEvent(event) }) -// Forward history operations to browser. -var sendHistoryOperation = function (...args) { - return ipcRenderer.send.apply(ipcRenderer, ['ELECTRON_NAVIGATION_CONTROLLER'].concat(args)) -} - -var getHistoryOperation = function (...args) { - return ipcRenderer.sendSync.apply(ipcRenderer, ['ELECTRON_SYNC_NAVIGATION_CONTROLLER'].concat(args)) -} - -window.history.back = function () { - return sendHistoryOperation('goBack') -} - -window.history.forward = function () { - return sendHistoryOperation('goForward') -} - -window.history.go = function (offset) { - return sendHistoryOperation('goToOffset', offset) -} - -Object.defineProperty(window.history, 'length', { - get: function () { - return getHistoryOperation('length') - } -}) - // The initial visibilityState. let cachedVisibilityState = process.argv.includes('--hidden-page') ? 'hidden' : 'visible' diff --git a/lib/renderer/web-view/guest-view-internal.js b/lib/renderer/web-view/guest-view-internal.js index a9d8589afd..ed9937f068 100644 --- a/lib/renderer/web-view/guest-view-internal.js +++ b/lib/renderer/web-view/guest-view-internal.js @@ -44,7 +44,8 @@ var WEB_VIEW_EVENTS = { 'update-target-url': ['url', 'x', 'y'], 'load-progress-changed': ['progress'], 'set-active': ['active'], - 'context-menu': ['params'] + 'context-menu': ['params'], + 'repost-form-warning': [] } var DEPRECATED_EVENTS = {