Add Firefox compatibility layer and improve API detection
Introduces firefox-compat.js to handle Firefox-specific messaging and storage, and updates background.js and popup.js to robustly detect and use the appropriate extension APIs for both Chrome and Firefox environments. Manifest and popup files are updated to load the new compatibility script, ensuring reliable settings management and messaging across browsers.
This commit is contained in:
1 parent
38e8d54129
commit
1fb387a32d
5 files changed
+321
-83
No files matched your search
+121
-36
@@ -8,15 +8,37 @@
|
||||
* - 管理扩展状态和配置
|
||||
*/
|
||||
|
||||
// 导入兼容层
|
||||
importScripts("webext-compat.js");
|
||||
// 对于Firefox Manifest V2,webext-compat.js 通过manifest加载
|
||||
// 对于Chrome Manifest V3,通过importScripts加载
|
||||
if (typeof importScripts !== "undefined") {
|
||||
try {
|
||||
importScripts("webext-compat.js");
|
||||
} catch (e) {
|
||||
console.log("Running in Firefox/Manifest V2 environment");
|
||||
}
|
||||
}
|
||||
|
||||
// 确保兼容层可用
|
||||
if (typeof webext === "undefined") {
|
||||
// 在非模块环境中,兼容层应该已经通过脚本标签加载
|
||||
console.error("WebExt compatibility layer not found");
|
||||
// 尝试使用全局的browser或chrome API
|
||||
if (typeof browser !== "undefined") {
|
||||
console.log("Using Firefox browser API directly");
|
||||
self.webext = browser;
|
||||
self.webextFlavor = { soup: new Set(["firefox"]) };
|
||||
} else if (typeof chrome !== "undefined") {
|
||||
console.log("Using Chrome API directly");
|
||||
self.webext = chrome;
|
||||
self.webextFlavor = { soup: new Set(["chromium"]) };
|
||||
}
|
||||
}
|
||||
|
||||
// 确认API可用性
|
||||
console.log("Background script initializing...");
|
||||
console.log("WebExt API available:", typeof webext !== "undefined");
|
||||
console.log("Storage API available:", webext && webext.storage ? "yes" : "no");
|
||||
console.log("Runtime API available:", webext && webext.runtime ? "yes" : "no");
|
||||
|
||||
/**
|
||||
* 平台配置定义
|
||||
* 支持的下载加速平台列表
|
||||
@@ -226,12 +248,26 @@ const DEFAULT_SETTINGS = {
|
||||
webext.runtime.onInstalled.addListener(async () => {
|
||||
console.log("Xget Now 已安装");
|
||||
|
||||
// 使用本地存储而不是同步存储以确保兼容性
|
||||
const storageAPI = webext.storage.sync || webext.storage.local;
|
||||
// 对于Firefox,优先使用local存储,因为sync可能不可用或有限制
|
||||
const isFirefox =
|
||||
webextFlavor && webextFlavor.soup && webextFlavor.soup.has("firefox");
|
||||
const storageAPI = isFirefox
|
||||
? webext.storage.local
|
||||
: webext.storage.sync || webext.storage.local;
|
||||
|
||||
// 如果尚未设置,则设置默认设置
|
||||
const settings = await storageAPI.get(DEFAULT_SETTINGS);
|
||||
await storageAPI.set(settings);
|
||||
try {
|
||||
// 如果尚未设置,则设置默认设置
|
||||
const settings = await storageAPI.get(DEFAULT_SETTINGS);
|
||||
await storageAPI.set(settings);
|
||||
} catch (error) {
|
||||
console.error("初始化设置时出错:", error);
|
||||
// 尝试使用local storage作为后备
|
||||
try {
|
||||
await webext.storage.local.set(DEFAULT_SETTINGS);
|
||||
} catch (localError) {
|
||||
console.error("使用本地存储初始化设置时出错:", localError);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 防止监听器被多次注册
|
||||
@@ -253,7 +289,12 @@ function handleDownload(downloadItem, suggest) {
|
||||
|
||||
async function processDownloadRedirect(downloadItem) {
|
||||
try {
|
||||
const storageAPI = webext.storage.sync || webext.storage.local;
|
||||
// 对于Firefox,优先使用local存储
|
||||
const isFirefox =
|
||||
webextFlavor && webextFlavor.soup && webextFlavor.soup.has("firefox");
|
||||
const storageAPI = isFirefox
|
||||
? webext.storage.local
|
||||
: webext.storage.sync || webext.storage.local;
|
||||
const settings = await storageAPI.get(DEFAULT_SETTINGS);
|
||||
|
||||
// 检查扩展是否已启用并配置了域名
|
||||
@@ -324,37 +365,81 @@ function transformUrlLegacy(url, settings) {
|
||||
}
|
||||
|
||||
// 监听来自弹出窗口/选项的消息
|
||||
webext.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
const storageAPI = webext.storage.sync || webext.storage.local;
|
||||
const messageListener = (request, sender, sendResponse) => {
|
||||
console.log("Received message:", request.action);
|
||||
|
||||
// 对于Firefox,优先使用local存储,因为sync可能不可用或有限制
|
||||
const isFirefox =
|
||||
webextFlavor && webextFlavor.soup && webextFlavor.soup.has("firefox");
|
||||
const storageAPI = isFirefox
|
||||
? webext.storage.local
|
||||
: webext.storage.sync || webext.storage.local;
|
||||
|
||||
if (request.action === "getSettings") {
|
||||
storageAPI.get(DEFAULT_SETTINGS).then(sendResponse);
|
||||
return true;
|
||||
} else if (request.action === "saveSettings") {
|
||||
storageAPI.set(request.settings).then(async () => {
|
||||
// 通知相关标签页刷新
|
||||
// 使用Promise处理异步操作
|
||||
(async () => {
|
||||
try {
|
||||
const tabs = await webext.tabs.query({
|
||||
url: Object.values(PLATFORMS).map((platform) => platform.base + "/*"),
|
||||
});
|
||||
|
||||
for (const tab of tabs) {
|
||||
try {
|
||||
await webext.tabs.sendMessage(tab.id, {
|
||||
action: "showNotification",
|
||||
message: "设置已更新!点击刷新页面",
|
||||
showRefreshButton: true,
|
||||
});
|
||||
} catch (error) {
|
||||
// 标签页可能没有加载内容脚本,忽略
|
||||
}
|
||||
}
|
||||
const settings = await storageAPI.get(DEFAULT_SETTINGS);
|
||||
console.log("Settings retrieved:", settings);
|
||||
sendResponse(settings || DEFAULT_SETTINGS);
|
||||
} catch (error) {
|
||||
console.log("无法通知标签页设置更新");
|
||||
console.error("获取设置时出错:", error);
|
||||
sendResponse(DEFAULT_SETTINGS);
|
||||
}
|
||||
})();
|
||||
return true; // 保持消息通道开放
|
||||
} else if (request.action === "saveSettings") {
|
||||
// 确保设置对象有效
|
||||
const settingsToSave = {
|
||||
...DEFAULT_SETTINGS,
|
||||
...request.settings,
|
||||
};
|
||||
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
(async () => {
|
||||
try {
|
||||
await storageAPI.set(settingsToSave);
|
||||
console.log("Settings saved successfully");
|
||||
|
||||
// 通知相关标签页刷新
|
||||
try {
|
||||
const tabs = await webext.tabs.query({
|
||||
url: Object.values(PLATFORMS).map(
|
||||
(platform) => platform.base + "/*"
|
||||
),
|
||||
});
|
||||
|
||||
for (const tab of tabs) {
|
||||
try {
|
||||
await webext.tabs.sendMessage(tab.id, {
|
||||
action: "showNotification",
|
||||
message: "设置已更新!点击刷新页面",
|
||||
showRefreshButton: true,
|
||||
});
|
||||
} catch (error) {
|
||||
// 标签页可能没有加载内容脚本,忽略
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("无法通知标签页设置更新");
|
||||
}
|
||||
|
||||
sendResponse({ success: true });
|
||||
} catch (error) {
|
||||
console.error("保存设置时出错:", error);
|
||||
sendResponse({ success: false, error: error.message });
|
||||
}
|
||||
})();
|
||||
return true; // 保持消息通道开放
|
||||
}
|
||||
});
|
||||
|
||||
// 未知消息
|
||||
return false;
|
||||
};
|
||||
|
||||
// 添加消息监听器
|
||||
if (webext && webext.runtime && webext.runtime.onMessage) {
|
||||
webext.runtime.onMessage.addListener(messageListener);
|
||||
console.log("Message listener registered");
|
||||
} else {
|
||||
console.error("Cannot register message listener - runtime API not available");
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Firefox 特定兼容性修复
|
||||
* 直接使用 browser API 处理 Firefox 中的消息传递问题
|
||||
*/
|
||||
|
||||
// 在 Firefox 中,直接使用 browser API
|
||||
if (
|
||||
typeof browser !== "undefined" &&
|
||||
browser.runtime &&
|
||||
browser.runtime.sendMessage
|
||||
) {
|
||||
console.log("Firefox environment detected, using native browser API");
|
||||
|
||||
// 为popup.js提供一个简化的API
|
||||
window.firefoxAPI = {
|
||||
sendMessage: function (message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
browser.runtime.sendMessage(message, (response) => {
|
||||
if (browser.runtime.lastError) {
|
||||
console.error(
|
||||
"Firefox sendMessage error:",
|
||||
browser.runtime.lastError
|
||||
);
|
||||
reject(browser.runtime.lastError);
|
||||
} else {
|
||||
resolve(response);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Firefox sendMessage exception:", error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getSettings: async function () {
|
||||
try {
|
||||
return await browser.storage.local.get({
|
||||
enabled: true,
|
||||
xgetDomain: "xget.xi-xu.me",
|
||||
enabledPlatforms: {
|
||||
gh: true,
|
||||
gl: true,
|
||||
gitea: true,
|
||||
codeberg: true,
|
||||
sf: true,
|
||||
aosp: true,
|
||||
hf: true,
|
||||
npm: true,
|
||||
pypi: true,
|
||||
"pypi-files": true,
|
||||
conda: true,
|
||||
"conda-community": true,
|
||||
maven: true,
|
||||
apache: true,
|
||||
gradle: true,
|
||||
rubygems: true,
|
||||
cran: true,
|
||||
cpan: true,
|
||||
ctan: true,
|
||||
golang: true,
|
||||
nuget: true,
|
||||
crates: true,
|
||||
packagist: true,
|
||||
arxiv: true,
|
||||
fdroid: true,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to get settings from storage:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
saveSettings: async function (settings) {
|
||||
try {
|
||||
await browser.storage.local.set(settings);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Failed to save settings to storage:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
+24
-32
@@ -1,31 +1,27 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Xget Now",
|
||||
"version": "2.0.4",
|
||||
"description": "为 Firefox 浏览器提供的 Xget 下载加速扩展,支持 GitHub、GitLab、npm、PyPI 等众多平台的文件下载加速",
|
||||
"background": {
|
||||
"persistent": true,
|
||||
"scripts": ["webext-compat.js", "background.js"]
|
||||
},
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
"128": "icons/icon128.png",
|
||||
"16": "icons/icon16.png",
|
||||
"32": "icons/icon32.png",
|
||||
"48": "icons/icon48.png",
|
||||
"128": "icons/icon128.png"
|
||||
"48": "icons/icon48.png"
|
||||
},
|
||||
"default_popup": "popup.html",
|
||||
"default_title": "Xget Now"
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"webext-compat.js",
|
||||
"background.js"
|
||||
],
|
||||
"persistent": false
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "i@xi-xu.me",
|
||||
"strict_min_version": "109.0"
|
||||
}
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"js": [
|
||||
"webext-compat.js",
|
||||
"content.js"
|
||||
],
|
||||
"js": ["webext-compat.js", "content.js"],
|
||||
"matches": [
|
||||
"https://github.com/*",
|
||||
"https://gitlab.com/*",
|
||||
@@ -56,6 +52,15 @@
|
||||
"run_at": "document_end"
|
||||
}
|
||||
],
|
||||
"description": "为 Firefox 浏览器提供的 Xget 下载加速扩展,支持 GitHub、GitLab、npm、PyPI 等众多平台的文件下载加速",
|
||||
"icons": {
|
||||
"128": "icons/icon128.png",
|
||||
"16": "icons/icon16.png",
|
||||
"32": "icons/icon32.png",
|
||||
"48": "icons/icon48.png"
|
||||
},
|
||||
"manifest_version": 2,
|
||||
"name": "Xget Now",
|
||||
"permissions": [
|
||||
"downloads",
|
||||
"storage",
|
||||
@@ -86,19 +91,6 @@
|
||||
"https://arxiv.org/*",
|
||||
"https://f-droid.org/*"
|
||||
],
|
||||
"icons": {
|
||||
"16": "icons/icon16.png",
|
||||
"32": "icons/icon32.png",
|
||||
"48": "icons/icon48.png",
|
||||
"128": "icons/icon128.png"
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
"icons/*"
|
||||
],
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "i@xi-xu.me",
|
||||
"strict_min_version": "109.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
"version": "2.0.4",
|
||||
"web_accessible_resources": ["icons/*"]
|
||||
}
|
||||
@@ -618,6 +618,7 @@
|
||||
</div>
|
||||
|
||||
<script src="webext-compat.js"></script>
|
||||
<script src="firefox-compat.js"></script>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -9,17 +9,30 @@
|
||||
*/
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
// 确保兼容层可用
|
||||
if (typeof webext === "undefined") {
|
||||
console.error("WebExt compatibility layer not found in popup");
|
||||
// 确保有可用的API
|
||||
if (
|
||||
typeof webext === "undefined" &&
|
||||
typeof browser === "undefined" &&
|
||||
!window.firefoxAPI
|
||||
) {
|
||||
console.error("No compatible extension API found");
|
||||
showStatus("扩展API加载失败", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载当前设置
|
||||
await loadSettings();
|
||||
// 延迟执行以确保background脚本已准备就绪
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
// 加载当前设置
|
||||
await loadSettings();
|
||||
|
||||
// 设置事件监听器
|
||||
setupEventListeners();
|
||||
// 设置事件监听器
|
||||
setupEventListeners();
|
||||
} catch (error) {
|
||||
console.error("Initialization error:", error);
|
||||
showStatus("初始化失败,请重新打开扩展", "error");
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -27,9 +40,41 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
*/
|
||||
async function loadSettings() {
|
||||
try {
|
||||
const settings = await webext.runtime.sendMessage({
|
||||
action: "getSettings",
|
||||
});
|
||||
console.log("Loading settings...");
|
||||
|
||||
let settings;
|
||||
|
||||
// Firefox 特殊处理
|
||||
if (window.firefoxAPI && typeof browser !== "undefined") {
|
||||
console.log("Using Firefox direct storage API");
|
||||
try {
|
||||
// 首先尝试消息传递
|
||||
settings = await window.firefoxAPI.sendMessage({
|
||||
action: "getSettings",
|
||||
});
|
||||
} catch (msgError) {
|
||||
console.log(
|
||||
"Message passing failed, using direct storage access:",
|
||||
msgError
|
||||
);
|
||||
// 如果消息传递失败,直接从存储读取
|
||||
settings = await window.firefoxAPI.getSettings();
|
||||
}
|
||||
} else if (webext && webext.runtime && webext.runtime.sendMessage) {
|
||||
// 标准 webext API
|
||||
console.log("Using standard webext API");
|
||||
settings = await webext.runtime.sendMessage({
|
||||
action: "getSettings",
|
||||
});
|
||||
} else {
|
||||
throw new Error("No compatible API available");
|
||||
}
|
||||
|
||||
console.log("Settings received:", settings);
|
||||
|
||||
if (!settings) {
|
||||
throw new Error("No response from background script");
|
||||
}
|
||||
|
||||
// 使用当前设置更新 UI
|
||||
const domainValue = settings.xgetDomain || "";
|
||||
@@ -301,12 +346,34 @@ async function saveSettings() {
|
||||
settings.xgetDomain = cleanupDomain(settings.xgetDomain);
|
||||
}
|
||||
|
||||
const response = await webext.runtime.sendMessage({
|
||||
action: "saveSettings",
|
||||
settings: settings,
|
||||
});
|
||||
let response;
|
||||
|
||||
if (response.success) {
|
||||
// Firefox 特殊处理
|
||||
if (window.firefoxAPI && typeof browser !== "undefined") {
|
||||
console.log("Saving settings using Firefox API");
|
||||
try {
|
||||
// 首先尝试消息传递
|
||||
response = await window.firefoxAPI.sendMessage({
|
||||
action: "saveSettings",
|
||||
settings: settings,
|
||||
});
|
||||
} catch (msgError) {
|
||||
console.log("Message passing failed, using direct storage:", msgError);
|
||||
// 如果消息传递失败,直接写入存储
|
||||
response = await window.firefoxAPI.saveSettings(settings);
|
||||
}
|
||||
} else if (webext && webext.runtime && webext.runtime.sendMessage) {
|
||||
// 标准 webext API
|
||||
console.log("Saving settings using standard webext API");
|
||||
response = await webext.runtime.sendMessage({
|
||||
action: "saveSettings",
|
||||
settings: settings,
|
||||
});
|
||||
} else {
|
||||
throw new Error("No compatible API available for saving settings");
|
||||
}
|
||||
|
||||
if (response && response.success) {
|
||||
// 显示适当的状态
|
||||
if (!settings.xgetDomain && settings.enabled) {
|
||||
showStatus("请配置你的 Xget 域名", "error");
|
||||
@@ -315,6 +382,13 @@ async function saveSettings() {
|
||||
} else {
|
||||
showStatus("✅ 设置已保存!查看页面通知中的刷新按钮", "success");
|
||||
}
|
||||
} else if (response && response.error) {
|
||||
console.error("保存设置失败:", response.error);
|
||||
showStatus("保存设置时出错: " + response.error, "error");
|
||||
} else {
|
||||
// 如果没有响应,可能是消息传递失败
|
||||
console.error("未收到后台脚本的响应");
|
||||
showStatus("保存设置时出错:无法与后台脚本通信", "error");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("保存设置时出错:", error);
|
||||
|
||||
Reference in new issue
Block a user