Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / dom / shadow / InsertionPoint.cpp
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 "HTMLNames.h"
35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/QualifiedName.h"
37 #include "core/dom/StaticNodeList.h"
38 #include "core/dom/shadow/ElementShadow.h"
39
40 namespace WebCore {
41
42 using namespace HTMLNames;
43
44 InsertionPoint::InsertionPoint(const QualifiedName& tagName, Document& document)
45     : HTMLElement(tagName, document, CreateInsertionPoint)
46     , m_registeredWithShadowRoot(false)
47 {
48     setHasCustomStyleCallbacks();
49 }
50
51 InsertionPoint::~InsertionPoint()
52 {
53 }
54
55 void InsertionPoint::setDistribution(ContentDistribution& distribution)
56 {
57     if (shouldUseFallbackElements()) {
58         for (Node* child = firstChild(); child; child = child->nextSibling())
59             child->lazyReattachIfAttached();
60     }
61
62     // Attempt not to reattach nodes that would be distributed to the exact same
63     // location by comparing the old and new distributions.
64
65     size_t i = 0;
66     size_t j = 0;
67
68     for ( ; i < m_distribution.size() && j < distribution.size(); ++i, ++j) {
69         if (m_distribution.size() < distribution.size()) {
70             // If the new distribution is larger than the old one, reattach all nodes in
71             // the new distribution that were inserted.
72             for ( ; j < distribution.size() && m_distribution.at(i) != distribution.at(j); ++j)
73                 distribution.at(j)->lazyReattachIfAttached();
74         } else if (m_distribution.size() > distribution.size()) {
75             // If the old distribution is larger than the new one, reattach all nodes in
76             // the old distribution that were removed.
77             for ( ; i < m_distribution.size() && m_distribution.at(i) != distribution.at(j); ++i)
78                 m_distribution.at(i)->lazyReattachIfAttached();
79         } else if (m_distribution.at(i) != distribution.at(j)) {
80             // If both distributions are the same length reattach both old and new.
81             m_distribution.at(i)->lazyReattachIfAttached();
82             distribution.at(j)->lazyReattachIfAttached();
83         }
84     }
85
86     // If we hit the end of either list above we need to reattach all remaining nodes.
87
88     for ( ; i < m_distribution.size(); ++i)
89         m_distribution.at(i)->lazyReattachIfAttached();
90
91     for ( ; j < distribution.size(); ++j)
92         distribution.at(j)->lazyReattachIfAttached();
93
94     m_distribution.swap(distribution);
95     m_distribution.shrinkToFit();
96 }
97
98 void InsertionPoint::attach(const AttachContext& context)
99 {
100     // We need to attach the distribution here so that they're inserted in the right order
101     // otherwise the n^2 protection inside RenderTreeBuilder will cause them to be
102     // inserted in the wrong place later. This also lets distributed nodes benefit from
103     // the n^2 protection.
104     for (size_t i = 0; i < m_distribution.size(); ++i) {
105         if (m_distribution.at(i)->needsAttach())
106             m_distribution.at(i)->attach(context);
107     }
108
109     HTMLElement::attach(context);
110 }
111
112 void InsertionPoint::detach(const AttachContext& context)
113 {
114     for (size_t i = 0; i < m_distribution.size(); ++i)
115         m_distribution.at(i)->lazyReattachIfAttached();
116
117     HTMLElement::detach(context);
118 }
119
120 void InsertionPoint::willRecalcStyle(StyleRecalcChange change)
121 {
122     if (change < Inherit)
123         return;
124     for (size_t i = 0; i < m_distribution.size(); ++i)
125         m_distribution.at(i)->setNeedsStyleRecalc(LocalStyleChange);
126 }
127
128 bool InsertionPoint::shouldUseFallbackElements() const
129 {
130     return isActive() && !hasDistribution();
131 }
132
133 bool InsertionPoint::canBeActive() const
134 {
135     if (!isInShadowTree())
136         return false;
137     for (Node* node = parentNode(); node; node = node->parentNode()) {
138         if (node->isInsertionPoint())
139             return false;
140     }
141     return true;
142 }
143
144 bool InsertionPoint::isActive() const
145 {
146     if (!canBeActive())
147         return false;
148     ShadowRoot* shadowRoot = containingShadowRoot();
149     if (!shadowRoot)
150         return false;
151     if (!hasTagName(shadowTag) || shadowRoot->descendantShadowElementCount() <= 1)
152         return true;
153
154     // Slow path only when there are more than one shadow elements in a shadow tree. That should be a rare case.
155     const Vector<RefPtr<InsertionPoint> >& insertionPoints = shadowRoot->descendantInsertionPoints();
156     for (size_t i = 0; i < insertionPoints.size(); ++i) {
157         InsertionPoint* point = insertionPoints[i].get();
158         if (point->hasTagName(shadowTag))
159             return point == this;
160     }
161     return true;
162 }
163
164 bool InsertionPoint::isShadowInsertionPoint() const
165 {
166     return hasTagName(shadowTag) && isActive();
167 }
168
169 bool InsertionPoint::isContentInsertionPoint() const
170 {
171     return hasTagName(contentTag) && isActive();
172 }
173
174 PassRefPtr<NodeList> InsertionPoint::getDistributedNodes()
175 {
176     document().updateDistributionForNodeIfNeeded(this);
177
178     Vector<RefPtr<Node> > nodes;
179     nodes.reserveInitialCapacity(m_distribution.size());
180     for (size_t i = 0; i < m_distribution.size(); ++i)
181         nodes.uncheckedAppend(m_distribution.at(i));
182
183     return StaticNodeList::adopt(nodes);
184 }
185
186 bool InsertionPoint::rendererIsNeeded(const RenderStyle& style)
187 {
188     return !isActive() && HTMLElement::rendererIsNeeded(style);
189 }
190
191 void InsertionPoint::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
192 {
193     HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
194     if (ShadowRoot* root = containingShadowRoot()) {
195         if (ElementShadow* rootOwner = root->owner())
196             rootOwner->setNeedsDistributionRecalc();
197     }
198 }
199
200 Node::InsertionNotificationRequest InsertionPoint::insertedInto(ContainerNode* insertionPoint)
201 {
202     HTMLElement::insertedInto(insertionPoint);
203     if (ShadowRoot* root = containingShadowRoot()) {
204         if (ElementShadow* rootOwner = root->owner()) {
205             rootOwner->setNeedsDistributionRecalc();
206             if (canBeActive() && !m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
207                 m_registeredWithShadowRoot = true;
208                 root->didAddInsertionPoint(this);
209                 rootOwner->didAffectApplyAuthorStyles();
210                 if (canAffectSelector())
211                     rootOwner->willAffectSelector();
212             }
213         }
214     }
215
216     return InsertionDone;
217 }
218
219 void InsertionPoint::removedFrom(ContainerNode* insertionPoint)
220 {
221     ShadowRoot* root = containingShadowRoot();
222     if (!root)
223         root = insertionPoint->containingShadowRoot();
224
225     if (root) {
226         if (ElementShadow* rootOwner = root->owner())
227             rootOwner->setNeedsDistributionRecalc();
228     }
229
230     // host can be null when removedFrom() is called from ElementShadow destructor.
231     ElementShadow* rootOwner = root ? root->owner() : 0;
232
233     // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up.
234     clearDistribution();
235
236     if (m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
237         ASSERT(root);
238         m_registeredWithShadowRoot = false;
239         root->didRemoveInsertionPoint(this);
240         if (rootOwner) {
241             rootOwner->didAffectApplyAuthorStyles();
242             if (canAffectSelector())
243                 rootOwner->willAffectSelector();
244         }
245     }
246
247     HTMLElement::removedFrom(insertionPoint);
248 }
249
250 void InsertionPoint::parseAttribute(const QualifiedName& name, const AtomicString& value)
251 {
252     if (name == reset_style_inheritanceAttr) {
253         if (!inDocument() || !isActive())
254             return;
255         containingShadowRoot()->host()->setNeedsStyleRecalc(SubtreeStyleChange);
256     } else
257         HTMLElement::parseAttribute(name, value);
258 }
259
260 bool InsertionPoint::resetStyleInheritance() const
261 {
262     return fastHasAttribute(reset_style_inheritanceAttr);
263 }
264
265 void InsertionPoint::setResetStyleInheritance(bool value)
266 {
267     setBooleanAttribute(reset_style_inheritanceAttr, value);
268 }
269
270 const InsertionPoint* resolveReprojection(const Node* projectedNode)
271 {
272     ASSERT(projectedNode);
273     const InsertionPoint* insertionPoint = 0;
274     const Node* current = projectedNode;
275     ElementShadow* lastElementShadow = 0;
276     while (true) {
277         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
278         if (!shadow || shadow == lastElementShadow)
279             break;
280         lastElementShadow = shadow;
281         const InsertionPoint* insertedTo = shadow->finalDestinationInsertionPointFor(projectedNode);
282         if (!insertedTo)
283             break;
284         ASSERT(current != insertedTo);
285         current = insertedTo;
286         insertionPoint = insertedTo;
287     }
288     return insertionPoint;
289 }
290
291 void collectDestinationInsertionPoints(const Node& node, Vector<InsertionPoint*, 8>& results)
292 {
293     const Node* current = &node;
294     ElementShadow* lastElementShadow = 0;
295     while (true) {
296         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
297         if (!shadow || shadow == lastElementShadow)
298             return;
299         lastElementShadow = shadow;
300         const DestinationInsertionPoints* insertionPoints = shadow->destinationInsertionPointsFor(&node);
301         if (!insertionPoints)
302             return;
303         for (size_t i = 0; i < insertionPoints->size(); ++i)
304             results.append(insertionPoints->at(i).get());
305         ASSERT(current != insertionPoints->last().get());
306         current = insertionPoints->last().get();
307     }
308 }
309
310 } // namespace WebCore