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 COMPUTER, 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#if USE(ACCELERATED_COMPOSITING)
29
30#include "GraphicsLayer.h"
31
32#include "FloatPoint.h"
33#include "FloatRect.h"
34#include "GraphicsContext.h"
35#include "LayoutRect.h"
36#include "RotateTransformOperation.h"
37#include "TextStream.h"
38#include <wtf/HashMap.h>
39#include <wtf/text/CString.h>
40#include <wtf/text/StringBuilder.h>
41#include <wtf/text/WTFString.h>
42
43#ifndef NDEBUG
44#include <stdio.h>
45#endif
46
47namespace WebCore {
48
49typedef HashMap<const GraphicsLayer*, Vector<FloatRect> > RepaintMap;
50static RepaintMap& repaintRectMap()
51{
52    DEFINE_STATIC_LOCAL(RepaintMap, map, ());
53    return map;
54}
55
56void KeyframeValueList::insert(PassOwnPtr<const AnimationValue> value)
57{
58    for (size_t i = 0; i < m_values.size(); ++i) {
59        const AnimationValue* curValue = m_values[i].get();
60        if (curValue->keyTime() == value->keyTime()) {
61            ASSERT_NOT_REACHED();
62            // insert after
63            m_values.insert(i + 1, value);
64            return;
65        }
66        if (curValue->keyTime() > value->keyTime()) {
67            // insert before
68            m_values.insert(i, value);
69            return;
70        }
71    }
72
73    m_values.append(value);
74}
75
76GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
77    : m_client(client)
78    , m_anchorPoint(0.5f, 0.5f, 0)
79    , m_opacity(1)
80    , m_zPosition(0)
81    , m_contentsOpaque(false)
82    , m_preserves3D(false)
83    , m_backfaceVisibility(true)
84    , m_usingTiledBacking(false)
85    , m_masksToBounds(false)
86    , m_drawsContent(false)
87    , m_contentsVisible(true)
88    , m_acceleratesDrawing(false)
89    , m_maintainsPixelAlignment(false)
90    , m_appliesPageScale(false)
91    , m_showDebugBorder(false)
92    , m_showRepaintCounter(false)
93    , m_paintingPhase(GraphicsLayerPaintAllWithOverflowClip)
94    , m_contentsOrientation(CompositingCoordinatesTopDown)
95    , m_parent(0)
96    , m_maskLayer(0)
97    , m_replicaLayer(0)
98    , m_replicatedLayer(0)
99    , m_repaintCount(0)
100{
101#ifndef NDEBUG
102    if (m_client)
103        m_client->verifyNotPainting();
104#endif
105}
106
107GraphicsLayer::~GraphicsLayer()
108{
109    resetTrackedRepaints();
110    ASSERT(!m_parent); // willBeDestroyed should have been called already.
111}
112
113void GraphicsLayer::willBeDestroyed()
114{
115#ifndef NDEBUG
116    if (m_client)
117        m_client->verifyNotPainting();
118#endif
119
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(0);
265    }
266}
267
268void GraphicsLayer::noteDeviceOrPageScaleFactorChangedIncludingDescendants()
269{
270    deviceOrPageScaleFactorChanged();
271
272    if (m_maskLayer)
273        m_maskLayer->deviceOrPageScaleFactorChanged();
274
275    if (m_replicaLayer)
276        m_replicaLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
277
278    const Vector<GraphicsLayer*>& childLayers = children();
279    size_t numChildren = childLayers.size();
280    for (size_t i = 0; i < numChildren; ++i)
281        childLayers[i]->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
282}
283
284void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer)
285{
286    if (m_replicaLayer == layer)
287        return;
288
289    if (m_replicaLayer)
290        m_replicaLayer->setReplicatedLayer(0);
291
292    if (layer)
293        layer->setReplicatedLayer(this);
294
295    m_replicaLayer = layer;
296}
297
298void GraphicsLayer::setOffsetFromRenderer(const IntSize& offset, ShouldSetNeedsDisplay shouldSetNeedsDisplay)
299{
300    if (offset == m_offsetFromRenderer)
301        return;
302
303    m_offsetFromRenderer = offset;
304
305    // If the compositing layer offset changes, we need to repaint.
306    if (shouldSetNeedsDisplay == SetNeedsDisplay)
307        setNeedsDisplay();
308}
309
310void GraphicsLayer::setSize(const FloatSize& size)
311{
312    if (size == m_size)
313        return;
314
315    m_size = size;
316
317    if (shouldRepaintOnSizeChange())
318        setNeedsDisplay();
319}
320
321void GraphicsLayer::setBackgroundColor(const Color& color)
322{
323    m_backgroundColor = color;
324}
325
326void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const IntRect& clip)
327{
328    if (m_client) {
329        IntSize offset = offsetFromRenderer();
330        context.translate(-offset);
331
332        IntRect clipRect(clip);
333        clipRect.move(offset);
334
335        m_client->paintContents(this, context, m_paintingPhase, clipRect);
336    }
337}
338
339String GraphicsLayer::animationNameForTransition(AnimatedPropertyID property)
340{
341    // | is not a valid identifier character in CSS, so this can never conflict with a keyframe identifier.
342    StringBuilder id;
343    id.appendLiteral("-|transition");
344    id.appendNumber(static_cast<int>(property));
345    id.append('-');
346    return id.toString();
347}
348
349void GraphicsLayer::suspendAnimations(double)
350{
351}
352
353void GraphicsLayer::resumeAnimations()
354{
355}
356
357void GraphicsLayer::getDebugBorderInfo(Color& color, float& width) const
358{
359    if (drawsContent()) {
360        if (m_usingTiledBacking) {
361            color = Color(255, 128, 0, 128); // tiled layer: orange
362            width = 2;
363            return;
364        }
365
366        color = Color(0, 128, 32, 128); // normal layer: green
367        width = 2;
368        return;
369    }
370
371    if (masksToBounds()) {
372        color = Color(128, 255, 255, 48); // masking layer: pale blue
373        width = 20;
374        return;
375    }
376
377    color = Color(255, 255, 0, 192); // container: yellow
378    width = 2;
379}
380
381void GraphicsLayer::updateDebugIndicators()
382{
383    if (!isShowingDebugBorder())
384        return;
385
386    Color borderColor;
387    float width = 0;
388    getDebugBorderInfo(borderColor, width);
389    setDebugBorder(borderColor, width);
390}
391
392void GraphicsLayer::setZPosition(float position)
393{
394    m_zPosition = position;
395}
396
397float GraphicsLayer::accumulatedOpacity() const
398{
399    if (!preserves3D())
400        return 1;
401
402    return m_opacity * (parent() ? parent()->accumulatedOpacity() : 1);
403}
404
405void GraphicsLayer::distributeOpacity(float accumulatedOpacity)
406{
407    // If this is a transform layer we need to distribute our opacity to all our children
408
409    // Incoming accumulatedOpacity is the contribution from our parent(s). We mutiply this by our own
410    // opacity to get the total contribution
411    accumulatedOpacity *= m_opacity;
412
413    setOpacityInternal(accumulatedOpacity);
414
415    if (preserves3D()) {
416        size_t numChildren = children().size();
417        for (size_t i = 0; i < numChildren; ++i)
418            children()[i]->distributeOpacity(accumulatedOpacity);
419    }
420}
421
422#if ENABLE(CSS_FILTERS)
423static inline const FilterOperations& filterOperationsAt(const KeyframeValueList& valueList, size_t index)
424{
425    return static_cast<const FilterAnimationValue&>(valueList.at(index)).value();
426}
427
428int GraphicsLayer::validateFilterOperations(const KeyframeValueList& valueList)
429{
430    ASSERT(valueList.property() == AnimatedPropertyWebkitFilter);
431
432    if (valueList.size() < 2)
433        return -1;
434
435    // Empty filters match anything, so find the first non-empty entry as the reference
436    size_t firstIndex = 0;
437    for ( ; firstIndex < valueList.size(); ++firstIndex) {
438        if (!filterOperationsAt(valueList, firstIndex).operations().isEmpty())
439            break;
440    }
441
442    if (firstIndex >= valueList.size())
443        return -1;
444
445    const FilterOperations& firstVal = filterOperationsAt(valueList, firstIndex);
446
447    for (size_t i = firstIndex + 1; i < valueList.size(); ++i) {
448        const FilterOperations& val = filterOperationsAt(valueList, i);
449
450        // An emtpy filter list matches anything.
451        if (val.operations().isEmpty())
452            continue;
453
454        if (!firstVal.operationsMatch(val))
455            return -1;
456    }
457
458    return firstIndex;
459}
460#endif
461
462// An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix
463// The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is
464// true if the rotation between any two keyframes is >= 180 degrees.
465
466static inline const TransformOperations& operationsAt(const KeyframeValueList& valueList, size_t index)
467{
468    return static_cast<const TransformAnimationValue&>(valueList.at(index)).value();
469}
470
471int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueList, bool& hasBigRotation)
472{
473    ASSERT(valueList.property() == AnimatedPropertyWebkitTransform);
474
475    hasBigRotation = false;
476
477    if (valueList.size() < 2)
478        return -1;
479
480    // Empty transforms match anything, so find the first non-empty entry as the reference.
481    size_t firstIndex = 0;
482    for ( ; firstIndex < valueList.size(); ++firstIndex) {
483        if (!operationsAt(valueList, firstIndex).operations().isEmpty())
484            break;
485    }
486
487    if (firstIndex >= valueList.size())
488        return -1;
489
490    const TransformOperations& firstVal = operationsAt(valueList, firstIndex);
491
492    // See if the keyframes are valid.
493    for (size_t i = firstIndex + 1; i < valueList.size(); ++i) {
494        const TransformOperations& val = operationsAt(valueList, i);
495
496        // An empty transform list matches anything.
497        if (val.operations().isEmpty())
498            continue;
499
500        if (!firstVal.operationsMatch(val))
501            return -1;
502    }
503
504    // Keyframes are valid, check for big rotations.
505    double lastRotAngle = 0.0;
506    double maxRotAngle = -1.0;
507
508    for (size_t j = 0; j < firstVal.operations().size(); ++j) {
509        TransformOperation::OperationType type = firstVal.operations().at(j)->getOperationType();
510
511        // if this is a rotation entry, we need to see if any angle differences are >= 180 deg
512        if (type == TransformOperation::ROTATE_X ||
513            type == TransformOperation::ROTATE_Y ||
514            type == TransformOperation::ROTATE_Z ||
515            type == TransformOperation::ROTATE_3D) {
516            lastRotAngle = static_cast<RotateTransformOperation*>(firstVal.operations().at(j).get())->angle();
517
518            if (maxRotAngle < 0)
519                maxRotAngle = fabs(lastRotAngle);
520
521            for (size_t i = firstIndex + 1; i < valueList.size(); ++i) {
522                const TransformOperations& val = operationsAt(valueList, i);
523                double rotAngle = val.operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val.operations().at(j).get())->angle());
524                double diffAngle = fabs(rotAngle - lastRotAngle);
525                if (diffAngle > maxRotAngle)
526                    maxRotAngle = diffAngle;
527                lastRotAngle = rotAngle;
528            }
529        }
530    }
531
532    hasBigRotation = maxRotAngle >= 180.0;
533
534    return firstIndex;
535}
536
537double GraphicsLayer::backingStoreMemoryEstimate() const
538{
539    if (!drawsContent())
540        return 0;
541
542    // Effects of page and device scale are ignored; subclasses should override to take these into account.
543    return static_cast<double>(4 * size().width()) * size().height();
544}
545
546void GraphicsLayer::resetTrackedRepaints()
547{
548    repaintRectMap().remove(this);
549}
550
551void GraphicsLayer::addRepaintRect(const FloatRect& repaintRect)
552{
553    if (m_client->isTrackingRepaints()) {
554        FloatRect largestRepaintRect(FloatPoint(), m_size);
555        largestRepaintRect.intersect(repaintRect);
556        RepaintMap::iterator repaintIt = repaintRectMap().find(this);
557        if (repaintIt == repaintRectMap().end()) {
558            Vector<FloatRect> repaintRects;
559            repaintRects.append(largestRepaintRect);
560            repaintRectMap().set(this, repaintRects);
561        } else {
562            Vector<FloatRect>& repaintRects = repaintIt->value;
563            repaintRects.append(largestRepaintRect);
564        }
565    }
566}
567
568void GraphicsLayer::writeIndent(TextStream& ts, int indent)
569{
570    for (int i = 0; i != indent; ++i)
571        ts << "  ";
572}
573
574void GraphicsLayer::dumpLayer(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const
575{
576    writeIndent(ts, indent);
577    ts << "(" << "GraphicsLayer";
578
579    if (behavior & LayerTreeAsTextDebug) {
580        ts << " " << static_cast<void*>(const_cast<GraphicsLayer*>(this));
581        ts << " \"" << m_name << "\"";
582    }
583
584    ts << "\n";
585    dumpProperties(ts, indent, behavior);
586    writeIndent(ts, indent);
587    ts << ")\n";
588}
589
590void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const
591{
592    if (m_position != FloatPoint()) {
593        writeIndent(ts, indent + 1);
594        ts << "(position " << m_position.x() << " " << m_position.y() << ")\n";
595    }
596
597    if (m_boundsOrigin != FloatPoint()) {
598        writeIndent(ts, indent + 1);
599        ts << "(bounds origin " << m_boundsOrigin.x() << " " << m_boundsOrigin.y() << ")\n";
600    }
601
602    if (m_anchorPoint != FloatPoint3D(0.5f, 0.5f, 0)) {
603        writeIndent(ts, indent + 1);
604        ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y() << ")\n";
605    }
606
607    if (m_size != IntSize()) {
608        writeIndent(ts, indent + 1);
609        ts << "(bounds " << m_size.width() << " " << m_size.height() << ")\n";
610    }
611
612    if (m_opacity != 1) {
613        writeIndent(ts, indent + 1);
614        ts << "(opacity " << m_opacity << ")\n";
615    }
616
617    if (m_usingTiledBacking) {
618        writeIndent(ts, indent + 1);
619        ts << "(usingTiledLayer " << m_usingTiledBacking << ")\n";
620    }
621
622    if (m_contentsOpaque) {
623        writeIndent(ts, indent + 1);
624        ts << "(contentsOpaque " << m_contentsOpaque << ")\n";
625    }
626
627    if (m_preserves3D) {
628        writeIndent(ts, indent + 1);
629        ts << "(preserves3D " << m_preserves3D << ")\n";
630    }
631
632    if (m_drawsContent) {
633        writeIndent(ts, indent + 1);
634        ts << "(drawsContent " << m_drawsContent << ")\n";
635    }
636
637    if (!m_contentsVisible) {
638        writeIndent(ts, indent + 1);
639        ts << "(contentsVisible " << m_contentsVisible << ")\n";
640    }
641
642    if (!m_backfaceVisibility) {
643        writeIndent(ts, indent + 1);
644        ts << "(backfaceVisibility " << (m_backfaceVisibility ? "visible" : "hidden") << ")\n";
645    }
646
647    if (behavior & LayerTreeAsTextDebug) {
648        writeIndent(ts, indent + 1);
649        ts << "(";
650        if (m_client)
651            ts << "client " << static_cast<void*>(m_client);
652        else
653            ts << "no client";
654        ts << ")\n";
655    }
656
657    if (m_backgroundColor.isValid()) {
658        writeIndent(ts, indent + 1);
659        ts << "(backgroundColor " << m_backgroundColor.nameForRenderTreeAsText() << ")\n";
660    }
661
662    if (!m_transform.isIdentity()) {
663        writeIndent(ts, indent + 1);
664        ts << "(transform ";
665        ts << "[" << m_transform.m11() << " " << m_transform.m12() << " " << m_transform.m13() << " " << m_transform.m14() << "] ";
666        ts << "[" << m_transform.m21() << " " << m_transform.m22() << " " << m_transform.m23() << " " << m_transform.m24() << "] ";
667        ts << "[" << m_transform.m31() << " " << m_transform.m32() << " " << m_transform.m33() << " " << m_transform.m34() << "] ";
668        ts << "[" << m_transform.m41() << " " << m_transform.m42() << " " << m_transform.m43() << " " << m_transform.m44() << "])\n";
669    }
670
671    // Avoid dumping the sublayer transform on the root layer, because it's used for geometry flipping, whose behavior
672    // differs between platforms.
673    if (parent() && !m_childrenTransform.isIdentity()) {
674        writeIndent(ts, indent + 1);
675        ts << "(childrenTransform ";
676        ts << "[" << m_childrenTransform.m11() << " " << m_childrenTransform.m12() << " " << m_childrenTransform.m13() << " " << m_childrenTransform.m14() << "] ";
677        ts << "[" << m_childrenTransform.m21() << " " << m_childrenTransform.m22() << " " << m_childrenTransform.m23() << " " << m_childrenTransform.m24() << "] ";
678        ts << "[" << m_childrenTransform.m31() << " " << m_childrenTransform.m32() << " " << m_childrenTransform.m33() << " " << m_childrenTransform.m34() << "] ";
679        ts << "[" << m_childrenTransform.m41() << " " << m_childrenTransform.m42() << " " << m_childrenTransform.m43() << " " << m_childrenTransform.m44() << "])\n";
680    }
681
682    if (m_replicaLayer) {
683        writeIndent(ts, indent + 1);
684        ts << "(replica layer";
685        if (behavior & LayerTreeAsTextDebug)
686            ts << " " << m_replicaLayer;
687        ts << ")\n";
688        m_replicaLayer->dumpLayer(ts, indent + 2, behavior);
689    }
690
691    if (m_replicatedLayer) {
692        writeIndent(ts, indent + 1);
693        ts << "(replicated layer";
694        if (behavior & LayerTreeAsTextDebug)
695            ts << " " << m_replicatedLayer;
696        ts << ")\n";
697    }
698
699    if (behavior & LayerTreeAsTextIncludeRepaintRects && repaintRectMap().contains(this) && !repaintRectMap().get(this).isEmpty()) {
700        writeIndent(ts, indent + 1);
701        ts << "(repaint rects\n";
702        for (size_t i = 0; i < repaintRectMap().get(this).size(); ++i) {
703            if (repaintRectMap().get(this)[i].isEmpty())
704                continue;
705            writeIndent(ts, indent + 2);
706            ts << "(rect ";
707            ts << repaintRectMap().get(this)[i].x() << " ";
708            ts << repaintRectMap().get(this)[i].y() << " ";
709            ts << repaintRectMap().get(this)[i].width() << " ";
710            ts << repaintRectMap().get(this)[i].height();
711            ts << ")\n";
712        }
713        writeIndent(ts, indent + 1);
714        ts << ")\n";
715    }
716
717    if (behavior & LayerTreeAsTextIncludePaintingPhases && paintingPhase()) {
718        writeIndent(ts, indent + 1);
719        ts << "(paintingPhases\n";
720        if (paintingPhase() & GraphicsLayerPaintBackground) {
721            writeIndent(ts, indent + 2);
722            ts << "GraphicsLayerPaintBackground\n";
723        }
724        if (paintingPhase() & GraphicsLayerPaintForeground) {
725            writeIndent(ts, indent + 2);
726            ts << "GraphicsLayerPaintForeground\n";
727        }
728        if (paintingPhase() & GraphicsLayerPaintMask) {
729            writeIndent(ts, indent + 2);
730            ts << "GraphicsLayerPaintMask\n";
731        }
732        if (paintingPhase() & GraphicsLayerPaintOverflowContents) {
733            writeIndent(ts, indent + 2);
734            ts << "GraphicsLayerPaintOverflowContents\n";
735        }
736        if (paintingPhase() & GraphicsLayerPaintCompositedScroll) {
737            writeIndent(ts, indent + 2);
738            ts << "GraphicsLayerPaintCompositedScroll\n";
739        }
740        writeIndent(ts, indent + 1);
741        ts << ")\n";
742    }
743
744    dumpAdditionalProperties(ts, indent, behavior);
745
746    if (m_children.size()) {
747        writeIndent(ts, indent + 1);
748        ts << "(children " << m_children.size() << "\n";
749
750        unsigned i;
751        for (i = 0; i < m_children.size(); i++)
752            m_children[i]->dumpLayer(ts, indent + 2, behavior);
753        writeIndent(ts, indent + 1);
754        ts << ")\n";
755    }
756}
757
758String GraphicsLayer::layerTreeAsText(LayerTreeAsTextBehavior behavior) const
759{
760    TextStream ts;
761
762    dumpLayer(ts, 0, behavior);
763    return ts.release();
764}
765
766} // namespace WebCore
767
768#ifndef NDEBUG
769void showGraphicsLayerTree(const WebCore::GraphicsLayer* layer)
770{
771    if (!layer)
772        return;
773
774    String output = layer->layerTreeAsText(LayerTreeAsTextDebug | LayerTreeAsTextIncludeVisibleRects | LayerTreeAsTextIncludeTileCaches);
775    fprintf(stderr, "%s\n", output.utf8().data());
776}
777#endif
778
779#endif // USE(ACCELERATED_COMPOSITING)
780