美文网首页
chrome extension manifest v3

chrome extension manifest v3

作者: niccky | 来源:发表于2023-08-12 22:02 被阅读0次

    记录 chrome exension manifest v3 踩过的坑,不要升级V3,不要升级V3,不要升级V3
    1、manifest 配置

    {
        "name": "demo",
        "version": "1.0.1",
        "manifest_version": 3,
        "description": "d",emo
        "background": {
            "service_worker": "background.js"
        },
        "omnibox": { "keyword" : "auto" }, 
        "action": {
            "default_title": demo,
            "default_popup": "popup.html"
        },
        "devtools_page": "devtools.html",
        "options_page": "options.html",
        "content_scripts": [
            {
                "matches": ["https://*/*"],
                "js": [
                    "content.js"
                ],
                "run_at": "document_start"
            }
        ],
        "permissions": [
            "background",
            "declarativeNetRequest",
            "declarativeNetRequestWithHostAccess",
            "declarativeNetRequestFeedback",
            "contextMenus",
            "activeTab",
            "tabs",
            "storage",
            "unlimitedStorage"
        ],
        "host_permissions": ["<all_urls>"]
    }
    

    2、background.js

    var ports = [];
    var globalId = 0;
    chrome.runtime.onConnect.addListener(function(port) {
        if (port.name !== "devtools") return;
        ports.push(port);
        port.onDisconnect.addListener(function() {
            var i = ports.indexOf(port);
            if (i !== -1) ports.splice(i, 1);
        });
        port.onMessage.addListener(async function(msg) {
            console.log('[bg]', msg);
            
            if(msg.action === 'sync') {
                syncLocal();
            }
        });
    });
    
    chrome.runtime.onMessage.addListener(async (msg, e, resp) => {
        if(msg.action === 'sync') {
            syncLocal();
        }
        console.log(msg);
    });
    
    function isEmpty(obj) {
        if(!obj) {
            return true;
        }
    
        for(const o in obj) {
            if(obj.hasOwnProperty(o)) {
                return false;
            }
        }
    
        return true;
    }
    
    async function getLocal(key) {
        const local = await chrome.storage.sync.get(key);
        if(isEmpty(local)) {
            return null;
        }
        return local;
    }
    
    async function setLocal(key, value) {
        await chrome.storage.sync.set({[key]: value});
    }
    
    chrome.runtime.onInstalled.addListener(async function (details) {
        if (details.reason == "install") {
            chrome.contextMenus.create({
                type: 'normal',
                title: 'demo',
                contexts: ['all'],
                id: 'add-rule'
            });
        } else if (details.reason == "update") {
            // perform some logic
        }
    });
    
    async function syncLocal() {
        const local = await getLocal('tmps');
        if(local && local.tmps) {
            for(const tmp in local.tmps) {
                if(local.tmps.hasOwnProperty(tmp)) {
                    console.log(local.tmps[tmp]);
                }
            }
            const rules = Object.values(local.tmps);
            const addedRules = await getRules();
            if(addedRules && addedRules.length) {
                const lastRule = addedRules.at(-1);
                const lastId =  Number(lastRule.id);
                globalId = lastId + 1;
            } else {
                globalId = 1;
            }
            const formatRules = rules.map(r => ruleFormat(r.type, r.url));
            for(const rule of rules) {
                await setLocal(rule.url, rule);
            }
        
            const addRules = {
                addRules: formatRules
            };
                   
            await updateDynamicRules(addRules);
            await removeLocal("tmps");
            await setLocal('ruleId', globalId);
        }
    }
    
    function ruleFormat(type, url) {
        const condition = {};
        // domainType: firstParty, thirdParty
        // resourceTypes: main_frame, xmlhttprequest,scripts
        if (type === 'host') {
            condition.domains = [url];
        } else {
            condition.urlFilter = url;
        }
    
        const rule = {
                "id": globalId++,
                "priority": 1,
                "action": {
                    "type": "block"
                },
                "condition": condition
        };
        return rule;
    }
    
    async function updateDynamicRules(rules) {
        await chrome.declarativeNetRequest.updateDynamicRules(
            rules
        );
    }
    
    async function removeLocal(key) {
        await chrome.storage.sync.remove(key);
    }
    
    async function clean() {
        await chrome.storage.sync.clear();
        await cleanRules();
    }
    
    async function getRules() {
        return chrome.declarativeNetRequest.getDynamicRules();
    }
    
    async function cleanRules(ids) {
        let ruleIds = ids;
        if(ruleIds == null) {
            const rules = await getRules();
            ruleIds = rules.map(r => r.id);
        }
        
        await chrome.declarativeNetRequest.updateDynamicRules({
            removeRuleIds: ruleIds
        });
    }
    
    async function removeRule(domainOrId) {
        let ids;
        if(isNaN(domainOrId)) {
            const rules = await getRules();
            ids = rules.filter(r=>r.condition.domains.includes(domainOrId) || r.condition.urlFilter.includes(domainOrId)).map(r=>r.id);
        }else {
            ids = [domainOrId];
        }
        cleanRules(ids);
    }
    

    3、content.js

    function sendMessage(msg) {
        chrome.runtime.sendMessage(msg);
    }
    
    chrome.runtime.onMessage.addListener((msg, sender, resp) => {   
        if(msg.action === 'get_urls') {
            resp({action: 'done', urls: window.performance.getEntries()});
        } else {
            resp({action: 'nc', msg});
        }
    });
    
    function getAllUrls() {
        const allUrls = window.performance.getEntries();
        return allUrls.filter(r => r.type === 'resource').map(r => r.name);
    }
    

    4、share.js

    const port = chrome.runtime.connect({ name: 'devtools' });
    
    port.onMessage.addListener((msg, sender) => {
        log('[share]', msg);
    });
    
    function log(...args) {
        chrome.devtools.inspectedWindow.eval(`
        console.log(...${JSON.stringify(args)});
    `)
    };
    
    function createElem(tag, type) {
        const elem = document.createElem(tag);
        return type == null ? elem : (elem.type = type, elem);
    }
    
    function getElem(id) {
        return document.getElementById(id);
    }
    
    function appendNode(dom, nodes) {
        if (Array.isArray(nodes)) {
            nodes.forEach(node => dom.appendChild(node));
        } else {
            dom.appendChild(nodes);
        }
    }
    
    function sendMessage(msg) {
        port.postMessage(msg);
    }
    
    async function startSyncReq() {
        sendMessage({action: 'sync'});
    }
    
    function addElem(dom, url) {
        const hostType = url.split('/')[2];
        const template = `
            <li>
                <label><input type='radio' name="${url}" data-type="host" data-url="${hostType}"/> Host </label>
                <label><input type='radio' name="${url}" data-type="path" data-url="${url}"/> Path </label>
                <a href="${url}" target="_blank">${url}</>
            </li>
        `;
        const root = new DOMParser().parseFromString(template, "text/html");
        const node = root.body.firstElementChild;
        appendNode(dom, node);
    }
    
    async function setLocalReq(type, url) {
        const local = await getLocal('tmps');
        if(local) {
            local.tmps[url]={type, url};
            await setLocal('tmps', local.tmps);
        } else {
            await setLocal('tmps', {[url]:{type, url}});
        }
    }
    
    async function setLocal(key, value) {
        await chrome.storage.sync.set({[key]: value});
    }
    
    async function getLocal(key) {
        const local = await chrome.storage.sync.get(key);
        if(isEmpty(local)) {
            return null;
        }
        return local;
    }
    
    async function clean() {
        await chrome.storage.sync.clear();
    }
    
    function isEmpty(obj) {
        if (!obj) return true;
        for (const o in obj) {
            if (obj.hasOwnProperty(o))
                return false;
        }
        return true;
    }
    

    5、panel.js

    chrome.devtools.network.onRequestFinished.addListener(
        function(req) {
            const url = req.request.url;
            const dom = getElem('network');
            addElem(dom, url);        
        }
      );
    
    window.onload = function() {
        const dom = getElem('network');
        const btn = {
            btnSync: {
                elem: getElem('sync'),
                action() {
                    startSyncReq();
                }
            },
            btnClean: {
                elem: getElem('clean'),
                async action() {
                    await clean();
                    const title = this.elem.textContent;
                    this.elem.innerText = 'cleaned';
                    setTimeout(() => {
                        this.elem.innerText = title;
                    }, 3000);
                }
            },
            btnCapture: {
                elem: getElem('network'),
                action(e) {
                    if(e.target.nodeName == 'LABEL') return;
                    if(e.target.nodeName != 'INPUT') {
                        return;
                    }
                    const {type, url} = e.target.dataset;
                    console.log('['+type + ']', url);
                    setLocalReq(type, url);
                    return false;
                }
            },
            btnReload: {
                elem: getElem('reload'),
                action(e) {
                    while(dom.firstChild) {
                        dom.removeChild(dom.firstChild);
                    }
                    chrome.tabs.reload();
                }
            }
        };
    
        ['btnSync', 'btnClean','btnReload', 'btnCapture'].forEach(type => {
            btn[type].elem.addEventListener('click', e => {
               return  btn[type].action(e);
            })
        });
    }
    

    6、option.js

    
    const dom = getElem('requstList');
    chrome.declarativeNetRequest.getDynamicRules(rules => {
        const data = [];
        for (const rule of rules) {
            const url = rule.condition.urlFilter || rule.condition.domains[0];
            data.push(url);
            const elem = createElem('li');
            elem.textContent = url;
            dom.appendChild(elem);
        }
        dom.rules = data;
    });
    
    function createElem(tag, type) {
        const elem = document.createElement(tag);
        if (type) {
            elem.type = type;
        }
        return elem;
    }
    
    function getElem(id) {
        return document.getElementById(id);
    }
    
    chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
        const [tab] = tabs;
        const obj = await chrome.tabs.sendMessage(tab.id, { action: 'get_urls' });
        if (obj && obj.urls.length) {
            const urls = obj.urls.filter(r => r.entryType === 'resource').map(r => r.name);
            for (const url of urls) {
                const elem = createElem('li');
                elem.textContent = url;
                dom.appendChild(elem);
            };
            dom.urls = urls;
        }
        console.log(obj);
    });
    
    async function setLocalReq(type, url) {
        const local = await getLocal('tmps');
        if(local) {
            local.tmps[url]={type, url};
            await setLocal('tmps', local.tmps);
        } else {
            await setLocal('tmps', {[url]:{type, url}});
        }
    }
    
    async function setLocal(key, value) {
        await chrome.storage.sync.set({[key]: value});
    }
    
    async function getLocal(key) {
        const local = await chrome.storage.sync.get(key);
        if(isEmpty(local)) {
            return null;
        }
        return local;
    }
    
    function isEmpty(obj) {
        if(!obj) return true;
        for(const o in obj) {
            if(obj.hasOwnProperty(o)) {
                return false;
            }
        }
        return true;
    }
    
    
    const btn = {
        btnReload: {
            elem: getElem('reload'),
            action(e) {
                while(dom.firstChild) {
                    dom.removeChild(dom.firstChild);
                }
                chrome.tabs.reload();
            }
        },
        btnAll: {
            elem: getElem("all"),
            action() {
                const title = this.elem.textContent;
                this.elem.textContent = '开始处理...';
                const rules = dom.rules || [];
                const urls = dom.urls || [];
                const dataset = [...rules, ...urls];
                const allUrls = dataset.map(url => ({type: 'path', url}));
                allUrls.forEach(async ({type, url}) => {
                    await setLocalReq(type, url);
                });
    
                setTimeout(() => {
                    this.elem.textContent = title;
                }, 2000);
            }
        },
        btnSync: {
            elem: getElem('sync'),
            async action() {
                sendMessage({action: 'sync'});
            }
        }
    }
    
    function sendMessage(msg) {
        chrome.runtime.sendMessage(msg);
    }
    
    const options = ['btnReload', 'btnAll', 'btnSync'];
    
    options.forEach(type => {
        btn[type].elem.addEventListener('click', e => {
            return btn[type].action(e);
        });
    });
    
    

    相关文章

      网友评论

          本文标题:chrome extension manifest v3

          本文链接:https://www.haomeiwen.com/subject/ydxfmdtx.html