XBL2
Planning
Attachment, detachment
Binding documents are loaded and stored using the nsIDocument::RequestExternalResource API. (Note, check all places that enumerate all resource docs and make sure they deal properly, currently only zooming)
We'll probably want to allow sharing between binding docs between documents. Especially for chrome. For chrome we could simply use raw sharing. For content documents we could use a cache of documents and clone them as needed.
When a binding document is added, we store all element="" selectors in a RuleHash, templatized as nsSelectorHash. These hashes are stored per resource document since binding documents are linked per resource document.
Notifications happen by modifying nsNodeUtils such that the XBL2 code is notified after the root of any subtree. This avoids having to have the XBL2 code register a nsIMutationObserver for every orphaned subtree.
When we get a notification, we'll currently have to reresolve the mutated node and all its descendants. And depending on if certain rules are present, the mutated nodes parent or sibling and all its descendants. This is currently handled by the logic in nsCSSFrameConstructor::RestyleForAppend/RestyleForInsertOrChange
Ideally we can optimize here for the situation when a selector only contains an element name. These never change if they match or not and so never needs to be retested. Further, we need to do something about having to reresolve all descendants. For example we could for each binding remember which other nodes were used in matching the selector, and only retest when one of those nodes is mutated. As well as test the mutated node against the "upper" parts of all selectors and only test descendants if the mutated node matches an upper part.
Whenever a document is mutated we reresolve relevant nodes and rebuild the list of bindings that apply to them. Each node holds a linked list of bindings attached to it. Each binding indicates how it was attached (CSS, addBinding, elements="") and if it was attached due to inheritance. Whenever the DOM is mutated, we rebuild the list of bindings that are attached using elements="". Then compare this list against the list of bindings attached, skipping inherited bindings, and remove or add bindings as appropriate.
CSS and .addBinding bindings are added and removed separately. For CSS bindings we should only attach bindings when normal frame construction finds a binding, i.e. no more hacks to force style resolution on dislay:none elements.
Shadow tree rendering
When reresolving the children of a bound node which is being displayed (has a frame), redistribute any children of that bound node into its insertion points.
When a node is added into an insertion point, do the following: 1. If the parent of the insertion point is a bound node, find the new insertion point to add the node to. Make this be the new insertion point. Rinse repeat. 2. Notify the frame constructor, give it the parent, the insertion point, and the index in the insertion point (the last parameter is not present in nsIMutationObserver)
Same applies for removals.
Ideally we notice when the insertion point is the last child, and when we're inserting last in an insertion point, and then call a ContentAppended-like function (but with an extra "insertion index" per above).
Create a flattened-shadow-tree-child-walker. Initialized using a parent, a child index, and optionally an insertion-point index. Or initialized using just a parent which initializes it to the first child. This can then walk forwards and backwards in the childlist for a node in the flattened DOM tree.