Hi all,
This is just a friendly reminder that out-of-process iframes are shipping to users—and we eventually expect to use RemoteFrame architecture for all cross-origin frames—so please make sure any code you write or review works with OOPIFs.
What does that mean? It means code should not assume that:
- the main frame is local, i.e. Page::DeprecatedLocalMainFrame(), WebViewImpl::MainFrameImpl()
- the frame owner is local, i.e. Frame::DeprecatedLocalOwner()
- or even if any given frame is local, i.e. the use of ToLocalFrame()
Note there are times when it’s valid to assume that a frame is local. In those cases, explicitly downcast with ToLocalFrame() and document why this assumption is safe. However, it’s often better design to hide the local / remote differences behind an interface, i.e. a method like Frame::Detach(). When in doubt, please free to reach out to
site-isol...@chromium.org: we’re always happy to help with design.
Important: developers working in input / layout code need to be aware of the frame tree fragment concept as well: please see below for more information about this.
tl;dr: If you find yourself writing or reviewing code that uses these functions, please consider carefully if it will work with OOPIFs. Using one of these functions can often be a signal that the code will fail to work with OOPIFs. More details below.
Daniel, on behalf of the Site Isolation Team
The longer version:
- --site-per-process means that frames are grouped into processes based on origin. This applies recursively, so a page with three frames A → B → C will have three different processes.
- Same-origin frames in the same page are always in the same process.
- The entire frame tree is visible in each renderer. However, remote frames only exist as an IPC sink, but can't otherwise be inspected, e.g. it is not possible to inspect the layout tree of a remote frame.
- Frame trees are broken up into frame tree fragments. A local frame with a local parent frame is part of the same tree fragment. Similarly, a local frame with a local child frame is part of the same tree fragment. A frame tree like A1 → A2 → B → A3 has three tree fragments: (A1, A2), (B), and (A3). Each fragment has its own WebFrameWidgetImpl connected to the root frame of that tree fragment.
- Lifecycle updates for each tree fragment are independent: lifecycle updates proceed from WebFrameWidgetImpl::UpdateAllLifecyclePhases and update only the frames in the same fragment. This means that in A1 → B → A2, frame A1 and A2 have different WebFrameWidgetImpls and different lifecycle timings. Be careful not to touch A2 from A1 during lifecycle updates and vice versa, since frames in different tree fragments may be stale.
- For legacy reasons, Blink stores a lot of data per-page rather than per-frame. Be careful that stuff from a widget that is not connected to your widget's tree fragment is not getting mistakenly processed on your fragment, e.g. https://crbug.com/792687. If you want to know whether two pieces of data should be processed together, see if they correspond to the same LocalFrameRoot.
Testing
Due to the nature of out-of-process iframes, it's often necessary to write manual tests to exercise cases where the main frame and the child frame are in different processes. We run the layout tests LayoutTests/http in --site-per-process mode to provide coverage of these tests. A more recent addition is
oopif.test: anything.oopif.test resolves to 127.0.0.1 but always forces the creation of an out-of-process iframe, whether or not --site-per-process is specified.
Visualization
-
FramesExplorer is an extension written by nasko@ to visualize the frame tree.
- Call ::showFrameTree() in a debugged renderer to dump the information to stdout.