• 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  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "core/dom/shadow/InsertionPoint.h"
33 
34 #include "core/HTMLNames.h"
35 #include "core/dom/Document.h"
36 #include "core/dom/ElementTraversal.h"
37 #include "core/dom/QualifiedName.h"
38 #include "core/dom/StaticNodeList.h"
39 #include "core/dom/shadow/ElementShadow.h"
40 
41 namespace blink {
42 
43 using namespace HTMLNames;
44 
InsertionPoint(const QualifiedName & tagName,Document & document)45 InsertionPoint::InsertionPoint(const QualifiedName& tagName, Document& document)
46     : HTMLElement(tagName, document, CreateInsertionPoint)
47     , m_registeredWithShadowRoot(false)
48 {
49     setHasCustomStyleCallbacks();
50 }
51 
~InsertionPoint()52 InsertionPoint::~InsertionPoint()
53 {
54 }
55 
setDistribution(ContentDistribution & distribution)56 void InsertionPoint::setDistribution(ContentDistribution& distribution)
57 {
58     if (shouldUseFallbackElements()) {
59         for (Node* child = firstChild(); child; child = child->nextSibling())
60             child->lazyReattachIfAttached();
61     }
62 
63     // Attempt not to reattach nodes that would be distributed to the exact same
64     // location by comparing the old and new distributions.
65 
66     size_t i = 0;
67     size_t j = 0;
68 
69     for ( ; i < m_distribution.size() && j < distribution.size(); ++i, ++j) {
70         if (m_distribution.size() < distribution.size()) {
71             // If the new distribution is larger than the old one, reattach all nodes in
72             // the new distribution that were inserted.
73             for ( ; j < distribution.size() && m_distribution.at(i) != distribution.at(j); ++j)
74                 distribution.at(j)->lazyReattachIfAttached();
75         } else if (m_distribution.size() > distribution.size()) {
76             // If the old distribution is larger than the new one, reattach all nodes in
77             // the old distribution that were removed.
78             for ( ; i < m_distribution.size() && m_distribution.at(i) != distribution.at(j); ++i)
79                 m_distribution.at(i)->lazyReattachIfAttached();
80         } else if (m_distribution.at(i) != distribution.at(j)) {
81             // If both distributions are the same length reattach both old and new.
82             m_distribution.at(i)->lazyReattachIfAttached();
83             distribution.at(j)->lazyReattachIfAttached();
84         }
85     }
86 
87     // If we hit the end of either list above we need to reattach all remaining nodes.
88 
89     for ( ; i < m_distribution.size(); ++i)
90         m_distribution.at(i)->lazyReattachIfAttached();
91 
92     for ( ; j < distribution.size(); ++j)
93         distribution.at(j)->lazyReattachIfAttached();
94 
95     m_distribution.swap(distribution);
96     m_distribution.shrinkToFit();
97 }
98 
attach(const AttachContext & context)99 void InsertionPoint::attach(const AttachContext& context)
100 {
101     // We need to attach the distribution here so that they're inserted in the right order
102     // otherwise the n^2 protection inside RenderTreeBuilder will cause them to be
103     // inserted in the wrong place later. This also lets distributed nodes benefit from
104     // the n^2 protection.
105     for (size_t i = 0; i < m_distribution.size(); ++i) {
106         if (m_distribution.at(i)->needsAttach())
107             m_distribution.at(i)->attach(context);
108     }
109 
110     HTMLElement::attach(context);
111 }
112 
detach(const AttachContext & context)113 void InsertionPoint::detach(const AttachContext& context)
114 {
115     for (size_t i = 0; i < m_distribution.size(); ++i)
116         m_distribution.at(i)->lazyReattachIfAttached();
117 
118     HTMLElement::detach(context);
119 }
120 
willRecalcStyle(StyleRecalcChange change)121 void InsertionPoint::willRecalcStyle(StyleRecalcChange change)
122 {
123     if (change < Inherit)
124         return;
125     for (size_t i = 0; i < m_distribution.size(); ++i)
126         m_distribution.at(i)->setNeedsStyleRecalc(SubtreeStyleChange);
127 }
128 
shouldUseFallbackElements() const129 bool InsertionPoint::shouldUseFallbackElements() const
130 {
131     return isActive() && !hasDistribution();
132 }
133 
canBeActive() const134 bool InsertionPoint::canBeActive() const
135 {
136     if (!isInShadowTree())
137         return false;
138     return !Traversal<InsertionPoint>::firstAncestor(*this);
139 }
140 
isActive() const141 bool InsertionPoint::isActive() const
142 {
143     if (!canBeActive())
144         return false;
145     ShadowRoot* shadowRoot = containingShadowRoot();
146     if (!shadowRoot)
147         return false;
148     if (!isHTMLShadowElement(*this) || shadowRoot->descendantShadowElementCount() <= 1)
149         return true;
150 
151     // Slow path only when there are more than one shadow elements in a shadow tree. That should be a rare case.
152     const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& insertionPoints = shadowRoot->descendantInsertionPoints();
153     for (size_t i = 0; i < insertionPoints.size(); ++i) {
154         InsertionPoint* point = insertionPoints[i].get();
155         if (isHTMLShadowElement(*point))
156             return point == this;
157     }
158     return true;
159 }
160 
isShadowInsertionPoint() const161 bool InsertionPoint::isShadowInsertionPoint() const
162 {
163     return isHTMLShadowElement(*this) && isActive();
164 }
165 
isContentInsertionPoint() const166 bool InsertionPoint::isContentInsertionPoint() const
167 {
168     return isHTMLContentElement(*this) && isActive();
169 }
170 
getDistributedNodes()171 PassRefPtrWillBeRawPtr<StaticNodeList> InsertionPoint::getDistributedNodes()
172 {
173     document().updateDistributionForNodeIfNeeded(this);
174 
175     WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
176     nodes.reserveInitialCapacity(m_distribution.size());
177     for (size_t i = 0; i < m_distribution.size(); ++i)
178         nodes.uncheckedAppend(m_distribution.at(i));
179 
180     return StaticNodeList::adopt(nodes);
181 }
182 
rendererIsNeeded(const RenderStyle & style)183 bool InsertionPoint::rendererIsNeeded(const RenderStyle& style)
184 {
185     return !isActive() && HTMLElement::rendererIsNeeded(style);
186 }
187 
childrenChanged(const ChildrenChange & change)188 void InsertionPoint::childrenChanged(const ChildrenChange& change)
189 {
190     HTMLElement::childrenChanged(change);
191     if (ShadowRoot* root = containingShadowRoot()) {
192         if (ElementShadow* rootOwner = root->owner())
193             rootOwner->setNeedsDistributionRecalc();
194     }
195 }
196 
insertedInto(ContainerNode * insertionPoint)197 Node::InsertionNotificationRequest InsertionPoint::insertedInto(ContainerNode* insertionPoint)
198 {
199     HTMLElement::insertedInto(insertionPoint);
200     if (ShadowRoot* root = containingShadowRoot()) {
201         if (ElementShadow* rootOwner = root->owner()) {
202             rootOwner->setNeedsDistributionRecalc();
203             if (canBeActive() && !m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
204                 m_registeredWithShadowRoot = true;
205                 root->didAddInsertionPoint(this);
206                 if (canAffectSelector())
207                     rootOwner->willAffectSelector();
208             }
209         }
210     }
211 
212     return InsertionDone;
213 }
214 
removedFrom(ContainerNode * insertionPoint)215 void InsertionPoint::removedFrom(ContainerNode* insertionPoint)
216 {
217     ShadowRoot* root = containingShadowRoot();
218     if (!root)
219         root = insertionPoint->containingShadowRoot();
220 
221     if (root) {
222         if (ElementShadow* rootOwner = root->owner())
223             rootOwner->setNeedsDistributionRecalc();
224     }
225 
226     // host can be null when removedFrom() is called from ElementShadow destructor.
227     ElementShadow* rootOwner = root ? root->owner() : 0;
228 
229     // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up.
230     clearDistribution();
231 
232     if (m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
233         ASSERT(root);
234         m_registeredWithShadowRoot = false;
235         root->didRemoveInsertionPoint(this);
236         if (rootOwner) {
237             if (canAffectSelector())
238                 rootOwner->willAffectSelector();
239         }
240     }
241 
242     HTMLElement::removedFrom(insertionPoint);
243 }
244 
trace(Visitor * visitor)245 void InsertionPoint::trace(Visitor* visitor)
246 {
247     visitor->trace(m_distribution);
248     HTMLElement::trace(visitor);
249 }
250 
resolveReprojection(const Node * projectedNode)251 const InsertionPoint* resolveReprojection(const Node* projectedNode)
252 {
253     ASSERT(projectedNode);
254     const InsertionPoint* insertionPoint = 0;
255     const Node* current = projectedNode;
256     ElementShadow* lastElementShadow = 0;
257     while (true) {
258         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
259         if (!shadow || shadow == lastElementShadow)
260             break;
261         lastElementShadow = shadow;
262         const InsertionPoint* insertedTo = shadow->finalDestinationInsertionPointFor(projectedNode);
263         if (!insertedTo)
264             break;
265         ASSERT(current != insertedTo);
266         current = insertedTo;
267         insertionPoint = insertedTo;
268     }
269     return insertionPoint;
270 }
271 
collectDestinationInsertionPoints(const Node & node,WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>,8> & results)272 void collectDestinationInsertionPoints(const Node& node, WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8>& results)
273 {
274     const Node* current = &node;
275     ElementShadow* lastElementShadow = 0;
276     while (true) {
277         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
278         if (!shadow || shadow == lastElementShadow)
279             return;
280         lastElementShadow = shadow;
281         const DestinationInsertionPoints* insertionPoints = shadow->destinationInsertionPointsFor(&node);
282         if (!insertionPoints)
283             return;
284         for (size_t i = 0; i < insertionPoints->size(); ++i)
285             results.append(insertionPoints->at(i).get());
286         ASSERT(current != insertionPoints->last().get());
287         current = insertionPoints->last().get();
288     }
289 }
290 
291 } // namespace blink
292