相信各位佬友经常会用到 Chrome浏览器 或者 类Chrome浏览器,这类浏览器有个非常好用的功能就是支持插件扩展。这个插件能让浏览器实现更多的玩法,而且让定制各类功能需求也变的得心应手。
恰巧今日得闲,花了一个小时手搓了一个简单的基础类扩展,也正好用这个实例为佬友们说说这个扩展的基本实现原理。实例的主要功能是 **防止WebRTC泄露真实IP** 和实现 **各标签页的自动刷新**。
用 [https://www.browserscan.net/zh/webrtc](https://www.browserscan.net/zh/webrtc) 来测试是否存在WebRTC泄露
[](https://s.rmimg.com/2025-03-14/1741918114-209444-image.png)
**教程的所有文件尽量保存为utf-8格式**
**主配置:**
manifest.json
</s><i> </i>{ "manifest_version": 3, "name": "My extension library", "version": "1.0", "description": "Welcome to my extension library", "permissions": ["tabs", "activeTab", "storage", "alarms", "privacy"], "background": { "service_worker": "background.js" }, "action": { "default_popup": "popup.html" }, "content_scripts": [{ "matches": ["<all_urls>"], "js": ["content.js"], "run_at": "document_start", "all_frames": true }] }<i> </i><e>
主配置中的**name**、**version**、**description**可以随意写,**permissions** 声明插件所需的权限,**content_scripts** 功能非常强大(暂定匹配所有URL)。至于插件icon不是必须的,也不是今天的重点,如果有这方面需求的可以自己在主配置中添加即可。
**主脚本:**
background.js
```
//定义一个变量(是否启用WebRTC)、一个常量(判断是不是firefox)。
let pubwebrtc=false;
const isFirefox = /Firefox/.test(navigator.userAgent) || typeof InstallTrigger !== ‘undefined’;
//这个是关闭标签页触发的回调,主要功能是删除计时器
chrome.tabs.onRemoved.addListener((tabId) => {
chrome.storage.local.get([“refreshTabs”], (data) => {
let refreshTabs = data.refreshTabs || {};
if (refreshTabs[tabId]){
if (refreshTabs[tabId][“timer”])chrome.alarms.clear(refreshTabs[tabId][“timer”]);
delete refreshTabs[tabId];
chrome.storage.local.set({ refreshTabs });
}
});
});
//这个是设置并开启定时刷新的功能,由popup.html(交互式窗口)触发
function setRefreshTimer(tabId, interval) {
chrome.storage.local.get([“refreshTabs”], (data) => {
let refreshTabs = data.refreshTabs || {};
if (!refreshTabs[tabId]) refreshTabs[tabId]={};
if (refreshTabs[tabId][“timer”])chrome.alarms.clear(refreshTabs[tabId][“timer”]);
refreshTabs[tabId][“timer”]=“auto”+tabId;
chrome.alarms.create(refreshTabs[tabId][“timer”], {delayInMinutes: 0, periodInMinutes: interval/60});
refreshTabs[tabId][“interval”] = interval;
chrome.storage.local.set({ refreshTabs });
});
}
//这个是接收popup.html传入的消息并做相应的处理,主要功能是开/关自动刷新、启用/禁用WebRTC
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
const { action, tabId, interval } = request;
if (action === “start_refresh”) {
setRefreshTimer(tabId, interval);
} else if (action === “stop_refresh”) {
chrome.storage.local.get([“refreshTabs”], (data) => {
let refreshTabs = data.refreshTabs || {};
if(refreshTabs[tabId]){
if(refreshTabs[tabId][“timer”]){
chrome.alarms.clear(refreshTabs[tabId][“timer”]);
delete refreshTabs[tabId][“timer”];
chrome.storage.local.set({ refreshTabs });
}
}
})
} else if (action === “start_webrtc”) {
webrtcfunc(true,true);
} else if (action === “stop_webrtc”) {
webrtcfunc(false,true);
}
sendResponse({ success: true });
});
//计时器主函数,主要功能是重载页面(不受其他外在因素影响)
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name.substr(0,4) == “auto”) {
let tabId=parseInt(alarm.name.substr(4));
try{chrome.tabs.reload(tabId)}catch(e){}
}
});
//启用或禁用WebRTC后调用的功能函数
function webrtcaction() {
chrome.storage.local.get({
enabled: !pubwebrtc,
eMode: isFirefox ? ‘proxy_only’ : ‘disable_non_proxied_udp’,
dMode: ‘default_public_interface_only’
}, prefs => {
const value = prefs.enabled ? prefs.eMode : prefs.dMode;
chrome.privacy.network.webRTCIPHandlingPolicy.clear({}, () => {chrome.privacy.network.webRTCIPHandlingPolicy.set({value}, () => {chrome.privacy.network.webRTCIPHandlingPolicy.get({}, s => {})})});
});
}
//开启、禁用WebRTC
function webrtcfunc(v,s){
let webrtc = v; pubwebrtc = webrtc;
if (s) chrome.storage.local.set({ webrtc });
webrtcaction();
}
//在插件加载、安装、更新时触发,主要功能是初始化WebRTC
function webrtcinit(){
chrome.storage.onChanged.addListener(() => {webrtcaction()});
chrome.storage.local.get([“webrtc”], (data) => {
let webrtc = data.webrtc || false;
webrtcfunc(webrtc,false);
});
}
//在插件加载、安装、更新时触发,主要功能是初始化计时器本地存储
function clearStoredTabs() {
let refreshTabs={};
chrome.storage.local.set({ refreshTabs });
}
chrome.runtime.onStartup.addListener(() => {webrtcinit(); clearStoredTabs()});
chrome.runtime.onInstalled.addListener((details) => {webrtcinit(); if (details.reason === “install”) clearStoredTabs()});
```
**交互式页面:**
popup.html
</s><i> </i><!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Auto refresh</title> <style> body { width: 200px; font-family: Arial, sans-serif; text-align: center; } input { width: 30%; margin: 5px 0; } button { width: 90%; margin: 5px 0; padding: 5px; } </style> </head> <body> 间隔 <input type="number" id="interval" value=60 min="1"> 秒,自动刷新 <button id="onebutton"></button> <script src="popup.js"></script> <button id="webrtc" title="作用于全局"></button> </body> </html><i> </i><e>
**交互式页面配套的脚本文件:**
popup.js
```
//定义提示文字常量
const strs=[“开始刷新”, “停止刷新”, “<font color=green>当前无WebRTC泄漏</font>”, “<font color=red>存在WebRTC泄漏风险</font>”]
//popup.html加载完成后执行,主要功能是和用户交互,并传递相应参数给background.js
document.addEventListener(“DOMContentLoaded”, () => {
const btn = document.getElementById(“onebutton”);
const wtn = document.getElementById(“webrtc”);
const intervalInput = document.getElementById(“interval”);
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs.length === 0) return;
const tabId = tabs[0].id;
btn.innerHTML=strs[0];
intervalInput.focus();intervalInput.select();
chrome.storage.local.get([“refreshTabs”], (data) => {
let refreshTabs = data.refreshTabs || {};
if (refreshTabs[tabId]){
if (refreshTabs[tabId][“interval”]){
intervalInput.value = refreshTabs[tabId][“interval”];
intervalInput.select();
}
if (refreshTabs[tabId][“timer”]) {
btn.innerHTML=strs[1];btn.setAttribute(“func”,“stop”);
}
}
});
btn.addEventListener(“click”, () => {
if (btn.getAttribute(“func”)){
chrome.runtime.sendMessage({ action: “stop_refresh”, tabId }, (response) => {
btn.innerHTML=strs[0];btn.removeAttribute(“func”);
});
}else{
if (isNaN(intervalInput.value) || intervalInput.value < 1) intervalInput.value=60;
let interval = parseFloat(intervalInput.value);
chrome.runtime.sendMessage({ action: “start_refresh”, tabId, interval }, (response) => {
btn.innerHTML=strs[1];btn.setAttribute(“func”,“stop”);
});
}
});
chrome.storage.local.get([“webrtc”], (data) => {
let webrtc = data.webrtc || false;
if (webrtc){
wtn.innerHTML=strs[3];wtn.setAttribute(“func”,“true”);
}else wtn.innerHTML=strs[2];
});
wtn.addEventListener(“click”, () => {
if (wtn.getAttribute(“func”)){
chrome.runtime.sendMessage({ action: “stop_webrtc”, tabId }, (response) => {
wtn.innerHTML=strs[2];wtn.removeAttribute(“func”);
});
}else{
chrome.runtime.sendMessage({ action: “start_webrtc”, tabId }, (response) => {
wtn.innerHTML=strs[3];wtn.setAttribute(“func”,“true”);
});
}
});
});
});
```
强大的功能脚本(由于过于强大,所以不方便展示)
content.js
```
//其实这个脚本的强大与否,主要还是取决于自己想要做什么。
//理论上只要看得见的就能进行个性化操作。
(function() {
document.addEventListener(‘DOMContentLoaded’, function() {
document.title = "Welcome " + document.title;
});
})();
```
**加载插件:**
1.将上述几个文件全部根据提示的文件名保存到本地(格式选UTF-8)
2.启动 **chrome** 并打开扩展页 **chrome://extensions/**
3.在扩展页面 **启用开发者模式**
4.点击 **加载已解压的扩展程序** ,选择上述文件保存的目录即可
用 [https://www.browserscan.net/zh/webrtc](https://www.browserscan.net/zh/webrtc) 再次来测试是否存在WebRTC泄露
[](https://s.rmimg.com/2025-03-14/1741927639-530398-image.png)
右上角的 **扩展程序** 按钮可以打开插件 **交互式页面**
[](https://s.rmimg.com/2025-03-14/1741918127-945918-image.png)
因为考虑到实用性和教程易于理解,所以代码基本上都采用较为基础的结构书写。
扩展插件的功能很多,在于你想干什么能干什么,在什么人手里用,所以剩下的就看佬友们的个人发挥了。
相信佬友们会写出更好的扩展,还请多多交流与分享。
Good luck to all