3

I'm trying to enhance an existing Firefox extension which relies on nsIContentPolicy to detect and abort certain network loads (in order to block the resulting UI action, i.e. tab navigation). Then handle loading that resource internally. Under rare circumstances, only after handling the load, it turns out we shouldn't have interrupted the load at all, so we flag it to be ignored and re-start it.

Under e10s/multi-process, that means the parent (where the content policy is running) must send a message to the child (handling the UI for the content) to restart the load. Today, that's done by:

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;
  }
};

Which is crazy complex, because e10s is crazy complex. But it works; it generates some object in the parent, upon which I can call .sendAsyncMessage(), and then the addMessageListener() handler in my frame/child script receives it, and does what it needs to do.


I'd like to switch from nsIContentPolicy to http-on-modify-request as it presents more information for making a better determination (block and handle this load?) earlier. Inside that observer I can do:

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

Which gives me an object which has a .messageManager which is some kind of message manager, and which has a .sendAsyncMessage() method. But when I use that .sendAsyncMessage(), the message disappears, never to be observed by the child.


Context: https://github.com/greasemonkey/greasemonkey/issues/2280

jerone
  • 16,206
  • 4
  • 39
  • 57
arantius
  • 1,715
  • 1
  • 17
  • 28

2 Answers2

2

This should work in principle, although the docshell tree traversal may do different things in e10s and non-e10s, so you have to be careful there. In e10s rootTreeItem -> nsIContentFrameMessageManager should give you the MM equivalent to a frame script and topFrameElement.frameLoader.messageManager should give you the <browser>'s MM, which pretty much is the parent side counterpart to it.

Potential sources of confusion:

  • e10s on vs. off
  • process MM vs. frame MM hierarchy
  • listening in the wrong frame for the message (registering in all frames might help for debugging purposes)
the8472
  • 40,999
  • 5
  • 70
  • 122
  • I've gotten the `topFrameElement.frameLoader.messageManager`. I've added a message listener to a global, parent, and browser frame. When I `.sendAsyncMessage()`, nobody hears it. Is there another frame to listen in? – arantius Feb 16 '16 at 14:58
  • I'm not sure if that's just badly worded, but attaching a listener to the global/parent/browser MM will not get you messages sent from the parent. They will only be received on the child side. Afaik the only other thing that can swallow messages are tab unloads and tab swapping. – the8472 Feb 16 '16 at 16:41
  • *facepalm* Ok, it totally works. It's just the whole e10s model not fitting in my head. All the things I've known how to do for years, now some only work in the parent, some only in the child, and there's so many kinds of message managers and directions for messages to be passed. Indeed, listening in the right place (the frame) makes this work. – arantius Feb 19 '16 at 14:33
-1

This is the function I use to find the content message manager:

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

}

So maybe get the content window that triggered that request, and then use this function.

Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • There is no content window available from an HTTP observer AFAIK. Especially with e10s on, even if I could force my way to one, it wouldn't exist (it would be in the child process). – arantius Feb 16 '16 at 14:59
  • Aren't you able to get the related contentWindow from the observer? I have done it in past – Noitidart Feb 16 '16 at 19:47