Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions Firefox_Installation_Guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# WorldQuant Scope - Firefox 版本安装指南

这是从 Chrome 扩展转换而来的 Firefox 版本。

## 安装步骤

### 当前仅支持临时安装(开发模式)
1. 打开 Firefox 浏览器
2. 在地址栏输入 `about:debugging`
3. 点击左侧的 "This Firefox"
4. 点击 "Load Temporary Add-on..." 按钮
5. 选择此文件夹中的 `manifest.json` 文件
6. 扩展将被临时安装(重启 Firefox 后会被移除)

## 主要变更

### 1. Manifest 更改
- `manifest_version`: 3 → 2 (Firefox 兼容)
- `action` → `browser_action`
- `service_worker` → `scripts` 数组
- 简化了 `web_accessible_resources` 格式
- 移除了 `host_permissions`

### 2. API 兼容性
- 所有 `chrome.*` API 调用都已更新为使用 `browserAPI`
- 自动检测环境并使用正确的 API (`browser` 或 `chrome`)
- `chrome.scripting` API 替换为 `tabs.executeScript` 和 `tabs.insertCSS`

### 3. 脚本注入更改
- 使用 Firefox 兼容的脚本注入方法
- 顺序注入脚本文件以确保依赖关系
- 使用代码字符串注入替代函数参数注入

## 功能验证

安装后请验证以下功能:
- [ ] 扩展图标显示在工具栏
- [ ] 弹出窗口正常打开
- [ ] 设置保存和加载正常
- [ ] 在 WorldQuant 平台页面正常工作
- [ ] 数据分析功能正常
- [ ] Genius 页面增强功能正常

## 故障排除

如遇到问题:
1. 打开 Firefox 开发者工具 (F12)
2. 查看控制台错误信息
3. 检查扩展是否在 about:addons 中正常启用
4. 确认网站权限设置正确

## 注意事项

- Firefox 版本可能在某些 API 调用上与 Chrome 版本有细微差异
- 建议在安装前备份浏览器数据
- 如需更新,需重新进行临时安装过程
28 changes: 9 additions & 19 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
"manifest_version": 3,
"manifest_version": 2,
"name": "WorldQuant Scope",
"description": "WorldQuant Scope is a browser extension that provides a set of tools to help you navigate the WorldQuant platform.",
"version": "0.9.3000",
"author": "zhangkaihua@buaa.edu.cn",
"icons": {
"128": "img/logo.png"
},
"action": {
"browser_action": {
"default_title": "WorldQuant Scope - Tools for WorldQuant Platform",
"default_popup": "src/html/popup/popup.html",
"default_icon": "img/logo.png"
},
"background": {
"service_worker": "src/scripts/background.js",
"type": "module"
"scripts": ["src/scripts/background.js"],
"persistent": false
},
"content_scripts": [
{
Expand Down Expand Up @@ -52,23 +52,13 @@
}
],
"web_accessible_resources": [
{
"resources": [
"data/*.bin",
"data/*.json",
"src/css/*.css",
"src/scripts/lib/*.js"
],
"matches": [
"*://platform.worldquantbrain.com/*"
]
}
],
"host_permissions": [
"https://*/*"
"data/*.bin",
"data/*.json",
"src/css/*.css",
"src/scripts/lib/*.js"
],
"permissions": [
"scripting",
"https://*/*",
"cookies",
"tabs",
"storage",
Expand Down
87 changes: 58 additions & 29 deletions src/html/popup/popup.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Description: 弹出窗口的 JS 文件
console.log('popup.js loaded');

// 检查依赖库是否加载
console.log('msgpack available:', typeof msgpack !== 'undefined');
console.log('pako available:', typeof pako !== 'undefined');

// 获取 HTML 元素
const dbAddressInput = document.getElementById('dbAddress');
const hiddenFeatureCheckbox = document.getElementById('hiddenFeature');
Expand All @@ -18,7 +22,7 @@ const importCommunityFile = document.getElementById('importCommunityFile');
// 加载用户设置
function loadSettings() {
statusText.textContent = '加载中...';
chrome.storage.local.get('WQPSettings', ({ WQPSettings }) => {
browser.storage.local.get('WQPSettings').then(({ WQPSettings }) => {
dbAddressInput.value = WQPSettings.apiAddress || '';
hiddenFeatureCheckbox.checked = WQPSettings.hiddenFeatureEnabled || false;
dataAnalysisCheckbox.checked = WQPSettings.dataAnalysisEnabled || false;
Expand All @@ -27,6 +31,8 @@ function loadSettings() {

saveBtn.disabled = !dbAddressInput.value.trim();
statusText.textContent = '';
}).catch(error => {
showStatusMessage('加载设置失败!', false);
});
}

Expand All @@ -47,17 +53,15 @@ function saveSettings(event) {
saveBtn.disabled = false;
return;
}
chrome.storage.local.set({ WQPSettings }, () => {
if (chrome.runtime.lastError) {
showStatusMessage('保存失败,请重试!', false);
browser.storage.local.set({ WQPSettings }).then(() => {
showStatusMessage('设置已保存!', true);
setTimeout(() => {
statusText.textContent = '';
saveBtn.disabled = false;
} else {
showStatusMessage('设置已保存!', true);
setTimeout(() => {
statusText.textContent = '';
saveBtn.disabled = false;
}, 2000);
}
}, 2000);
}).catch(error => {
showStatusMessage('保存失败,请重试!', false);
saveBtn.disabled = false;
});
}

Expand Down Expand Up @@ -111,7 +115,7 @@ function formatNow() {

function handleExportCommunity() {
statusText.textContent = '导出中...';
chrome.storage.local.get('WQPCommunityState', ({ WQPCommunityState }) => {
browser.storage.local.get('WQPCommunityState').then(({ WQPCommunityState }) => {
try {
if (!WQPCommunityState) {
showStatusMessage('没有可导出的社区数据。', false);
Expand All @@ -124,12 +128,25 @@ function handleExportCommunity() {
console.error(e);
showStatusMessage('导出失败。', false);
}
}).catch(error => {
showStatusMessage('获取数据失败。', false);
});
}

function handleExportCommunityCompressed() {
statusText.textContent = '导出(压缩)中...';
chrome.storage.local.get('WQPCommunityState', ({ WQPCommunityState }) => {

// 检查依赖库
if (typeof msgpack === 'undefined') {
showStatusMessage('msgpack库未加载,无法压缩导出。', false);
return;
}
if (typeof pako === 'undefined') {
showStatusMessage('pako库未加载,无法压缩导出。', false);
return;
}

browser.storage.local.get('WQPCommunityState').then(({ WQPCommunityState }) => {
try {
if (!WQPCommunityState) {
showStatusMessage('没有可导出的社区数据。', false);
Expand All @@ -141,9 +158,12 @@ function handleExportCommunityCompressed() {
downloadBytes(`WQPCommunityState_${formatNow()}.wqcs`, deflated, 'application/octet-stream');
showStatusMessage('压缩导出完成。', true);
} catch (e) {
console.error(e);
showStatusMessage('压缩导出失败。', false);
console.error('压缩导出错误:', e);
showStatusMessage(`压缩导出失败: ${e.message}`, false);
}
}).catch(error => {
console.error('获取数据错误:', error);
showStatusMessage('获取数据失败。', false);
});
}

Expand All @@ -157,23 +177,34 @@ function handleImportFileChange(evt) {
if (!file) return;
statusText.textContent = '导入中...';
const isCompressed = /\.wqcs$/i.test(file.name);

// 如果是压缩文件,检查依赖库
if (isCompressed) {
if (typeof msgpack === 'undefined') {
showStatusMessage('msgpack库未加载,无法导入压缩文件。', false);
return;
}
if (typeof pako === 'undefined') {
showStatusMessage('pako库未加载,无法导入压缩文件。', false);
return;
}
}

const reader = new FileReader();
if (isCompressed) {
reader.onload = () => {
try {
const arr = new Uint8Array(reader.result);
const inflated = pako.inflate(arr);
const obj = msgpack.decode(inflated);
chrome.storage.local.set({ WQPCommunityState: obj }, () => {
if (chrome.runtime.lastError) {
showStatusMessage('写入存储失败。', false);
} else {
showStatusMessage('导入成功。', true);
}
browser.storage.local.set({ WQPCommunityState: obj }).then(() => {
showStatusMessage('导入成功。', true);
}).catch(error => {
showStatusMessage('写入存储失败。', false);
});
} catch (e) {
console.error(e);
showStatusMessage('导入失败:压缩内容无法解析。', false);
console.error('导入压缩文件错误:', e);
showStatusMessage(`导入失败:${e.message}`, false);
}
};
reader.onerror = () => showStatusMessage('读取文件失败。', false);
Expand All @@ -182,12 +213,10 @@ function handleImportFileChange(evt) {
reader.onload = () => {
try {
const obj = JSON.parse(reader.result);
chrome.storage.local.set({ WQPCommunityState: obj }, () => {
if (chrome.runtime.lastError) {
showStatusMessage('写入存储失败。', false);
} else {
showStatusMessage('导入成功。', true);
}
browser.storage.local.set({ WQPCommunityState: obj }).then(() => {
showStatusMessage('导入成功。', true);
}).catch(error => {
showStatusMessage('写入存储失败。', false);
});
} catch (e) {
console.error(e);
Expand Down
Loading