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

Firefox扩展向注入内容脚本发消息报错:Receiving end does not exist

这个问题我太熟悉了!你遇到的核心问题是内容脚本的注入是异步操作——当你调用browser.tabs.executeScript()后,浏览器需要时间去加载并执行content.js,而你紧接着就发送消息,这时候content.js里的消息监听器还没被注册,自然就会抛出“Receiving end does not exist”的错误。

下面给你两种最常用的解决方案,根据你的场景选就行:


方案1:等待脚本注入完成再发送消息(最简单直接)

Firefox的browser.tabs.executeScript()返回一个Promise,我们可以利用async/await或者.then()来等待脚本注入完成,确保监听器已经注册后再发送消息。

修改你的background.js:

// 用async/await的写法(推荐,更清晰)
browser.contextMenus.onClicked.addListener(async (info, tab) => {
  try {
    // 等待content.js完全注入并执行
    await browser.tabs.executeScript(tab.id, { file: "content.js" });
    
    // 现在可以安全地发送消息了
    const response = await browser.tabs.sendMessage(tab.id, { 
      action: "yourAction",
      data: "anyDataYouNeed"
    });
    
    console.log("收到content script的响应:", response);
  } catch (error) {
    console.error("注入脚本或发送消息失败:", error);
  }
});

如果不习惯async/await,也可以用Promise链式调用:

browser.contextMenus.onClicked.addListener((info, tab) => {
  browser.tabs.executeScript(tab.id, { file: "content.js" })
    .then(() => {
      return browser.tabs.sendMessage(tab.id, { action: "yourAction" });
    })
    .then(response => {
      console.log("响应:", response);
    })
    .catch(error => {
      console.error("出错了:", error);
    });
});

方案2:让content script主动“报到”(适合复杂初始化场景)

如果你的content.js需要做一些初始化操作(比如DOM准备、数据加载),不是注入完就立刻就绪,那可以让content脚本加载完成后主动给background发一条“我准备好了”的消息,background收到后再发送业务消息。

修改content.js:

// 脚本加载完成后,立刻通知background
browser.runtime.sendMessage({ action: "contentScriptReady" });

// 注册消息监听器
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === "yourAction") {
    // 处理你的业务逻辑
    console.log("收到background的消息:", message);
    sendResponse({ status: "success", result: "something" });
  }
});

修改background.js:

// 用Set存储已经就绪的标签页ID
const readyTabs = new Set();

// 监听content script的就绪消息
browser.runtime.onMessage.addListener((message, sender) => {
  if (message.action === "contentScriptReady" && sender.tab) {
    readyTabs.add(sender.tab.id);
    
    // 如果是右键触发的操作,可以在这里直接发送业务消息
    // 或者结合右键点击时的缓存逻辑,这里就不展开了
  }
});

browser.contextMenus.onClicked.addListener((info, tab) => {
  browser.tabs.executeScript(tab.id, { file: "content.js" })
    .then(() => {
      // 轮询检查标签页是否就绪(也可以用Promise封装更优雅的等待逻辑)
      const checkReadyInterval = setInterval(() => {
        if (readyTabs.has(tab.id)) {
          clearInterval(checkReadyInterval);
          // 发送业务消息
          browser.tabs.sendMessage(tab.id, { action: "yourAction" })
            .then(response => {
              console.log("响应:", response);
              readyTabs.delete(tab.id); // 用完移除,避免重复处理
            });
        }
      }, 50);
    })
    .catch(error => {
      console.error("出错了:", error);
    });
});

额外注意事项

  1. 权限配置:确保你的manifest.json里已经声明了必要的权限,比如activeTab(针对当前激活标签)或者<all_urls>(针对所有页面),还有contextMenus权限:
{
  "manifest_version": 2,
  "name": "Your Extension",
  "version": "1.0",
  "permissions": ["activeTab", "contextMenus"],
  "background": {
    "scripts": ["background.js"]
  },
  "content_scripts": []
}
  1. 避免重复注入:如果担心多次右键点击会重复注入脚本,可以在background里先检查标签页是否已经注入过脚本(比如用browser.tabs.executeScript的runAt参数,或者存储已注入标签页的ID)。
  2. Firefox兼容性:Firefox原生支持browser命名空间,如果要兼容Chrome,记得引入webextension-polyfill。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:53:19