浏览器插件实现cookie、localStorage、sessionStorage信息复制粘贴功能
背景
在日常开发中,我们总是会先在本地环境进行开发调试代码,但在开发过程中又依赖测试环境的一些数据配置信息,因此我们会通过各种方式将本地环境获取测试环境的信息配置,例如配置域名代理,或者将测试环境的数据作为mock数据供本地调用,又或者将测试环境的用户登录信息cookie复制到本地。本章讲述开发一个将cookie信息快速复制、粘贴小功能的浏览器插件。
插件功能概述
- 复制功能
- 从当前页面获取
cookie
、sessionStorage
、localStorage
数据。 - 用户可以选择哪些信息(如:
cookie
、sessionStorage
、localStorage
)需要复制。
- 从当前页面获取
- 粘贴功能
- 将选中的信息粘贴到目标页面
- 目标页面可以是当前打开的页面或一个新的tab页
插件结构
- 插件结构如下:
- manifest.json:插件的元数据和权限配置。
- contentScript.js:内容脚本,处理页面信息的复制与粘贴。
- popup.html:插件的用户界面,用于选择复制和粘贴的内容。
- popup.js:插件界面逻辑,处理用户操作。
- icon.png:插件的图标文件。
话不多说,直接上代码
manifest.json
插件的manifest.json文件配置了插件的基本信息和权限
{
"manifest_version": 3,
"name": "Copy Cookie & Storage",
"version": "0.0.1",
"description": "实现cookie、sessionStorage、localStorage信息复制与粘贴功能的插件",
"permissions": ["cookies", "activeTab", "sessions"],
"host_permissions": [
"*://*/*"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentScript.js"]
}
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "/icon.png",
"32": "/icon.png",
"48": "/icon.png",
"128": "/icon.png"
}
},
"icons": {
"16": "/icon.png",
"32": "/icon.png",
"48": "/icon.png",
"128": "/icon.png"
},
"author": "milk"
}
contentScript.js
contentScript.js文件负责从当前页面提取信息,并将信息粘贴到目标页面。
// content_script.js
// 监听请求获取当前 tab 的 cookies
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
// 可以在这里对消息进行处理
if (message.name === "from_pupop_get_storages") {
const allLocalStorage = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
allLocalStorage[key] = value;
}
// 获取所有sessionStorage键值对
const allSessionStorage = {};
for (let i = 0; i < sessionStorage.length; i++) {
const key = sessionStorage.key(i);
const value = sessionStorage.getItem(key);
allSessionStorage[key] = value;
}
sendResponse({
local: allLocalStorage,
session: allSessionStorage,
})
}
if (message.name === "from_pupop_send_local_storage") {
for (const key in message.value) {
if (Object.hasOwnProperty.call(message.value, key)) {
const element = message.value[key];
localStorage.setItem(key, element)
}
}
}
if (message.name === "from_pupop_send_session_storage") {
for (const key in message.value) {
if (Object.hasOwnProperty.call(message.value, key)) {
const element = message.value[key];
sessionStorage.setItem(key, element)
}
}
}
if (message.name === "from_pupop_clear_storages") {
if (message.value.includes('sessionStorage')) {
sessionStorage.clear();
}
if (message.value.includes('localStorage')) {
localStorage.clear();
}
}
// 如果需要,可以向popup.js发送回复消息
// sendResponse({ received: true });
});
popup.html
这是插件的用户界面,允许用户选择要复制的信息,并粘贴到目标页面
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<style>
body {
min-width: 320px;
overflow-x: hidden;
font-family: Arial, sans-serif;
font-size: 12px;
}
#container {
min-height: 210px;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 6px;
position: relative;
}
.welcomeLabel {
position: absolute;
top: 90px;
left: 50%;
margin-left: -90px;
font-size: 22px;
font-weight: 600;
width: 180px;
text-align: center;
font-family: fantasy;
color: #544f4f;
}
.titile {
font-weight: 600;
font-size: 14px;
text-align: center;
}
.radio-rows {
font-size: 14px;
font-weight: 600;
display: flex;
justify-content: space-around;
align-items: center;
flex-direction: column;
}
.radio-rows button {
margin: 10px 0;
padding: 5px 10px;
width: 140px;
display: flex;
align-items: center;
}
.radio-rows input {
margin-top: 0;
}
.logoWrapper {
flex: 1;
text-align: center;
}
.logoWrapper img {
max-height: 64px;
}
.formWrapper1,
.formWrapper2 {
display: flex;
flex-direction: column;
display: none;
}
.container1 .formWrapper1 {
display: block;
}
.container2 .formWrapper2 {
display: block;
}
.successIcon {
width: 20px;
margin-right: 10px;
}
.successCopy {
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
margin-bottom: 10px;
}
.successPasteLabel {
display: none;
}
.inputWrapper {
display: flex;
flex-direction: column;
}
.inputWrapper label {
font-weight: bold;
}
input[type='text'] {
width: 100%;
border: 2px solid #aaa;
border-radius: 4px;
margin: 8px 0;
outline: none;
padding: 8px;
box-sizing: border-box;
transition: 0.3s;
}
input[type='text']:focus {
border-color: dodgerBlue;
box-shadow: 0 0 8px 0 dodgerBlue;
}
.buttonWrapper {
display: flex;
align-items: center;
justify-content: space-between;
margin-left: -10px;
}
button {
height: 30px;
flex: 1;
margin-left: 10px;
background-color: #3498db;
border: unset;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
button:hover {
background-color: #217dbb;
}
.footer {
font-size: 10px;
margin-top: 8px;
color: #756e6e;
display: flex;
align-items: center;
justify-content: space-between;
}
.footer span {
display: flex;
align-items: center;
justify-content: center;
}
.footer img {
width: 12px;
margin: 0 4px;
}
.link {
font-weight: bold;
color: #756e6e;
}
.versionLabel {
position: absolute;
top: 6px;
right: 6px;
font-size: 12px;
color: #756e6e;
}
</style>
<script src="popup.js"></script>
</head>
<body>
<span class="versionLabel">v0.0.1</span>
<div id="container">
<div class="titile">选择你要拷贝或清除的数据</div>
<div class="radio-rows">
<button class="radio-row">localStorage:<input class="radio" type="radio" name="localStorage" value="localStorage" /></button>
<button class="radio-row">SessionStorage:<input class="radio" type="radio" name="SessionStorage" value="sessionStorage" /></button>
<button class="radio-row">Cookies:<input class="radio" type="radio" name="Cookies" value="cookies" /></button>
</div>
<div class="formWrapper1">
<div id="successPasteLabel" class="successPasteLabel">
<div class="successCopy">
<img class="successIcon" src="images/success.png" />
<span>拷贝成功</span>
</div>
</div>
<div class="buttonWrapper">
<button id="copyButton">拷贝</button>
<button id="clearButton">清除</button>
</div>
</div>
<div class="formWrapper2">
<div class="successCopy">
<img class="successIcon" src="images/success.png" />
<span>Copied Successfully!</span>
</div>
<div class="buttonWrapper">
<button id="pasteButton">粘贴</button>
<button id="resetButton">重置</button>
</div>
</div>
</div>
</body>
</html>
popup.js
popup.js负责处理UI的交互逻辑,向contentScript.js发送请求来获取或粘贴数据
const copyContentSet = new Set(['localStorage', 'sessionStorage', 'cookies']);
const onCopyButtonClick = () => {
chrome.tabs.query(
{
status: 'complete',
windowId: chrome.windows.WINDOW_ID_CURRENT,
active: true,
},
tab => {
chrome.tabs.sendMessage(tab[0].id, { name: "from_pupop_get_storages" }, function(response) {
if (response) {
localStorage.copyStorageData = JSON.stringify(response)
}
});
chrome.cookies.getAll({ url: tab[0].url }, cookie => {
localStorage.copyCookieData = JSON.stringify(cookie);
setTimeout(() => handlePopupUI('copy'), 100);
});
},
);
};
const removeOldCookies = (cookies, index, url) => {
if (!cookies[index]) return;
try {
chrome.cookies.remove({
url: url + cookies[index].path,
name: cookies[index].name,
}, () => removeOldCookies(cookies, index + 1, url));
} catch (e) {
console.error(`移除cookie错误: ${error}`, cookies[index].name);
}
};
const clearStorages = () => {
chrome.tabs.query(
{
status: 'complete',
windowId: chrome.windows.WINDOW_ID_CURRENT,
active: true,
},
tab => {
chrome.tabs.sendMessage(tab[0].id, { name: "from_pupop_clear_storages", value: Array.from(copyContentSet.values()).join(',') }, function(response) {
document
.getElementById('clearButton').innerHTML = '已清除'
});
if (copyContentSet.has('cookies')) {
chrome.cookies.getAll({ url: tab[0].url }, cookies => {
removeOldCookies(cookies, 0, tab[0].url);
});
}
},
);
}
const onPasteButtonClick = async () => {
let copyCookieData;
let copyStorageData
try {
copyCookieData = localStorage.copyCookieData
? JSON.parse(localStorage.copyCookieData)
: null;
copyStorageData = localStorage.copyStorageData
? JSON.parse(localStorage.copyStorageData)
: null;
} catch (e) {
return alert('粘贴cookie报错,请重试');
}
if (!copyCookieData)
return alert('请先复制cookie');
chrome.tabs.query(
{
status: 'complete',
windowId: chrome.windows.WINDOW_ID_CURRENT,
active: true,
},
tab => {
if (!tab?.[0]?.url) {
return alert('URL无效');
}
if (copyContentSet.has('cookies')) {
chrome.cookies.getAll({ url: tab[0].url }, cookies => {
copyCookieData.forEach(({ name, value, path }) => {
try {
chrome.cookies.set({
url: tab[0].url,
name,
value,
});
} catch (error) {
console.error(`报错: ${error}`);
}
});
onResetButtonClick('paste');
});
}
if (copyContentSet.has('localStorage')) {
chrome.tabs.sendMessage(tab[0].id, { name: "from_pupop_send_local_storage", value: copyStorageData.local });
}
if (copyContentSet.has('sessionStorage')) {
chrome.tabs.sendMessage(tab[0].id, { name: "from_pupop_send_session_storage", value: copyStorageData.session });
}
}
);
};
const onResetButtonClick = action => {
localStorage.removeItem('copyCookieData');
handlePopupUI(action);
};
const handlePopupUI = action => {
const copyCookieData = localStorage.copyCookieData;
const containerElement = document.getElementById('container');
containerElement.setAttribute('class', '');
if (copyCookieData) {
containerElement.classList.add('container2');
} else {
containerElement.classList.add('container1');
}
const successPasteLabel = document.getElementById('successPasteLabel');
const welcomeLabel = document.getElementById('welcomeLabel');
if (action === 'paste') {
successPasteLabel.setAttribute('style', 'display: block');
} else {
successPasteLabel.setAttribute('style', 'display: none');
}
};
window.addEventListener('load', () => {
handlePopupUI();
document
.getElementById('copyButton')
.addEventListener('click', onCopyButtonClick);
document
.getElementById('clearButton')
.addEventListener('click', clearStorages);
document
.getElementById('pasteButton')
.addEventListener('click', onPasteButtonClick);
document
.getElementById('resetButton')
.addEventListener('click', () => onResetButtonClick('reset'));
const radios = document
.getElementsByClassName('radio')
for (let index = 0; index < radios.length; index++) {
const element = radios[index];
element.checked = true;
element.parentElement.addEventListener('click', (evt) => {
if (copyContentSet.has(element.value)) {
copyContentSet.delete(element.value)
element.checked = false
element.removeAttribute('checked')
} else {
copyContentSet.add(element.value)
element.checked = true
}
})
}
});
测试和调试
- 打开chrome浏览器,进入
chrome://extensions/
- 启用开发者模式并点击 加载已解压的扩展程序。
- 选择你的插件文件夹并加载插件。
- 你可以在浏览器工具栏点击插件图标来使用它。
总结
这个浏览器插件实现了复制和粘贴 cookie
、sessionStorage
和 localStorage
信息的功能。用户可以选择哪些信息需要复制,并且将其粘贴到当前页面或其他页面。通过使用 Chrome 的 cookies 和 storage API,插件能够灵活地获取和设置数据。