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

Chrome插件学习笔记

Chrome插件学习笔记

参考文章:

  • https://www.bookstack.cn/read/chrome-plugin-develop/spilt.2.8bdb1aac68bbdc44.md
  • https://juejin.cn/post/7498336041410052132
  • https://juejin.cn/post/7159443319938875428

1、认识Chrome插件

1.1、Chrome插件是什么

注意,浏览器插件权限非常高,一定不要安装来路不明的浏览器插件!!!

严格来讲,Chrome插件东西应该叫Chrome扩展(Chrome Extension),是一个用Web技术开发、用来增强浏览器功能的软件。

1.2、Chrome插件基本结构

my-extension/├── manifest.json       # 核心配置文件(必需)├── background.js   		# 后台逻辑(Manifest V3 必需)├── popup/              # 弹出窗口相关文件│   ├── popup.html│   ├── popup.js│   └── popup.css├── content/    				# 注入网页的脚本│   └── content-scripts.js├── icons/              # 插件图标│   ├── icon16.png│   ├── icon48.png│   └── icon128.png└── options/            # 设置页面├── options.html├── options.js└── options.css

1.3、Chrome插件如何使用

这里如果不打开开发者模式是没办法以文件夹的形式直接加载插件,只能安装crx格式的文件
在这里插入图片描述
在这里插入图片描述

这里以modheader插件为例,将其固定到导航栏的位置后,可以点击插件快速为所有的网络请求加指定的header头
在这里插入图片描述
在这里插入图片描述

2、Chrome插件开发

Chrome插件开发不需要专门的IDE,普通的web开发工具即可,这里使用的是Vscode

开发插件名称为Credential Manager,主要用于快速复制cookie、storage,同时监控网页的内存使用情况

2.1、开始造轮子

我们首先

1、manifest.json

Chrome插件最重要的文件就是manifest.json,用来说明管理插件的信息,需要什么权限,什么icon显示等等

{"manifest_version": 3,"name": "Credential Manager","version": "0.0.1","description": "Manage credentials easily","icons": {"16": "assets/icon16.png","48": "assets/icon48.png","128": "assets/icon128.png"},"action": {"default_icon": "assets/icon16.png","default_popup": "popup/popup.html"},"background": {"service_worker": "background.js","type": "module"}
}
2、popup.html
<!DOCTYPE html>
<html><head><link rel="stylesheet" href="popup.css">
</head><body><h1>popup.html</h1>
</body></html>
3、background.js

4、加载Chrome插件

注意代码变动一定要重新加载浏览器插件
在这里插入图片描述
在这里插入图片描述

5、页面效果

在这里插入图片描述

到这里我们完成了一个最简陋的浏览器插件,接下来我们来完善cookie相关功能

2.2、调试技巧

Chrome插件主要分为三部分:background、popup、content(暂未涉及)

1、background

所有在background.js中打印的内容都在service worker这个控制台里面,包括后续在background中存储的内容也都在service worker这个控制台里面
在这里插入图片描述

2、popup

注意,popup的控制台会在插件弹窗消失的时候消失!!!

popup需要在点开插件的情况下右键打开控制台

在这里插入图片描述

3、content

与网页共享控制台,可以f12直接打开

2.3、Cookie复制功能

1、manifest.json

访问Cookie需要在manifest.json声明cookie权限,同时需要声明访问哪些域名

{"manifest_version": 3,"name": "Credential Manager","version": "0.0.1","description": "Manage credentials easily","icons": {"16": "assets/icon16.png","48": "assets/icon48.png","128": "assets/icon128.png"},"permissions": ["cookies"],"host_permissions": ["*://*/*"],"action": {"default_icon": "assets/icon16.png","default_popup": "popup/popup.html"},"background": {"service_worker": "background.js","type": "module"}
}
2、popup.html
<!DOCTYPE html>
<html><head><link rel="stylesheet" href="popup.css">
</head><body><div class="tab-nav"><button class="tab-nav-item active" data-tab="cookies">Cookies</button></div><!-- Cookies Tab --><div class="tab-content active" data-tab="cookies"><div class="toolbar"><input type="text" id="cookieSearch" class="search-input" placeholder="Filter by domain"><button class="button" id="getCookies">Get Cookies</button></div><div class="data-container" id="cookieContainer"><span class="init">try to checking cookies...</span></div></div><script type="module" src="popup.js"></script>
</body></html>
3、popup.css
body {width: 500px;padding: 10px;
}.tab-nav {display: flex;border-bottom: 1px solid #ddd;margin-bottom: 10px;
}.tab-nav-item {font-size: 16px;font-weight: bold;padding: 10px 20px;background: none;border: none;cursor: pointer;border-bottom: 2px solid transparent;
}.tab-nav-item.active {border-bottom-color: #0078d4;color: #0078d4;
}.tab-content {display: none;padding: 10px 0;
}.tab-content.active {display: block;
}.button {padding: 10px 15px;border: none;border-radius: 8px;font-size: 14px;font-weight: 500;cursor: pointer;transition: all 0.3s ease;display: inline-flex;align-items: center;gap: 8px;position: relative;overflow: hidden;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);background: linear-gradient(135deg, #0078d4, #006cbd);color: white;
}.button:hover {background: linear-gradient(135deg, #006cbd, #005ea6);box-shadow: 0 4px 8px rgba(0, 120, 212, 0.2);transform: translateY(-1px);
}.toolbar {display: flex;gap: 10px;margin-bottom: 15px;
}.search-input {flex: 1;padding: 10px;border: 1px solid #ddd;border-radius: 8px;font-size: 14px;transition: all 0.3s ease;
}.search-input:focus {outline: none;border-color: #0078d4;box-shadow: 0 0 0 2px rgba(0, 120, 212, 0.1);
}.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;
}.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;word-wrap: break-word;overflow-wrap: anywhere;
}.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;
}
4、cookie-manager.js
// Cookie 管理器(静态方法直接调用)
export class CookieManager {// 获取所有cookiestatic async cookies(domainFilter = '') {// domainFilter支持""、"example.com"、".example.com"return new Promise(resolve => {chrome.cookies.getAll({ domain: domainFilter == '' ? null : domainFilter }, resolve);});}// 添加cookiestatic async add(cookieData) {//{// url: string,       Cookie 所属的完整 URL// name: string,      Cookie 名称// value: string,     Cookie 值// domain?: string,   域名(可选)// path?: string,     路径(默认 "/")// secure?: boolean,  是否仅 HTTPS(默认当前页面协议)// expirationDate?: number 过期时间戳// }return new Promise(resolve => {chrome.cookies.set(cookieData, resolve);});}// 移除cookiestatic async remove(cookie) {// cookie = { domain: ".example.com", path: "/api", secure: true }const url = `http${cookie.secure ? 's' : ''}://${cookie.domain}${cookie.path}`;return new Promise(resolve => {chrome.cookies.remove({ url, name: cookie.name }, resolve);});}
}
5、popup.js
import { CookieManager } from '../manager/cookie-manager.js';document.addEventListener('DOMContentLoaded', () => {const cookieContainer = document.getElementById('cookieContainer');// Tab页切换const tabs = document.querySelectorAll('.tab-nav-item');const tabContents = document.querySelectorAll('.tab-content');tabs.forEach(tab => {tab.addEventListener('click', () => {// 移除所有Tab的激活状态tabs.forEach(t => t.classList.remove('active'));tabContents.forEach(c => c.classList.remove('active'));// 设置当前激活Tab的状态tab.classList.add('active');const targetTab = document.querySelector(`.tab-content[data-tab="${tab.dataset.tab}"]`);targetTab.classList.add('active');});});// Cookie 操作async function showCookies() {try {const filter = cookieSearch.value.trim();const cookies = await CookieManager.cookies(filter);cookieContainer.innerHTML = cookies.map(c => `<div class="data-item" data-value="${c.value}"><div class="copy-badge">Click to Copy</div><div class="data-metas"><div class="meta"><strong>Domain: </strong>${c.domain}</div><div class="meta"><strong>Name: </strong>${c.name}</div><div class="meta"><strong>Value: </strong>${c.value}</div></div>
</div>`).join('')} catch (error) {cookieContainer.textContent = `错误: ${error.message}`;}}document.getElementById('getCookies').addEventListener('click', showCookies);// 粘贴data-item值document.addEventListener('click', async (e) => {const item = e.target.closest('.data-item');if (item) {try {const value = item.dataset.value;await navigator.clipboard.writeText(value);// 添加视觉反馈item.style.backgroundColor = '#93b5cf';setTimeout(() => {item.style.backgroundColor = '#f8f9fa';}, 500);} catch (err) {console.error('复制失败:', err);}}});
});
6、background.js
console.log("welcome credential manager!")// Cookie变化监听
chrome.cookies.onChanged.addListener(changeInfo => {chrome.storage.local.get(['cookieLogs'], ({ cookieLogs = [] }) => {const newLog = {timestamp: Date.now(),...changeInfo};chrome.storage.local.set({cookieLogs: [...cookieLogs.slice(-99), newLog]});});
});
7、页面效果

此时就拿到了浏览器所有的cookie,并且可以根据域名去筛选cookie,点击即可复制
在这里插入图片描述

2.4、Storage复制功能

这里只针对localstorage

1、manifest.json

访问storage需要在manifest.json声明storage权限;同时storage是基于页面的,需要激活页面的权限;获取storage需要获取网页的storage,所以需要执行脚本的权限

{"manifest_version": 3,"name": "Credential Manager","version": "0.0.1","description": "Manage credentials easily","icons": {"16": "assets/icon16.png","48": "assets/icon48.png","128": "assets/icon128.png"},"permissions": ["cookies","storage","activeTab","scripting"],"host_permissions": ["*://*/*"],"action": {"default_icon": "assets/icon16.png","default_popup": "popup/popup.html"},"background": {"service_worker": "background.js","type": "module"}
}
2、popup.html
<!DOCTYPE html>
<html><head><link rel="stylesheet" href="popup.css">
</head><body><div class="tab-nav"><button class="tab-nav-item active" data-tab="cookies">Cookies</button><button class="tab-nav-item" data-tab="storage">Storage</button></div><!-- Cookies Tab --><div class="tab-content active" data-tab="cookies"><div class="toolbar"><input type="text" id="cookieSearch" class="search-input" placeholder="Filter by domain"><button class="button" id="getCookies">Get Cookies</button></div><div class="data-container" id="cookieContainer"><span class="init">try to checking cookies...</span></div></div><!-- Storage Tab --><div class="tab-content" data-tab="storage"><div class="toolbar"><input type="text" id="storageSearch" class="search-input" placeholder="Filter by item key"><button class="button" id="getStorage">Get storage</button></div><div class="data-container" id="storageContainer"><span class="init">try to checking storages...</span></div></div><script type="module" src="popup.js"></script>
</body></html>
3、storage-manager.js
// Storage 管理器(静态方法直接调用)
export class StorageManager {static async currentTab() {return new Promise(resolve => {chrome.tabs.query({ active: true, currentWindow: true }, resolve);});}// 获取当前页的LocalStorage数据static async localStorage() {const [tab] = await this.currentTab()return new Promise(resolve => {chrome.scripting.executeScript({target: { tabId: tab.id },func: () => {return Object.keys(localStorage).map(key => ([key, localStorage.getItem(key)]));}}, (results) => {resolve(results[0]?.result || {});});});}// 添加localStorage项static async addLocalStorageItem(key, value, options = {}) {const [tab] = await this.currentTab()return new Promise((resolve, reject) => {chrome.scripting.executeScript({target: { tabId: tab.id },args: [key, value, options],func: (k, v, opts) => {// 安全写入机制try {localStorage.setItem(k, v);return true;} catch (error) {console.error('Storage item add failed:', error);return false;}}}, (results) => {if (results?.[0]?.result) {resolve({ success: true });} else {reject(new Error('Storage item add failed'));}});});}// 移除localStorage项static async removeLocalStorageItem(key) {const [tab] = await this.currentTab()return new Promise((resolve, reject) => {chrome.scripting.executeScript({target: { tabId: tab.id },args: [key],func: (k) => {try {localStorage.removeItem(k, v);return true;} catch (error) {console.error('Storage item remove failed:', error);return false;}}}, (results) => {if (results?.[0]?.result) {resolve({ success: true });} else {reject(new Error('Storage item remove failed'));}});});}
}
4、popop.js
import { CookieManager } from '../manager/cookie-manager.js';
import { StorageManager } from '../manager/storage-manager.js';document.addEventListener('DOMContentLoaded', () => {const cookieContainer = document.getElementById('cookieContainer');const storageContainer = document.getElementById('storageContainer');// Tab页切换const tabs = document.querySelectorAll('.tab-nav-item');const tabContents = document.querySelectorAll('.tab-content');tabs.forEach(tab => {tab.addEventListener('click', () => {// 移除所有Tab的激活状态tabs.forEach(t => t.classList.remove('active'));tabContents.forEach(c => c.classList.remove('active'));// 设置当前激活Tab的状态tab.classList.add('active');const targetTab = document.querySelector(`.tab-content[data-tab="${tab.dataset.tab}"]`);targetTab.classList.add('active');});});// Cookie 操作async function showCookies() {try {const filter = cookieSearch.value.trim();const cookies = await CookieManager.cookies(filter);cookieContainer.innerHTML = cookies.map(c => `<div class="data-item" data-value="${c.value}"><div class="copy-badge">Click to Copy</div><div class="data-metas"><div class="meta"><strong>Domain: </strong>${c.domain}</div><div class="meta"><strong>Name: </strong>${c.name}</div><div class="meta"><strong>Value: </strong>${c.value}</div></div>
</div>`).join('')} catch (error) {cookieContainer.textContent = `错误: ${error.message}`;}}document.getElementById('getCookies').addEventListener('click', showCookies);// Storage 操作async function showStorages() {try {const filter = storageSearch.value.trim().toLowerCase();const storages = await StorageManager.localStorage();const filtered = storages.filter((item) => {if (item[0].toLowerCase().includes(filter)) {return item}})storageContainer.innerHTML = filtered.map(f => `<div class="data-item" data-value="${f[1]}"><div class="copy-badge">Click to Copy</div><div class="data-metas"><div class="meta"><strong>Key: </strong>${f[0]}</div><div class="meta"><strong>Value: </strong>${f[1]}</div></div>
</div>`).join('')} catch (error) {storageContainer.textContent = `获取失败: ${error.message}`;}}document.getElementById('getStorage').addEventListener('click', showStorages);// 粘贴data-item值document.addEventListener('click', async (e) => {const item = e.target.closest('.data-item');if (item) {try {const value = item.dataset.value;await navigator.clipboard.writeText(value);// 添加视觉反馈item.style.backgroundColor = '#93b5cf';setTimeout(() => {item.style.backgroundColor = '#f8f9fa';}, 500);} catch (err) {console.error('复制失败:', err);}}});
});
5、background.js

这里会tab页切换历史,同时获取最新的激活tab页,为后续获取storage作准备

console.log("welcome credential manager!")let lastTabId = 0// 激活Tab页监听
async function handleTabActivated(tabId, windowId, type = "active") {console.log("lastTabId", lastTabId, "tabId", tabId);if (lastTabId == tabId && type == 'active') {return}try {const tab = await chrome.tabs.get(tabId);console.log("当前激活页面", tab);chrome.storage.local.get(['tabLogs'], ({ tabLogs = [] }) => {const newLog = {timestamp: Date.now(),windowId,...tab};chrome.storage.local.set({tabLogs: [...tabLogs.slice(-99), newLog]});});} catch (error) {console.warn('获取标签页失败:', error);}
}
// Tab页切换监听
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {console.log('tabId:', tabId, "changeInfo", changeInfo);handleTabActivated(tabId, tab?.windowId, "update");
});
chrome.tabs.onActivated.addListener(activeInfo => {handleTabActivated(activeInfo.tabId, activeInfo.windowId);
});
// 窗口切换监听(切换窗口导致的tab页切换不会触发chrome.tabs.onActivated事件)
chrome.windows.onFocusChanged.addListener(windowId => {if (windowId !== chrome.windows.WINDOW_ID_NONE) {chrome.tabs.query({ active: true, windowId }, ([tab]) => {handleTabActivated(tab.id, windowId);});}
});// Cookie变化监听
chrome.cookies.onChanged.addListener(changeInfo => {chrome.storage.local.get(['cookieLogs'], ({ cookieLogs = [] }) => {const newLog = {timestamp: Date.now(),...changeInfo};chrome.storage.local.set({cookieLogs: [...cookieLogs.slice(-99), newLog]});});
});
6、页面效果

在这里插入图片描述

2.5、页面内存监控

1、manifest.json

访问网页需要声明content_scripts相关内容

{"manifest_version": 3,"name": "Credential Manager","version": "0.0.1","description": "Manage credentials easily","icons": {"16": "assets/icon16.png","48": "assets/icon48.png","128": "assets/icon128.png"},"permissions": ["cookies","storage","activeTab","scripting"],"host_permissions": ["*://*/*"],"action": {"default_icon": "assets/icon16.png","default_popup": "popup/popup.html"},"background": {"service_worker": "background.js","type": "module"},"content_scripts": [{"matches": ["*://*/*"],"js": ["content/content-script.js"],"css": ["content/content-script.css"],"run_at": "document_end"}]
}
2、popup.html
<!DOCTYPE html>
<html><head><link rel="stylesheet" href="popup.css">
</head><body><div class="tab-nav"><button class="tab-nav-item active" data-tab="cookies">Cookies</button><button class="tab-nav-item" data-tab="storage">Storage</button><button class="tab-nav-item" data-tab="monitor">Monitor</button></div><!-- Cookies Tab --><div class="tab-content active" data-tab="cookies"><div class="toolbar"><input type="text" id="cookieSearch" class="search-input" placeholder="Filter by domain"><button class="button" id="getCookies">Get Cookies</button></div><div class="data-container" id="cookieContainer"><span class="init">try to checking cookies...</span></div></div><!-- Storage Tab --><div class="tab-content" data-tab="storage"><div class="toolbar"><input type="text" id="storageSearch" class="search-input" placeholder="Filter by item key"><button class="button" id="getStorage">Get storage</button></div><div class="data-container" id="storageContainer"><span class="init">try to checking storages...</span></div></div><!-- Monitor Tab --><div class="tab-content" data-tab="monitor"><div class="data-container" id="monitorContainer"><span class="init">try to checking storages...</span></div></div><script type="module" src="popup.js"></script>
</body></html>
3、popop.js

popop中会每五秒向background发送消息获取网页的内存情况

import { CookieManager } from '../manager/cookie-manager.js';
import { StorageManager } from '../manager/storage-manager.js';document.addEventListener('DOMContentLoaded', () => {const cookieContainer = document.getElementById('cookieContainer');const storageContainer = document.getElementById('storageContainer');const monitorContainer = document.getElementById('monitorContainer');// Tab页切换const tabs = document.querySelectorAll('.tab-nav-item');const tabContents = document.querySelectorAll('.tab-content');tabs.forEach(tab => {tab.addEventListener('click', () => {// 移除所有Tab的激活状态tabs.forEach(t => t.classList.remove('active'));tabContents.forEach(c => c.classList.remove('active'));// 设置当前激活Tab的状态tab.classList.add('active');const targetTab = document.querySelector(`.tab-content[data-tab="${tab.dataset.tab}"]`);targetTab.classList.add('active');});});// Cookie 操作async function showCookies() {try {const filter = cookieSearch.value.trim();const cookies = await CookieManager.cookies(filter);cookieContainer.innerHTML = cookies.map(c => `<div class="data-item" data-value="${c.value}"><div class="copy-badge">Click to Copy</div><div class="data-metas"><div class="meta"><strong>Domain: </strong>${c.domain}</div><div class="meta"><strong>Name: </strong>${c.name}</div><div class="meta"><strong>Value: </strong>${c.value}</div></div>
</div>`).join('')} catch (error) {cookieContainer.textContent = `错误: ${error.message}`;}}document.getElementById('getCookies').addEventListener('click', showCookies);// Storage 操作async function showStorages() {try {const filter = storageSearch.value.trim().toLowerCase();const storages = await StorageManager.localStorage();const filtered = storages.filter((item) => {if (item[0].toLowerCase().includes(filter)) {return item}})storageContainer.innerHTML = filtered.map(f => `<div class="data-item" data-value="${f[1]}"><div class="copy-badge">Click to Copy</div><div class="data-metas"><div class="meta"><strong>Key: </strong>${f[0]}</div><div class="meta"><strong>Value: </strong>${f[1]}</div></div>
</div>`).join('')} catch (error) {storageContainer.textContent = `获取失败: ${error.message}`;}}document.getElementById('getStorage').addEventListener('click', showStorages);// 粘贴data-item值document.addEventListener('click', async (e) => {const item = e.target.closest('.data-item');if (item) {try {const value = item.dataset.value;await navigator.clipboard.writeText(value);// 添加视觉反馈item.style.backgroundColor = '#93b5cf';setTimeout(() => {item.style.backgroundColor = '#f8f9fa';}, 500);} catch (err) {console.error('复制失败:', err);}}});// async function getPerformanceMetrics() {const { memory, timestamp } = await chrome.runtime.sendMessage({type: 'getPerformanceMetrics',});monitorContainer.innerHTML = `<div>Memory:${memory}</div>
<div>Datetime:${timestamp}</div>`}setInterval(getPerformanceMetrics, 5000)getPerformanceMetrics()
});
4、content.js

content中会每秒向background发送消息,同步最新的内存占用情况;同时会在页面加入悬浮窗,显示页面实时内存占用情况

// 创建悬浮窗容器
const monitor = document.createElement('div');
monitor.id = 'performance-monitor';
document.body.appendChild(monitor);let start = falsesetInterval(update, 1000);console.log("当前页面已注入content");function update() {usedJSHeapSize = performance.memory.usedJSHeapSize;if (usedJSHeapSize >= 1024 * 1024 * 100) {monitor.innerHTML = `${(usedJSHeapSize / 1024 / 1024 / 1024).toFixed(1)}G`} else {monitor.innerHTML = `${(usedJSHeapSize / 1024 / 1024).toFixed(0)}M`}if (start) {chrome.runtime.sendMessage({type: 'setPerformanceMetrics',data: {memory: monitor.innerHTML,timestamp: Date.now()}});}
}chrome.runtime.onMessage.addListener((message) => {if (message.type == "startCollect") {console.log("开始收集tab【" + message.data.title + "】的数据",formatDate(new Date()))start = true}if (message.type == "stopCollect") {console.log("因切换到tab【" + message.data.title + "】停止收集数据", formatDate(new Date()))start = false}return true
});let isDragging = false;
let startX, startY, initialX, initialY, initialW, initialH, dx, dy;monitor.addEventListener('mousedown', (e) => {isDragging = true;const computedStyle = getComputedStyle(monitor);initialX = parseFloat(computedStyle.right);initialY = parseFloat(computedStyle.top);initialW = parseFloat(computedStyle.width);initialH = parseFloat(computedStyle.height);startX = e.clientX;startY = e.clientY;monitor.style.transition = 'none';
});document.addEventListener('mousemove', e => {if (!isDragging) return;requestAnimationFrame(() => {dx = e.clientX - startX;dy = e.clientY - startY;monitor.style.right = `${Math.min(Math.max(initialX - dx, 0), document.documentElement.clientWidth - initialW)}px`;monitor.style.top = `${Math.min(Math.max(initialY + dy, 0), document.documentElement.clientHeight - initialH)}px`;});
});document.addEventListener('mouseup', () => {if (!isDragging) return;monitor.style.transition = 'transition: all 0.3s';// 清理状态isDragging = false;
});function formatDate(date) {const year = date.getFullYear();const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 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}`;
}
5、background.js

在每次激活tab页变更时的时候通知旧的tab页无需发送内存数据,通知新的tab页开始发送内存数据,收到页面发送的数据后将其存在background的localstorage中(注意是background的localstorage中)
在这里插入图片描述

console.log("welcome credential manager!")let lastTabId = 0// 激活Tab页监听
async function handleTabActivated(tabId, windowId, type = "active") {console.log("lastTabId", lastTabId, "tabId", tabId);if (lastTabId == tabId && type == 'active') {return}try {const tab = await chrome.tabs.get(tabId);console.log("当前激活页面", tab);chrome.tabs.sendMessage(lastTabId, {type: 'stopCollect',data: tab}).catch(() => { });lastTabId = tabIdchrome.tabs.sendMessage(tabId, {type: 'startCollect',data: tab}).catch(() => { });chrome.storage.local.get(['tabLogs'], ({ tabLogs = [] }) => {const newLog = {timestamp: Date.now(),windowId,...tab};chrome.storage.local.set({tabLogs: [...tabLogs.slice(-99), newLog]});});} catch (error) {console.warn('获取标签页失败:', error);}
}
// Tab页切换监听
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {console.log('tabId:', tabId, "changeInfo", changeInfo);handleTabActivated(tabId, tab?.windowId, "update");
});
chrome.tabs.onActivated.addListener(activeInfo => {handleTabActivated(activeInfo.tabId, activeInfo.windowId);
});
// 窗口切换监听(切换窗口导致的tab页切换不会触发chrome.tabs.onActivated事件)
chrome.windows.onFocusChanged.addListener(windowId => {if (windowId !== chrome.windows.WINDOW_ID_NONE) {chrome.tabs.query({ active: true, windowId }, ([tab]) => {handleTabActivated(tab.id, windowId);});}
});// Cookie变化监听
chrome.cookies.onChanged.addListener(changeInfo => {chrome.storage.local.get(['cookieLogs'], ({ cookieLogs = [] }) => {const newLog = {timestamp: Date.now(),...changeInfo};chrome.storage.local.set({cookieLogs: [...cookieLogs.slice(-99), newLog]});});
});chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {console.log("接收到消息", message);if (message.type == "setPerformanceMetrics") {chrome.storage.local.set({performanceMetrics: message.data});}if (message.type == "getPerformanceMetrics") {chrome.storage.local.get("performanceMetrics", (result) => {sendResponse(result.performanceMetrics); // 通过 sendResponse 返回结果});}return true
});
6、页面效果

在这里插入图片描述

3、结语

相信通过文中代码就可以感受到chrome插件强大的功能,再次提醒不要安装来路不明的chrome插件!!!

相关文章:

  • 专题五:floodfill算法(太平洋大西洋水流问题)
  • POJ3107树的重心
  • java collection集合特点知识点详解
  • Golang的Web应用架构设计
  • CentOS相关操作hub(更新中)
  • Hooks实现原理与自定义Hooks
  • 头歌之软件工程-用例设计
  • 【C++】不推荐使用的std::allocator<void>
  • ETL数据集成产品选型需要关注哪些方面?
  • 嵌入式自学第二十四天
  • 操作系统————五种页面置换算法(OPT,FIFO,LRU,NRU,加强版NRU)大总结
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(24):受身形
  • 4.7 时间模块
  • 《深入理解数组名:sizeof(arr)、arr 和 arr 的区别》
  • 开源情报的发展前景与行业运用
  • 后退的风景
  • Spring boot 集成 Knife4j
  • 专题五:floodfill算法(扫雷游戏精讲)
  • vs2017编译zlib1.2.11
  • 定积分,不定积分,变限积分和反常积分的对比记忆
  • 王毅同丹麦外交大臣会谈,表示在格陵兰问题充分尊重丹麦主权和领土完整
  • 首届巴塞尔艺术奖公布:大卫·哈蒙斯、曹斐等36人获奖
  • 国家主席习近平任免驻外大使
  • “80后”南京大学天文与空间科学学院教授施勇加盟西湖大学
  • 光明日报社副总编辑薄洁萍调任求是杂志社副总编辑
  • “80后”萍乡市安源区区长邱伟,拟任县(区)委书记