From eece6a4578f7aebd568310a07cbd8888f95d5351 Mon Sep 17 00:00:00 2001 From: TrickyPR <23250792+trickypr@users.noreply.github.com> Date: Sat, 13 Apr 2024 19:57:46 +1000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Implement=20forward=20and=20back?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/extensions/lib/parent/ext-tabs.js | 55 +++++++++++++++++++ apps/extensions/lib/schemaTypes/tabs.d.ts | 2 + apps/extensions/lib/schemas/tabs.json | 12 +++++ apps/extensions/scripts/buildTypes.js | 2 +- apps/tests/integrations/extensions/tabs.mjs | 58 +++++++++++++++++++++ 5 files changed, 128 insertions(+), 1 deletion(-) diff --git a/apps/extensions/lib/parent/ext-tabs.js b/apps/extensions/lib/parent/ext-tabs.js index 7051d95..a2d209e 100644 --- a/apps/extensions/lib/parent/ext-tabs.js +++ b/apps/extensions/lib/parent/ext-tabs.js @@ -119,6 +119,61 @@ this.tabs = class extends ExtensionAPIPersistent { return serialize(extension)([tab, window]) }, + async goBack(tabId) { + let tab + + if (tabId) { + tab = await get(tabId).then((all) => all.tab) + } else { + tab = lazy.WindowTracker.getActiveWindow()?.activeTab() + if (!tab) { + return + } + } + const complete = new Promise((res) => { + /** @param {boolean} isLoading */ + function complete(isLoading) { + if (isLoading) { + return + } + tab.view.events.off('loadingChange', complete) + res(undefined) + } + + tab.view.events.on('loadingChange', complete) + }) + tab.view.browser.goBack() + return complete + }, + + async goForward(tabId) { + let tab + + if (tabId) { + tab = await get(tabId).then((all) => all.tab) + } else { + tab = lazy.WindowTracker.getActiveWindow()?.activeTab() + if (!tab) { + return + } + } + + const complete = new Promise((res) => { + /** @param {boolean} isLoading */ + function complete(isLoading) { + if (isLoading) { + return + } + tab.view.events.off('loadingChange', complete) + res(undefined) + } + + tab.view.events.on('loadingChange', complete) + }) + tab.view.browser.goForward() + return complete + }, + async query(queryInfo) { return query(queryInfo).map(serialize(extension)) }, diff --git a/apps/extensions/lib/schemaTypes/tabs.d.ts b/apps/extensions/lib/schemaTypes/tabs.d.ts index 157f1ff..1d13734 100644 --- a/apps/extensions/lib/schemaTypes/tabs.d.ts +++ b/apps/extensions/lib/schemaTypes/tabs.d.ts @@ -34,6 +34,8 @@ declare module tabs__tabs { type ApiGetterReturn = { tabs: { get: (tabId: number) => Promise + goBack: (tabId?: number) => unknown + goForward: (tabId?: number) => unknown query: (queryInfo: QueryInfo) => Promise remove: (tabIds: number | number[]) => unknown reload: (tabIds: number | number[]) => unknown diff --git a/apps/extensions/lib/schemas/tabs.json b/apps/extensions/lib/schemas/tabs.json index ad1a283..40b0ef7 100644 --- a/apps/extensions/lib/schemas/tabs.json +++ b/apps/extensions/lib/schemas/tabs.json @@ -120,6 +120,18 @@ "$ref": "Tab" } }, + { + "name": "goBack", + "type": "function", + "async": true, + "parameters": [{ "name": "tabId", "type": "integer", "optional": true }] + }, + { + "name": "goForward", + "type": "function", + "async": true, + "parameters": [{ "name": "tabId", "type": "integer", "optional": true }] + }, { "name": "query", "type": "function", diff --git a/apps/extensions/scripts/buildTypes.js b/apps/extensions/scripts/buildTypes.js index b6e1725..1f2a339 100644 --- a/apps/extensions/scripts/buildTypes.js +++ b/apps/extensions/scripts/buildTypes.js @@ -233,7 +233,7 @@ function generateTypeNode(type) { undefined, undefined, factory.createIdentifier(param.name), - typeof type.optional !== 'undefined' && type.optional + typeof param.optional !== 'undefined' && param.optional ? QUESTION_TOKEN : undefined, generateTypeNode(param), diff --git a/apps/tests/integrations/extensions/tabs.mjs b/apps/tests/integrations/extensions/tabs.mjs index 2ce99ac..9a954be 100644 --- a/apps/tests/integrations/extensions/tabs.mjs +++ b/apps/tests/integrations/extensions/tabs.mjs @@ -345,5 +345,63 @@ await TestManager.withBrowser( .then((e) => e.awaitMsg('done')) .then((e) => e.unload()) }) + + await TestManager.test('tabs - Navigation', async (test) => { + const extension = ExtensionTestUtils.loadExtension( + { + manifest: { + permissions: ['tabs'], + }, + async background() { + /** @type {import('resource://app/modules/ExtensionTestUtils.sys.mjs').TestBrowser} */ + const b = this.browser + + b.test.onMessage.addListener(async (msg) => { + const windowId = Number(msg) + + let windowResults = await b.tabs.query({ + windowId, + }) + + // History stack already setup on the tab at index 0, so we can + // use that here instead. Its probibly going to cause flaky tests + // in the future, but it stops spawning a new window + + b.test.assertEq( + 'about:blank', + windowResults[0].url, + 'Previous tests should leave as about:blank', + ) + + await b.tabs.goBack(windowResults[0].id) + + b.test.assertEq( + 'https://example.com/', + await b.tabs.get(windowResults[0].id || -1).then((t) => t.url), + 'New url should be last page', + ) + + await b.tabs.goForward(windowResults[0].id) + + b.test.assertEq( + 'about:blank', + await b.tabs.get(windowResults[0].id || -1).then((t) => t.url), + 'New url should be next page', + ) + + b.test.sendMessage('done') + }) + }, + }, + test, + ) + + await extension + .testCount(3) + .startup() + .then((e) => e.sendMsg(window.windowId.toString())) + .then((e) => e.awaitMsg('done')) + .then((e) => e.unload()) + }) }, )