mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-10-05 07:13:06 +08:00
fix: handle extension ports across BFCache navigation
This commit is contained in:
parent
0b99643100
commit
793c343fb6
@ -11,8 +11,9 @@
|
||||
"dev:web": "vite",
|
||||
"dev:background": "npm run build:background -- --mode development",
|
||||
"dev:js": "npm run build:js -- --mode development",
|
||||
"build": "cross-env NODE_ENV=production run-s clear build:web build:prepare build:background build:js",
|
||||
"build-firefox": "cross-env NODE_ENV=production EXTENSION=firefox run-s clear-firefox build:web build:prepare build:background build:js",
|
||||
"build": "cross-env NODE_ENV=production run-s clear build:bridge build:web build:prepare build:background build:js",
|
||||
"build-firefox": "cross-env NODE_ENV=production EXTENSION=firefox run-s clear-firefox build:bridge build:web build:prepare build:background build:js",
|
||||
"build:bridge": "npm --prefix webext-bridge run build",
|
||||
"build:prepare": "esno scripts/prepare.ts",
|
||||
"build:background": "vite build --config vite.config.background.mts",
|
||||
"build:web": "vite build",
|
||||
@ -34,6 +35,8 @@
|
||||
"test:extension-runtime": "node scripts/verify-extension-runtime.mjs",
|
||||
"test:remote-endpoint": "esno scripts/test-remote-endpoint.ts",
|
||||
"test:production-endpoint": "node scripts/verify-production-endpoint.mjs",
|
||||
"test:port-disconnect": "esno scripts/test-port-disconnect.ts",
|
||||
"test:port-disconnect-build": "node scripts/verify-port-disconnect-build.mjs",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
13
scripts/test-port-disconnect.ts
Normal file
13
scripts/test-port-disconnect.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { isBackForwardCachePortError } from '../webext-bridge/src/internal/port-disconnect';
|
||||
|
||||
assert.equal(
|
||||
isBackForwardCachePortError(
|
||||
'The page keeping the extension port is moved into back/forward cache, so the message channel is closed.',
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(isBackForwardCachePortError('Extension context invalidated.'), false);
|
||||
assert.equal(isBackForwardCachePortError(), false);
|
||||
|
||||
console.log('Port disconnect tests passed.');
|
||||
16
scripts/verify-port-disconnect-build.mjs
Normal file
16
scripts/verify-port-disconnect-build.mjs
Normal file
@ -0,0 +1,16 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
const background = await readFile('extension/dist/background/index.mjs', 'utf8');
|
||||
const contentScript = await readFile('extension/dist/contentScripts/index.global.js', 'utf8');
|
||||
|
||||
if (!background.includes('lastError')) {
|
||||
throw new Error('Background bundle does not consume runtime.lastError on port disconnect.');
|
||||
}
|
||||
if (!contentScript.includes('back/forward cache')) {
|
||||
throw new Error('Content script bundle does not recognize BFCache port closures.');
|
||||
}
|
||||
if (!contentScript.includes('pagehide') || !contentScript.includes('pageshow')) {
|
||||
throw new Error('Content script bundle does not suspend and resume the BFCache port lifecycle.');
|
||||
}
|
||||
|
||||
console.log('Production BFCache port handling verification passed.');
|
||||
@ -279,6 +279,10 @@ browser.runtime.onConnect.addListener((incomingPort) => {
|
||||
});
|
||||
|
||||
incomingPort.onDisconnect.addListener(() => {
|
||||
// Reading lastError prevents Chrome from reporting an expected BFCache port closure as
|
||||
// an unchecked runtime error in the extension service worker console.
|
||||
void browser.runtime.lastError;
|
||||
|
||||
// sometimes previous content script's onDisconnect is called *after* the fresh content-script's
|
||||
// onConnect. So without this fingerprint equality check, we would remove the new port from map
|
||||
if (connMap.get(connArgs.endpointName)?.fingerprint === connArgs.fingerprint)
|
||||
|
||||
@ -7,6 +7,7 @@ import { encodeConnectionArgs } from './connection-args';
|
||||
import { createDeliveryLogger } from './delivery-logger';
|
||||
import type { StatusMessage } from './port-message';
|
||||
import { PortMessage } from './port-message';
|
||||
import { isBackForwardCachePortError } from './port-disconnect';
|
||||
|
||||
/**
|
||||
* Manfiest V3 extensions can have their service worker terminated at any point
|
||||
@ -18,6 +19,8 @@ import { PortMessage } from './port-message';
|
||||
export const createPersistentPort = (name = '') => {
|
||||
const fingerprint = createFingerprint();
|
||||
let port: Runtime.Port;
|
||||
let connected = false;
|
||||
let inBackForwardCache = false;
|
||||
let undeliveredQueue: ReadonlyArray<QueuedMessage> = [];
|
||||
const pendingResponses = createDeliveryLogger();
|
||||
const onMessageListeners = new Set<(message: InternalMessage, port: Runtime.Port) => void>();
|
||||
@ -77,14 +80,28 @@ export const createPersistentPort = (name = '') => {
|
||||
};
|
||||
|
||||
const connect = () => {
|
||||
if (connected || inBackForwardCache) return;
|
||||
|
||||
port = browser.runtime.connect({
|
||||
name: encodeConnectionArgs({
|
||||
endpointName: name,
|
||||
fingerprint,
|
||||
}),
|
||||
});
|
||||
connected = true;
|
||||
const connectedPort = port;
|
||||
port.onMessage.addListener(handleMessage);
|
||||
port.onDisconnect.addListener(connect);
|
||||
port.onDisconnect.addListener(() => {
|
||||
const disconnectMessage = browser.runtime.lastError?.message;
|
||||
if (port !== connectedPort) return;
|
||||
|
||||
connected = false;
|
||||
if (isBackForwardCachePortError(disconnectMessage)) {
|
||||
inBackForwardCache = true;
|
||||
return;
|
||||
}
|
||||
connect();
|
||||
});
|
||||
|
||||
PortMessage.toBackground(port, {
|
||||
type: 'sync',
|
||||
@ -95,6 +112,17 @@ export const createPersistentPort = (name = '') => {
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('pagehide', (event) => {
|
||||
if (event.persisted) inBackForwardCache = true;
|
||||
});
|
||||
window.addEventListener('pageshow', (event) => {
|
||||
if (!event.persisted) return;
|
||||
inBackForwardCache = false;
|
||||
connect();
|
||||
});
|
||||
}
|
||||
|
||||
connect();
|
||||
|
||||
return {
|
||||
|
||||
3
webext-bridge/src/internal/port-disconnect.ts
Normal file
3
webext-bridge/src/internal/port-disconnect.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isBackForwardCachePortError(message?: string): boolean {
|
||||
return message?.includes('back/forward cache') ?? false;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user