| /* |
| * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. |
| * |
| * Portions are Copyright (C) 1998 Netscape Communications Corporation. |
| * |
| * Other contributors: |
| * Robert O'Callahan <roc+@cs.cmu.edu> |
| * David Baron <dbaron@fas.harvard.edu> |
| * Christian Biesinger <cbiesinger@web.de> |
| * Randall Jesup <rjesup@wgate.com> |
| * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> |
| * Josh Soref <timeless@mac.com> |
| * Boris Zbarsky <bzbarsky@mit.edu> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| * Alternatively, the contents of this file may be used under the terms |
| * of either the Mozilla Public License Version 1.1, found at |
| * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public |
| * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html |
| * (the "GPL"), in which case the provisions of the MPL or the GPL are |
| * applicable instead of those above. If you wish to allow use of your |
| * version of this file only under the terms of one of those two |
| * licenses (the MPL or the GPL) and not to allow others to use your |
| * version of this file under the LGPL, indicate your decision by |
| * deletingthe provisions above and replace them with the notice and |
| * other provisions required by the MPL or the GPL, as the case may be. |
| * If you do not delete the provisions above, a recipient may use your |
| * version of this file under any of the LGPL, the MPL or the GPL. |
| */ |
| |
| #include "config.h" |
| #include "core/rendering/RenderLayer.h" |
| |
| #include "CSSPropertyNames.h" |
| #include "HTMLNames.h" |
| #include "RuntimeEnabledFeatures.h" |
| #include "SVGNames.h" |
| #include "core/css/PseudoStyleRequest.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/DocumentEventQueue.h" |
| #include "core/dom/shadow/ShadowRoot.h" |
| #include "core/html/HTMLFrameElement.h" |
| #include "core/html/HTMLFrameOwnerElement.h" |
| #include "core/page/EventHandler.h" |
| #include "core/page/FocusController.h" |
| #include "core/page/Frame.h" |
| #include "core/page/FrameView.h" |
| #include "core/page/Page.h" |
| #include "core/page/Settings.h" |
| #include "core/page/UseCounter.h" |
| #include "core/page/animation/AnimationController.h" |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| #include "core/platform/HistogramSupport.h" |
| #include "core/platform/Partitions.h" |
| #include "core/platform/PlatformGestureEvent.h" |
| #include "core/platform/PlatformMouseEvent.h" |
| #include "core/platform/ScrollAnimator.h" |
| #include "core/platform/Scrollbar.h" |
| #include "core/platform/ScrollbarTheme.h" |
| #include "core/platform/chromium/TraceEvent.h" |
| #include "core/platform/graphics/FloatPoint3D.h" |
| #include "core/platform/graphics/FloatRect.h" |
| #include "core/platform/graphics/GraphicsContextStateSaver.h" |
| #include "core/platform/graphics/filters/ReferenceFilter.h" |
| #include "core/platform/graphics/filters/SourceGraphic.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterGlobalContext.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterOperation.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterValidatedProgram.h" |
| #include "core/platform/graphics/filters/custom/ValidatedCustomFilterOperation.h" |
| #include "core/platform/graphics/transforms/ScaleTransformOperation.h" |
| #include "core/platform/graphics/transforms/TransformationMatrix.h" |
| #include "core/platform/graphics/transforms/TranslateTransformOperation.h" |
| #include "core/rendering/ColumnInfo.h" |
| #include "core/rendering/FilterEffectRenderer.h" |
| #include "core/rendering/HitTestRequest.h" |
| #include "core/rendering/HitTestResult.h" |
| #include "core/rendering/HitTestingTransformState.h" |
| #include "core/rendering/RenderFlowThread.h" |
| #include "core/rendering/RenderGeometryMap.h" |
| #include "core/rendering/RenderInline.h" |
| #include "core/rendering/RenderLayerBacking.h" |
| #include "core/rendering/RenderLayerCompositor.h" |
| #include "core/rendering/RenderReplica.h" |
| #include "core/rendering/RenderScrollbar.h" |
| #include "core/rendering/RenderScrollbarPart.h" |
| #include "core/rendering/RenderTreeAsText.h" |
| #include "core/rendering/RenderView.h" |
| #include "core/rendering/svg/ReferenceFilterBuilder.h" |
| #include "core/rendering/svg/RenderSVGResourceClipper.h" |
| #include "wtf/StdLibExtras.h" |
| #include "wtf/UnusedParam.h" |
| #include "wtf/text/CString.h" |
| |
| #define MIN_INTERSECT_FOR_REVEAL 32 |
| |
| using namespace std; |
| |
| namespace WebCore { |
| |
| using namespace HTMLNames; |
| |
| const int MinimumWidthWhileResizing = 100; |
| const int MinimumHeightWhileResizing = 40; |
| const int ResizerControlExpandRatioForTouch = 2; |
| |
| bool ClipRect::intersects(const HitTestLocation& hitTestLocation) const |
| { |
| return hitTestLocation.intersects(m_rect); |
| } |
| |
| RenderLayer::RenderLayer(RenderLayerModelObject* renderer) |
| : m_inResizeMode(false) |
| , m_normalFlowListDirty(true) |
| , m_hasSelfPaintingLayerDescendant(false) |
| , m_hasSelfPaintingLayerDescendantDirty(false) |
| , m_hasOutOfFlowPositionedDescendant(false) |
| , m_hasOutOfFlowPositionedDescendantDirty(true) |
| , m_hasUnclippedDescendant(false) |
| , m_isUnclippedDescendant(false) |
| , m_needsCompositedScrolling(false) |
| , m_canBePromotedToStackingContainer(false) |
| , m_canBePromotedToStackingContainerDirty(true) |
| , m_isRootLayer(renderer->isRenderView()) |
| , m_usedTransparency(false) |
| , m_paintingInsideReflection(false) |
| , m_inOverflowRelayout(false) |
| , m_repaintStatus(NeedsNormalRepaint) |
| , m_visibleContentStatusDirty(true) |
| , m_hasVisibleContent(false) |
| , m_visibleDescendantStatusDirty(false) |
| , m_hasVisibleDescendant(false) |
| , m_isPaginated(false) |
| , m_3DTransformedDescendantStatusDirty(true) |
| , m_has3DTransformedDescendant(false) |
| , m_containsDirtyOverlayScrollbars(false) |
| #if !ASSERT_DISABLED |
| , m_layerListMutationAllowed(true) |
| #endif |
| , m_canSkipRepaintRectsUpdateOnScroll(renderer->isTableCell()) |
| , m_hasFilterInfo(false) |
| , m_blendMode(BlendModeNormal) |
| , m_renderer(renderer) |
| , m_parent(0) |
| , m_previous(0) |
| , m_next(0) |
| , m_first(0) |
| , m_last(0) |
| , m_staticInlinePosition(0) |
| , m_staticBlockPosition(0) |
| , m_reflection(0) |
| , m_scrollCorner(0) |
| , m_resizer(0) |
| , m_enclosingPaginationLayer(0) |
| , m_forceNeedsCompositedScrolling(DoNotForceCompositedScrolling) |
| // FIXME: We could lazily allocate our ScrollableArea based on style properties |
| // ('overflow', ...) but for now, we are always allocating it as it's safer. |
| , m_scrollableArea(adoptPtr(new RenderLayerScrollableArea(this))) |
| |
| { |
| m_isNormalFlowOnly = shouldBeNormalFlowOnly(); |
| m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); |
| |
| // Non-stacking containers should have empty z-order lists. As this is already the case, |
| // there is no need to dirty / recompute these lists. |
| m_zOrderListsDirty = isStackingContainer(); |
| |
| if (!renderer->firstChild() && renderer->style()) { |
| m_visibleContentStatusDirty = false; |
| m_hasVisibleContent = renderer->style()->visibility() == VISIBLE; |
| } |
| |
| updateResizerAreaSet(); |
| } |
| |
| RenderLayer::~RenderLayer() |
| { |
| if (inResizeMode() && !renderer()->documentBeingDestroyed()) { |
| if (Frame* frame = renderer()->frame()) |
| frame->eventHandler()->resizeLayerDestroyed(); |
| } |
| |
| if (Frame* frame = renderer()->frame()) { |
| if (FrameView* frameView = frame->view()) |
| frameView->removeResizerArea(this); |
| } |
| |
| if (!m_renderer->documentBeingDestroyed()) |
| compositor()->removeOutOfFlowPositionedLayer(this); |
| |
| destroyScrollbar(HorizontalScrollbar); |
| destroyScrollbar(VerticalScrollbar); |
| |
| if (renderer()->frame() && renderer()->frame()->page()) { |
| if (ScrollingCoordinator* scrollingCoordinator = renderer()->frame()->page()->scrollingCoordinator()) |
| scrollingCoordinator->willDestroyRenderLayer(this); |
| } |
| |
| if (m_reflection) |
| removeReflection(); |
| |
| removeFilterInfoIfNeeded(); |
| |
| // Child layers will be deleted by their corresponding render objects, so |
| // we don't need to delete them ourselves. |
| |
| clearBacking(true); |
| |
| if (m_scrollCorner) |
| m_scrollCorner->destroy(); |
| if (m_resizer) |
| m_resizer->destroy(); |
| } |
| |
| String RenderLayer::debugName() const |
| { |
| String name = renderer()->debugName(); |
| if (!isReflection()) |
| return name; |
| return name + " (reflection)"; |
| } |
| |
| RenderLayerCompositor* RenderLayer::compositor() const |
| { |
| if (!renderer()->view()) |
| return 0; |
| return renderer()->view()->compositor(); |
| } |
| |
| void RenderLayer::contentChanged(ContentChangeType changeType) |
| { |
| // This can get called when video becomes accelerated, so the layers may change. |
| if ((changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged) && compositor()->updateLayerCompositingState(this)) |
| compositor()->setCompositingLayersNeedRebuild(); |
| |
| if (m_backing) |
| m_backing->contentChanged(changeType); |
| } |
| |
| bool RenderLayer::canRender3DTransforms() const |
| { |
| return compositor()->canRender3DTransforms(); |
| } |
| |
| bool RenderLayer::paintsWithFilters() const |
| { |
| // FIXME: Eventually there will be more factors than isComposited() to decide whether or not to render the filter |
| if (!renderer()->hasFilter()) |
| return false; |
| |
| if (!isComposited()) |
| return true; |
| |
| if (!m_backing || !m_backing->canCompositeFilters()) |
| return true; |
| |
| return false; |
| } |
| |
| bool RenderLayer::requiresFullLayerImageForFilters() const |
| { |
| if (!paintsWithFilters()) |
| return false; |
| FilterEffectRenderer* filter = filterRenderer(); |
| return filter ? filter->hasFilterThatMovesPixels() : false; |
| } |
| |
| LayoutPoint RenderLayer::computeOffsetFromRoot(bool& hasLayerOffset) const |
| { |
| hasLayerOffset = true; |
| |
| if (!parent()) |
| return LayoutPoint(); |
| |
| // This is similar to root() but we check if an ancestor layer would |
| // prevent the optimization from working. |
| const RenderLayer* rootLayer = 0; |
| for (const RenderLayer* parentLayer = parent(); parentLayer; rootLayer = parentLayer, parentLayer = parentLayer->parent()) { |
| hasLayerOffset = parentLayer->canUseConvertToLayerCoords(); |
| if (!hasLayerOffset) |
| return LayoutPoint(); |
| } |
| ASSERT(rootLayer == root()); |
| |
| LayoutPoint offset; |
| parent()->convertToLayerCoords(rootLayer, offset); |
| return offset; |
| } |
| |
| void RenderLayer::updateLayerPositionsAfterLayout(const RenderLayer* rootLayer, UpdateLayerPositionsFlags flags) |
| { |
| RenderGeometryMap geometryMap(UseTransforms); |
| if (this != rootLayer) |
| geometryMap.pushMappingsToAncestor(parent(), 0); |
| updateLayerPositions(&geometryMap, flags); |
| } |
| |
| void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLayerPositionsFlags flags) |
| { |
| updateLayerPosition(); // For relpositioned layers or non-positioned layers, |
| // we need to keep in sync, since we may have shifted relative |
| // to our parent layer. |
| if (geometryMap) |
| geometryMap->pushMappingsToAncestor(this, parent()); |
| |
| // Clear our cached clip rect information. |
| clearClipRects(); |
| |
| if (hasOverflowControls()) { |
| LayoutPoint offsetFromRoot; |
| if (geometryMap) |
| offsetFromRoot = LayoutPoint(geometryMap->absolutePoint(FloatPoint())); |
| else { |
| // FIXME: It looks suspicious to call convertToLayerCoords here |
| // as canUseConvertToLayerCoords may be true for an ancestor layer. |
| convertToLayerCoords(root(), offsetFromRoot); |
| } |
| positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot))); |
| } |
| |
| updateDescendantDependentFlags(); |
| |
| if (flags & UpdatePagination) |
| updatePagination(); |
| else { |
| m_isPaginated = false; |
| m_enclosingPaginationLayer = 0; |
| } |
| |
| if (m_hasVisibleContent) { |
| RenderView* view = renderer()->view(); |
| ASSERT(view); |
| // FIXME: LayoutState does not work with RenderLayers as there is not a 1-to-1 |
| // mapping between them and the RenderObjects. It would be neat to enable |
| // LayoutState outside the layout() phase and use it here. |
| ASSERT(!view->layoutStateEnabled()); |
| |
| RenderLayerModelObject* repaintContainer = renderer()->containerForRepaint(); |
| LayoutRect oldRepaintRect = m_repaintRect; |
| LayoutRect oldOutlineBox = m_outlineBox; |
| computeRepaintRects(repaintContainer, geometryMap); |
| |
| // FIXME: Should ASSERT that value calculated for m_outlineBox using the cached offset is the same |
| // as the value not using the cached offset, but we can't due to https://bugs.webkit.org/show_bug.cgi?id=37048 |
| if (flags & CheckForRepaint) { |
| if (view && !view->printing()) { |
| if (m_repaintStatus & NeedsFullRepaint) { |
| renderer()->repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldRepaintRect)); |
| if (m_repaintRect != oldRepaintRect) |
| renderer()->repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_repaintRect)); |
| } else if (shouldRepaintAfterLayout()) |
| renderer()->repaintAfterLayoutIfNeeded(repaintContainer, oldRepaintRect, oldOutlineBox, &m_repaintRect, &m_outlineBox); |
| } |
| } |
| } else |
| clearRepaintRects(); |
| |
| m_repaintStatus = NeedsNormalRepaint; |
| |
| // Go ahead and update the reflection's position and size. |
| if (m_reflection) |
| m_reflection->layout(); |
| |
| // Clear the IsCompositingUpdateRoot flag once we've found the first compositing layer in this update. |
| bool isUpdateRoot = (flags & IsCompositingUpdateRoot); |
| if (isComposited()) |
| flags &= ~IsCompositingUpdateRoot; |
| |
| if (useRegionBasedColumns() && renderer()->isInFlowRenderFlowThread()) { |
| updatePagination(); |
| flags |= UpdatePagination; |
| } |
| |
| if (renderer()->hasColumns()) |
| flags |= UpdatePagination; |
| |
| for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
| child->updateLayerPositions(geometryMap, flags); |
| |
| if ((flags & UpdateCompositingLayers) && isComposited()) { |
| RenderLayerBacking::UpdateAfterLayoutFlags updateFlags = RenderLayerBacking::CompositingChildrenOnly; |
| if (flags & NeedsFullRepaintInBacking) |
| updateFlags |= RenderLayerBacking::NeedsFullRepaint; |
| if (isUpdateRoot) |
| updateFlags |= RenderLayerBacking::IsUpdateRoot; |
| backing()->updateAfterLayout(updateFlags); |
| } |
| |
| if (geometryMap) |
| geometryMap->popMappingsToAncestor(parent()); |
| } |
| |
| LayoutRect RenderLayer::repaintRectIncludingNonCompositingDescendants() const |
| { |
| LayoutRect repaintRect = m_repaintRect; |
| for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
| // Don't include repaint rects for composited child layers; they will paint themselves and have a different origin. |
| if (child->isComposited()) |
| continue; |
| |
| repaintRect.unite(child->repaintRectIncludingNonCompositingDescendants()); |
| } |
| return repaintRect; |
| } |
| |
| void RenderLayer::setAncestorChainHasSelfPaintingLayerDescendant() |
| { |
| for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| if (!layer->m_hasSelfPaintingLayerDescendantDirty && layer->hasSelfPaintingLayerDescendant()) |
| break; |
| |
| layer->m_hasSelfPaintingLayerDescendantDirty = false; |
| layer->m_hasSelfPaintingLayerDescendant = true; |
| } |
| } |
| |
| void RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus() |
| { |
| for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| layer->m_hasSelfPaintingLayerDescendantDirty = true; |
| // If we have reached a self-painting layer, we know our parent should have a self-painting descendant |
| // in this case, there is no need to dirty our ancestors further. |
| if (layer->isSelfPaintingLayer()) { |
| ASSERT(!parent() || parent()->m_hasSelfPaintingLayerDescendantDirty || parent()->hasSelfPaintingLayerDescendant()); |
| break; |
| } |
| } |
| } |
| |
| void RenderLayer::setAncestorChainHasOutOfFlowPositionedDescendant() |
| { |
| for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| if (!layer->m_hasOutOfFlowPositionedDescendantDirty && layer->hasOutOfFlowPositionedDescendant()) |
| break; |
| |
| layer->setHasOutOfFlowPositionedDescendantDirty(false); |
| layer->setHasOutOfFlowPositionedDescendant(true); |
| } |
| } |
| |
| void RenderLayer::dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus() |
| { |
| for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| layer->setHasOutOfFlowPositionedDescendantDirty(true); |
| |
| // We may or may not have an unclipped descendant. If we do, we'll reset |
| // this to true the next time composited scrolling state is updated. |
| layer->setHasUnclippedDescendant(false); |
| |
| // If we have reached an out of flow positioned layer, we know our parent should have an out-of-flow positioned descendant. |
| // In this case, there is no need to dirty our ancestors further. |
| if (layer->renderer()->isOutOfFlowPositioned()) { |
| ASSERT(!parent() || parent()->m_hasOutOfFlowPositionedDescendantDirty || parent()->hasOutOfFlowPositionedDescendant()); |
| break; |
| } |
| } |
| } |
| |
| bool RenderLayer::acceleratedCompositingForOverflowScrollEnabled() const |
| { |
| const Settings* settings = renderer()->document().settings(); |
| return settings && settings->acceleratedCompositingForOverflowScrollEnabled(); |
| } |
| |
| // FIXME: This is a temporary flag and should be removed once accelerated |
| // overflow scroll is ready (crbug.com/254111). |
| bool RenderLayer::compositorDrivenAcceleratedScrollingEnabled() const |
| { |
| if (!acceleratedCompositingForOverflowScrollEnabled()) |
| return false; |
| |
| const Settings* settings = renderer()->document().settings(); |
| return settings && settings->isCompositorDrivenAcceleratedScrollingEnabled(); |
| } |
| |
| // Determine whether the current layer can be promoted to a stacking container. |
| // We do this by computing what positive and negative z-order lists would look |
| // like before and after promotion, and ensuring that proper stacking order is |
| // preserved between the two sets of lists. |
| void RenderLayer::updateCanBeStackingContainer() |
| { |
| TRACE_EVENT0("blink_rendering,comp-scroll", "RenderLayer::updateCanBeStackingContainer"); |
| |
| if (isStackingContext() || !m_canBePromotedToStackingContainerDirty || !acceleratedCompositingForOverflowScrollEnabled()) |
| return; |
| |
| FrameView* frameView = renderer()->view()->frameView(); |
| if (!frameView || !frameView->containsScrollableArea(scrollableArea())) |
| return; |
| |
| RenderLayer* ancestorStackingContext = this->ancestorStackingContext(); |
| if (!ancestorStackingContext) |
| return; |
| |
| OwnPtr<Vector<RenderLayer*> > posZOrderListBeforePromote = adoptPtr(new Vector<RenderLayer*>); |
| OwnPtr<Vector<RenderLayer*> > negZOrderListBeforePromote = adoptPtr(new Vector<RenderLayer*>); |
| OwnPtr<Vector<RenderLayer*> > posZOrderListAfterPromote = adoptPtr(new Vector<RenderLayer*>); |
| OwnPtr<Vector<RenderLayer*> > negZOrderListAfterPromote = adoptPtr(new Vector<RenderLayer*>); |
| |
| collectBeforePromotionZOrderList(ancestorStackingContext, posZOrderListBeforePromote, negZOrderListBeforePromote); |
| collectAfterPromotionZOrderList(ancestorStackingContext, posZOrderListAfterPromote, negZOrderListAfterPromote); |
| |
| size_t maxIndex = std::min(posZOrderListAfterPromote->size() + negZOrderListAfterPromote->size(), posZOrderListBeforePromote->size() + negZOrderListBeforePromote->size()); |
| |
| m_canBePromotedToStackingContainerDirty = false; |
| m_canBePromotedToStackingContainer = false; |
| |
| const RenderLayer* layerAfterPromote = 0; |
| for (size_t i = 0; i < maxIndex && layerAfterPromote != this; ++i) { |
| const RenderLayer* layerBeforePromote = i < negZOrderListBeforePromote->size() |
| ? negZOrderListBeforePromote->at(i) |
| : posZOrderListBeforePromote->at(i - negZOrderListBeforePromote->size()); |
| layerAfterPromote = i < negZOrderListAfterPromote->size() |
| ? negZOrderListAfterPromote->at(i) |
| : posZOrderListAfterPromote->at(i - negZOrderListAfterPromote->size()); |
| |
| if (layerBeforePromote != layerAfterPromote && (layerAfterPromote != this || renderer()->hasBackground())) |
| return; |
| } |
| |
| layerAfterPromote = 0; |
| for (size_t i = 0; i < maxIndex && layerAfterPromote != this; ++i) { |
| const RenderLayer* layerBeforePromote = i < posZOrderListBeforePromote->size() |
| ? posZOrderListBeforePromote->at(posZOrderListBeforePromote->size() - i - 1) |
| : negZOrderListBeforePromote->at(negZOrderListBeforePromote->size() + posZOrderListBeforePromote->size() - i - 1); |
| layerAfterPromote = i < posZOrderListAfterPromote->size() |
| ? posZOrderListAfterPromote->at(posZOrderListAfterPromote->size() - i - 1) |
| : negZOrderListAfterPromote->at(negZOrderListAfterPromote->size() + posZOrderListAfterPromote->size() - i - 1); |
| |
| if (layerBeforePromote != layerAfterPromote && layerAfterPromote != this) |
| return; |
| } |
| |
| m_canBePromotedToStackingContainer = true; |
| } |
| |
| static inline bool isPositionedContainer(const RenderLayer* layer) |
| { |
| // FIXME: This is not in sync with containingBlock. |
| // RenderObject::canContainFixedPositionedObject() should probably be used |
| // instead. |
| RenderLayerModelObject* layerRenderer = layer->renderer(); |
| return layer->isRootLayer() || layerRenderer->isPositioned() || layer->hasTransform(); |
| } |
| |
| void RenderLayer::collectBeforePromotionZOrderList(RenderLayer* ancestorStackingContext, OwnPtr<Vector<RenderLayer*> >& posZOrderListBeforePromote, OwnPtr<Vector<RenderLayer*> >& negZOrderListBeforePromote) |
| { |
| ancestorStackingContext->rebuildZOrderLists(posZOrderListBeforePromote, negZOrderListBeforePromote, this, OnlyStackingContextsCanBeStackingContainers); |
| |
| const RenderLayer* positionedAncestor = parent(); |
| while (positionedAncestor && !isPositionedContainer(positionedAncestor) && !positionedAncestor->isStackingContext()) |
| positionedAncestor = positionedAncestor->parent(); |
| if (positionedAncestor && (!isPositionedContainer(positionedAncestor) || positionedAncestor->isStackingContext())) |
| positionedAncestor = 0; |
| |
| if (!posZOrderListBeforePromote) |
| posZOrderListBeforePromote = adoptPtr(new Vector<RenderLayer*>()); |
| else if (posZOrderListBeforePromote->find(this) != notFound) |
| return; |
| |
| // The current layer will appear in the z-order lists after promotion, so |
| // for a meaningful comparison, we must insert it in the z-order lists |
| // before promotion if it does not appear there already. |
| if (!positionedAncestor) { |
| posZOrderListBeforePromote->prepend(this); |
| return; |
| } |
| |
| for (size_t index = 0; index < posZOrderListBeforePromote->size(); index++) { |
| if (posZOrderListBeforePromote->at(index) == positionedAncestor) { |
| posZOrderListBeforePromote->insert(index + 1, this); |
| return; |
| } |
| } |
| } |
| |
| void RenderLayer::collectAfterPromotionZOrderList(RenderLayer* ancestorStackingContext, OwnPtr<Vector<RenderLayer*> >& posZOrderListAfterPromote, OwnPtr<Vector<RenderLayer*> >& negZOrderListAfterPromote) |
| { |
| ancestorStackingContext->rebuildZOrderLists(posZOrderListAfterPromote, negZOrderListAfterPromote, this, ForceLayerToStackingContainer); |
| } |
| |
| // Compute what positive and negative z-order lists would look like before and |
| // after promotion, so we can later ensure that proper stacking order is |
| // preserved between the two sets of lists. |
| // |
| // A few examples: |
| // c = currentLayer |
| // - = negative z-order child of currentLayer |
| // + = positive z-order child of currentLayer |
| // a = positioned ancestor of currentLayer |
| // x = any other RenderLayer in the list |
| // |
| // (a) xxxxx-----++a+++x |
| // (b) xxx-----c++++++xx |
| // |
| // Normally the current layer would be painted in the normal flow list if it |
| // doesn't already appear in the positive z-order list. However, in the case |
| // that the layer has a positioned ancestor, it will paint directly after the |
| // positioned ancestor. In example (a), the current layer would be painted in |
| // the middle of its own positive z-order children, so promoting would cause a |
| // change in paint order (since a promoted layer will paint all of its positive |
| // z-order children strictly after it paints itself). |
| // |
| // In example (b), it is ok to promote the current layer only if it does not |
| // have a background. If it has a background, the background gets painted before |
| // the layer's negative z-order children, so again, a promotion would cause a |
| // change in paint order (causing the background to get painted after the |
| // negative z-order children instead of before). |
| // |
| void RenderLayer::computePaintOrderList(PaintOrderListType type, Vector<RefPtr<Node> >& list) |
| { |
| OwnPtr<Vector<RenderLayer*> > posZOrderList; |
| OwnPtr<Vector<RenderLayer*> > negZOrderList; |
| |
| RenderLayer* stackingContext = ancestorStackingContext(); |
| |
| if (!stackingContext) |
| return; |
| |
| switch (type) { |
| case BeforePromote: |
| collectBeforePromotionZOrderList(stackingContext, posZOrderList, negZOrderList); |
| break; |
| case AfterPromote: |
| collectAfterPromotionZOrderList(stackingContext, posZOrderList, negZOrderList); |
| break; |
| } |
| |
| if (negZOrderList) { |
| for (size_t index = 0; index < negZOrderList->size(); ++index) |
| list.append(negZOrderList->at(index)->renderer()->node()); |
| } |
| |
| if (posZOrderList) { |
| for (size_t index = 0; index < posZOrderList->size(); ++index) |
| list.append(posZOrderList->at(index)->renderer()->node()); |
| } |
| } |
| |
| bool RenderLayer::scrollsWithRespectTo(const RenderLayer* other) const |
| { |
| const EPosition position = renderer()->style()->position(); |
| const EPosition otherPosition = other->renderer()->style()->position(); |
| const RenderObject* containingBlock = renderer()->containingBlock(); |
| const RenderObject* otherContainingBlock = other->renderer()->containingBlock(); |
| const RenderLayer* rootLayer = renderer()->view()->compositor()->rootRenderLayer(); |
| |
| // Fixed-position elements are a special case. They are static with respect |
| // to the viewport, which is not represented by any RenderObject, and their |
| // containingBlock() method returns the root HTML element (while its true |
| // containingBlock should really be the viewport). The real measure for a |
| // non-transformed fixed-position element is as follows: any fixed position |
| // element, A, scrolls with respect an element, B, if and only if B is not |
| // fixed position. |
| // |
| // Unfortunately, it gets a bit more complicated - a fixed-position element |
| // which has a transform acts exactly as an absolute-position element |
| // (including having a real, non-viewport containing block). |
| // |
| // Below, a "root" fixed position element is defined to be one whose |
| // containing block is the root. These root-fixed-position elements are |
| // the only ones that need this special case code - other fixed position |
| // elements, as well as all absolute, relative, and static elements use the |
| // logic below. |
| const bool isRootFixedPos = position == FixedPosition && containingBlock->enclosingLayer() == rootLayer; |
| const bool otherIsRootFixedPos = otherPosition == FixedPosition && otherContainingBlock->enclosingLayer() == rootLayer; |
| |
| if (isRootFixedPos && otherIsRootFixedPos) |
| return false; |
| if (isRootFixedPos || otherIsRootFixedPos) |
| return true; |
| |
| FrameView* frameView = renderer()->view()->frameView(); |
| |
| if (containingBlock == otherContainingBlock) |
| return false; |
| |
| // Maintain a set of containing blocks between the first layer and its |
| // closest scrollable ancestor. |
| HashSet<const RenderObject*> containingBlocks; |
| while (containingBlock) { |
| if (frameView && frameView->containsScrollableArea(containingBlock->enclosingLayer()->scrollableArea())) |
| break; |
| containingBlocks.add(containingBlock); |
| containingBlock = containingBlock->containingBlock(); |
| } |
| |
| // Do the same for the 2nd layer, but if we find a common containing block, |
| // it means both layers are contained within a single non-scrolling subtree. |
| // Hence, they will not scroll with respect to each other. |
| while (otherContainingBlock) { |
| if (containingBlocks.contains(otherContainingBlock)) |
| return false; |
| if (frameView && frameView->containsScrollableArea(otherContainingBlock->enclosingLayer()->scrollableArea())) |
| break; |
| otherContainingBlock = otherContainingBlock->containingBlock(); |
| } |
| |
| return true; |
| } |
| |
| void RenderLayer::computeRepaintRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) |
| { |
| ASSERT(!m_visibleContentStatusDirty); |
| |
| m_repaintRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); |
| m_outlineBox = renderer()->outlineBoundsForRepaint(repaintContainer, geometryMap); |
| } |
| |
| |
| void RenderLayer::computeRepaintRectsIncludingDescendants() |
| { |
| // FIXME: computeRepaintRects() has to walk up the parent chain for every layer to compute the rects. |
| // We should make this more efficient. |
| // FIXME: it's wrong to call this when layout is not up-to-date, which we do. |
| computeRepaintRects(renderer()->containerForRepaint()); |
| |
| for (RenderLayer* layer = firstChild(); layer; layer = layer->nextSibling()) |
| layer->computeRepaintRectsIncludingDescendants(); |
| } |
| |
| void RenderLayer::clearRepaintRects() |
| { |
| ASSERT(!m_hasVisibleContent); |
| ASSERT(!m_visibleContentStatusDirty); |
| |
| m_repaintRect = IntRect(); |
| m_outlineBox = IntRect(); |
| } |
| |
| void RenderLayer::updateLayerPositionsAfterDocumentScroll() |
| { |
| ASSERT(this == renderer()->view()->layer()); |
| |
| RenderGeometryMap geometryMap(UseTransforms); |
| updateLayerPositionsAfterScroll(&geometryMap); |
| } |
| |
| void RenderLayer::updateLayerPositionsAfterOverflowScroll() |
| { |
| RenderGeometryMap geometryMap(UseTransforms); |
| RenderView* view = renderer()->view(); |
| if (this != view->layer()) |
| geometryMap.pushMappingsToAncestor(parent(), 0); |
| |
| // FIXME: why is it OK to not check the ancestors of this layer in order to |
| // initialize the HasSeenViewportConstrainedAncestor and HasSeenAncestorWithOverflowClip flags? |
| updateLayerPositionsAfterScroll(&geometryMap, IsOverflowScroll); |
| } |
| |
| void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap, UpdateLayerPositionsAfterScrollFlags flags) |
| { |
| // FIXME: This shouldn't be needed, but there are some corner cases where |
| // these flags are still dirty. Update so that the check below is valid. |
| updateDescendantDependentFlags(); |
| |
| // If we have no visible content and no visible descendants, there is no point recomputing |
| // our rectangles as they will be empty. If our visibility changes, we are expected to |
| // recompute all our positions anyway. |
| if (!m_hasVisibleDescendant && !m_hasVisibleContent) |
| return; |
| |
| bool positionChanged = updateLayerPosition(); |
| if (positionChanged) |
| flags |= HasChangedAncestor; |
| |
| if (geometryMap) |
| geometryMap->pushMappingsToAncestor(this, parent()); |
| |
| if (flags & HasChangedAncestor || flags & HasSeenViewportConstrainedAncestor || flags & IsOverflowScroll) |
| clearClipRects(); |
| |
| if (renderer()->style()->hasViewportConstrainedPosition()) |
| flags |= HasSeenViewportConstrainedAncestor; |
| |
| if (renderer()->hasOverflowClip()) |
| flags |= HasSeenAncestorWithOverflowClip; |
| |
| if (flags & HasSeenViewportConstrainedAncestor |
| || (flags & IsOverflowScroll && flags & HasSeenAncestorWithOverflowClip && !m_canSkipRepaintRectsUpdateOnScroll)) { |
| // FIXME: We could track the repaint container as we walk down the tree. |
| computeRepaintRects(renderer()->containerForRepaint(), geometryMap); |
| } else { |
| // Check that our cached rects are correct. |
| // FIXME: re-enable these assertions when the issue with table cells is resolved: https://bugs.webkit.org/show_bug.cgi?id=103432 |
| // ASSERT(m_repaintRect == renderer()->clippedOverflowRectForRepaint(renderer()->containerForRepaint())); |
| // ASSERT(m_outlineBox == renderer()->outlineBoundsForRepaint(renderer()->containerForRepaint(), geometryMap)); |
| } |
| |
| for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
| child->updateLayerPositionsAfterScroll(geometryMap, flags); |
| |
| // We don't update our reflection as scrolling is a translation which does not change the size() |
| // of an object, thus RenderReplica will still repaint itself properly as the layer position was |
| // updated above. |
| |
| if (geometryMap) |
| geometryMap->popMappingsToAncestor(parent()); |
| } |
| |
| void RenderLayer::positionNewlyCreatedOverflowControls() |
| { |
| if (!backing()->hasUnpositionedOverflowControlsLayers()) |
| return; |
| |
| RenderGeometryMap geometryMap(UseTransforms); |
| RenderView* view = renderer()->view(); |
| if (this != view->layer() && parent()) |
| geometryMap.pushMappingsToAncestor(parent(), 0); |
| |
| LayoutPoint offsetFromRoot = LayoutPoint(geometryMap.absolutePoint(FloatPoint())); |
| positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot))); |
| } |
| |
| bool RenderLayer::hasBlendMode() const |
| { |
| return RuntimeEnabledFeatures::cssCompositingEnabled() && renderer()->hasBlendMode(); |
| } |
| |
| void RenderLayer::updateBlendMode() |
| { |
| if (!RuntimeEnabledFeatures::cssCompositingEnabled()) |
| return; |
| |
| BlendMode newBlendMode = renderer()->style()->blendMode(); |
| if (newBlendMode != m_blendMode) { |
| m_blendMode = newBlendMode; |
| if (backing()) |
| backing()->setBlendMode(newBlendMode); |
| } |
| } |
| |
| void RenderLayer::updateTransform() |
| { |
| // hasTransform() on the renderer is also true when there is transform-style: preserve-3d or perspective set, |
| // so check style too. |
| bool hasTransform = renderer()->hasTransform() && renderer()->style()->hasTransform(); |
| bool had3DTransform = has3DTransform(); |
| |
| bool hadTransform = m_transform; |
| if (hasTransform != hadTransform) { |
| if (hasTransform) |
| m_transform = adoptPtr(new TransformationMatrix); |
| else |
| m_transform.clear(); |
| |
| // Layers with transforms act as clip rects roots, so clear the cached clip rects here. |
| clearClipRectsIncludingDescendants(); |
| } |
| |
| if (hasTransform) { |
| RenderBox* box = renderBox(); |
| ASSERT(box); |
| m_transform->makeIdentity(); |
| box->style()->applyTransform(*m_transform, box->pixelSnappedBorderBoxRect().size(), RenderStyle::IncludeTransformOrigin); |
| makeMatrixRenderable(*m_transform, canRender3DTransforms()); |
| } |
| |
| if (had3DTransform != has3DTransform()) |
| dirty3DTransformedDescendantStatus(); |
| } |
| |
| TransformationMatrix RenderLayer::currentTransform(RenderStyle::ApplyTransformOrigin applyOrigin) const |
| { |
| if (!m_transform) |
| return TransformationMatrix(); |
| |
| if (renderer()->style()->isRunningAcceleratedAnimation()) { |
| TransformationMatrix currTransform; |
| RefPtr<RenderStyle> style = renderer()->animation()->getAnimatedStyleForRenderer(renderer()); |
| style->applyTransform(currTransform, renderBox()->pixelSnappedBorderBoxRect().size(), applyOrigin); |
| makeMatrixRenderable(currTransform, canRender3DTransforms()); |
| return currTransform; |
| } |
| |
| // m_transform includes transform-origin, so we need to recompute the transform here. |
| if (applyOrigin == RenderStyle::ExcludeTransformOrigin) { |
| RenderBox* box = renderBox(); |
| TransformationMatrix currTransform; |
| box->style()->applyTransform(currTransform, box->pixelSnappedBorderBoxRect().size(), RenderStyle::ExcludeTransformOrigin); |
| makeMatrixRenderable(currTransform, canRender3DTransforms()); |
| return currTransform; |
| } |
| |
| return *m_transform; |
| } |
| |
| TransformationMatrix RenderLayer::renderableTransform(PaintBehavior paintBehavior) const |
| { |
| if (!m_transform) |
| return TransformationMatrix(); |
| |
| if (paintBehavior & PaintBehaviorFlattenCompositingLayers) { |
| TransformationMatrix matrix = *m_transform; |
| makeMatrixRenderable(matrix, false /* flatten 3d */); |
| return matrix; |
| } |
| |
| return *m_transform; |
| } |
| |
| static bool checkContainingBlockChainForPagination(RenderLayerModelObject* renderer, RenderBox* ancestorColumnsRenderer) |
| { |
| RenderView* view = renderer->view(); |
| RenderLayerModelObject* prevBlock = renderer; |
| RenderBlock* containingBlock; |
| for (containingBlock = renderer->containingBlock(); |
| containingBlock && containingBlock != view && containingBlock != ancestorColumnsRenderer; |
| containingBlock = containingBlock->containingBlock()) |
| prevBlock = containingBlock; |
| |
| // If the columns block wasn't in our containing block chain, then we aren't paginated by it. |
| if (containingBlock != ancestorColumnsRenderer) |
| return false; |
| |
| // If the previous block is absolutely positioned, then we can't be paginated by the columns block. |
| if (prevBlock->isOutOfFlowPositioned()) |
| return false; |
| |
| // Otherwise we are paginated by the columns block. |
| return true; |
| } |
| |
| bool RenderLayer::useRegionBasedColumns() const |
| { |
| const Settings* settings = renderer()->document().settings(); |
| return settings && settings->regionBasedColumnsEnabled(); |
| } |
| |
| void RenderLayer::updatePagination() |
| { |
| m_isPaginated = false; |
| m_enclosingPaginationLayer = 0; |
| |
| if (isComposited() || !parent()) |
| return; // FIXME: We will have to deal with paginated compositing layers someday. |
| // FIXME: For now the RenderView can't be paginated. Eventually printing will move to a model where it is though. |
| |
| // The main difference between the paginated booleans for the old column code and the new column code |
| // is that each paginated layer has to paint on its own with the new code. There is no |
| // recurring into child layers. This means that the m_isPaginated bits for the new column code can't just be set on |
| // "roots" that get split and paint all their descendants. Instead each layer has to be checked individually and |
| // genuinely know if it is going to have to split itself up when painting only its contents (and not any other descendant |
| // layers). We track an enclosingPaginationLayer instead of using a simple bit, since we want to be able to get back |
| // to that layer easily. |
| bool regionBasedColumnsUsed = useRegionBasedColumns(); |
| if (regionBasedColumnsUsed && renderer()->isInFlowRenderFlowThread()) { |
| m_enclosingPaginationLayer = this; |
| return; |
| } |
| |
| if (isNormalFlowOnly()) { |
| if (regionBasedColumnsUsed) { |
| // Content inside a transform is not considered to be paginated, since we simply |
| // paint the transform multiple times in each column, so we don't have to use |
| // fragments for the transformed content. |
| m_enclosingPaginationLayer = parent()->enclosingPaginationLayer(); |
| if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransform()) |
| m_enclosingPaginationLayer = 0; |
| } else |
| m_isPaginated = parent()->renderer()->hasColumns(); |
| return; |
| } |
| |
| // For the new columns code, we want to walk up our containing block chain looking for an enclosing layer. Once |
| // we find one, then we just check its pagination status. |
| if (regionBasedColumnsUsed) { |
| RenderView* view = renderer()->view(); |
| RenderBlock* containingBlock; |
| for (containingBlock = renderer()->containingBlock(); |
| containingBlock && containingBlock != view; |
| containingBlock = containingBlock->containingBlock()) { |
| if (containingBlock->hasLayer()) { |
| // Content inside a transform is not considered to be paginated, since we simply |
| // paint the transform multiple times in each column, so we don't have to use |
| // fragments for the transformed content. |
| m_enclosingPaginationLayer = containingBlock->layer()->enclosingPaginationLayer(); |
| if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransform()) |
| m_enclosingPaginationLayer = 0; |
| return; |
| } |
| } |
| return; |
| } |
| |
| // If we're not normal flow, then we need to look for a multi-column object between us and our stacking container. |
| RenderLayer* ancestorStackingContainer = this->ancestorStackingContainer(); |
| for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { |
| if (curr->renderer()->hasColumns()) { |
| m_isPaginated = checkContainingBlockChainForPagination(renderer(), curr->renderBox()); |
| return; |
| } |
| if (curr == ancestorStackingContainer) |
| return; |
| } |
| } |
| |
| bool RenderLayer::canBeStackingContainer() const |
| { |
| if (isStackingContext() || !ancestorStackingContainer()) |
| return true; |
| |
| ASSERT(!m_canBePromotedToStackingContainerDirty); |
| return m_canBePromotedToStackingContainer; |
| } |
| |
| void RenderLayer::setHasVisibleContent() |
| { |
| if (m_hasVisibleContent && !m_visibleContentStatusDirty) { |
| ASSERT(!parent() || parent()->hasVisibleDescendant()); |
| return; |
| } |
| |
| m_visibleContentStatusDirty = false; |
| m_hasVisibleContent = true; |
| computeRepaintRects(renderer()->containerForRepaint()); |
| if (!isNormalFlowOnly()) { |
| // We don't collect invisible layers in z-order lists if we are not in compositing mode. |
| // As we became visible, we need to dirty our stacking containers ancestors to be properly |
| // collected. FIXME: When compositing, we could skip this dirtying phase. |
| for (RenderLayer* sc = ancestorStackingContainer(); sc; sc = sc->ancestorStackingContainer()) { |
| sc->dirtyZOrderLists(); |
| if (sc->hasVisibleContent()) |
| break; |
| } |
| } |
| |
| if (parent()) |
| parent()->setAncestorChainHasVisibleDescendant(); |
| } |
| |
| void RenderLayer::dirtyVisibleContentStatus() |
| { |
| m_visibleContentStatusDirty = true; |
| if (parent()) |
| parent()->dirtyAncestorChainVisibleDescendantStatus(); |
| } |
| |
| void RenderLayer::dirtyAncestorChainVisibleDescendantStatus() |
| { |
| for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| if (layer->m_visibleDescendantStatusDirty) |
| break; |
| |
| layer->m_visibleDescendantStatusDirty = true; |
| } |
| } |
| |
| void RenderLayer::setAncestorChainHasVisibleDescendant() |
| { |
| for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| if (!layer->m_visibleDescendantStatusDirty && layer->hasVisibleDescendant()) |
| break; |
| |
| layer->m_hasVisibleDescendant = true; |
| layer->m_visibleDescendantStatusDirty = false; |
| } |
| } |
| |
| void RenderLayer::updateHasUnclippedDescendant() |
| { |
| TRACE_EVENT0("blink_rendering", "RenderLayer::updateHasUnclippedDescendant"); |
| ASSERT(renderer()->isOutOfFlowPositioned()); |
| if (!m_hasVisibleContent && !m_hasVisibleDescendant) |
| return; |
| |
| const RenderObject* containingBlock = renderer()->containingBlock(); |
| setIsUnclippedDescendant(false); |
| for (RenderLayer* ancestor = parent(); ancestor && ancestor->renderer() != containingBlock; ancestor = ancestor->parent()) { |
| // TODO(vollick): This isn't quite right. Whenever ancestor is composited and clips |
| // overflow, we're technically unclipped. However, this will currently cause a huge |
| // number of layers to report that they are unclipped. Eventually, when we've formally |
| // separated the clipping, transform, opacity, and stacking trees here and in the |
| // compositor, we will be able to relax this restriction without it being prohibitively |
| // expensive (currently, we have to do a lot of work in the compositor to honor a |
| // clip child/parent relationship). |
| if (ancestor->needsCompositedScrolling()) |
| setIsUnclippedDescendant(true); |
| ancestor->setHasUnclippedDescendant(true); |
| } |
| } |
| |
| static bool subtreeContainsOutOfFlowPositionedLayer(const RenderLayer* subtreeRoot) |
| { |
| return (subtreeRoot->renderer() && subtreeRoot->renderer()->isOutOfFlowPositioned()) || subtreeRoot->hasOutOfFlowPositionedDescendant(); |
| } |
| |
| void RenderLayer::updateDescendantDependentFlags() |
| { |
| if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty || m_hasOutOfFlowPositionedDescendantDirty) { |
| m_hasVisibleDescendant = false; |
| m_hasSelfPaintingLayerDescendant = false; |
| m_hasOutOfFlowPositionedDescendant = false; |
| |
| for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
| child->updateDescendantDependentFlags(); |
| |
| bool hasVisibleDescendant = child->m_hasVisibleContent || child->m_hasVisibleDescendant; |
| bool hasSelfPaintingLayerDescendant = child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant(); |
| bool hasOutOfFlowPositionedDescendant = subtreeContainsOutOfFlowPositionedLayer(child); |
| |
| m_hasVisibleDescendant |= hasVisibleDescendant; |
| m_hasSelfPaintingLayerDescendant |= hasSelfPaintingLayerDescendant; |
| m_hasOutOfFlowPositionedDescendant |= hasOutOfFlowPositionedDescendant; |
| |
| if (m_hasVisibleDescendant && m_hasSelfPaintingLayerDescendant && hasOutOfFlowPositionedDescendant) |
| break; |
| } |
| |
| m_visibleDescendantStatusDirty = false; |
| m_hasSelfPaintingLayerDescendantDirty = false; |
| m_hasOutOfFlowPositionedDescendantDirty = false; |
| } |
| |
| if (m_visibleContentStatusDirty) { |
| if (renderer()->style()->visibility() == VISIBLE) |
| m_hasVisibleContent = true; |
| else { |
| // layer may be hidden but still have some visible content, check for this |
| m_hasVisibleContent = false; |
| RenderObject* r = renderer()->firstChild(); |
| while (r) { |
| if (r->style()->visibility() == VISIBLE && !r->hasLayer()) { |
| m_hasVisibleContent = true; |
| break; |
| } |
| if (r->firstChild() && !r->hasLayer()) |
| r = r->firstChild(); |
| else if (r->nextSibling()) |
| r = r->nextSibling(); |
| else { |
| do { |
| r = r->parent(); |
| if (r == renderer()) |
| r = 0; |
| } while (r && !r->nextSibling()); |
| if (r) |
| r = r->nextSibling(); |
| } |
| } |
| } |
| m_visibleContentStatusDirty = false; |
| } |
| } |
| |
| void RenderLayer::dirty3DTransformedDescendantStatus() |
| { |
| RenderLayer* curr = ancestorStackingContainer(); |
| if (curr) |
| curr->m_3DTransformedDescendantStatusDirty = true; |
| |
| // This propagates up through preserve-3d hierarchies to the enclosing flattening layer. |
| // Note that preserves3D() creates stacking context, so we can just run up the stacking containers. |
| while (curr && curr->preserves3D()) { |
| curr->m_3DTransformedDescendantStatusDirty = true; |
| curr = curr->ancestorStackingContainer(); |
| } |
| } |
| |
| // Return true if this layer or any preserve-3d descendants have 3d. |
| bool RenderLayer::update3DTransformedDescendantStatus() |
| { |
| if (m_3DTransformedDescendantStatusDirty) { |
| m_has3DTransformedDescendant = false; |
| |
| updateZOrderLists(); |
| |
| // Transformed or preserve-3d descendants can only be in the z-order lists, not |
| // in the normal flow list, so we only need to check those. |
| if (Vector<RenderLayer*>* positiveZOrderList = posZOrderList()) { |
| for (unsigned i = 0; i < positiveZOrderList->size(); ++i) |
| m_has3DTransformedDescendant |= positiveZOrderList->at(i)->update3DTransformedDescendantStatus(); |
| } |
| |
| // Now check our negative z-index children. |
| if (Vector<RenderLayer*>* negativeZOrderList = negZOrderList()) { |
| for (unsigned i = 0; i < negativeZOrderList->size(); ++i) |
| m_has3DTransformedDescendant |= negativeZOrderList->at(i)->update3DTransformedDescendantStatus(); |
| } |
| |
| m_3DTransformedDescendantStatusDirty = false; |
| } |
| |
| // If we live in a 3d hierarchy, then the layer at the root of that hierarchy needs |
| // the m_has3DTransformedDescendant set. |
| if (preserves3D()) |
| return has3DTransform() || m_has3DTransformedDescendant; |
| |
| return has3DTransform(); |
| } |
| |
| bool RenderLayer::updateLayerPosition() |
| { |
| LayoutPoint localPoint; |
| LayoutSize inlineBoundingBoxOffset; // We don't put this into the RenderLayer x/y for inlines, so we need to subtract it out when done. |
| if (renderer()->isInline() && renderer()->isRenderInline()) { |
| RenderInline* inlineFlow = toRenderInline(renderer()); |
| IntRect lineBox = inlineFlow->linesBoundingBox(); |
| setSize(lineBox.size()); |
| inlineBoundingBoxOffset = toSize(lineBox.location()); |
| localPoint += inlineBoundingBoxOffset; |
| } else if (RenderBox* box = renderBox()) { |
| // FIXME: Is snapping the size really needed here for the RenderBox case? |
| setSize(pixelSnappedIntSize(box->size(), box->location())); |
| localPoint += box->topLeftLocationOffset(); |
| } |
| |
| if (!renderer()->isOutOfFlowPositioned() && renderer()->parent()) { |
| // We must adjust our position by walking up the render tree looking for the |
| // nearest enclosing object with a layer. |
| RenderObject* curr = renderer()->parent(); |
| while (curr && !curr->hasLayer()) { |
| if (curr->isBox() && !curr->isTableRow()) { |
| // Rows and cells share the same coordinate space (that of the section). |
| // Omit them when computing our xpos/ypos. |
| localPoint += toRenderBox(curr)->topLeftLocationOffset(); |
| } |
| curr = curr->parent(); |
| } |
| if (curr->isBox() && curr->isTableRow()) { |
| // Put ourselves into the row coordinate space. |
| localPoint -= toRenderBox(curr)->topLeftLocationOffset(); |
| } |
| } |
| |
| // Subtract our parent's scroll offset. |
| if (renderer()->isOutOfFlowPositioned() && enclosingPositionedAncestor()) { |
| RenderLayer* positionedParent = enclosingPositionedAncestor(); |
| |
| // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. |
| if (positionedParent->renderer()->hasOverflowClip()) { |
| LayoutSize offset = positionedParent->scrolledContentOffset(); |
| localPoint -= offset; |
| } |
| |
| if (renderer()->isOutOfFlowPositioned() && positionedParent->renderer()->isInFlowPositioned() && positionedParent->renderer()->isRenderInline()) { |
| LayoutSize offset = toRenderInline(positionedParent->renderer())->offsetForInFlowPositionedInline(toRenderBox(renderer())); |
| localPoint += offset; |
| } |
| } else if (parent()) { |
| if (isComposited()) { |
| // FIXME: Composited layers ignore pagination, so about the best we can do is make sure they're offset into the appropriate column. |
| // They won't split across columns properly. |
| LayoutSize columnOffset; |
| if (!parent()->renderer()->hasColumns() && parent()->renderer()->isRoot() && renderer()->view()->hasColumns()) |
| renderer()->view()->adjustForColumns(columnOffset, localPoint); |
| else |
| parent()->renderer()->adjustForColumns(columnOffset, localPoint); |
| |
| localPoint += columnOffset; |
| } |
| |
| if (parent()->renderer()->hasOverflowClip()) { |
| IntSize scrollOffset = parent()->scrolledContentOffset(); |
| localPoint -= scrollOffset; |
| } |
| } |
| |
| bool positionOrOffsetChanged = false; |
| if (renderer()->isInFlowPositioned()) { |
| LayoutSize newOffset = toRenderBoxModelObject(renderer())->offsetForInFlowPosition(); |
| positionOrOffsetChanged = newOffset != m_offsetForInFlowPosition; |
| m_offsetForInFlowPosition = newOffset; |
| localPoint.move(m_offsetForInFlowPosition); |
| } else { |
| m_offsetForInFlowPosition = LayoutSize(); |
| } |
| |
| // FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers. |
| localPoint -= inlineBoundingBoxOffset; |
| |
| positionOrOffsetChanged |= location() != localPoint; |
| setLocation(localPoint); |
| return positionOrOffsetChanged; |
| } |
| |
| TransformationMatrix RenderLayer::perspectiveTransform() const |
| { |
| if (!renderer()->hasTransform()) |
| return TransformationMatrix(); |
| |
| RenderStyle* style = renderer()->style(); |
| if (!style->hasPerspective()) |
| return TransformationMatrix(); |
| |
| // Maybe fetch the perspective from the backing? |
| const IntRect borderBox = toRenderBox(renderer())->pixelSnappedBorderBoxRect(); |
| const float boxWidth = borderBox.width(); |
| const float boxHeight = borderBox.height(); |
| |
| float perspectiveOriginX = floatValueForLength(style->perspectiveOriginX(), boxWidth); |
| float perspectiveOriginY = floatValueForLength(style->perspectiveOriginY(), boxHeight); |
| |
| // A perspective origin of 0,0 makes the vanishing point in the center of the element. |
| // We want it to be in the top-left, so subtract half the height and width. |
| perspectiveOriginX -= boxWidth / 2.0f; |
| perspectiveOriginY -= boxHeight / 2.0f; |
| |
| TransformationMatrix t; |
| t.translate(perspectiveOriginX, perspectiveOriginY); |
| t.applyPerspective(style->perspective()); |
| t.translate(-perspectiveOriginX, -perspectiveOriginY); |
| |
| return t; |
| } |
| |
| FloatPoint RenderLayer::perspectiveOrigin() const |
| { |
| if (!renderer()->hasTransform()) |
| return FloatPoint(); |
| |
| const LayoutRect borderBox = toRenderBox(renderer())->borderBoxRect(); |
| RenderStyle* style = renderer()->style(); |
| |
| return FloatPoint(floatValueForLength(style->perspectiveOriginX(), borderBox.width()), |
| floatValueForLength(style->perspectiveOriginY(), borderBox.height())); |
| } |
| |
| RenderLayer* RenderLayer::ancestorStackingContainer() const |
| { |
| RenderLayer* ancestor = parent(); |
| while (ancestor && !ancestor->isStackingContainer()) |
| ancestor = ancestor->parent(); |
| return ancestor; |
| } |
| |
| RenderLayer* RenderLayer::ancestorStackingContext() const |
| { |
| RenderLayer* ancestor = parent(); |
| while (ancestor && !ancestor->isStackingContext()) |
| ancestor = ancestor->parent(); |
| return ancestor; |
| } |
| |
| static inline bool isFixedPositionedContainer(RenderLayer* layer) |
| { |
| return layer->isRootLayer() || layer->hasTransform(); |
| } |
| |
| RenderLayer* RenderLayer::enclosingPositionedAncestor() const |
| { |
| RenderLayer* curr = parent(); |
| while (curr && !isPositionedContainer(curr)) |
| curr = curr->parent(); |
| |
| return curr; |
| } |
| |
| RenderLayer* RenderLayer::enclosingScrollableLayer() const |
| { |
| for (RenderObject* nextRenderer = renderer()->parent(); nextRenderer; nextRenderer = nextRenderer->parent()) { |
| if (nextRenderer->isBox() && toRenderBox(nextRenderer)->canBeScrolledAndHasScrollableArea()) |
| return nextRenderer->enclosingLayer(); |
| } |
| |
| return 0; |
| } |
| |
| IntRect RenderLayer::scrollableAreaBoundingBox() const |
| { |
| return renderer()->absoluteBoundingBoxRect(); |
| } |
| |
| bool RenderLayer::userInputScrollable(ScrollbarOrientation orientation) const |
| { |
| RenderBox* box = renderBox(); |
| ASSERT(box); |
| |
| if (box->isIntristicallyScrollable(orientation)) |
| return true; |
| |
| EOverflow overflowStyle = (orientation == HorizontalScrollbar) ? |
| renderer()->style()->overflowX() : renderer()->style()->overflowY(); |
| return (overflowStyle == OSCROLL || overflowStyle == OAUTO || overflowStyle == OOVERLAY); |
| } |
| |
| int RenderLayer::pageStep(ScrollbarOrientation orientation) const |
| { |
| RenderBox* box = renderBox(); |
| ASSERT(box); |
| |
| int length = (orientation == HorizontalScrollbar) ? |
| box->pixelSnappedClientWidth() : box->pixelSnappedClientHeight(); |
| int minPageStep = static_cast<float>(length) * ScrollableArea::minFractionToStepWhenPaging(); |
| int pageStep = max(minPageStep, length - ScrollableArea::maxOverlapBetweenPages()); |
| |
| return max(pageStep, 1); |
| } |
| |
| RenderLayer* RenderLayer::enclosingTransformedAncestor() const |
| { |
| RenderLayer* curr = parent(); |
| while (curr && !curr->isRootLayer() && !curr->transform()) |
| curr = curr->parent(); |
| |
| return curr; |
| } |
| |
| static inline const RenderLayer* compositingContainer(const RenderLayer* layer) |
| { |
| return layer->isNormalFlowOnly() ? layer->parent() : layer->ancestorStackingContainer(); |
| } |
| |
| inline bool RenderLayer::shouldRepaintAfterLayout() const |
| { |
| if (m_repaintStatus == NeedsNormalRepaint) |
| return true; |
| |
| // Composited layers that were moved during a positioned movement only |
| // layout, don't need to be repainted. They just need to be recomposited. |
| ASSERT(m_repaintStatus == NeedsFullRepaintForPositionedMovementLayout); |
| return !isComposited(); |
| } |
| |
| RenderLayer* RenderLayer::enclosingCompositingLayer(bool includeSelf) const |
| { |
| if (includeSelf && isComposited()) |
| return const_cast<RenderLayer*>(this); |
| |
| for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { |
| if (curr->isComposited()) |
| return const_cast<RenderLayer*>(curr); |
| } |
| |
| return 0; |
| } |
| |
| RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(bool includeSelf) const |
| { |
| if (includeSelf && isComposited() && !backing()->paintsIntoCompositedAncestor()) |
| return const_cast<RenderLayer*>(this); |
| |
| for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { |
| if (curr->isComposited() && !curr->backing()->paintsIntoCompositedAncestor()) |
| return const_cast<RenderLayer*>(curr); |
| } |
| |
| return 0; |
| } |
| |
| RenderLayer* RenderLayer::ancestorScrollingLayer() const |
| { |
| if (!acceleratedCompositingForOverflowScrollEnabled()) |
| return 0; |
| |
| RenderObject* containingBlock = renderer()->containingBlock(); |
| if (!containingBlock) |
| return 0; |
| |
| for (RenderLayer* ancestorLayer = containingBlock->enclosingLayer(); ancestorLayer; ancestorLayer = ancestorLayer->parent()) { |
| if (ancestorLayer->needsCompositedScrolling()) |
| return ancestorLayer; |
| } |
| |
| return 0; |
| } |
| |
| RenderLayer* RenderLayer::enclosingFilterLayer(bool includeSelf) const |
| { |
| const RenderLayer* curr = includeSelf ? this : parent(); |
| for (; curr; curr = curr->parent()) { |
| if (curr->requiresFullLayerImageForFilters()) |
| return const_cast<RenderLayer*>(curr); |
| } |
| |
| return 0; |
| } |
| |
| RenderLayer* RenderLayer::enclosingFilterRepaintLayer() const |
| { |
| for (const RenderLayer* curr = this; curr; curr = curr->parent()) { |
| if ((curr != this && curr->requiresFullLayerImageForFilters()) || curr->isComposited() || curr->isRootLayer()) |
| return const_cast<RenderLayer*>(curr); |
| } |
| return 0; |
| } |
| |
| void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect) |
| { |
| if (rect.isEmpty()) |
| return; |
| |
| LayoutRect rectForRepaint = rect; |
| renderer()->style()->filterOutsets().expandRect(rectForRepaint); |
| |
| RenderLayerFilterInfo* filterInfo = this->filterInfo(); |
| ASSERT(filterInfo); |
| filterInfo->expandDirtySourceRect(rectForRepaint); |
| |
| ASSERT(filterInfo->renderer()); |
| if (filterInfo->renderer()->hasCustomShaderFilter()) { |
| // If we have at least one custom shader, we need to update the whole bounding box of the layer, because the |
| // shader can address any ouput pixel. |
| // Note: This is only for output rect, so there's no need to expand the dirty source rect. |
| rectForRepaint.unite(calculateLayerBounds(this)); |
| } |
| |
| RenderLayer* parentLayer = enclosingFilterRepaintLayer(); |
| ASSERT(parentLayer); |
| FloatQuad repaintQuad(rectForRepaint); |
| LayoutRect parentLayerRect = renderer()->localToContainerQuad(repaintQuad, parentLayer->renderer()).enclosingBoundingBox(); |
| |
| if (parentLayer->isComposited()) { |
| parentLayer->setBackingNeedsRepaintInRect(parentLayerRect); |
| return; |
| } |
| |
| if (parentLayer->paintsWithFilters()) { |
| parentLayer->setFilterBackendNeedsRepaintingInRect(parentLayerRect); |
| return; |
| } |
| |
| if (parentLayer->isRootLayer()) { |
| RenderView* view = toRenderView(parentLayer->renderer()); |
| view->repaintViewRectangle(parentLayerRect); |
| return; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| } |
| |
| bool RenderLayer::hasAncestorWithFilterOutsets() const |
| { |
| for (const RenderLayer* curr = this; curr; curr = curr->parent()) { |
| RenderLayerModelObject* renderer = curr->renderer(); |
| if (renderer->style()->hasFilterOutsets()) |
| return true; |
| } |
| return false; |
| } |
| |
| RenderLayer* RenderLayer::clippingRootForPainting() const |
| { |
| if (isComposited()) |
| return const_cast<RenderLayer*>(this); |
| |
| const RenderLayer* current = this; |
| while (current) { |
| if (current->isRootLayer()) |
| return const_cast<RenderLayer*>(current); |
| |
| current = compositingContainer(current); |
| ASSERT(current); |
| if (current->transform() |
| || (current->isComposited() && !current->backing()->paintsIntoCompositedAncestor()) |
| ) |
| return const_cast<RenderLayer*>(current); |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return 0; |
| } |
| |
| LayoutPoint RenderLayer::absoluteToContents(const LayoutPoint& absolutePoint) const |
| { |
| // We don't use convertToLayerCoords because it doesn't know about transforms |
| return roundedLayoutPoint(renderer()->absoluteToLocal(absolutePoint, UseTransforms)); |
| } |
| |
| bool RenderLayer::cannotBlitToWindow() const |
| { |
| if (isTransparent() || hasReflection() || hasTransform()) |
| return true; |
| if (!parent()) |
| return false; |
| return parent()->cannotBlitToWindow(); |
| } |
| |
| bool RenderLayer::isTransparent() const |
| { |
| // FIXME: This seems incorrect; why would SVG layers be opaque? |
| if (renderer()->node() && renderer()->node()->namespaceURI() == SVGNames::svgNamespaceURI) |
| return false; |
| |
| return renderer()->isTransparent() || renderer()->hasMask(); |
| } |
| |
| RenderLayer* RenderLayer::transparentPaintingAncestor() |
| { |
| if (isComposited()) |
| return 0; |
| |
| for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { |
| if (curr->isComposited()) |
| return 0; |
| if (curr->isTransparent()) |
| return curr; |
| } |
| return 0; |
| } |
| |
| enum TransparencyClipBoxBehavior { |
| PaintingTransparencyClipBox, |
| HitTestingTransparencyClipBox |
| }; |
| |
| enum TransparencyClipBoxMode { |
| DescendantsOfTransparencyClipBox, |
| RootOfTransparencyClipBox |
| }; |
| |
| static LayoutRect transparencyClipBox(const RenderLayer*, const RenderLayer* rootLayer, TransparencyClipBoxBehavior, TransparencyClipBoxMode, PaintBehavior = 0); |
| |
| static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer, |
| TransparencyClipBoxBehavior transparencyBehavior, PaintBehavior paintBehavior) |
| { |
| // If we have a mask, then the clip is limited to the border box area (and there is |
| // no need to examine child layers). |
| if (!layer->renderer()->hasMask()) { |
| // Note: we don't have to walk z-order lists since transparent elements always establish |
| // a stacking container. This means we can just walk the layer tree directly. |
| for (RenderLayer* curr = layer->firstChild(); curr; curr = curr->nextSibling()) { |
| if (!layer->reflection() || layer->reflectionLayer() != curr) |
| clipRect.unite(transparencyClipBox(curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior)); |
| } |
| } |
| |
| // If we have a reflection, then we need to account for that when we push the clip. Reflect our entire |
| // current transparencyClipBox to catch all child layers. |
| // FIXME: Accelerated compositing will eventually want to do something smart here to avoid incorporating this |
| // size into the parent layer. |
| if (layer->renderer()->hasReflection()) { |
| LayoutPoint delta; |
| layer->convertToLayerCoords(rootLayer, delta); |
| clipRect.move(-delta.x(), -delta.y()); |
| clipRect.unite(layer->renderBox()->reflectedRect(clipRect)); |
| clipRect.moveBy(delta); |
| } |
| } |
| |
| static LayoutRect transparencyClipBox(const RenderLayer* layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior, |
| TransparencyClipBoxMode transparencyMode, PaintBehavior paintBehavior) |
| { |
| // FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the |
| // paintDirtyRect, and that should cut down on the amount we have to paint. Still it |
| // would be better to respect clips. |
| |
| if (rootLayer != layer && ((transparencyBehavior == PaintingTransparencyClipBox && layer->paintsWithTransform(paintBehavior)) |
| || (transparencyBehavior == HitTestingTransparencyClipBox && layer->hasTransform()))) { |
| // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass |
| // the transformed layer and all of its children. |
| const RenderLayer* paginationLayer = transparencyMode == DescendantsOfTransparencyClipBox ? layer->enclosingPaginationLayer() : 0; |
| const RenderLayer* rootLayerForTransform = paginationLayer ? paginationLayer : rootLayer; |
| LayoutPoint delta; |
| layer->convertToLayerCoords(rootLayerForTransform, delta); |
| |
| TransformationMatrix transform; |
| transform.translate(delta.x(), delta.y()); |
| transform = transform * *layer->transform(); |
| |
| // We don't use fragment boxes when collecting a transformed layer's bounding box, since it always |
| // paints unfragmented. |
| LayoutRect clipRect = layer->boundingBox(layer); |
| expandClipRectForDescendantsAndReflection(clipRect, layer, layer, transparencyBehavior, paintBehavior); |
| layer->renderer()->style()->filterOutsets().expandRect(clipRect); |
| LayoutRect result = transform.mapRect(clipRect); |
| if (!paginationLayer) |
| return result; |
| |
| // We have to break up the transformed extent across our columns. |
| // Split our box up into the actual fragment boxes that render in the columns/pages and unite those together to |
| // get our true bounding box. |
| RenderFlowThread* enclosingFlowThread = toRenderFlowThread(paginationLayer->renderer()); |
| result = enclosingFlowThread->fragmentsBoundingBox(result); |
| |
| LayoutPoint rootLayerDelta; |
| paginationLayer->convertToLayerCoords(rootLayer, rootLayerDelta); |
| result.moveBy(rootLayerDelta); |
| return result; |
| } |
| |
| LayoutRect clipRect = layer->boundingBox(rootLayer, RenderLayer::UseFragmentBoxes); |
| expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, transparencyBehavior, paintBehavior); |
| layer->renderer()->style()->filterOutsets().expandRect(clipRect); |
| return clipRect; |
| } |
| |
| LayoutRect RenderLayer::paintingExtent(const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior) |
| { |
| return intersection(transparencyClipBox(this, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintBehavior), paintDirtyRect); |
| } |
| |
| void RenderLayer::beginTransparencyLayers(GraphicsContext* context, const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior) |
| { |
| if (context->paintingDisabled() || (paintsWithTransparency(paintBehavior) && m_usedTransparency)) |
| return; |
| |
| RenderLayer* ancestor = transparentPaintingAncestor(); |
| if (ancestor) |
| ancestor->beginTransparencyLayers(context, rootLayer, paintDirtyRect, paintBehavior); |
| |
| if (paintsWithTransparency(paintBehavior)) { |
| m_usedTransparency = true; |
| context->save(); |
| LayoutRect clipRect = paintingExtent(rootLayer, paintDirtyRect, paintBehavior); |
| context->clip(clipRect); |
| context->beginTransparencyLayer(renderer()->opacity()); |
| #ifdef REVEAL_TRANSPARENCY_LAYERS |
| context->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f)); |
| context->fillRect(clipRect); |
| #endif |
| } |
| } |
| |
| void* RenderLayer::operator new(size_t sz) |
| { |
| return partitionAlloc(Partitions::getRenderingPartition(), sz); |
| } |
| |
| void RenderLayer::operator delete(void* ptr) |
| { |
| partitionFree(ptr); |
| } |
| |
| void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) |
| { |
| RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild(); |
| if (prevSibling) { |
| child->setPreviousSibling(prevSibling); |
| prevSibling->setNextSibling(child); |
| ASSERT(prevSibling != child); |
| } else |
| setFirstChild(child); |
| |
| if (beforeChild) { |
| beforeChild->setPreviousSibling(child); |
| child->setNextSibling(beforeChild); |
| ASSERT(beforeChild != child); |
| } else |
| setLastChild(child); |
| |
| child->setParent(this); |
| |
| if (child->isNormalFlowOnly()) |
| dirtyNormalFlowList(); |
| |
| if (!child->isNormalFlowOnly() || child->firstChild()) { |
| // Dirty the z-order list in which we are contained. The ancestorStackingContainer() can be null in the |
| // case where we're building up generated content layers. This is ok, since the lists will start |
| // off dirty in that case anyway. |
| child->dirtyStackingContainerZOrderLists(); |
| } |
| |
| child->updateDescendantDependentFlags(); |
| if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) |
| setAncestorChainHasVisibleDescendant(); |
| |
| if (child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant()) |
| setAncestorChainHasSelfPaintingLayerDescendant(); |
| |
| if (subtreeContainsOutOfFlowPositionedLayer(child)) { |
| // Now that the out of flow positioned descendant is in the tree, we |
| // need to tell the compositor to reevaluate the compositing |
| // requirements since we may be able to mark more layers as having |
| // an 'unclipped' descendant. |
| compositor()->setNeedsUpdateCompositingRequirementsState(); |
| setAncestorChainHasOutOfFlowPositionedDescendant(); |
| } |
| |
| // When we first dirty a layer, we will also dirty all the siblings in that |
| // layer's stacking context. We need to manually do it here as well, in case |
| // we're adding this layer after the stacking context has already been |
| // updated. |
| child->m_canBePromotedToStackingContainerDirty = true; |
| compositor()->layerWasAdded(this, child); |
| } |
| |
| RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) |
| { |
| if (!renderer()->documentBeingDestroyed()) |
| compositor()->layerWillBeRemoved(this, oldChild); |
| |
| // remove the child |
| if (oldChild->previousSibling()) |
| oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); |
| if (oldChild->nextSibling()) |
| oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); |
| |
| if (m_first == oldChild) |
| m_first = oldChild->nextSibling(); |
| if (m_last == oldChild) |
| m_last = oldChild->previousSibling(); |
| |
| if (oldChild->isNormalFlowOnly()) |
| dirtyNormalFlowList(); |
| if (!oldChild->isNormalFlowOnly() || oldChild->firstChild()) { |
| // Dirty the z-order list in which we are contained. When called via the |
| // reattachment process in removeOnlyThisLayer, the layer may already be disconnected |
| // from the main layer tree, so we need to null-check the |stackingContainer| value. |
| oldChild->dirtyStackingContainerZOrderLists(); |
| } |
| |
| oldChild->setPreviousSibling(0); |
| oldChild->setNextSibling(0); |
| oldChild->setParent(0); |
| |
| oldChild->updateDescendantDependentFlags(); |
| if (subtreeContainsOutOfFlowPositionedLayer(oldChild)) { |
| // It may now be the case that a layer no longer has an unclipped |
| // descendant. Let the compositor know that it needs to reevaluate |
| // its compositing requirements to check this. |
| compositor()->setNeedsUpdateCompositingRequirementsState(); |
| dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); |
| } |
| |
| if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) |
| dirtyAncestorChainVisibleDescendantStatus(); |
| |
| if (oldChild->isSelfPaintingLayer() || oldChild->hasSelfPaintingLayerDescendant()) |
| dirtyAncestorChainHasSelfPaintingLayerDescendantStatus(); |
| |
| return oldChild; |
| } |
| |
| void RenderLayer::removeOnlyThisLayer() |
| { |
| if (!m_parent) |
| return; |
| |
| // Mark that we are about to lose our layer. This makes render tree |
| // walks ignore this layer while we're removing it. |
| m_renderer->setHasLayer(false); |
| |
| compositor()->layerWillBeRemoved(m_parent, this); |
| |
| // Dirty the clip rects. |
| clearClipRectsIncludingDescendants(); |
| |
| RenderLayer* nextSib = nextSibling(); |
| |
| // Remove the child reflection layer before moving other child layers. |
| // The reflection layer should not be moved to the parent. |
| if (reflection()) |
| removeChild(reflectionLayer()); |
| |
| // Now walk our kids and reattach them to our parent. |
| RenderLayer* current = m_first; |
| while (current) { |
| RenderLayer* next = current->nextSibling(); |
| removeChild(current); |
| m_parent->addChild(current, nextSib); |
| current->setRepaintStatus(NeedsFullRepaint); |
| // updateLayerPositions depends on hasLayer() already being false for proper layout. |
| ASSERT(!renderer()->hasLayer()); |
| current->updateLayerPositions(0); // FIXME: use geometry map. |
| current = next; |
| } |
| |
| // Remove us from the parent. |
| m_parent->removeChild(this); |
| m_renderer->destroyLayer(); |
| } |
| |
| void RenderLayer::insertOnlyThisLayer() |
| { |
| if (!m_parent && renderer()->parent()) { |
| // We need to connect ourselves when our renderer() has a parent. |
| // Find our enclosingLayer and add ourselves. |
| RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); |
| ASSERT(parentLayer); |
| RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0; |
| parentLayer->addChild(this, beforeChild); |
| } |
| |
| // Remove all descendant layers from the hierarchy and add them to the new position. |
| for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling()) |
| curr->moveLayers(m_parent, this); |
| |
| // Clear out all the clip rects. |
| clearClipRectsIncludingDescendants(); |
| } |
| |
| void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& roundedLocation) const |
| { |
| LayoutPoint location = roundedLocation; |
| convertToLayerCoords(ancestorLayer, location); |
| roundedLocation = roundedIntPoint(location); |
| } |
| |
| void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntRect& roundedRect) const |
| { |
| LayoutRect rect = roundedRect; |
| convertToLayerCoords(ancestorLayer, rect); |
| roundedRect = pixelSnappedIntRect(rect); |
| } |
| |
| // Returns the layer reached on the walk up towards the ancestor. |
| static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location) |
| { |
| ASSERT(ancestorLayer != layer); |
| |
| const RenderLayerModelObject* renderer = layer->renderer(); |
| EPosition position = renderer->style()->position(); |
| |
| // FIXME: Special casing RenderFlowThread so much for fixed positioning here is not great. |
| RenderFlowThread* fixedFlowThreadContainer = position == FixedPosition ? renderer->flowThreadContainingBlock() : 0; |
| if (fixedFlowThreadContainer && !fixedFlowThreadContainer->isOutOfFlowPositioned()) |
| fixedFlowThreadContainer = 0; |
| |
| // FIXME: Positioning of out-of-flow(fixed, absolute) elements collected in a RenderFlowThread |
| // may need to be revisited in a future patch. |
| // If the fixed renderer is inside a RenderFlowThread, we should not compute location using localToAbsolute, |
| // since localToAbsolute maps the coordinates from named flow to regions coordinates and regions can be |
| // positioned in a completely different place in the viewport (RenderView). |
| if (position == FixedPosition && !fixedFlowThreadContainer && (!ancestorLayer || ancestorLayer == renderer->view()->layer())) { |
| // If the fixed layer's container is the root, just add in the offset of the view. We can obtain this by calling |
| // localToAbsolute() on the RenderView. |
| FloatPoint absPos = renderer->localToAbsolute(FloatPoint(), IsFixed); |
| location += LayoutSize(absPos.x(), absPos.y()); |
| return ancestorLayer; |
| } |
| |
| // For the fixed positioned elements inside a render flow thread, we should also skip the code path below |
| // Otherwise, for the case of ancestorLayer == rootLayer and fixed positioned element child of a transformed |
| // element in render flow thread, we will hit the fixed positioned container before hitting the ancestor layer. |
| if (position == FixedPosition && !fixedFlowThreadContainer) { |
| // For a fixed layers, we need to walk up to the root to see if there's a fixed position container |
| // (e.g. a transformed layer). It's an error to call convertToLayerCoords() across a layer with a transform, |
| // so we should always find the ancestor at or before we find the fixed position container. |
| RenderLayer* fixedPositionContainerLayer = 0; |
| bool foundAncestor = false; |
| for (RenderLayer* currLayer = layer->parent(); currLayer; currLayer = currLayer->parent()) { |
| if (currLayer == ancestorLayer) |
| foundAncestor = true; |
| |
| if (isFixedPositionedContainer(currLayer)) { |
| fixedPositionContainerLayer = currLayer; |
| ASSERT_UNUSED(foundAncestor, foundAncestor); |
| break; |
| } |
| } |
| |
| ASSERT(fixedPositionContainerLayer); // We should have hit the RenderView's layer at least. |
| |
| if (fixedPositionContainerLayer != ancestorLayer) { |
| LayoutPoint fixedContainerCoords; |
| layer->convertToLayerCoords(fixedPositionContainerLayer, fixedContainerCoords); |
| |
| LayoutPoint ancestorCoords; |
| ancestorLayer->convertToLayerCoords(fixedPositionContainerLayer, ancestorCoords); |
| |
| location += (fixedContainerCoords - ancestorCoords); |
| } else { |
| location += toSize(layer->location()); |
| } |
| return ancestorLayer; |
| } |
| |
| RenderLayer* parentLayer; |
| if (position == AbsolutePosition || position == FixedPosition) { |
| // Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way. |
| parentLayer = layer->parent(); |
| bool foundAncestorFirst = false; |
| while (parentLayer) { |
| // RenderFlowThread is a positioned container, child of RenderView, positioned at (0,0). |
| // This implies that, for out-of-flow positioned elements inside a RenderFlowThread, |
| // we are bailing out before reaching root layer. |
| if (isPositionedContainer(parentLayer)) |
| break; |
| |
| if (parentLayer == ancestorLayer) { |
| foundAncestorFirst = true; |
| break; |
| } |
| |
| parentLayer = parentLayer->parent(); |
| } |
| |
| // We should not reach RenderView layer past the RenderFlowThread layer for any |
| // children of the RenderFlowThread. |
| if (renderer->flowThreadContainingBlock() && !layer->isOutOfFlowRenderFlowThread()) |
| ASSERT(parentLayer != renderer->view()->layer()); |
| |
| if (foundAncestorFirst) { |
| // Found ancestorLayer before the abs. positioned container, so compute offset of both relative |
| // to enclosingPositionedAncestor and subtract. |
| RenderLayer* positionedAncestor = parentLayer->enclosingPositionedAncestor(); |
| |
| LayoutPoint thisCoords; |
| layer->convertToLayerCoords(positionedAncestor, thisCoords); |
| |
| LayoutPoint ancestorCoords; |
| ancestorLayer->convertToLayerCoords(positionedAncestor, ancestorCoords); |
| |
| location += (thisCoords - ancestorCoords); |
| return ancestorLayer; |
| } |
| } else |
| parentLayer = layer->parent(); |
| |
| if (!parentLayer) |
| return 0; |
| |
| location += toSize(layer->location()); |
| return parentLayer; |
| } |
| |
| void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint& location) const |
| { |
| if (ancestorLayer == this) |
| return; |
| |
| const RenderLayer* currLayer = this; |
| while (currLayer && currLayer != ancestorLayer) |
| currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, location); |
| } |
| |
| void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect& rect) const |
| { |
| LayoutPoint delta; |
| convertToLayerCoords(ancestorLayer, delta); |
| rect.move(-delta.x(), -delta.y()); |
| } |
| |
| bool RenderLayer::usesCompositedScrolling() const |
| { |
| RenderBox* box = renderBox(); |
| |
| // Scroll form controls on the main thread so they exhibit correct touch scroll event bubbling |
| if (box && (box->isIntristicallyScrollable(VerticalScrollbar) || box->isIntristicallyScrollable(HorizontalScrollbar))) |
| return false; |
| |
| return isComposited() && backing()->scrollingLayer(); |
| } |
| |
| bool RenderLayer::needsCompositedScrolling() const |
| { |
| if (!compositorDrivenAcceleratedScrollingEnabled()) |
| return needsToBeStackingContainer(); |
| if (FrameView* frameView = renderer()->view()->frameView()) |
| return frameView->containsScrollableArea(scrollableArea()); |
| return false; |
| } |
| |
| bool RenderLayer::needsToBeStackingContainer() const |
| { |
| switch (m_forceNeedsCompositedScrolling) { |
| case DoNotForceCompositedScrolling: |
| return m_needsCompositedScrolling; |
| case CompositedScrollingAlwaysOn: |
| return true; |
| case CompositedScrollingAlwaysOff: |
| return false; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return m_needsCompositedScrolling; |
| } |
| |
| void RenderLayer::updateNeedsCompositedScrolling() |
| { |
| TRACE_EVENT0("comp-scroll", "RenderLayer::updateNeedsCompositedScrolling"); |
| |
| updateCanBeStackingContainer(); |
| updateDescendantDependentFlags(); |
| |
| ASSERT(renderer()->view()->frameView() && renderer()->view()->frameView()->containsScrollableArea(scrollableArea())); |
| bool needsCompositedScrolling = acceleratedCompositingForOverflowScrollEnabled() |
| && canBeStackingContainer() |
| && !hasUnclippedDescendant(); |
| |
| // We gather a boolean value for use with Google UMA histograms to |
| // quantify the actual effects of a set of patches attempting to |
| // relax composited scrolling requirements, thereby increasing the |
| // number of composited overflow divs. |
| if (acceleratedCompositingForOverflowScrollEnabled()) |
| HistogramSupport::histogramEnumeration("Renderer.NeedsCompositedScrolling", needsCompositedScrolling, 2); |
| |
| setNeedsCompositedScrolling(needsCompositedScrolling); |
| } |
| |
| void RenderLayer::setNeedsCompositedScrolling(bool needsCompositedScrolling) |
| { |
| if (m_needsCompositedScrolling == needsCompositedScrolling) |
| return; |
| |
| m_needsCompositedScrolling = needsCompositedScrolling; |
| |
| // Note, the z-order lists may need to be rebuilt, but our code guarantees |
| // that we have not affected stacking, so we will not dirty |
| // m_canBePromotedToStackingContainer for either us or our stacking context |
| // or container. |
| didUpdateNeedsCompositedScrolling(); |
| } |
| |
| void RenderLayer::setForceNeedsCompositedScrolling(RenderLayer::ForceNeedsCompositedScrollingMode mode) |
| { |
| if (m_forceNeedsCompositedScrolling == mode) |
| return; |
| |
| m_forceNeedsCompositedScrolling = mode; |
| didUpdateNeedsCompositedScrolling(); |
| } |
| |
| void RenderLayer::didUpdateNeedsCompositedScrolling() |
| { |
| updateIsNormalFlowOnly(); |
| updateSelfPaintingLayer(); |
| |
| if (isStackingContainer()) |
| dirtyZOrderLists(); |
| else |
| clearZOrderLists(); |
| |
| dirtyStackingContainerZOrderLists(); |
| |
| compositor()->setShouldReevaluateCompositingAfterLayout(); |
| compositor()->setCompositingLayersNeedRebuild(); |
| } |
| |
| static inline int adjustedScrollDelta(int beginningDelta) { |
| // This implemention matches Firefox's. |
| // http://mxr.mozilla.org/firefox/source/toolkit/content/widgets/browser.xml#856. |
| const int speedReducer = 12; |
| |
| int adjustedDelta = beginningDelta / speedReducer; |
| if (adjustedDelta > 1) |
| adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1; |
| else if (adjustedDelta < -1) |
| adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-adjustedDelta))) + 1; |
| |
| return adjustedDelta; |
| } |
| |
| static inline IntSize adjustedScrollDelta(const IntSize& delta) |
| { |
| return IntSize(adjustedScrollDelta(delta.width()), adjustedScrollDelta(delta.height())); |
| } |
| |
| void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) |
| { |
| Frame* frame = renderer()->frame(); |
| if (!frame) |
| return; |
| |
| IntPoint lastKnownMousePosition = frame->eventHandler()->lastKnownMousePosition(); |
| |
| // We need to check if the last known mouse position is out of the window. When the mouse is out of the window, the position is incoherent |
| static IntPoint previousMousePosition; |
| if (lastKnownMousePosition.x() < 0 || lastKnownMousePosition.y() < 0) |
| lastKnownMousePosition = previousMousePosition; |
| else |
| previousMousePosition = lastKnownMousePosition; |
| |
| IntSize delta = lastKnownMousePosition - sourcePoint; |
| |
| if (abs(delta.width()) <= ScrollView::noPanScrollRadius) // at the center we let the space for the icon |
| delta.setWidth(0); |
| if (abs(delta.height()) <= ScrollView::noPanScrollRadius) |
| delta.setHeight(0); |
| |
| scrollByRecursively(adjustedScrollDelta(delta), ScrollOffsetClamped); |
| } |
| |
| void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping clamp) |
| { |
| if (delta.isZero()) |
| return; |
| |
| bool restrictedByLineClamp = false; |
| if (renderer()->parent()) |
| restrictedByLineClamp = !renderer()->parent()->style()->lineClamp().isNone(); |
| |
| if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { |
| IntSize newScrollOffset = adjustedScrollOffset() + delta; |
| m_scrollableArea->scrollToOffset(newScrollOffset, clamp); |
| |
| // If this layer can't do the scroll we ask the next layer up that can scroll to try |
| IntSize remainingScrollOffset = newScrollOffset - adjustedScrollOffset(); |
| if (!remainingScrollOffset.isZero() && renderer()->parent()) { |
| if (RenderLayer* scrollableLayer = enclosingScrollableLayer()) |
| scrollableLayer->scrollByRecursively(remainingScrollOffset, clamp); |
| |
| Frame* frame = renderer()->frame(); |
| if (frame && frame->page()) |
| frame->page()->updateAutoscrollRenderer(); |
| } |
| } else if (renderer()->view()->frameView()) { |
| // If we are here, we were called on a renderer that can be programmatically scrolled, but doesn't |
| // have an overflow clip. Which means that it is a document node that can be scrolled. |
| renderer()->view()->frameView()->scrollBy(delta); |
| |
| // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement? |
| // https://bugs.webkit.org/show_bug.cgi?id=28237 |
| } |
| } |
| |
| void RenderLayer::scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClamping clamp) |
| { |
| m_scrollableArea->scrollToOffset(scrollOffset, clamp); |
| } |
| |
| static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView* frameView) |
| { |
| // If scrollbars aren't explicitly forbidden, permit scrolling. |
| if (frameElementBase && frameElementBase->scrollingMode() != ScrollbarAlwaysOff) |
| return true; |
| |
| // If scrollbars are forbidden, user initiated scrolls should obviously be ignored. |
| if (frameView->wasScrolledByUser()) |
| return false; |
| |
| // Forbid autoscrolls when scrollbars are off, but permits other programmatic scrolls, |
| // like navigation to an anchor. |
| Page* page = frameView->frame().page(); |
| if (!page) |
| return false; |
| return !page->autoscrollInProgress(); |
| } |
| |
| void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) |
| { |
| RenderLayer* parentLayer = 0; |
| LayoutRect newRect = rect; |
| |
| // We may end up propagating a scroll event. It is important that we suspend events until |
| // the end of the function since they could delete the layer or the layer's renderer(). |
| FrameView* frameView = renderer()->document().view(); |
| if (frameView) |
| frameView->pauseScheduledEvents(); |
| |
| bool restrictedByLineClamp = false; |
| if (renderer()->parent()) { |
| parentLayer = renderer()->parent()->enclosingLayer(); |
| restrictedByLineClamp = !renderer()->parent()->style()->lineClamp().isNone(); |
| } |
| |
| if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { |
| // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. |
| // This will prevent us from revealing text hidden by the slider in Safari RSS. |
| RenderBox* box = renderBox(); |
| ASSERT(box); |
| LayoutRect localExposeRect(box->absoluteToLocalQuad(FloatQuad(FloatRect(rect)), UseTransforms).boundingBox()); |
| LayoutRect layerBounds(0, 0, box->clientWidth(), box->clientHeight()); |
| LayoutRect r = getRectToExpose(layerBounds, localExposeRect, alignX, alignY); |
| |
| IntSize clampedScrollOffset = m_scrollableArea->clampScrollOffset(adjustedScrollOffset() + toIntSize(roundedIntRect(r).location())); |
| if (clampedScrollOffset != adjustedScrollOffset()) { |
| IntSize oldScrollOffset = adjustedScrollOffset(); |
| m_scrollableArea->scrollToOffset(clampedScrollOffset); |
| IntSize scrollOffsetDifference = adjustedScrollOffset() - oldScrollOffset; |
| localExposeRect.move(-scrollOffsetDifference); |
| newRect = LayoutRect(box->localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox()); |
| } |
| } else if (!parentLayer && renderer()->isBox() && renderBox()->canBeProgramaticallyScrolled()) { |
| if (frameView) { |
| Element* ownerElement = renderer()->document().ownerElement(); |
| |
| if (ownerElement && ownerElement->renderer()) { |
| HTMLFrameElementBase* frameElementBase = 0; |
| |
| if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) |
| frameElementBase = toHTMLFrameElementBase(ownerElement); |
| |
| if (frameElementAndViewPermitScroll(frameElementBase, frameView)) { |
| LayoutRect viewRect = frameView->visibleContentRect(); |
| LayoutRect exposeRect = getRectToExpose(viewRect, rect, alignX, alignY); |
| |
| int xOffset = roundToInt(exposeRect.x()); |
| int yOffset = roundToInt(exposeRect.y()); |
| // Adjust offsets if they're outside of the allowable range. |
| xOffset = max(0, min(frameView->contentsWidth(), xOffset)); |
| yOffset = max(0, min(frameView->contentsHeight(), yOffset)); |
| |
| frameView->setScrollPosition(IntPoint(xOffset, yOffset)); |
| if (frameView->safeToPropagateScrollToParent()) { |
| parentLayer = ownerElement->renderer()->enclosingLayer(); |
| // FIXME: This doesn't correctly convert the rect to |
| // absolute coordinates in the parent. |
| newRect.setX(rect.x() - frameView->scrollX() + frameView->x()); |
| newRect.setY(rect.y() - frameView->scrollY() + frameView->y()); |
| } else |
| parentLayer = 0; |
| } |
| } else { |
| LayoutRect viewRect = frameView->visibleContentRect(); |
| LayoutRect r = getRectToExpose(viewRect, rect, alignX, alignY); |
| frameView->setScrollPosition(roundedIntPoint(r.location())); |
| } |
| } |
| } |
| |
| if (parentLayer) |
| parentLayer->scrollRectToVisible(newRect, alignX, alignY); |
| |
| if (frameView) |
| frameView->resumeScheduledEvents(); |
| } |
| |
| void RenderLayer::updateCompositingLayersAfterScroll() |
| { |
| if (compositor()->inCompositingMode()) { |
| // Our stacking container is guaranteed to contain all of our descendants that may need |
| // repositioning, so update compositing layers from there. |
| if (RenderLayer* compositingAncestor = ancestorStackingContainer()->enclosingCompositingLayer()) { |
| if (usesCompositedScrolling()) |
| compositor()->updateCompositingLayers(CompositingUpdateOnCompositedScroll, compositingAncestor); |
| else |
| compositor()->updateCompositingLayers(CompositingUpdateOnScroll, compositingAncestor); |
| } |
| } |
| } |
| |
| LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) |
| { |
| // Determine the appropriate X behavior. |
| ScrollBehavior scrollX; |
| LayoutRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height()); |
| LayoutUnit intersectWidth = intersection(visibleRect, exposeRectX).width(); |
| if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL) |
| // If the rectangle is fully visible, use the specified visible behavior. |
| // If the rectangle is partially visible, but over a certain threshold, |
| // then treat it as fully visible to avoid unnecessary horizontal scrolling |
| scrollX = ScrollAlignment::getVisibleBehavior(alignX); |
| else if (intersectWidth == visibleRect.width()) { |
| // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. |
| scrollX = ScrollAlignment::getVisibleBehavior(alignX); |
| if (scrollX == alignCenter) |
| scrollX = noScroll; |
| } else if (intersectWidth > 0) |
| // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior |
| scrollX = ScrollAlignment::getPartialBehavior(alignX); |
| else |
| scrollX = ScrollAlignment::getHiddenBehavior(alignX); |
| // If we're trying to align to the closest edge, and the exposeRect is further right |
| // than the visibleRect, and not bigger than the visible area, then align with the right. |
| if (scrollX == alignToClosestEdge && exposeRect.maxX() > visibleRect.maxX() && exposeRect.width() < visibleRect.width()) |
| scrollX = alignRight; |
| |
| // Given the X behavior, compute the X coordinate. |
| LayoutUnit x; |
| if (scrollX == noScroll) |
| x = visibleRect.x(); |
| else if (scrollX == alignRight) |
| x = exposeRect.maxX() - visibleRect.width(); |
| else if (scrollX == alignCenter) |
| x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2; |
| else |
| x = exposeRect.x(); |
| |
| // Determine the appropriate Y behavior. |
| ScrollBehavior scrollY; |
| LayoutRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height()); |
| LayoutUnit intersectHeight = intersection(visibleRect, exposeRectY).height(); |
| if (intersectHeight == exposeRect.height()) |
| // If the rectangle is fully visible, use the specified visible behavior. |
| scrollY = ScrollAlignment::getVisibleBehavior(alignY); |
| else if (intersectHeight == visibleRect.height()) { |
| // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. |
| scrollY = ScrollAlignment::getVisibleBehavior(alignY); |
| if (scrollY == alignCenter) |
| scrollY = noScroll; |
| } else if (intersectHeight > 0) |
| // If the rectangle is partially visible, use the specified partial behavior |
| scrollY = ScrollAlignment::getPartialBehavior(alignY); |
| else |
| scrollY = ScrollAlignment::getHiddenBehavior(alignY); |
| // If we're trying to align to the closest edge, and the exposeRect is further down |
| // than the visibleRect, and not bigger than the visible area, then align with the bottom. |
| if (scrollY == alignToClosestEdge && exposeRect.maxY() > visibleRect.maxY() && exposeRect.height() < visibleRect.height()) |
| scrollY = alignBottom; |
| |
| // Given the Y behavior, compute the Y coordinate. |
| LayoutUnit y; |
| if (scrollY == noScroll) |
| y = visibleRect.y(); |
| else if (scrollY == alignBottom) |
| y = exposeRect.maxY() - visibleRect.height(); |
| else if (scrollY == alignCenter) |
| y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2; |
| else |
| y = exposeRect.y(); |
| |
| return LayoutRect(LayoutPoint(x, y), visibleRect.size()); |
| } |
| |
| void RenderLayer::autoscroll(const IntPoint& position) |
| { |
| Frame* frame = renderer()->frame(); |
| if (!frame) |
| return; |
| |
| FrameView* frameView = frame->view(); |
| if (!frameView) |
| return; |
| |
| IntPoint currentDocumentPosition = frameView->windowToContents(position); |
| scrollRectToVisible(LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); |
| } |
| |
| bool RenderLayer::canResize() const |
| { |
| if (!renderer()) |
| return false; |
| // We need a special case for <iframe> because they never have |
| // hasOverflowClip(). However, they do "implicitly" clip their contents, so |
| // we want to allow resizing them also. |
| return (renderer()->hasOverflowClip() || renderer()->isRenderIFrame()) && renderer()->style()->resize() != RESIZE_NONE; |
| } |
| |
| void RenderLayer::resize(const PlatformEvent& evt, const LayoutSize& oldOffset) |
| { |
| // FIXME: This should be possible on generated content but is not right now. |
| if (!inResizeMode() || !canResize() || !renderer()->node()) |
| return; |
| |
| ASSERT(renderer()->node()->isElementNode()); |
| Element* element = toElement(renderer()->node()); |
| RenderBox* renderer = toRenderBox(element->renderer()); |
| |
| Document& document = element->document(); |
| |
| IntPoint pos; |
| const PlatformGestureEvent* gevt = 0; |
| |
| switch (evt.type()) { |
| case PlatformEvent::MouseMoved: |
| if (!document.frame()->eventHandler()->mousePressed()) |
| return; |
| pos = static_cast<const PlatformMouseEvent*>(&evt)->position(); |
| break; |
| case PlatformEvent::GestureScrollUpdate: |
| case PlatformEvent::GestureScrollUpdateWithoutPropagation: |
| pos = static_cast<const PlatformGestureEvent*>(&evt)->position(); |
| gevt = static_cast<const PlatformGestureEvent*>(&evt); |
| pos = gevt->position(); |
| pos.move(gevt->deltaX(), gevt->deltaY()); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| |
| float zoomFactor = renderer->style()->effectiveZoom(); |
| |
| LayoutSize newOffset = offsetFromResizeCorner(document.view()->windowToContents(pos)); |
| newOffset.setWidth(newOffset.width() / zoomFactor); |
| newOffset.setHeight(newOffset.height() / zoomFactor); |
| |
| LayoutSize currentSize = LayoutSize(renderer->width() / zoomFactor, renderer->height() / zoomFactor); |
| LayoutSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize); |
| element->setMinimumSizeForResizing(minimumSize); |
| |
| LayoutSize adjustedOldOffset = LayoutSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor); |
| if (renderer->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { |
| newOffset.setWidth(-newOffset.width()); |
| adjustedOldOffset.setWidth(-adjustedOldOffset.width()); |
| } |
| |
| LayoutSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize; |
| |
| bool isBoxSizingBorder = renderer->style()->boxSizing() == BORDER_BOX; |
| |
| EResize resize = renderer->style()->resize(); |
| if (resize != RESIZE_VERTICAL && difference.width()) { |
| if (element->isFormControlElement()) { |
| // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). |
| element->setInlineStyleProperty(CSSPropertyMarginLeft, renderer->marginLeft() / zoomFactor, CSSPrimitiveValue::CSS_PX); |
| element->setInlineStyleProperty(CSSPropertyMarginRight, renderer->marginRight() / zoomFactor, CSSPrimitiveValue::CSS_PX); |
| } |
| LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? LayoutUnit() : renderer->borderAndPaddingWidth()); |
| baseWidth = baseWidth / zoomFactor; |
| element->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth + difference.width()), CSSPrimitiveValue::CSS_PX); |
| } |
| |
| if (resize != RESIZE_HORIZONTAL && difference.height()) { |
| if (element->isFormControlElement()) { |
| // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). |
| element->setInlineStyleProperty(CSSPropertyMarginTop, renderer->marginTop() / zoomFactor, CSSPrimitiveValue::CSS_PX); |
| element->setInlineStyleProperty(CSSPropertyMarginBottom, renderer->marginBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX); |
| } |
| LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? LayoutUnit() : renderer->borderAndPaddingHeight()); |
| baseHeight = baseHeight / zoomFactor; |
| element->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight + difference.height()), CSSPrimitiveValue::CSS_PX); |
| } |
| |
| document.updateLayout(); |
| |
| // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view. |
| } |
| |
| int RenderLayer::scrollSize(ScrollbarOrientation orientation) const |
| { |
| IntSize scrollDimensions = scrollableArea()->maximumScrollPosition() - scrollableArea()->minimumScrollPosition(); |
| return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height(); |
| } |
| |
| IntSize RenderLayer::overhangAmount() const |
| { |
| return IntSize(); |
| } |
| |
| bool RenderLayer::isActive() const |
| { |
| Page* page = renderer()->frame()->page(); |
| return page && page->focusController().isActive(); |
| } |
| |
| static int cornerStart(const RenderStyle* style, int minX, int maxX, int thickness) |
| { |
| if (style->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) |
| return minX + style->borderLeftWidth(); |
| return maxX - thickness - style->borderRightWidth(); |
| } |
| |
| static IntRect cornerRect(const RenderStyle* style, const Scrollbar* horizontalScrollbar, const Scrollbar* verticalScrollbar, const IntRect& bounds) |
| { |
| int horizontalThickness; |
| int verticalThickness; |
| if (!verticalScrollbar && !horizontalScrollbar) { |
| // FIXME: This isn't right. We need to know the thickness of custom scrollbars |
| // even when they don't exist in order to set the resizer square size properly. |
| horizontalThickness = ScrollbarTheme::theme()->scrollbarThickness(); |
| verticalThickness = horizontalThickness; |
| } else if (verticalScrollbar && !horizontalScrollbar) { |
| horizontalThickness = verticalScrollbar->width(); |
| verticalThickness = horizontalThickness; |
| } else if (horizontalScrollbar && !verticalScrollbar) { |
| verticalThickness = horizontalScrollbar->height(); |
| horizontalThickness = verticalThickness; |
| } else { |
| horizontalThickness = verticalScrollbar->width(); |
| verticalThickness = horizontalScrollbar->height(); |
| } |
| return IntRect(cornerStart(style, bounds.x(), bounds.maxX(), horizontalThickness), |
| bounds.maxY() - verticalThickness - style->borderBottomWidth(), |
| horizontalThickness, verticalThickness); |
| } |
| |
| IntRect RenderLayer::scrollCornerRect() const |
| { |
| // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box. |
| // This happens when: |
| // (a) A resizer is present and at least one scrollbar is present |
| // (b) Both scrollbars are present. |
| bool hasHorizontalBar = horizontalScrollbar(); |
| bool hasVerticalBar = verticalScrollbar(); |
| bool hasResizer = renderer()->style()->resize() != RESIZE_NONE; |
| if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar))) |
| return cornerRect(renderer()->style(), horizontalScrollbar(), verticalScrollbar(), renderBox()->pixelSnappedBorderBoxRect()); |
| return IntRect(); |
| } |
| |
| IntRect RenderLayer::resizerCornerRect(const IntRect& bounds, ResizerHitTestType resizerHitTestType) const |
| { |
| ASSERT(renderer()->isBox()); |
| if (renderer()->style()->resize() == RESIZE_NONE) |
| return IntRect(); |
| IntRect corner = cornerRect(renderer()->style(), horizontalScrollbar(), verticalScrollbar(), bounds); |
| |
| if (resizerHitTestType == ResizerForTouch) { |
| // We make the resizer virtually larger for touch hit testing. With the |
| // expanding ratio k = ResizerControlExpandRatioForTouch, we first move |
| // the resizer rect (of width w & height h), by (-w * (k-1), -h * (k-1)), |
| // then expand the rect by new_w/h = w/h * k. |
| int expand_ratio = ResizerControlExpandRatioForTouch - 1; |
| corner.move(-corner.width() * expand_ratio, -corner.height() * expand_ratio); |
| corner.expand(corner.width() * expand_ratio, corner.height() * expand_ratio); |
| } |
| |
| return corner; |
| } |
| |
| IntRect RenderLayer::scrollCornerAndResizerRect() const |
| { |
| RenderBox* box = renderBox(); |
| if (!box) |
| return IntRect(); |
| IntRect scrollCornerAndResizer = scrollCornerRect(); |
| if (scrollCornerAndResizer.isEmpty()) |
| scrollCornerAndResizer = resizerCornerRect(box->pixelSnappedBorderBoxRect(), ResizerForPointer); |
| return scrollCornerAndResizer; |
| } |
| |
| bool RenderLayer::isScrollCornerVisible() const |
| { |
| ASSERT(renderer()->isBox()); |
| return !scrollCornerRect().isEmpty(); |
| } |
| |
| IntRect RenderLayer::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const |
| { |
| RenderView* view = renderer()->view(); |
| if (!view) |
| return scrollbarRect; |
| |
| IntRect rect = scrollbarRect; |
| rect.move(scrollbarOffset(scrollbar)); |
| |
| return view->frameView()->convertFromRenderer(renderer(), rect); |
| } |
| |
| IntRect RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const |
| { |
| RenderView* view = renderer()->view(); |
| if (!view) |
| return parentRect; |
| |
| IntRect rect = view->frameView()->convertToRenderer(renderer(), parentRect); |
| rect.move(-scrollbarOffset(scrollbar)); |
| return rect; |
| } |
| |
| IntPoint RenderLayer::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const |
| { |
| RenderView* view = renderer()->view(); |
| if (!view) |
| return scrollbarPoint; |
| |
| IntPoint point = scrollbarPoint; |
| point.move(scrollbarOffset(scrollbar)); |
| return view->frameView()->convertFromRenderer(renderer(), point); |
| } |
| |
| IntPoint RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const |
| { |
| RenderView* view = renderer()->view(); |
| if (!view) |
| return parentPoint; |
| |
| IntPoint point = view->frameView()->convertToRenderer(renderer(), parentPoint); |
| |
| point.move(-scrollbarOffset(scrollbar)); |
| return point; |
| } |
| |
| int RenderLayer::visibleHeight() const |
| { |
| return m_layerSize.height(); |
| } |
| |
| int RenderLayer::visibleWidth() const |
| { |
| return m_layerSize.width(); |
| } |
| |
| bool RenderLayer::shouldSuspendScrollAnimations() const |
| { |
| RenderView* view = renderer()->view(); |
| if (!view) |
| return true; |
| return view->frameView()->shouldSuspendScrollAnimations(); |
| } |
| |
| bool RenderLayer::scrollbarsCanBeActive() const |
| { |
| RenderView* view = renderer()->view(); |
| if (!view) |
| return false; |
| return view->frameView()->scrollbarsCanBeActive(); |
| } |
| |
| IntPoint RenderLayer::lastKnownMousePosition() const |
| { |
| return renderer()->frame() ? renderer()->frame()->eventHandler()->lastKnownMousePosition() : IntPoint(); |
| } |
| |
| IntRect RenderLayer::rectForHorizontalScrollbar(const IntRect& borderBoxRect) const |
| { |
| if (!m_hBar) |
| return IntRect(); |
| |
| const RenderBox* box = renderBox(); |
| const IntRect& scrollCorner = scrollCornerRect(); |
| |
| return IntRect(horizontalScrollbarStart(borderBoxRect.x()), |
| borderBoxRect.maxY() - box->borderBottom() - m_hBar->height(), |
| borderBoxRect.width() - (box->borderLeft() + box->borderRight()) - scrollCorner.width(), |
| m_hBar->height()); |
| } |
| |
| IntRect RenderLayer::rectForVerticalScrollbar(const IntRect& borderBoxRect) const |
| { |
| if (!m_vBar) |
| return IntRect(); |
| |
| const RenderBox* box = renderBox(); |
| const IntRect& scrollCorner = scrollCornerRect(); |
| |
| return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX()), |
| borderBoxRect.y() + box->borderTop(), |
| m_vBar->width(), |
| borderBoxRect.height() - (box->borderTop() + box->borderBottom()) - scrollCorner.height()); |
| } |
| |
| LayoutUnit RenderLayer::verticalScrollbarStart(int minX, int maxX) const |
| { |
| const RenderBox* box = renderBox(); |
| if (renderer()->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) |
| return minX + box->borderLeft(); |
| return maxX - box->borderRight() - m_vBar->width(); |
| } |
| |
| LayoutUnit RenderLayer::horizontalScrollbarStart(int minX) const |
| { |
| const RenderBox* box = renderBox(); |
| int x = minX + box->borderLeft(); |
| if (renderer()->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) |
| x += m_vBar ? m_vBar->width() : resizerCornerRect(box->pixelSnappedBorderBoxRect(), ResizerForPointer).width(); |
| return x; |
| } |
| |
| IntSize RenderLayer::scrollbarOffset(const Scrollbar* scrollbar) const |
| { |
| RenderBox* box = renderBox(); |
| |
| if (scrollbar == m_vBar.get()) |
| return IntSize(verticalScrollbarStart(0, box->width()), box->borderTop()); |
| |
| if (scrollbar == m_hBar.get()) |
| return IntSize(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar->height()); |
| |
| ASSERT_NOT_REACHED(); |
| return IntSize(); |
| } |
| |
| void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) |
| { |
| if (scrollbar == m_vBar.get()) { |
| if (GraphicsLayer* layer = layerForVerticalScrollbar()) { |
| layer->setNeedsDisplayInRect(rect); |
| return; |
| } |
| } else { |
| if (GraphicsLayer* layer = layerForHorizontalScrollbar()) { |
| layer->setNeedsDisplayInRect(rect); |
| return; |
| } |
| } |
| |
| IntRect scrollRect = rect; |
| RenderBox* box = renderBox(); |
| ASSERT(box); |
| // If we are not yet inserted into the tree, there is no need to repaint. |
| if (!box->parent()) |
| return; |
| |
| if (scrollbar == m_vBar.get()) |
| scrollRect.move(verticalScrollbarStart(0, box->width()), box->borderTop()); |
| else |
| scrollRect.move(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar->height()); |
| renderer()->repaintRectangle(scrollRect); |
| } |
| |
| void RenderLayer::invalidateScrollCornerRect(const IntRect& rect) |
| { |
| if (GraphicsLayer* layer = layerForScrollCorner()) { |
| layer->setNeedsDisplayInRect(rect); |
| return; |
| } |
| |
| if (m_scrollCorner) |
| m_scrollCorner->repaintRectangle(rect); |
| if (m_resizer) |
| m_resizer->repaintRectangle(rect); |
| } |
| |
| static inline RenderObject* rendererForScrollbar(RenderObject* renderer) |
| { |
| if (Node* node = renderer->node()) { |
| if (ShadowRoot* shadowRoot = node->containingShadowRoot()) { |
| if (shadowRoot->type() == ShadowRoot::UserAgentShadowRoot) |
| return shadowRoot->host()->renderer(); |
| } |
| } |
| |
| return renderer; |
| } |
| |
| PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) |
| { |
| RefPtr<Scrollbar> widget; |
| RenderObject* actualRenderer = rendererForScrollbar(renderer()); |
| bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style()->hasPseudoStyle(SCROLLBAR); |
| if (hasCustomScrollbarStyle) |
| widget = RenderScrollbar::createCustomScrollbar(scrollableArea(), orientation, actualRenderer->node()); |
| else { |
| widget = Scrollbar::createNativeScrollbar(scrollableArea(), orientation, RegularScrollbar); |
| if (orientation == HorizontalScrollbar) |
| scrollableArea()->didAddHorizontalScrollbar(widget.get()); |
| else |
| scrollableArea()->didAddVerticalScrollbar(widget.get()); |
| } |
| renderer()->document().view()->addChild(widget.get()); |
| return widget.release(); |
| } |
| |
| void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation) |
| { |
| RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar; |
| if (!scrollbar) |
| return; |
| |
| if (!scrollbar->isCustomScrollbar()) { |
| if (orientation == HorizontalScrollbar) |
| scrollableArea()->willRemoveHorizontalScrollbar(scrollbar.get()); |
| else |
| scrollableArea()->willRemoveVerticalScrollbar(scrollbar.get()); |
| } |
| |
| scrollbar->removeFromParent(); |
| scrollbar->disconnectFromScrollableArea(); |
| scrollbar = 0; |
| } |
| |
| void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) |
| { |
| if (hasScrollbar == hasHorizontalScrollbar()) |
| return; |
| |
| if (hasScrollbar) |
| m_hBar = createScrollbar(HorizontalScrollbar); |
| else |
| destroyScrollbar(HorizontalScrollbar); |
| |
| // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. |
| if (m_hBar) |
| m_hBar->styleChanged(); |
| if (m_vBar) |
| m_vBar->styleChanged(); |
| |
| // Force an update since we know the scrollbars have changed things. |
| if (renderer()->document().hasAnnotatedRegions()) |
| renderer()->document().setAnnotatedRegionsDirty(true); |
| } |
| |
| void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar) |
| { |
| if (hasScrollbar == hasVerticalScrollbar()) |
| return; |
| |
| if (hasScrollbar) |
| m_vBar = createScrollbar(VerticalScrollbar); |
| else |
| destroyScrollbar(VerticalScrollbar); |
| |
| // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. |
| if (m_hBar) |
| m_hBar->styleChanged(); |
| if (m_vBar) |
| m_vBar->styleChanged(); |
| |
| // Force an update since we know the scrollbars have changed things. |
| if (renderer()->document().hasAnnotatedRegions()) |
| renderer()->document().setAnnotatedRegionsDirty(true); |
| } |
| |
| ScrollableArea* RenderLayer::enclosingScrollableArea() const |
| { |
| if (RenderLayer* scrollableLayer = enclosingScrollableLayer()) |
| return scrollableLayer->scrollableArea(); |
| |
| // FIXME: We should return the frame view here (or possibly an ancestor frame view, |
| // if the frame view isn't scrollable. |
| return 0; |
| } |
| |
| int RenderLayer::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const |
| { |
| if (!m_vBar || (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting()))) |
| return 0; |
| return m_vBar->width(); |
| } |
| |
| int RenderLayer::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const |
| { |
| if (!m_hBar || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting()))) |
| return 0 |