Implement our own NavigationController
authorCheng Zhao <zcbenz@gmail.com>
Sun, 26 Apr 2015 13:28:30 +0000 (21:28 +0800)
committerCheng Zhao <zcbenz@gmail.com>
Mon, 27 Apr 2015 07:11:59 +0000 (15:11 +0800)
atom/browser/api/atom_api_web_contents.cc
atom/browser/api/atom_api_web_contents.h
atom/browser/api/lib/navigation-controller.coffee [new file with mode: 0644]
atom/browser/api/lib/web-contents.coffee
filenames.gypi

index 5853b10..4e27e1e 100644 (file)
@@ -18,6 +18,7 @@
 #include "atom/common/native_mate_converters/value_converter.h"
 #include "base/strings/utf_string_conversions.h"
 #include "brightray/browser/inspectable_web_contents.h"
+#include "brightray/browser/media/media_stream_devices_controller.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_entry.h"
@@ -30,7 +31,6 @@
 #include "content/public/browser/web_contents.h"
 #include "native_mate/dictionary.h"
 #include "native_mate/object_template_builder.h"
-#include "vendor/brightray/browser/media/media_stream_devices_controller.h"
 
 #include "atom/common/node_includes.h"
 
@@ -312,8 +312,7 @@ void WebContents::WebContentsDestroyed() {
 
 void WebContents::NavigationEntryCommitted(
     const content::LoadCommittedDetails& load_details) {
-  auto entry = web_contents()->GetController().GetLastCommittedEntry();
-  entry->SetVirtualURL(load_details.entry->GetOriginalRequestURL());
+  Emit("navigation-entry-commited", load_details.entry->GetURL());
 }
 
 void WebContents::DidAttach(int guest_proxy_routing_id) {
@@ -385,13 +384,6 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
   web_contents()->GetController().LoadURLWithParams(params);
 }
 
-GURL WebContents::GetURL() const {
-  auto entry = web_contents()->GetController().GetLastCommittedEntry();
-  if (!entry)
-    return GURL::EmptyGURL();
-  return entry->GetVirtualURL();
-}
-
 base::string16 WebContents::GetTitle() const {
   return web_contents()->GetTitle();
 }
@@ -415,46 +407,8 @@ void WebContents::Stop() {
   web_contents()->Stop();
 }
 
-void WebContents::Reload(const mate::Dictionary& options) {
-  // Navigating to a URL would always restart the renderer process, we want this
-  // because normal reloading will break our node integration.
-  // This is done by AtomBrowserClient::ShouldSwapProcessesForNavigation.
-  LoadURL(GetURL(), options);
-}
-
-void WebContents::ReloadIgnoringCache(const mate::Dictionary& options) {
-  // Hack to remove pending entries that ignores cache and treated as a fresh
-  // load.
+void WebContents::ReloadIgnoringCache() {
   web_contents()->GetController().ReloadIgnoringCache(false);
-  Reload(options);
-}
-
-bool WebContents::CanGoBack() const {
-  return web_contents()->GetController().CanGoBack();
-}
-
-bool WebContents::CanGoForward() const {
-  return web_contents()->GetController().CanGoForward();
-}
-
-bool WebContents::CanGoToOffset(int offset) const {
-  return web_contents()->GetController().CanGoToOffset(offset);
-}
-
-void WebContents::GoBack() {
-  web_contents()->GetController().GoBack();
-}
-
-void WebContents::GoForward() {
-  web_contents()->GetController().GoForward();
-}
-
-void WebContents::GoToIndex(int index) {
-  web_contents()->GetController().GoToIndex(index);
-}
-
-void WebContents::GoToOffset(int offset) {
-  web_contents()->GetController().GoToOffset(offset);
 }
 
 int WebContents::GetRoutingID() const {
@@ -599,21 +553,12 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
         .SetMethod("destroy", &WebContents::Destroy)
         .SetMethod("isAlive", &WebContents::IsAlive)
         .SetMethod("_loadUrl", &WebContents::LoadURL)
-        .SetMethod("getUrl", &WebContents::GetURL)
         .SetMethod("getTitle", &WebContents::GetTitle)
         .SetMethod("getFavicon", &WebContents::GetFavicon)
         .SetMethod("isLoading", &WebContents::IsLoading)
         .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
-        .SetMethod("stop", &WebContents::Stop)
-        .SetMethod("_reload", &WebContents::Reload)
+        .SetMethod("_stop", &WebContents::Stop)
         .SetMethod("_reloadIgnoringCache", &WebContents::ReloadIgnoringCache)
-        .SetMethod("canGoBack", &WebContents::CanGoBack)
-        .SetMethod("canGoForward", &WebContents::CanGoForward)
-        .SetMethod("canGoToOffset", &WebContents::CanGoToOffset)
-        .SetMethod("goBack", &WebContents::GoBack)
-        .SetMethod("goForward", &WebContents::GoForward)
-        .SetMethod("goToIndex", &WebContents::GoToIndex)
-        .SetMethod("goToOffset", &WebContents::GoToOffset)
         .SetMethod("getRoutingId", &WebContents::GetRoutingID)
         .SetMethod("getProcessId", &WebContents::GetProcessID)
         .SetMethod("isCrashed", &WebContents::IsCrashed)
index 4ccf3d8..371c338 100644 (file)
@@ -47,21 +47,12 @@ class WebContents : public mate::EventEmitter,
   void Destroy();
   bool IsAlive() const;
   void LoadURL(const GURL& url, const mate::Dictionary& options);
-  GURL GetURL() const;
   base::string16 GetTitle() const;
   gfx::Image GetFavicon() const;
   bool IsLoading() const;
   bool IsWaitingForResponse() const;
   void Stop();
-  void Reload(const mate::Dictionary& options);
-  void ReloadIgnoringCache(const mate::Dictionary& options);
-  bool CanGoBack() const;
-  bool CanGoForward() const;
-  bool CanGoToOffset(int offset) const;
-  void GoBack();
-  void GoForward();
-  void GoToIndex(int index);
-  void GoToOffset(int offset);
+  void ReloadIgnoringCache();
   int GetRoutingID() const;
   int GetProcessID() const;
   bool IsCrashed() const;
diff --git a/atom/browser/api/lib/navigation-controller.coffee b/atom/browser/api/lib/navigation-controller.coffee
new file mode 100644 (file)
index 0000000..8f193de
--- /dev/null
@@ -0,0 +1,76 @@
+# 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.
+class NavigationController
+  constructor: (@webContents) ->
+    @history = []
+    @currentIndex = -1
+    @pendingIndex = -1
+
+    @webContents.on 'navigation-entry-commited', (event, url) =>
+      if @pendingIndex >= 0  # Go to index.
+        @currentIndex = @pendingIndex
+        @pendingIndex = -1
+        @history[@currentIndex] = url
+      else  # Normal navigation.
+        @history = @history.slice 0, @currentIndex + 1  # Clear history.
+        if @history[@currentIndex] isnt url
+          @currentIndex++
+          @history.push url
+
+  loadUrl: (url, options={}) ->
+    @pendingIndex = -1
+    @webContents._loadUrl url, options
+
+  getUrl: ->
+    if @currentIndex is -1
+      ''
+    else
+      @history[@currentIndex]
+
+  stop: ->
+    @pendingIndex = -1
+    @webContents._stop()
+
+  reload: ->
+    @pendingIndex = @currentIndex
+    @webContents._loadUrl @getUrl(), {}
+
+  reloadIgnoringCache: ->
+    @webContents._reloadIgnoringCache()  # Rely on WebContents to clear cache.
+    @reload()
+
+  canGoBack: ->
+    @currentIndex > 0
+
+  canGoForward: ->
+    @currentIndex < @history.length
+
+  canGoToIndex: (index) ->
+    index >=0 and index < @history.length
+
+  canGoToOffset: (offset) ->
+    @canGoToIndex @currentIndex + offset
+
+  goBack: ->
+    return unless @canGoBack()
+    @pendingIndex = @currentIndex - 1
+    @webContents._loadUrl @history[@pendingIndex], {}
+
+  goForward: ->
+    return unless @canGoForward()
+    @pendingIndex = @currentIndex + 1
+    @webContents._loadUrl @history[@pendingIndex], {}
+
+  goToIndex: (index) ->
+    return unless @canGoToIndex index
+    @pendingIndex = index
+    @webContents._loadUrl @history[@pendingIndex], {}
+
+  goToOffset: (offset) ->
+    return unless @canGoToOffset offset
+    @goToIndex @currentIndex + offset
+
+module.exports = NavigationController
index cb772fd..e249ccf 100644 (file)
@@ -1,4 +1,5 @@
 EventEmitter = require('events').EventEmitter
+NavigationController = require './navigation-controller'
 binding = process.atomBinding 'web_contents'
 ipc = require 'ipc'
 
@@ -26,10 +27,12 @@ module.exports.wrap = (webContents) ->
   webContents.getId = -> "#{@getProcessId()}-#{@getRoutingId()}"
   webContents.equal = (other) -> @getId() is other.getId()
 
-  # Provide a default parameter for |urlOptions|.
-  webContents.loadUrl = (url, urlOptions={}) -> @_loadUrl url, urlOptions
-  webContents.reload = (urlOptions={}) -> @_reload urlOptions
-  webContents.reloadIgnoringCache = (urlOptions={}) -> @_reloadIgnoringCache urlOptions
+  # The navigation controller.
+  controller = new NavigationController(webContents)
+  webContents.controller = controller
+  for name, method of NavigationController.prototype when method instanceof Function
+    do (name, method) ->
+      webContents[name] = -> method.apply controller, arguments
 
   # Translate |disposition| to string for 'new-window' event.
   webContents.on '-new-window', (args..., disposition) ->
index 586d67c..0931368 100644 (file)
@@ -18,6 +18,7 @@
       'atom/browser/api/lib/ipc.coffee',
       'atom/browser/api/lib/menu.coffee',
       'atom/browser/api/lib/menu-item.coffee',
+      'atom/browser/api/lib/navigation-controller.coffee',
       'atom/browser/api/lib/power-monitor.coffee',
       'atom/browser/api/lib/protocol.coffee',
       'atom/browser/api/lib/screen.coffee',