Nested Content Processes

From MozillaWiki
Jump to: navigation, search

B2G is multi-process, but the hierarchy of processes is depth two: Only the root process can have child processes.

This creates a problem for us on B2G with the browser app. We'd like the browser app to run outside the root process, but we'd also like the browser app's tabs to run outside the browser app process (so that when a tab crashes e.g. due to OOM, it doesn't take down the whole browser). Right now we have to compromise. We can either run the browser app in the root process and run the browser tabs in their own processes, or we can run the browser app outside the root process and run the browser tabs inside the same process as the browser app. We chose the former.

One way this fix this is we could allow child processes to have their own children. But this would be a lot of work. One problem is that because an arbitrary-depth hierarchy of processes doesn't exist right now, I expect that most of our APIs won't work properly if we have nested content processes. Many of them explicitly don't even try to work correctly under these circumstances. We'd have to fix up all of these APIs and then get the graphics stack to work with nested content processes.

There is an alternative and perhaps simpler way of accomplishing our goal of moving the browser app out of the main process. It turns out that a child-of-a-child doesn't need to talk to its parent very often. Most of what it needs (e.g. network access, permission checks, hardware access) comes from the main process. So we could made our browser tab inside the browser process a child of the main process. We'd then fix up the few cases when the process wants to talk to its now-sibling.

This page describes how nested content processes is implemented.

Terminology

Chrome Process, Root Process or B2G Process
The process with highest privilege which can access virtually all resources on the device. Many security and privacy sensitive API could only be performed by the chrome process. Content processes have to delegate the action to the chrome process through IPC.
Content Process or Child Process
Processes with lower privilege. Almost all web apps and browser tabs run in this kind of process thus the name content process.
Grand Child Process
The content processes whose parents are logically other content processes. In the OS's point of view, every content processes are still siblings.
IPC Protocol
The protocol defines IPC messages sent between two threads or two processes. In code, a protocol class is usually named P<something>, see also IPC Protocols.
Bridge
Toplevel protocol usually connects the chrome process with content processes. A bridge protocol connects two content processes.

Implementation

Process Creation

Nested Process

PBrowser are now managed by either PContent or PContentBridge so TabParents could be created in content process now.

Here is the steps for setting up nested OOP frame:

  1. Child process A ask chrome process to create a grand child process via PContent::CreateChildProcess()
  2. Child process A ask chrome process to bridge to grand child process B via PContent::BridgeToChildProcess()
  3. Child process A creates new PBrowser over PContentBridge::PBrowser

GFX

We will talk about how does layer composition work for nested processes.

Terms
RenderFrameParent
Each TabParent has a RenderFrameParent which maintains the render state of the remote tab.
nsSubDocumentFrame
A frame is a layout unit in the frame construction phase. A nsSubDocumentFrame usually corresponding to a <iframe> and alike.
CompositorChild
The child counterpart of the PCompositor protocol. Each process could use a CompositorChild to communicate with the compositing process.
nsDisplayRemote
A DisplayItem is a command in the DisplayList that describes how to create a layer and draw into it. nsDisplayRemote knows how to build a RefLayer for remote frames.
RefLayer
A RefLayer is a anchor point for remote layer trees. It has a unique id for looking up the actual layer tree.
Flow
  1. On Tab creation
    1. In TabChild::InitRenderingState() it creates the PRenderFrame using SendPRenderFrameConstructor and get a mLayersId back.
      • If the TabParent/RenderFrameParent lives in the b2g process, use CompositorParent::AllocateLayerTreeId() to allocate layerId and register layerId via in process CompositorChild
      • otherwise use ContentChild::GetSingleton()->SendAllocateLayerTreeId(aId) to allocate layerId and register layerId via OOP CompositorChild
    2. Use the mLayersId to create a shadow layer manager. The shadow layer manager then could used to create layers.
  1. On composition
    1. On frame construction we create a nsSubDocumentFrame
    2. If this nsSubDocumentFrame has a RenderFrameParent, then use it to create a nsDisplayRemote display item
    3. On layer tree construction, nsDisplayRemote will create a RefLayer and bind the mLayersId of the RenderFrameParent to it.
    4. When compositing the AsyncCompositionManager will WalkTheTree and resolve RefLayers using the mLayersId
  1. On Tab destruction
    1. nsFrameLoader::DestroyComplete() will be called in TabParent::ActorDestroy
    2. Next, TabParent::Destroy() will be called in nsFrameLoader::DestroyComplete() and then TabParent::DestroyInternal() will be triggered
    3. After finishing TabParent::DestroyInternal(), ContentParent::NotifyTabDestroying will be called.
      • If TabParent is in chrome process, then its Manager() will be converted from nsIContentParent into ContentParent and pass its ContentParentId to ContentParent::NotifyTabDestroying
      • Otherwise, if TabParent is in content process, then its Manager() will be converted from nsIContentParent into ContentBridgeParent and pass its ContentBridgeParentId to ContentParent::NotifyTabDestroying.
    4. TabParent::Recv__delete__() will be called in case PBrowser::Msg___delete____ID of PBrowserParent::OnMessageReceived after PBrowserParent::SendDestroy() is called in TabParent::DestroyInternal()
      • If TabParent is in chrome process, then its Manager() will be converted from nsIContentParent into ContentParent and pass its ContentParentId to ContentParent::DeallocateTabId. Next, ContentParent::NotifyTabDestroyed will be called.
      • Otherwise, if TabParent is in content process
      1. ContentBridgeParent::NotifyTabDestroyed() will be called first to check whether or not we should close the PContentBridge Channel. If the closed tab is the last one in its process, then the channel will be closed.
      2. Next, its Manager() will be converted from nsIContentParent into ContentBridgeParent and pass its ContentBridgeParentId to ContentParent::DeallocateTabId. ContentParent::DeallocateTabId here will fire a IPC calling, ContentChild::SendDeallocateTabId, to request its parent process to deallocate the closed tab
    5. After completing TabParent::Recv__delete__(), the closed tab will be removed from array mManagedPBrowserParent returned from ManagedPBrowserParent() in case PBrowserMsgStart of PContentBridgeParent::RemoveManagee
    6. Finally, ContentBridgeParent::DeallocPBrowserParent will be fired

Security

[Needs security review]

Process based sandbox

The sandbox model is built on the process boundary. Generally the chrome process shouldn't trust the information coming from a content processes. The content processes also shouldn't trust the information coming from other content processes.

How do we check the integrity? For the messages from child to chrome we could

  1. Check if the process is the app as it claimed to be by comparing ContentParent::OwnOrContainingAppId() bug 1020157
  2. Check if the is indeed created and managed by the PContentBridge. When the content process creates a new TabParent/TabChild pair between it and the grand content process, it should send a notification to the chrome process. The chrome process will note that and check if the PBrowserId is indeed owned by the content process bug 1020172

How to check nested TabParent?

When a content process wants to create a new Tab, it should first ask the chrome process to allocate a PBrowserId and passing the TabContext to chrome. Chrome process then checks if the process could create the new Tab with the TabContext. If the check passed then the chrome process will allocate a new PBrowerId and record the (PBrowserId, TabContext) pair in the ContentParent. Next time when the TabChild wants to use some privileged API via ContentChild, the chrome process can check if the (PBrowserId, TabContext) matched the internal record. If not, then the child process will be killed.

framless

Testing

TBD

Work in progress

Full Query
ID Summary Priority Status
756376 Back out bug 755001 -- Remove "remote" attribute from <iframe mozbrowser> -- NEW
772155 Disallow nested content processes -- RESOLVED
812403 Support wake locks in nested content processes -- RESOLVED
845401 Make Marionette support nested processes -- RESOLVED
845668 Stand up nested content processes -- RESOLVED
845669 Support running mochitest "app" in content process -- RESOLVED
845686 Hook up GeckoContentController for nested content processes -- RESOLVED
845693 Add reftest support for nested content processes -- RESOLVED
845701 Reevaluate necko remoting in the context of bug 761935 -- RESOLVED
846047 Prevent nested subprocesses from attacking each other and their parent -- RESOLVED
846097 Support for running WebAPI marionette tests in nested subprocesses -- RESOLVED
879475 Allow nested oop <iframe mozbrowser> without nested content processes -- RESOLVED
883362 Support system messages for in-browser pages in the Firefox app. -- RESOLVED
885653 Add support for IPDL bridges that connect two of the same protocol -- RESOLVED
890570 Wean Necko off passing around PBrowsers -- RESOLVED
1020157 NeckoParent::GetValidatedAppInfo should recognize nested oop iframe -- RESOLVED
1020169 Render remote layers from nested oop iframe -- RESOLVED
1020172 Manage TabParent in chrome process -- RESOLVED
1020179 Move PContentPermissionRequest to be managed by PContent -- RESOLVED
1020186 Make POfflineCacheUpdate be managed by PContent -- RESOLVED
1020199 Make sure APZ works with nested oop iframe -- RESOLVED
1020204 Allow creating nested oop MozApp iframe -- RESOLVED
1024943 Check IsContentParent() before use AsContentParent() -- RESOLVED
1033984 Nested-OOP support for v2.1 -- VERIFIED
1035833 Fix layersId allocation in nested content process -- RESOLVED
1038620 Add --nested_oop option to mach test commands -- RESOLVED
1040561 Nested-oop does not works with Nuwa -- RESOLVED
1041417 Nested oop only works when Nuwa is disabled -- RESOLVED
1041419 Nested oop browser frame crashes when trying to play a video -- RESOLVED
1041425 Browser app crashes when sending touch event to nested child tab -- RESOLVED
1057230 Follow up bug 1020157, bug 1040561: Inproc-app-remote-browser doesn't have correct mContainingAppId -- RESOLVED
1097479 [BrowserAPI] Allow embed remote apps or widgets in content process if nested-oop is enabled -- RESOLVED
1098987 "Assertion failure: !mLastBridge" happens when creating a nested content process -- RESOLVED
1102060 PScreenManager::ScreenForBrowser doesn't work in nested content process -- RESOLVED
1104422 Should use PBrowserOrId in WyciwygChannelChild::AsyncOpen -- RESOLVED
1110650 [meta] Get nested-oop passing all tests -- RESOLVED
1114507 Permission is not removed when a nested oop app is killed -- RESOLVED
1114872 Duplicate mouse events in nested content process -- RESOLVED
1124087 [nested-oop] Process crashes when sending touch event to nested-oop iframe -- RESOLVED
1131444 B2G: Assertion failure: work_queue_.empty(), at message_loop.cc:410 -- RESOLVED
1138793 [nested_oop] ServiceWorkerRegistrar::Get() is not working in content process -- RESOLVED
1158663 [nested_oop] BlobParent cannot live in content process -- RESOLVED
1226535 Use ContentProcessManager::AllocateTabId to allocate tabId for each frameloader -- RESOLVED

43 Total; 1 Open (2.33%); 41 Resolved (95.35%); 1 Verified (2.33%);

Get nested-oop passing all tests

Full Query
ID Summary Priority Status
1102060 PScreenManager::ScreenForBrowser doesn't work in nested content process -- RESOLVED
1104422 Should use PBrowserOrId in WyciwygChannelChild::AsyncOpen -- RESOLVED
1114872 Duplicate mouse events in nested content process -- RESOLVED
1118088 [nested-oop] window.open() fails in nested content process -- RESOLVED
1120839 [nested-oop] nested oop iframe has no permission for remote XUL -- RESOLVED
1138270 [nested_oop] Can not pass the test case: dom/html/test/test_bug209275.xhtml -- RESOLVED
1139286 Get nested oop tests running on inbound, central, and try -- RESOLVED

7 Total; 0 Open (0%); 7 Resolved (100%); 0 Verified (0%);