Chrome插件学习笔记(二)

article/2025/7/5 8:29:48

Chrome插件学习笔记(二)

参考文章:

  • https://developer.chrome.com/docs/extensions/reference/api/sidePanel?hl=zh-cn
  • https://developer.chrome.com/docs/extensions/reference/api/webRequest?hl=zh-cn
  • https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest?hl=zh-cn

1、什么是sidePanel

上一篇文章Chrome插件学习笔记(一)认识了Chrome插件中的Popup,虽然Popup很轻便并且资源占用很低,但是有时候不能满足诉求,比如不能失去焦点后就会消失,无法长期保持打开状态

如下图浏览器页面右侧即为sidePanel,会占据页面一部分空间,可长期保持打开状态,适合需要持续交互的功能

在这里插入图片描述

2、Chrome插件开发

在浏览器使用过程中有时候打开页面的时候并没有及时打开控制台导致无法及时看到请求信息,这次插件功能是做一个快速复制请求Cookie/Curl,同时可以为修改/阻止请求的插件

2.1、第一个sidePanel插件

1、manifest.json

注意这里不能设置default_popup

{"name": "NetWorker","description": "Listen for network requests","version": "0.0.1","manifest_version": 3,"permissions": ["sidePanel"],"host_permissions": ["*://*/*"],"action": {"default_title": "Click to switch side panel"},"background": {"service_worker": "background.js","type": "module"},"side_panel": {"default_path": "sidepanel/sidepanel.html"}
}
2、background.js

这里可以设置openPanelOnActionClick控制sidePanel的展开和折叠,同时也可以通过其他的交互手动调用open方法展开sidePanel(注意这里没有close方法!!!)

chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })
3、sidespanel.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="data-container">sidespanel!</div><script type="module" src="sidepanel.js"></script>
</body>
</html>
4、页面效果

在这里插入图片描述

2.2、修改请求功能

1、manifest.json

ManifestV3中已经无法使用webRequest修改请求,需要使用declarativeNetRequest

{"name": "NetWorker","description": "Listen for network requests","version": "0.0.1","manifest_version": 3,"permissions": ["sidePanel","declarativeNetRequest"],"host_permissions": ["*://*/*"],"action": {"default_title": "Click to switch side panel"},"background": {"service_worker": "background.js","type": "module"},"side_panel": {"default_path": "sidepanel/sidepanel.html"}
}
2、background.js
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })chrome.declarativeNetRequest.getDynamicRules(rules => {let ruleIds = rules.map(rule => rule.id);chrome.declarativeNetRequest.updateDynamicRules({removeRuleIds: ruleIds,addRules: [{id: 10001,priority: 10001,action: {type: "modifyHeaders",requestHeaders: [{header: "X-My-Custom-Header",operation: "set",value: "HelloWorld"},{header: "Accept-Language",operation: "set",value: "en-US,en;q=0.9"}]},condition: {urlFilter: "*",resourceTypes: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket", "other"]}}]});
});
3、页面效果

在这里插入图片描述

2.3、阻止请求功能

1、background.js
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })chrome.declarativeNetRequest.getDynamicRules(rules => {let ruleIds = rules.map(rule => rule.id);chrome.declarativeNetRequest.updateDynamicRules({removeRuleIds: ruleIds,addRules: [{id: 10001,priority: 10001,action: {type: "modifyHeaders",requestHeaders: [{header: "X-My-Custom-Header",operation: "set",value: "HelloWorld"},{header: "Accept-Language",operation: "set",value: "en-US,en;q=0.9"}]},condition: {urlFilter: "*",resourceTypes: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket", "other"]}},{id: 10002,priority: 1,action: {type: "block"},condition: {urlFilter: "wwads.cn",resourceTypes: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket", "other"]}}]});
});
2、页面效果

未屏蔽广告相关请求

在这里插入图片描述

屏蔽广告相关请求

在这里插入图片描述

2.4、监听请求功能

1、manifest.json
{"name": "NetWorker","description": "Listen for network requests","version": "0.0.1","manifest_version": 3,"permissions": ["sidePanel","declarativeNetRequest","webRequest","storage"],"host_permissions": ["*://*/*"],"action": {"default_title": "Click to switch side panel"},"background": {"service_worker": "background.js","type": "module"},"side_panel": {"default_path": "sidepanel/sidepanel.html"}
}
2、background.js

注意onBeforeRequest中只能获取requestBody,onSendHeaders中只能获取requestHeaders、extraHeaders,因此需要进行数据拼接

chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })chrome.declarativeNetRequest.getDynamicRules(rules => {let ruleIds = rules.map(rule => rule.id);chrome.declarativeNetRequest.updateDynamicRules({removeRuleIds: ruleIds,addRules: [{id: 10001,priority: 10001,action: {type: "modifyHeaders",requestHeaders: [{header: "X-My-Custom-Header",operation: "set",value: "HelloWorld"},{header: "Accept-Language",operation: "set",value: "en-US,en;q=0.9"}]},condition: {urlFilter: "*",resourceTypes: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket", "other"]}},{id: 10002,priority: 1,action: {type: "block"},condition: {urlFilter: "wwads.cn",resourceTypes: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket", "other"]}}]});
});const requestBodys = {}chrome.webRequest.onBeforeRequest.addListener(details => {if ('requestBody' in details) {if ('raw' in details['requestBody']) {const requestBodyRaw = details['requestBody']['raw']const parseRequestBodyRaw = requestBodyRaw.map(r => {if ('bytes' in r) {return { bytes: new TextDecoder().decode(r.bytes) }}return r})const newDetails = JSON.parse(JSON.stringify(details))newDetails.requestBody.raw = parseRequestBodyRawrequestBodys[details.requestId] = newDetails}} else {requestBodys[details.requestId] = details}},{ urls: ["<all_urls>"] },["requestBody", "extraHeaders"]
);chrome.webRequest.onSendHeaders.addListener(details => {if (!(details.requestId in requestBodys)) {return}chrome.storage.local.get(['networklogs'], ({ networklogs = [] }) => {const newLog = {requestHeaders: details['requestHeaders'],...requestBodys[details.requestId],};chrome.storage.local.set({networklogs: [newLog, ...networklogs.slice(-99)]});delete requestBodys[details.requestId];});},{ urls: ["<all_urls>"] },["requestHeaders", "extraHeaders"]
);// chrome.webRequest.onCompleted.addListener(
//     details => {
//         console.log('onCompleted:', details);
//     },
//     { urls: ["<all_urls>"] },
//     ["responseHeaders"]
// );chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {if (message.action === 'networklogs') {chrome.storage.local.get(['networklogs'], ({ networklogs }) => {sendResponse(networklogs); // 通过 sendResponse 返回结果});return true;}
});
3、sidepanel.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="sidespanel.css">
</head>
<body><div id="data-container"></div><script type="module" src="sidepanel.js"></script>
</body>
</html>
4、sidepanel.js
import { timestampToYmshms } from '../util/datetime.js';const container = document.getElementById("data-container")document.addEventListener('click', async (e) => {const item = e.target.closest('.data-item');if (item) {try {const value = item.dataset.requestid;await navigator.clipboard.writeText(cookies[value]);item.style.backgroundColor = '#93b5cf';setTimeout(() => {item.style.backgroundColor = '#f8f9fa';}, 500);} catch (err) {console.error('复制失败:', err);}}
});document.addEventListener('dblclick', async (e) => {const item = e.target.closest('.data-item');if (item) {try {const value = item.dataset.requestid;await navigator.clipboard.writeText(curls[value]);item.style.backgroundColor = '#ff9300';setTimeout(() => {item.style.backgroundColor = '#f8f9fa';}, 500);} catch (err) {console.error('复制失败:', err);}}
});let cookies
let curlssetInterval(async () => {const networklogs = await chrome.runtime.sendMessage({action: 'networklogs',});cookies = {}curls = {}container.innerHTML = networklogs.map(n => {const requestId = parseInt(n['requestId'])const url = n['url']const method = n['method']const requestHeaders = n['requestHeaders']const requestBody = n?.['requestBody']const cookieHeader = requestHeaders?.filter(h => {return h['name'] === 'Cookie'})let cookieif (cookieHeader?.length > 1) {cookie = 'Multiple Cookie'} else if (cookieHeader?.length < 1) {cookie = 'No Cookie'} else if (cookieHeader?.length == 1) {cookie = cookieHeader[0]['value']}cookies[requestId] = cookielet curl = `curl -X ${method} '${url}'`;requestHeaders.forEach(header => {curl += ` -H '${header.name.toLowerCase()}: ${header.value}'`;});if (requestBody && requestBody.raw) {curl += ' --data-raw $\''requestBody.raw.forEach(element => {curl += element.bytes || '';});curl += '\''}curls[requestId] = curl.replaceAll('\"', '"').replaceAll('\r\n', '\\r\\n')return `<div class="data-item" data-requestid="${requestId}"><div class="copy-badge">Click to Copy</div><div class="data-metas"><div class="meta"><strong>url: </strong>${url}</div><div class="meta"><strong>method: </strong>${method}</div><div class="meta"><strong>date: </strong>${timestampToYmshms(n['timeStamp'])}</div></div></div>`}).join('')
}, 2000)
5、sidepanel.css
.data-container {background: white;border-radius: 8px;border: 1px solid #eee;padding: 10px;max-height: 400px;overflow-y: auto;overflow-x: hidden;
}.data-container .init{font-size: 14px;color: #cccccc;
}.data-item {padding: 12px;margin: 8px 0;background: #f8f9fa;border-radius: 6px;border-left: 4px solid #0078d4;transition: all 0.2s;cursor: pointer;position: relative;user-select: none;
}.data-item:hover {transform: translateX(2px);box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.1);
}.data-metas .meta {padding: 1px 0;font-size: 12px;max-height: 50px;overflow: hidden;          /* 隐藏溢出的内容 */white-space: nowrap;       /* 不自动换行 */text-overflow: ellipsis;   /* 用省略号替代超出的文本 */
}.copy-badge {position: absolute;right: 10px;top: 12px;transform: translateY(-50%);background: rgba(0, 120, 212, 0.1);color: #0078d4;padding: 4px 8px;border-radius: 4px;font-size: 12px;opacity: 0;transition: opacity 0.3s;z-index: 999;
}.data-item:hover .copy-badge {opacity: 1;
}
6、datetime.js
 export function timestampToYmshms(timestamp) {const date = new Date(timestamp);const year = date.getFullYear();const month = String(date.getMonth() + 1).padStart(2, '0');const day = String(date.getDate()).padStart(2, '0');const hours = String(date.getHours()).padStart(2, '0');const minutes = String(date.getMinutes()).padStart(2, '0');const seconds = String(date.getSeconds()).padStart(2, '0');return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
7、页面效果

点击card复制请求cookie,双击card复制请求的curl格式代码

在这里插入图片描述


http://www.hkcw.cn/article/IoruRtIPWr.shtml

相关文章

判断质数的基础方法

判断一个数是否为质数&#xff1a;基础方法(运算效率较慢) 另一种运用API来提高运算效率&#xff1a; 以下是添加了详细注释的代码版本&#xff0c;并优化了部分逻辑&#xff1a; package test;public class test5 {public static void main(String[] args) {//判断一个数是否…

列表单独展开收起同时关闭其余子项的问题优化

如图所示&#xff0c;当在列表中&#xff0c;需要分别单独点开子选项时&#xff0c;直接这样用一个index参数判断即可&#xff0c;非常简单方便&#xff0c;只需要满足点开当前index,然后想同index用null值自动关闭即可

java25

1.可变参数 2.集合工具类Collections 3.综合练习 集合嵌套&#xff1a; 4.不可变集合 JDK9以后才能用 这个静态方法名是of&#xff0c;返回值是List<E>,是泛型方法。 JDK10以后的简化版&#xff1a; 5.Stream流 爽一下&#xff1a; 简化后的: 注意&#xff1a;stream.ma…

中方:南南合作始终是对外合作优先方向

当地时间5月30日,联合国南南合作基金30周年纪念活动在纽约联合国总部举行。中国常驻联合国代表傅聪在活动致辞中表示,中方高度赞赏基金支持的务实合作成果。中方表示,始终坚定支持联合国发展支柱,始终坚定支持真正的多边主义,始终将南南合作作为对外合作的优先方向。中方指…

在线博客系统【测试报告】

&#x1f552; 一. 项目背景 由于纸质笔记容易丢失&#xff0c;携带不变&#xff0c;为了方便自己学习的过程中记录笔记&#xff0c;特开发了这个博客系统。这个系统后端采用 SpringBoot MyBatis SpringMVC &#xff1b;前端使用Html CSS JS&#xff1b;数据库使用的是Mysq…

近期手上的一个基于Function Grap(类AWS的Lambda)小项目的改造引发的思考

函数式Function是云计算里最近几年流行起来的新的架构和模式&#xff0c;因为它不依赖云主机&#xff0c;非常轻量&#xff0c;按需使用&#xff0c;甚至是免费使用&#xff0c;特别适合哪种数据同步&#xff0c;数据转发&#xff0c;本身不需要保存数据的业务场景&#xff0c;…

C++ - 模板(一) #泛型编程 #函数模板 #类模板

文章目录 前言 一、泛型编程 二、函数模板 1、函数模板的概念 2、函数模板的格式 3、函数模板的原理 4、函数模板的实例化 1、隐式实例化&#xff1a; 2、显式实例化&#xff1a; 5、模板参数的匹配原则 三、类模板 1、类模板的定义格式 2、类模板的实例化 总结 …

智能制造全场景数字化解决方案

制造企业数字化转型面临的挑战 数智化转型已成为中国制造业高质量发展的关键战略。面对全球制造业格局调整&#xff0c;如何快速构建覆盖全业务流程的可视化应用&#xff0c;通过数据驱动的方式为企业经营管理、预警监测、质量管控、决策支持提供全面支撑&#xff0c;是企业面…

Vue-收集表单信息

收集表单信息 Input label for 和 input id 关联, 点击账号标签 也能聚焦 input 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>表单数据</title><!-- 引入Vue --><scrip…

篮球分组问题讨论

1 问题概述 问题&#xff1a;有5支球队在同一块场地上进行单循环赛,共要进行10场比赛。下表是一个赛程安排&#xff0c;有些队觉得不公平。研究以下问题 A B C D E 每两场比赛间相隔场次数 A X 1 9 3 6 1, 2, 2 B 1 X 2 5 8 0, 2, 2 C 9 2 X 7 10 4…

成都鼎讯--通信干扰设备功能全解析

在现代电子战与通信对抗领域&#xff0c;一款高性能的通信干扰设备是掌握电磁频谱主动权的关键。本文将深入解析一款先进的通信干扰设备&#xff0c;其凭借多频段覆盖、多通道并行、多样化调制方式及灵活供电等特性&#xff0c;成为部队、科研院所等机构在电磁对抗训练与研究中…

vscode中让文件夹一直保持展开不折叠

vscode中让文件夹一直保持展开不折叠 问题 很多小伙伴使用vscode发现空文件夹会折叠显示, 让人看起来非常难受, 如下图 解决办法 首先打开设置->setting, 搜索compact Folders, 去掉勾选即可, 如下图所示 效果如下 看起来非常爽 ! ! !

中国城市间地理距离矩阵(2024)

1825 中国城市间地理距离矩阵(2024) 数据简介 中国城市间地理距离矩阵数据集&#xff0c;通过审图号GS(2024)0650的中国城市地图在Albers投影坐标系中进行计算得出矩阵表格&#xff0c;单位为KM&#xff0c;方便大家研究使用。 中国城市地理距离矩阵数据通过计算城市中心距离…

Linux中的shell脚本

什么是shell脚本 shell脚本是文本的一种shell脚本是可以运行的文本shell脚本的内容是由逻辑和数据组成shell脚本是解释型语言 用file命令可以查看文件是否是一个脚本文件 file filename 脚本书写规范 注释 单行注释 使用#号来进行单行注释 多行注释 使用 : " 注释内容…

20250530-C#知识:抽象类、抽象方法、接口

C#知识&#xff1a;抽象类、抽象方法、接口 在开发过程中接口一般用得较多&#xff0c;程序框架往往定义一堆接口规范&#xff0c;然后程序员自己写逻辑来实现接口功能。掌握接口的知识还是很有必要的。 1、抽象类 用abstract关键字修饰的类不能用来实例化对象可以包含抽象方法…

韩国首尔一地铁车厢内遭纵火 乘客被紧急疏散

当地时间5月31日8时47分左右,韩国首尔地铁5号线一辆列车车厢内起火,乘客随后被紧急疏散。据初步调查,火灾原因为有人纵火,嫌疑人已被抓获。目前暂无人员伤亡报告。受火灾事件影响,该地铁线路部分区段一度暂停运行,首尔市交通部门10时13分通报,事故处理已经完毕,暂停运行…

跨平台浏览器集成库JxBrowser 支持 Chrome 扩展程序,高效赋能 Java 桌面应用

JxBrowser 是 TeamDev 开发的跨平台库&#xff0c;用于在 Java 应用程序中集成 Chromium 浏览器。它支持 HTML5、CSS3、JavaScript 等&#xff0c;具备硬件加速渲染、双向 Java 与 JavaScript 连接、丰富的事件监听等功能&#xff0c;能处理网页保存、打印等操作&#xff0c;助…

聊聊网络变压器的浪涌等级标准是怎样划分的呢?

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;聊聊网络变压器的浪涌等级标准是怎样划分的呢&#xff1f; 在和做防雷产品的客户的深度沟通网络变压器产品选型中发现&#xff1a;客户对网络变压器的浪涌等级划分也很希望有更深的了解&#xff0c;今天就这个问题和…

探索Air780EPM:N种GPIO控制LED的创新应用!

通过创新思维与实用技巧&#xff0c;本文将带你了解Air780EPM如何通过GPIO实现LED控制的N种可能&#xff0c;从简单到复杂&#xff0c;激发项目灵感。 一、GPIO直接驱动LED 1.1 适用场景 低功耗场景&#xff1a;LED电流 ≤ 5mA&#xff08;普通GPIO的驱动能力限制&#xff09;…

JS 事件循环详解

JS 事件循环详解 文章目录 JS 事件循环详解一、JS 的单线程模型与异步机制二、事件循环的核心组件1. 执行栈&#xff08;Call Stack&#xff09;2. 任务队列&#xff08;Task Queue&#xff09;3. Web APIs 三、事件循环的执行流程四、任务类型详解1. 宏任务&#xff08;Macrot…