feat: extend navigationHistory API (#42014)
* feat: extend navigationHistory API * refactor: simplify index checking * refactor: rename 'getHistory' and 'replaceHistory' methods of navigationHistory * refactor: rename delete*() methods to remove*() * feat: remove navigationHistory.replaceHistory() * tests: add tests for removeEntryAtIndex and getAllEntries
This commit is contained in:
parent
4c3014944c
commit
189675575c
8 changed files with 105 additions and 6 deletions
|
@ -35,10 +35,7 @@ Returns `Integer` - The index of the current page, from which we would go back/f
|
||||||
|
|
||||||
* `index` Integer
|
* `index` Integer
|
||||||
|
|
||||||
Returns `Object`:
|
Returns [`NavigationEntry`](structures/navigation-entry.md) - Navigation entry at the given index.
|
||||||
|
|
||||||
* `url` string - The URL of the navigation entry at the given index.
|
|
||||||
* `title` string - The page title of the navigation entry at the given index.
|
|
||||||
|
|
||||||
If index is out of bounds (greater than history length or less than 0), null will be returned.
|
If index is out of bounds (greater than history length or less than 0), null will be returned.
|
||||||
|
|
||||||
|
@ -65,3 +62,15 @@ Navigates to the specified offset from the current entry.
|
||||||
#### `navigationHistory.length()`
|
#### `navigationHistory.length()`
|
||||||
|
|
||||||
Returns `Integer` - History length.
|
Returns `Integer` - History length.
|
||||||
|
|
||||||
|
#### `navigationHistory.removeEntryAtIndex(index)`
|
||||||
|
|
||||||
|
* `index` Integer
|
||||||
|
|
||||||
|
Removes the navigation entry at the given index. Can't remove entry at the "current active index".
|
||||||
|
|
||||||
|
Returns `boolean` - Whether the navigation entry was removed from the webContents history.
|
||||||
|
|
||||||
|
#### `navigationHistory.getAllEntries()`
|
||||||
|
|
||||||
|
Returns [`NavigationEntry[]`](structures/navigation-entry.md) - WebContents complete history.
|
||||||
|
|
4
docs/api/structures/navigation-entry.md
Normal file
4
docs/api/structures/navigation-entry.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# NavigationEntry Object
|
||||||
|
|
||||||
|
* `url` string
|
||||||
|
* `title` string
|
|
@ -105,6 +105,7 @@ auto_filenames = {
|
||||||
"docs/api/structures/mime-typed-buffer.md",
|
"docs/api/structures/mime-typed-buffer.md",
|
||||||
"docs/api/structures/mouse-input-event.md",
|
"docs/api/structures/mouse-input-event.md",
|
||||||
"docs/api/structures/mouse-wheel-input-event.md",
|
"docs/api/structures/mouse-wheel-input-event.md",
|
||||||
|
"docs/api/structures/navigation-entry.md",
|
||||||
"docs/api/structures/notification-action.md",
|
"docs/api/structures/notification-action.md",
|
||||||
"docs/api/structures/notification-response.md",
|
"docs/api/structures/notification-response.md",
|
||||||
"docs/api/structures/open-external-permission-request.md",
|
"docs/api/structures/open-external-permission-request.md",
|
||||||
|
|
|
@ -595,7 +595,9 @@ WebContents.prototype._init = function () {
|
||||||
goToOffset: this._goToOffset.bind(this),
|
goToOffset: this._goToOffset.bind(this),
|
||||||
getActiveIndex: this._getActiveIndex.bind(this),
|
getActiveIndex: this._getActiveIndex.bind(this),
|
||||||
length: this._historyLength.bind(this),
|
length: this._historyLength.bind(this),
|
||||||
getEntryAtIndex: this._getNavigationEntryAtIndex.bind(this)
|
getEntryAtIndex: this._getNavigationEntryAtIndex.bind(this),
|
||||||
|
removeEntryAtIndex: this._removeNavigationEntryAtIndex.bind(this),
|
||||||
|
getAllEntries: this._getHistory.bind(this)
|
||||||
},
|
},
|
||||||
writable: false,
|
writable: false,
|
||||||
enumerable: true
|
enumerable: true
|
||||||
|
|
|
@ -2496,6 +2496,31 @@ content::NavigationEntry* WebContents::GetNavigationEntryAtIndex(
|
||||||
return web_contents()->GetController().GetEntryAtIndex(index);
|
return web_contents()->GetController().GetEntryAtIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WebContents::RemoveNavigationEntryAtIndex(int index) {
|
||||||
|
if (!CanGoToIndex(index))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return web_contents()->GetController().RemoveEntryAtIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<content::NavigationEntry*> WebContents::GetHistory() const {
|
||||||
|
const int history_length = GetHistoryLength();
|
||||||
|
auto& controller = web_contents()->GetController();
|
||||||
|
|
||||||
|
// If the history is empty, it contains only one entry and that is
|
||||||
|
// "InitialEntry"
|
||||||
|
if (history_length == 1 && controller.GetEntryAtIndex(0)->IsInitialEntry())
|
||||||
|
return std::vector<content::NavigationEntry*>();
|
||||||
|
|
||||||
|
std::vector<content::NavigationEntry*> history;
|
||||||
|
history.reserve(history_length);
|
||||||
|
|
||||||
|
for (int i = 0; i < history_length; i++)
|
||||||
|
history.push_back(controller.GetEntryAtIndex(i));
|
||||||
|
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
void WebContents::ClearHistory() {
|
void WebContents::ClearHistory() {
|
||||||
// In some rare cases (normally while there is no real history) we are in a
|
// In some rare cases (normally while there is no real history) we are in a
|
||||||
// state where we can't prune navigation entries
|
// state where we can't prune navigation entries
|
||||||
|
@ -4295,6 +4320,9 @@ void WebContents::FillObjectTemplate(v8::Isolate* isolate,
|
||||||
.SetMethod("_getNavigationEntryAtIndex",
|
.SetMethod("_getNavigationEntryAtIndex",
|
||||||
&WebContents::GetNavigationEntryAtIndex)
|
&WebContents::GetNavigationEntryAtIndex)
|
||||||
.SetMethod("_historyLength", &WebContents::GetHistoryLength)
|
.SetMethod("_historyLength", &WebContents::GetHistoryLength)
|
||||||
|
.SetMethod("_removeNavigationEntryAtIndex",
|
||||||
|
&WebContents::RemoveNavigationEntryAtIndex)
|
||||||
|
.SetMethod("_getHistory", &WebContents::GetHistory)
|
||||||
.SetMethod("_clearHistory", &WebContents::ClearHistory)
|
.SetMethod("_clearHistory", &WebContents::ClearHistory)
|
||||||
.SetMethod("isCrashed", &WebContents::IsCrashed)
|
.SetMethod("isCrashed", &WebContents::IsCrashed)
|
||||||
.SetMethod("forcefullyCrashRenderer",
|
.SetMethod("forcefullyCrashRenderer",
|
||||||
|
|
|
@ -204,6 +204,8 @@ class WebContents : public ExclusiveAccessContext,
|
||||||
void GoToIndex(int index);
|
void GoToIndex(int index);
|
||||||
int GetActiveIndex() const;
|
int GetActiveIndex() const;
|
||||||
content::NavigationEntry* GetNavigationEntryAtIndex(int index) const;
|
content::NavigationEntry* GetNavigationEntryAtIndex(int index) const;
|
||||||
|
bool RemoveNavigationEntryAtIndex(int index);
|
||||||
|
std::vector<content::NavigationEntry*> GetHistory() const;
|
||||||
void ClearHistory();
|
void ClearHistory();
|
||||||
int GetHistoryLength() const;
|
int GetHistoryLength() const;
|
||||||
const std::string GetWebRTCIPHandlingPolicy() const;
|
const std::string GetWebRTCIPHandlingPolicy() const;
|
||||||
|
|
|
@ -567,6 +567,39 @@ describe('webContents module', () => {
|
||||||
w = new BrowserWindow({ show: false });
|
w = new BrowserWindow({ show: false });
|
||||||
});
|
});
|
||||||
afterEach(closeAllWindows);
|
afterEach(closeAllWindows);
|
||||||
|
describe('navigationHistory.removeEntryAtIndex(index) API', () => {
|
||||||
|
it('should remove a navigation entry given a valid index', async () => {
|
||||||
|
await w.loadURL(urlPage1);
|
||||||
|
await w.loadURL(urlPage2);
|
||||||
|
await w.loadURL(urlPage3);
|
||||||
|
const initialLength = w.webContents.navigationHistory.length();
|
||||||
|
const wasRemoved = w.webContents.navigationHistory.removeEntryAtIndex(1); // Attempt to remove the second entry
|
||||||
|
const newLength = w.webContents.navigationHistory.length();
|
||||||
|
expect(wasRemoved).to.be.true();
|
||||||
|
expect(newLength).to.equal(initialLength - 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not remove the current active navigation entry', async () => {
|
||||||
|
await w.loadURL(urlPage1);
|
||||||
|
await w.loadURL(urlPage2);
|
||||||
|
const activeIndex = w.webContents.navigationHistory.getActiveIndex();
|
||||||
|
const wasRemoved = w.webContents.navigationHistory.removeEntryAtIndex(activeIndex);
|
||||||
|
expect(wasRemoved).to.be.false();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false given an invalid index larger than history length', async () => {
|
||||||
|
await w.loadURL(urlPage1);
|
||||||
|
const wasRemoved = w.webContents.navigationHistory.removeEntryAtIndex(5); // Index larger than history length
|
||||||
|
expect(wasRemoved).to.be.false();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false given an invalid negative index', async () => {
|
||||||
|
await w.loadURL(urlPage1);
|
||||||
|
const wasRemoved = w.webContents.navigationHistory.removeEntryAtIndex(-1); // Negative index
|
||||||
|
expect(wasRemoved).to.be.false();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('navigationHistory.canGoBack and navigationHistory.goBack API', () => {
|
describe('navigationHistory.canGoBack and navigationHistory.goBack API', () => {
|
||||||
it('should not be able to go back if history is empty', async () => {
|
it('should not be able to go back if history is empty', async () => {
|
||||||
expect(w.webContents.navigationHistory.canGoBack()).to.be.false();
|
expect(w.webContents.navigationHistory.canGoBack()).to.be.false();
|
||||||
|
@ -706,6 +739,24 @@ describe('webContents module', () => {
|
||||||
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
expect(w.webContents.navigationHistory.length()).to.equal(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('navigationHistory.getAllEntries() API', () => {
|
||||||
|
it('should return all navigation entries as an array of NavigationEntry objects', async () => {
|
||||||
|
await w.loadURL(urlPage1);
|
||||||
|
await w.loadURL(urlPage2);
|
||||||
|
await w.loadURL(urlPage3);
|
||||||
|
const entries = w.webContents.navigationHistory.getAllEntries();
|
||||||
|
expect(entries.length).to.equal(3);
|
||||||
|
expect(entries[0]).to.deep.equal({ url: urlPage1, title: 'Page 1' });
|
||||||
|
expect(entries[1]).to.deep.equal({ url: urlPage2, title: 'Page 2' });
|
||||||
|
expect(entries[2]).to.deep.equal({ url: urlPage3, title: 'Page 3' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array when there is no navigation history', async () => {
|
||||||
|
const entries = w.webContents.navigationHistory.getAllEntries();
|
||||||
|
expect(entries.length).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getFocusedWebContents() API', () => {
|
describe('getFocusedWebContents() API', () => {
|
||||||
|
|
4
typings/internal-electron.d.ts
vendored
4
typings/internal-electron.d.ts
vendored
|
@ -87,7 +87,7 @@ declare namespace Electron {
|
||||||
_print(options: any, callback?: (success: boolean, failureReason: string) => void): void;
|
_print(options: any, callback?: (success: boolean, failureReason: string) => void): void;
|
||||||
_getPrintersAsync(): Promise<Electron.PrinterInfo[]>;
|
_getPrintersAsync(): Promise<Electron.PrinterInfo[]>;
|
||||||
_init(): void;
|
_init(): void;
|
||||||
_getNavigationEntryAtIndex(index: number): Electron.EntryAtIndex | null;
|
_getNavigationEntryAtIndex(index: number): Electron.NavigationEntry | null;
|
||||||
_getActiveIndex(): number;
|
_getActiveIndex(): number;
|
||||||
_historyLength(): number;
|
_historyLength(): number;
|
||||||
_canGoBack(): boolean;
|
_canGoBack(): boolean;
|
||||||
|
@ -97,6 +97,8 @@ declare namespace Electron {
|
||||||
_goForward(): void;
|
_goForward(): void;
|
||||||
_goToOffset(index: number): void;
|
_goToOffset(index: number): void;
|
||||||
_goToIndex(index: number): void;
|
_goToIndex(index: number): void;
|
||||||
|
_removeNavigationEntryAtIndex(index: number): boolean;
|
||||||
|
_getHistory(): Electron.NavigationEntry[];
|
||||||
_clearHistory():void
|
_clearHistory():void
|
||||||
canGoToIndex(index: number): boolean;
|
canGoToIndex(index: number): boolean;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
|
|
Loading…
Reference in a new issue