• 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/ContainerNodeAlgorithms.h"
32 #include "core/dom/ElementTraversal.h"
33 #include "core/dom/NodeTraversal.h"
34 #include "core/dom/shadow/ContentDistribution.h"
35 #include "core/html/HTMLContentElement.h"
36 #include "core/html/HTMLShadowElement.h"
37 #include "core/inspector/InspectorInstrumentation.h"
38 
39 namespace WebCore {
40 
41 class DistributionPool FINAL {
42     STACK_ALLOCATED();
43 public:
44     explicit DistributionPool(const ContainerNode&);
45     void clear();
46     ~DistributionPool();
47     void distributeTo(InsertionPoint*, ElementShadow*);
48     void populateChildren(const ContainerNode&);
49 
50 private:
51     void detachNonDistributedNodes();
52     WillBeHeapVector<RawPtrWillBeMember<Node>, 32> m_nodes;
53     Vector<bool, 32> m_distributed;
54 };
55 
DistributionPool(const ContainerNode & parent)56 inline DistributionPool::DistributionPool(const ContainerNode& parent)
57 {
58     populateChildren(parent);
59 }
60 
clear()61 inline void DistributionPool::clear()
62 {
63     detachNonDistributedNodes();
64     m_nodes.clear();
65     m_distributed.clear();
66 }
67 
populateChildren(const ContainerNode & parent)68 inline void DistributionPool::populateChildren(const ContainerNode& parent)
69 {
70     clear();
71     for (Node* child = parent.firstChild(); child; child = child->nextSibling()) {
72         if (isActiveInsertionPoint(*child)) {
73             InsertionPoint* insertionPoint = toInsertionPoint(child);
74             for (size_t i = 0; i < insertionPoint->size(); ++i)
75                 m_nodes.append(insertionPoint->at(i));
76         } else {
77             m_nodes.append(child);
78         }
79     }
80     m_distributed.resize(m_nodes.size());
81     m_distributed.fill(false);
82 }
83 
distributeTo(InsertionPoint * insertionPoint,ElementShadow * elementShadow)84 void DistributionPool::distributeTo(InsertionPoint* insertionPoint, ElementShadow* elementShadow)
85 {
86     ContentDistribution distribution;
87 
88     for (size_t i = 0; i < m_nodes.size(); ++i) {
89         if (m_distributed[i])
90             continue;
91 
92         if (isHTMLContentElement(*insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(m_nodes, i))
93             continue;
94 
95         Node* node = m_nodes[i];
96         distribution.append(node);
97         elementShadow->didDistributeNode(node, insertionPoint);
98         m_distributed[i] = true;
99     }
100 
101     // Distributes fallback elements
102     if (insertionPoint->isContentInsertionPoint() && distribution.isEmpty()) {
103         for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) {
104             distribution.append(fallbackNode);
105             elementShadow->didDistributeNode(fallbackNode, insertionPoint);
106         }
107     }
108     insertionPoint->setDistribution(distribution);
109 }
110 
~DistributionPool()111 inline DistributionPool::~DistributionPool()
112 {
113     detachNonDistributedNodes();
114 }
115 
detachNonDistributedNodes()116 inline void DistributionPool::detachNonDistributedNodes()
117 {
118     for (size_t i = 0; i < m_nodes.size(); ++i) {
119         if (m_distributed[i])
120             continue;
121         if (m_nodes[i]->renderer())
122             m_nodes[i]->lazyReattachIfAttached();
123     }
124 }
125 
create()126 PassOwnPtrWillBeRawPtr<ElementShadow> ElementShadow::create()
127 {
128     return adoptPtrWillBeNoop(new ElementShadow());
129 }
130 
ElementShadow()131 ElementShadow::ElementShadow()
132     : m_needsDistributionRecalc(false)
133     , m_needsSelectFeatureSet(false)
134 {
135 }
136 
~ElementShadow()137 ElementShadow::~ElementShadow()
138 {
139 #if !ENABLE(OILPAN)
140     removeDetachedShadowRoots();
141 #endif
142 }
143 
addShadowRoot(Element & shadowHost,ShadowRoot::ShadowRootType type)144 ShadowRoot& ElementShadow::addShadowRoot(Element& shadowHost, ShadowRoot::ShadowRootType type)
145 {
146     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = ShadowRoot::create(shadowHost.document(), type);
147 
148     if (type == ShadowRoot::AuthorShadowRoot && (!youngestShadowRoot() || youngestShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot))
149         shadowHost.willAddFirstAuthorShadowRoot();
150 
151     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
152         root->lazyReattachIfAttached();
153 
154     shadowRoot->setParentOrShadowHostNode(&shadowHost);
155     shadowRoot->setParentTreeScope(shadowHost.treeScope());
156     m_shadowRoots.push(shadowRoot.get());
157     shadowHost.notifyNodeInserted(*shadowRoot);
158     setNeedsDistributionRecalc();
159 
160     InspectorInstrumentation::didPushShadowRoot(&shadowHost, shadowRoot.get());
161 
162     ASSERT(m_shadowRoots.head());
163     ASSERT(shadowRoot.get() == m_shadowRoots.head());
164     return *m_shadowRoots.head();
165 }
166 
167 #if !ENABLE(OILPAN)
removeDetachedShadowRoots()168 void ElementShadow::removeDetachedShadowRoots()
169 {
170     // Dont protect this ref count.
171     Element* shadowHost = host();
172     ASSERT(shadowHost);
173 
174     while (RefPtrWillBeRawPtr<ShadowRoot> oldRoot = m_shadowRoots.head()) {
175         InspectorInstrumentation::willPopShadowRoot(shadowHost, oldRoot.get());
176         shadowHost->document().removeFocusedElementOfSubtree(oldRoot.get());
177         m_shadowRoots.removeHead();
178         oldRoot->setParentOrShadowHostNode(0);
179         oldRoot->setParentTreeScope(shadowHost->document());
180         oldRoot->setPrev(0);
181         oldRoot->setNext(0);
182     }
183 }
184 #endif
185 
attach(const Node::AttachContext & context)186 void ElementShadow::attach(const Node::AttachContext& context)
187 {
188     Node::AttachContext childrenContext(context);
189     childrenContext.resolvedStyle = 0;
190 
191     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
192         if (root->needsAttach())
193             root->attach(childrenContext);
194     }
195 }
196 
detach(const Node::AttachContext & context)197 void ElementShadow::detach(const Node::AttachContext& context)
198 {
199     Node::AttachContext childrenContext(context);
200     childrenContext.resolvedStyle = 0;
201 
202     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
203         root->detach(childrenContext);
204 }
205 
setNeedsDistributionRecalc()206 void ElementShadow::setNeedsDistributionRecalc()
207 {
208     if (m_needsDistributionRecalc)
209         return;
210     m_needsDistributionRecalc = true;
211     host()->markAncestorsWithChildNeedsDistributionRecalc();
212     clearDistribution();
213 }
214 
hasSameStyles(const ElementShadow * other) const215 bool ElementShadow::hasSameStyles(const ElementShadow* other) const
216 {
217     ShadowRoot* root = youngestShadowRoot();
218     ShadowRoot* otherRoot = other->youngestShadowRoot();
219     while (root || otherRoot) {
220         if (!root || !otherRoot)
221             return false;
222 
223         StyleSheetList* list = root->styleSheets();
224         StyleSheetList* otherList = otherRoot->styleSheets();
225 
226         if (list->length() != otherList->length())
227             return false;
228 
229         for (size_t i = 0; i < list->length(); i++) {
230             if (toCSSStyleSheet(list->item(i))->contents() != toCSSStyleSheet(otherList->item(i))->contents())
231                 return false;
232         }
233         root = root->olderShadowRoot();
234         otherRoot = otherRoot->olderShadowRoot();
235     }
236 
237     return true;
238 }
239 
finalDestinationInsertionPointFor(const Node * key) const240 const InsertionPoint* ElementShadow::finalDestinationInsertionPointFor(const Node* key) const
241 {
242     ASSERT(key && !key->document().childNeedsDistributionRecalc());
243     NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
244     return it == m_nodeToInsertionPoints.end() ? 0: it->value.last().get();
245 }
246 
destinationInsertionPointsFor(const Node * key) const247 const DestinationInsertionPoints* ElementShadow::destinationInsertionPointsFor(const Node* key) const
248 {
249     ASSERT(key && !key->document().childNeedsDistributionRecalc());
250     NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
251     return it == m_nodeToInsertionPoints.end() ? 0: &it->value;
252 }
253 
distribute()254 void ElementShadow::distribute()
255 {
256     host()->setNeedsStyleRecalc(SubtreeStyleChange);
257     Vector<HTMLShadowElement*, 32> shadowInsertionPoints;
258     DistributionPool pool(*host());
259 
260     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
261         HTMLShadowElement* shadowInsertionPoint = 0;
262         const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& insertionPoints = root->descendantInsertionPoints();
263         for (size_t i = 0; i < insertionPoints.size(); ++i) {
264             InsertionPoint* point = insertionPoints[i].get();
265             if (!point->isActive())
266                 continue;
267             if (isHTMLShadowElement(*point)) {
268                 ASSERT(!shadowInsertionPoint);
269                 shadowInsertionPoint = toHTMLShadowElement(point);
270                 shadowInsertionPoints.append(shadowInsertionPoint);
271             } else {
272                 pool.distributeTo(point, this);
273                 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*point))
274                     shadow->setNeedsDistributionRecalc();
275             }
276         }
277     }
278 
279     for (size_t i = shadowInsertionPoints.size(); i > 0; --i) {
280         HTMLShadowElement* shadowInsertionPoint = shadowInsertionPoints[i - 1];
281         ShadowRoot* root = shadowInsertionPoint->containingShadowRoot();
282         ASSERT(root);
283         if (root->isOldest()) {
284             pool.distributeTo(shadowInsertionPoint, this);
285         } else if (root->olderShadowRoot()->type() == root->type()) {
286             // Only allow reprojecting older shadow roots between the same type to
287             // disallow reprojecting UA elements into author shadows.
288             DistributionPool olderShadowRootPool(*root->olderShadowRoot());
289             olderShadowRootPool.distributeTo(shadowInsertionPoint, this);
290             root->olderShadowRoot()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint);
291         }
292         if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*shadowInsertionPoint))
293             shadow->setNeedsDistributionRecalc();
294     }
295 }
296 
didDistributeNode(const Node * node,InsertionPoint * insertionPoint)297 void ElementShadow::didDistributeNode(const Node* node, InsertionPoint* insertionPoint)
298 {
299     NodeToDestinationInsertionPoints::AddResult result = m_nodeToInsertionPoints.add(node, DestinationInsertionPoints());
300     result.storedValue->value.append(insertionPoint);
301 }
302 
ensureSelectFeatureSet()303 const SelectRuleFeatureSet& ElementShadow::ensureSelectFeatureSet()
304 {
305     if (!m_needsSelectFeatureSet)
306         return m_selectFeatures;
307 
308     m_selectFeatures.clear();
309     for (ShadowRoot* root = oldestShadowRoot(); root; root = root->youngerShadowRoot())
310         collectSelectFeatureSetFrom(*root);
311     m_needsSelectFeatureSet = false;
312     return m_selectFeatures;
313 }
314 
collectSelectFeatureSetFrom(ShadowRoot & root)315 void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot& root)
316 {
317     if (!root.containsShadowRoots() && !root.containsContentElements())
318         return;
319 
320     for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(*element, &root)) {
321         if (ElementShadow* shadow = element->shadow())
322             m_selectFeatures.add(shadow->ensureSelectFeatureSet());
323         if (!isHTMLContentElement(*element))
324             continue;
325         const CSSSelectorList& list = toHTMLContentElement(*element).selectorList();
326         for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(*selector)) {
327             for (const CSSSelector* component = selector; component; component = component->tagHistory())
328                 m_selectFeatures.collectFeaturesFromSelector(*component);
329         }
330     }
331 }
332 
didAffectSelector(AffectedSelectorMask mask)333 void ElementShadow::didAffectSelector(AffectedSelectorMask mask)
334 {
335     if (ensureSelectFeatureSet().hasSelectorFor(mask))
336         setNeedsDistributionRecalc();
337 }
338 
willAffectSelector()339 void ElementShadow::willAffectSelector()
340 {
341     for (ElementShadow* shadow = this; shadow; shadow = shadow->containingShadow()) {
342         if (shadow->needsSelectFeatureSet())
343             break;
344         shadow->setNeedsSelectFeatureSet();
345     }
346     setNeedsDistributionRecalc();
347 }
348 
clearDistribution()349 void ElementShadow::clearDistribution()
350 {
351     m_nodeToInsertionPoints.clear();
352 
353     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
354         root->setShadowInsertionPointOfYoungerShadowRoot(nullptr);
355 }
356 
trace(Visitor * visitor)357 void ElementShadow::trace(Visitor* visitor)
358 {
359     visitor->trace(m_nodeToInsertionPoints);
360     visitor->trace(m_selectFeatures);
361     // Shadow roots are linked with previous and next pointers which are traced.
362     // It is therefore enough to trace one of the shadow roots here and the
363     // rest will be traced from there.
364     visitor->trace(m_shadowRoots.head());
365 }
366 
367 } // namespace
368