fix(extension): Chrome background 改为能力探测;README 纠正检测描述
This commit is contained in:
@ -29,7 +29,7 @@
|
||||
|------|------|
|
||||
| 导航服务地址 | 你的门户根地址,如 `https://nav.example.com`(不要带 `/search` 等路径) |
|
||||
| 同步令牌 | 服务端环境变量 `NAVI_SYNC_TOKEN` 的值(**服务端开启鉴权时必填**) |
|
||||
| 设备名称 | 可选,留空自动检测(desktop-chrome / desktop-firefox / mobile-*) |
|
||||
| 设备名称 | 可选,留空则自动检测。**Firefox 版**按 UA 识别(desktop-firefox / mobile-* / desktop-edge 等);**Chrome 版**的 MV3 service worker 里没有 `navigator`,只能回退为 `desktop-browser`(需要区分设备时请手填) |
|
||||
|
||||
## 行为
|
||||
|
||||
@ -43,6 +43,11 @@
|
||||
## 技术细节
|
||||
|
||||
- Chrome/Edge: Manifest V3,service worker + `chrome.alarms`(最小周期 30s)
|
||||
- Firefox: Manifest V2,event page + `browser.alarms`
|
||||
- Firefox: Manifest V2,event page(`persistent: true`)+ `browser.alarms`
|
||||
- 存储: `storage.local`(pending 队列 + 配置)
|
||||
- API: `history.search` + `alarms` + `runtime.onMessage`
|
||||
- `visit_count` 上报的是 **Chrome/Firefox history API 的绝对累计值**,不是增量。
|
||||
服务端(`history-server`)按设备取绝对值再求和,所以重复上报不会把计数放大。
|
||||
- 两个目录的 `background.js` **不是同一份文件**:Firefox 额外做设备名识别
|
||||
(MV2 常驻后台,`navigator` 可用),并过滤 `moz-extension://`。
|
||||
改动时两边都要改,但不要直接整份覆盖。
|
||||
|
||||
@ -31,8 +31,25 @@ let pendingItems = [];
|
||||
|
||||
// ── 检测标记:只向用户配置的导航站注入 content script ──
|
||||
// 已安装的浏览器上,导航站会拿到 data-navi-ext 属性,从而隐藏安装入口。
|
||||
//
|
||||
// MV3 与 MV2 的注册方式不同,必须分别处理(此前只写了 MV3 那条,
|
||||
// Firefox 走 chrome.scripting 早退,于是 data-navi-ext 永不写入,
|
||||
// 安装入口在 Firefox 上永远藏不住):
|
||||
// · MV3 / Chromium:chrome.scripting.registerContentScripts,callback 风格
|
||||
// · MV2 / Firefox :browser.contentScripts.register,返回 Promise,
|
||||
// 注销只能靠 register() 返回的句柄上的 .unregister()(无按 id 注销的 API)
|
||||
const DETECT_SCRIPT_ID = 'navi-detect';
|
||||
const DETECT_FILE = 'detect.js';
|
||||
// MV2 句柄:MV2 没有「按 id 查询已注册脚本」的 API,只能自己留住上次注册
|
||||
// 返回的对象,换地址时调它的 unregister()。MV3 另有 storage 字段标记。
|
||||
let mv2RegisteredHandle = null;
|
||||
|
||||
function extApi() {
|
||||
// Firefox 注入 browser.* 兼 chrome.* 兼容层;优先用它拿 MV2 的 contentScripts。
|
||||
return (typeof browser !== 'undefined' && browser && browser.contentScripts)
|
||||
? browser
|
||||
: (typeof chrome !== 'undefined' ? chrome : null);
|
||||
}
|
||||
|
||||
function hostMatchPattern(serverUrl) {
|
||||
try {
|
||||
@ -43,19 +60,55 @@ function hostMatchPattern(serverUrl) {
|
||||
}
|
||||
}
|
||||
|
||||
async function syncDetectScript(serverUrl) {
|
||||
if (!chrome.scripting || !chrome.scripting.registerContentScripts) return;
|
||||
// mv2HasContentScripts 判断当前运行的是 Firefox MV2 那条路。
|
||||
function mv2HasContentScripts(api) {
|
||||
return !!(api && api.contentScripts && typeof api.contentScripts.register === 'function');
|
||||
}
|
||||
|
||||
async function unregisterMV2() {
|
||||
if (!mv2RegisteredHandle) return;
|
||||
const h = mv2RegisteredHandle;
|
||||
mv2RegisteredHandle = null;
|
||||
try {
|
||||
const existing = await chrome.scripting.getRegisteredContentScripts({ ids: [DETECT_SCRIPT_ID] });
|
||||
if (typeof h.unregister === 'function') await h.unregister();
|
||||
} catch (e) { /* 从未注册 / 已被注销 */ }
|
||||
}
|
||||
|
||||
async function syncDetectScript(serverUrl) {
|
||||
const api = extApi();
|
||||
if (!api) return;
|
||||
|
||||
// ① Firefox MV2:contentScripts.register(Promise)
|
||||
if (mv2HasContentScripts(api)) {
|
||||
await unregisterMV2();
|
||||
const pattern = hostMatchPattern(serverUrl);
|
||||
if (!pattern) return;
|
||||
try {
|
||||
mv2RegisteredHandle = await api.contentScripts.register({
|
||||
matches: [pattern],
|
||||
js: [DETECT_FILE],
|
||||
runAt: 'document_start',
|
||||
allFrames: false,
|
||||
});
|
||||
} catch (e) {
|
||||
// 静默降级:未装扩展时只是多显示一个安装入口,不影响同步
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ② Chromium MV3:chrome.scripting.registerContentScripts(callback)
|
||||
if (!api.scripting || !api.scripting.registerContentScripts) return;
|
||||
try {
|
||||
const existing = await api.scripting.getRegisteredContentScripts({ ids: [DETECT_SCRIPT_ID] });
|
||||
if (existing && existing.length) {
|
||||
await chrome.scripting.unregisterContentScripts({ ids: [DETECT_SCRIPT_ID] });
|
||||
await api.scripting.unregisterContentScripts({ ids: [DETECT_SCRIPT_ID] });
|
||||
}
|
||||
} catch (e) { /* 从未注册过 */ }
|
||||
|
||||
const pattern = hostMatchPattern(serverUrl);
|
||||
if (!pattern) return;
|
||||
try {
|
||||
await chrome.scripting.registerContentScripts([{
|
||||
await api.scripting.registerContentScripts([{
|
||||
id: DETECT_SCRIPT_ID,
|
||||
matches: [pattern],
|
||||
js: [DETECT_FILE],
|
||||
|
||||
Reference in New Issue
Block a user