浏览器插件实现复制并粘贴cookie、localStorage、sessionStorage信息功能

浏览器插件实现cookie、localStorage、sessionStorage信息复制粘贴功能

背景

在日常开发中,我们总是会先在本地环境进行开发调试代码,但在开发过程中又依赖测试环境的一些数据配置信息,因此我们会通过各种方式将本地环境获取测试环境的信息配置,例如配置域名代理,或者将测试环境的数据作为mock数据供本地调用,又或者将测试环境的用户登录信息cookie复制到本地。本章讲述开发一个将cookie信息快速复制、粘贴小功能的浏览器插件。

插件功能概述

  1. 复制功能
    • 从当前页面获取cookiesessionStoragelocalStorage数据。
    • 用户可以选择哪些信息(如:cookiesessionStoragelocalStorage)需要复制。
  2. 粘贴功能
    • 将选中的信息粘贴到目标页面
    • 目标页面可以是当前打开的页面或一个新的tab页

插件结构

  1. 插件结构如下
  • 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
            }
        })
    }
});

测试和调试

  1. 打开chrome浏览器,进入chrome://extensions/
  2. 启用开发者模式并点击 加载已解压的扩展程序
  3. 选择你的插件文件夹并加载插件。
  4. 你可以在浏览器工具栏点击插件图标来使用它。

总结

这个浏览器插件实现了复制和粘贴 cookiesessionStorage localStorage 信息的功能。用户可以选择哪些信息需要复制,并且将其粘贴到当前页面或其他页面。通过使用 Chrome 的 cookies 和 storage API,插件能够灵活地获取和设置数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值