diff --git a/docs/api/navigation-history.md b/docs/api/navigation-history.md index 7eed76851c..9b7c491e19 100644 --- a/docs/api/navigation-history.md +++ b/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. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index bf17202915..9128e0f061 100644 --- a/docs/api/web-contents.md +++ b/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. diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index 1e958e8aa5..7099131f69 100644 --- a/docs/breaking-changes.md +++ b/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. diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts index 97364de7a8..0c5ff37d54 100644 --- a/lib/browser/api/web-contents.ts +++ b/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) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index b678a24f14..2db8e9a109 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/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) diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index a54990fcb8..3c98d6a35b 100644 --- a/spec/api-web-contents-spec.ts +++ b/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); diff --git a/typings/internal-electron.d.ts b/typings/internal-electron.d.ts index 21c9ebee29..1f09f9a473 100644 --- a/typings/internal-electron.d.ts +++ b/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; //