Commit 4f141bf6 authored by antti@apple.com's avatar antti@apple.com
Browse files

Test if non-immediate descendants obscure background

https://bugs.webkit.org/show_bug.cgi?id=113137

Reviewed by Simon Fraser.

Source/WebCore: 

The current obscuration test only covers immediate children. We can find more cases by looking deeper into descendants.
        
The patch makes the test sufficiently smart to stop a heavy fully obscured gif animation on micrsoft.com.

* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::animationAdvanced):
* rendering/RenderBox.cpp:
(WebCore::RenderBox::styleDidChange):
        
    Invalidate parents to max test depth.

(WebCore::RenderBox::backgroundPaintedExtent):
        
    Background painting is pixel snapped.

(WebCore::isCandidateForOpaquenessTest):
(WebCore::RenderBox::foregroundIsKnownToBeOpaqueInRect):
        
    Separate foreground testing and make it recursive.
    Add fast bailout for common static positioned case.
    Remove maximum child count, the fast bailouts should prevent long tests.
    Add maximum depth so we know how deep we need to invalidate in styleDidChange.

(WebCore::RenderBox::computeBackgroundIsKnownToBeObscured):
(WebCore):
* rendering/RenderBox.h:
(RenderBox):
* rendering/RenderImage.cpp:
(WebCore::RenderImage::foregroundIsKnownToBeOpaqueInRect):
(WebCore):
(WebCore::RenderImage::computeBackgroundIsKnownToBeObscured):
        
* rendering/RenderImage.h:
(RenderImage):

LayoutTests: 

* fast/backgrounds/obscured-background-child-style-change-expected.html:
* fast/backgrounds/obscured-background-child-style-change.html:
* fast/repaint/obscured-background-no-repaint-expected.txt:
* fast/repaint/obscured-background-no-repaint.html:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@146955 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent be03d6d0
2013-03-26 Antti Koivisto <antti@apple.com>
Test if non-immediate descendants obscure background
https://bugs.webkit.org/show_bug.cgi?id=113137
Reviewed by Simon Fraser.
* fast/backgrounds/obscured-background-child-style-change-expected.html:
* fast/backgrounds/obscured-background-child-style-change.html:
* fast/repaint/obscured-background-no-repaint-expected.txt:
* fast/repaint/obscured-background-no-repaint.html:
2013-03-26 Chris Fleizach <cfleizach@apple.com> 2013-03-26 Chris Fleizach <cfleizach@apple.com>
   
WebKit does not expose @required or @aria-required as AXRequired on select elements WebKit does not expose @required or @aria-required as AXRequired on select elements
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
} }
.child { .child {
position: relative; position: relative;
background-color: rgba(0,255,0,0.2); background-color: rgba(0,255,0,0.8);
width: 100px; width: 100px;
height: 100px; height: 100px;
} }
...@@ -16,3 +16,17 @@ ...@@ -16,3 +16,17 @@
<div class=child> <div class=child>
</div> </div>
</div> </div>
<div class=parent>
<div>
<div class=child>
</div>
</div>
</div>
<script>
document.body.offsetTop;
if (window.testRunner)
testRunner.display();
var children = document.getElementsByClassName("child");
children[0].style.backgroundColor = "rgba(0,255,0,0.5)";
children[1].style.backgroundColor = "rgba(0,255,0,0.5)";
</script>
...@@ -6,17 +6,26 @@ ...@@ -6,17 +6,26 @@
height: 100px; height: 100px;
} }
.child { .child {
position: relative;
background-color: red; background-color: red;
width: 100px; width: 100px;
height: 100px; height: 100px;
} }
</style> </style>
<div class=parent> <div class=parent>
<div class=child> <div class=child>
</div>
</div> </div>
<div class=parent>
<div>
<div class=child>
</div>
</div>
</div> </div>
<script> <script>
document.body.offsetTop; document.body.offsetTop;
document.getElementsByClassName("child")[0].style.backgroundColor = "rgba(0,255,0,0.2)"; if (window.testRunner)
testRunner.display();
var children = document.getElementsByClassName("child");
children[0].style.backgroundColor = "rgba(0,255,0,0.5)";
children[1].style.backgroundColor = "rgba(0,255,0,0.5)";
</script> </script>
...@@ -8,3 +8,5 @@ PASS successfullyParsed is true ...@@ -8,3 +8,5 @@ PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
...@@ -34,6 +34,15 @@ ...@@ -34,6 +34,15 @@
#test3 img { #test3 img {
background-image: url(resources/animated.gif) background-image: url(resources/animated.gif)
} }
#test4 .parent {
position: relative;
height: 100px;
width: 100px;
background-color: red;
background-repeat: no-repeat;
background-position: center;
background-image: url(resources/animated.gif)
}
</style> </style>
<script> <script>
description("Test that obscured animated gif does not trigger repaints. This test requires DRT."); description("Test that obscured animated gif does not trigger repaints. This test requires DRT.");
...@@ -81,6 +90,16 @@ ...@@ -81,6 +90,16 @@
<div id="test3"> <div id="test3">
<img src="resources/apple.jpg"> <img src="resources/apple.jpg">
</div> </div>
<div id="test4">
<div class="parent">
<a>
<div></div>
<div>
<img src="resources/apple.jpg">
</div>
</a>
</div>
</div>
</body> </body>
<script src="../js/resources/js-test-post.js"></script> <script src="../js/resources/js-test-post.js"></script>
</html> </html>
2013-03-26 Antti Koivisto <antti@apple.com>
Test if non-immediate descendants obscure background
https://bugs.webkit.org/show_bug.cgi?id=113137
Reviewed by Simon Fraser.
The current obscuration test only covers immediate children. We can find more cases by looking deeper into descendants.
The patch makes the test sufficiently smart to stop a heavy fully obscured gif animation on micrsoft.com.
* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::animationAdvanced):
* rendering/RenderBox.cpp:
(WebCore::RenderBox::styleDidChange):
Invalidate parents to max test depth.
(WebCore::RenderBox::backgroundPaintedExtent):
Background painting is pixel snapped.
(WebCore::isCandidateForOpaquenessTest):
(WebCore::RenderBox::foregroundIsKnownToBeOpaqueInRect):
Separate foreground testing and make it recursive.
Add fast bailout for common static positioned case.
Remove maximum child count, the fast bailouts should prevent long tests.
Add maximum depth so we know how deep we need to invalidate in styleDidChange.
(WebCore::RenderBox::computeBackgroundIsKnownToBeObscured):
(WebCore):
* rendering/RenderBox.h:
(RenderBox):
* rendering/RenderImage.cpp:
(WebCore::RenderImage::foregroundIsKnownToBeOpaqueInRect):
(WebCore):
(WebCore::RenderImage::computeBackgroundIsKnownToBeObscured):
* rendering/RenderImage.h:
(RenderImage):
2013-03-26 Benjamin Poulain <bpoulain@apple.com> 2013-03-26 Benjamin Poulain <bpoulain@apple.com>
   
Regression (r145601): out-of-bounds read in line breaking / new width cache Regression (r145601): out-of-bounds read in line breaking / new width cache
...@@ -89,6 +89,7 @@ static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = 0; ...@@ -89,6 +89,7 @@ static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = 0;
// Size of border belt for autoscroll. When mouse pointer in border belt, // Size of border belt for autoscroll. When mouse pointer in border belt,
// autoscroll is started. // autoscroll is started.
static const int autoscrollBeltSize = 20; static const int autoscrollBeltSize = 20;
static const unsigned backgroundObscurationTestMaxDepth = 4;
bool RenderBox::s_hadOverflowClip = false; bool RenderBox::s_hadOverflowClip = false;
...@@ -283,8 +284,13 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle ...@@ -283,8 +284,13 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle
} }
// Our opaqueness might have changed without triggering layout. // Our opaqueness might have changed without triggering layout.
if (parent() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintLayer)) if (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintLayer) {
parent()->invalidateBackgroundObscurationStatus(); RenderObject* parentToInvalidate = parent();
for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) {
parentToInvalidate->invalidateBackgroundObscurationStatus();
parentToInvalidate = parentToInvalidate->parent();
}
}
bool isBodyRenderer = isBody(); bool isBodyRenderer = isBody();
bool isRootRenderer = isRoot(); bool isRootRenderer = isRoot();
...@@ -1150,7 +1156,7 @@ void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& pa ...@@ -1150,7 +1156,7 @@ void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& pa
LayoutRect RenderBox::backgroundPaintedExtent() const LayoutRect RenderBox::backgroundPaintedExtent() const
{ {
ASSERT(hasBackground()); ASSERT(hasBackground());
LayoutRect backgroundRect = borderBoxRect(); LayoutRect backgroundRect = pixelSnappedIntRect(borderBoxRect());
Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor);
if (backgroundColor.isValid() && backgroundColor.alpha()) if (backgroundColor.isValid() && backgroundColor.alpha())
...@@ -1196,52 +1202,74 @@ bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) c ...@@ -1196,52 +1202,74 @@ bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) c
return backgroundRect.contains(localRect); return backgroundRect.contains(localRect);
} }
bool RenderBox::computeBackgroundIsKnownToBeObscured() static bool isCandidateForOpaquenessTest(RenderBox* childBox)
{ {
// Test to see if the children trivially obscure the background. RenderStyle* childStyle = childBox->style();
// FIXME: This test can be much more comprehensive. if (childStyle->position() != StaticPosition && childBox->containingBlock() != childBox->parent())
if (!hasBackground())
return false; return false;
// Table and root background painting is special. if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside())
if (isTable() || isRoot())
return false; return false;
if (!childBox->width() || !childBox->height())
return false;
if (RenderLayer* childLayer = childBox->layer()) {
#if USE(ACCELERATED_COMPOSITING)
if (childLayer->isComposited())
return false;
#endif
// FIXME: Deal with z-index.
if (!childStyle->hasAutoZIndex())
return false;
if (childLayer->hasTransform() || childLayer->isTransparent() || childLayer->hasFilter())
return false;
}
return true;
}
LayoutRect backgroundRect = backgroundPaintedExtent(); bool RenderBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
// If we don't find a covering child fast there probably isn't one. {
static const unsigned maximumChildrenCountToTest = 4; if (!maxDepthToTest)
unsigned count = 0; return false;
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (++count > maximumChildrenCountToTest)
break;
if (!child->isBox()) if (!child->isBox())
continue; continue;
RenderBox* childBox = toRenderBox(child); RenderBox* childBox = toRenderBox(child);
RenderStyle* childStyle = child->style(); if (!isCandidateForOpaquenessTest(childBox))
if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside())
continue;
if (childStyle->position() != StaticPosition && childBox->containingBlock() != this)
continue; continue;
LayoutPoint childLocation = childBox->location(); LayoutPoint childLocation = childBox->location();
if (childBox->isRelPositioned()) if (childBox->isRelPositioned())
childLocation.move(childBox->relativePositionOffset()); childLocation.move(childBox->relativePositionOffset());
LayoutRect childLocalBackgroundRect = backgroundRect; LayoutRect childLocalRect = localRect;
childLocalBackgroundRect.moveBy(-childLocation); childLocalRect.moveBy(-childLocation);
if (RenderLayer* childLayer = childBox->layer()) { if (childLocalRect.y() < 0 || childLocalRect.x() < 0) {
#if USE(ACCELERATED_COMPOSITING) // If there is unobscured area above/left of a static positioned box then the rect is probably not covered.
if (childLayer->isComposited()) if (childBox->style()->position() == StaticPosition)
continue; return false;
#endif continue;
if (childLayer->zIndex() < 0)
continue;
if (childLayer->hasTransform() || childLayer->isTransparent())
continue;
} }
if (childBox->backgroundIsKnownToBeOpaqueInRect(childLocalBackgroundRect)) if (childLocalRect.maxY() > childBox->height() || childLocalRect.maxX() > childBox->width())
continue;
if (childBox->backgroundIsKnownToBeOpaqueInRect(childLocalRect))
return true;
if (childBox->foregroundIsKnownToBeOpaqueInRect(childLocalRect, maxDepthToTest - 1))
return true; return true;
} }
return false; return false;
} }
bool RenderBox::computeBackgroundIsKnownToBeObscured()
{
// Test to see if the children trivially obscure the background.
// FIXME: This test can be much more comprehensive.
if (!hasBackground())
return false;
// Table and root background painting is special.
if (isTable() || isRoot())
return false;
LayoutRect backgroundRect = backgroundPaintedExtent();
return foregroundIsKnownToBeOpaqueInRect(backgroundRect, backgroundObscurationTestMaxDepth);
}
bool RenderBox::backgroundHasOpaqueTopLayer() const bool RenderBox::backgroundHasOpaqueTopLayer() const
{ {
const FillLayer* fillLayer = style()->backgroundLayers(); const FillLayer* fillLayer = style()->backgroundLayers();
......
...@@ -594,7 +594,9 @@ protected: ...@@ -594,7 +594,9 @@ protected:
virtual void updateFromStyle() OVERRIDE; virtual void updateFromStyle() OVERRIDE;
LayoutRect backgroundPaintedExtent() const; LayoutRect backgroundPaintedExtent() const;
virtual bool foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const;
virtual bool computeBackgroundIsKnownToBeObscured() OVERRIDE; virtual bool computeBackgroundIsKnownToBeObscured() OVERRIDE;
void paintBackground(const PaintInfo&, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone); void paintBackground(const PaintInfo&, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone);
void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance, CompositeOperator, RenderObject* backgroundObject); void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance, CompositeOperator, RenderObject* backgroundObject);
......
...@@ -484,13 +484,15 @@ bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance ...@@ -484,13 +484,15 @@ bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance
return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured(); return !const_cast<RenderImage*>(this)->backgroundIsKnownToBeObscured();
} }
bool RenderImage::computeBackgroundIsKnownToBeObscured() bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
{ {
UNUSED_PARAM(maxDepthToTest);
if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) if (!m_imageResource->hasImage() || m_imageResource->errorOccurred())
return false; return false;
if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded()) if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded())
return false; return false;
if (!contentBoxRect().contains(localRect))
return false;
EFillBox backgroundClip = style()->backgroundClip(); EFillBox backgroundClip = style()->backgroundClip();
// Background paints under borders. // Background paints under borders.
if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground()) if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground())
...@@ -498,11 +500,17 @@ bool RenderImage::computeBackgroundIsKnownToBeObscured() ...@@ -498,11 +500,17 @@ bool RenderImage::computeBackgroundIsKnownToBeObscured()
// Background shows in padding area. // Background shows in padding area.
if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding()) if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding())
return false; return false;
// Check for image with alpha. // Check for image with alpha.
return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this); return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this);
} }
bool RenderImage::computeBackgroundIsKnownToBeObscured()
{
if (!hasBackground())
return false;
return foregroundIsKnownToBeOpaqueInRect(backgroundPaintedExtent(), 0);
}
LayoutUnit RenderImage::minimumReplacedHeight() const LayoutUnit RenderImage::minimumReplacedHeight() const
{ {
return m_imageResource->errorOccurred() ? intrinsicSize().height() : LayoutUnit(); return m_imageResource->errorOccurred() ? intrinsicSize().height() : LayoutUnit();
......
...@@ -88,6 +88,7 @@ private: ...@@ -88,6 +88,7 @@ private:
virtual void paintReplaced(PaintInfo&, const LayoutPoint&); virtual void paintReplaced(PaintInfo&, const LayoutPoint&);
virtual bool foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const OVERRIDE;
virtual bool computeBackgroundIsKnownToBeObscured() OVERRIDE; virtual bool computeBackgroundIsKnownToBeObscured() OVERRIDE;
virtual LayoutUnit minimumReplacedHeight() const OVERRIDE; virtual LayoutUnit minimumReplacedHeight() const OVERRIDE;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment