【问题标题】:Firefox extension, identification of contextFirefox 扩展,上下文识别
【发布时间】:2025-12-26 14:05:12
【问题描述】:

我已经构建了一个 AddIn,一个国际象棋游戏可视化工具,它在桌面浏览器 Firefox、Chrome、Opera、Safari 和 IE 上运行了多年。插件在从上下文菜单调用时运行,并解析当前页面中的选定文本。所选文本应为Chess Notation Format,可以是 FEN 或 PGN。
这是第二次在 FF 版本更改时插件与 Firefox 不兼容,我必须进行架构更改。新的 firefox 架构是为了与 Chrome 兼容,但它只是部分兼容,所以我不能完全重用 chrome 的代码。我还使用了来自 Communicate data from popup to content script injected by popup with executeScript()Communicate data from popup to content script injected by popup with executeScript()

这是我的问题:

  1. 看background.js的代码,browser.runtime.sendMessage在pop.js中调用browser.runtime.onMessage.addListener之前被调用。在完全创建由 browser.windows.create 打开的窗口、所有脚本结束执行并完全加载文档后,我不明白如何从 background.js 发送消息。我也不能在调用document.addEventListener('DOMContentLoaded' 时执行此操作,当我调用 browser.runtime.onMessage.addListener 时,它会生成错误Error: sendRemoveListener on closed conduit b212f9bfb4f0394efe56168a583c91346caf2d00@temporary-addon.412316861125 并且可能根本没有执行。也许有我可以处理的特殊事件,我没有看到太多的文档。在 Chrome 中,这种方法大体相似,但效果很好。
  2. 在一些不可用的情况下,我仍然可以捕捉到从 background.js 发送的消息:如果我打开了两个窗口,但对于调试目的还是不错的。问题是在 pop.js 的处理程序内部,document.innerHTML 是未定义的,无论我尝试做什么。我无法处理来自扩展 api 处理程序的窗口内容。我认为文档已完全加载,因为它由两个打开的窗口中的第一个处理。

注意,我可以通过调用 URL 参数将选择文本发送到新打开的弹出窗口。它有效,但我认为这是一种解决方法。我在 Opera 和 Safari 上使用它,它也适用于 Firefox,但我正在考虑尽可能摆脱它。

有场景,简化:
背景.js:

{//Firefox create context menus
   var id      = chrome.contextMenus.create({"title": "Chess game", "contexts":["selection", "page"]});
   var childId = chrome.contextMenus.create
                    ({
                       "title": "Play Small","contexts":["selection", "page"],
                       "parentId": id,
                       "onclick": (info, tab) => { playBoard   (info, tab, "mini18"); }
                    });
}


function onCreated(windowInfo, request)
{
   browser.windows.get(windowInfo.id).then
   (
      (wnd) => { browser.runtime.sendMessage(request); }
   );
}

function playBoard (info, tab, imagePath)
{
   let creating = browser.windows.create({ type:"detached_panel", url:"pop.html", width:250, height:100 });
   creating.then
   (
      (inf) =>
      {onCreated
         (
            inf,
            {
               chessObject:
               {
                  gametype : "PGN_OR_FEN_board",
                  content : selection,
                  imgPath : info.selectionText
               }
            }
         )
      }
   );
}

pop.js:

browser.runtime.onMessage.addListener
(
   (request) =>
   {
      console.log("popup chrome listen to game: " + request.chessObject.content);
      console.log("popup chrome listen to game: document inner html: " + document.innerHTML);
   }
);

pop.html:

<!DOCTYPE html>
<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
   <title>hello world</title>
   <script language="javascript" src="pop.js"></script>
</head>
<body>
   hello popup
</body>
</html>

manifest.json:

{
  "manifest_version": 2,
  "name": "Roatta Waaayyyy!!!",
  "version": "1.0.0.4",

  "description": "Chess AddIn for Firefox",

  "icons": { "32": "icons/roatta_32_32.jpg" },
  "permissions": [ "contextMenus" ],
  "background": { "scripts": ["background.js"] }
}

For reference here is the full source code on GIT,我现在尝试实现的是 Firefox65,它旨在取代以前的 Firefox。基本版本是用于 Chrome 的版本,最初是用于 Firefox 的版本。

【问题讨论】:

  • 最简单的解决办法大概是把通讯方向反过来,直接在pop.js中使用sendMessage从后台脚本中获取数据。
  • @wOxxOm 谢谢,我会试试的。
  • @wOxxOm 反向模式效果很好,作为答案发布。谢谢

标签: javascript firefox-addon firefox-addon-webextensions


【解决方案1】:

正如@wOxxOm 所建议的那样,我应用了反向消息模式,并且它有效。
现在 background.js 注册一个监听器并等待被调用,然后它会移除监听器:

{//Firefox
   console.log("Creating context menus");
   var id      = chrome.contextMenus.create({"title": "Chess game", "contexts":["selection", "page"]});
   var childId = chrome.contextMenus.create
                    ({
                       "title": "Play Small","contexts":["selection", "page"],
                       "parentId": id,
                       "onclick": (info, tab) => { playBoard   (info, tab, "mini18"); }
                    });
}

function playBoard (info, tab, imagePath)
{
   var createData =
   {
      type  : "detached_panel",
      url   : "pop.html",
      width : 250,
      height: 100

   };

   let requestData =
      {
         chessObject:
         {
            gametype : "PGN_OR_FEN_board",
            content  :  info.selectionText,
            imgPath  : imagePath
         }
      };
   try
   {
      console.log ("Play Board: " + imagePath);
      console.log ("Selection: " + info.selectionText);

      let creating = browser.windows.create(createData);
      
      function onGameDataExchange(request)
      {
          browser.runtime.onMessage.removeListener(onGameDataExchange);
          return Promise.resolve(requestData);
      }

      browser.runtime.onMessage.addListener (onGameDataExchange);

      console.log ("opened pop.html");

   }
   catch (err)
   {
      console.log ("Error: main background playBoard() " + err);
   }
}

pop.js 是请求消息的那个:

document.addEventListener('DOMContentLoaded',
   function (event)
   {
      browser.runtime.sendMessage (  { chessObject: { gametype : "PGN_OR_FEN_board" } }  )
                .then
                (
                   (request) =>
                   {
                       console.log("on DOMContentLoaded message: " + request.chessObject.gametype);
                       console.log("on DOMContentLoaded message: " + request.chessObject.content);
                       console.log("on DOMContentLoaded message: " + request.chessObject.imgPath);
                   }
                );
      console.log("Popup DOMContentLoaded send message end");

   }
);

【讨论】: