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