You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

浏览器扩展中如何覆写后续创建的Worker的postMessage/onmessage方法

监控所有Worker发送消息的实现方案

核心问题分析

你之前的尝试无效,本质是因为浏览器扩展的内容脚本运行在隔离的沙盒环境(ISOLATED世界),和页面的全局上下文完全独立。你修改的Worker.prototype是内容脚本自己的上下文对象,而非页面实际创建Worker时使用的全局对象。

关键解决方案:注入主世界脚本

要实现监控,必须将覆写代码注入到页面的主世界(MAIN world)——也就是页面自身的全局上下文,这样代码才能访问并修改页面实际使用的Worker构造函数和原型。

步骤1:配置扩展权限与注入时机

在扩展的manifest.json中,需要配置必要权限,并指定脚本注入到主世界,且在页面加载前执行:

{
  "manifest_version": 3,
  "name": "Worker消息监控工具",
  "version": "1.0",
  "permissions": ["scripting", "webNavigation"],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["inject.js"],
      "run_at": "document_start",
      "world": "MAIN"
    }
  ]
}
  • run_at: "document_start":确保在页面创建任何Worker前完成覆写
  • world: "MAIN":将脚本注入到页面的全局上下文(Chrome 112+支持)

步骤2:编写主世界注入脚本(inject.js)

以下代码会覆盖所有类型Worker的消息发送逻辑,包括页面端向Worker发送消息、Worker内部发送消息两种场景:

// 1. 覆写页面端向专用Worker发送消息的方法
const originalWorkerPostMessage = Worker.prototype.postMessage;
Worker.prototype.postMessage = function(...args) {
  console.log('[监控] 页面→专用Worker:', this, args);
  // 可在此添加自定义逻辑:修改参数、记录日志等
  return originalWorkerPostMessage.apply(this, args);
};

// 2. 覆写页面端向共享Worker发送消息的方法
const originalSharedWorkerPostMessage = SharedWorker.prototype.postMessage;
SharedWorker.prototype.postMessage = function(...args) {
  console.log('[监控] 页面→共享Worker:', this, args);
  return originalSharedWorkerPostMessage.apply(this, args);
};

// 3. 代理Worker构造函数,覆写Worker内部的postMessage
const originalWorkerConstructor = Worker;
window.Worker = new Proxy(originalWorkerConstructor, {
  construct(target, args) {
    const scriptUrl = args[0];
    // 生成代理脚本:先覆写Worker内部的postMessage,再导入原脚本
    const proxyScript = `
      // 覆写Worker内部发送消息的方法
      const originalInnerPostMessage = self.postMessage;
      self.postMessage = function(...args) {
        console.log('[监控] 专用Worker内部发送消息:', args);
        return originalInnerPostMessage.apply(this, args);
      };
      // 导入原Worker脚本
      importScripts('${scriptUrl}');
    `;
    const proxyBlob = new Blob([proxyScript], { type: 'application/javascript' });
    const proxyUrl = URL.createObjectURL(proxyBlob);
    args[0] = proxyUrl;
    
    const worker = new target(...args);
    URL.revokeObjectURL(proxyUrl);
    return worker;
  }
});

// 4. 代理SharedWorker构造函数,覆写SharedWorker内部的postMessage
const originalSharedWorkerConstructor = SharedWorker;
window.SharedWorker = new Proxy(originalSharedWorkerConstructor, {
  construct(target, args) {
    const scriptUrl = args[0];
    const proxyScript = `
      const originalInnerPostMessage = self.postMessage;
      self.postMessage = function(...args) {
        console.log('[监控] 共享Worker内部发送消息:', args);
        return originalInnerPostMessage.apply(this, args);
      };
      importScripts('${scriptUrl}');
    `;
    const proxyBlob = new Blob([proxyScript], { type: 'application/javascript' });
    const proxyUrl = URL.createObjectURL(proxyBlob);
    args[0] = proxyUrl;
    
    const sharedWorker = new target(...args);
    URL.revokeObjectURL(proxyUrl);
    return sharedWorker;
  }
});

// 5. 拦截Service Worker注册,覆写其内部消息发送逻辑
const originalSWRegister = navigator.serviceWorker.register;
navigator.serviceWorker.register = async function(scriptUrl, options) {
  // 获取原Service Worker脚本内容
  const response = await fetch(scriptUrl);
  const originalScript = await response.text();
  
  // 注入覆写代码到脚本开头
  const injectedScript = `
    // 覆写Service Worker内部发送消息的方法
    const originalSWPostMessage = self.postMessage;
    self.postMessage = function(...args) {
      console.log('[监控] Service Worker内部发送消息:', args);
      return originalSWPostMessage.apply(this, args);
    };
    
    // 覆写Service Worker向客户端发送消息的方法
    if (self.clients) {
      const originalClientPostMessage = Client.prototype.postMessage;
      Client.prototype.postMessage = function(...args) {
        console.log('[监控] Service Worker→客户端:', this, args);
        return originalClientPostMessage.apply(this, args);
      };
    }
  ` + originalScript;
  
  // 创建新的Blob URL作为注册地址
  const blob = new Blob([injectedScript], { type: 'application/javascript' });
  const newScriptUrl = URL.createObjectURL(blob);
  
  // 执行原注册逻辑
  const registration = await originalSWRegister.call(this, newScriptUrl, options);
  
  // 清理临时URL
  URL.revokeObjectURL(newScriptUrl);
  return registration;
};

关键细节说明

  • 页面端vs Worker内部:上述代码同时覆盖了两个方向的消息发送——页面主动给Worker发消息,以及Worker内部主动向外发消息。
  • Service Worker特殊处理:Service Worker运行在独立的后台上下文,无法通过原型直接覆写,因此需要拦截注册过程,修改脚本内容注入覆写代码。
  • Blob URL兼容:对于动态创建的Blob URL类型Worker,通过代理构造函数生成中间脚本,确保覆写代码优先执行。

内容的提问来源于stack exchange,提问作者Andreas Toresäter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:24:54