2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Neither the name of Google Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "core/dom/shadow/ElementShadow.h"
30 #include "core/css/StyleSheetList.h"
31 #include "core/dom/ElementTraversal.h"
32 #include "core/dom/NodeTraversal.h"
33 #include "core/dom/shadow/DistributedNodes.h"
34 #include "core/frame/UseCounter.h"
35 #include "core/html/HTMLContentElement.h"
36 #include "core/html/HTMLShadowElement.h"
37 #include "core/inspector/InspectorInstrumentation.h"
38 #include "platform/EventDispatchForbiddenScope.h"
39 #include "platform/ScriptForbiddenScope.h"
43 class DistributionPool final
{
46 explicit DistributionPool(const ContainerNode
&);
49 void distributeTo(InsertionPoint
*, ElementShadow
*);
50 void populateChildren(const ContainerNode
&);
53 void detachNonDistributedNodes();
54 WillBeHeapVector
<RawPtrWillBeMember
<Node
>, 32> m_nodes
;
55 Vector
<bool, 32> m_distributed
;
58 inline DistributionPool::DistributionPool(const ContainerNode
& parent
)
60 populateChildren(parent
);
63 inline void DistributionPool::clear()
65 detachNonDistributedNodes();
67 m_distributed
.clear();
70 inline void DistributionPool::populateChildren(const ContainerNode
& parent
)
73 for (Node
* child
= parent
.firstChild(); child
; child
= child
->nextSibling()) {
74 if (isActiveInsertionPoint(*child
)) {
75 InsertionPoint
* insertionPoint
= toInsertionPoint(child
);
76 for (size_t i
= 0; i
< insertionPoint
->distributedNodesSize(); ++i
)
77 m_nodes
.append(insertionPoint
->distributedNodeAt(i
));
79 m_nodes
.append(child
);
82 m_distributed
.resize(m_nodes
.size());
83 m_distributed
.fill(false);
86 void DistributionPool::distributeTo(InsertionPoint
* insertionPoint
, ElementShadow
* elementShadow
)
88 DistributedNodes distributedNodes
;
90 for (size_t i
= 0; i
< m_nodes
.size(); ++i
) {
94 if (isHTMLContentElement(*insertionPoint
) && !toHTMLContentElement(insertionPoint
)->canSelectNode(m_nodes
, i
))
97 Node
* node
= m_nodes
[i
];
98 distributedNodes
.append(node
);
99 elementShadow
->didDistributeNode(node
, insertionPoint
);
100 m_distributed
[i
] = true;
103 // Distributes fallback elements
104 if (insertionPoint
->isContentInsertionPoint() && distributedNodes
.isEmpty()) {
105 for (Node
* fallbackNode
= insertionPoint
->firstChild(); fallbackNode
; fallbackNode
= fallbackNode
->nextSibling()) {
106 distributedNodes
.append(fallbackNode
);
107 elementShadow
->didDistributeNode(fallbackNode
, insertionPoint
);
110 insertionPoint
->setDistributedNodes(distributedNodes
);
113 inline DistributionPool::~DistributionPool()
115 detachNonDistributedNodes();
118 inline void DistributionPool::detachNonDistributedNodes()
120 for (size_t i
= 0; i
< m_nodes
.size(); ++i
) {
121 if (m_distributed
[i
])
123 if (m_nodes
[i
]->layoutObject())
124 m_nodes
[i
]->lazyReattachIfAttached();
128 PassOwnPtrWillBeRawPtr
<ElementShadow
> ElementShadow::create()
130 return adoptPtrWillBeNoop(new ElementShadow());
133 ElementShadow::ElementShadow()
134 : m_needsDistributionRecalc(false)
135 , m_needsSelectFeatureSet(false)
139 ElementShadow::~ElementShadow()
142 removeDetachedShadowRoots();
146 ShadowRoot
& ElementShadow::addShadowRoot(Element
& shadowHost
, ShadowRootType type
)
148 EventDispatchForbiddenScope assertNoEventDispatch
;
149 ScriptForbiddenScope forbidScript
;
151 if (type
== ShadowRootType::OpenByDefault
) {
152 if (!youngestShadowRoot()) {
153 shadowHost
.willAddFirstAuthorShadowRoot();
154 } else if (youngestShadowRoot()->type() == ShadowRootType::UserAgent
) {
155 shadowHost
.willAddFirstAuthorShadowRoot();
156 UseCounter::countDeprecation(shadowHost
.document(), UseCounter::ElementCreateShadowRootMultipleWithUserAgentShadowRoot
);
158 UseCounter::countDeprecation(shadowHost
.document(), UseCounter::ElementCreateShadowRootMultiple
);
160 } else if (type
== ShadowRootType::Open
|| type
== ShadowRootType::Closed
) {
161 shadowHost
.willAddFirstAuthorShadowRoot();
164 for (ShadowRoot
* root
= youngestShadowRoot(); root
; root
= root
->olderShadowRoot())
165 root
->lazyReattachIfAttached();
167 RefPtrWillBeRawPtr
<ShadowRoot
> shadowRoot
= ShadowRoot::create(shadowHost
.document(), type
);
168 shadowRoot
->setParentOrShadowHostNode(&shadowHost
);
169 shadowRoot
->setParentTreeScope(shadowHost
.treeScope());
170 m_shadowRoots
.push(shadowRoot
.get());
171 setNeedsDistributionRecalc();
173 shadowRoot
->insertedInto(&shadowHost
);
174 shadowHost
.setChildNeedsStyleRecalc();
175 shadowHost
.setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::Shadow
));
177 InspectorInstrumentation::didPushShadowRoot(&shadowHost
, shadowRoot
.get());
183 void ElementShadow::removeDetachedShadowRoots()
185 // Dont protect this ref count.
186 Element
* shadowHost
= host();
189 while (RefPtrWillBeRawPtr
<ShadowRoot
> oldRoot
= m_shadowRoots
.head()) {
190 InspectorInstrumentation::willPopShadowRoot(shadowHost
, oldRoot
.get());
191 shadowHost
->document().removeFocusedElementOfSubtree(oldRoot
.get());
192 m_shadowRoots
.removeHead();
193 oldRoot
->setParentOrShadowHostNode(0);
194 oldRoot
->setParentTreeScope(shadowHost
->document());
201 void ElementShadow::attach(const Node::AttachContext
& context
)
203 Node::AttachContext
childrenContext(context
);
204 childrenContext
.resolvedStyle
= 0;
206 for (ShadowRoot
* root
= youngestShadowRoot(); root
; root
= root
->olderShadowRoot()) {
207 if (root
->needsAttach())
208 root
->attach(childrenContext
);
212 void ElementShadow::detach(const Node::AttachContext
& context
)
214 Node::AttachContext
childrenContext(context
);
215 childrenContext
.resolvedStyle
= 0;
217 for (ShadowRoot
* root
= youngestShadowRoot(); root
; root
= root
->olderShadowRoot())
218 root
->detach(childrenContext
);
221 void ElementShadow::setNeedsDistributionRecalc()
223 if (m_needsDistributionRecalc
)
225 m_needsDistributionRecalc
= true;
226 host()->markAncestorsWithChildNeedsDistributionRecalc();
230 bool ElementShadow::hasSameStyles(const ElementShadow
* other
) const
232 ShadowRoot
* root
= youngestShadowRoot();
233 ShadowRoot
* otherRoot
= other
->youngestShadowRoot();
234 while (root
|| otherRoot
) {
235 if (!root
|| !otherRoot
)
238 StyleSheetList
* list
= root
->styleSheets();
239 StyleSheetList
* otherList
= otherRoot
->styleSheets();
241 if (list
->length() != otherList
->length())
244 for (size_t i
= 0; i
< list
->length(); i
++) {
245 if (toCSSStyleSheet(list
->item(i
))->contents() != toCSSStyleSheet(otherList
->item(i
))->contents())
248 root
= root
->olderShadowRoot();
249 otherRoot
= otherRoot
->olderShadowRoot();
255 const InsertionPoint
* ElementShadow::finalDestinationInsertionPointFor(const Node
* key
) const
257 ASSERT(key
&& !key
->needsDistributionRecalc());
258 NodeToDestinationInsertionPoints::const_iterator it
= m_nodeToInsertionPoints
.find(key
);
260 return it
== m_nodeToInsertionPoints
.end() ? nullptr : it
->value
->last().get();
262 return it
== m_nodeToInsertionPoints
.end() ? nullptr : it
->value
.last().get();
266 const DestinationInsertionPoints
* ElementShadow::destinationInsertionPointsFor(const Node
* key
) const
268 ASSERT(key
&& !key
->needsDistributionRecalc());
269 NodeToDestinationInsertionPoints::const_iterator it
= m_nodeToInsertionPoints
.find(key
);
271 return it
== m_nodeToInsertionPoints
.end() ? nullptr : it
->value
.get();
273 return it
== m_nodeToInsertionPoints
.end() ? nullptr : &it
->value
;
277 void ElementShadow::distribute()
279 host()->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::Shadow
));
280 WillBeHeapVector
<RawPtrWillBeMember
<HTMLShadowElement
>, 32> shadowInsertionPoints
;
281 DistributionPool
pool(*host());
283 for (ShadowRoot
* root
= youngestShadowRoot(); root
; root
= root
->olderShadowRoot()) {
284 HTMLShadowElement
* shadowInsertionPoint
= 0;
285 const WillBeHeapVector
<RefPtrWillBeMember
<InsertionPoint
>>& insertionPoints
= root
->descendantInsertionPoints();
286 for (size_t i
= 0; i
< insertionPoints
.size(); ++i
) {
287 InsertionPoint
* point
= insertionPoints
[i
].get();
288 if (!point
->isActive())
290 if (isHTMLShadowElement(*point
)) {
291 ASSERT(!shadowInsertionPoint
);
292 shadowInsertionPoint
= toHTMLShadowElement(point
);
293 shadowInsertionPoints
.append(shadowInsertionPoint
);
295 pool
.distributeTo(point
, this);
296 if (ElementShadow
* shadow
= shadowWhereNodeCanBeDistributed(*point
))
297 shadow
->setNeedsDistributionRecalc();
302 for (size_t i
= shadowInsertionPoints
.size(); i
> 0; --i
) {
303 HTMLShadowElement
* shadowInsertionPoint
= shadowInsertionPoints
[i
- 1];
304 ShadowRoot
* root
= shadowInsertionPoint
->containingShadowRoot();
306 if (root
->isOldest()) {
307 pool
.distributeTo(shadowInsertionPoint
, this);
308 } else if (root
->olderShadowRoot()->type() == root
->type()) {
309 // Only allow reprojecting older shadow roots between the same type to
310 // disallow reprojecting UA elements into author shadows.
311 DistributionPool
olderShadowRootPool(*root
->olderShadowRoot());
312 olderShadowRootPool
.distributeTo(shadowInsertionPoint
, this);
313 root
->olderShadowRoot()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint
);
315 if (ElementShadow
* shadow
= shadowWhereNodeCanBeDistributed(*shadowInsertionPoint
))
316 shadow
->setNeedsDistributionRecalc();
318 InspectorInstrumentation::didPerformElementShadowDistribution(host());
321 void ElementShadow::didDistributeNode(const Node
* node
, InsertionPoint
* insertionPoint
)
324 NodeToDestinationInsertionPoints::AddResult result
= m_nodeToInsertionPoints
.add(node
, nullptr);
325 if (result
.isNewEntry
)
326 result
.storedValue
->value
= adoptPtrWillBeNoop(new DestinationInsertionPoints());
327 result
.storedValue
->value
->append(insertionPoint
);
329 NodeToDestinationInsertionPoints::AddResult result
= m_nodeToInsertionPoints
.add(node
, DestinationInsertionPoints());
330 result
.storedValue
->value
.append(insertionPoint
);
334 const SelectRuleFeatureSet
& ElementShadow::ensureSelectFeatureSet()
336 if (!m_needsSelectFeatureSet
)
337 return m_selectFeatures
;
339 m_selectFeatures
.clear();
340 for (ShadowRoot
* root
= oldestShadowRoot(); root
; root
= root
->youngerShadowRoot())
341 collectSelectFeatureSetFrom(*root
);
342 m_needsSelectFeatureSet
= false;
343 return m_selectFeatures
;
346 void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot
& root
)
348 if (!root
.containsShadowRoots() && !root
.containsContentElements())
351 for (Element
& element
: ElementTraversal::descendantsOf(root
)) {
352 if (ElementShadow
* shadow
= element
.shadow())
353 m_selectFeatures
.add(shadow
->ensureSelectFeatureSet());
354 if (!isHTMLContentElement(element
))
356 const CSSSelectorList
& list
= toHTMLContentElement(element
).selectorList();
357 m_selectFeatures
.collectFeaturesFromSelectorList(list
);
361 void ElementShadow::willAffectSelector()
363 for (ElementShadow
* shadow
= this; shadow
; shadow
= shadow
->containingShadow()) {
364 if (shadow
->needsSelectFeatureSet())
366 shadow
->setNeedsSelectFeatureSet();
368 setNeedsDistributionRecalc();
371 void ElementShadow::clearDistribution()
373 m_nodeToInsertionPoints
.clear();
375 for (ShadowRoot
* root
= youngestShadowRoot(); root
; root
= root
->olderShadowRoot())
376 root
->setShadowInsertionPointOfYoungerShadowRoot(nullptr);
379 DEFINE_TRACE(ElementShadow
)
382 visitor
->trace(m_nodeToInsertionPoints
);
383 visitor
->trace(m_selectFeatures
);
384 // Shadow roots are linked with previous and next pointers which are traced.
385 // It is therefore enough to trace one of the shadow roots here and the
386 // rest will be traced from there.
387 visitor
->trace(m_shadowRoots
.head());