What's that all about?
Here is what was happening before fission. When navigating between two WebSites, you were staying in the same content process:
The current tab's content process
+---------------------------------------------------------+
| DocShell |
| +-----------------------+ +-------------------------+ |
| | Document: mozilla.org | | Document: wikipedia.org | |
| | +------+ | | +------+ | |
| | |Window| | | |Window| |...|
| | +------+ | | +------+ | |
| +-----------------------+ +-------------------------+ |
+---------------------------------------------------------+
But now, with fission, when navigating, we may change to another process, if we load another origin:
mozilla.org's content process wikipedia.org's content process
+-----------------------------+ +-------------------------------+
| DocShell | | DocShell |
| +-----------------------+ | | +-------------------------+ |
| | Document: mozilla.org | | | | Document: wikipedia.org | |
| | +------+ | | | | +------+ | |
| | |Window| |...| | | |Window| |...|
| | +------+ | | | | +------+ | |
| +-----------------------+ | | +-------------------------+ |
+-----------------------------+ +-------------------------------+
=> The issue here is that we have two DocShell instances. One per process. But many attributes, listener, code is reset, while we navigate. While DocShell was really meant to survive and track documents over navigations!
One key solution is to introduce a new class "BrowsingContext" which would be the same across navigations.
+---------------------------------------------------------------------+
|BrowsingContext for the tab |
| mozilla.org's content process wikipedia.org's content process |
| +-----------------------------+ +-------------------------------+ |
| | DocShell | | DocShell | |
| | +-----------------------+ | | +-------------------------+ | |
| | | Document: mozilla.org | | | | Document: wikipedia.org | | |
| | | +------+ | | | | +------+ | | |
| | | |Window| |...| | | |Window| |...| |
| | | +------+ | | | | +------+ | | |
| | +-----------------------+ | | +-------------------------+ | |
| +-----------------------------+ +-------------------------------+ |
+---------------------------------------------------------------------+
=> This is a simplified view, but explains the goal of this class: "A cross process DocShell". BrowsingContext is actually specified in Web Standards: "A browsing context is an environment in which Document objects are presented to the user." (From W3C) https://html.spec.whatwg.org/multipage/browsers.html#windows
Here is a slightly better representation of the BrowsingContext. There is actually multiple instances of the exact same BrowsingContext, running in all the processes. They are all synced and should have the same state.
+----------------------------------------------------------------------------+
|BrowsingContext for the tab, in the Parent Process |
| |
| +-----------------------------------+ +-----------------------------------+|
| | mozilla.org's content process | | wikipedia.org's content process ||
| | | | ||
| | BrowsingContext for this docshell,| | BrowsingContext for this docshell,||
| | in the content process | | in the content process ||
| | +-----------------------------+ | | +-------------------------------+ ||
| | | DocShell | | | | DocShell | ||
| | | +-----------------------+ | | | | +-------------------------+ | ||
| | | | Document: mozilla.org | | | | | | Document: wikipedia.org | | ||
| | | | +------+ | | | | | | +------+ | | ||
| | | | |Window| |...| | | | | |Window| |...| ||
| | | | +------+ | | | | | | +------+ | | ||
| | | +-----------------------+ | | | | +-------------------------+ | ||
| | +-----------------------------+ | | +-------------------------------+ ||
| +-----------------------------------+ +-----------------------------------+|
+----------------------------------------------------------------------------+
Note that the BrowsingContext in the parent process implements more attributes and functions. It implements CanonicalBrowsingContext while the BrowsingContext in the content process only implements BrowsingContext
BrowsingContext exposes a new class called WindowGlobal. This other class isn't specific in Web Standards. It is typically accessible via BrowsingContext.currentWindowGlobal attribute, from the Parent Process. WindowGlobal is an helper class to expose "nsGlobalWindowInner" class in all the processes. "nsGlobalWindowInner" is described as "Global Object for Scripting". It relates to the window object for a given DOM document. In the parent process, you will have access to the WindowGlobalParent interface, while, in the content processes, you will have the WindowGlobalChild interface. These objects help keeping track of each document's window object, without requiring to have access to the real window object. Instead you only have a few metadata about it and a function function to interact with it.
+-----------------------------------------------------------------+
| +--------------------------------------------------------------+|
| | mozilla.org's content process ||
| | ||
| | BrowsingContext, in the content process ||
| | ||
| | +-----------------------------------------------------------+||
| | | DocShell |||
| | | +--------------------------+ +--------------------------+ |||
| | | | Document: mozilla.org/foo| | Document: mozilla.org/bar| |||
| | | | +------+ | | +------+ | |||
| | | | |Window| | | |Window| | |||
| | | | +------+ | | +------+ | |||
| | | +----^---------------------+ +----^---------------------+ |||
| | +------|----------------------------|-----------------------+||
| | | | ||
| | +--------------+ +--------------+ ||
| | | WindowGlobal | | WindowGlobal | ||
| | +--------------+ +--------------+ ||
| +--------|----------------------------|------------------------+|
| | | |
| +--------------+ +--------------+ |
| | WindowGlobal | | WindowGlobal | |
| +--------------+ +--------------+ |
| BrowsingContext, in the Parent Process |
+-----------------------------------------------------------------+
If we simplify, and only refer to the new BrowsingContext classes, it is simplier:
+-----------------------------------------------------+
| CanonicalBrowsingContext, in the Parent Process |
| +-------------------------------------------------+ |
| | BrowsingContext, in the content process | |
| | | |
| | +--------------------+ +--------------------+ | |
| | | WindowGlobalChild | | WindowGlobalChild | | |
| | | | | | | |
| | |for: mozilla.org/foo| |for: mozilla.org/bar| | |
| | +--------------------+ +--------------------+ | |
| | ^ ^ | |
| +----------|------------------------|-------------+ |
| | | |
| +------------------+ +------------------+ |
| |WindowGlobalParent| |WindowGlobalParent| |
| +------------------+ +------------------+ |
+-----------------------------------------------------+
+-----------------------------------------------------------+
| BrowsingContext id=1 |
| |
| +--------------------------+ +--------------------------+ |
| | WindowGlobal | | WindowGlobal | |
| | | | | |
| |outerWindowId = 100 | |outerWindowId = 100 | |
| |innerWindowID = 1000 | |innerWindowID = 1001 | |
| |osPid = 500 | |osPid = 500 | |
| | | | | |
| |Document = mozilla.org/foo| |Document = mozilla.org/bar| |
| +--------------------------+ +--------------------------+ |
+-----------------------------------------------------------+
=> Only the Inner Window ID changes. Same BrowsingContext. Same process. Same outer window.
+-----------------------------------------------------------+
| BrowsingContext id=1 |
| |
| +--------------------------+ +--------------------------+ |
| | WindowGlobal | | WindowGlobal | |
| | | | | |
| |outerWindowId = 100 | |outerWindowId = 101 | |
| |innerWindowID = 1000 | |innerWindowID = 1001 | |
| |osPid = 500 | |osPid = 501 | |
| | | | | |
| |Document = mozilla.org | |Document = wikipedia.org | |
| +--------------------------+ +--------------------------+ |
+-----------------------------------------------------------+
=> This time the process changes as well as the Inner Window ID. But we keep the same BrowsingContext.
Note that, for now, the process allocation code is temporary. For now it ignore any process count limit and uses one process per origin. In the future, we may tweak that and, in some case, share the same process between two distinct origins. If that happens, this will be similar to the previous case. Only the Inner Window ID will change.
+------------------------------+ +------------------------------+
| BrowsingContext id=1 | | BrowsingContext id=2 |
| | | |
| +--------------------------+ | | +--------------------------+ |
| | WindowGlobal | | | | WindowGlobal | |
| | | | | | | |
| |outerWindowId = 100 | | | |outerWindowId = 101 | |
| |innerWindowID = 1000 | | | |innerWindowID = 1001 | |
| |osPid = 500 | | | |osPid = -1 (=parent) | |
| | | | | | | |
| |Document = mozilla.org/foo| | | |Document = about:robots | |
| +--------------------------+ | | +--------------------------+ |
+------------------------------+ +------------------------------+
=> Everything changes, especially the BrowsingContext.
Note that, we might in the future introduce yet another abstraction on top of BrowsingContext. A "Session" class, which would be stable over any kind of navigation, even these ones.
+-----------------------------------------------------------+
| BrowsingContext id=1 |
| |
| +--------------------------+ +--------------------------+ |
| | WindowGlobal | | WindowGlobal | |
| | | | | |
| |outerWindowId = 100 | |outerWindowId = 100 | |
| |innerWindowID = 1000 | |innerWindowID = 1001 | |
| |osPid = 500 | |osPid = 500 | |
| | | | | |
| |Document = mozilla.org/foo| |Document = mozilla.org/bar| |
| +--------------------------+ +--------------------------+ |
+-----------------------------------------------------------+
=> Only the Inner Window ID changes. Same BrowsingContext. Same process. Same outer window.
+-----------------------------------------------------------+
| BrowsingContext id=1 |
| |
| +--------------------------+ +--------------------------+ |
| | WindowGlobal | | WindowGlobal | |
| | | | | |
| |outerWindowId = 100 | |outerWindowId = 101 | |
| |innerWindowID = 1000 | |innerWindowID = 1001 | |
| |osPid = 500 | |osPid = 501 | |
| | | | | |
| |Document = mozilla.org | |Document = wikipedia.org | |
| +--------------------------+ +--------------------------+ |
+-----------------------------------------------------------+
=> This time the process changes as well as the Inner Window ID. But we keep the same BrowsingContext.
Note that, for now, the process allocation code is temporary. For now it ignore any process count limit and uses one process per origin. In the future, we may tweak that and, in some case, share the same process between two distinct origins. If that happens, this will be similar to the previous case. Only the Inner Window ID will change.
+------------------------------+ +------------------------------+
| BrowsingContext id=1 | | BrowsingContext id=2 |
| | | |
| +--------------------------+ | | +--------------------------+ |
| | WindowGlobal | | | | WindowGlobal | |
| | | | | | | |
| |outerWindowId = 100 | | | |outerWindowId = 101 | |
| |innerWindowID = 1000 | | | |innerWindowID = 1001 | |
| |osPid = 500 | | | |osPid = -1 (=parent) | |
| | | | | | | |
| |Document = mozilla.org/foo| | | |Document = about:robots | |
| +--------------------------+ | | +--------------------------+ |
+------------------------------+ +------------------------------+
=> Everything changes, especially the BrowsingContext.
Note that, we might in the future introduce yet another abstraction on top of BrowsingContext. A "Session" class, which would be stable over any kind of navigation, even these ones.
It is useful to take a minute to see at what happens with nested content processes. When a tab document bundles an iframe, which is loaded in another origin and process.
Let take this example with three nested documents, each loaded in its own process:
a.org: +----------------+
<iframe src="b.org" /> | a.org: |
| +-----------+ |
b.org: | | b.org: | |
<iframe src="c.org" /> | | +------+ | |
| | |c.org:| | |
c.org: | | | Foo | | |
Foo | | +------+ | |
| +-----------+ |
+----------------+
Now, let's look at all the available instances of BrowsingContext and WindowGlobal, in all these processes.
+------------------------------+ +------------------------------+ +------------------------------+
| BrowsingContext id=1 | | BrowsingContext id=2 | | BrowsingContext id=3 |
| | | | | |
| getChildren()-----------------> | getChildren()-----------------> | getChildren() |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
| | WindowGlobal | | | | WindowGlobal | | | | WindowGlobal | |
| | | | | | | | | | | |
| |isInProcess = false | | | |isIsProcess = false | | | |isIsProcess = false | |
| |osPid = 100 | | | |osPid = 101 | | | |osPid = 102 | |
| | | | | | | | | | | |
| |Document = a.org | | | |Document = b.org | | | |Document = c.org | |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
+------------------------------+ +------------------------------+ +------------------------------+
+------------------------------+ +------------------------------+ +------------------------------+
| BrowsingContext id=1 | | BrowsingContext id=2 | | BrowsingContext id=3 |
| | | | | |
| getChildren()-----------------> | getChildren()-----------------> | getChildren() |
| docShell = a.org's docshell | | docShell = null | | docShell = null |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
| | WindowGlobal | | | | WindowGlobal | | | | WindowGlobal | |
| | | | | | | | | | | |
| |isInProcess = true | | | |isIsProcess = false | | | |isIsProcess = false | |
| | | | | | | | | | | |
| |Document = a.org | | | |Document = b.org | | | |Document = c.org | |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
+------------------------------+ +------------------------------+ +------------------------------+
+------------------------------+ +------------------------------+ +------------------------------+
| BrowsingContext id=1 | | BrowsingContext id=2 | | BrowsingContext id=3 |
| | | | | |
| getChildren()-----------------> | getChildren()-----------------> | getChildren() |
| docShell = null | | docShell = b.org's docshell | | docShell = null |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
| | WindowGlobal | | | | WindowGlobal | | | | WindowGlobal | |
| | | | | | | | | | | |
| |isInProcess = false | | | |isIsProcess = true | | | |isIsProcess = false | |
| | | | | | | | | | | |
| |Document = a.org | | | |Document = b.org | | | |Document = c.org | |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
+------------------------------+ +------------------------------+ +------------------------------+
+------------------------------+ +------------------------------+ +------------------------------+
| BrowsingContext id=1 | | BrowsingContext id=2 | | BrowsingContext id=3 |
| | | | | |
| getChildren()-----------------> | getChildren()-----------------> | getChildren() |
| docShell = null | | docShell = null | | docShell = c.org's docshell |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
| | WindowGlobal | | | | WindowGlobal | | | | WindowGlobal | |
| | | | | | | | | | | |
| |isInProcess = false | | | |isIsProcess = false | | | |isIsProcess = true | |
| | | | | | | | | | | |
| |Document = a.org | | | |Document = b.org | | | |Document = c.org | |
| +--------------------------+ | | +--------------------------+ | | +--------------------------+ |
+------------------------------+ +------------------------------+ +------------------------------+
BrowsingContext also exposes a third class: JSWindowActor. Similarly, the interface will be different based on the process:
There is some extensive documentation about this particular API over here But long story short, these classes allow interacting with a given WindowGlobal, that, from any process. Parent and content processes. One important fact to note is that the lifecycle of JSWindowActor follows WindowGlobal one. So, every new WindowGlobal will spawn its own dedicated set of JSWindowActor pair (one in the parent process, one in the content process).
Parent Process | Content Process for mozilla.org
| +----------+
+--------------------------------+ | +-------------------------+ | DocShell |
| CanonicalBrowsingContext | | | BrowsingContext | | |
| | | | | |.domWindow|---+
| .id | | | .docShell -----------------------------> +----------+ |
| .docShell = null | | | .window | v
| .window = null | <-----------+ | | .id | +---------------------+
| .embedderElement = | | | | .embedderElement = null | | Window |
| <browser src="mozilla.org"/> | | | +-------------------------+ | |
| .loadURI() | | | ^ +----------|.getWindowGlobalChild|
| .currentWindowGlobal:| | | | | | |.document-+ |
+----------------------|---------+ | | | | |.docShell | |
+--------------------+ | | | v +----------|----------+
| | | +----------------------+ | +-----------------+ v ^
+------------------+<-----------------------------+ | | | JSWindowActorChild | | |WindowGlobalChild| +------------+ |
|WindowGlobalParent| +----------------------+ | | | | | | | | | Document | |
| | | JSWindowActorParent | | | | | .browsingContext -------+---|.browsingContext | | | |
| .isInProcess | | | | | | | .manager ------------------>| | |.defaultView|----+
| .browsingContext | | .manager --------------+ | | | | |.isInProcess | +------------+
| .innerWindowId | | .browsingContext --------+ | | | |.innerWindowId |
| .outerWindowId | | | | | | |.outerWindowId | ^
| .osPid | | .sendAsyncMessage()------------> .receiveMessage() | | | |
| .getActor()----------->| .sendQuery() | | | | |.getActor() | |
+------------------+ | | | | .sendAsyncMessage() | | | |
| .receiveMessage()<---------------.sendQuery() | +-----------------+ |
+----------------------+ | | | |
| | .docShell | |
| | .document ---------------------------------------------+
| | .contentWindow |
| +----------------------+