| | 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." |