107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch// found in the LICENSE file.
407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch#include "config.h"
65d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)#include "core/rendering/compositing/CompositingInputsUpdater.h"
707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
8f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)#include "core/rendering/RenderBlock.h"
907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch#include "core/rendering/RenderLayer.h"
1007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch#include "core/rendering/compositing/CompositedLayerMapping.h"
11197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch#include "core/rendering/compositing/RenderLayerCompositor.h"
12197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch#include "platform/TraceEvent.h"
1307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
14c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink {
1507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
165d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)CompositingInputsUpdater::CompositingInputsUpdater(RenderLayer* rootRenderLayer)
1707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    : m_geometryMap(UseTransforms)
1807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    , m_rootRenderLayer(rootRenderLayer)
1907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch{
2007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch}
2107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
225d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)CompositingInputsUpdater::~CompositingInputsUpdater()
2307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch{
2407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch}
2507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
26197021e6b966cfb06891637935ef33fff06433d1Ben Murdochvoid CompositingInputsUpdater::update()
27197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch{
28e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    TRACE_EVENT0("blink", "CompositingInputsUpdater::update");
29197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    updateRecursive(m_rootRenderLayer, DoNotForceUpdate, AncestorInfo());
30197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch}
31197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
32197021e6b966cfb06891637935ef33fff06433d1Ben Murdochstatic const RenderLayer* findParentLayerOnClippingContainerChain(const RenderLayer* layer)
33197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch{
34197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    RenderObject* current = layer->renderer();
35197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    while (current) {
36197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (current->style()->position() == FixedPosition) {
37197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            for (current = current->parent(); current && !current->canContainFixedPositionObjects(); current = current->parent()) {
387242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                // All types of clips apply to fixed-position descendants of other fixed-position elements.
397242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                // Note: it's unclear whether this is what the spec says. Firefox does not clip, but Chrome does.
407242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                if (current->style()->position() == FixedPosition && current->hasClipOrOverflowClip()) {
417242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                    ASSERT(current->hasLayer());
427242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                    return static_cast<const RenderLayerModelObject*>(current)->layer();
437242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                }
447242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
45197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                // CSS clip applies to fixed position elements even for ancestors that are not what the
46197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                // fixed element is positioned with respect to.
47197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                if (current->hasClip()) {
48197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    ASSERT(current->hasLayer());
49197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    return static_cast<const RenderLayerModelObject*>(current)->layer();
50197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                }
51197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            }
52197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        } else {
53197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            current = current->containingBlock();
54197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }
55197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
56197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (current->hasLayer())
57197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return static_cast<const RenderLayerModelObject*>(current)->layer();
58197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // Having clip or overflow clip forces the RenderObject to become a layer.
59197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        ASSERT(!current->hasClipOrOverflowClip());
60197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
61197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    ASSERT_NOT_REACHED();
62197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    return 0;
63197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch}
64197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
65197021e6b966cfb06891637935ef33fff06433d1Ben Murdochstatic const RenderLayer* findParentLayerOnContainingBlockChain(const RenderObject* object)
66197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch{
67197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    for (const RenderObject* current = object; current; current = current->containingBlock()) {
68197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (current->hasLayer())
69197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return static_cast<const RenderLayerModelObject*>(current)->layer();
70197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
71197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    ASSERT_NOT_REACHED();
72197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    return 0;
73197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch}
74197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
75197021e6b966cfb06891637935ef33fff06433d1Ben Murdochstatic bool hasClippedStackingAncestor(const RenderLayer* layer, const RenderLayer* clippingLayer)
76197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch{
77197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    if (layer == clippingLayer)
78197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        return false;
79197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    const RenderObject* clippingRenderer = clippingLayer->renderer();
80197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    for (const RenderLayer* current = layer->compositingContainer(); current && current != clippingLayer; current = current->compositingContainer()) {
81c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        if (current->renderer()->hasClipOrOverflowClip() && !clippingRenderer->isDescendantOf(current->renderer()))
82c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            return true;
83c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)
84197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (const RenderObject* container = current->clippingContainer()) {
85197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (clippingRenderer != container && !clippingRenderer->isDescendantOf(container))
86197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return true;
87197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }
88197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
89197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    return false;
90197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch}
91197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
92197021e6b966cfb06891637935ef33fff06433d1Ben Murdochvoid CompositingInputsUpdater::updateRecursive(RenderLayer* layer, UpdateType updateType, AncestorInfo info)
9307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch{
945d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    if (!layer->childNeedsCompositingInputsUpdate() && updateType != ForceUpdate)
9507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch        return;
9607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
9707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    m_geometryMap.pushMappingsToAncestor(layer, layer->parent());
9807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
9907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    if (layer->hasCompositedLayerMapping())
100f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        info.enclosingCompositedLayer = layer;
10107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
1025d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    if (layer->needsCompositingInputsUpdate()) {
103f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        if (info.enclosingCompositedLayer)
10476c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles)            info.enclosingCompositedLayer->compositedLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree);
10507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch        updateType = ForceUpdate;
10607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    }
10707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
10807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    if (updateType == ForceUpdate) {
109197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        RenderLayer::AncestorDependentCompositingInputs properties;
11007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
11107a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch        if (!layer->isRootLayer()) {
112a9984bf9ddc3cf73fdae3f29134a2bab379e7029Ben Murdoch            properties.clippedAbsoluteBoundingBox = enclosingIntRect(m_geometryMap.absoluteRect(layer->boundingBoxForCompositingOverlapTest()));
11307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch            // FIXME: Setting the absBounds to 1x1 instead of 0x0 makes very little sense,
11407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch            // but removing this code will make JSGameBench sad.
11507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch            // See https://codereview.chromium.org/13912020/
11607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch            if (properties.clippedAbsoluteBoundingBox.isEmpty())
11707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch                properties.clippedAbsoluteBoundingBox.setSize(IntSize(1, 1));
11807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
11907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch            IntRect clipRect = pixelSnappedIntRect(layer->clipper().backgroundClipRect(ClipRectsContext(m_rootRenderLayer, AbsoluteClipRects)).rect());
12007a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch            properties.clippedAbsoluteBoundingBox.intersect(clipRect);
1216f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch
1226f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch            const RenderLayer* parent = layer->parent();
123197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            properties.opacityAncestor = parent->isTransparent() ? parent : parent->opacityAncestor();
1247242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            properties.transformAncestor = parent->hasTransform() ? parent : parent->transformAncestor();
125197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            properties.filterAncestor = parent->hasFilter() ? parent : parent->filterAncestor();
126197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
127197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (info.hasAncestorWithClipOrOverflowClip) {
128197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                const RenderLayer* parentLayerOnClippingContainerChain = findParentLayerOnClippingContainerChain(layer);
129197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                const bool parentHasClipOrOverflowClip = parentLayerOnClippingContainerChain->renderer()->hasClipOrOverflowClip();
130197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                properties.clippingContainer = parentHasClipOrOverflowClip ? parentLayerOnClippingContainerChain->renderer() : parentLayerOnClippingContainerChain->clippingContainer();
131197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            }
132197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
133197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (info.lastScrollingAncestor) {
134197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                const RenderObject* containingBlock = layer->renderer()->containingBlock();
135197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                const RenderLayer* parentLayerOnContainingBlockChain = findParentLayerOnContainingBlockChain(containingBlock);
136197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
137197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain->ancestorScrollingLayer();
138197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                if (parentLayerOnContainingBlockChain->scrollsOverflow())
139197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain;
140197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
141197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                if (layer->renderer()->isOutOfFlowPositioned() && !layer->subtreeIsInvisible()) {
142197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    const RenderObject* lastScroller = info.lastScrollingAncestor->renderer();
143197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    const RenderLayer* clippingLayer = properties.clippingContainer ? properties.clippingContainer->enclosingLayer() : layer->compositor()->rootRenderLayer();
144197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    properties.isUnclippedDescendant = lastScroller != containingBlock && lastScroller->isDescendantOf(containingBlock);
145197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    if (hasClippedStackingAncestor(layer, clippingLayer))
146197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                        properties.clipParent = clippingLayer;
147197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                }
148197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
149197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                if (!layer->stackingNode()->isNormalFlowOnly()
150197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    && properties.ancestorScrollingLayer
151197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    && !info.ancestorStackingContext->renderer()->isDescendantOf(properties.ancestorScrollingLayer->renderer()))
152197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    properties.scrollParent = properties.ancestorScrollingLayer;
153f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            }
15407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch        }
15507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
156197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        properties.hasAncestorWithClipPath = info.hasAncestorWithClipPath;
157197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        layer->updateAncestorDependentCompositingInputs(properties);
15807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    }
15907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
160197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    if (layer->stackingNode()->isStackingContext())
161197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        info.ancestorStackingContext = layer;
162197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
163f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    if (layer->scrollsOverflow())
164197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        info.lastScrollingAncestor = layer;
165f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
166197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    if (layer->renderer()->hasClipOrOverflowClip())
167197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        info.hasAncestorWithClipOrOverflowClip = true;
168197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
169197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    if (layer->renderer()->hasClipPath())
170197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        info.hasAncestorWithClipPath = true;
171197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
172197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    RenderLayer::DescendantDependentCompositingInputs descendantProperties;
173197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    for (RenderLayer* child = layer->firstChild(); child; child = child->nextSibling()) {
174197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        updateRecursive(child, updateType, info);
175e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
176197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        descendantProperties.hasDescendantWithClipPath |= child->hasDescendantWithClipPath() || child->renderer()->hasClipPath();
177e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        descendantProperties.hasDescendantWithBlendMode |= child->hasDescendantWithBlendMode() || child->renderer()->hasBlendMode();
178197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
17907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
180197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    layer->updateDescendantDependentCompositingInputs(descendantProperties);
181197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    layer->didUpdateCompositingInputs();
182e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
183e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    m_geometryMap.popMappingsToAncestor(layer->parent());
18407a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch}
18507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
186197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch#if ENABLE(ASSERT)
18707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
1885d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)void CompositingInputsUpdater::assertNeedsCompositingInputsUpdateBitsCleared(RenderLayer* layer)
18907a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch{
1905d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    ASSERT(!layer->childNeedsCompositingInputsUpdate());
1915d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    ASSERT(!layer->needsCompositingInputsUpdate());
19207a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
19307a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch    for (RenderLayer* child = layer->firstChild(); child; child = child->nextSibling())
1945d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        assertNeedsCompositingInputsUpdateBitsCleared(child);
19507a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch}
19607a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
19707a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch#endif
19807a852d8c1953036774d8f3b65d18dcfea3bb4a2Ben Murdoch
199c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)} // namespace blink
200