• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
13  *
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.
25  */
26 
27 #include "config.h"
28 #include "core/dom/shadow/ElementShadow.h"
29 
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"
39 
40 namespace blink {
41 
42 class DistributionPool FINAL {
43     STACK_ALLOCATED();
44 public:
45     explicit DistributionPool(const ContainerNode&);
46     void clear();
47     ~DistributionPool();
48     void distributeTo(InsertionPoint*, ElementShadow*);
49     void populateChildren(const ContainerNode&);
50 
51 private:
52     void detachNonDistributedNodes();
53     WillBeHeapVector<RawPtrWillBeMember<Node>, 32> m_nodes;
54     Vector<bool, 32> m_distributed;
55 };
56 
DistributionPool(const ContainerNode & parent)57 inline DistributionPool::DistributionPool(const ContainerNode& parent)
58 {
59     populateChildren(parent);
60 }
61 
clear()62 inline void DistributionPool::clear()
63 {
64     detachNonDistributedNodes();
65     m_nodes.clear();
66     m_distributed.clear();
67 }
68 
populateChildren(const ContainerNode & parent)69 inline void DistributionPool::populateChildren(const ContainerNode& parent)
70 {
71     clear();
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));
77         } else {
78             m_nodes.append(child);
79         }
80     }
81     m_distributed.resize(m_nodes.size());
82     m_distributed.fill(false);
83 }
84 
distributeTo(InsertionPoint * insertionPoint,ElementShadow * elementShadow)85 void DistributionPool::distributeTo(InsertionPoint* insertionPoint, ElementShadow* elementShadow)
86 {
87     ContentDistribution distribution;
88 
89     for (size_t i = 0; i < m_nodes.size(); ++i) {
90         if (m_distributed[i])
91             continue;
92 
93         if (isHTMLContentElement(*insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(m_nodes, i))
94             continue;
95 
96         Node* node = m_nodes[i];
97         distribution.append(node);
98         elementShadow->didDistributeNode(node, insertionPoint);
99         m_distributed[i] = true;
100     }
101 
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);
107         }
108     }
109     insertionPoint->setDistribution(distribution);
110 }
111 
~DistributionPool()112 inline DistributionPool::~DistributionPool()
113 {
114     detachNonDistributedNodes();
115 }
116 
detachNonDistributedNodes()117 inline void DistributionPool::detachNonDistributedNodes()
118 {
119     for (size_t i = 0; i < m_nodes.size(); ++i) {
120         if (m_distributed[i])
121             continue;
122         if (m_nodes[i]->renderer())
123             m_nodes[i]->lazyReattachIfAttached();
124     }
125 }
126 
create()127 PassOwnPtrWillBeRawPtr<ElementShadow> ElementShadow::create()
128 {
129     return adoptPtrWillBeNoop(new ElementShadow());
130 }
131 
ElementShadow()132 ElementShadow::ElementShadow()
133     : m_needsDistributionRecalc(false)
134     , m_needsSelectFeatureSet(false)
135 {
136 }
137 
~ElementShadow()138 ElementShadow::~ElementShadow()
139 {
140 #if !ENABLE(OILPAN)
141     removeDetachedShadowRoots();
142 #endif
143 }
144 
addShadowRoot(Element & shadowHost,ShadowRoot::ShadowRootType type)145 ShadowRoot& ElementShadow::addShadowRoot(Element& shadowHost, ShadowRoot::ShadowRootType type)
146 {
147     EventDispatchForbiddenScope assertNoEventDispatch;
148     ScriptForbiddenScope forbidScript;
149 
150     if (type == ShadowRoot::AuthorShadowRoot && (!youngestShadowRoot() || youngestShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot))
151         shadowHost.willAddFirstAuthorShadowRoot();
152 
153     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
154         root->lazyReattachIfAttached();
155 
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();
161 
162     shadowRoot->insertedInto(&shadowHost);
163     InspectorInstrumentation::didPushShadowRoot(&shadowHost, shadowRoot.get());
164 
165     return *shadowRoot;
166 }
167 
168 #if !ENABLE(OILPAN)
removeDetachedShadowRoots()169 void ElementShadow::removeDetachedShadowRoots()
170 {
171     // Dont protect this ref count.
172     Element* shadowHost = host();
173     ASSERT(shadowHost);
174 
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());
181         oldRoot->setPrev(0);
182         oldRoot->setNext(0);
183     }
184 }
185 #endif
186 
attach(const Node::AttachContext & context)187 void ElementShadow::attach(const Node::AttachContext& context)
188 {
189     Node::AttachContext childrenContext(context);
190     childrenContext.resolvedStyle = 0;
191 
192     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
193         if (root->needsAttach())
194             root->attach(childrenContext);
195     }
196 }
197 
detach(const Node::AttachContext & context)198 void ElementShadow::detach(const Node::AttachContext& context)
199 {
200     Node::AttachContext childrenContext(context);
201     childrenContext.resolvedStyle = 0;
202 
203     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
204         root->detach(childrenContext);
205 }
206 
setNeedsDistributionRecalc()207 void ElementShadow::setNeedsDistributionRecalc()
208 {
209     if (m_needsDistributionRecalc)
210         return;
211     m_needsDistributionRecalc = true;
212     host()->markAncestorsWithChildNeedsDistributionRecalc();
213     clearDistribution();
214 }
215 
hasSameStyles(const ElementShadow * other) const216 bool ElementShadow::hasSameStyles(const ElementShadow* other) const
217 {
218     ShadowRoot* root = youngestShadowRoot();
219     ShadowRoot* otherRoot = other->youngestShadowRoot();
220     while (root || otherRoot) {
221         if (!root || !otherRoot)
222             return false;
223 
224         StyleSheetList* list = root->styleSheets();
225         StyleSheetList* otherList = otherRoot->styleSheets();
226 
227         if (list->length() != otherList->length())
228             return false;
229 
230         for (size_t i = 0; i < list->length(); i++) {
231             if (toCSSStyleSheet(list->item(i))->contents() != toCSSStyleSheet(otherList->item(i))->contents())
232                 return false;
233         }
234         root = root->olderShadowRoot();
235         otherRoot = otherRoot->olderShadowRoot();
236     }
237 
238     return true;
239 }
240 
finalDestinationInsertionPointFor(const Node * key) const241 const InsertionPoint* ElementShadow::finalDestinationInsertionPointFor(const Node* key) const
242 {
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();
246 }
247 
destinationInsertionPointsFor(const Node * key) const248 const DestinationInsertionPoints* ElementShadow::destinationInsertionPointsFor(const Node* key) const
249 {
250     ASSERT(key && !key->document().childNeedsDistributionRecalc());
251     NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
252     return it == m_nodeToInsertionPoints.end() ? 0: &it->value;
253 }
254 
distribute()255 void ElementShadow::distribute()
256 {
257     host()->setNeedsStyleRecalc(SubtreeStyleChange);
258     WillBeHeapVector<RawPtrWillBeMember<HTMLShadowElement>, 32> shadowInsertionPoints;
259     DistributionPool pool(*host());
260 
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())
267                 continue;
268             if (isHTMLShadowElement(*point)) {
269                 ASSERT(!shadowInsertionPoint);
270                 shadowInsertionPoint = toHTMLShadowElement(point);
271                 shadowInsertionPoints.append(shadowInsertionPoint);
272             } else {
273                 pool.distributeTo(point, this);
274                 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*point))
275                     shadow->setNeedsDistributionRecalc();
276             }
277         }
278     }
279 
280     for (size_t i = shadowInsertionPoints.size(); i > 0; --i) {
281         HTMLShadowElement* shadowInsertionPoint = shadowInsertionPoints[i - 1];
282         ShadowRoot* root = shadowInsertionPoint->containingShadowRoot();
283         ASSERT(root);
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);
292         }
293         if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*shadowInsertionPoint))
294             shadow->setNeedsDistributionRecalc();
295     }
296 }
297 
didDistributeNode(const Node * node,InsertionPoint * insertionPoint)298 void ElementShadow::didDistributeNode(const Node* node, InsertionPoint* insertionPoint)
299 {
300     NodeToDestinationInsertionPoints::AddResult result = m_nodeToInsertionPoints.add(node, DestinationInsertionPoints());
301     result.storedValue->value.append(insertionPoint);
302 }
303 
ensureSelectFeatureSet()304 const SelectRuleFeatureSet& ElementShadow::ensureSelectFeatureSet()
305 {
306     if (!m_needsSelectFeatureSet)
307         return m_selectFeatures;
308 
309     m_selectFeatures.clear();
310     for (ShadowRoot* root = oldestShadowRoot(); root; root = root->youngerShadowRoot())
311         collectSelectFeatureSetFrom(*root);
312     m_needsSelectFeatureSet = false;
313     return m_selectFeatures;
314 }
315 
collectSelectFeatureSetFrom(ShadowRoot & root)316 void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot& root)
317 {
318     if (!root.containsShadowRoots() && !root.containsContentElements())
319         return;
320 
321     for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(*element, &root)) {
322         if (ElementShadow* shadow = element->shadow())
323             m_selectFeatures.add(shadow->ensureSelectFeatureSet());
324         if (!isHTMLContentElement(*element))
325             continue;
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);
330         }
331     }
332 }
333 
distributedNodePseudoStateChanged(CSSSelector::PseudoType pseudo)334 void ElementShadow::distributedNodePseudoStateChanged(CSSSelector::PseudoType pseudo)
335 {
336     if (ensureSelectFeatureSet().hasSelectorForPseudoType(pseudo))
337         setNeedsDistributionRecalc();
338 }
339 
willAffectSelector()340 void ElementShadow::willAffectSelector()
341 {
342     for (ElementShadow* shadow = this; shadow; shadow = shadow->containingShadow()) {
343         if (shadow->needsSelectFeatureSet())
344             break;
345         shadow->setNeedsSelectFeatureSet();
346     }
347     setNeedsDistributionRecalc();
348 }
349 
clearDistribution()350 void ElementShadow::clearDistribution()
351 {
352     m_nodeToInsertionPoints.clear();
353 
354     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
355         root->setShadowInsertionPointOfYoungerShadowRoot(nullptr);
356 }
357 
trace(Visitor * visitor)358 void ElementShadow::trace(Visitor* visitor)
359 {
360 #if ENABLE(OILPAN)
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());
367 #endif
368 }
369 
370 } // namespace
371