随着Chrome浏览器对Manifest V3的强制要求,许多基于Manifest V2的Chrome扩展都面临着升级的挑战。本文将详细介绍如何将IPIP网站IP信息查询Chrome插件从Manifest V2升级到V3版本,包括遇到的问题、解决方案以及最佳实践。
IPIP网站IP信息查询插件是一个专业的网络工具,主要功能包括:
- 🌍 自动显示当前网站的真实服务器IP地址
- 🏳️ 在扩展图标上显示对应国家旗帜
- 📊 提供详细的IP地理位置信息(国家、省份、城市)
- 🖱️ 支持右键菜单快速查询选中的IP地址
- 🌐 多语言支持(中文/英文)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| {
"manifest_version": 3, // V2 → V3
"action": { // browser_action → action
"default_icon": {
"19": "images/icon_19.png",
"38": "images/icon_38.png"
},
"default_title": "WebSite IP Information query Powered by IPIP.net",
"default_popup": "popup.html"
},
"background": {
"service_worker": "js/background.js" // scripts → service_worker
},
"permissions": [
"contextMenus",
"activeTab",
"storage",
"tabs",
"webRequest"
],
"host_permissions": [ // 新增独立的主机权限声明
"http://*/*",
"https://*/*"
]
}
|
- manifest_version:
2
→ 3
- browser_action → action: 统一的操作API
- background.scripts → background.service_worker: 使用Service Worker
- 权限分离: 将主机权限独立到
host_permissions
Manifest V3最大的变化是将Background Page改为Service Worker,这带来了以下挑战:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // V2 中的 XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.send();
// V3 中使用 fetch API
var ajaxGet = async function(url, callback) {
try {
const response = await fetch(url);
if (!response.ok) {
callback({ ret: -1, msg: "Network Error" });
return;
}
const resp = await response.json();
callback(resp);
} catch (e) {
callback({ ret: 100, msg: "Server Response Error" });
}
};
|
1
2
3
4
5
6
7
8
9
| // V2 API
chrome.browserAction.setTitle({title: "..."});
chrome.browserAction.setIcon({path: "..."});
chrome.browserAction.enable(tabId);
// V3 API
chrome.action.setTitle({title: "..."});
chrome.action.setIcon({path: chrome.runtime.getURL("...")});
chrome.action.enable(tabId);
|
在Service Worker中,相对路径需要使用 chrome.runtime.getURL()
转换:
1
2
3
4
5
6
7
| // V2 中的相对路径
chrome.browserAction.setIcon({path: "icons/CN.png"});
// V3 中需要绝对路径
chrome.action.setIcon({
path: chrome.runtime.getURL("icons/CN.png")
});
|
经过深入研究Chrome官方文档,我们发现:
- ✅ webRequest API在V3中仍然可用
- ✅ 可以观察和分析网络请求
- ✅ 可以获取真实的IP地址 (
details.ip
) - ❌ 不能使用blocking模式(除非是企业策略安装的扩展)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // V3中使用webRequest API(只观察模式,不阻塞)
chrome.webRequest.onCompleted.addListener(function(details) {
if (details.ip && details.tabId >= 0) {
var domain = new URL(details.url).hostname;
tabsDomainMap[details.tabId] = domain;
tabsIPMap[details.tabId] = details.ip;
// 使用真实IP查询地理位置信息
const apiUrl = "https://clientapi.ipip.net/browser/chrome?ip=" +
details.ip + '&l=' + navigator.language + '&domain=' + domain;
ajaxGet(apiUrl, function(info){
if (info.ret == 0) {
ipData[details.ip] = info.data;
dnsData[details.ip] = info.dns;
renderIcon(info.data);
chrome.action.enable(details.tabId);
}
});
}
}, {
urls: ["http://*/*", "https://*/*"],
types: ["main_frame"]
});
|
V3中移除了直接访问background页面的能力,需要使用消息传递:
1
2
3
4
5
6
7
8
9
10
| // V2 中直接访问
var background = chrome.extension.getBackgroundPage();
var ipData = background.ipData[ip];
// V3 中使用消息传递
chrome.runtime.sendMessage({action: 'getIPData', ip: ip}, function(response) {
if (response && response.ipData) {
render(response.ipData);
}
});
|
1
2
3
4
5
6
7
8
9
10
| chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
if (request.action === 'getIPData') {
sendResponse({
ipData: ipData[request.ip],
dnsData: dnsData[request.ip]
});
return true;
}
// ... 其他消息处理
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 避免重复创建contextMenu
chrome.runtime.onStartup.addListener(createContextMenu);
chrome.runtime.onInstalled.addListener(createContextMenu);
function createContextMenu() {
chrome.contextMenus.removeAll(() => {
chrome.contextMenus.create({
id: "ipip",
contexts: ["selection"],
title: "使用IPIP.NET搜索 \"%s\""
});
});
}
// V3中使用onClicked事件监听器
chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId === "ipip") {
getSelection(info, tab);
}
});
|
问题: 外部脚本加载被阻止
1
| Refused to load the script 'https://ajs.ipip.net/chrome.js' because it violates the following Content Security Policy directive
|
解决方案: 移除外部脚本加载
1
2
| // 移除这行代码
// $.getScript('https://ajs.ipip.net/chrome.js');
|
问题:
1
| Uncaught (in promise) Error: Failed to set icon 'images/icon_gray_38.png': Failed to fetch
|
解决方案: 使用绝对路径
1
2
3
| chrome.action.setIcon({
path: chrome.runtime.getURL("images/icon_gray_38.png")
});
|
问题:
1
| Unchecked runtime.lastError: Cannot create item with duplicate id ipip
|
解决方案: 创建前先清理
1
2
3
| chrome.contextMenus.removeAll(() => {
// 然后创建新的菜单项
});
|
由于使用了广泛的主机权限,需要向Chrome商店提供详细的权限使用说明:
- 技术原因: 必须监听所有网站的网络请求才能获取真实服务器IP
- 无法替代: activeTab权限无法提供网络请求详情
- 安全保证: 仅观察模式,不修改或阻塞任何请求
1
2
| 本扩展专门用于查询和显示网站的IP地理位置信息,帮助用户了解所访问网站的服务器位置。
所有功能都围绕IP地理位置查询这一核心目的,不包含其他无关功能。
|
为了便于问题排查,添加了详细的调试日志:
1
2
3
4
| console.log('🚀 IPIP Extension Service Worker 启动');
console.log('📡 发起API请求:', url);
console.log('🌐 WebRequest完成:', details.url, 'IP:', details.ip);
console.log('🎨 渲染图标,IP信息:', info);
|
1
2
3
4
5
| // 缓存IP数据,避免重复查询
var ipData = {};
var dnsData = {};
var tabsIPMap = {};
var tabsDomainMap = {};
|
Manifest V3的升级虽然带来了挑战,但也提供了更好的安全性和性能。关键要点:
- Service Worker适配: 使用fetch替代XMLHttpRequest
- API更新: browserAction → action
- 权限分离: host_permissions独立声明
- 消息传递: 替代直接访问background页面
- 路径处理: Service Worker中使用绝对路径
- webRequest保留: 核心IP获取功能得以保持
通过这次升级,我们不仅保持了原有的所有功能,还提升了扩展的安全性和稳定性。希望这个升级指南能帮助其他开发者顺利完成Manifest V3的迁移。
本文基于实际项目升级经验编写,如有问题欢迎交流讨论。