浏览器插件注入-拦截页面对应请求

黄粱一梦2024-08-210

浏览器插件注入拦截页面请求

拦截效果

效果图

通过content-script.js

注入通过content.js在宿主页面进行修改宿主页面的fetch或者XHR请求然后进行拦截

injected.js文件


    (function (xhr) {

        var XHR = XMLHttpRequest.prototype;

        var open = XHR.open;
        var send = XHR.send;

        let interceptList = [
            // {
            //     url:'/getDouyinReverseNotify',eventName:'findDouyinOrder'
            // },
            {
                url:'/getDispatchOrderCount',eventName: 'dispatchOrderCount'
        }] // 需要拦截的请求路径

        XHR.open = function (method, url) {
            this._method = method;
            this._url = url;
            return open.apply(this, arguments);
        };

        XHR.send = function (postData) {
            // console.log('xhr request:', this._method, this._url, postData);
            let method = this._method
            let url = this._url
            // 适合的请求拦截 并发送给content.js
            let intercept = interceptList.find(intercep => url.includes(intercep.url))
            // console.log('intercept',intercept)
            if (intercept) {
                // 符合拦截情况的请求
                // console.log('符合拦截列表的XHR请求 xhr request:', method, url, postData);
                this.addEventListener('load', function () {
                    // sessionStorage['key'] = JSON.stringify(response); // 插件需要添加'storage'权限
                    // document.cookie
                    // localStorage['key']
                    // 将url和method传递给content.js然后进行判断是否是要进行相应的逻辑
                    window.postMessage({ type: 'xhr', data: this.response,url,method,eventName:intercept.eventName }, '*');  // 将响应发送到 content script
                });
            }
            return send.apply(this, arguments);
        };
    })(XMLHttpRequest);

(function () {
    let origFetch = window.fetch;
    window.fetch = async function (...args) {
        const response = await origFetch(...args);
        console.log('fetch request:', args);

        response
            .clone()
            .blob() // 此处需要根据不同数据调用不同方法,这里演示的是二进制大文件,比如音频
            .then(data => {
                // 对于二进制大文件可以创建为URL(blob:开头),供其它脚本访问
                //sessionStorage['wave'] = URL.createObjectURL(data); // 插件需要添加'storage'权限
                window.postMessage({ type: 'fetch', data: URL.createObjectURL(data) }, '*'); // send to content script
            })
            .catch(err => console.error(err));
        return response;
    }
})();

content-script.js

/**
 * 需要拦截请求地址
 * xhr request: POST http://c.eastisred.top/api/dispatchprocess/getDouyinReverseNotify 
 *  */


console.log('content script start');

let pageConfig = {
    voicePath:""
}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    console.log('接受到参数', request);
    // 修改颜色
    if (request.name == 'changeBg') {
        changBg(request.url, request.opc)
    }
    else if (request.name == 'changeFont') {
        setFontWeight(request.weight)
    }else if (request.name == 'changeVoice') {
        let { path } = request
        pageConfig.voicePath = path
        console.log('当前播放的音色地址',pageConfig.voicePath)
        // setPlayVoice()
    }
});
function changBg(url, opc) {
    console.log('当前需要更改的地址', url);
    let appmain = document.querySelector('.app-main')
    console.log(appmain)
    appmain.style.backgroundImage = `url('${url}')`
    appmain.style.backgroundRepeat = 'no-repeat'
    appmain.style.backgroundSize = 'cover'
    let opcityDom = document.querySelector('.app-main>.w-full')
    opcityDom.style.opacity = opc ? opc : 0.9
}

function setFontWeight(weight) {
    document.querySelector('html').style.fontWeight = weight
}

/**
 * inject injected script
 * 
 * 开始拦截注入
 * 
 * */


/**
 *
 * 抖音订单来了时候处理函数
 *
 * 抖音返回信息格式
 *
 * {
 *     "data": {
 *         "result": [],
 *         "count": 0
 *     },
 *     "success": true,
 *     "code": 0,
 *     "msg": "success",
 *     "timestamp": 1724039906925
 * }
 *
 * */

let audio = null
function createAudio(src){
    audio = new Audio()
    audio.src = chrome.runtime.getURL(src || 'static/source/defaultvoice.WAV')
    audio.addEventListener('canplaythrough', () => {
        // 音频文件已经加载完毕,准备播放
        audio.play().catch(error => {
            console.error('播放音频时出错:', error);
        });
    });
    audio.addEventListener('error', (event) => {
        console.error('音频加载出错:', event);
    });
}

function playAudio(src){
    if(audio){
        audio.src = chrome.runtime.getURL(src || 'static/source/defaultvoice.WAV')
        audio.play()
    }else {
        createAudio(src)
    }
}


function findDouyinOrderHandler(responseData){
    let { data } = responseData
    console.log('data_count----',data.data.count)
    if(data && data.success && data.data.count > 0){
        console.log('Blocked Order 需要播报音乐', '事件名称:',data.eventName,'method:', data.method,'请求地址:', data.url,)
        playAudio()
        return
    }
    playAudio()
}


/**
 * @name dispatchOrderCountHandler
 * @description 拦截分配订单的数量
 *
 * */

function dispatchOrderCountHandler(responseData){
    let { data } = responseData
    console.log('待处理订单数量',data.data,'\n播放订单音乐地址',pageConfig.voicePath || 'static/source/defaultvoice.WAV')
    if(data && data.success && data.data > 0){
        console.log('Blocked Order 需要播报音乐', '事件名称:',data.eventName,'method:', data.method,'请求地址:', data.url,)
        playAudio(pageConfig.voicePath || 'static/source/defaultvoice.WAV')
        return
    }
    // playAudio(pageConfig.voicePath || 'static/source/defaultvoice.WAV')
}

var s = document.createElement('script');
s.src = chrome.runtime.getURL('injected.js');
s.onload = function () {
    this.remove();
};
(document.head || document.documentElement).appendChild(s);

// receive message from injected script
window.addEventListener('message', function (e) {
    console.log('content script received:', e.data);
    let { eventName } = e.data

    switch (eventName) {
        case 'findDouyinOrder':
            findDouyinOrderHandler(e.data)
            break;
        case 'dispatchOrderCount':
            dispatchOrderCountHandler(e.data)
    }
});

manifest.json 配置文件

{
  "name": "XFPlugin",
  "version": "1.0.0",
  "manifest_version": 3,
  "author": "XF",
  "description": "XFPlugin -- 5.25",
  "icons": {
    "16": "static/img/default-icon.png",
    "19": "static/img/default-icon.png",
    "38": "static/img/default-icon.png",
    "48": "static/img/default-icon.png",
    "128": "static/img/default-icon.png"
  },
  "background": {
    "service_worker": "background.js",
    "type": "module"
  },
  "action": {
    "default_icon": {
      "19": "static/img/default-icon.png"
    },
    "default_title": "XFPlugin",
    "default_popup": "page/popup.html"
  },
  "content_scripts": [
    {
      "run_at": "document_start",
      "all_frames": true,
      "matches": [
        "http://*/*",
        "https://*/*"
      ],
      "js": [
        "content-script.js"
      ]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": [
        "static/source/defaultvoice.WAV",
        "static/source/tianmeinvhai.WAV",
        "static/source/labixiaoxin.WAV",
        "static/source/keaiyangyang.WAV",
        "static/source/koushao.mp3",
        "static/source/dingdong.mp3",
        "static/source/dingdong2.mp3",
        "static/source/Freesound.wav",
        "static/source/freesound.wav",
        "static/source/caixukun.mp3",
        "static/source/newSound.mp3",
        "injected.js"
      ],
      "matches": [
        "http://*/*",
        "https://*/*",
        "<all_urls>"
      ],
      "use_dynamic_url": true
    }
  ],
  "permissions": [
    "tabs",
    "webRequest",
    "cookies",
    "storage", 
    "<all_urls>"
  ],
  "host_permissions": [
    "http://*/*",
    "https://*/*"
  ]
}

注意引入的静态资源文件需要在web_accessible_resources字段的resource字段中进行声明不然无法使用.

分类:随笔

标签:浏览器插件

上一篇开发者工具,调试 network 请求排序问题?下一篇电脑浏览器截图时颜色泛白,高曝光解决方法

版权声明

本文系作者 @黄粱一梦 转载请注明出处,文中若有转载的以及参考文章地址也需注明。\(^o^)/~

Preview