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

Safari扩展:弹出页向内容脚本发送消息异常的排查与修复

问题分析与修复方案

首先,你的消息传递异常主要是由异步时序不匹配以及Chrome和Safari扩展API的细微行为差异导致的,咱们拆解来看:

核心原因

  1. 异步执行顺序冲突
    你同时使用了await处理chrome.scripting.executeScript的Promise,又附加了回调函数。虽然await会等待脚本注入完成,但回调的执行时机并不一定和Promise的resolved完全同步,这就可能导致chrome.runtime.sendMessage在内容脚本的chrome.runtime.onMessage监听器还没完成注册时就发送了消息,自然收不到预期的响应。

  2. Safari扩展的API行为差异
    虽然Safari通过转换器兼容Chrome扩展API,但在内容脚本注入后的初始化时机上,可能和Chrome有细微差别。Chrome中脚本注入完成后通常会立即执行,但Safari可能需要极短的时间窗口完成脚本加载和监听器注册,直接发送消息容易错过时机。

  3. 循环闭包的潜在问题
    你的for循环中使用index变量,虽然用了let(块级作用域),但如果inboxTabId是在循环外定义的变量,可能会出现预期外的取值问题(不过这不是你当前消息异常的核心原因,但也是需要优化的点)。

修复步骤

1. 调整异步逻辑,确保脚本注入完成后再发消息

移除executeScript的回调函数,利用await的特性,在脚本注入完成后直接发送消息:

const buttonList = document.querySelectorAll(".app-button-inbox");
// 用for...of循环避免闭包问题
for (const button of buttonList) {
  button.addEventListener("click", async () => {
    try {
      console.log('点击触发,目标tab:', inboxTabId);
      // 等待脚本注入完成
      await chrome.scripting.executeScript({ 
        target: { tabId: inboxTabId, allFrames: true}, 
        files: ['contentScript.js'] 
      });
      // 脚本注入完成后再发送消息
      chrome.runtime.sendMessage({message: 'hello'});
    } catch (error) {
      console.log('注入脚本出错:', error);
    }
  });
}

2. 增加内容脚本的"就绪"确认(可选但更可靠)

为了彻底避免时序问题,可以让内容脚本加载完成后主动给Popup发送一个"就绪"信号,Popup收到后再发送目标消息:

修改contentScript.js:

console.log('内容脚本加载完成');
// 主动通知Popup脚本已就绪
chrome.runtime.sendMessage({type: 'scriptReady'});

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
  console.log('收到消息:', message, sender);
  // 如果需要响应,可以调用sendResponse
  sendResponse({status: 'received'});
});

修改Popup中的点击事件逻辑:

const buttonList = document.querySelectorAll(".app-button-inbox");
// 监听内容脚本的就绪信号
const waitForScriptReady = () => {
  return new Promise(resolve => {
    const listener = (message) => {
      if (message.type === 'scriptReady') {
        chrome.runtime.onMessage.removeListener(listener);
        resolve();
      }
    };
    chrome.runtime.onMessage.addListener(listener);
  });
};

for (const button of buttonList) {
  button.addEventListener("click", async () => {
    try {
      console.log('点击触发,目标tab:', inboxTabId);
      await chrome.scripting.executeScript({ 
        target: { tabId: inboxTabId, allFrames: true}, 
        files: ['contentScript.js'] 
      });
      // 等待内容脚本就绪
      await waitForScriptReady();
      // 发送目标消息
      const response = await chrome.runtime.sendMessage({message: 'hello'});
      console.log('收到响应:', response);
    } catch (error) {
      console.log('注入脚本出错:', error);
    }
  });
}

3. 检查Safari扩展的权限配置

确保你的manifest.json(或Safari扩展的配置文件)中已经声明了必要的权限:

{
  "permissions": ["scripting", "activeTab"],
  "host_permissions": ["<all_urls>"] // 根据你的需求调整匹配规则
}

额外提示

  • 在Safari中测试扩展时,建议开启开发菜单 -> 显示扩展构建器,可以更方便地查看扩展的日志和调试信息。
  • 如果仍然有问题,可以在内容脚本的onMessage监听器中加入console.log确认监听器是否被注册,同时在Popup中检查sendMessage的返回值(如果用await的话可以捕获响应)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 23:17:35