当前位置: 首页 > news >正文

Chrome插件学习笔记(二)

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格式代码

在这里插入图片描述

相关文章:

  • 【AI非常道】二零二五年五月,AI非常道
  • 软件架构基础
  • 计算机模拟生物/化学反应有哪些软件?
  • 特伦斯 S75 电钢琴:奏响极致音乐体验的华丽乐章
  • centos7.6阿里云镜像各个版本介绍
  • 说一说SAP系统从Non-Unicode到Unicode的演化
  • Google Earth Pro 7.3 中文绿色版 - 谷歌地球专业版(精准定位,清晰查看您家位置)
  • PlankAssembly 笔记 DeepWiki 正交视图三维重建
  • Java枚举详解:从基础到高级应用
  • 从认识AI开始-----解密LSTM:RNN的进化之路
  • @Async 注解 走的是主线程 还是子线程呢
  • C# 将HTML文档、HTML字符串转换为图片
  • 代购企业如何解决选品管理问题?
  • IPD流程体系-TR2评审要素表
  • LangChain表达式 (LCEL)
  • RSTP介绍加实操
  • Spring Cloud 学习 —— 简单了解
  • ⼤模型驱动的DeepInsight Copilot在蚂蚁的技术实践
  • Express教程【002】:Express监听GET和POST请求
  • 两阶段uplift建模(因果估计+预算分配)的讲座与自己动手实践(一)
  • 做照片的网站/现在做百度推广有用吗
  • 网站平台怎么做的好处/怎样和政府交换友链
  • 免费企业网站哪个好/百度做网站推广电话
  • 门户网站介绍/百度视频免费高清影视
  • 达州做网站的公司有哪些/二维码推广赚佣金平台
  • 农村小伙创业做网站/互联网推广招聘