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
187 changes: 187 additions & 0 deletions MANIFEST_V3_MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# XSwitch Manifest V3 升级指南

## 🚨 问题

Chrome从版本88开始不再支持Manifest V2,所有使用V2的插件已被下架或无法安装。

## 📋 主要变化

### 1. Manifest.json 变更

```json
// V2 → V3
{
"manifest_version": 2 → 3,

"browser_action": {
// → "action": {
"default_icon": {...},
"default_popup": "XSwitch.html"
},

"background": {
"scripts": ["background.min.js"]
// → "service_worker": "background.min.js"
},

"permissions": [
"webRequest",
"webRequestBlocking"
// → "declarativeNetRequest",
// → "declarativeNetRequestFeedback"
],

// 新增
"host_permissions": [
"<all_urls>"
]
}
```

### 2. webRequest → declarativeNetRequest

**V2 (阻塞式):**
```javascript
chrome.webRequest.onBeforeRequest.addListener(
(details) => {
return { redirectUrl: newUrl };
},
{ urls: ['<all_urls>'] },
['blocking']
);
```

**V3 (声明式):**
```javascript
// 1. 先定义规则
const rules = [{
id: 1,
priority: 1,
condition: {
urlFilter: 'example.com',
resourceTypes: ['main_frame'],
},
action: {
type: 'redirect',
redirect: { url: 'new-url.com' },
},
}];

// 2. 添加规则
chrome.declarativeNetRequest.updateDynamicRules({
addRules: rules,
removeRuleIds: [1],
});

// 3. 监听事件
chrome.declarativeNetRequest.onBeforeRequest.addListener(
(details) => {
return { cancel: false };
},
{ urls: ['<all_urls>'] }
);
```

### 3. 响应头修改

**V2:**
```javascript
chrome.webRequest.onHeadersReceived.addListener(
(details) => {
details.responseHeaders.push({
name: 'Access-Control-Allow-Origin',
value: '*'
});
return { responseHeaders: details.responseHeaders };
},
{ urls: ['<all_urls>'] },
['blocking', 'responseHeaders']
);
```

**V3:**
```javascript
// V3中使用 modifyHeaders
chrome.declarativeNetRequest.onHeadersReceived.addListener(
(details) => {
return {
responseHeaders: [
...details.responseHeaders,
{ name: 'Access-Control-Allow-Origin', value: '*' }
]
};
},
{ urls: ['<all_urls>'] },
['blocking', 'responseHeaders']
);
```

### 4. Service Worker

V3中background脚本是Service Worker,不能持久化运行。

```javascript
// V2: 脚本一直运行
let globalState = {};

// V3: Service Worker可能在不活动时被终止
// 需要使用chrome.storage持久化状态
chrome.storage.local.set({ globalState });
```

### 5. API 变化

| V2 API | V3 等效 |
|---------|---------|
| `chrome.webRequest` | `chrome.declarativeNetRequest` |
| `chrome.browserAction` | `chrome.action` |
| `chrome.runtime.lastError` | `chrome.runtime.lastError` (仍然支持) |
| `window.matchMedia` | 在service worker中不可用 |

## 🔧 迁移步骤

### 步骤1: 更新 manifest.json
- 升级 `manifest_version` 到 3
- 将 `browser_action` 改为 `action`
- 将 `background.scripts` 改为 `background.service_worker`
- 将 `webRequest` 相关权限移到单独的 `host_permissions`
- 添加 `declarativeNetRequest` 权限

### 步骤2: 重写 background.ts
- 将所有 `chrome.webRequest` 调用改为 `chrome.declarativeNetRequest`
- 将动态拦截改为声明式规则
- 移除持久化状态,改用chrome.storage
- 将 `chrome.browserAction` 改为 `chrome.action`

### 步骤3: 更新构建配置
- 确保生成service worker而非persistent script
- 更新webpack配置

### 步骤4: 测试
- 测试URL转发规则
- 测试CORS跨域
- 测试缓存禁用
- 测试图标和角标

## ⚠️ 已知限制

1. **正则表达式限制**: declarativeNetRequest只支持有限的正则表达式
2. **动态修改请求**: V3中不能像V2那样动态修改每个请求
3. **状态持久化**: Service Worker可能被终止,需要依赖chrome.storage
4. **matchMedia**: Service Worker中不可用,需要其他方式检测主题

## 📦 构建命令

```bash
# V3 构建
npm run build:v3

# 发布
npm run pub:v3
```

## 🔗 参考文档

- [Chrome Extensions Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/)
- [Migrating to Manifest V3](https://developer.chrome.com/docs/extensions/mv3/migration/)
- [Declarative Net Request](https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/)
66 changes: 66 additions & 0 deletions build/webpack.v3.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Webpack Config for Manifest V3
*
* This configuration generates a service worker instead of persistent background script.
*/

const path = require('path');
const webpack = require('webpack');

module.exports = {
// Entry point for background script
entry: {
background: './src/background.ts',
},

output: {
path: path.resolve(__dirname, 'build'),
filename: 'background.min.js',
libraryTarget: 'this',
},

resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},

module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
transpileOnly: true,
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},

plugins: [
// Define environment for V3
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'MANIFEST_VERSION': 3,
}),
],

// Mode
mode: 'production',

// Optimization
optimization: {
minimize: true,
},

// Service worker specific
devtool: false,

// External - don't bundle chrome API
externals: {
chrome: 'chrome',
},
};
31 changes: 22 additions & 9 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@
"name": "XSwitch",
"description": "A tool for redirecting URLs and allowing CORS to make the local development experience easy and happy.",
"short_name": "xs",
"version": "1.17.1",
"manifest_version": 2,
"browser_action": {
"default_icon": "images/grey_128.png",
"version": "1.18.0",
"manifest_version": 3,
"action": {
"default_icon": {
"16": "images/grey_16.png",
"48": "images/grey_128.png",
"128": "images/grey_128.png"
},
"default_title": "XSwitch",
"default_popup": "XSwitch.html"
},
"permissions": [
"webRequest",
"storage",
"webRequestBlocking",
"browsingData",
"declarativeNetRequest",
"declarativeNetRequestFeedback"
],
"host_permissions": [
"<all_urls>"
],
"icons": {
"48": "images/grey_128.png",
"128": "images/grey_128.png"
},
"commands": {
"_execute_browser_action": {
"_execute_action": {
"suggested_key": {
"windows": "Ctrl+Shift+X",
"mac": "Command+Shift+X",
Expand All @@ -31,6 +37,13 @@
},
"options_page": "options.html",
"background": {
"scripts": ["background.min.js"]
}
"service_worker": "background.min.js",
"type": "module"
},
"web_accessible_resources": [
{
"resources": ["*.js", "*.css", "*.html"],
"matches": ["<all_urls>"]
}
]
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"scripts": {
"start": "nowa2 start",
"build": "TYPESCRIPT_NO_CHECK=true nowa2 build && cp -rf images lib XSwitch.html options.html manifest.json build",
"build:v3": "webpack --config build/webpack.v3.config.js && cp -rf images lib XSwitch.html options.html manifest.json build",
"pub": "npm run build && npm t && sh pub.sh",
"pub:v3": "npm run build:v3 && npm test",
"test": "jest",
"ci": "jest --coverage && cat ./coverage/lcov.info | coveralls"
},
Expand Down
Loading