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/ContentDistribution.h"
34 #include "core/html/HTMLContentElement.h"
35 #include "core/html/HTMLShadowElement.h"
36 #include "core/inspector/InspectorInstrumentation.h"
37 #include "platform/EventDispatchForbiddenScope.h"
38 #include "platform/ScriptForbiddenScope.h"
42 class DistributionPool final {
45 explicit DistributionPool(const ContainerNode&);
48 void distributeTo(InsertionPoint*, ElementShadow*);
49 void populateChildren(const ContainerNode&);
52 void detachNonDistributedNodes();
53 WillBeHeapVector<RawPtrWillBeMember<Node>, 32> m_nodes;
54 Vector<bool, 32> m_distributed;
57 inline DistributionPool::DistributionPool(const ContainerNode& parent)
59 populateChildren(parent);
62 inline void DistributionPool::clear()
64 detachNonDistributedNodes();
66 m_distributed.clear();
69 inline void DistributionPool::populateChildren(const ContainerNode& parent)
72 for (Node* child = parent.firstChild(); child; child = child->nextSibling()) {
73 if (isActiveInsertionPoint(*child)) {
74 InsertionPoint* insertionPoint = toInsertionPoint(child);
75 for (size_t i = 0; i < insertionPoint->size(); ++i)
76 m_nodes.append(insertionPoint->at(i));
78 m_nodes.append(child);
81 m_distributed.resize(m_nodes.size());
82 m_distributed.fill(false);
85 void DistributionPool::distributeTo(InsertionPoint* insertionPoint, ElementShadow* elementShadow)
87 ContentDistribution distribution;
89 for (size_t i = 0; i < m_nodes.size(); ++i) {
93 if (isHTMLContentElement(*insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(m_nodes, i))
96 Node* node = m_nodes[i];
97 distribution.append(node);
98 elementShadow->didDistributeNode(node, insertionPoint);
99 m_distributed[i] = true;
102 // Distributes fallback elements
103 if (insertionPoint->isContentInsertionPoint() && distribution.isEmpty()) {
104 for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) {
105 distribution.append(fallbackNode);
106 elementShadow->didDistributeNode(fallbackNode, insertionPoint);
109 insertionPoint->setDistribution(distribution);
112 inline DistributionPool::~DistributionPool()
114 detachNonDistributedNodes();
117 inline void DistributionPool::detachNonDistributedNodes()
119 for (size_t i = 0; i < m_nodes.size(); ++i) {
120 if (m_distributed[i])
122 if (m_nodes[i]->renderer())
123 m_nodes[i]->lazyReattachIfAttached();
127 PassOwnPtrWillBeRawPtr<ElementShadow> ElementShadow::create()
129 return adoptPtrWillBeNoop(new ElementShadow());
132 ElementShadow::ElementShadow()
133 : m_needsDistributionRecalc(false)
134 , m_needsSelectFeatureSet(false)
138 ElementShadow::~ElementShadow()
141 removeDetachedShadowRoots();
145 ShadowRoot& ElementShadow::addShadowRoot(Element& shadowHost, ShadowRoot::ShadowRootType type)
147 EventDispatchForbiddenScope assertNoEventDispatch;
148 ScriptForbiddenScope forbidScript;
150 if (type == ShadowRoot::AuthorShadowRoot && (!youngestShadowRoot() || youngestShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot))
151 shadowHost.willAddFirstAuthorShadowRoot();
153 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
154 root->lazyReattachIfAttached();
156 RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = ShadowRoot::create(shadowHost.document(), type);
157 shadowRoot->setParentOrShadowHostNode(&shadowHost);
158 shadowRoot->setParentTreeScope(shadowHost.treeScope());
159 m_shadowRoots.push(shadowRoot.get());
160 setNeedsDistributionRecalc();
162 shadowRoot->insertedInto(&shadowHost);
163 InspectorInstrumentation::didPushShadowRoot(&shadowHost, shadowRoot.get());
169 void ElementShadow::removeDetachedShadowRoots()
171 // Dont protect this ref count.
172 Element* shadowHost = host();
175 while (RefPtrWillBeRawPtr<ShadowRoot> oldRoot = m_shadowRoots.head()) {
176 InspectorInstrumentation::willPopShadowRoot(shadowHost, oldRoot.get());
177 shadowHost->document().removeFocusedElementOfSubtree(oldRoot.get());
178 m_shadowRoots.removeHead();
179 oldRoot->setParentOrShadowHostNode(0);
180 oldRoot->setParentTreeScope(shadowHost->document());
187 void ElementShadow::attach(const Node::AttachContext& context)
189 Node::AttachContext childrenContext(context);
190 childrenContext.resolvedStyle = 0;
192 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
193 if (root->needsAttach())
194 root->attach(childrenContext);
198 void ElementShadow::detach(const Node::AttachContext& context)
200 Node::AttachContext childrenContext(context);
201 childrenContext.resolvedStyle = 0;
203 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
204 root->detach(childrenContext);
207 void ElementShadow::setNeedsDistributionRecalc()
209 if (m_needsDistributionRecalc)
211 m_needsDistributionRecalc = true;
212 host()->markAncestorsWithChildNeedsDistributionRecalc();
216 bool ElementShadow::hasSameStyles(const ElementShadow* other) const
218 ShadowRoot* root = youngestShadowRoot();
219 ShadowRoot* otherRoot = other->youngestShadowRoot();
220 while (root || otherRoot) {
221 if (!root || !otherRoot)
224 StyleSheetList* list = root->styleSheets();
225 StyleSheetList* otherList = otherRoot->styleSheets();
227 if (list->length() != otherList->length())
230 for (size_t i = 0; i < list->length(); i++) {
231 if (toCSSStyleSheet(list->item(i))->contents() != toCSSStyleSheet(otherList->item(i))->contents())
234 root = root->olderShadowRoot();
235 otherRoot = otherRoot->olderShadowRoot();
241 const InsertionPoint* ElementShadow::finalDestinationInsertionPointFor(const Node* key) const
243 ASSERT(key && !key->document().childNeedsDistributionRecalc());
244 NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
245 return it == m_nodeToInsertionPoints.end() ? 0: it->value.last().get();
248 const DestinationInsertionPoints* ElementShadow::destinationInsertionPointsFor(const Node* key) const
250 ASSERT(key && !key->document().childNeedsDistributionRecalc());
251 NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
252 return it == m_nodeToInsertionPoints.end() ? 0: &it->value;
255 void ElementShadow::distribute()
257 host()->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Shadow));
258 WillBeHeapVector<RawPtrWillBeMember<HTMLShadowElement>, 32> shadowInsertionPoints;
259 DistributionPool pool(*host());
261 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
262 HTMLShadowElement* shadowInsertionPoint = 0;
263 const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& insertionPoints = root->descendantInsertionPoints();
264 for (size_t i = 0; i < insertionPoints.size(); ++i) {
265 InsertionPoint* point = insertionPoints[i].get();
266 if (!point->isActive())
268 if (isHTMLShadowElement(*point)) {
269 ASSERT(!shadowInsertionPoint);
270 shadowInsertionPoint = toHTMLShadowElement(point);
271 shadowInsertionPoints.append(shadowInsertionPoint);
273 pool.distributeTo(point, this);
274 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*point))
275 shadow->setNeedsDistributionRecalc();
280 for (size_t i = shadowInsertionPoints.size(); i > 0; --i) {
281 HTMLShadowElement* shadowInsertionPoint = shadowInsertionPoints[i - 1];
282 ShadowRoot* root = shadowInsertionPoint->containingShadowRoot();
284 if (root->isOldest()) {
285 pool.distributeTo(shadowInsertionPoint, this);
286 } else if (root->olderShadowRoot()->type() == root->type()) {
287 // Only allow reprojecting older shadow roots between the same type to
288 // disallow reprojecting UA elements into author shadows.
289 DistributionPool olderShadowRootPool(*root->olderShadowRoot());
290 olderShadowRootPool.distributeTo(shadowInsertionPoint, this);
291 root->olderShadowRoot()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint);
293 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*shadowInsertionPoint))
294 shadow->setNeedsDistributionRecalc();
298 void ElementShadow::didDistributeNode(const Node* node, InsertionPoint* insertionPoint)
300 NodeToDestinationInsertionPoints::AddResult result = m_nodeToInsertionPoints.add(node, DestinationInsertionPoints());
301 result.storedValue->value.append(insertionPoint);
304 const SelectRuleFeatureSet& ElementShadow::ensureSelectFeatureSet()
306 if (!m_needsSelectFeatureSet)
307 return m_selectFeatures;
309 m_selectFeatures.clear();
310 for (ShadowRoot* root = oldestShadowRoot(); root; root = root->youngerShadowRoot())
311 collectSelectFeatureSetFrom(*root);
312 m_needsSelectFeatureSet = false;
313 return m_selectFeatures;
316 void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot& root)
318 if (!root.containsShadowRoots() && !root.containsContentElements())
321 for (Element& element : ElementTraversal::descendantsOf(root)) {
322 if (ElementShadow* shadow = element.shadow())
323 m_selectFeatures.add(shadow->ensureSelectFeatureSet());
324 if (!isHTMLContentElement(element))
326 const CSSSelectorList& list = toHTMLContentElement(element).selectorList();
327 for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(*selector)) {
328 for (const CSSSelector* component = selector; component; component = component->tagHistory())
329 m_selectFeatures.collectFeaturesFromSelector(*component);
334 void ElementShadow::distributedNodePseudoStateChanged(CSSSelector::PseudoType pseudo)
336 if (ensureSelectFeatureSet().hasSelectorForPseudoType(pseudo))
337 setNeedsDistributionRecalc();
340 void ElementShadow::willAffectSelector()
342 for (ElementShadow* shadow = this; shadow; shadow = shadow->containingShadow()) {
343 if (shadow->needsSelectFeatureSet())
345 shadow->setNeedsSelectFeatureSet();
347 setNeedsDistributionRecalc();
350 void ElementShadow::clearDistribution()
352 m_nodeToInsertionPoints.clear();
354 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
355 root->setShadowInsertionPointOfYoungerShadowRoot(nullptr);
358 void ElementShadow::trace(Visitor* visitor)
361 visitor->trace(m_nodeToInsertionPoints);
362 visitor->trace(m_selectFeatures);
363 // Shadow roots are linked with previous and next pointers which are traced.
364 // It is therefore enough to trace one of the shadow roots here and the
365 // rest will be traced from there.
366 visitor->trace(m_shadowRoots.head());