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>
 
WebKit does not expose @required or @aria-required as AXRequired on select elements
......@@ -7,7 +7,7 @@
}
.child {
position: relative;
background-color: rgba(0,255,0,0.2);
background-color: rgba(0,255,0,0.8);
width: 100px;
height: 100px;
}
......@@ -16,3 +16,17 @@
<div class=child>
</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 @@
height: 100px;
}
.child {
position: relative;
background-color: red;
width: 100px;
height: 100px;
}
</style>
<div class=parent>
<div class=child>
<div class=child>
</div>
</div>
<div class=parent>
<div>
<div class=child>
</div>
</div>
</div>
<script>
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>
......@@ -8,3 +8,5 @@ PASS successfullyParsed is true
TEST COMPLETE
......@@ -34,6 +34,15 @@
#test3 img {
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>
<script>
description("Test that obscured animated gif does not trigger repaints. This test requires DRT.");
......@@ -81,6 +90,16 @@
<div id="test3">
<img src="resources/apple.jpg">
</div>
<div id="test4">
<div class="parent">
<a>
<div></div>
<div>
<img src="resources/apple.jpg">
</div>
</a>
</div>
</div>
</body>
<script src="../js/resources/js-test-post.js"></script>
</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>
 
Regression (r145601): out-of-bounds read in line breaking / new width cache
......@@ -89,6 +89,7 @@ static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = 0;
// Size of border belt for autoscroll. When mouse pointer in border belt,
// autoscroll is started.
static const int autoscrollBeltSize = 20;
static const unsigned backgroundObscurationTestMaxDepth = 4;
bool RenderBox::s_hadOverflowClip = false;
......@@ -283,8 +284,13 @@ void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle
}
// Our opaqueness might have changed without triggering layout.
if (parent() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintLayer))
parent()->invalidateBackgroundObscurationStatus();
if (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintLayer) {
RenderObject* parentToInvalidate = parent();
for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) {
parentToInvalidate->invalidateBackgroundObscurationStatus();
parentToInvalidate = parentToInvalidate->parent();
}
}
bool isBodyRenderer = isBody();
bool isRootRenderer = isRoot();
......@@ -1150,7 +1156,7 @@ void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& pa
LayoutRect RenderBox::backgroundPaintedExtent() const
{
ASSERT(hasBackground());
LayoutRect backgroundRect = borderBoxRect();
LayoutRect backgroundRect = pixelSnappedIntRect(borderBoxRect());
Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor);
if (backgroundColor.isValid() && backgroundColor.alpha())
......@@ -1196,52 +1202,74 @@ bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) c
return backgroundRect.contains(localRect);
}
bool RenderBox::computeBackgroundIsKnownToBeObscured()
static bool isCandidateForOpaquenessTest(RenderBox* childBox)
{
// Test to see if the children trivially obscure the background.
// FIXME: This test can be much more comprehensive.
if (!hasBackground())
RenderStyle* childStyle = childBox->style();
if (childStyle->position() != StaticPosition && childBox->containingBlock() != childBox->parent())
return false;
// Table and root background painting is special.
if (isTable() || isRoot())
if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside())
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();
// If we don't find a covering child fast there probably isn't one.
static const unsigned maximumChildrenCountToTest = 4;
unsigned count = 0;
bool RenderBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
{
if (!maxDepthToTest)
return false;
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (++count > maximumChildrenCountToTest)
break;
if (!child->isBox())
continue;
RenderBox* childBox = toRenderBox(child);
RenderStyle* childStyle = child->style();
if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside())
continue;
if (childStyle->position() != StaticPosition && childBox->containingBlock() != this)
if (!isCandidateForOpaquenessTest(childBox))
continue;
LayoutPoint childLocation = childBox->location();
if (childBox->isRelPositioned())
childLocation.move(childBox->relativePositionOffset());
LayoutRect childLocalBackgroundRect = backgroundRect;
childLocalBackgroundRect.moveBy(-childLocation);
if (RenderLayer* childLayer = childBox->layer()) {
#if USE(ACCELERATED_COMPOSITING)
if (childLayer->isComposited())
continue;
#endif
if (childLayer->zIndex() < 0)
continue;
if (childLayer->hasTransform() || childLayer->isTransparent())
continue;
LayoutRect childLocalRect = localRect;
childLocalRect.moveBy(-childLocation);
if (childLocalRect.y() < 0 || childLocalRect.x() < 0) {
// If there is unobscured area above/left of a static positioned box then the rect is probably not covered.
if (childBox->style()->position() == StaticPosition)
return false;
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 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
{
const FillLayer* fillLayer = style()->backgroundLayers();
......
......@@ -594,7 +594,9 @@ protected:
virtual void updateFromStyle() OVERRIDE;
LayoutRect backgroundPaintedExtent() const;
virtual bool foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const;
virtual bool computeBackgroundIsKnownToBeObscured() OVERRIDE;
void paintBackground(const PaintInfo&, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone);
void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance, CompositeOperator, RenderObject* backgroundObject);
......
......@@ -484,13 +484,15 @@ bool RenderImage::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance
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())
return false;
if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded())
return false;
if (!contentBoxRect().contains(localRect))
return false;
EFillBox backgroundClip = style()->backgroundClip();
// Background paints under borders.
if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground())
......@@ -498,11 +500,17 @@ bool RenderImage::computeBackgroundIsKnownToBeObscured()
// Background shows in padding area.
if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding())
return false;
// Check for image with alpha.
return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this);
}
bool RenderImage::computeBackgroundIsKnownToBeObscured()
{
if (!hasBackground())
return false;
return foregroundIsKnownToBeOpaqueInRect(backgroundPaintedExtent(), 0);
}
LayoutUnit RenderImage::minimumReplacedHeight() const
{
return m_imageResource->errorOccurred() ? intrinsicSize().height() : LayoutUnit();
......
......@@ -88,6 +88,7 @@ private:
virtual void paintReplaced(PaintInfo&, const LayoutPoint&);
virtual bool foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const OVERRIDE;
virtual bool computeBackgroundIsKnownToBeObscured() 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