Browse Source

feat: duplicate navigation related APIs to `contents.navigationHistory` (#41752)

* refactor: move navigation related api to navigationHistory

* docs: add deprecation messages to old web content methods

* fix: add deprecation warnings to webcontents

* fix: add deprecation warnings and make existing naviagation apis internal

* Update docs/api/web-contents.md

Co-authored-by: Sam Maddock <[email protected]>

* Update docs/api/web-contents.md

Co-authored-by: Sam Maddock <[email protected]>

* Update docs/api/web-contents.md

Co-authored-by: Sam Maddock <[email protected]>

* Update docs/api/web-contents.md

Co-authored-by: Sam Maddock <[email protected]>

* docs: fix links

* docs: add breaking change to 31

* docs: move breaking change to 32

* chore: re-run pipeline

---------

Co-authored-by: Sam Maddock <[email protected]>
Alice Zhao 10 months ago
parent
commit
406f644d26

+ 38 - 0
docs/api/navigation-history.md

@@ -9,6 +9,24 @@ Each navigation entry corresponds to a specific page. The indexing system follow
 
 ### Instance Methods
 
+#### `navigationHistory.canGoBack()`
+
+Returns `boolean` - Whether the browser can go back to previous web page.
+
+#### `navigationHistory.canGoForward()`
+
+Returns `boolean` - Whether the browser can go forward to next web page.
+
+#### `navigationHistory.canGoToOffset(offset)`
+
+* `offset` Integer
+
+Returns `boolean` - Whether the web page can go to the specified `offset` from the current entry.
+
+#### `navigationHistory.clear()`
+
+Clears the navigation history.
+
 #### `navigationHistory.getActiveIndex()`
 
 Returns `Integer` - The index of the current page, from which we would go back/forward or reload.
@@ -24,6 +42,26 @@ Returns `Object`:
 
 If index is out of bounds (greater than history length or less than 0), null will be returned.
 
+#### `navigationHistory.goBack()`
+
+Makes the browser go back a web page.
+
+#### `navigationHistory.goForward()`
+
+Makes the browser go forward a web page.
+
+#### `navigationHistory.goToIndex(index)`
+
+* `index` Integer
+
+Navigates browser to the specified absolute web page index.
+
+#### `navigationHistory.goToOffset(offset)`
+
+* `offset` Integer
+
+Navigates to the specified offset from the current entry.
+
 #### `navigationHistory.length()`
 
 Returns `Integer` - History length.

+ 24 - 8
docs/api/web-contents.md

@@ -1124,44 +1124,60 @@ Reloads the current web page.
 
 Reloads current page and ignores cache.
 
-#### `contents.canGoBack()`
+#### `contents.canGoBack()` _Deprecated_
 
 Returns `boolean` - Whether the browser can go back to previous web page.
 
-#### `contents.canGoForward()`
+**Deprecated:** Should use the new [`contents.navigationHistory.canGoBack`](navigation-history.md#navigationhistorycangoback) API.
+
+#### `contents.canGoForward()` _Deprecated_
 
 Returns `boolean` - Whether the browser can go forward to next web page.
 
-#### `contents.canGoToOffset(offset)`
+**Deprecated:** Should use the new [`contents.navigationHistory.canGoForward`](navigation-history.md#navigationhistorycangoforward) API.
+
+#### `contents.canGoToOffset(offset)` _Deprecated_
 
 * `offset` Integer
 
 Returns `boolean` - Whether the web page can go to `offset`.
 
-#### `contents.clearHistory()`
+**Deprecated:** Should use the new [`contents.navigationHistory.canGoToOffset`](navigation-history.md#navigationhistorycangotooffsetoffset) API.
+
+#### `contents.clearHistory()` _Deprecated_
 
 Clears the navigation history.
 
-#### `contents.goBack()`
+**Deprecated:** Should use the new [`contents.navigationHistory.clear`](navigation-history.md#navigationhistoryclear) API.
+
+#### `contents.goBack()` _Deprecated_
 
 Makes the browser go back a web page.
 
-#### `contents.goForward()`
+**Deprecated:** Should use the new [`contents.navigationHistory.goBack`](navigation-history.md#navigationhistorygoback) API.
+
+#### `contents.goForward()` _Deprecated_
 
 Makes the browser go forward a web page.
 
-#### `contents.goToIndex(index)`
+**Deprecated:** Should use the new [`contents.navigationHistory.goForward`](navigation-history.md#navigationhistorygoforward) API.
+
+#### `contents.goToIndex(index)` _Deprecated_
 
 * `index` Integer
 
 Navigates browser to the specified absolute web page index.
 
-#### `contents.goToOffset(offset)`
+**Deprecated:** Should use the new [`contents.navigationHistory.goToIndex`](navigation-history.md#navigationhistorygotoindexindex) API.
+
+#### `contents.goToOffset(offset)` _Deprecated_
 
 * `offset` Integer
 
 Navigates to the specified offset from the "current entry".
 
+**Deprecated:** Should use the new [`contents.navigationHistory.goToOffset`](navigation-history.md#navigationhistorygotooffsetoffset) API.
+
 #### `contents.isCrashed()`
 
 Returns `boolean` - Whether the renderer process has crashed.

+ 27 - 1
docs/breaking-changes.md

@@ -44,6 +44,32 @@ contextBridge.exposeInMainWorld('electron', {
 })
 ```
 
+### Deprecated: `clearHistory`, `canGoBack`, `goBack`, `canGoForward`, `goForward`, `canGoToOffset`, `goToOffset` on `WebContents`
+
+The navigation-related APIs are now deprecated.
+
+These APIs have been moved to the `navigationHistory` property of `WebContents` to provide a more structured and intuitive interface for managing navigation history.
+
+```js
+// Deprecated
+win.webContents.clearHistory()
+win.webContents.canGoBack()
+win.webContents.goBack()
+win.webContents.canGoForward()
+win.webContents.goForward()
+win.webContents.canGoToOffset()
+win.webContents.goToOffset(index)
+
+// Replace with
+win.webContents.navigationHistory.clear()
+win.webContents.navigationHistory.canGoBack()
+win.webContents.navigationHistory.goBack()
+win.webContents.navigationHistory.canGoForward()
+win.webContents.navigationHistory.goForward()
+win.webContents.navigationHistory.canGoToOffset()
+win.webContents.navigationHistory.goToOffset(index)
+```
+
 ## Planned Breaking API Changes (31.0)
 
 ### Removed: `WebSQL` support
@@ -52,7 +78,7 @@ Chromium has removed support for WebSQL upstream, transitioning it to Android on
 [Chromium's intent to remove discussion](https://groups.google.com/a/chromium.org/g/blink-dev/c/fWYb6evVA-w/m/wGI863zaAAAJ)
 for more information.
 
-### Behavior Changed: `nativeImage.toDataURL` will preseve PNG colorspace
+### Behavior Changed: `nativeImage.toDataURL` will preserve PNG colorspace
 
 PNG decoder implementation has been changed to preserve colorspace data, the
 encoded data returned from this function now matches it.

+ 56 - 0
lib/browser/api/web-contents.ts

@@ -510,6 +510,54 @@ const environment = process._linkedBinding('electron_common_environment');
 const loggingEnabled = () => {
   return environment.hasVar('ELECTRON_ENABLE_LOGGING') || commandLine.hasSwitch('enable-logging');
 };
+// Deprecation warnings for navigation related APIs.
+const canGoBackDeprecated = deprecate.warnOnce('webContents.canGoBack', 'webContents.navigationHistory.canGoBack');
+WebContents.prototype.canGoBack = function () {
+  canGoBackDeprecated();
+  return this._canGoBack();
+};
+
+const canGoForwardDeprecated = deprecate.warnOnce('webContents.canGoForward', 'webContents.navigationHistory.canGoForward');
+WebContents.prototype.canGoForward = function () {
+  canGoForwardDeprecated();
+  return this._canGoForward();
+};
+
+const canGoToOffsetDeprecated = deprecate.warnOnce('webContents.canGoToOffset', 'webContents.navigationHistory.canGoToOffset');
+WebContents.prototype.canGoToOffset = function () {
+  canGoToOffsetDeprecated();
+  return this._canGoToOffset();
+};
+
+const clearHistoryDeprecated = deprecate.warnOnce('webContents.clearHistory', 'webContents.navigationHistory.clear');
+WebContents.prototype.clearHistory = function () {
+  clearHistoryDeprecated();
+  return this._clearHistory();
+};
+
+const goBackDeprecated = deprecate.warnOnce('webContents.goBack', 'webContents.navigationHistory.goBack');
+WebContents.prototype.goBack = function () {
+  goBackDeprecated();
+  return this._goBack();
+};
+
+const goForwardDeprecated = deprecate.warnOnce('webContents.goForward', 'webContents.navigationHistory.goForward');
+WebContents.prototype.goForward = function () {
+  goForwardDeprecated();
+  return this._goForward();
+};
+
+const goToIndexDeprecated = deprecate.warnOnce('webContents.goToIndex', 'webContents.navigationHistory.goToIndex');
+WebContents.prototype.goToIndex = function (index: number) {
+  goToIndexDeprecated();
+  return this._goToIndex(index);
+};
+
+const goToOffsetDeprecated = deprecate.warnOnce('webContents.goToOffset', 'webContents.navigationHistory.goToOffset');
+WebContents.prototype.goToOffset = function (index: number) {
+  goToOffsetDeprecated();
+  return this._goToOffset(index);
+};
 
 // Add JavaScript wrappers for WebContents class.
 WebContents.prototype._init = function () {
@@ -537,6 +585,14 @@ WebContents.prototype._init = function () {
   // maintaining a list of navigation entries for backward and forward navigation.
   Object.defineProperty(this, 'navigationHistory', {
     value: {
+      canGoBack: this._canGoBack.bind(this),
+      canGoForward: this._canGoForward.bind(this),
+      canGoToOffset: this._canGoToOffset.bind(this),
+      clear: this._clearHistory.bind(this),
+      goBack: this._goBack.bind(this),
+      goForward: this._goForward.bind(this),
+      goToIndex: this._goToIndex.bind(this),
+      goToOffset: this._goToOffset.bind(this),
       getActiveIndex: this._getActiveIndex.bind(this),
       length: this._historyLength.bind(this),
       getEntryAtIndex: this._getNavigationEntryAtIndex.bind(this)

+ 8 - 8
shell/browser/api/electron_api_web_contents.cc

@@ -4276,19 +4276,19 @@ void WebContents::FillObjectTemplate(v8::Isolate* isolate,
       .SetMethod("isLoadingMainFrame", &WebContents::IsLoadingMainFrame)
       .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
       .SetMethod("stop", &WebContents::Stop)
-      .SetMethod("canGoBack", &WebContents::CanGoBack)
-      .SetMethod("goBack", &WebContents::GoBack)
-      .SetMethod("canGoForward", &WebContents::CanGoForward)
-      .SetMethod("goForward", &WebContents::GoForward)
-      .SetMethod("canGoToOffset", &WebContents::CanGoToOffset)
-      .SetMethod("goToOffset", &WebContents::GoToOffset)
+      .SetMethod("_canGoBack", &WebContents::CanGoBack)
+      .SetMethod("_goBack", &WebContents::GoBack)
+      .SetMethod("_canGoForward", &WebContents::CanGoForward)
+      .SetMethod("_goForward", &WebContents::GoForward)
+      .SetMethod("_canGoToOffset", &WebContents::CanGoToOffset)
+      .SetMethod("_goToOffset", &WebContents::GoToOffset)
       .SetMethod("canGoToIndex", &WebContents::CanGoToIndex)
-      .SetMethod("goToIndex", &WebContents::GoToIndex)
+      .SetMethod("_goToIndex", &WebContents::GoToIndex)
       .SetMethod("_getActiveIndex", &WebContents::GetActiveIndex)
       .SetMethod("_getNavigationEntryAtIndex",
                  &WebContents::GetNavigationEntryAtIndex)
       .SetMethod("_historyLength", &WebContents::GetHistoryLength)
-      .SetMethod("clearHistory", &WebContents::ClearHistory)
+      .SetMethod("_clearHistory", &WebContents::ClearHistory)
       .SetMethod("isCrashed", &WebContents::IsCrashed)
       .SetMethod("forcefullyCrashRenderer",
                  &WebContents::ForcefullyCrashRenderer)

+ 78 - 0
spec/api-web-contents-spec.ts

@@ -567,6 +567,84 @@ describe('webContents module', () => {
       w = new BrowserWindow({ show: false });
     });
     afterEach(closeAllWindows);
+    describe('navigationHistory.canGoBack and navigationHistory.goBack API', () => {
+      it('should not be able to go back if history is empty', async () => {
+        expect(w.webContents.navigationHistory.canGoBack()).to.be.false();
+      });
+
+      it('should be able to go back if history is not empty', async () => {
+        await w.loadURL(urlPage1);
+        await w.loadURL(urlPage2);
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(1);
+        expect(w.webContents.navigationHistory.canGoBack()).to.be.true();
+        w.webContents.navigationHistory.goBack();
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
+      });
+    });
+
+    describe('navigationHistory.canGoForward and navigationHistory.goForward API', () => {
+      it('should not be able to go forward if history is empty', async () => {
+        expect(w.webContents.navigationHistory.canGoForward()).to.be.false();
+      });
+
+      it('should not be able to go forward if current index is same as history length', async () => {
+        await w.loadURL(urlPage1);
+        await w.loadURL(urlPage2);
+        expect(w.webContents.navigationHistory.canGoForward()).to.be.false();
+      });
+
+      it('should be able to go forward if history is not empty and active index is less than history length', async () => {
+        await w.loadURL(urlPage1);
+        await w.loadURL(urlPage2);
+        w.webContents.navigationHistory.goBack();
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
+        expect(w.webContents.navigationHistory.canGoForward()).to.be.true();
+        w.webContents.navigationHistory.goForward();
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(1);
+      });
+    });
+
+    describe('navigationHistory.canGoToOffset(index) and navigationHistory.goToOffset(index) API', () => {
+      it('should not be able to go to invalid offset', async () => {
+        expect(w.webContents.navigationHistory.canGoToOffset(-1)).to.be.false();
+        expect(w.webContents.navigationHistory.canGoToOffset(10)).to.be.false();
+      });
+
+      it('should be able to go to valid negative offset', async () => {
+        await w.loadURL(urlPage1);
+        await w.loadURL(urlPage2);
+        await w.loadURL(urlPage3);
+        expect(w.webContents.navigationHistory.canGoToOffset(-2)).to.be.true();
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(2);
+        w.webContents.navigationHistory.goToOffset(-2);
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0);
+      });
+
+      it('should be able to go to valid positive offset', async () => {
+        await w.loadURL(urlPage1);
+        await w.loadURL(urlPage2);
+        await w.loadURL(urlPage3);
+
+        w.webContents.navigationHistory.goBack();
+        expect(w.webContents.navigationHistory.canGoToOffset(1)).to.be.true();
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(1);
+        w.webContents.navigationHistory.goToOffset(1);
+        expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(2);
+      });
+    });
+
+    describe('navigationHistory.clear API', () => {
+      it('should be able clear history', async () => {
+        await w.loadURL(urlPage1);
+        await w.loadURL(urlPage2);
+        await w.loadURL(urlPage3);
+
+        expect(w.webContents.navigationHistory.length()).to.equal(3);
+        w.webContents.navigationHistory.clear();
+        expect(w.webContents.navigationHistory.length()).to.equal(1);
+      });
+    });
+
     describe('navigationHistory.getEntryAtIndex(index) API ', () => {
       it('should fetch default navigation entry when no urls are loaded', async () => {
         const result = w.webContents.navigationHistory.getEntryAtIndex(0);

+ 8 - 0
typings/internal-electron.d.ts

@@ -88,6 +88,14 @@ declare namespace Electron {
     _getNavigationEntryAtIndex(index: number): Electron.EntryAtIndex | null;
     _getActiveIndex(): number;
     _historyLength(): number;
+    _canGoBack(): boolean;
+    _canGoForward(): boolean;
+    _canGoToOffset(): boolean;
+    _goBack(): void;
+    _goForward(): void;
+    _goToOffset(index: number): void;
+    _goToIndex(index: number): void;
+    _clearHistory():void
     canGoToIndex(index: number): boolean;
     destroy(): void;
     // <webview>