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