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