/* * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #if USE(ACCELERATED_COMPOSITING) #include "RenderLayerCompositor.h" #include "AnimationController.h" #include "CanvasRenderingContext.h" #include "CSSPropertyNames.h" #include "Chrome.h" #include "ChromeClient.h" #include "Frame.h" #include "FrameView.h" #include "GraphicsLayer.h" #include "HTMLCanvasElement.h" #include "HTMLIFrameElement.h" #include "HTMLNames.h" #include "HitTestResult.h" #include "NodeList.h" #include "Page.h" #include "RenderApplet.h" #include "RenderEmbeddedObject.h" #include "RenderFullScreen.h" #include "RenderIFrame.h" #include "RenderLayerBacking.h" #include "RenderReplica.h" #include "RenderVideo.h" #include "RenderView.h" #include "Settings.h" #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) #include "HTMLMediaElement.h" #endif #if PROFILE_LAYER_REBUILD #include <wtf/CurrentTime.h> #endif #ifndef NDEBUG #include "RenderTreeAsText.h" #endif #if ENABLE(3D_RENDERING) // This symbol is used to determine from a script whether 3D rendering is enabled (via 'nm'). bool WebCoreHas3DRendering = true; #endif namespace WebCore { using namespace HTMLNames; struct CompositingState { CompositingState(RenderLayer* compAncestor) : m_compositingAncestor(compAncestor) , m_subtreeIsCompositing(false) #if ENABLE(COMPOSITED_FIXED_ELEMENTS) , m_positionedSibling(false) , m_hasFixedElement(false) #endif #if ENABLE(ANDROID_OVERFLOW_SCROLL) , m_hasScrollableElement(false) #endif #ifndef NDEBUG , m_depth(0) #endif { } RenderLayer* m_compositingAncestor; bool m_subtreeIsCompositing; // m_compositingBounds is only used in computeCompositingRequirements. It can be either the // ancestor bounds or the bounds for the sibling layers which are above the composited layer. // It is used to reject creating unnecesary layers. IntRect m_compositingBounds; #if ENABLE(COMPOSITED_FIXED_ELEMENTS) bool m_positionedSibling; bool m_hasFixedElement; #endif #if ENABLE(ANDROID_OVERFLOW_SCROLL) bool m_hasScrollableElement; #endif #ifndef NDEBUG int m_depth; #endif }; RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) : m_renderView(renderView) , m_rootPlatformLayer(0) , m_updateCompositingLayersTimer(this, &RenderLayerCompositor::updateCompositingLayersTimerFired) , m_hasAcceleratedCompositing(true) , m_compositingTriggers(static_cast<ChromeClient::CompositingTriggerFlags>(ChromeClient::AllTriggers)) , m_showDebugBorders(false) , m_showRepaintCounter(false) , m_compositingConsultsOverlap(true) , m_compositingDependsOnGeometry(false) , m_compositing(false) , m_compositingLayersNeedRebuild(false) , m_flushingLayers(false) , m_forceCompositingMode(false) , m_rootLayerAttachment(RootLayerUnattached) #if PROFILE_LAYER_REBUILD , m_rootLayerUpdateCount(0) #endif // PROFILE_LAYER_REBUILD { Settings* settings = m_renderView->document()->settings(); // Even when forcing compositing mode, ignore child frames, or this will trigger // layer creation from the enclosing RenderIFrame. ASSERT(m_renderView->document()->frame()); if (settings && settings->forceCompositingMode() && settings->acceleratedCompositingEnabled() && !m_renderView->document()->frame()->tree()->parent()) { m_forceCompositingMode = true; enableCompositingMode(); } } RenderLayerCompositor::~RenderLayerCompositor() { ASSERT(m_rootLayerAttachment == RootLayerUnattached); } void RenderLayerCompositor::enableCompositingMode(bool enable /* = true */) { if (enable != m_compositing) { m_compositing = enable; if (m_compositing) { ensureRootPlatformLayer(); notifyIFramesOfCompositingChange(); } else destroyRootPlatformLayer(); } } void RenderLayerCompositor::cacheAcceleratedCompositingFlags() { bool hasAcceleratedCompositing = false; bool showDebugBorders = false; bool showRepaintCounter = false; if (Settings* settings = m_renderView->document()->settings()) { hasAcceleratedCompositing = settings->acceleratedCompositingEnabled(); showDebugBorders = settings->showDebugBorders(); showRepaintCounter = settings->showRepaintCounter(); } // We allow the chrome to override the settings, in case the page is rendered // on a chrome that doesn't allow accelerated compositing. if (hasAcceleratedCompositing) { Frame* frame = m_renderView->frameView()->frame(); Page* page = frame ? frame->page() : 0; if (page) { ChromeClient* chromeClient = page->chrome()->client(); m_compositingTriggers = chromeClient->allowedCompositingTriggers(); hasAcceleratedCompositing = m_compositingTriggers; } } if (hasAcceleratedCompositing != m_hasAcceleratedCompositing || showDebugBorders != m_showDebugBorders || showRepaintCounter != m_showRepaintCounter) setCompositingLayersNeedRebuild(); m_hasAcceleratedCompositing = hasAcceleratedCompositing; m_showDebugBorders = showDebugBorders; m_showRepaintCounter = showRepaintCounter; } bool RenderLayerCompositor::canRender3DTransforms() const { return hasAcceleratedCompositing() && (m_compositingTriggers & ChromeClient::ThreeDTransformTrigger); } void RenderLayerCompositor::setCompositingLayersNeedRebuild(bool needRebuild) { if (inCompositingMode()) m_compositingLayersNeedRebuild = needRebuild; } void RenderLayerCompositor::scheduleLayerFlush() { Frame* frame = m_renderView->frameView()->frame(); Page* page = frame ? frame->page() : 0; if (!page) return; page->chrome()->client()->scheduleCompositingLayerSync(); } void RenderLayerCompositor::flushPendingLayerChanges() { ASSERT(!m_flushingLayers); m_flushingLayers = true; // FIXME: FrameView::syncCompositingStateRecursive() calls this for each // frame, so when compositing layers are connected between frames, we'll // end up syncing subframe's layers multiple times. // https://bugs.webkit.org/show_bug.cgi?id=52489 if (GraphicsLayer* rootLayer = rootPlatformLayer()) rootLayer->syncCompositingState(); ASSERT(m_flushingLayers); m_flushingLayers = false; } RenderLayerCompositor* RenderLayerCompositor::enclosingCompositorFlushingLayers() const { if (!m_renderView->frameView()) return 0; for (Frame* frame = m_renderView->frameView()->frame(); frame; frame = frame->tree()->parent()) { RenderLayerCompositor* compositor = frame->contentRenderer() ? frame->contentRenderer()->compositor() : 0; if (compositor->isFlushingLayers()) return compositor; } return 0; } void RenderLayerCompositor::scheduleCompositingLayerUpdate() { if (!m_updateCompositingLayersTimer.isActive()) m_updateCompositingLayersTimer.startOneShot(0); } bool RenderLayerCompositor::compositingLayerUpdatePending() const { return m_updateCompositingLayersTimer.isActive(); } void RenderLayerCompositor::updateCompositingLayersTimerFired(Timer<RenderLayerCompositor>*) { updateCompositingLayers(); } void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType updateType, RenderLayer* updateRoot) { m_updateCompositingLayersTimer.stop(); if (!m_compositingDependsOnGeometry && !m_compositing) return; bool checkForHierarchyUpdate = m_compositingDependsOnGeometry; bool needGeometryUpdate = false; switch (updateType) { case CompositingUpdateAfterLayoutOrStyleChange: case CompositingUpdateOnPaitingOrHitTest: checkForHierarchyUpdate = true; break; case CompositingUpdateOnScroll: if (m_compositingConsultsOverlap) checkForHierarchyUpdate = true; // Overlap can change with scrolling, so need to check for hierarchy updates. needGeometryUpdate = true; break; } if (!checkForHierarchyUpdate && !needGeometryUpdate) return; bool needHierarchyUpdate = m_compositingLayersNeedRebuild; if (!updateRoot || m_compositingConsultsOverlap) { // Only clear the flag if we're updating the entire hierarchy. m_compositingLayersNeedRebuild = false; updateRoot = rootRenderLayer(); } #if PROFILE_LAYER_REBUILD ++m_rootLayerUpdateCount; double startTime = WTF::currentTime(); #endif if (checkForHierarchyUpdate) { // Go through the layers in presentation order, so that we can compute which RenderLayers need compositing layers. // FIXME: we could maybe do this and the hierarchy udpate in one pass, but the parenting logic would be more complex. CompositingState compState(updateRoot); bool layersChanged = false; #if ENABLE(COMPOSITED_FIXED_ELEMENTS) compState.m_positionedSibling = false; compState.m_hasFixedElement = false; #endif #if ENABLE(ANDROID_OVERFLOW_SCROLL) compState.m_hasScrollableElement = false; #endif if (m_compositingConsultsOverlap) { OverlapMap overlapTestRequestMap; computeCompositingRequirements(updateRoot, &overlapTestRequestMap, compState, layersChanged); } else computeCompositingRequirements(updateRoot, 0, compState, layersChanged); needHierarchyUpdate |= layersChanged; } if (needHierarchyUpdate) { // Update the hierarchy of the compositing layers. CompositingState compState(updateRoot); Vector<GraphicsLayer*> childList; rebuildCompositingLayerTree(updateRoot, compState, childList); // Host the document layer in the RenderView's root layer. if (updateRoot == rootRenderLayer()) { if (childList.isEmpty()) destroyRootPlatformLayer(); else m_rootPlatformLayer->setChildren(childList); } } else if (needGeometryUpdate) { // We just need to do a geometry update. This is only used for position:fixed scrolling; // most of the time, geometry is updated via RenderLayer::styleChanged(). updateLayerTreeGeometry(updateRoot); } #if PROFILE_LAYER_REBUILD double endTime = WTF::currentTime(); if (updateRoot == rootRenderLayer()) fprintf(stderr, "Update %d: computeCompositingRequirements for the world took %fms\n", m_rootLayerUpdateCount, 1000.0 * (endTime - startTime)); #endif ASSERT(updateRoot || !m_compositingLayersNeedRebuild); if (!hasAcceleratedCompositing()) enableCompositingMode(false); } bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeRepaint shouldRepaint) { bool layerChanged = false; #if ENABLE(ANDROID_OVERFLOW_SCROLL) if (needsToBeComposited(layer) || layer->shouldComposite()) { #else if (needsToBeComposited(layer)) { #endif enableCompositingMode(); // 3D transforms turn off the testing of overlap. if (requiresCompositingForTransform(layer->renderer())) setCompositingConsultsOverlap(false); #if ENABLE(ANDROID_OVERFLOW_SCROLL) // If we are a child of a scrollable layer, ignore the overlap from the // scrollable layer as it can cause child layers to become composited // siblings and will not scroll with the main content layer. if (layer->hasOverflowParent()) setCompositingConsultsOverlap(false); #endif if (!layer->backing()) { // If we need to repaint, do so before making backing if (shouldRepaint == CompositingChangeRepaintNow) repaintOnCompositingChange(layer); layer->ensureBacking(); #if PLATFORM(MAC) && USE(CA) if (m_renderView->document()->settings()->acceleratedDrawingEnabled()) layer->backing()->graphicsLayer()->setAcceleratesDrawing(true); else if (layer->renderer()->isCanvas()) { HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(layer->renderer()->node()); if (canvas->renderingContext() && canvas->renderingContext()->isAccelerated()) layer->backing()->graphicsLayer()->setAcceleratesDrawing(true); } #endif layerChanged = true; } } else { if (layer->backing()) { // If we're removing backing on a reflection, clear the source GraphicsLayer's pointer to // its replica GraphicsLayer. In practice this should never happen because reflectee and reflection // are both either composited, or not composited. if (layer->isReflection()) { RenderLayer* sourceLayer = toRenderBoxModelObject(layer->renderer()->parent())->layer(); if (RenderLayerBacking* backing = sourceLayer->backing()) { ASSERT(backing->graphicsLayer()->replicaLayer() == layer->backing()->graphicsLayer()); backing->graphicsLayer()->setReplicatedByLayer(0); } } layer->clearBacking(); layerChanged = true; // The layer's cached repaints rects are relative to the repaint container, so change when // compositing changes; we need to update them here. layer->computeRepaintRects(); // If we need to repaint, do so now that we've removed the backing if (shouldRepaint == CompositingChangeRepaintNow) repaintOnCompositingChange(layer); } } #if ENABLE(VIDEO) if (layerChanged && layer->renderer()->isVideo()) { // If it's a video, give the media player a chance to hook up to the layer. RenderVideo* video = toRenderVideo(layer->renderer()); video->acceleratedRenderingStateChanged(); } #endif if (layerChanged && layer->renderer()->isRenderPart()) { RenderLayerCompositor* innerCompositor = frameContentsCompositor(toRenderPart(layer->renderer())); if (innerCompositor && innerCompositor->inCompositingMode()) innerCompositor->updateRootLayerAttachment(); } return layerChanged; } bool RenderLayerCompositor::updateLayerCompositingState(RenderLayer* layer, CompositingChangeRepaint shouldRepaint) { bool layerChanged = updateBacking(layer, shouldRepaint); // See if we need content or clipping layers. Methods called here should assume // that the compositing state of descendant layers has not been updated yet. if (layer->backing() && layer->backing()->updateGraphicsLayerConfiguration()) layerChanged = true; return layerChanged; } void RenderLayerCompositor::repaintOnCompositingChange(RenderLayer* layer) { // If the renderer is not attached yet, no need to repaint. if (layer->renderer() != m_renderView && !layer->renderer()->parent()) return; RenderBoxModelObject* repaintContainer = layer->renderer()->containerForRepaint(); if (!repaintContainer) repaintContainer = m_renderView; layer->repaintIncludingNonCompositingDescendants(repaintContainer); if (repaintContainer == m_renderView) { // The contents of this layer may be moving between the window // and a GraphicsLayer, so we need to make sure the window system // synchronizes those changes on the screen. m_renderView->frameView()->setNeedsOneShotDrawingSynchronization(); } } // The bounds of the GraphicsLayer created for a compositing layer is the union of the bounds of all the descendant // RenderLayers that are rendered by the composited RenderLayer. IntRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer) { if (!canBeComposited(layer)) return IntRect(); IntRect boundingBoxRect = layer->localBoundingBox(); if (layer->renderer()->isRoot()) { // If the root layer becomes composited (e.g. because some descendant with negative z-index is composited), // then it has to be big enough to cover the viewport in order to display the background. This is akin // to the code in RenderBox::paintRootBoxFillLayers(). if (m_renderView->frameView()) { int rw = m_renderView->frameView()->contentsWidth(); int rh = m_renderView->frameView()->contentsHeight(); boundingBoxRect.setWidth(max(boundingBoxRect.width(), rw - boundingBoxRect.x())); boundingBoxRect.setHeight(max(boundingBoxRect.height(), rh - boundingBoxRect.y())); } } IntRect unionBounds = boundingBoxRect; if (layer->renderer()->hasOverflowClip() || layer->renderer()->hasMask()) { int ancestorRelX = 0, ancestorRelY = 0; layer->convertToLayerCoords(ancestorLayer, ancestorRelX, ancestorRelY); boundingBoxRect.move(ancestorRelX, ancestorRelY); return boundingBoxRect; } if (RenderLayer* reflection = layer->reflectionLayer()) { if (!reflection->isComposited()) { IntRect childUnionBounds = calculateCompositedBounds(reflection, layer); unionBounds.unite(childUnionBounds); } } ASSERT(layer->isStackingContext() || (!layer->m_posZOrderList || layer->m_posZOrderList->size() == 0)); if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); if (!curLayer->isComposited()) { IntRect childUnionBounds = calculateCompositedBounds(curLayer, layer); unionBounds.unite(childUnionBounds); } } } if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); if (!curLayer->isComposited()) { IntRect childUnionBounds = calculateCompositedBounds(curLayer, layer); unionBounds.unite(childUnionBounds); } } } if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); if (!curLayer->isComposited()) { IntRect curAbsBounds = calculateCompositedBounds(curLayer, layer); unionBounds.unite(curAbsBounds); } } } if (layer->paintsWithTransform(PaintBehaviorNormal)) { TransformationMatrix* affineTrans = layer->transform(); boundingBoxRect = affineTrans->mapRect(boundingBoxRect); unionBounds = affineTrans->mapRect(unionBounds); } int ancestorRelX = 0, ancestorRelY = 0; layer->convertToLayerCoords(ancestorLayer, ancestorRelX, ancestorRelY); unionBounds.move(ancestorRelX, ancestorRelY); return unionBounds; } void RenderLayerCompositor::layerWasAdded(RenderLayer* /*parent*/, RenderLayer* /*child*/) { setCompositingLayersNeedRebuild(); } void RenderLayerCompositor::layerWillBeRemoved(RenderLayer* parent, RenderLayer* child) { if (!child->isComposited() || parent->renderer()->documentBeingDestroyed()) return; setCompositingParent(child, 0); RenderLayer* compLayer = parent->enclosingCompositingLayer(); if (compLayer) { ASSERT(compLayer->backing()); IntRect compBounds = child->backing()->compositedBounds(); int offsetX = 0, offsetY = 0; child->convertToLayerCoords(compLayer, offsetX, offsetY); compBounds.move(offsetX, offsetY); compLayer->setBackingNeedsRepaintInRect(compBounds); // The contents of this layer may be moving from a GraphicsLayer to the window, // so we need to make sure the window system synchronizes those changes on the screen. m_renderView->frameView()->setNeedsOneShotDrawingSynchronization(); } setCompositingLayersNeedRebuild(); } RenderLayer* RenderLayerCompositor::enclosingNonStackingClippingLayer(const RenderLayer* layer) const { for (RenderLayer* curr = layer->parent(); curr != 0; curr = curr->parent()) { if (curr->isStackingContext()) return 0; if (curr->renderer()->hasOverflowClip() || curr->renderer()->hasClip()) return curr; } return 0; } void RenderLayerCompositor::addToOverlapMap(OverlapMap& overlapMap, RenderLayer* layer, IntRect& layerBounds, bool& boundsComputed) { if (layer->isRootLayer()) return; if (!boundsComputed) { layerBounds = layer->renderer()->localToAbsoluteQuad(FloatRect(layer->localBoundingBox())).enclosingBoundingBox(); // Empty rects never intersect, but we need them to for the purposes of overlap testing. if (layerBounds.isEmpty()) layerBounds.setSize(IntSize(1, 1)); boundsComputed = true; } overlapMap.add(layer, layerBounds); } bool RenderLayerCompositor::overlapsCompositedLayers(OverlapMap& overlapMap, const IntRect& layerBounds) { RenderLayerCompositor::OverlapMap::const_iterator end = overlapMap.end(); for (RenderLayerCompositor::OverlapMap::const_iterator it = overlapMap.begin(); it != end; ++it) { const IntRect& bounds = it->second; if (layerBounds.intersects(bounds)) { return true; } } return false; } #if ENABLE(COMPOSITED_FIXED_ELEMENTS) bool RenderLayerCompositor::checkForPositionedElements(Vector<RenderLayer*>* list) { int listSize = list->size(); int haveFixedLayer = -1; bool fixedSibling = false; bool positionedSibling = false; #if 0 // For absolute positioned elements, we need to check if they are followed // by a composited element; if so, they also need to be composited, as the // layer display rendering might be incorrect (absolute elements being // removed from the flow). for (int i = 0; i < listSize; ++i) { RenderLayer* currentLayer = list->at(i); if (!needsToBeComposited(currentLayer) && !currentLayer->shouldComposite() && currentLayer->renderer()->isPositioned()) { positionedSibling = true; // check if there is a composited layer later, if so we should be // composited. for (int j = i + 1; j < listSize; ++j) { RenderLayer* layer = list->at(j); if (needsToBeComposited(layer)) { currentLayer->setShouldComposite(true); break; } } break; } } #endif // If we find a fixed layer, let's mark all the following layers as being // composited. The layers' surfaces will be merged if needed UI-side. for (int j = 0; j < listSize; ++j) { RenderLayer* currentLayer = list->at(j); // clear the composited flag first currentLayer->setShouldComposite(false); if (currentLayer->isFixed() && needsToBeComposited(currentLayer)) { // Ignore fixed layers with a width or height or 1 or less... IntRect currentLayerBounds = currentLayer->renderer()->localToAbsoluteQuad( FloatRect(currentLayer->localBoundingBox())).enclosingBoundingBox(); if (currentLayerBounds.width() > 1 && currentLayerBounds.height() > 1) { haveFixedLayer = j; fixedSibling = true; } continue; } if (haveFixedLayer != -1) currentLayer->setShouldComposite(true); } return positionedSibling || fixedSibling; } #endif // Recurse through the layers in z-index and overflow order (which is equivalent to painting order) // For the z-order children of a compositing layer: // If a child layers has a compositing layer, then all subsequent layers must // be compositing in order to render above that layer. // // If a child in the negative z-order list is compositing, then the layer itself // must be compositing so that its contents render over that child. // This implies that its positive z-index children must also be compositing. // void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, OverlapMap* overlapMap, struct CompositingState& compositingState, bool& layersChanged) { layer->updateLayerPosition(); layer->updateZOrderLists(); layer->updateNormalFlowList(); #if PLATFORM(ANDROID) RenderObject* renderer = layer->renderer(); bool intCom = requiresCompositingLayer(layer); layer->setIntrinsicallyComposited(intCom); #endif // Clear the flag layer->setHasCompositingDescendant(false); bool mustOverlapCompositedLayers = compositingState.m_subtreeIsCompositing; bool haveComputedBounds = false; IntRect absBounds; if (overlapMap && !overlapMap->isEmpty()) { // If we're testing for overlap, we only need to composite if we overlap something that is already composited. absBounds = layer->renderer()->localToAbsoluteQuad(FloatRect(layer->localBoundingBox())).enclosingBoundingBox(); // Empty rects never intersect, but we need them to for the purposes of overlap testing. if (absBounds.isEmpty()) absBounds.setSize(IntSize(1, 1)); haveComputedBounds = true; // If the current subtree is not compositing, and the layer is fully inside the current compositing bounnds, // there is no need to do the overlap test. This reduces the total number of the composited layers. if (compositingState.m_subtreeIsCompositing || !compositingState.m_compositingBounds.contains(absBounds)) mustOverlapCompositedLayers = overlapsCompositedLayers(*overlapMap, absBounds); } layer->setMustOverlapCompositedLayers(mustOverlapCompositedLayers); // The children of this layer don't need to composite, unless there is // a compositing layer among them, so start by inheriting the compositing // ancestor with m_subtreeIsCompositing set to false. CompositingState childState(compositingState.m_compositingAncestor); if (compositingState.m_subtreeIsCompositing) childState.m_compositingBounds = absBounds; else childState.m_compositingBounds = compositingState.m_compositingBounds; #ifndef NDEBUG ++childState.m_depth; #endif bool willBeComposited = needsToBeComposited(layer); #if 0 && ENABLE(COMPOSITED_FIXED_ELEMENTS) willBeComposited |= layer->shouldComposite(); layer->setMustOverlapCompositedLayers(layer->shouldComposite()); #endif #if ENABLE(ANDROID_OVERFLOW_SCROLL) // tell the parent it has scrollable descendants. if (layer->hasOverflowScroll()) compositingState.m_hasScrollableElement = true; #endif #if ENABLE(COMPOSITED_FIXED_ELEMENTS) if (layer->isFixed()) compositingState.m_hasFixedElement = true; #endif if (willBeComposited) { // Tell the parent it has compositing descendants. compositingState.m_subtreeIsCompositing = true; // This layer now acts as the ancestor for kids. childState.m_compositingAncestor = layer; childState.m_compositingBounds = absBounds; if (overlapMap) addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); } #if ENABLE(VIDEO) // Video is special. It's a replaced element with a content layer, but has shadow content // for the controller that must render in front. Without this, the controls fail to show // when the video element is a stacking context (e.g. due to opacity or transform). if (willBeComposited && layer->renderer()->isVideo()) childState.m_subtreeIsCompositing = true; #endif if (layer->isStackingContext()) { ASSERT(!layer->m_zOrderListsDirty); if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); #if ENABLE(COMPOSITED_FIXED_ELEMENTS) childState.m_positionedSibling = compositingState.m_positionedSibling; if (checkForPositionedElements(negZOrderList)) childState.m_positionedSibling = true; #endif for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); // If we have to make a layer for this child, make one now so we can have a contents layer // (since we need to ensure that the -ve z-order child renders underneath our contents). #ifdef ANDROID // Normally we try to reduce the number of layers by not promoting all fixed // or scrollable elements to their own compositing layer. But in the case that // we have such an element in the negative z-order, we must make it a layer // otherwise the content will be painted at a higher z-index. This breaks pages // that set a large image with a z-index of -1 to implement a background image, // for example. bool childRequiresCompositing = childState.m_hasFixedElement || childState.m_hasScrollableElement; if (!willBeComposited && (childState.m_subtreeIsCompositing || childRequiresCompositing)) { #else if (!willBeComposited && childState.m_subtreeIsCompositing) { #endif // make layer compositing layer->setMustOverlapCompositedLayers(true); childState.m_compositingAncestor = layer; if (overlapMap) addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); willBeComposited = true; } } } } ASSERT(!layer->m_normalFlowListDirty); if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); #if ENABLE(COMPOSITED_FIXED_ELEMENTS) childState.m_positionedSibling = compositingState.m_positionedSibling; if (checkForPositionedElements(normalFlowList)) childState.m_positionedSibling = true; #endif for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); } } if (layer->isStackingContext()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); #if ENABLE(COMPOSITED_FIXED_ELEMENTS) childState.m_positionedSibling = compositingState.m_positionedSibling; if (checkForPositionedElements(posZOrderList)) childState.m_positionedSibling = true; #endif for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); } } } // If we just entered compositing mode, the root will have become composited (as long as accelerated compositing is enabled). if (layer->isRootLayer()) { if (inCompositingMode() && m_hasAcceleratedCompositing) willBeComposited = true; } ASSERT(willBeComposited == needsToBeComposited(layer)); // If we have a software transform, and we have layers under us, we need to also // be composited. Also, if we have opacity < 1, then we need to be a layer so that // the child layers are opaque, then rendered with opacity on this layer. if (!willBeComposited && canBeComposited(layer) && childState.m_subtreeIsCompositing && requiresCompositingWhenDescendantsAreCompositing(layer->renderer())) { layer->setMustOverlapCompositedLayers(true); if (overlapMap) addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); willBeComposited = true; } ASSERT(willBeComposited == needsToBeComposited(layer)); if (layer->reflectionLayer()) layer->reflectionLayer()->setMustOverlapCompositedLayers(willBeComposited); // Subsequent layers in the parent stacking context also need to composite. if (childState.m_subtreeIsCompositing) compositingState.m_subtreeIsCompositing = true; #if ENABLE(COMPOSITED_FIXED_ELEMENTS) if (childState.m_hasFixedElement) compositingState.m_hasFixedElement = true; if (childState.m_positionedSibling) compositingState.m_positionedSibling = true; #endif #if ENABLE(ANDROID_OVERFLOW_SCROLL) if (childState.m_hasScrollableElement) compositingState.m_hasScrollableElement = true; #endif // Set the flag to say that this SC has compositing children. layer->setHasCompositingDescendant(childState.m_subtreeIsCompositing); // setHasCompositingDescendant() may have changed the answer to needsToBeComposited() when clipping, // so test that again. if (!willBeComposited && canBeComposited(layer) && clipsCompositingDescendants(layer)) { if (overlapMap) addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); willBeComposited = true; } // If we're back at the root, and no other layers need to be composited, and the root layer itself doesn't need // to be composited, then we can drop out of compositing mode altogether. #if ENABLE(ANDROID_OVERFLOW_SCROLL) // We also need to check that we don't have a scrollable layer, as this // would not have set the m_subtreeIsCompositing flag if (layer->isRootLayer() && !childState.m_subtreeIsCompositing && !childState.m_hasScrollableElement && !childState.m_positionedSibling && !childState.m_hasFixedElement && !requiresCompositingLayer(layer) && !m_forceCompositingMode) { #else if (layer->isRootLayer() && !childState.m_subtreeIsCompositing && !requiresCompositingLayer(layer) && !m_forceCompositingMode) { #endif enableCompositingMode(false); willBeComposited = false; } // If the layer is going into compositing mode, repaint its old location. ASSERT(willBeComposited == needsToBeComposited(layer)); if (!layer->isComposited() && willBeComposited) repaintOnCompositingChange(layer); // Update backing now, so that we can use isComposited() reliably during tree traversal in rebuildCompositingLayerTree(). if (updateBacking(layer, CompositingChangeRepaintNow)) layersChanged = true; if (layer->reflectionLayer() && updateLayerCompositingState(layer->reflectionLayer(), CompositingChangeRepaintNow)) layersChanged = true; } void RenderLayerCompositor::setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer) { ASSERT(!parentLayer || childLayer->ancestorCompositingLayer() == parentLayer); ASSERT(childLayer->isComposited()); // It's possible to be called with a parent that isn't yet composited when we're doing // partial updates as required by painting or hit testing. Just bail in that case; // we'll do a full layer update soon. if (!parentLayer || !parentLayer->isComposited()) return; if (parentLayer) { GraphicsLayer* hostingLayer = parentLayer->backing()->parentForSublayers(); GraphicsLayer* hostedLayer = childLayer->backing()->childForSuperlayers(); hostingLayer->addChild(hostedLayer); } else childLayer->backing()->childForSuperlayers()->removeFromParent(); } void RenderLayerCompositor::removeCompositedChildren(RenderLayer* layer) { ASSERT(layer->isComposited()); GraphicsLayer* hostingLayer = layer->backing()->parentForSublayers(); hostingLayer->removeAllChildren(); } #if ENABLE(VIDEO) bool RenderLayerCompositor::canAccelerateVideoRendering(RenderVideo* o) const { if (!m_hasAcceleratedCompositing) return false; return o->supportsAcceleratedRendering(); } #endif void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, const CompositingState& compositingState, Vector<GraphicsLayer*>& childLayersOfEnclosingLayer) { // Make the layer compositing if necessary, and set up clipping and content layers. // Note that we can only do work here that is independent of whether the descendant layers // have been processed. computeCompositingRequirements() will already have done the repaint if necessary. RenderLayerBacking* layerBacking = layer->backing(); if (layerBacking) { // The compositing state of all our children has been updated already, so now // we can compute and cache the composited bounds for this layer. layerBacking->updateCompositedBounds(); if (RenderLayer* reflection = layer->reflectionLayer()) { if (reflection->backing()) reflection->backing()->updateCompositedBounds(); } layerBacking->updateGraphicsLayerConfiguration(); layerBacking->updateGraphicsLayerGeometry(); if (!layer->parent()) updateRootLayerPosition(); } // If this layer has backing, then we are collecting its children, otherwise appending // to the compositing child list of an enclosing layer. Vector<GraphicsLayer*> layerChildren; Vector<GraphicsLayer*>& childList = layerBacking ? layerChildren : childLayersOfEnclosingLayer; CompositingState childState = compositingState; if (layer->isComposited()) childState.m_compositingAncestor = layer; #ifndef NDEBUG ++childState.m_depth; #endif // The children of this stacking context don't need to composite, unless there is // a compositing layer among them, so start by assuming false. childState.m_subtreeIsCompositing = false; if (layer->isStackingContext()) { ASSERT(!layer->m_zOrderListsDirty); if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); rebuildCompositingLayerTree(curLayer, childState, childList); } } // If a negative z-order child is compositing, we get a foreground layer which needs to get parented. if (layerBacking && layerBacking->foregroundLayer()) childList.append(layerBacking->foregroundLayer()); } ASSERT(!layer->m_normalFlowListDirty); if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); rebuildCompositingLayerTree(curLayer, childState, childList); } } if (layer->isStackingContext()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); rebuildCompositingLayerTree(curLayer, childState, childList); } } } if (layerBacking) { bool parented = false; if (layer->renderer()->isRenderPart()) parented = parentFrameContentLayers(toRenderPart(layer->renderer())); // If the layer has a clipping layer the overflow controls layers will be siblings of the clipping layer. // Otherwise, the overflow control layers are normal children. if (!layerBacking->hasClippingLayer()) { if (GraphicsLayer* overflowControlLayer = layerBacking->layerForHorizontalScrollbar()) { overflowControlLayer->removeFromParent(); layerChildren.append(overflowControlLayer); } if (GraphicsLayer* overflowControlLayer = layerBacking->layerForVerticalScrollbar()) { overflowControlLayer->removeFromParent(); layerChildren.append(overflowControlLayer); } if (GraphicsLayer* overflowControlLayer = layerBacking->layerForScrollCorner()) { overflowControlLayer->removeFromParent(); layerChildren.append(overflowControlLayer); } } if (!parented) layerBacking->parentForSublayers()->setChildren(layerChildren); #if ENABLE(FULLSCREEN_API) // For the sake of clients of the full screen renderer, don't reparent // the full screen layer out from under them if they're in the middle of // animating. if (layer->renderer()->isRenderFullScreen() && toRenderFullScreen(layer->renderer())->isAnimating()) return; #endif childLayersOfEnclosingLayer.append(layerBacking->childForSuperlayers()); } } void RenderLayerCompositor::frameViewDidChangeLocation(const IntPoint& contentsOffset) { if (m_overflowControlsHostLayer) m_overflowControlsHostLayer->setPosition(contentsOffset); } void RenderLayerCompositor::frameViewDidChangeSize() { if (m_clipLayer) { FrameView* frameView = m_renderView->frameView(); m_clipLayer->setSize(frameView->visibleContentRect(false /* exclude scrollbars */).size()); IntPoint scrollPosition = frameView->scrollPosition(); m_scrollLayer->setPosition(FloatPoint(-scrollPosition.x(), -scrollPosition.y())); updateOverflowControlsLayers(); } } void RenderLayerCompositor::frameViewDidScroll(const IntPoint& scrollPosition) { if (m_scrollLayer) m_scrollLayer->setPosition(FloatPoint(-scrollPosition.x(), -scrollPosition.y())); } String RenderLayerCompositor::layerTreeAsText(bool showDebugInfo) { if (compositingLayerUpdatePending()) updateCompositingLayers(); if (!m_rootPlatformLayer) return String(); // We skip dumping the scroll and clip layers to keep layerTreeAsText output // similar between platforms. return m_rootPlatformLayer->layerTreeAsText(showDebugInfo ? LayerTreeAsTextDebug : LayerTreeAsTextBehaviorNormal); } RenderLayerCompositor* RenderLayerCompositor::frameContentsCompositor(RenderPart* renderer) { if (!renderer->node()->isFrameOwnerElement()) return 0; HTMLFrameOwnerElement* element = static_cast<HTMLFrameOwnerElement*>(renderer->node()); if (Document* contentDocument = element->contentDocument()) { if (RenderView* view = contentDocument->renderView()) return view->compositor(); } return 0; } bool RenderLayerCompositor::parentFrameContentLayers(RenderPart* renderer) { RenderLayerCompositor* innerCompositor = frameContentsCompositor(renderer); if (!innerCompositor || !innerCompositor->inCompositingMode() || innerCompositor->rootLayerAttachment() != RootLayerAttachedViaEnclosingFrame) return false; RenderLayer* layer = renderer->layer(); if (!layer->isComposited()) return false; RenderLayerBacking* backing = layer->backing(); GraphicsLayer* hostingLayer = backing->parentForSublayers(); GraphicsLayer* rootLayer = innerCompositor->rootPlatformLayer(); if (hostingLayer->children().size() != 1 || hostingLayer->children()[0] != rootLayer) { hostingLayer->removeAllChildren(); hostingLayer->addChild(rootLayer); } return true; } // This just updates layer geometry without changing the hierarchy. void RenderLayerCompositor::updateLayerTreeGeometry(RenderLayer* layer) { if (RenderLayerBacking* layerBacking = layer->backing()) { // The compositing state of all our children has been updated already, so now // we can compute and cache the composited bounds for this layer. layerBacking->updateCompositedBounds(); if (RenderLayer* reflection = layer->reflectionLayer()) { if (reflection->backing()) reflection->backing()->updateCompositedBounds(); } layerBacking->updateGraphicsLayerConfiguration(); layerBacking->updateGraphicsLayerGeometry(); if (!layer->parent()) updateRootLayerPosition(); } if (layer->isStackingContext()) { ASSERT(!layer->m_zOrderListsDirty); if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) updateLayerTreeGeometry(negZOrderList->at(i)); } } ASSERT(!layer->m_normalFlowListDirty); if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) updateLayerTreeGeometry(normalFlowList->at(i)); } if (layer->isStackingContext()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) updateLayerTreeGeometry(posZOrderList->at(i)); } } } // Recurs down the RenderLayer tree until its finds the compositing descendants of compositingAncestor and updates their geometry. void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer* layer, RenderLayerBacking::UpdateDepth updateDepth) { if (layer != compositingAncestor) { if (RenderLayerBacking* layerBacking = layer->backing()) { layerBacking->updateCompositedBounds(); if (RenderLayer* reflection = layer->reflectionLayer()) { if (reflection->backing()) reflection->backing()->updateCompositedBounds(); } layerBacking->updateGraphicsLayerGeometry(); if (updateDepth == RenderLayerBacking::CompositingChildren) return; } } if (layer->reflectionLayer()) updateCompositingDescendantGeometry(compositingAncestor, layer->reflectionLayer(), updateDepth); if (!layer->hasCompositingDescendant()) return; if (layer->isStackingContext()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) updateCompositingDescendantGeometry(compositingAncestor, negZOrderList->at(i), updateDepth); } } if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) updateCompositingDescendantGeometry(compositingAncestor, normalFlowList->at(i), updateDepth); } if (layer->isStackingContext()) { if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) updateCompositingDescendantGeometry(compositingAncestor, posZOrderList->at(i), updateDepth); } } } void RenderLayerCompositor::repaintCompositedLayersAbsoluteRect(const IntRect& absRect) { recursiveRepaintLayerRect(rootRenderLayer(), absRect); } void RenderLayerCompositor::recursiveRepaintLayerRect(RenderLayer* layer, const IntRect& rect) { // FIXME: This method does not work correctly with transforms. if (layer->isComposited()) layer->setBackingNeedsRepaintInRect(rect); if (layer->hasCompositingDescendant()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); int x = 0; int y = 0; curLayer->convertToLayerCoords(layer, x, y); IntRect childRect(rect); childRect.move(-x, -y); recursiveRepaintLayerRect(curLayer, childRect); } } if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); int x = 0; int y = 0; curLayer->convertToLayerCoords(layer, x, y); IntRect childRect(rect); childRect.move(-x, -y); recursiveRepaintLayerRect(curLayer, childRect); } } } if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); int x = 0; int y = 0; curLayer->convertToLayerCoords(layer, x, y); IntRect childRect(rect); childRect.move(-x, -y); recursiveRepaintLayerRect(curLayer, childRect); } } } RenderLayer* RenderLayerCompositor::rootRenderLayer() const { return m_renderView->layer(); } GraphicsLayer* RenderLayerCompositor::rootPlatformLayer() const { if (m_overflowControlsHostLayer) return m_overflowControlsHostLayer.get(); return m_rootPlatformLayer.get(); } void RenderLayerCompositor::didMoveOnscreen() { if (!inCompositingMode() || m_rootLayerAttachment != RootLayerUnattached) return; RootLayerAttachment attachment = shouldPropagateCompositingToEnclosingFrame() ? RootLayerAttachedViaEnclosingFrame : RootLayerAttachedViaChromeClient; attachRootPlatformLayer(attachment); } void RenderLayerCompositor::willMoveOffscreen() { if (!inCompositingMode() || m_rootLayerAttachment == RootLayerUnattached) return; detachRootPlatformLayer(); } void RenderLayerCompositor::updateRootLayerPosition() { if (m_rootPlatformLayer) { m_rootPlatformLayer->setSize(FloatSize(m_renderView->docWidth(), m_renderView->docHeight())); m_rootPlatformLayer->setPosition(FloatPoint(m_renderView->docLeft(), m_renderView->docTop())); } if (m_clipLayer) { FrameView* frameView = m_renderView->frameView(); m_clipLayer->setSize(frameView->visibleContentRect(false /* exclude scrollbars */).size()); } } void RenderLayerCompositor::didStartAcceleratedAnimation(CSSPropertyID property) { // If an accelerated animation or transition runs, we have to turn off overlap checking because // we don't do layout for every frame, but we have to ensure that the layering is // correct between the animating object and other objects on the page. if (property == CSSPropertyWebkitTransform) setCompositingConsultsOverlap(false); } bool RenderLayerCompositor::has3DContent() const { return layerHas3DContent(rootRenderLayer()); } bool RenderLayerCompositor::allowsIndependentlyCompositedFrames(const FrameView* view) { #if PLATFORM(MAC) // frames are only independently composited in Mac pre-WebKit2. return view->platformWidget(); #endif return false; } bool RenderLayerCompositor::shouldPropagateCompositingToEnclosingFrame() const { #if PLATFORM(ANDROID) if (enclosingFrameElement() && !allowsIndependentlyCompositedFrames(m_renderView->frameView())) return true; #endif // Parent document content needs to be able to render on top of a composited frame, so correct behavior // is to have the parent document become composited too. However, this can cause problems on platforms that // use native views for frames (like Mac), so disable that behavior on those platforms for now. HTMLFrameOwnerElement* ownerElement = enclosingFrameElement(); RenderObject* renderer = ownerElement ? ownerElement->renderer() : 0; // If we are the top-level frame, don't propagate. if (!ownerElement) return false; if (!allowsIndependentlyCompositedFrames(m_renderView->frameView())) return true; if (!renderer || !renderer->isRenderPart()) return false; // On Mac, only propagate compositing if the frame is overlapped in the parent // document, or the parent is already compositing, or the main frame is scaled. Frame* frame = m_renderView->frameView()->frame(); Page* page = frame ? frame->page() : 0; if (page->mainFrame()->pageScaleFactor() != 1) return true; RenderPart* frameRenderer = toRenderPart(renderer); if (frameRenderer->widget()) { ASSERT(frameRenderer->widget()->isFrameView()); FrameView* view = static_cast<FrameView*>(frameRenderer->widget()); if (view->isOverlappedIncludingAncestors() || view->hasCompositingAncestor()) return true; } return false; } HTMLFrameOwnerElement* RenderLayerCompositor::enclosingFrameElement() const { if (HTMLFrameOwnerElement* ownerElement = m_renderView->document()->ownerElement()) return (ownerElement->hasTagName(iframeTag) || ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(objectTag)) ? ownerElement : 0; return 0; } bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const { if (!canBeComposited(layer)) return false; // The root layer always has a compositing layer, but it may not have backing. #if PLATFORM(ANDROID) // If we do not have a root platform layer, don't use the // mustOverlapCompositedLayers() as a cue that this layer needs to be // composited -- the layers tree has been detached. // Otherwise we can end up in a cycle where updateBacking() switches composited // mode on because a layer has mustOverlapCompositedLayers() (by calling // enableCompositingMode()), while computeCompositingRequirements() will // (correctly) say that we do not need to be in composited mode and turns it // off, rince and repeat... return requiresCompositingLayer(layer) || (m_rootPlatformLayer && layer->mustOverlapCompositedLayers()) || (inCompositingMode() && layer->isRootLayer()); #else return requiresCompositingLayer(layer) || layer->mustOverlapCompositedLayers() || (inCompositingMode() && layer->isRootLayer()); #endif } #if PLATFORM(ANDROID) bool RenderLayerCompositor::requiresCompositingForAndroidLayers(const RenderLayer* layer) const { #if ENABLE(ANDROID_OVERFLOW_SCROLL) if (layer->hasOverflowScroll()) return true; if (layer->isRootLayer() && m_renderView->frameView()->hasOverflowScroll()) return true; #endif #if ENABLE(COMPOSITED_FIXED_ELEMENTS) // Enable composited layers (for fixed elements) if (layer->isFixed()) return true; #endif if (layer->renderer()->isCanvas()) return true; if (layer->renderer()->style()->hasFixedBackgroundImage()) return true; return false; } #endif // Note: this specifies whether the RL needs a compositing layer for intrinsic reasons. // Use needsToBeComposited() to determine if a RL actually needs a compositing layer. // static bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) const { RenderObject* renderer = layer->renderer(); // The compositing state of a reflection should match that of its reflected layer. if (layer->isReflection()) { renderer = renderer->parent(); // The RenderReplica's parent is the object being reflected. layer = toRenderBoxModelObject(renderer)->layer(); } return requiresCompositingForTransform(renderer) #if PLATFORM(ANDROID) || requiresCompositingForAndroidLayers(layer) #endif || requiresCompositingForVideo(renderer) || requiresCompositingForCanvas(renderer) || requiresCompositingForPlugin(renderer) || requiresCompositingForFrame(renderer) || (canRender3DTransforms() && renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden) || clipsCompositingDescendants(layer) || requiresCompositingForAnimation(renderer) || requiresCompositingForFullScreen(renderer); } bool RenderLayerCompositor::canBeComposited(const RenderLayer* layer) const { return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer(); } // Return true if the given layer has some ancestor in the RenderLayer hierarchy that clips, // up to the enclosing compositing ancestor. This is required because compositing layers are parented // according to the z-order hierarchy, yet clipping goes down the renderer hierarchy. // Thus, a RenderLayer can be clipped by a RenderLayer that is an ancestor in the renderer hierarchy, // but a sibling in the z-order hierarchy. bool RenderLayerCompositor::clippedByAncestor(RenderLayer* layer) const { if (!layer->isComposited() || !layer->parent()) return false; RenderLayer* compositingAncestor = layer->ancestorCompositingLayer(); if (!compositingAncestor) return false; // If the compositingAncestor clips, that will be taken care of by clipsCompositingDescendants(), // so we only care about clipping between its first child that is our ancestor (the computeClipRoot), // and layer. RenderLayer* computeClipRoot = 0; RenderLayer* curr = layer; while (curr) { RenderLayer* next = curr->parent(); if (next == compositingAncestor) { computeClipRoot = curr; break; } curr = next; } if (!computeClipRoot || computeClipRoot == layer) return false; IntRect backgroundRect = layer->backgroundClipRect(computeClipRoot, true); return backgroundRect != PaintInfo::infiniteRect(); } // Return true if the given layer is a stacking context and has compositing child // layers that it needs to clip. In this case we insert a clipping GraphicsLayer // into the hierarchy between this layer and its children in the z-order hierarchy. bool RenderLayerCompositor::clipsCompositingDescendants(const RenderLayer* layer) const { #if ENABLE(ANDROID_OVERFLOW_SCROLL) if (layer->hasOverflowScroll()) return false; #endif return layer->hasCompositingDescendant() && (layer->renderer()->hasOverflowClip() || layer->renderer()->hasClip()); } bool RenderLayerCompositor::requiresCompositingForTransform(RenderObject* renderer) const { if (!(m_compositingTriggers & ChromeClient::ThreeDTransformTrigger)) return false; RenderStyle* style = renderer->style(); // Note that we ask the renderer if it has a transform, because the style may have transforms, // but the renderer may be an inline that doesn't suppport them. return renderer->hasTransform() && (style->transform().has3DOperation() || style->transformStyle3D() == TransformStyle3DPreserve3D || style->hasPerspective()); } bool RenderLayerCompositor::requiresCompositingForVideo(RenderObject* renderer) const { if (!(m_compositingTriggers & ChromeClient::VideoTrigger)) return false; #if ENABLE(VIDEO) if (renderer->isVideo()) { RenderVideo* video = toRenderVideo(renderer); return video->shouldDisplayVideo() && canAccelerateVideoRendering(video); } #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) else if (renderer->isRenderPart()) { if (!m_hasAcceleratedCompositing) return false; Node* node = renderer->node(); if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) return false; HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node); return mediaElement->player() ? mediaElement->player()->supportsAcceleratedRendering() : false; } #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) #else UNUSED_PARAM(renderer); #endif return false; } bool RenderLayerCompositor::requiresCompositingForCanvas(RenderObject* renderer) const { if (!(m_compositingTriggers & ChromeClient::CanvasTrigger)) return false; if (renderer->isCanvas()) { HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(renderer->node()); return canvas->renderingContext() && canvas->renderingContext()->isAccelerated(); } return false; } bool RenderLayerCompositor::requiresCompositingForPlugin(RenderObject* renderer) const { if (!(m_compositingTriggers & ChromeClient::PluginTrigger)) return false; bool composite = (renderer->isEmbeddedObject() && toRenderEmbeddedObject(renderer)->allowsAcceleratedCompositing()) || (renderer->isApplet() && toRenderApplet(renderer)->allowsAcceleratedCompositing()); if (!composite) return false; m_compositingDependsOnGeometry = true; RenderWidget* pluginRenderer = toRenderWidget(renderer); // If we can't reliably know the size of the plugin yet, don't change compositing state. if (pluginRenderer->needsLayout()) return pluginRenderer->hasLayer() && pluginRenderer->layer()->isComposited(); // Don't go into compositing mode if height or width are zero, or size is 1x1. IntRect contentBox = pluginRenderer->contentBoxRect(); #if PLATFORM(ANDROID) // allow all plugins including 1x1 to be composited, so that they are drawn, // and acquire an ANativeWindow on the UI thread return contentBox.height() * contentBox.width() > 0; #else return contentBox.height() * contentBox.width() > 1; #endif } bool RenderLayerCompositor::requiresCompositingForFrame(RenderObject* renderer) const { if (!renderer->isRenderPart()) return false; RenderPart* frameRenderer = toRenderPart(renderer); if (!frameRenderer->requiresAcceleratedCompositing()) return false; m_compositingDependsOnGeometry = true; RenderLayerCompositor* innerCompositor = frameContentsCompositor(frameRenderer); if (!innerCompositor || !innerCompositor->shouldPropagateCompositingToEnclosingFrame()) return false; // If we can't reliably know the size of the iframe yet, don't change compositing state. if (renderer->needsLayout()) return frameRenderer->hasLayer() && frameRenderer->layer()->isComposited(); // Don't go into compositing mode if height or width are zero. IntRect contentBox = frameRenderer->contentBoxRect(); return contentBox.height() * contentBox.width() > 0; } bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* renderer) const { if (!(m_compositingTriggers & ChromeClient::AnimationTrigger)) return false; if (AnimationController* animController = renderer->animation()) { #if PLATFORM(ANDROID) // android renders an opacity animation much faster if it's composited return (animController->isRunningAnimationOnRenderer(renderer, CSSPropertyOpacity)) #else return (animController->isRunningAnimationOnRenderer(renderer, CSSPropertyOpacity) && inCompositingMode()) #endif || animController->isRunningAnimationOnRenderer(renderer, CSSPropertyWebkitTransform); } return false; } bool RenderLayerCompositor::requiresCompositingWhenDescendantsAreCompositing(RenderObject* renderer) const { return renderer->hasTransform() || renderer->isTransparent() || renderer->hasMask() || renderer->hasReflection(); } bool RenderLayerCompositor::requiresCompositingForFullScreen(RenderObject* renderer) const { #if ENABLE(FULLSCREEN_API) return renderer->isRenderFullScreen() && toRenderFullScreen(renderer)->isAnimating(); #else UNUSED_PARAM(renderer); return false; #endif } // If an element has negative z-index children, those children render in front of the // layer background, so we need an extra 'contents' layer for the foreground of the layer // object. bool RenderLayerCompositor::needsContentsCompositingLayer(const RenderLayer* layer) const { return (layer->m_negZOrderList && layer->m_negZOrderList->size() > 0); } bool RenderLayerCompositor::requiresScrollLayer(RootLayerAttachment attachment) const { // We need to handle our own scrolling if we're: return !m_renderView->frameView()->platformWidget() // viewless (i.e. non-Mac, or Mac in WebKit2) || attachment == RootLayerAttachedViaEnclosingFrame; // a composited frame on Mac } static void paintScrollbar(Scrollbar* scrollbar, GraphicsContext& context, const IntRect& clip) { if (!scrollbar) return; context.save(); const IntRect& scrollbarRect = scrollbar->frameRect(); context.translate(-scrollbarRect.x(), -scrollbarRect.y()); IntRect transformedClip = clip; transformedClip.move(scrollbarRect.x(), scrollbarRect.y()); scrollbar->paint(&context, transformedClip); context.restore(); } void RenderLayerCompositor::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect& clip) { if (graphicsLayer == layerForHorizontalScrollbar()) paintScrollbar(m_renderView->frameView()->horizontalScrollbar(), context, clip); else if (graphicsLayer == layerForVerticalScrollbar()) paintScrollbar(m_renderView->frameView()->verticalScrollbar(), context, clip); else if (graphicsLayer == layerForScrollCorner()) { const IntRect& scrollCorner = m_renderView->frameView()->scrollCornerRect(); context.save(); context.translate(-scrollCorner.x(), -scrollCorner.y()); IntRect transformedClip = clip; transformedClip.move(scrollCorner.x(), scrollCorner.y()); m_renderView->frameView()->paintScrollCorner(&context, transformedClip); context.restore(); } } static bool shouldCompositeOverflowControls(ScrollView* view) { if (view->platformWidget()) return false; #if !PLATFORM(CHROMIUM) if (!view->hasOverlayScrollbars()) return false; #endif return true; } bool RenderLayerCompositor::requiresHorizontalScrollbarLayer() const { ScrollView* view = m_renderView->frameView(); return shouldCompositeOverflowControls(view) && view->horizontalScrollbar(); } bool RenderLayerCompositor::requiresVerticalScrollbarLayer() const { ScrollView* view = m_renderView->frameView(); return shouldCompositeOverflowControls(view) && view->verticalScrollbar(); } bool RenderLayerCompositor::requiresScrollCornerLayer() const { ScrollView* view = m_renderView->frameView(); return shouldCompositeOverflowControls(view) && view->isScrollCornerVisible(); } void RenderLayerCompositor::updateOverflowControlsLayers() { bool layersChanged = false; if (requiresHorizontalScrollbarLayer()) { m_layerForHorizontalScrollbar = GraphicsLayer::create(this); #ifndef NDEBUG m_layerForHorizontalScrollbar->setName("horizontal scrollbar"); #endif m_overflowControlsHostLayer->addChild(m_layerForHorizontalScrollbar.get()); layersChanged = true; } else if (m_layerForHorizontalScrollbar) { m_layerForHorizontalScrollbar->removeFromParent(); m_layerForHorizontalScrollbar = 0; layersChanged = true; } if (requiresVerticalScrollbarLayer()) { m_layerForVerticalScrollbar = GraphicsLayer::create(this); #ifndef NDEBUG m_layerForVerticalScrollbar->setName("vertical scrollbar"); #endif m_overflowControlsHostLayer->addChild(m_layerForVerticalScrollbar.get()); layersChanged = true; } else if (m_layerForVerticalScrollbar) { m_layerForVerticalScrollbar->removeFromParent(); m_layerForVerticalScrollbar = 0; layersChanged = true; } if (requiresScrollCornerLayer()) { m_layerForScrollCorner = GraphicsLayer::create(this); #ifndef NDEBUG m_layerForScrollCorner->setName("scroll corner"); #endif m_overflowControlsHostLayer->addChild(m_layerForScrollCorner.get()); layersChanged = true; } else if (m_layerForScrollCorner) { m_layerForScrollCorner->removeFromParent(); m_layerForScrollCorner = 0; layersChanged = true; } if (layersChanged) m_renderView->frameView()->positionScrollbarLayers(); } void RenderLayerCompositor::ensureRootPlatformLayer() { RootLayerAttachment expectedAttachment = shouldPropagateCompositingToEnclosingFrame() ? RootLayerAttachedViaEnclosingFrame : RootLayerAttachedViaChromeClient; if (expectedAttachment == m_rootLayerAttachment) return; if (!m_rootPlatformLayer) { m_rootPlatformLayer = GraphicsLayer::create(0); #ifndef NDEBUG m_rootPlatformLayer->setName("Root platform"); #endif m_rootPlatformLayer->setSize(FloatSize(m_renderView->maxXLayoutOverflow(), m_renderView->maxYLayoutOverflow())); m_rootPlatformLayer->setPosition(FloatPoint()); // Need to clip to prevent transformed content showing outside this frame m_rootPlatformLayer->setMasksToBounds(true); } if (requiresScrollLayer(expectedAttachment)) { if (!m_overflowControlsHostLayer) { ASSERT(!m_scrollLayer); ASSERT(!m_clipLayer); // Create a layer to host the clipping layer and the overflow controls layers. m_overflowControlsHostLayer = GraphicsLayer::create(0); #ifndef NDEBUG m_overflowControlsHostLayer->setName("overflow controls host"); #endif // Create a clipping layer if this is an iframe m_clipLayer = GraphicsLayer::create(this); #ifndef NDEBUG m_clipLayer->setName("iframe Clipping"); #endif m_clipLayer->setMasksToBounds(true); m_scrollLayer = GraphicsLayer::create(this); #ifndef NDEBUG m_scrollLayer->setName("iframe scrolling"); #endif // Hook them up m_overflowControlsHostLayer->addChild(m_clipLayer.get()); m_clipLayer->addChild(m_scrollLayer.get()); m_scrollLayer->addChild(m_rootPlatformLayer.get()); frameViewDidChangeSize(); frameViewDidScroll(m_renderView->frameView()->scrollPosition()); } } else { if (m_overflowControlsHostLayer) { m_overflowControlsHostLayer = 0; m_clipLayer = 0; m_scrollLayer = 0; } } // Check to see if we have to change the attachment if (m_rootLayerAttachment != RootLayerUnattached) detachRootPlatformLayer(); attachRootPlatformLayer(expectedAttachment); } void RenderLayerCompositor::destroyRootPlatformLayer() { if (!m_rootPlatformLayer) return; detachRootPlatformLayer(); if (m_layerForHorizontalScrollbar) { m_layerForHorizontalScrollbar->removeFromParent(); m_layerForHorizontalScrollbar = 0; if (Scrollbar* horizontalScrollbar = m_renderView->frameView()->verticalScrollbar()) m_renderView->frameView()->invalidateScrollbar(horizontalScrollbar, IntRect(IntPoint(0, 0), horizontalScrollbar->frameRect().size())); } if (m_layerForVerticalScrollbar) { m_layerForVerticalScrollbar->removeFromParent(); m_layerForVerticalScrollbar = 0; if (Scrollbar* verticalScrollbar = m_renderView->frameView()->verticalScrollbar()) m_renderView->frameView()->invalidateScrollbar(verticalScrollbar, IntRect(IntPoint(0, 0), verticalScrollbar->frameRect().size())); } if (m_layerForScrollCorner) { m_layerForScrollCorner = 0; m_renderView->frameView()->invalidateScrollCorner(); } if (m_overflowControlsHostLayer) { m_overflowControlsHostLayer = 0; m_clipLayer = 0; m_scrollLayer = 0; } ASSERT(!m_scrollLayer); m_rootPlatformLayer = 0; } void RenderLayerCompositor::attachRootPlatformLayer(RootLayerAttachment attachment) { if (!m_rootPlatformLayer) return; switch (attachment) { case RootLayerUnattached: ASSERT_NOT_REACHED(); break; case RootLayerAttachedViaChromeClient: { Frame* frame = m_renderView->frameView()->frame(); Page* page = frame ? frame->page() : 0; if (!page) return; page->chrome()->client()->attachRootGraphicsLayer(frame, rootPlatformLayer()); break; } case RootLayerAttachedViaEnclosingFrame: { // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration() // for the frame's renderer in the parent document. scheduleNeedsStyleRecalc(m_renderView->document()->ownerElement()); break; } } m_rootLayerAttachment = attachment; rootLayerAttachmentChanged(); } void RenderLayerCompositor::detachRootPlatformLayer() { if (!m_rootPlatformLayer || m_rootLayerAttachment == RootLayerUnattached) return; switch (m_rootLayerAttachment) { case RootLayerAttachedViaEnclosingFrame: { // The layer will get unhooked up via RenderLayerBacking::updateGraphicsLayerConfiguration() // for the frame's renderer in the parent document. if (m_overflowControlsHostLayer) m_overflowControlsHostLayer->removeFromParent(); else m_rootPlatformLayer->removeFromParent(); if (HTMLFrameOwnerElement* ownerElement = m_renderView->document()->ownerElement()) scheduleNeedsStyleRecalc(ownerElement); break; } case RootLayerAttachedViaChromeClient: { Frame* frame = m_renderView->frameView()->frame(); Page* page = frame ? frame->page() : 0; if (!page) return; page->chrome()->client()->attachRootGraphicsLayer(frame, 0); } break; case RootLayerUnattached: break; } m_rootLayerAttachment = RootLayerUnattached; rootLayerAttachmentChanged(); } void RenderLayerCompositor::updateRootLayerAttachment() { ensureRootPlatformLayer(); } void RenderLayerCompositor::rootLayerAttachmentChanged() { // The attachment can affect whether the RenderView layer's paintingGoesToWindow() behavior, // so call updateGraphicsLayerGeometry() to udpate that. RenderLayer* layer = m_renderView->layer(); if (RenderLayerBacking* backing = layer ? layer->backing() : 0) backing->updateDrawsContent(); } static void needsStyleRecalcCallback(Node* node) { node->setNeedsStyleRecalc(SyntheticStyleChange); } void RenderLayerCompositor::scheduleNeedsStyleRecalc(Element* element) { if (ContainerNode::postAttachCallbacksAreSuspended()) ContainerNode::queuePostAttachCallback(needsStyleRecalcCallback, element); else element->setNeedsStyleRecalc(SyntheticStyleChange); } // IFrames are special, because we hook compositing layers together across iframe boundaries // when both parent and iframe content are composited. So when this frame becomes composited, we have // to use a synthetic style change to get the iframes into RenderLayers in order to allow them to composite. void RenderLayerCompositor::notifyIFramesOfCompositingChange() { Frame* frame = m_renderView->frameView() ? m_renderView->frameView()->frame() : 0; if (!frame) return; for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->traverseNext(frame)) { if (child->document() && child->document()->ownerElement()) scheduleNeedsStyleRecalc(child->document()->ownerElement()); } // Compositing also affects the answer to RenderIFrame::requiresAcceleratedCompositing(), so // we need to schedule a style recalc in our parent document. if (HTMLFrameOwnerElement* ownerElement = m_renderView->document()->ownerElement()) scheduleNeedsStyleRecalc(ownerElement); } bool RenderLayerCompositor::layerHas3DContent(const RenderLayer* layer) const { const RenderStyle* style = layer->renderer()->style(); if (style && (style->transformStyle3D() == TransformStyle3DPreserve3D || style->hasPerspective() || style->transform().has3DOperation())) return true; if (layer->isStackingContext()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = negZOrderList->at(i); if (layerHas3DContent(curLayer)) return true; } } if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = posZOrderList->at(i); if (layerHas3DContent(curLayer)) return true; } } } if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) { RenderLayer* curLayer = normalFlowList->at(i); if (layerHas3DContent(curLayer)) return true; } } return false; } void RenderLayerCompositor::updateContentsScale(float scale, RenderLayer* layer) { if (!layer) layer = rootRenderLayer(); layer->updateContentsScale(scale); if (layer->isStackingContext()) { if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { size_t listSize = negZOrderList->size(); for (size_t i = 0; i < listSize; ++i) updateContentsScale(scale, negZOrderList->at(i)); } if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { size_t listSize = posZOrderList->size(); for (size_t i = 0; i < listSize; ++i) updateContentsScale(scale, posZOrderList->at(i)); } } if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { size_t listSize = normalFlowList->size(); for (size_t i = 0; i < listSize; ++i) updateContentsScale(scale, normalFlowList->at(i)); } } } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING)