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

browser.tabs.sendMessage Chrome正常Firefox返回undefined问题

问题根因

Chrome和Firefox对扩展消息接口runtime.onMessage的异步响应逻辑存在实现差异:

  • Chrome环境下,只要消息监听器同步返回true,就会永久保持消息通信端口开放,直到sendResponse被调用,哪怕sendResponse放在setTimeout这类跨事件循环的宏任务里延迟执行,端口也不会被回收。
  • Firefox环境下,同步返回true仅能保持端口开放到当前事件循环的微任务队列执行完毕,一旦sendResponse在setTimeout这类下一轮事件循环才执行的宏任务中被调用,端口会被提前静默回收,不会抛出任何runtime.lastError,直接导致sendMessage立即返回undefined。

另外你的代码还存在一个隐藏的时序问题:onInstalled回调中调用browser.scripting.executeScript时没有加await,动态注入内容脚本的操作是异步执行的,极端情况下会出现消息发送时内容脚本还没注入完成的问题,只是这个问题在Chrome测试时恰好没触发。

修复方案

不要使用return true+宏任务内调用sendResponse的旧写法,这是Chrome早期的非标准实现,跨浏览器兼容性差。直接使用WebExtension标准规定的「监听器返回Promise」写法即可,Chrome和Firefox全版本兼容:

  1. 修改inject.js中的消息监听逻辑,去掉sendResponse参数和return true,直接返回Promise,将需要返回的内容放在Promise的resolve中:
//inject.js
(async () => {
    if (typeof browser === "undefined") {
        var browser = chrome;
    }

    browser.runtime.onMessage.addListener((msg, sender) => {
        console.log(msg);
        // 直接返回Promise,不需要手动调用sendResponse
        return new Promise((resolve) => {
            setTimeout(() => {
                let pageObject = {a:1};
                resolve(pageObject);
            },1000)
        })
    });
})();
  1. 修复动态注入的时序问题,给executeScript加上await,确保内容脚本注入完成后再执行后续逻辑:
browser.runtime.onInstalled.addListener(async () => {
    let manifest = browser.runtime.getManifest();
    for (const cs of manifest.content_scripts) {
        for (const tab of await browser.tabs.query({ url: cs.matches })) {
            // 等待注入完成
            await browser.scripting.executeScript({
                target: { tabId: tab.id },
                files: cs.js,
            });
        }
    }
});

补充说明:如果你的异步逻辑本身就是Promise-based的(比如fetch、浏览器API调用),直接return这个Promise即可,不需要额外套一层Promise包装。

内容的提问来源于stack exchange,提问作者sanjihan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:45:45