| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | A ShadowTree is an extra thing that is attached to a DOM element. The |
| 7 | ShadowTree is the stack of ShadowRoot elements; it has a list of |
| 8 | ShadowRoots, a HTMLContentSelector. |
| 9 | |
| 10 | Issue 1: HTMLContentSelector has a doubly linked list of RefPtr |
| 11 | references to nodes--this needs to be fixed. |
| 12 | |
| 13 | Each ShadowRoot is a document fragment because it is behaviorally |
| 14 | similar to a document fragment. When you operate on it in DOM, you |
| 15 | aren’t able to copy it. If you try to appendChild(shadowRoot), |
| 16 | appendChild will steal its children but not disconnect the ShadowRoot |
| 17 | from his host, etc. |
| 18 | |
| 19 | apply-author-sheets: This controls whether stylesheets from the |
| 20 | document apply to elements in the shadow. |
| 21 | |
| 22 | When you look at the element and see how it operates on the shadow |
| 23 | tree, it passes pretty much all of the style recalc things to it if |
| 24 | you look at attach, and the shadow tree passes that on to the |
| 25 | ShadowRoots in the stack. |
| 26 | |
| 27 | Terminology--oldest and youngest ShadowRoots. Younger shadow roots are |
| 28 | ones that were created later; they are towards the top of the |
| 29 | stack. User-agent shadows are always the oldest, bottom-of-the-stack |
| 30 | shadows. |
| 31 | |
| 32 | There are two special nodes, HTMLContentElement and HTMLShadowElement, |
| 33 | which are subtypes of InsertionPoint. These are special in Shadow |
| 34 | DOM--see the spec--a thing that doesn't render but stuff goes into |
| 35 | it. The InsertionPoint has a list of nodes that are distributed to the |
| 36 | insertion point. |
| 37 | |
| 38 | The InsertionPoint takes the children of the host and, using the a CSS |
| 39 | selector, lays claim to light children of a node to populate this list. |
| 40 | |
| 41 | (??? The CSS selector for <content> is actually a member of |
| 42 | InsertionPoint. This is to simulate user-agent shadows for all |
| 43 | elements. ???) |
| 44 | |
| 45 | HTMLShadowElement is an InsertionPoint that is a reference to an older |
| 46 | tree in the stack. There are pointers from the ShadowRoot to the |
| 47 | HTMLShadowElement that includes it. We need these back pointers to |
| 48 | walk nodes in the flattened tree -- we never explicitly construct the |
| 49 | flattened tree, just leave enough breadcrumbs in the tree to construct |
| 50 | it implicitly. |
| 51 | |
| 52 | There's a thing called NodeRenderingContext which knows how to find |
| 53 | the renderer that is the renderer of the node that is effectively the |
| 54 | parent of a node in a flattened tree. For example, <content> and |
| 55 | <shadow> are "erased" by the flattened tree, so if your nextSibling in |
| 56 | the DOM is a <content>, NodeRenderingContext knows to recurse into |
| 57 | what nodes it has selected. Since flattened tree construction is |
| 58 | implicit, this is effected in how the renderers are wired up. Hence |
| 59 | NodeRenderingContext::parentNodeForRenderingAndStyle. |
| 60 | |
| 61 | Issues: NodeRenderingContext is a beast with phases, etc. etc. We like |
| 62 | having all of this indirection logic in one class, just the class |
| 63 | needs to be simplified. |
| 64 | |
| 65 | Issues: Distribution should be separated from attachment. |
| 66 | |
| 67 | With Shadow DOM suddenly you have an added level of complexity on node |
| 68 | attachment and detachment, computing styles, etc. |
| 69 | |
| 70 | Q? How do we calculate the insertion point for a node? |
| 71 | |
| 72 | In InsertionPoint::attach, ShadowTree starts with the youngest |
| 73 | ShadowRoot and distributes light children into its <content> insertion |
| 74 | points and the next oldest ShadowRoot into its <shadow> insertion |
| 75 | point (if any.) If the next oldest ShadowRoot was distributed into |
| 76 | this ShadowRoot, assignment iterates and and does assignment (and |
| 77 | attach) in the next oldest ShadowRoot on the stack. |
| 78 | |
| 79 | HTMLContentSelector has the logic to populate itself with light |
| 80 | children if they match a CSS selector. |
| 81 | |
| 82 | First it collects all of the host children to distribute later. Then |
| 83 | each <content> is traversed and candidates are drawn from the set. |
| 84 | |
| 85 | HTMLContentSelector has a "phase" which is to prevent reentrancy. If a |
| 86 | ShadowTree subtree is reattached, you don’t want to tickle the |
| 87 | distribution in reattach. tree->selector().isSelecting(). |
| 88 | |
| 89 | HTMLContentSelector is basically the table mapping insertion point to |
| 90 | the nodes distributed to it. |
| 91 | |
| 92 | Bug: Rename ShadowTree to ShadowRootStack. |
| 93 | |
| 94 | Bug: Switch from HTMLContentSelector phase to a flag as the reentrancy |
| 95 | guard. |
| 96 | |
| 97 | (TODO: UML diagram) |
| 98 | |
| 99 | Bug: Remove the HTMLContent* (eg SelectionList, etc.) naming |
| 100 | convention, because HTMLX is for HTML element X, and we already have |
| 101 | HTMLContentElement. So let’s call it Content*. |
| 102 | |
| 103 | Bug: Rename HTMLContentSelection to something. "Selection" is bad |
| 104 | because it is like click-and-drag selection. |
| 105 | |
| 106 | Bug: Can HTMLContentSelection be just a list of nodes in the insertion |
| 107 | point, and get rid of HTMLContentSelection? |
| 108 | |
| 109 | We need the table to find the rendering parent of the distributed |
| 110 | node. |
| 111 | |
| 112 | Could consider a design where a ShadowRoot has a bit whether it has |
| 113 | been included with <shadow> or not, but not a pointer to the specific |
| 114 | <shadow> element itself, and have each ShadowRoot point to its first |
| 115 | docorder <shadow>. |
| 116 | |
| 117 | ComposedShadowTreeWalker -- this class gives you a facility to walk |
| 118 | the DOM as it is viewed as a result of the composition of this tree, |
| 119 | kind of as the user sitting in front of the browser sees it: |
| 120 | firstChild, lastChild, nextSibling, next (in tree order) etc. If you |
| 121 | want to access the tree "as it all looks as it is composed", use |
| 122 | ComposedTreeWalker. This is useful for event dispatch and things like |
| 123 | that. For example FocusController uses this because it needs to walk |
| 124 | the composed tree. |
| 125 | |
| 126 | TreeScope -- Document is a tree scope, but you can have subtrees that |
| 127 | are in separate tree scopes. It is a tree overlaid on the document |
| 128 | tree. Shadow DOM subtrees are in their own TreeScope and it is used to |
| 129 | control how style (CSSStyleSelector |
| 130 | ... treeScope->applyAuthorSheets()) is applied in Shadow DOM. |
| 131 | |
| 132 | Replaced Elements and Shadow DOM |
| 133 | -------------------------------- |
| 134 | |
| 135 | Suppose you have an <img> element or a <video> element and you create |
| 136 | a ShadowRoot for it. What the hell happens? ShadowRoot implies that |
| 137 | there is content and it messes with (replaces) the content. But |
| 138 | replaced elements are RenderReplaced and don’t render their content |
| 139 | like a <div>, etc. |
| 140 | |
| 141 | But what if you want to have an <img> and use Shadow DOM to give it a |
| 142 | polaroid frame? |
| 143 | |
| 144 | Conceptually this translates into the <img> node being built like this: |
| 145 | |
| 146 | img |
| 147 | + {SR} <<user-agent shadow root>> |
| 148 | + {image surface} |
| 149 | |
| 150 | Essentially what you have is an img node and then it has a Shadow Root |
| 151 | built-in (user-agent ShadowRoot) and it has something magic called an image surface which really renders the image. |
| 152 | |
| 153 | So if you have this: |
| 154 | |
| 155 | img |
| 156 | + {SR} <<author shadow root>> |
| 157 | + div |
| 158 | + div |
| 159 | + <shadow> |
| 160 | + {SR} <<user-agent shadow root>> |
| 161 | + {image surface} |
| 162 | |
| 163 | then you can achieve your Polaroid border. |
| 164 | |
| 165 | But today RenderReplaced is a completely black box, so we’re looking |
| 166 | for ideas about how to achieve this {image surface} thing. |
| 167 | |
| 168 | For form elements we just converted them to use Shadow DOM. With the |
| 169 | replaced elements like video and image... video is an interesting case |
| 170 | because it has some surface which is part of its render replaced |
| 171 | identity. |
| 172 | |
| 173 | RenderMedia m_children has one element in there. It lays out the image |
| 174 | to get the coordinates and then it lays out the controls. |
| 175 | |
| 176 | rniwa wanted to add :before and :after to RenderReplaced, so this |
| 177 | might be useful discussion for him. |
| 178 | |
| 179 | We could shim RenderReplaceds that have Shadow DOM and keep the |
| 180 | original RenderObject for use at <shadow>. |
| 181 | |
| 182 | This applies to: iframe, object, embed, img, video, audio, etc. |
| 183 | |
| 184 | One radical idea of dglazkov's: We create a different renderer |
| 185 | depending on whether it has a ShadowRoot or not. In createRenderer we |
| 186 | ask, do you have a ShadowRoot attached, and create a RenderBlock if it |
| 187 | does. We still have the same exact RenderMedia, RenderImage, etc. that |
| 188 | will set its size the right way and style will just work |
| 189 | correctly. Let's try it and see. shinyak thinks it will work. |
| 190 | |
| 191 | Events and Shadow DOM |
| 192 | --------------------- |
| 193 | |
| 194 | Event retargeting is really really hard. hayato has a document |
| 195 | ("Events in Shadow DOM") with ideas on how to improve it. It has to do |
| 196 | with the retargeting of related target eevents. Suppose we are in the |
| 197 | tree somewhere, moving a mouse from node X to node Y. |
| 198 | |
| 199 | o |
| 200 | + o |
| 201 | + Y <<mouseover>> |
| 202 | + o |
| 203 | + X <<mouseout>> |
| 204 | |
| 205 | the target for mouseout will be X and related target will by Y. The |
| 206 | target for mouseover will be Y and related target will be X. |
| 207 | |
| 208 | When you get into Shadow DOM this gets really complex. The code is |
| 209 | simple but it is conceptually hard. It is OK if your nodes are in the |
| 210 | same tree, but what if they’re in a different tree? For example: |
| 211 | |
| 212 | o |
| 213 | + {SR} |
| 214 | + Y |
| 215 | + X |
| 216 | |
| 217 | you don't want to leak node Y. So you have to retarget it to o. |
| 218 | |
| 219 | If you have |
| 220 | |
| 221 | a |
| 222 | + {SR} |
| 223 | + Y |
| 224 | + X |
| 225 | + o |
| 226 | |
| 227 | when you listen on a and retarget the events for X and Y to a, and the |
| 228 | event bubbles, you have to not let the event escape. Otherwise you get |
| 229 | nonsense like mouseout from a to a and mouseover form a to a. |
| 230 | |
| 231 | When you have multiple shadow trees things get really complicated. |
| 232 | |
| 233 | Event war story: The first implementation of the media controls |
| 234 | generated a spew of mouseover mouseout as the mouse moved over the |
| 235 | volume button to the volume slider and they were fired out of the |
| 236 | video element, with mouseover video from video to video and mouseout |
| 237 | of video from video to video etc. |
| 238 | |
| 239 | Events are basically dispatched "as rendered." |