Firefox 扩展:响应 parent 中观察到的 http-on-modify-request,并向负责加载的 child 框架发送消息

Firefox Extension: responding to an http-on-modify-request observed in the parent with a message to the child frame responsible for the load

我正在尝试增强现有的 Firefox 扩展,它依赖于 nsIContentPolicy 来检测和中止某些网络负载(以阻止由此产生的 UI 操作,即标签导航)。然后在内部处理加载该资源。在极少数情况下,只有在处理完负载后,才发现我们根本不应该中断负载,因此我们将其标记为忽略并 re-start 它。

在 e10s/multi-process 下,这意味着 parent(内容策略为 运行)必须向 child(处理 UI 为内容)重新启动负载。今天,完成者:

function findMessageManager(aContext) {
  // With e10s off, context is a <browser> with a direct reference to
  // the docshell loaded therein.
  var docShell = aContext && aContext.docShell;

  if (!docShell) {
    // But with e10s on, context is a content window and we have to work hard
    // to find the docshell, from which we can find the message manager.
    docShell = aContext
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebNavigation)
        .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem;
  }

  try {
    return docShell
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIContentFrameMessageManager);
  } catch (e) {
    return null;
  }
};

这太复杂了,因为 e10s 太复杂了。但它有效;它在 parent 中生成一些 object,我可以调用 .sendAsyncMessage(),然后我的 frame/child 脚本中的 addMessageListener() 处理程序接收它,并执行什么它需要做。


我想从 nsIContentPolicy 切换到 http-on-modify-request,因为它提供了更多信息以便更早地做出更好的决定(阻止并处理此负载?)。在那个观察者里面我可以做:

var browser = httpChannel
    .notificationCallbacks.getInterface(Ci.nsILoadContext)
    .topFrameElement;

它给了我一个 object,它有一个 .messageManager,它是 某种 的消息管理器,它有一个 .sendAsyncMessage() 方法.但是当我使用 .sendAsyncMessage() 时,消息消失了,child.

永远不会观察到

上下文:https://github.com/greasemonkey/greasemonkey/issues/2280

这在原则上应该可行,尽管 docshell 树遍历在 e10s 和非 e10s 中可能做不同的事情,所以你必须小心。在 e10s rootTreeItem -> nsIContentFrameMessageManager 应该给你相当于一个框架脚本的 MM 而 topFrameElement.frameLoader.messageManager 应该给你 <browser> 的 MM,这几乎是父端与之对应。

潜在的混淆来源:

  • e10s 开启与关闭
  • 进程 MM 与框架 MM 层次结构
  • 在错误的帧中收听消息(在所有帧中注册可能有助于调试)

这是我用来查找内容消息管理器的功能:

function contentMMFromContentWindow_Method2(aContentWindow) {
    if (!gCFMM) {
        gCFMM = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDocShell)
                              .QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIContentFrameMessageManager);
    }
    return gCFMM;

}

所以也许获取触发该请求的内容window,然后使用此功能。