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