Bug 1428993 - Part 2: Override dirty rect for stacking contexts between OOF frame placeholder and the containing block r=mattwoodrow

MozReview-Commit-ID: FoX9uyoiqj4

--HG--
extra : rebase_source : 64baafadcb35509e08f8db30b2153eb16c41c369
This commit is contained in:
Miko Mynttinen 2018-01-12 18:02:14 +01:00
Родитель ca313152eb
Коммит c6350c2dd1
8 изменённых файлов: 271 добавлений и 38 удалений

Просмотреть файл

@ -2707,7 +2707,9 @@ nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame,
ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aFlags);
while (parent && parent != aAncestor &&
(!(aFlags & nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
(!parent->IsStackingContext() && !FrameHasDisplayPort(parent)))) {
(!parent->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
!parent->IsStackingContext() &&
!FrameHasDisplayPort(parent)))) {
if (!parent->Extend3DContext()) {
ctm.ProjectTo2D();
}

Просмотреть файл

@ -1019,6 +1019,11 @@ nsIFrame::MarkNeedsDisplayItemRebuild()
return;
}
if (Type() == LayoutFrameType::Placeholder) {
// Do not mark placeholder frames modified.
return;
}
nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
MOZ_ASSERT(displayRoot);

Просмотреть файл

@ -6,6 +6,7 @@
*/
#include "RetainedDisplayListBuilder.h"
#include "nsPlaceholderFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsViewManager.h"
@ -624,7 +625,7 @@ static void
ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder& aBuilder,
AnimatedGeometryRoot** aAGR, nsRect& aOverflow,
nsIFrame* aStopAtFrame, nsTArray<nsIFrame*>& aOutFramesWithProps,
const bool /* aStopAtStackingContext */)
const bool aStopAtStackingContext)
{
nsIFrame* currentFrame = aFrame;
@ -633,13 +634,47 @@ ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder& aBuilder,
// Convert 'aOverflow' into the coordinate space of the nearest stacking context
// or display port ancestor and update 'currentFrame' to point to that frame.
aOverflow = nsLayoutUtils::TransformFrameRectToAncestor(currentFrame, aOverflow,
aStopAtFrame,
nullptr, nullptr,
/* aStopAtStackingContextAndDisplayPort = */ true,
&currentFrame);
nsIFrame* previousFrame = currentFrame;
aOverflow = nsLayoutUtils::TransformFrameRectToAncestor(currentFrame, aOverflow, aStopAtFrame,
nullptr, nullptr,
/* aStopAtStackingContextAndDisplayPortAndOOFFrame = */ true,
&currentFrame);
MOZ_ASSERT(currentFrame);
// If the current frame is an OOF frame, DisplayListBuildingData needs to be
// set on all the ancestor stacking contexts of the placeholder frame, up
// to the containing block of the OOF frame. This is done to ensure that the
// content that might be behind the OOF frame is built for merging.
nsIFrame* placeholder = previousFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)
? previousFrame->GetPlaceholderFrame()
: nullptr;
if (placeholder) {
nsRect placeholderOverflow =
aOverflow + previousFrame->GetOffsetTo(placeholder);
CRR_LOG("Processing placeholder %p for OOF frame %p\n",
placeholder, previousFrame);
CRR_LOG("OOF frame draw area: %d %d %d %d\n",
placeholderOverflow.x, placeholderOverflow.y,
placeholderOverflow.width, placeholderOverflow.height);
// Tracking AGRs for the placeholder processing is not necessary, as the
// goal is to only modify the DisplayListBuildingData rect.
AnimatedGeometryRoot* dummyAGR = nullptr;
// Find a common ancestor frame to handle frame continuations.
// TODO: It might be possible to write a more specific and efficient
// function for this.
nsIFrame* ancestor =
nsLayoutUtils::FindNearestCommonAncestorFrame(previousFrame->GetParent(),
placeholder->GetParent());
ProcessFrame(placeholder, aBuilder, &dummyAGR, placeholderOverflow,
ancestor, aOutFramesWithProps, false);
}
if (nsLayoutUtils::FrameHasDisplayPort(currentFrame)) {
CRR_LOG("Frame belongs to displayport frame %p\n", currentFrame);
nsIScrollableFrame* sf = do_QueryFrame(currentFrame);
@ -662,7 +697,8 @@ ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder& aBuilder,
aOutFramesWithProps.AppendElement(currentFrame);
}
rect->UnionRect(*rect, r);
CRR_LOG("Adding area to displayport draw area: %d %d %d %d\n", r.x, r.y, r.width, r.height);
CRR_LOG("Adding area to displayport draw area: %d %d %d %d\n",
r.x, r.y, r.width, r.height);
// TODO: Can we just use MarkFrameForDisplayIfVisible, plus MarkFramesForDifferentAGR to
// ensure that this displayport, plus any items that move relative to it get rebuilt,
@ -680,40 +716,50 @@ ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder& aBuilder,
// If we found an intermediate stacking context with an existing display item
// then we can store the dirty rect there and stop. If we couldn't find one then
// we need to keep bubbling up to the next stacking context.
if (currentFrame != aBuilder.RootReferenceFrame() &&
currentFrame->HasDisplayItems()) {
aBuilder.MarkFrameForDisplayIfVisible(currentFrame,
aBuilder.RootReferenceFrame());
// Store the stacking context relative dirty area such
// that display list building will pick it up when it
// gets to it.
nsDisplayListBuilder::DisplayListBuildingData* data =
currentFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
if (!data) {
data = new nsDisplayListBuilder::DisplayListBuildingData;
currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingRect(), data);
currentFrame->SetHasOverrideDirtyRegion(true);
aOutFramesWithProps.AppendElement(currentFrame);
}
data->mDirtyRect.UnionRect(data->mDirtyRect, aOverflow);
CRR_LOG("Adding area to stacking context draw area: %d %d %d %d\n",
aOverflow.x, aOverflow.y, aOverflow.width, aOverflow.height);
if (!data->mModifiedAGR) {
data->mModifiedAGR = *aAGR;
} else if (data->mModifiedAGR != *aAGR) {
data->mDirtyRect = currentFrame->GetVisualOverflowRectRelativeToSelf();
CRR_LOG("Found multiple modified AGRs within this stacking context, giving up\n");
}
// Don't contribute to the root dirty area at all.
*aAGR = nullptr;
aOverflow.SetEmpty();
break;
if (currentFrame == aBuilder.RootReferenceFrame() ||
!currentFrame->HasDisplayItems()) {
continue;
}
aBuilder.MarkFrameForDisplayIfVisible(currentFrame,
aBuilder.RootReferenceFrame());
// Store the stacking context relative dirty area such
// that display list building will pick it up when it
// gets to it.
nsDisplayListBuilder::DisplayListBuildingData* data =
currentFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
if (!data) {
data = new nsDisplayListBuilder::DisplayListBuildingData();
currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingRect(), data);
currentFrame->SetHasOverrideDirtyRegion(true);
aOutFramesWithProps.AppendElement(currentFrame);
}
CRR_LOG("Adding area to stacking context draw area: %d %d %d %d\n",
aOverflow.x, aOverflow.y, aOverflow.width, aOverflow.height);
data->mDirtyRect.UnionRect(data->mDirtyRect, aOverflow);
if (!aStopAtStackingContext) {
// Continue ascending the frame tree until we reach aStopAtFrame.
continue;
}
if (!data->mModifiedAGR) {
data->mModifiedAGR = *aAGR;
} else if (data->mModifiedAGR != *aAGR) {
data->mDirtyRect = currentFrame->GetVisualOverflowRectRelativeToSelf();
CRR_LOG("Found multiple modified AGRs within this stacking context, giving up\n");
}
// Don't contribute to the root dirty area at all.
aOverflow.SetEmpty();
*aAGR = nullptr;
break;
}
}
}
/**
* Given a list of frames that has been modified, computes the region that we need to
* do display list building for in order to build all modified display items.

Просмотреть файл

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Retained display list test</title>
<style type="text/css">
.container {
position: fixed;
top: 0px;
left: 0px;
width: 400px;
height: 400px;
background-color: green;
}
</style>
</head>
<body>
<div class="container">
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Retained display list test</title>
<style type="text/css">
.box {
left: 0px;
top: 0px;
width: 400px;
height: 400px;
}
button {
position: fixed;
outline: none;
background-color: green;
border: none;
}
.red {
position: absolute;
background-color: red;
}
.translate {
transform: translateX(0px);
}
.container {
position: absolute;
top: 0px;
left: 0px;
z-index: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="box red"></div>
<button class="box" id="green"></button>
</div>
<script type="text/javascript">
function doTest() {
document.getElementById("green").classList.add("translate");
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
// setTimeout(doTest, 5000);
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Retained display list test</title>
<style type="text/css">
#child {
position: absolute;
width: 100px;
height: 100px;
top: 300px;
left: 0px;
background-color: green;
}
.container {
position: absolute;
top: 0px;
left: 0px;
width: 200px;
height: 200px;
z-index: 1;
background-color: green;
}
</style>
</head>
<body>
<div class="container">
<div id="child"></div>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Retained display list test</title>
<style type="text/css">
.back {
width: 200px;
height: 200px;
background-color: red;
}
#parent {
position: fixed;
width: 200px;
height: 200px;
top: 0px;
left: 0px;
background-color: green;
}
#child {
position: fixed;
width: 100px;
height: 100px;
top: 300px;
background-color: green;
}
.translate {
transform: translateX(0px);
}
.container {
position: absolute;
top: 0px;
left: 0px;
z-index: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="back"></div>
<div id="parent">
<div id="child"></div>
</div>
</div>
<script type="text/javascript">
function doTest() {
document.getElementById("parent").classList.add("translate");
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
// setTimeout(doTest, 5000);
</script>
</body>
</html>

Просмотреть файл

@ -12,3 +12,5 @@ fuzzy(1,235200) == 1413073.html 1413073-ref.html
== 1416291.html 1416291-ref.html
== 1417601-1.html 1417601-1-ref.html
== 1418945-1.html 1418945-1-ref.html
skip-if(Android) == 1428993-1.html 1428993-1-ref.html
== 1428993-2.html 1428993-2-ref.html