Gecko:Key Gecko Structures And Invariants

From MozillaWiki
Jump to: navigation, search

There are four trees associated with a loaded Web page.

  • the content tree, representing the DOM structure of the document. It's basically what you expect a data structure for an XML document to look like. This is a tree of objects which implement the nsIContent interface.
  • the frame tree, which is the visual representation of the document. Each frame can be thought of as a rectangular area on the page. The content nodes for XML elements are usually associated with one or more frames which display the element --- one frame if the element is rectangular, more if the element is something more complex (like a chunk of bolded text that happens to be word-wrapped), like this:
     aaaaaaaaaa BBBBBB
     BBBB aaaaaaaaaaaa

Frames implement the nsIFrame interface. Frames are a bit complicated because they can have children in several different linked lists; each one is called a "child list". nsIFrame::GetAdditionalChildListName lets you iterate through all the child list names supported by a particular frame. In theory there can be multiple frame trees for a given document (in theory we support multiple renderings of the same underlying DOM structure) but we don't use that capability in practice.

  • the view tree, which is a projection of the frame tree. Frames are lightweight because we often create thousands of them. Sometimes you need something heavyweight to handle complex rendering for an element (for example, an element that has the CSS3 'opacity' property set on it, or one that is positioned using CSS2 positioning.) In those cases, and others, we attach a view to the frame. Views are formed into a tree which must correspond to the frame tree in certain ways. There are extra issues with views, such as the fact that when documents are arranged in a tree (e.g. a document which contains subdocuments via the HTML FRAME or IFRAME elements) the views for the documents are arranged into a single tree. Views implement the nsIView interface.
  • the widget tree, which is a projection of the view tree. Some views need a platform native widget (e.g. a Win32 HWND) associated with them to function properly. Most views don't. The widgets must form a tree which corresponds to the view tree. Widgets implement the nsIWidget interface.

Dynamic changes to the content tree cause incremental updates to cascade through the other trees. Because we're trying to handle these changes efficiently, the code is complex and it's easy to break the invariants of the trees. If you break the invariants, generally chaos ensues, and probably (but not necessarily) a crash.

Here are some invariants off the top of my head:

http://lxr.mozilla.org/mozilla/source/content/base/public/nsIContent.h The tree that you get when you use the operations nsIContent::GetParent(), nsIContent::GetChildCount/GetChildAt really is a consistent tree.

http://lxr.mozilla.org/mozilla/source/layout/generic/nsIFrame.h The tree that you get when you use the operations nsIFrame::GetParent(), nsIFrame::GetFirstChild(), nsIFrame::GetNextSibling() really is a consistent tree (don't forget to use nsIFrame::GetAdditionalChildListName() to find all the children on all potential child lists!). nsIFrame::GetPrevInFlow/GetNextInFlow should give a consistent linked list (null terminated at each end) of all the frames mapping a given nsIContent node (i.e. every frame in the list should have the same nsIFrame::GetContent(), every frame in the list is in the same frame tree, and if two frames in the same tree return the same nsIFrame::GetContent(), then they are in the same linked list [I don't believe that's true for block-inside-inline splits -- in that case the NextSibling() of the first part of the inline is the child block, and the NextSibling() of the block is the second part of the inline; both parts of the inline have the same GetContent() but they're not in-flows of each other. --bz]).

http://lxr.mozilla.org/mozilla/source/view/public/nsIView.h The tree that you get when you use the operations nsIView::GetParent(), nsIView::GetFirstChild() and nsIView::GetNextSibling() really is a consistent tree. The frame for a view, if there is one, is stored in nsIView->GetClientData(); so for any frame F, if F->HasView() is true, then F->GetView() is nonnull and F->GetView()->GetClientData() == F. Also, the structure of the view tree has to be a projection of the frame tree structure. So if F1 and F2 are frames with views, and F2 is the nearest ancestor of F1 with a view, then F1->GetView()->GetParent() == F2->GetView().

http://lxr.mozilla.org/mozilla/source/widget/public/nsIWidget.h The tree you get when you use the operations nsIWidget::GetParent(), GetFirstChild(), GetNextSibling() really is a consistent tree. The view for a widget, if there is one, is stored in nsIWidget->GetClientData(), so etc etc. Likewise the structure of the widget tree has to be a projection of the view tree in the same way as the view tree is a projection of the frame tree.

Here is a recently fixed bug that involved breakage of the frame tree invariants and also breakage of the connection between view and frame trees: http://bugzilla.mozilla.org/show_bug.cgi?id=230417 Here is a similar bug: http://bugzilla.mozilla.org/show_bug.cgi?id=185357

See Also