相信各位佬友经常会用到 Chrome浏览器 或者 类Chrome浏览器,这类浏览器有个非常好用的功能就是支持插件扩展。这个插件能让浏览器实现更多的玩法,而且让定制各类功能需求也变的得心应手。
恰巧今日得闲,花了一个小时手搓了一个简单的基础类扩展,也正好用这个实例为佬友们说说这个扩展的基本实现原理。实例的主要功能是 防止WebRTC泄露真实IP 和实现 各标签页的自动刷新。
用 https://www.browserscan.net/zh/webrtc 来测试是否存在WebRTC泄露

教程的所有文件尽量保存为utf-8格式
主配置:
manifest.json
{
"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
}]
}
主配置中的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
<!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>
交互式页面配套的脚本文件:
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 再次来测试是否存在WebRTC泄露

右上角的 扩展程序 按钮可以打开插件 交互式页面

因为考虑到实用性和教程易于理解,所以代码基本上都采用较为基础的结构书写。
扩展插件的功能很多,在于你想干什么能干什么,在什么人手里用,所以剩下的就看佬友们的个人发挥了。
相信佬友们会写出更好的扩展,还请多多交流与分享。
Good luck to all