diff --git a/package.json b/package.json index 924028b..b1badc2 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,8 @@ "p-retry": "^5.1.0", "serialize-error": "^11.0.0", "type-fest": "^2.12.1", - "webext-detect-page": "^4.0.1" + "webext-detect-page": "^4.0.1", + "webext-tools": "^1.1.0" }, "devDependencies": { "@parcel/config-webextension": "^2.4.0", diff --git a/source/sender.ts b/source/sender.ts index fad20f2..340c46b 100644 --- a/source/sender.ts +++ b/source/sender.ts @@ -1,5 +1,6 @@ import pRetry from "p-retry"; import { isBackground } from "webext-detect-page"; +import { doesTabExist } from "webext-tools"; import { deserializeError } from "serialize-error"; import { @@ -22,9 +23,11 @@ import { } from "./shared.js"; import { SetReturnType } from "type-fest"; -export const errorNonExistingTarget = +const _errorNonExistingTarget = "Could not establish connection. Receiving end does not exist."; +export const errorTabDoesntExist = "The tab doesn't exist"; + function isMessengerResponse(response: unknown): response is MessengerResponse { return isObject(response) && response["__webextMessenger"] === true; } @@ -81,12 +84,20 @@ async function manageMessage( minTimeout: 100, factor: 1.3, maxRetryTime: 4000, - onFailedAttempt(error) { + async onFailedAttempt(error) { if ( // Don't retry sending to the background page unless it really hasn't loaded yet (target.page !== "background" && error instanceof MessengerError) || - String(error.message).startsWith(errorNonExistingTarget) + String(error.message).startsWith(_errorNonExistingTarget) ) { + if ( + browser.tabs && + typeof target.tabId === "number" && + !(await doesTabExist(target.tabId)) + ) { + throw new Error(errorTabDoesntExist); + } + debug(type, "will retry. Attempt", error.attemptNumber); } else { throw error; diff --git a/source/test/contentscript/api.test.ts b/source/test/contentscript/api.test.ts index 201c326..a9d08b0 100644 --- a/source/test/contentscript/api.test.ts +++ b/source/test/contentscript/api.test.ts @@ -1,10 +1,11 @@ import test from "tape"; import { isBackground, isContentScript, isWebPage } from "webext-detect-page"; import { PageTarget, Sender, Target } from "../.."; +import { errorTabDoesntExist, errorTargetClosedEarly } from "../../sender"; import { expectRejection, sleep, trackSettleTime } from "../helpers"; import * as backgroundContext from "../background/api"; import * as localContext from "../background/testingApi"; -import { expectRejection } from "../helpers"; +import * as contentScriptContext from "./api"; import { getPageTitle, setPageTitle, @@ -220,6 +221,19 @@ async function init() { await closeTab(tabId); }); + test("stops trying immediately if specific tab ID doesn't exist", async (t) => { + const request = getPageTitle({ tabId }); + const durationPromise = trackSettleTime(request); + + await expectRejection(t, request, new Error(errorTabDoesntExist)); + + const duration = await durationPromise; + t.ok( + duration < 100, + `It should take less than 100 ms (took ${duration}ms)` + ); + }); + test("retries until it times out", async (t) => { const tabId = await openTab( "https://fregante.github.io/pixiebrix-testing-ground/No-static-content-scripts"