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

FireFox扩展报错Uncaught(in Promise) Error:接收端不存在问题排查

扩展调试连接错误分析与修复

问题现象

在FireFox的about:debugging页面调试扩展时,多次触发Uncaught(in Promise) Error: Could not establish connection. Receiving end does not exist错误,多次报错后扩展可正常显示根证书信息。

相关代码

background.js

const securityInfo = await browser.webRequest.getSecurityInfo(
  details.requestId,
  { certificateChain: true }
);
if (securityInfo.state === "insecure" || securityInfo.state === "broken") {
  browser.runtime.sendMessage("no");
}
else if (!securityInfo.isUntrusted
  && securityInfo.certificates.length > 0) { //if Root Info exists
  //"subject" property from CertificateInfo Object
  const root = securityInfo.certificates[securityInfo.certificates.length - 1].issuer;
  //substring to only include the root CA name (comma seperated list)
  let rootCA = root.substring(3, root.indexOf(","));
  browser.runtime.sendMessage({ rootCA }); //send to popup.js
}
document.addEventListener("DOMContentLoaded", () => {
  // ... 

  var faviconImage = document.getElementById('faviconImage'); //Favicon (Logo)
  var websiteUrlElement = document.getElementById('websiteUrl'); //URL

  // get the information on the extension
  browser.tabs.query({ active: true, currentWindow: true }, function (tabs) {
    const url = tabs[0].url;
    const favicon = tabs[0].favIconUrl;
    websiteUrlElement.textContent = url;
    faviconImage.src = favicon;

    // Receive message from background.js for CA Info and update html
    browser.runtime.onMessage.addListener((request) => {
      if (request.rootCA) { // Check if root CA exists in the request
        caInfo = request.rootCA;
        document.getElementById("rootCAInfo").textContent = caInfo;
        checkCA(url, caInfo);
      }
      if (request.secure) {
        if (request.secure === "no") {
          siteStatusDivs.notMarked.style.display = "none";
          siteStatusDivs.unsecure.style.display = "block";
        }
      }
    });
});

错误原因

  1. 消息发送时机不匹配:background.js在web请求触发时立即发送消息,但此时popup可能尚未打开,或者popup的消息监听还未完成注册,导致没有接收端处理消息,触发连接错误。
  2. 消息格式不一致:background发送的"no"是字符串,而popup里判断的是request.secure,两者不匹配,同时无接收端时未捕获Promise错误,导致报错抛出。
  3. popup监听注册滞后:popup的onMessage监听被嵌套在tabs.query的回调中,进一步延迟了监听注册时间,增加了消息发送时无接收端的概率。

修复方案

方案1:捕获消息发送错误,统一消息格式

修改background.js,封装消息发送逻辑,捕获无接收端的错误,同时统一消息为对象格式,避免格式不匹配:

const securityInfo = await browser.webRequest.getSecurityInfo(
  details.requestId,
  { certificateChain: true }
);

// 封装消息发送,捕获错误避免抛出未处理Promise异常
const sendPopupMessage = (msg) => {
  browser.runtime.sendMessage(msg)
    .catch(err => {
      // 仅做日志记录,不抛出错误
      console.debug("消息发送失败(popup未打开):", err);
    });
};

if (securityInfo.state === "insecure" || securityInfo.state === "broken") {
  sendPopupMessage({ secure: "no" }); // 统一用对象格式
} else if (!securityInfo.isUntrusted && securityInfo.certificates.length > 0) {
  const root = securityInfo.certificates[securityInfo.certificates.length - 1].issuer;
  let rootCA = root.substring(3, root.indexOf(","));
  sendPopupMessage({ rootCA });
}

方案2:由popup主动请求数据(推荐)

这种方式从根源上避免时机问题,只有当popup打开后才发起数据请求:

调整background.js,添加请求监听与数据缓存

// 缓存每个标签的最新安全信息
const tabSecurityCache = {};

// 监听web请求,缓存安全信息
browser.webRequest.onHeadersReceived.addListener(async (details) => {
  const securityInfo = await browser.webRequest.getSecurityInfo(
    details.requestId,
    { certificateChain: true }
  );
  tabSecurityCache[details.tabId] = securityInfo;
}, { urls: ["<all_urls>"] }, ["blocking"]);

// 响应popup的请求
browser.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  if (request.action === "fetchRootCA") {
    const [currentTab] = await browser.tabs.query({ active: true, currentWindow: true });
    const securityInfo = tabSecurityCache[currentTab.id];
    
    if (!securityInfo) {
      sendResponse({ error: "无可用安全信息" });
      return;
    }

    if (securityInfo.state === "insecure" || securityInfo.state === "broken") {
      sendResponse({ secure: "no" });
    } else if (!securityInfo.isUntrusted && securityInfo.certificates.length > 0) {
      const root = securityInfo.certificates[securityInfo.certificates.length - 1].issuer;
      let rootCA = root.substring(3, root.indexOf(","));
      sendResponse({ rootCA });
    }
    return true; // 标记为异步响应
  }
});

修改popup.js,主动发起请求

document.addEventListener("DOMContentLoaded", () => {
  var faviconImage = document.getElementById('faviconImage');
  var websiteUrlElement = document.getElementById('websiteUrl');

  browser.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
    const url = tabs[0].url;
    const favicon = tabs[0].favIconUrl;
    websiteUrlElement.textContent = url;
    faviconImage.src = favicon;

    // 主动向background请求根CA信息
    try {
      const response = await browser.runtime.sendMessage({ action: "fetchRootCA" });
      if (response.rootCA) {
        const caInfo = response.rootCA;
        document.getElementById("rootCAInfo").textContent = caInfo;
        checkCA(url, caInfo);
      }
      if (response.secure === "no") {
        siteStatusDivs.notMarked.style.display = "none";
        siteStatusDivs.unsecure.style.display = "block";
      }
    } catch (err) {
      console.debug("请求根CA信息失败:", err);
    }
  });
});

方案3:提前注册popup的消息监听

将popup的onMessage监听移到tabs.query回调外,提前完成注册:

document.addEventListener("DOMContentLoaded", () => {
  var faviconImage = document.getElementById('faviconImage');
  var websiteUrlElement = document.getElementById('websiteUrl');
  let currentTabUrl = "";

  // 提前注册消息监听
  browser.runtime.onMessage.addListener((request) => {
    if (request.rootCA) {
      const caInfo = request.rootCA;
      document.getElementById("rootCAInfo").textContent = caInfo;
      checkCA(currentTabUrl, caInfo);
    }
    if (request.secure === "no") {
      siteStatusDivs.notMarked.style.display = "none";
      siteStatusDivs.unsecure.style.display = "block";
    }
  });

  browser.tabs.query({ active: true, currentWindow: true }, (tabs) => {
    const url = tabs[0].url;
    const favicon = tabs[0].favIconUrl;
    websiteUrlElement.textContent = url;
    faviconImage.src = favicon;
    currentTabUrl = url;
  });
});

内容的提问来源于stack exchange,提问作者Armina Parvaresh Rizi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:37:08