зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
ca313152eb
Коммит
c6350c2dd1
|
@ -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,
|
||||
¤tFrame);
|
||||
nsIFrame* previousFrame = currentFrame;
|
||||
aOverflow = nsLayoutUtils::TransformFrameRectToAncestor(currentFrame, aOverflow, aStopAtFrame,
|
||||
nullptr, nullptr,
|
||||
/* aStopAtStackingContextAndDisplayPortAndOOFFrame = */ true,
|
||||
¤tFrame);
|
||||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче