2472af6556e706de11b9484bffbfe53216051bcb
[WebKit-https.git] / Source / WebCore / platform / graphics / GraphicsLayer.cpp
1 /*
2  * Copyright (C) 2009 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #include "GraphicsLayer.h"
29
30 #include "FloatPoint.h"
31 #include "FloatRect.h"
32 #include "GraphicsContext.h"
33 #include "LayoutRect.h"
34 #include "RotateTransformOperation.h"
35 #include "TextStream.h"
36 #include <wtf/HashMap.h>
37 #include <wtf/text/CString.h>
38 #include <wtf/text/StringBuilder.h>
39 #include <wtf/text/WTFString.h>
40
41 #ifndef NDEBUG
42 #include <stdio.h>
43 #endif
44
45 namespace WebCore {
46
47 typedef HashMap<const GraphicsLayer*, Vector<FloatRect>> RepaintMap;
48 static RepaintMap& repaintRectMap()
49 {
50     DEPRECATED_DEFINE_STATIC_LOCAL(RepaintMap, map, ());
51     return map;
52 }
53
54 void KeyframeValueList::insert(PassOwnPtr<const AnimationValue> value)
55 {
56     for (size_t i = 0; i < m_values.size(); ++i) {
57         const AnimationValue* curValue = m_values[i].get();
58         if (curValue->keyTime() == value->keyTime()) {
59             ASSERT_NOT_REACHED();
60             // insert after
61             m_values.insert(i + 1, value);
62             return;
63         }
64         if (curValue->keyTime() > value->keyTime()) {
65             // insert before
66             m_values.insert(i, value);
67             return;
68         }
69     }
70     
71     m_values.append(value);
72 }
73
74 GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
75     : m_client(client)
76     , m_anchorPoint(0.5f, 0.5f, 0)
77     , m_opacity(1)
78     , m_zPosition(0)
79 #if ENABLE(CSS_COMPOSITING)
80     , m_blendMode(BlendModeNormal)
81 #endif
82     , m_contentsOpaque(false)
83     , m_preserves3D(false)
84     , m_backfaceVisibility(true)
85     , m_usingTiledBacking(false)
86     , m_masksToBounds(false)
87     , m_drawsContent(false)
88     , m_contentsVisible(true)
89     , m_acceleratesDrawing(false)
90     , m_maintainsPixelAlignment(false)
91     , m_appliesPageScale(false)
92     , m_showDebugBorder(false)
93     , m_showRepaintCounter(false)
94     , m_paintingPhase(GraphicsLayerPaintAllWithOverflowClip)
95     , m_contentsOrientation(CompositingCoordinatesTopDown)
96     , m_parent(0)
97     , m_maskLayer(0)
98     , m_replicaLayer(0)
99     , m_replicatedLayer(0)
100     , m_repaintCount(0)
101     , m_customAppearance(NoCustomAppearance)
102     , m_customBehavior(NoCustomBehavior)
103 {
104 #ifndef NDEBUG
105     if (m_client)
106         m_client->verifyNotPainting();
107 #endif
108 }
109
110 GraphicsLayer::~GraphicsLayer()
111 {
112     resetTrackedRepaints();
113     ASSERT(!m_parent); // willBeDestroyed should have been called already.
114 }
115
116 void GraphicsLayer::willBeDestroyed()
117 {
118 #ifndef NDEBUG
119     if (m_client)
120         m_client->verifyNotPainting();
121 #endif
122     if (m_replicaLayer)
123         m_replicaLayer->setReplicatedLayer(0);
124
125     if (m_replicatedLayer)
126         m_replicatedLayer->setReplicatedByLayer(0);
127
128     removeAllChildren();
129     removeFromParent();
130 }
131
132 void GraphicsLayer::setParent(GraphicsLayer* layer)
133 {
134     ASSERT(!layer || !layer->hasAncestor(this));
135     m_parent = layer;
136 }
137
138 bool GraphicsLayer::hasAncestor(GraphicsLayer* ancestor) const
139 {
140     for (GraphicsLayer* curr = parent(); curr; curr = curr->parent()) {
141         if (curr == ancestor)
142             return true;
143     }
144     
145     return false;
146 }
147
148 bool GraphicsLayer::setChildren(const Vector<GraphicsLayer*>& newChildren)
149 {
150     // If the contents of the arrays are the same, nothing to do.
151     if (newChildren == m_children)
152         return false;
153
154     removeAllChildren();
155     
156     size_t listSize = newChildren.size();
157     for (size_t i = 0; i < listSize; ++i)
158         addChild(newChildren[i]);
159     
160     return true;
161 }
162
163 void GraphicsLayer::addChild(GraphicsLayer* childLayer)
164 {
165     ASSERT(childLayer != this);
166     
167     if (childLayer->parent())
168         childLayer->removeFromParent();
169
170     childLayer->setParent(this);
171     m_children.append(childLayer);
172 }
173
174 void GraphicsLayer::addChildAtIndex(GraphicsLayer* childLayer, int index)
175 {
176     ASSERT(childLayer != this);
177
178     if (childLayer->parent())
179         childLayer->removeFromParent();
180
181     childLayer->setParent(this);
182     m_children.insert(index, childLayer);
183 }
184
185 void GraphicsLayer::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling)
186 {
187     ASSERT(childLayer != this);
188     childLayer->removeFromParent();
189
190     bool found = false;
191     for (unsigned i = 0; i < m_children.size(); i++) {
192         if (sibling == m_children[i]) {
193             m_children.insert(i, childLayer);
194             found = true;
195             break;
196         }
197     }
198
199     childLayer->setParent(this);
200
201     if (!found)
202         m_children.append(childLayer);
203 }
204
205 void GraphicsLayer::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling)
206 {
207     childLayer->removeFromParent();
208     ASSERT(childLayer != this);
209
210     bool found = false;
211     for (unsigned i = 0; i < m_children.size(); i++) {
212         if (sibling == m_children[i]) {
213             m_children.insert(i+1, childLayer);
214             found = true;
215             break;
216         }
217     }
218
219     childLayer->setParent(this);
220
221     if (!found)
222         m_children.append(childLayer);
223 }
224
225 bool GraphicsLayer::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
226 {
227     ASSERT(!newChild->parent());
228     bool found = false;
229     for (unsigned i = 0; i < m_children.size(); i++) {
230         if (oldChild == m_children[i]) {
231             m_children[i] = newChild;
232             found = true;
233             break;
234         }
235     }
236     if (found) {
237         oldChild->setParent(0);
238
239         newChild->removeFromParent();
240         newChild->setParent(this);
241         return true;
242     }
243     return false;
244 }
245
246 void GraphicsLayer::removeAllChildren()
247 {
248     while (m_children.size()) {
249         GraphicsLayer* curLayer = m_children[0];
250         ASSERT(curLayer->parent());
251         curLayer->removeFromParent();
252     }
253 }
254
255 void GraphicsLayer::removeFromParent()
256 {
257     if (m_parent) {
258         unsigned i;
259         for (i = 0; i < m_parent->m_children.size(); i++) {
260             if (this == m_parent->m_children[i]) {
261                 m_parent->m_children.remove(i);
262                 break;
263             }
264         }
265
266         setParent(0);
267     }
268 }
269
270 void GraphicsLayer::noteDeviceOrPageScaleFactorChangedIncludingDescendants()
271 {
272     deviceOrPageScaleFactorChanged();
273
274     if (m_maskLayer)
275         m_maskLayer->deviceOrPageScaleFactorChanged();
276
277     if (m_replicaLayer)
278         m_replicaLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
279
280     const Vector<GraphicsLayer*>& childLayers = children();
281     size_t numChildren = childLayers.size();
282     for (size_t i = 0; i < numChildren; ++i)
283         childLayers[i]->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
284 }
285
286 void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer)
287 {
288     if (m_replicaLayer == layer)
289         return;
290
291     if (m_replicaLayer)
292         m_replicaLayer->setReplicatedLayer(0);
293
294     if (layer)
295         layer->setReplicatedLayer(this);
296
297     m_replicaLayer = layer;
298 }
299
300 void GraphicsLayer::setOffsetFromRenderer(const FloatSize& offset, ShouldSetNeedsDisplay shouldSetNeedsDisplay)
301 {
302     if (offset == m_offsetFromRenderer)
303         return;
304
305     m_offsetFromRenderer = offset;
306
307     // If the compositing layer offset changes, we need to repaint.
308     if (shouldSetNeedsDisplay == SetNeedsDisplay)
309         setNeedsDisplay();
310 }
311
312 void GraphicsLayer::setSize(const FloatSize& size)
313 {
314     if (size == m_size)
315         return;
316     
317     m_size = size;
318
319     if (shouldRepaintOnSizeChange())
320         setNeedsDisplay();
321 }
322
323 void GraphicsLayer::setBackgroundColor(const Color& color)
324 {
325     m_backgroundColor = color;
326 }
327
328 void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const FloatRect& clip)
329 {
330     if (m_client) {
331         FloatSize offset = offsetFromRenderer();
332         context.translate(-offset);
333
334         FloatRect clipRect(clip);
335         clipRect.move(offset);
336
337         m_client->paintContents(this, context, m_paintingPhase, clipRect);
338     }
339 }
340
341 String GraphicsLayer::animationNameForTransition(AnimatedPropertyID property)
342 {
343     // | is not a valid identifier character in CSS, so this can never conflict with a keyframe identifier.
344     StringBuilder id;
345     id.appendLiteral("-|transition");
346     id.appendNumber(static_cast<int>(property));
347     id.append('-');
348     return id.toString();
349 }
350
351 void GraphicsLayer::suspendAnimations(double)
352 {
353 }
354
355 void GraphicsLayer::resumeAnimations()
356 {
357 }
358
359 void GraphicsLayer::getDebugBorderInfo(Color& color, float& width) const
360 {
361     if (drawsContent()) {
362         if (m_usingTiledBacking) {
363             color = Color(255, 128, 0, 128); // tiled layer: orange
364             width = 2;
365             return;
366         }
367
368         color = Color(0, 128, 32, 128); // normal layer: green
369         width = 2;
370         return;
371     }
372
373     if (hasContentsLayer()) {
374         color = Color(255, 150, 255, 200); // non-painting layer with contents: pink
375         width = 2;
376         return;
377     }
378     
379     if (masksToBounds()) {
380         color = Color(128, 255, 255, 48); // masking layer: pale blue
381         width = 20;
382         return;
383     }
384
385     color = Color(255, 255, 0, 192); // container: yellow
386     width = 2;
387 }
388
389 void GraphicsLayer::updateDebugIndicators()
390 {
391     if (!isShowingDebugBorder())
392         return;
393
394     Color borderColor;
395     float width = 0;
396     getDebugBorderInfo(borderColor, width);
397     setDebugBorder(borderColor, width);
398 }
399
400 void GraphicsLayer::setZPosition(float position)
401 {
402     m_zPosition = position;
403 }
404
405 float GraphicsLayer::accumulatedOpacity() const
406 {
407     if (!preserves3D())
408         return 1;
409         
410     return m_opacity * (parent() ? parent()->accumulatedOpacity() : 1);
411 }
412
413 void GraphicsLayer::distributeOpacity(float accumulatedOpacity)
414 {
415     // If this is a transform layer we need to distribute our opacity to all our children
416     
417     // Incoming accumulatedOpacity is the contribution from our parent(s). We mutiply this by our own
418     // opacity to get the total contribution
419     accumulatedOpacity *= m_opacity;
420     
421     setOpacityInternal(accumulatedOpacity);
422     
423     if (preserves3D()) {
424         size_t numChildren = children().size();
425         for (size_t i = 0; i < numChildren; ++i)
426             children()[i]->distributeOpacity(accumulatedOpacity);
427     }
428 }
429
430 #if ENABLE(CSS_FILTERS)
431 static inline const FilterOperations& filterOperationsAt(const KeyframeValueList& valueList, size_t index)
432 {
433     return static_cast<const FilterAnimationValue&>(valueList.at(index)).value();
434 }
435
436 int GraphicsLayer::validateFilterOperations(const KeyframeValueList& valueList)
437 {
438     ASSERT(valueList.property() == AnimatedPropertyWebkitFilter);
439
440     if (valueList.size() < 2)
441         return -1;
442
443     // Empty filters match anything, so find the first non-empty entry as the reference
444     size_t firstIndex = 0;
445     for ( ; firstIndex < valueList.size(); ++firstIndex) {
446         if (!filterOperationsAt(valueList, firstIndex).operations().isEmpty())
447             break;
448     }
449
450     if (firstIndex >= valueList.size())
451         return -1;
452
453     const FilterOperations& firstVal = filterOperationsAt(valueList, firstIndex);
454     
455     for (size_t i = firstIndex + 1; i < valueList.size(); ++i) {
456         const FilterOperations& val = filterOperationsAt(valueList, i);
457         
458         // An emtpy filter list matches anything.
459         if (val.operations().isEmpty())
460             continue;
461         
462         if (!firstVal.operationsMatch(val))
463             return -1;
464     }
465     
466     return firstIndex;
467 }
468 #endif
469
470 // An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix
471 // The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is 
472 // true if the rotation between any two keyframes is >= 180 degrees.
473
474 static inline const TransformOperations& operationsAt(const KeyframeValueList& valueList, size_t index)
475 {
476     return static_cast<const TransformAnimationValue&>(valueList.at(index)).value();
477 }
478
479 int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueList, bool& hasBigRotation)
480 {
481     ASSERT(valueList.property() == AnimatedPropertyWebkitTransform);
482
483     hasBigRotation = false;
484     
485     if (valueList.size() < 2)
486         return -1;
487     
488     // Empty transforms match anything, so find the first non-empty entry as the reference.
489     size_t firstIndex = 0;
490     for ( ; firstIndex < valueList.size(); ++firstIndex) {
491         if (!operationsAt(valueList, firstIndex).operations().isEmpty())
492             break;
493     }
494     
495     if (firstIndex >= valueList.size())
496         return -1;
497         
498     const TransformOperations& firstVal = operationsAt(valueList, firstIndex);
499     
500     // See if the keyframes are valid.
501     for (size_t i = firstIndex + 1; i < valueList.size(); ++i) {
502         const TransformOperations& val = operationsAt(valueList, i);
503         
504         // An empty transform list matches anything.
505         if (val.operations().isEmpty())
506             continue;
507             
508         if (!firstVal.operationsMatch(val))
509             return -1;
510     }
511
512     // Keyframes are valid, check for big rotations.    
513     double lastRotAngle = 0.0;
514     double maxRotAngle = -1.0;
515         
516     for (size_t j = 0; j < firstVal.operations().size(); ++j) {
517         TransformOperation::OperationType type = firstVal.operations().at(j)->type();
518         
519         // if this is a rotation entry, we need to see if any angle differences are >= 180 deg
520         if (type == TransformOperation::ROTATE_X ||
521             type == TransformOperation::ROTATE_Y ||
522             type == TransformOperation::ROTATE_Z ||
523             type == TransformOperation::ROTATE_3D) {
524             lastRotAngle = static_cast<RotateTransformOperation*>(firstVal.operations().at(j).get())->angle();
525             
526             if (maxRotAngle < 0)
527                 maxRotAngle = fabs(lastRotAngle);
528             
529             for (size_t i = firstIndex + 1; i < valueList.size(); ++i) {
530                 const TransformOperations& val = operationsAt(valueList, i);
531                 double rotAngle = val.operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val.operations().at(j).get())->angle());
532                 double diffAngle = fabs(rotAngle - lastRotAngle);
533                 if (diffAngle > maxRotAngle)
534                     maxRotAngle = diffAngle;
535                 lastRotAngle = rotAngle;
536             }
537         }
538     }
539     
540     hasBigRotation = maxRotAngle >= 180.0;
541     
542     return firstIndex;
543 }
544
545 double GraphicsLayer::backingStoreMemoryEstimate() const
546 {
547     if (!drawsContent())
548         return 0;
549     
550     // Effects of page and device scale are ignored; subclasses should override to take these into account.
551     return static_cast<double>(4 * size().width()) * size().height();
552 }
553
554 void GraphicsLayer::resetTrackedRepaints()
555 {
556     repaintRectMap().remove(this);
557 }
558
559 void GraphicsLayer::addRepaintRect(const FloatRect& repaintRect)
560 {
561     if (m_client->isTrackingRepaints()) {
562         FloatRect largestRepaintRect(FloatPoint(), m_size);
563         largestRepaintRect.intersect(repaintRect);
564         RepaintMap::iterator repaintIt = repaintRectMap().find(this);
565         if (repaintIt == repaintRectMap().end()) {
566             Vector<FloatRect> repaintRects;
567             repaintRects.append(largestRepaintRect);
568             repaintRectMap().set(this, repaintRects);
569         } else {
570             Vector<FloatRect>& repaintRects = repaintIt->value;
571             repaintRects.append(largestRepaintRect);
572         }
573     }
574 }
575
576 void GraphicsLayer::dumpLayer(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const
577 {
578     writeIndent(ts, indent);
579     ts << "(" << "GraphicsLayer";
580
581     if (behavior & LayerTreeAsTextDebug) {
582         ts << " " << static_cast<void*>(const_cast<GraphicsLayer*>(this));
583         ts << " \"" << m_name << "\"";
584     }
585
586     ts << "\n";
587     dumpProperties(ts, indent, behavior);
588     writeIndent(ts, indent);
589     ts << ")\n";
590 }
591
592 void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const
593 {
594     if (m_position != FloatPoint()) {
595         writeIndent(ts, indent + 1);
596         ts << "(position " << m_position.x() << " " << m_position.y() << ")\n";
597     }
598
599     if (m_boundsOrigin != FloatPoint()) {
600         writeIndent(ts, indent + 1);
601         ts << "(bounds origin " << m_boundsOrigin.x() << " " << m_boundsOrigin.y() << ")\n";
602     }
603
604     if (m_anchorPoint != FloatPoint3D(0.5f, 0.5f, 0)) {
605         writeIndent(ts, indent + 1);
606         ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y() << ")\n";
607     }
608
609     if (m_size != IntSize()) {
610         writeIndent(ts, indent + 1);
611         ts << "(bounds " << m_size.width() << " " << m_size.height() << ")\n";
612     }
613
614     if (m_opacity != 1) {
615         writeIndent(ts, indent + 1);
616         ts << "(opacity " << m_opacity << ")\n";
617     }
618
619 #if ENABLE(CSS_COMPOSITING)
620     if (m_blendMode != BlendModeNormal) {
621         writeIndent(ts, indent + 1);
622         ts << "(blendMode " << compositeOperatorName(CompositeSourceOver, m_blendMode) << ")\n";
623     }
624 #endif
625
626     if (m_usingTiledBacking) {
627         writeIndent(ts, indent + 1);
628         ts << "(usingTiledLayer " << m_usingTiledBacking << ")\n";
629     }
630
631     if (m_contentsOpaque) {
632         writeIndent(ts, indent + 1);
633         ts << "(contentsOpaque " << m_contentsOpaque << ")\n";
634     }
635
636     if (m_preserves3D) {
637         writeIndent(ts, indent + 1);
638         ts << "(preserves3D " << m_preserves3D << ")\n";
639     }
640
641     if (m_drawsContent && m_client->shouldDumpPropertyForLayer(this, "drawsContent")) {
642         writeIndent(ts, indent + 1);
643         ts << "(drawsContent " << m_drawsContent << ")\n";
644     }
645
646     if (!m_contentsVisible) {
647         writeIndent(ts, indent + 1);
648         ts << "(contentsVisible " << m_contentsVisible << ")\n";
649     }
650
651     if (!m_backfaceVisibility) {
652         writeIndent(ts, indent + 1);
653         ts << "(backfaceVisibility " << (m_backfaceVisibility ? "visible" : "hidden") << ")\n";
654     }
655
656     if (behavior & LayerTreeAsTextDebug) {
657         writeIndent(ts, indent + 1);
658         ts << "(";
659         if (m_client)
660             ts << "client " << static_cast<void*>(m_client);
661         else
662             ts << "no client";
663         ts << ")\n";
664     }
665
666     if (m_backgroundColor.isValid() && m_client->shouldDumpPropertyForLayer(this, "backgroundColor")) {
667         writeIndent(ts, indent + 1);
668         ts << "(backgroundColor " << m_backgroundColor.nameForRenderTreeAsText() << ")\n";
669     }
670
671     if (!m_transform.isIdentity()) {
672         writeIndent(ts, indent + 1);
673         ts << "(transform ";
674         ts << "[" << m_transform.m11() << " " << m_transform.m12() << " " << m_transform.m13() << " " << m_transform.m14() << "] ";
675         ts << "[" << m_transform.m21() << " " << m_transform.m22() << " " << m_transform.m23() << " " << m_transform.m24() << "] ";
676         ts << "[" << m_transform.m31() << " " << m_transform.m32() << " " << m_transform.m33() << " " << m_transform.m34() << "] ";
677         ts << "[" << m_transform.m41() << " " << m_transform.m42() << " " << m_transform.m43() << " " << m_transform.m44() << "])\n";
678     }
679
680     // Avoid dumping the sublayer transform on the root layer, because it's used for geometry flipping, whose behavior
681     // differs between platforms.
682     if (parent() && !m_childrenTransform.isIdentity()) {
683         writeIndent(ts, indent + 1);
684         ts << "(childrenTransform ";
685         ts << "[" << m_childrenTransform.m11() << " " << m_childrenTransform.m12() << " " << m_childrenTransform.m13() << " " << m_childrenTransform.m14() << "] ";
686         ts << "[" << m_childrenTransform.m21() << " " << m_childrenTransform.m22() << " " << m_childrenTransform.m23() << " " << m_childrenTransform.m24() << "] ";
687         ts << "[" << m_childrenTransform.m31() << " " << m_childrenTransform.m32() << " " << m_childrenTransform.m33() << " " << m_childrenTransform.m34() << "] ";
688         ts << "[" << m_childrenTransform.m41() << " " << m_childrenTransform.m42() << " " << m_childrenTransform.m43() << " " << m_childrenTransform.m44() << "])\n";
689     }
690
691     if (m_replicaLayer) {
692         writeIndent(ts, indent + 1);
693         ts << "(replica layer";
694         if (behavior & LayerTreeAsTextDebug)
695             ts << " " << m_replicaLayer;
696         ts << ")\n";
697         m_replicaLayer->dumpLayer(ts, indent + 2, behavior);
698     }
699
700     if (m_replicatedLayer) {
701         writeIndent(ts, indent + 1);
702         ts << "(replicated layer";
703         if (behavior & LayerTreeAsTextDebug)
704             ts << " " << m_replicatedLayer;
705         ts << ")\n";
706     }
707
708     if (behavior & LayerTreeAsTextIncludeRepaintRects && repaintRectMap().contains(this) && !repaintRectMap().get(this).isEmpty() && m_client->shouldDumpPropertyForLayer(this, "repaintRects")) {
709         writeIndent(ts, indent + 1);
710         ts << "(repaint rects\n";
711         for (size_t i = 0; i < repaintRectMap().get(this).size(); ++i) {
712             if (repaintRectMap().get(this)[i].isEmpty())
713                 continue;
714             writeIndent(ts, indent + 2);
715             ts << "(rect ";
716             ts << repaintRectMap().get(this)[i].x() << " ";
717             ts << repaintRectMap().get(this)[i].y() << " ";
718             ts << repaintRectMap().get(this)[i].width() << " ";
719             ts << repaintRectMap().get(this)[i].height();
720             ts << ")\n";
721         }
722         writeIndent(ts, indent + 1);
723         ts << ")\n";
724     }
725
726     if (behavior & LayerTreeAsTextIncludePaintingPhases && paintingPhase()) {
727         writeIndent(ts, indent + 1);
728         ts << "(paintingPhases\n";
729         if (paintingPhase() & GraphicsLayerPaintBackground) {
730             writeIndent(ts, indent + 2);
731             ts << "GraphicsLayerPaintBackground\n";
732         }
733         if (paintingPhase() & GraphicsLayerPaintForeground) {
734             writeIndent(ts, indent + 2);
735             ts << "GraphicsLayerPaintForeground\n";
736         }
737         if (paintingPhase() & GraphicsLayerPaintMask) {
738             writeIndent(ts, indent + 2);
739             ts << "GraphicsLayerPaintMask\n";
740         }
741         if (paintingPhase() & GraphicsLayerPaintOverflowContents) {
742             writeIndent(ts, indent + 2);
743             ts << "GraphicsLayerPaintOverflowContents\n";
744         }
745         if (paintingPhase() & GraphicsLayerPaintCompositedScroll) {
746             writeIndent(ts, indent + 2);
747             ts << "GraphicsLayerPaintCompositedScroll\n";
748         }
749         writeIndent(ts, indent + 1);
750         ts << ")\n";
751     }
752
753     dumpAdditionalProperties(ts, indent, behavior);
754     
755     if (m_children.size()) {
756         TextStream childrenStream;
757         unsigned totalChildCount = m_children.size();
758         for (size_t childIndex = 0; childIndex < m_children.size(); childIndex++) {
759             GraphicsLayer* child = m_children[childIndex];
760             if (!child->client()->shouldSkipLayerInDump(child)) {
761                 child->dumpLayer(childrenStream, indent + 2, behavior);
762                 continue;
763             }
764             
765             const Vector<GraphicsLayer*>& grandChildren = child->children();
766             totalChildCount += grandChildren.size() - 1;
767             for (size_t grandChildIndex = 0; grandChildIndex < grandChildren.size(); grandChildIndex++)
768                 grandChildren[grandChildIndex]->dumpLayer(childrenStream, indent + 2, behavior);
769         }
770
771         writeIndent(childrenStream, indent + 1);
772         childrenStream << ")\n";
773
774         if (totalChildCount) {
775             writeIndent(ts, indent + 1);
776             ts << "(children " << totalChildCount << "\n";
777             ts << childrenStream.release();
778         }
779     }
780 }
781
782 String GraphicsLayer::layerTreeAsText(LayerTreeAsTextBehavior behavior) const
783 {
784     TextStream ts;
785
786     dumpLayer(ts, 0, behavior);
787     return ts.release();
788 }
789
790 } // namespace WebCore
791
792 #ifndef NDEBUG
793 void showGraphicsLayerTree(const WebCore::GraphicsLayer* layer)
794 {
795     if (!layer)
796         return;
797
798     String output = layer->layerTreeAsText(LayerTreeAsTextDebug | LayerTreeAsTextIncludeVisibleRects | LayerTreeAsTextIncludeTileCaches);
799     fprintf(stderr, "%s\n", output.utf8().data());
800 }
801 #endif