diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
commit | 2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47 (patch) | |
tree | 988e8c5b116dd0466244ae2fe5af8ee9be926d76 /Source/WebCore/rendering/RenderLayer.cpp | |
parent | dd91e772430dc294e3bf478c119ef8d43c0a3358 (diff) | |
download | qtwebkit-2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47.tar.gz |
Imported WebKit commit 7e538425aa020340619e927792f3d895061fb54b (http://svn.webkit.org/repository/webkit/trunk@116286)
Diffstat (limited to 'Source/WebCore/rendering/RenderLayer.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderLayer.cpp | 788 |
1 files changed, 602 insertions, 186 deletions
diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp index 9cf767ee8..33379cf24 100644 --- a/Source/WebCore/rendering/RenderLayer.cpp +++ b/Source/WebCore/rendering/RenderLayer.cpp @@ -46,7 +46,6 @@ #include "ColumnInfo.h" #include "CSSPropertyNames.h" -#include "CSSStyleSelector.h" #include "Chrome.h" #include "Document.h" #include "DocumentEventQueue.h" @@ -92,6 +91,7 @@ #include "Settings.h" #include "SourceGraphic.h" #include "StylePropertySet.h" +#include "StyleResolver.h" #include "TextStream.h" #include "TransformationMatrix.h" #include "TranslateTransformOperation.h" @@ -165,7 +165,13 @@ RenderLayer::RenderLayer(RenderBoxModelObject* renderer) , m_mustOverlapCompositedLayers(false) #endif , m_containsDirtyOverlayScrollbars(false) +#if !ASSERT_DISABLED + , m_layerListMutationAllowed(true) +#endif , m_canSkipRepaintRectsUpdateOnScroll(renderer->isTableCell()) +#if ENABLE(CSS_FILTERS) + , m_hasFilterInfo(false) +#endif , m_renderer(renderer) , m_parent(0) , m_previous(0) @@ -194,6 +200,14 @@ RenderLayer::RenderLayer(RenderBoxModelObject* renderer) m_visibleContentStatusDirty = false; m_hasVisibleContent = renderer->style()->visibility() == VISIBLE; } + + Node* node = renderer->node(); + if (node && node->isElementNode()) { + // We save and restore only the scrollOffset as the other scroll values are recalculated. + Element* element = toElement(node); + m_scrollOffset = element->savedLayerScrollOffset(); + element->setSavedLayerScrollOffset(IntSize()); + } } RenderLayer::~RenderLayer() @@ -208,11 +222,21 @@ RenderLayer::~RenderLayer() frameView->removeScrollableArea(this); } + if (!m_renderer->documentBeingDestroyed()) { + Node* node = m_renderer->node(); + if (node && node->isElementNode()) + toElement(node)->setSavedLayerScrollOffset(m_scrollOffset); + } + destroyScrollbar(HorizontalScrollbar); destroyScrollbar(VerticalScrollbar); if (m_reflection) removeReflection(); + +#if ENABLE(CSS_FILTERS) + removeFilterInfoIfNeeded(); +#endif // Child layers will be deleted by their corresponding render objects, so // we don't need to delete them ourselves. @@ -223,7 +247,7 @@ RenderLayer::~RenderLayer() delete m_marquee; #if USE(ACCELERATED_COMPOSITING) - clearBacking(); + clearBacking(true); #endif // Make sure we have no lingering clip rects. @@ -253,15 +277,6 @@ void RenderLayer::contentChanged(ContentChangeType changeType) } #endif // USE(ACCELERATED_COMPOSITING) -bool RenderLayer::hasAcceleratedCompositing() const -{ -#if USE(ACCELERATED_COMPOSITING) - return compositor()->hasAcceleratedCompositing(); -#else - return false; -#endif -} - bool RenderLayer::canRender3DTransforms() const { #if USE(ACCELERATED_COMPOSITING) @@ -278,14 +293,24 @@ bool RenderLayer::paintsWithFilters() const if (!renderer()->hasFilter()) return false; +#if USE(ACCELERATED_COMPOSITING) if (!isComposited()) return true; if (!m_backing || !m_backing->canCompositeFilters()) return true; +#endif return false; } + +bool RenderLayer::requiresFullLayerImageForFilters() const +{ + if (!paintsWithFilters()) + return false; + FilterEffectRenderer* filter = filterRenderer(); + return filter ? filter->hasFilterThatMovesPixels() : false; +} #endif LayoutPoint RenderLayer::computeOffsetFromRoot(bool& hasLayerOffset) const @@ -739,12 +764,13 @@ void RenderLayer::updateLayerPosition() 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()->isRenderInline()) { RenderInline* inlineFlow = toRenderInline(renderer()); - LayoutRect lineBox = inlineFlow->linesBoundingBox(); + IntRect lineBox = inlineFlow->linesBoundingBox(); setSize(lineBox.size()); inlineBoundingBoxOffset = toSize(lineBox.location()); localPoint += inlineBoundingBoxOffset; } else if (RenderBox* box = renderBox()) { - setSize(box->size()); + // FIXME: Is snapping the size really needed here for the RenderBox case? + setSize(pixelSnappedIntSize(box->size(), box->location())); localPoint += box->topLeftLocationOffset(); } @@ -820,8 +846,8 @@ TransformationMatrix RenderLayer::perspectiveTransform() const const float boxWidth = borderBox.width(); const float boxHeight = borderBox.height(); - float perspectiveOriginX = style->perspectiveOriginX().calcFloatValue(boxWidth); - float perspectiveOriginY = style->perspectiveOriginY().calcFloatValue(boxHeight); + 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. @@ -844,8 +870,8 @@ FloatPoint RenderLayer::perspectiveOrigin() const const LayoutRect borderBox = toRenderBox(renderer())->borderBoxRect(); RenderStyle* style = renderer()->style(); - return FloatPoint(style->perspectiveOriginX().calcFloatValue(borderBox.width()), - style->perspectiveOriginY().calcFloatValue(borderBox.height())); + return FloatPoint(floatValueForLength(style->perspectiveOriginX(), borderBox.width()), + floatValueForLength(style->perspectiveOriginY(), borderBox.height())); } RenderLayer* RenderLayer::stackingContext() const @@ -934,8 +960,107 @@ RenderLayer* RenderLayer::enclosingCompositingLayer(bool includeSelf) const 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; +} +#endif + +#if ENABLE(CSS_FILTERS) +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, bool immediate) +{ + if (rect.isEmpty()) + return; + + LayoutRect rectForRepaint = rect; + +#if ENABLE(CSS_FILTERS) + if (renderer()->style()->hasFilterOutsets()) { + int topOutset; + int rightOutset; + int bottomOutset; + int leftOutset; + renderer()->style()->getFilterOutsets(topOutset, rightOutset, bottomOutset, leftOutset); + rectForRepaint.move(-leftOutset, -topOutset); + rectForRepaint.expand(leftOutset + rightOutset, topOutset + bottomOutset); + } +#endif + + RenderLayerFilterInfo* filterInfo = this->filterInfo(); + ASSERT(filterInfo); + filterInfo->expandDirtySourceRect(rectForRepaint); + +#if ENABLE(CSS_SHADERS) + 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, this)); + } +#endif + + RenderLayer* parentLayer = enclosingFilterRepaintLayer(); + ASSERT(parentLayer); + FloatQuad repaintQuad(rectForRepaint); + LayoutRect parentLayerRect = renderer()->localToContainerQuad(repaintQuad, parentLayer->renderer()).enclosingBoundingBox(); + +#if USE(ACCELERATED_COMPOSITING) + if (parentLayer->isComposited()) { + if (!parentLayer->backing()->paintsIntoWindow()) { + parentLayer->setBackingNeedsRepaintInRect(parentLayerRect); + return; + } + // If the painting goes to window, redirect the painting to the parent RenderView. + parentLayer = renderer()->view()->layer(); + parentLayerRect = renderer()->localToContainerQuad(repaintQuad, parentLayer->renderer()).enclosingBoundingBox(); + } #endif + if (parentLayer->paintsWithFilters()) { + parentLayer->setFilterBackendNeedsRepaintingInRect(parentLayerRect, immediate); + return; + } + + if (parentLayer->isRootLayer()) { + RenderView* view = toRenderView(parentLayer->renderer()); + view->repaintViewRectangle(parentLayerRect, immediate); + return; + } + + ASSERT_NOT_REACHED(); +} +#endif + RenderLayer* RenderLayer::clippingRoot() const { #if USE(ACCELERATED_COMPOSITING) @@ -952,7 +1077,7 @@ RenderLayer* RenderLayer::clippingRoot() const ASSERT(current); if (current->transform() #if USE(ACCELERATED_COMPOSITING) - || current->isComposited() + || (current->isComposited() && !current->backing()->paintsIntoCompositedAncestor()) #endif ) return const_cast<RenderLayer*>(current); @@ -1480,23 +1605,9 @@ void RenderLayer::scrollTo(int x, int y) view->updateWidgetPositions(); } -#if USE(ACCELERATED_COMPOSITING) - if (compositor()->inCompositingMode()) { - // Our stacking context is guaranteed to contain all of our descendants that may need - // repositioning, so update compositing layers from there. - if (RenderLayer* compositingAncestor = stackingContext()->enclosingCompositingLayer()) { - if (compositor()->compositingConsultsOverlap()) - compositor()->updateCompositingLayers(CompositingUpdateOnScroll, compositingAncestor); - else { - bool isUpdateRoot = true; - compositingAncestor->backing()->updateAfterLayout(RenderLayerBacking::AllDescendants, isUpdateRoot); - } - } - } -#endif + updateCompositingLayersAfterScroll(); RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); - Frame* frame = renderer()->frame(); if (frame) { // The caret rect needs to be invalidated after scrolling @@ -1587,9 +1698,12 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignm yOffset = max(0, min(frameView->contentsHeight(), yOffset)); frameView->setScrollPosition(IntPoint(xOffset, yOffset)); - parentLayer = ownerElement->renderer()->enclosingLayer(); - newRect.setX(rect.x() - frameView->scrollX() + frameView->x()); - newRect.setY(rect.y() - frameView->scrollY() + frameView->y()); + if (frameView->safeToPropagateScrollToParent()) { + parentLayer = ownerElement->renderer()->enclosingLayer(); + newRect.setX(rect.x() - frameView->scrollX() + frameView->x()); + newRect.setY(rect.y() - frameView->scrollY() + frameView->y()); + } else + parentLayer = 0; } } else { LayoutRect viewRect = frameView->visibleContentRect(); @@ -1617,6 +1731,24 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignm frameView->resumeScheduledEvents(); } +void RenderLayer::updateCompositingLayersAfterScroll() +{ +#if USE(ACCELERATED_COMPOSITING) + if (compositor()->inCompositingMode()) { + // Our stacking context is guaranteed to contain all of our descendants that may need + // repositioning, so update compositing layers from there. + if (RenderLayer* compositingAncestor = stackingContext()->enclosingCompositingLayer()) { + if (compositor()->compositingConsultsOverlap()) + compositor()->updateCompositingLayers(CompositingUpdateOnScroll, compositingAncestor); + else { + bool isUpdateRoot = true; + compositingAncestor->backing()->updateAfterLayout(RenderLayerBacking::AllDescendants, isUpdateRoot); + } + } + } +#endif +} + LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) { // Determine the appropriate X behavior. @@ -1755,9 +1887,9 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOff styledElement->setInlineStyleProperty(CSSPropertyMarginLeft, String::number(renderer->marginLeft() / zoomFactor) + "px", false); styledElement->setInlineStyleProperty(CSSPropertyMarginRight, String::number(renderer->marginRight() / zoomFactor) + "px", false); } - LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? zeroLayoutUnit : renderer->borderAndPaddingWidth()); + LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? ZERO_LAYOUT_UNIT : renderer->borderAndPaddingWidth()); baseWidth = baseWidth / zoomFactor; - styledElement->setInlineStyleProperty(CSSPropertyWidth, String::number(baseWidth + difference.width()) + "px", false); + styledElement->setInlineStyleProperty(CSSPropertyWidth, String::number(roundToInt(baseWidth + difference.width())) + "px", false); } if (resize != RESIZE_HORIZONTAL && difference.height()) { @@ -1766,9 +1898,9 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOff styledElement->setInlineStyleProperty(CSSPropertyMarginTop, String::number(renderer->marginTop() / zoomFactor) + "px", false); styledElement->setInlineStyleProperty(CSSPropertyMarginBottom, String::number(renderer->marginBottom() / zoomFactor) + "px", false); } - LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? zeroLayoutUnit : renderer->borderAndPaddingHeight()); + LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? ZERO_LAYOUT_UNIT : renderer->borderAndPaddingHeight()); baseHeight = baseHeight / zoomFactor; - styledElement->setInlineStyleProperty(CSSPropertyHeight, String::number(baseHeight + difference.height()) + "px", false); + styledElement->setInlineStyleProperty(CSSPropertyHeight, String::number(roundToInt(baseHeight + difference.height())) + "px", false); } document->updateLayout(); @@ -1878,7 +2010,7 @@ IntRect RenderLayer::scrollCornerRect() const bool hasVerticalBar = verticalScrollbar(); bool hasResizer = renderer()->style()->resize() != RESIZE_NONE; if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar))) - return cornerRect(this, pixelSnappedIntRect(renderBox()->borderBoxRect())); + return cornerRect(this, renderBox()->pixelSnappedBorderBoxRect()); return IntRect(); } @@ -1897,7 +2029,7 @@ IntRect RenderLayer::scrollCornerAndResizerRect() const return IntRect(); IntRect scrollCornerAndResizer = scrollCornerRect(); if (scrollCornerAndResizer.isEmpty()) - scrollCornerAndResizer = resizerCornerRect(this, pixelSnappedIntRect(box->borderBoxRect())); + scrollCornerAndResizer = resizerCornerRect(this, box->pixelSnappedBorderBoxRect()); return scrollCornerAndResizer; } @@ -1955,7 +2087,7 @@ IntPoint RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar* scro IntSize RenderLayer::contentsSize() const { - return IntSize(const_cast<RenderLayer*>(this)->scrollWidth(), const_cast<RenderLayer*>(this)->scrollHeight()); + return IntSize(scrollWidth(), scrollHeight()); } int RenderLayer::visibleHeight() const @@ -1999,7 +2131,7 @@ 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(this, box->borderBoxRect()).width(); + x += m_vBar ? m_vBar->width() : resizerCornerRect(this, box->pixelSnappedBorderBoxRect()).width(); return x; } @@ -2097,7 +2229,7 @@ bool RenderLayer::scrollsOverflow() const { if (!renderer()->isBox()) return false; - + return toRenderBox(renderer())->scrollsOverflow(); } @@ -2108,7 +2240,7 @@ bool RenderLayer::allowsScrolling() const void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) { - if (hasScrollbar == (m_hBar != 0)) + if (hasScrollbar == hasHorizontalScrollbar()) return; if (hasScrollbar) @@ -2131,7 +2263,7 @@ void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar) { - if (hasScrollbar == (m_vBar != 0)) + if (hasScrollbar == hasVerticalScrollbar()) return; if (hasScrollbar) @@ -2202,7 +2334,7 @@ void RenderLayer::positionOverflowControls(const IntSize& offsetFromLayer) if (!box) return; - const IntRect borderBox = pixelSnappedIntRect(box->borderBoxRect()); + const IntRect borderBox = box->pixelSnappedBorderBoxRect(); const IntRect& scrollCorner = scrollCornerRect(); IntRect absBounds(borderBox.location() + offsetFromLayer, borderBox.size()); if (m_vBar) @@ -2247,18 +2379,20 @@ void RenderLayer::positionOverflowControls(const IntSize& offsetFromLayer) m_resizer->setFrameRect(resizerCornerRect(this, borderBox)); } -int RenderLayer::scrollWidth() +int RenderLayer::scrollWidth() const { + ASSERT(renderBox()); if (m_scrollDimensionsDirty) - computeScrollDimensions(); - return m_scrollSize.width(); + const_cast<RenderLayer*>(this)->computeScrollDimensions(); + return snapSizeToPixel(m_scrollSize.width(), renderBox()->clientLeft()); } -int RenderLayer::scrollHeight() +int RenderLayer::scrollHeight() const { + ASSERT(renderBox()); if (m_scrollDimensionsDirty) - computeScrollDimensions(); - return m_scrollSize.height(); + const_cast<RenderLayer*>(this)->computeScrollDimensions(); + return snapSizeToPixel(m_scrollSize.height(), renderBox()->clientTop()); } LayoutUnit RenderLayer::overflowTop() const @@ -2293,11 +2427,11 @@ LayoutUnit RenderLayer::overflowRight() const return overflowRect.maxX(); } -void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) +void RenderLayer::computeScrollDimensions() { RenderBox* box = renderBox(); ASSERT(box); - + m_scrollDimensionsDirty = false; m_scrollOverflow.setWidth(overflowLeft() - box->borderLeft()); @@ -2305,60 +2439,47 @@ void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) m_scrollSize.setWidth(overflowRight() - overflowLeft()); m_scrollSize.setHeight(overflowBottom() - overflowTop()); - + setScrollOrigin(IntPoint(-m_scrollOverflow.width(), -m_scrollOverflow.height())); +} - if (needHBar) - *needHBar = scrollWidth() > box->pixelSnappedClientWidth(); - if (needVBar) - *needVBar = scrollHeight() > box->pixelSnappedClientHeight(); +bool RenderLayer::hasHorizontalOverflow() const +{ + ASSERT(!m_scrollDimensionsDirty); + + return scrollWidth() > renderBox()->pixelSnappedClientWidth(); } -void RenderLayer::updateScrollInfoAfterLayout() +bool RenderLayer::hasVerticalOverflow() const { - RenderBox* box = renderBox(); - if (!box) - return; + ASSERT(!m_scrollDimensionsDirty); - m_scrollDimensionsDirty = true; - IntSize scrollOffsetOriginal(scrollXOffset(), scrollYOffset()); + return scrollHeight() > renderBox()->pixelSnappedClientHeight(); +} - bool horizontalOverflow, verticalOverflow; - computeScrollDimensions(&horizontalOverflow, &verticalOverflow); +void RenderLayer::updateScrollbarsAfterLayout() +{ + RenderBox* box = renderBox(); + ASSERT(box); - if (box->style()->overflowX() != OMARQUEE) { - // Layout may cause us to be in an invalid scroll position. In this case we need - // to pull our scroll offsets back to the max (or push them up to the min). - int newX = max(0, min<int>(scrollXOffset(), scrollWidth() - box->clientWidth())); - int newY = max(0, min<int>(scrollYOffset(), scrollHeight() - box->clientHeight())); - if (newX != scrollXOffset() || newY != scrollYOffset()) - scrollToOffset(newX, newY); - } + bool hasHorizontalOverflow = this->hasHorizontalOverflow(); + bool hasVerticalOverflow = this->hasVerticalOverflow(); - bool haveHorizontalBar = m_hBar; - bool haveVerticalBar = m_vBar; - // overflow:scroll should just enable/disable. if (m_hBar && renderer()->style()->overflowX() == OSCROLL) - m_hBar->setEnabled(horizontalOverflow); + m_hBar->setEnabled(hasHorizontalOverflow); if (m_vBar && renderer()->style()->overflowY() == OSCROLL) - m_vBar->setEnabled(verticalOverflow); + m_vBar->setEnabled(hasVerticalOverflow); - // A dynamic change from a scrolling overflow to overflow:hidden means we need to get rid of any - // scrollbars that may be present. - if (renderer()->style()->overflowX() == OHIDDEN && haveHorizontalBar) - setHasHorizontalScrollbar(false); - if (renderer()->style()->overflowY() == OHIDDEN && haveVerticalBar) - setHasVerticalScrollbar(false); - // overflow:auto may need to lay out again if scrollbars got added/removed. - bool scrollbarsChanged = (box->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || - (box->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); - if (scrollbarsChanged) { + bool autoHorizontalScrollBarChanged = box->hasAutoHorizontalScrollbar() && (hasHorizontalScrollbar() != hasHorizontalOverflow); + bool autoVerticalScrollBarChanged = box->hasAutoVerticalScrollbar() && (hasVerticalScrollbar() != hasVerticalOverflow); + + if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) { if (box->hasAutoHorizontalScrollbar()) - setHasHorizontalScrollbar(horizontalOverflow); + setHasHorizontalScrollbar(hasHorizontalOverflow); if (box->hasAutoVerticalScrollbar()) - setHasVerticalScrollbar(verticalOverflow); + setHasVerticalScrollbar(hasVerticalOverflow); #if ENABLE(DASHBOARD_SUPPORT) // Force an update since we know the scrollbars have changed things. @@ -2372,24 +2493,17 @@ void RenderLayer::updateScrollInfoAfterLayout() if (!m_inOverflowRelayout) { // Our proprietary overflow: overlay value doesn't trigger a layout. m_inOverflowRelayout = true; - renderer()->setNeedsLayout(true, false); + renderer()->setNeedsLayout(true, MarkOnlyThis); if (renderer()->isRenderBlock()) { RenderBlock* block = toRenderBlock(renderer()); - block->scrollbarsChanged(box->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow, - box->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); - block->layoutBlock(true); // FIXME: Need to handle positioned floats triggering extra relayouts. + block->scrollbarsChanged(autoHorizontalScrollBarChanged, autoVerticalScrollBarChanged); + block->layoutBlock(true); } else renderer()->layout(); m_inOverflowRelayout = false; } } } - - // If overflow:scroll is turned into overflow:auto a bar might still be disabled (Bug 11985). - if (m_hBar && box->hasAutoHorizontalScrollbar()) - m_hBar->setEnabled(true); - if (m_vBar && box->hasAutoVerticalScrollbar()) - m_vBar->setEnabled(true); // Set up the range (and page step/line step). if (m_hBar) { @@ -2405,6 +2519,31 @@ void RenderLayer::updateScrollInfoAfterLayout() m_vBar->setProportion(clientHeight, m_scrollSize.height()); } + updateScrollableAreaSet((hasHorizontalOverflow || hasVerticalOverflow) && scrollsOverflow()); +} + +void RenderLayer::updateScrollInfoAfterLayout() +{ + RenderBox* box = renderBox(); + if (!box) + return; + + m_scrollDimensionsDirty = true; + IntSize scrollOffsetOriginal(scrollXOffset(), scrollYOffset()); + + computeScrollDimensions(); + + if (box->style()->overflowX() != OMARQUEE) { + // Layout may cause us to be at an invalid scroll position. In this case we need + // to pull our scroll offsets back to the max (or push them up to the min). + int newX = max(0, min<int>(scrollXOffset(), scrollWidth() - box->clientWidth())); + int newY = max(0, min<int>(scrollYOffset(), scrollHeight() - box->clientHeight())); + if (newX != scrollXOffset() || newY != scrollYOffset()) + scrollToOffset(newX, newY); + } + + updateScrollbarsAfterLayout(); + if (scrollOffsetOriginal != scrollOffset()) scrollToOffsetWithoutAnimation(IntPoint(scrollXOffset(), scrollYOffset())); } @@ -2534,7 +2673,7 @@ void RenderLayer::paintResizer(GraphicsContext* context, const IntPoint& paintOf RenderBox* box = renderBox(); ASSERT(box); - IntRect absRect = resizerCornerRect(this, pixelSnappedIntRect(box->borderBoxRect())); + IntRect absRect = resizerCornerRect(this, box->pixelSnappedBorderBoxRect()); absRect.moveBy(paintOffset); if (!absRect.intersects(damageRect)) return; @@ -2589,7 +2728,7 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& IntRect resizeControlRect; if (renderer()->style()->resize() != RESIZE_NONE) { - resizeControlRect = resizerCornerRect(this, pixelSnappedIntRect(box->borderBoxRect())); + resizeControlRect = resizerCornerRect(this, box->pixelSnappedBorderBoxRect()); if (resizeControlRect.contains(localPoint)) return true; } @@ -2747,7 +2886,7 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* context, // but we need to ensure that we don't cache clip rects computed with the wrong root in this case. if (context->updatingControlTints() || (paintBehavior & PaintBehaviorFlattenCompositingLayers)) paintFlags |= PaintLayerTemporaryClipRects; - else if (!backing()->paintingGoesToWindow() && !shouldDoSoftwarePaint(this, paintFlags & PaintLayerPaintingReflection)) { + else if (!backing()->paintsIntoWindow() && !backing()->paintsIntoCompositedAncestor() && !shouldDoSoftwarePaint(this, paintFlags & PaintLayerPaintingReflection)) { // If this RenderLayer should paint into its backing, that will be done via RenderLayerBacking::paintIntoLayer(). return; } @@ -2761,6 +2900,10 @@ void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* context, if (!renderer()->opacity()) return; + // Non self-painting leaf layers don't need to be painted as their renderer() should properly paint itself. + if (!isSelfPaintingLayer() && !firstChild()) + return; + if (paintsWithTransparency(paintBehavior)) paintFlags |= PaintLayerHaveTransparency; @@ -2836,7 +2979,7 @@ void RenderLayer::paintLayerContentsAndReflection(RenderLayer* rootLayer, Graphi } void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* context, - const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior, + const LayoutRect& parentPaintDirtyRect, PaintBehavior paintBehavior, RenderObject* paintingRoot, RenderRegion* region, OverlapTestRequestMap* overlapTestRequests, PaintLayerFlags paintFlags) { @@ -2852,14 +2995,50 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co LayoutRect layerBounds; ClipRect damageRect, clipRectToApply, outlineRect; LayoutPoint paintOffset; + LayoutRect paintDirtyRect = parentPaintDirtyRect; + + bool useClipRect = true; + GraphicsContext* transparencyLayerContext = context; + + // Ensure our lists are up-to-date. + updateLayerListsIfNeeded(); + +#if ENABLE(CSS_FILTERS) + FilterEffectRendererHelper filterPainter(filterRenderer() && paintsWithFilters()); + if (filterPainter.haveFilterEffect() && !context->paintingDisabled()) { + LayoutPoint rootLayerOffset; + convertToLayerCoords(rootLayer, rootLayerOffset); + RenderLayerFilterInfo* filterInfo = this->filterInfo(); + ASSERT(filterInfo); + LayoutRect filterRepaintRect = filterInfo->dirtySourceRect(); + filterRepaintRect.move(rootLayerOffset.x(), rootLayerOffset.y()); + if (filterPainter.prepareFilterEffect(this, calculateLayerBounds(this, rootLayer, 0), parentPaintDirtyRect, filterRepaintRect)) { + // Now we know for sure, that the source image will be updated, so we can revert our tracking repaint rect back to zero. + filterInfo->resetDirtySourceRect(); + + // Rewire the old context to a memory buffer, so that we can capture the contents of the layer. + // NOTE: We saved the old context in the "transparencyLayerContext" local variable, to be able to start a transparency layer + // on the original context and avoid duplicating "beginFilterEffect" after each transpareny layer call. Also, note that + // beginTransparencyLayers will only create a single lazy transparency layer, even though it is called twice in this method. + context = filterPainter.beginFilterEffect(context); + + // Check that we didn't fail to allocate the graphics context for the offscreen buffer. + if (filterPainter.hasStartedFilterEffect()) { + paintDirtyRect = filterPainter.repaintRect(); + // If the filter needs the full source image, we need to avoid using the clip rectangles. + // Otherwise, if for example this layer has overflow:hidden, a drop shadow will not compute correctly. + // Note that we will still apply the clipping on the final rendering of the filter. + useClipRect = !filterRenderer()->hasFilterThatMovesPixels(); + } + } + } +#endif + if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) { calculateRects(rootLayer, region, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, localPaintFlags & PaintLayerTemporaryClipRects); paintOffset = toPoint(layerBounds.location() - renderBoxLocation()); } - // Ensure our lists are up-to-date. - updateLayerListsIfNeeded(); - bool forceBlackText = paintBehavior & PaintBehaviorForceBlackText; bool selectionOnly = paintBehavior & PaintBehaviorSelectionOnly; @@ -2874,10 +3053,6 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co if (overlapTestRequests && isSelfPaintingLayer) performOverlapTests(*overlapTestRequests, rootLayer, this); -#if ENABLE(CSS_FILTERS) - FilterEffectRendererHelper filterPainter(paintsWithFilters()); -#endif - // We want to paint our layer, but only if we intersect the damage rect. shouldPaintContent &= intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer); @@ -2885,23 +3060,22 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co if (shouldPaintContent && !selectionOnly) { // Begin transparency layers lazily now that we know we have to paint something. if (haveTransparency) - beginTransparencyLayers(context, rootLayer, paintDirtyRect, paintBehavior); + beginTransparencyLayers(transparencyLayerContext, rootLayer, paintDirtyRect, paintBehavior); -#if ENABLE(CSS_FILTERS) - if (filterPainter.haveFilterEffect() && !context->paintingDisabled()) - context = filterPainter.beginFilterEffect(this, context, paintingExtent(rootLayer, paintDirtyRect, paintBehavior)); -#endif - - // Paint our background first, before painting any child layers. - // Establish the clip used to paint our background. - clipToRect(rootLayer, context, paintDirtyRect, damageRect, DoNotIncludeSelfForBorderRadius); // Background painting will handle clipping to self. - + if (useClipRect) { + // Paint our background first, before painting any child layers. + // Establish the clip used to paint our background. + clipToRect(rootLayer, context, paintDirtyRect, damageRect, DoNotIncludeSelfForBorderRadius); // Background painting will handle clipping to self. + } + // Paint the background. - PaintInfo paintInfo(context, damageRect.rect(), PaintPhaseBlockBackground, false, paintingRootForRenderer, region, 0); + PaintInfo paintInfo(context, pixelSnappedIntRect(damageRect.rect()), PaintPhaseBlockBackground, false, paintingRootForRenderer, region, 0); renderer()->paint(paintInfo, paintOffset); - // Restore the clip. - restoreClip(context, paintDirtyRect, damageRect); + if (useClipRect) { + // Restore the clip. + restoreClip(context, paintDirtyRect, damageRect); + } } // Now walk the sorted list of children with negative z-indices. @@ -2913,16 +3087,14 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co if (shouldPaintContent && !clipRectToApply.isEmpty()) { // Begin transparency layers lazily now that we know we have to paint something. if (haveTransparency) - beginTransparencyLayers(context, rootLayer, paintDirtyRect, paintBehavior); + beginTransparencyLayers(transparencyLayerContext, rootLayer, paintDirtyRect, paintBehavior); -#if ENABLE(CSS_FILTERS) - // If the filter was not started yet, start it now, after the transparency layer was lazily created. - if (filterPainter.haveFilterEffect() && !filterPainter.hasStartedFilterEffect() && !context->paintingDisabled()) - context = filterPainter.beginFilterEffect(this, context, paintingExtent(rootLayer, paintDirtyRect, paintBehavior)); -#endif - // Set up the clip used when painting our children. - clipToRect(rootLayer, context, paintDirtyRect, clipRectToApply); - PaintInfo paintInfo(context, clipRectToApply.rect(), + if (useClipRect) { + // Set up the clip used when painting our children. + clipToRect(rootLayer, context, paintDirtyRect, clipRectToApply); + } + + PaintInfo paintInfo(context, pixelSnappedIntRect(clipRectToApply.rect()), selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, forceBlackText, paintingRootForRenderer, region, 0); renderer()->paint(paintInfo, paintOffset); @@ -2936,13 +3108,15 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co renderer()->paint(paintInfo, paintOffset); } - // Now restore our clip. - restoreClip(context, paintDirtyRect, clipRectToApply); + if (useClipRect) { + // Now restore our clip. + restoreClip(context, paintDirtyRect, clipRectToApply); + } } if (shouldPaintOutline && !outlineRect.isEmpty()) { // Paint our own outline - PaintInfo paintInfo(context, outlineRect.rect(), PaintPhaseSelfOutline, false, paintingRootForRenderer, region, 0); + PaintInfo paintInfo(context, pixelSnappedIntRect(outlineRect.rect()), PaintPhaseSelfOutline, false, paintingRootForRenderer, region, 0); clipToRect(rootLayer, context, paintDirtyRect, outlineRect, DoNotIncludeSelfForBorderRadius); renderer()->paint(paintInfo, paintOffset); restoreClip(context, paintDirtyRect, outlineRect); @@ -2956,14 +3130,17 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co } if ((localPaintFlags & PaintLayerPaintingCompositingMaskPhase) && shouldPaintContent && renderer()->hasMask() && !selectionOnly) { - clipToRect(rootLayer, context, paintDirtyRect, damageRect, DoNotIncludeSelfForBorderRadius); // Mask painting will handle clipping to self. + if (useClipRect) + clipToRect(rootLayer, context, paintDirtyRect, damageRect, DoNotIncludeSelfForBorderRadius); // Mask painting will handle clipping to self. // Paint the mask. - PaintInfo paintInfo(context, damageRect.rect(), PaintPhaseMask, false, paintingRootForRenderer, region, 0); + PaintInfo paintInfo(context, pixelSnappedIntRect(damageRect.rect()), PaintPhaseMask, false, paintingRootForRenderer, region, 0); renderer()->paint(paintInfo, paintOffset); - // Restore the clip. - restoreClip(context, paintDirtyRect, damageRect); + if (useClipRect) { + // Restore the clip. + restoreClip(context, paintDirtyRect, damageRect); + } } if (isPaintingOverlayScrollbars) { @@ -2973,10 +3150,17 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co } #if ENABLE(CSS_FILTERS) - if (filterPainter.hasStartedFilterEffect()) + if (filterPainter.hasStartedFilterEffect()) { + // Apply the correct clipping (ie. overflow: hidden). + clipToRect(rootLayer, transparencyLayerContext, paintDirtyRect, damageRect); context = filterPainter.applyFilterEffect(); + restoreClip(transparencyLayerContext, paintDirtyRect, damageRect); + } #endif + // Make sure that we now use the original transparency context. + ASSERT(transparencyLayerContext == context); + // End our transparency layer if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) { context->endTransparencyLayer(); @@ -2992,7 +3176,11 @@ void RenderLayer::paintList(Vector<RenderLayer*>* list, RenderLayer* rootLayer, { if (!list) return; - + +#if !ASSERT_DISABLED + LayerListMutationDetector mutationChecker(this); +#endif + for (size_t i = 0; i < list->size(); ++i) { RenderLayer* childLayer = list->at(i); if (!childLayer->isPaginated()) @@ -3132,9 +3320,9 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) { renderer()->document()->updateLayout(); - IntRect hitTestArea = renderer()->isRenderFlowThread() ? toRenderFlowThread(renderer())->borderBoxRect() : renderer()->view()->documentRect(); + LayoutRect hitTestArea = renderer()->isRenderFlowThread() ? toRenderFlowThread(renderer())->borderBoxRect() : renderer()->view()->documentRect(); if (!request.ignoreClipping()) - hitTestArea.intersect(pixelSnappedIntRect(frameVisibleRect(renderer()))); + hitTestArea.intersect(frameVisibleRect(renderer())); RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, result.point(), false); if (!insideLayer) { @@ -3377,7 +3565,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. if (fgRect.intersects(hitTestArea) && isSelfPaintingLayer()) { // Hit test with a temporary HitTestResult, because we only want to commit to 'result' if we know we're frontmost. - HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding()); + HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding(), result.shadowContentFilterPolicy()); if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestDescendants) && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { if (result.isRectBasedTest()) @@ -3406,7 +3594,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont return candidateLayer; if (bgRect.intersects(hitTestArea) && isSelfPaintingLayer()) { - HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding()); + HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding(), result.shadowContentFilterPolicy()); if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestSelf) && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { if (result.isRectBasedTest()) @@ -3424,7 +3612,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const LayoutRect& layerBounds, const LayoutPoint& hitTestPoint, HitTestFilter hitTestFilter) const { if (!renderer()->hitTest(request, result, hitTestPoint, - toPoint(layerBounds.location() - renderBoxLocation()), + toLayoutPoint(layerBounds.location() - renderBoxLocation()), hitTestFilter)) { // It's wrong to set innerNode, but then claim that you didn't hit anything, unless it is // a rect-based test. @@ -3462,7 +3650,7 @@ RenderLayer* RenderLayer::hitTestList(Vector<RenderLayer*>* list, RenderLayer* r for (int i = list->size() - 1; i >= 0; --i) { RenderLayer* childLayer = list->at(i); RenderLayer* hitLayer = 0; - HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding()); + HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding(), result.shadowContentFilterPolicy()); if (childLayer->isPaginated()) hitLayer = hitTestPaginatedChildLayer(childLayer, rootLayer, request, tempResult, hitTestRect, hitTestPoint, transformState, zOffsetForDescendants); else @@ -3722,8 +3910,11 @@ ClipRect RenderLayer::backgroundClipRect(const RenderLayer* rootLayer, RenderReg ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, renderer()->style()->position()); RenderView* view = renderer()->view(); ASSERT(view); - if (parentRects.fixed() && rootLayer->renderer() == view) + + // Note: infinite clipRects should not be scrolled here, otherwise they will accidentally no longer be considered infinite. + if (parentRects.fixed() && rootLayer->renderer() == view && backgroundClipRect != PaintInfo::infiniteRect()) backgroundClipRect.move(view->frameView()->scrollXForFixedPosition(), view->frameView()->scrollYForFixedPosition()); + return backgroundClipRect; } @@ -3804,6 +3995,26 @@ LayoutRect RenderLayer::selfClipRect() const return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(backgroundRect.rect())).enclosingBoundingBox(); } +LayoutRect RenderLayer::localClipRect() const +{ + // FIXME: border-radius not accounted for. + // FIXME: Regions not accounted for. + RenderLayer* clippingRootLayer = clippingRoot(); + LayoutRect layerBounds; + ClipRect backgroundRect, foregroundRect, outlineRect; + calculateRects(clippingRootLayer, 0, PaintInfo::infiniteRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); + + LayoutRect clipRect = backgroundRect.rect(); + if (clipRect == PaintInfo::infiniteRect()) + return clipRect; + + LayoutPoint clippingRootOffset; + convertToLayerCoords(clippingRootLayer, clippingRootOffset); + clipRect.moveBy(-clippingRootOffset); + + return clipRect; +} + void RenderLayer::addBlockSelectionGapsBounds(const LayoutRect& bounds) { m_blockSelectionGapsBounds.unite(bounds); @@ -3919,9 +4130,116 @@ LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer) const return result; } -LayoutRect RenderLayer::absoluteBoundingBox() const +IntRect RenderLayer::absoluteBoundingBox() const { - return boundingBox(root()); + return pixelSnappedIntRect(boundingBox(root())); +} + +IntRect RenderLayer::calculateLayerBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer, CalculateLayerBoundsFlags flags) +{ + if (!layer->isSelfPaintingLayer()) + return IntRect(); + + LayoutRect boundingBoxRect = layer->localBoundingBox(); + if (layer->renderer()->isRoot()) { + // If the root layer becomes composited (e.g. because some descendant with negative z-index is composited), + // then it has to be big enough to cover the viewport in order to display the background. This is akin + // to the code in RenderBox::paintRootBoxFillLayers(). + if (FrameView* frameView = layer->renderer()->view()->frameView()) { + LayoutUnit contentsWidth = frameView->contentsWidth(); + LayoutUnit contentsHeight = frameView->contentsHeight(); + + boundingBoxRect.setWidth(max(boundingBoxRect.width(), contentsWidth - boundingBoxRect.x())); + boundingBoxRect.setHeight(max(boundingBoxRect.height(), contentsHeight - boundingBoxRect.y())); + } + } + + LayoutRect unionBounds = boundingBoxRect; + + if (flags & UseLocalClipRectIfPossible) { + LayoutRect localClipRect = layer->localClipRect(); + if (localClipRect != PaintInfo::infiniteRect()) { + LayoutPoint ancestorRelOffset; + layer->convertToLayerCoords(ancestorLayer, ancestorRelOffset); + localClipRect.moveBy(ancestorRelOffset); + return pixelSnappedIntRect(localClipRect); + } + } + + const_cast<RenderLayer*>(layer)->updateLayerListsIfNeeded(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (!reflection->isComposited()) { + IntRect childUnionBounds = calculateLayerBounds(reflection, layer); + unionBounds.unite(childUnionBounds); + } + } + + ASSERT(layer->isStackingContext() || (!layer->posZOrderList() || !layer->posZOrderList()->size())); + +#if !ASSERT_DISABLED + LayerListMutationDetector mutationChecker(const_cast<RenderLayer*>(layer)); +#endif + + if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + if (!curLayer->isComposited()) { + IntRect childUnionBounds = calculateLayerBounds(curLayer, layer); + unionBounds.unite(childUnionBounds); + } + } + } + + if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + if (!curLayer->isComposited()) { + IntRect childUnionBounds = calculateLayerBounds(curLayer, layer); + unionBounds.unite(childUnionBounds); + } + } + } + + if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + if (!curLayer->isComposited()) { + IntRect curAbsBounds = calculateLayerBounds(curLayer, layer); + unionBounds.unite(curAbsBounds); + } + } + } + +#if ENABLE(CSS_FILTERS) + // FIXME: We can optimize the size of the composited layers, by not enlarging + // filtered areas with the outsets if we know that the filter is going to render in hardware. + // https://bugs.webkit.org/show_bug.cgi?id=81239 + if ((flags & IncludeLayerFilterOutsets) && layer->renderer()->style()->hasFilterOutsets()) { + int topOutset; + int rightOutset; + int bottomOutset; + int leftOutset; + layer->renderer()->style()->getFilterOutsets(topOutset, rightOutset, bottomOutset, leftOutset); + unionBounds.move(-leftOutset, -topOutset); + unionBounds.expand(leftOutset + rightOutset, topOutset + bottomOutset); + } +#endif + + if ((flags & IncludeSelfTransform) && layer->paintsWithTransform(PaintBehaviorNormal)) { + TransformationMatrix* affineTrans = layer->transform(); + boundingBoxRect = affineTrans->mapRect(boundingBoxRect); + unionBounds = affineTrans->mapRect(unionBounds); + } + + LayoutPoint ancestorRelOffset; + layer->convertToLayerCoords(ancestorLayer, ancestorRelOffset); + unionBounds.moveBy(ancestorRelOffset); + + return pixelSnappedIntRect(unionBounds); } void RenderLayer::clearClipRectsIncludingDescendants() @@ -3960,16 +4278,18 @@ RenderLayerBacking* RenderLayer::ensureBacking() return m_backing.get(); } -void RenderLayer::clearBacking() +void RenderLayer::clearBacking(bool layerBeingDestroyed) { if (m_backing && !renderer()->documentBeingDestroyed()) compositor()->layerBecameNonComposited(this); m_backing.clear(); #if ENABLE(CSS_FILTERS) - updateOrRemoveFilterEffect(); + if (!layerBeingDestroyed) + updateOrRemoveFilterEffect(); +#else + UNUSED_PARAM(layerBeingDestroyed); #endif - } bool RenderLayer::hasCompositedMask() const @@ -3996,7 +4316,7 @@ GraphicsLayer* RenderLayer::layerForScrollCorner() const bool RenderLayer::paintsWithTransform(PaintBehavior paintBehavior) const { #if USE(ACCELERATED_COMPOSITING) - bool paintsToWindow = !isComposited() || backing()->paintingGoesToWindow(); + bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow(); #else bool paintsToWindow = true; #endif @@ -4141,6 +4461,10 @@ static inline bool compareZIndex(RenderLayer* first, RenderLayer* second) void RenderLayer::dirtyZOrderLists() { + ASSERT(m_layerListMutationAllowed); + // We cannot assume that we are called on a stacking context as it + // is called when we just got demoted from being a stacking context. + if (m_posZOrderList) m_posZOrderList->clear(); if (m_negZOrderList) @@ -4162,6 +4486,8 @@ void RenderLayer::dirtyStackingContextZOrderLists() void RenderLayer::dirtyNormalFlowList() { + ASSERT(m_layerListMutationAllowed); + if (m_normalFlowList) m_normalFlowList->clear(); m_normalFlowListDirty = true; @@ -4172,8 +4498,11 @@ void RenderLayer::dirtyNormalFlowList() #endif } -void RenderLayer::updateZOrderListsSlowCase() +void RenderLayer::rebuildZOrderLists() { + ASSERT(m_layerListMutationAllowed); + ASSERT(isDirtyStackingContext()); + #if USE(ACCELERATED_COMPOSITING) bool includeHiddenLayers = compositor()->inCompositingMode(); #else @@ -4197,7 +4526,9 @@ void RenderLayer::updateNormalFlowList() { if (!m_normalFlowListDirty) return; - + + ASSERT(m_layerListMutationAllowed); + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { // Ignore non-overflow layers and reflections. if (child->isNormalFlowOnly() && (!m_reflection || reflectionLayer() != child)) { @@ -4243,13 +4574,18 @@ void RenderLayer::updateLayerListsIfNeeded() { updateZOrderLists(); updateNormalFlowList(); + + if (RenderLayer* reflectionLayer = this->reflectionLayer()) { + reflectionLayer->updateZOrderLists(); + reflectionLayer->updateNormalFlowList(); + } } void RenderLayer::updateCompositingAndLayerListsIfNeeded() { #if USE(ACCELERATED_COMPOSITING) if (compositor()->inCompositingMode()) { - if ((isStackingContext() && m_zOrderListsDirty) || m_normalFlowListDirty) + if (isDirtyStackingContext() || m_normalFlowListDirty) compositor()->updateCompositingLayers(CompositingUpdateOnHitTest, this); return; } @@ -4268,7 +4604,7 @@ void RenderLayer::repaintIncludingDescendants() void RenderLayer::setBackingNeedsRepaint() { ASSERT(isComposited()); - if (backing()->paintingGoesToWindow()) { + if (backing()->paintsIntoWindow()) { // If we're trying to repaint the placeholder document layer, propagate the // repaint to the native view system. RenderView* view = renderer()->view(); @@ -4283,7 +4619,7 @@ void RenderLayer::setBackingNeedsRepaintInRect(const LayoutRect& r) // https://bugs.webkit.org/show_bug.cgi?id=61159 describes an unreproducible crash here, // so assert but check that the layer is composited. ASSERT(isComposited()); - if (!isComposited() || backing()->paintingGoesToWindow()) { + if (!isComposited() || backing()->paintsIntoWindow()) { // If we're trying to repaint the placeholder document layer, propagate the // repaint to the native view system. LayoutRect absRect(r); @@ -4343,6 +4679,40 @@ bool RenderLayer::isSelfPaintingLayer() const || renderer()->isRenderIFrame(); } +static bool overflowCanHaveAScrollbar(EOverflow overflow) +{ + return overflow == OAUTO || overflow == OSCROLL || overflow == OOVERLAY; +} + +void RenderLayer::updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle) +{ + // Overflow are a box concept. + if (!renderBox()) + return; + + EOverflow overflowX = renderBox()->style()->overflowX(); + EOverflow overflowY = renderBox()->style()->overflowY(); + if (hasHorizontalScrollbar() && !overflowCanHaveAScrollbar(overflowX)) + setHasHorizontalScrollbar(false); + if (hasVerticalScrollbar() && !overflowCanHaveAScrollbar(overflowY)) + setHasVerticalScrollbar(false); + + // With overflow: scroll, scrollbars are always visible but may be disabled. + // When switching to another value, we need to re-enable them (see bug 11985). + if (hasHorizontalScrollbar() && oldStyle->overflowX() == OSCROLL && overflowX != OSCROLL) { + ASSERT(overflowCanHaveAScrollbar(overflowX)); + m_hBar->setEnabled(true); + } + + if (hasVerticalScrollbar() && oldStyle->overflowY() == OSCROLL && overflowY != OSCROLL) { + ASSERT(overflowCanHaveAScrollbar(overflowY)); + m_vBar->setEnabled(true); + } + + if (!m_scrollDimensionsDirty) + updateScrollableAreaSet((hasHorizontalOverflow() || hasVerticalOverflow()) && scrollsOverflow()); +} + void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) { bool isNormalFlowOnly = shouldBeNormalFlowOnly(); @@ -4364,6 +4734,8 @@ void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) m_marquee = 0; } + updateScrollbarsAfterStyleChange(oldStyle); + if (!hasReflection() && m_reflection) removeReflection(); else if (hasReflection()) { @@ -4372,19 +4744,6 @@ void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) updateReflectionStyle(); } -#if ENABLE(CSS_FILTERS) - updateOrRemoveFilterEffect(); -#endif - - if (Frame* frame = renderer()->frame()) { - if (FrameView* frameView = frame->view()) { - if (scrollsOverflow()) - frameView->addScrollableArea(this); - else - frameView->removeScrollableArea(this); - } - } - // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa). if (m_hBar) m_hBar->styleChanged(); @@ -4394,11 +4753,18 @@ void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) updateScrollCornerStyle(); updateResizerStyle(); +#if ENABLE(CSS_FILTERS) + bool backingDidCompositeLayers = isComposited() && backing()->canCompositeFilters(); +#endif + #if USE(ACCELERATED_COMPOSITING) + updateVisibilityStatus(); updateTransform(); if (compositor()->updateLayerCompositingState(this)) compositor()->setCompositingLayersNeedRebuild(); + else if (oldStyle && (oldStyle->clip() != renderer()->style()->clip() || oldStyle->hasClip() != renderer()->style()->hasClip())) + compositor()->setCompositingLayersNeedRebuild(); else if (m_backing) m_backing->updateGraphicsLayerGeometry(); else if (oldStyle && oldStyle->overflowX() != renderer()->style()->overflowX()) { @@ -4406,6 +4772,35 @@ void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) compositor()->setCompositingLayersNeedRebuild(); } #endif + +#if ENABLE(CSS_FILTERS) + updateOrRemoveFilterEffect(); + if (isComposited() && backingDidCompositeLayers && !backing()->canCompositeFilters()) { + // The filters used to be drawn by platform code, but now the platform cannot draw them anymore. + // Fallback to drawing them in software. + setBackingNeedsRepaint(); + } +#endif +} + +void RenderLayer::updateScrollableAreaSet(bool hasOverflow) +{ + Frame* frame = renderer()->frame(); + if (!frame) + return; + + FrameView* frameView = frame->view(); + if (!frameView) + return; + + bool isVisibleToHitTest = renderer()->visibleToHitTesting(); + if (HTMLFrameOwnerElement* owner = frame->ownerElement()) + isVisibleToHitTest &= owner->renderer() && owner->renderer()->visibleToHitTesting(); + + if (hasOverflow && isVisibleToHitTest) + frameView->addScrollableArea(this); + else + frameView->removeScrollableArea(this); } void RenderLayer::updateScrollCornerStyle() @@ -4502,17 +4897,38 @@ void RenderLayer::updateReflectionStyle() #if ENABLE(CSS_FILTERS) void RenderLayer::updateOrRemoveFilterEffect() { - if (paintsWithFilters()) { - if (!m_filter) { - m_filter = FilterEffectRenderer::create(this); - RenderingMode renderingMode = renderer()->frame()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; - m_filter->setRenderingMode(renderingMode); - } + if (!hasFilter()) { + removeFilterInfoIfNeeded(); + return; + } - m_filter->build(renderer()->document(), renderer()->style()->filter()); - } else { - m_filter = 0; +#if ENABLE(CSS_SHADERS) + if (renderer()->style()->filter().hasCustomFilter()) + ensureFilterInfo()->updateCustomFilterClients(renderer()->style()->filter()); + else if (hasFilterInfo()) + filterInfo()->removeCustomFilterClients(); +#endif + + if (!paintsWithFilters()) { + // Don't delete the whole filter info here, because we might use it + // for loading CSS shader files. + if (RenderLayerFilterInfo* filterInfo = this->filterInfo()) + filterInfo->setRenderer(0); + return; } + + RenderLayerFilterInfo* filterInfo = ensureFilterInfo(); + if (!filterInfo->renderer()) { + RefPtr<FilterEffectRenderer> filterRenderer = FilterEffectRenderer::create(); + RenderingMode renderingMode = renderer()->frame()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; + filterRenderer->setRenderingMode(renderingMode); + filterInfo->setRenderer(filterRenderer.release()); + } + + // If the filter fails to build, remove it from the layer. It will still attempt to + // go through regular processing (e.g. compositing), but never apply anything. + if (!filterInfo->renderer()->build(renderer()->document(), renderer()->style()->filter())) + filterInfo->setRenderer(0); } void RenderLayer::filterNeedsRepaint() |