Bug 1404181 - Part 9: Add code for detecting if display list building happened for a given frame, and use it to add some tests for retained display lists. r=mstange

MozReview-Commit-ID: AIb0AWU7iiS

--HG--
extra : rebase_source : f6277bbe2c8ffd571e32bb886903243df655918e
This commit is contained in:
Matt Woodrow ext:(%2C%20Miko%20Mynttinen%20%3Cmikokm%40gmail.com%3E%2C%20Timothy%20Nikkel%20%3Ctnikkel%40gmail.com%3E) 2017-09-27 17:17:11 +13:00
Родитель 31f97ddc43
Коммит db37a82e0b
26 изменённых файлов: 520 добавлений и 2 удалений

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

@ -3161,6 +3161,40 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::CheckAndClearDisplayListState(nsIDOMElement* aElement, bool* aResult)
{
if (!aElement) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsIFrame* frame = content->GetPrimaryFrame();
if (!frame) {
*aResult = false;
return NS_OK;
}
// Get the outermost frame for the content node, so that we can test
// canvasframe invalidations by observing the documentElement.
for (;;) {
nsIFrame* parentFrame = frame->GetParent();
if (parentFrame && parentFrame->GetContent() == content) {
frame = parentFrame;
} else {
break;
}
}
*aResult = frame->CheckAndClearDisplayListState();
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::IsPartOfOpaqueLayer(nsIDOMElement* aElement, bool* aResult)
{

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

@ -1606,6 +1606,12 @@ interface nsIDOMWindowUtils : nsISupports {
*/
boolean checkAndClearPaintedState(in nsIDOMElement aElement);
/**
* Check if any display list building has been done for this element,
* clears the display list flags if they have.
*/
boolean checkAndClearDisplayListState(in nsIDOMElement aElement);
/**
* Check whether all display items of the primary frame of aElement have been
* assigned to the same single PaintedLayer in the last paint. If that is the

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

@ -388,6 +388,25 @@ nsIFrame::CheckAndClearPaintedState()
return result;
}
bool
nsIFrame::CheckAndClearDisplayListState()
{
bool result = BuiltDisplayList();
SetBuiltDisplayList(false);
nsIFrame::ChildListIterator lists(this);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* child = childFrames.get();
if (child->CheckAndClearDisplayListState()) {
result = true;
}
}
}
return result;
}
bool
nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const
{
@ -3187,6 +3206,8 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
awayFromCommonPath = true;
}
child->SetBuiltDisplayList(true);
// Child is composited if it's transformed, partially transparent, or has
// SVG effects or a blend mode..
EffectSet* effectSet = EffectSet::GetEffectSet(child);

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

@ -625,6 +625,7 @@ public:
, mIsWrapperBoxNeedingRestyle(false)
, mReflowRequestedForCharDataChange(false)
, mForceDescendIntoIfVisible(false)
, mBuiltDisplayList(false)
, mIsPrimaryFrame(false)
{
mozilla::PodZero(&mOverflow);
@ -3838,6 +3839,10 @@ public:
// clears this bit if so.
bool CheckAndClearPaintedState();
// Checks if we (or any of our descendents) have mBuiltDisplayList set, and
// clears this bit if so.
bool CheckAndClearDisplayListState();
// CSS visibility just doesn't cut it because it doesn't inherit through
// documents. Also if this frame is in a hidden card of a deck then it isn't
// visible either and that isn't expressed using CSS visibility. Also if it
@ -4091,6 +4096,9 @@ public:
bool ForceDescendIntoIfVisible() { return mForceDescendIntoIfVisible; }
void SetForceDescendIntoIfVisible(bool aForce) { mForceDescendIntoIfVisible = aForce; }
bool BuiltDisplayList() { return mBuiltDisplayList; }
void SetBuiltDisplayList(bool aBuilt) { mBuiltDisplayList = aBuilt; }
protected:
/**
@ -4259,6 +4267,14 @@ protected:
*/
bool mForceDescendIntoIfVisible : 1;
/**
* True if we have built display items for this frame since
* the last call to CheckAndClearDisplayListState, false
* otherwise. Used for the reftest harness to verify minimal
* display list building.
*/
bool mBuiltDisplayList : 1;
private:
/**
* True if this is the primary frame for mContent.
@ -4267,7 +4283,7 @@ private:
protected:
// There is a 8-bit gap left here.
// There is a 7-bit gap left here.
// Helpers
/**

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

@ -0,0 +1,9 @@
skip-if(!retainedDisplayList) == retained-dl-style-change-1.html retained-dl-style-change-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-frame-deleted-1.html retained-dl-style-change-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-frame-created-1.html retained-dl-style-change-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-style-change-stacking-context-1.html retained-dl-style-change-stacking-context-1-ref.html
skip-if(!retainedDisplayList||!asyncPan) == retained-dl-async-scrolled-1.html retained-dl-async-scrolled-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-remove-for-ancestor-change-1.html retained-dl-remove-for-ancestor-change-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-scroll-out-of-view-1.html retained-dl-scroll-out-of-view-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-displayport-1.html retained-dl-displayport-1-ref.html
skip-if(!retainedDisplayList) == retained-dl-prerender-transform-1.html retained-dl-prerender-transform-1-ref.html

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

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
}
div {
left: 200px;
top: 200px;
position:absolute;
}
</style>
</head>
<body>
<div style="width: 200px; height: 200px; background-color: blue;"></div>
<div style="width: 100px; height: 100px; background-color: red;"></div>
</body>
</html>

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

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html reftest-async-scroll
reftest-displayport-x="0" reftest-displayport-y="0"
reftest-displayport-w="800" reftest-displayport-h="2000"
reftest-async-scroll-x="0" reftest-async-scroll-y="400"
class="reftest-wait">
<head>
<style>
body {
margin: 0;
height: 4000px;
overflow:hidden;
}
div {
left: 200px;
top: 200px;
width: 200px;
height: 200px;
}
.scrolled {
position: absolute;
z-index: 1;
}
.fixed {
position: fixed;
background-color: red;
}
</style>
</head>
<body>
<div class="scrolled reftest-no-display-list" style="top: 200px; background-color: green"></div>
<div class="scrolled" style="top: 600px;" id="scrolled"></div>
<div class="fixed" style="top: 200px"></div>
<div class="fixed" style="top: 200px; width: 100px; height: 100px; z-index: 2"></div>
</body>
<script>
function doTest() {
document.getElementById("scrolled").style.backgroundColor = "blue";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,11 @@
<html>
<head>
</head>
<body>
<div id="container" style="width: 100px; height: 100px; overflow: auto;">
<div id="first" style="background-color:blue; width: 10px; height: 10px;"></div>
<div id="second" style="background-color:green; width: 10px; height: 10px;"></div>
<div id="spacer" style="height: 200px;"></div>
</div>
</body>
</html>

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

@ -0,0 +1,21 @@
<html class="reftest-wait reftest-async-scroll">
<head>
</head>
<body>
<div id="container" style="width: 100px; height: 100px; overflow: auto;"
reftest-displayport-x="0" reftest-displayport-y="0"
reftest-displayport-width="100" reftest-displayport-height="100">
<div id="first" style="background-color:blue; width: 10px; height: 10px;" class="reftest-no-display-list"></div>
<div id="second" style="background-color:red; width: 10px; height: 10px;"></div>
<div id="spacer" style="height: 200px;"></div>
</div>
</body>
<script>
function doTest() {
document.getElementById("second").style.backgroundColor = "green";
document.documentElement.classList.remove("reftest-wait");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,25 @@
<html class="reftest-wait">
<head>
<style>
div {
width:10px;
height:10px;
background-color:green;
display: inline-block;
}
</style>
</head>
<body id="body">
<div id="first" class="reftest-no-display-list"></div>
</body>
<script>
function doTest() {
var div = document.createElement("div");
var prev = document.getElementById("first");
prev.parentNode.insertBefore(div, prev.nextSibling);
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,24 @@
<html class="reftest-wait">
<head>
<style>
div {
width:10px;
height:10px;
background-color:green;
display: inline-block;
}
</style>
</head>
<body id="body">
<div id="first" class="reftest-no-display-list"></div><div id="second" class="reftest-no-display-list"></div><div id="third"></div>
</body>
<script>
function doTest() {
var elem = document.getElementById("third");
elem.parentNode.removeChild(elem);
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,24 @@
<html>
<head>
<style>
* {
margin: 0px;
padding: 0px;
}
.inner {
width: 100px;
height: 100px;
background-color: green;
display: inline-block;
}
body {
overflow: hidden;
}
</style>
</head>
<body id="body">
<div id="transformed" style="transform:translateX(700px);">
<div id="first" class="inner"></div>
</div>
</body>
</html>

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

@ -0,0 +1,36 @@
<html class="reftest-wait">
<head>
<style>
* {
margin: 0px;
padding: 0px;
}
.inner {
width: 100px;
height: 100px;
background-color: green;
display: inline-block;
}
#third {
background-color: red;
}
body {
overflow: hidden;
}
</style>
</head>
<body id="body">
<div id="transformed" style="transform:translateX(700px); will-change:transform;">
<div id="first" class="reftest-no-display-list inner"></div><div id="second" class="reftest-no-display-list inner"></div><div id="third" class="reftest-display-list inner"></div>
</div>
</body>
<script>
function doTest() {
var third = document.getElementById("third")
third.style.backgroundColor = "green";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,10 @@
<html>
<head>
</head>
<body>
<div id="container" style="height: 40px; overflow: hidden;">
<div id="spacer" style="height: 50px;"></div>
<div id="second" style="background-color:red; width: 10px; height: 10px;"></div>
</div>
</body>
</html>

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

@ -0,0 +1,18 @@
<html class="reftest-wait">
<head>
</head>
<body>
<div id="container" style="height: 100px; overflow: hidden;">
<div id="spacer" style="height: 50px;"></div>
<div id="second" style="background-color:red; width: 10px; height: 10px;"></div>
</div>
</body>
<script>
function doTest() {
document.getElementById("container").style.height = "40px";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
<div id="container" style="height: 100px; overflow: hidden;">
</div>
</body>
</html>

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

@ -0,0 +1,18 @@
<html class="reftest-wait">
<head>
</head>
<body>
<div id="container" style="height: 100px; overflow: hidden;">
<div id="second" style="background-color:red; width: 10px; height: 10px;"></div>
<div id="spacer" style="height: 200px;"></div>
</div>
</body>
<script>
function doTest() {
document.getElementById("container").scrollTop = 100;
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,15 @@
<html>
<head>
<style>
div {
width:10px;
height:10px;
background-color:green;
display: inline-block;
}
</style>
</head>
<body id="body">
<div id="first"></div><div id="second"></div>
</body>
</html>

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

@ -0,0 +1,23 @@
<html class="reftest-wait">
<head>
<style>
div {
width:10px;
height:10px;
background-color:green;
display: inline-block;
}
</style>
</head>
<body id="body">
<div id="first" class="reftest-no-display-list"></div><div id="second" style="background-color:red"></div>
</body>
<script>
function doTest() {
document.getElementById("second").style.backgroundColor = "green";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -0,0 +1,18 @@
<html>
<head>
<style>
body {
margin: 0px;
}
div {
width:200px;
height:200px;
display: inline-block;
position: absolute;
}
</style>
</head>
<body>
<div style="background-color:green"></div>
</body>
</html>

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

@ -0,0 +1,33 @@
<html class="reftest-wait">
<head>
<style>
body {
margin: 0px;
}
div {
width:100px;
height:100px;
display: inline-block;
position:absolute;
}
</style>
</head>
<body>
<div id="first" style="background-color:green; width:200px; height:200px" class="reftest-no-display-list"></div>
<div style="transform:translateZ(1px)">
<div id="second" style="background-color:red"></div>
</div>
<div style="position:fixed; left:100px">
<div id="third" style="background-color:red"></div>
</div>
</body>
<script>
function doTest() {
document.getElementById("second").style.backgroundColor = "green";
document.getElementById("third").style.backgroundColor = "green";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest);
</script>
</html>

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

@ -443,5 +443,8 @@ include ../../dom/encoding/test/reftest/reftest.list
# APZ/async positioning tests
include ../../gfx/layers/apz/test/reftest/reftest.list
# Display list building
include display-list/reftest.list
# Media
include ../../dom/media/test/reftest/reftest.list

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

@ -128,6 +128,8 @@ for (let [key, val] of Object.entries({
crashDumpDir: undefined,
pendingCrashDumpDir: undefined,
failedNoPaint: false,
failedNoDisplayList: false,
failedDisplayList: false,
failedOpaqueLayer: false,
failedOpaqueLayerMessages: [],
failedAssignedLayer: false,

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

@ -476,6 +476,9 @@ function BuildConditionSandbox(aURL) {
g.windowUtils.usingAdvancedLayers == true;
sandbox.layerChecksEnabled = !sandbox.webrender;
sandbox.retainedDisplayList =
prefs.getBoolPref("layout.display-list.retain");
// Shortcuts for widget toolkits.
sandbox.Android = xr.OS == "Android";
sandbox.cocoaWidget = xr.widgetToolkit == "cocoa";

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

@ -440,6 +440,12 @@ function shouldSnapshotWholePage(contentRootElement) {
function getNoPaintElements(contentRootElement) {
return contentRootElement.getElementsByClassName('reftest-no-paint');
}
function getNoDisplayListElements(contentRootElement) {
return contentRootElement.getElementsByClassName('reftest-no-display-list');
}
function getDisplayListElements(contentRootElement) {
return contentRootElement.getElementsByClassName('reftest-display-list');
}
function getOpaqueLayerElements(contentRootElement) {
return contentRootElement.getElementsByClassName('reftest-opaque-layer');
@ -598,6 +604,14 @@ function WaitForTestEnd(contentRootElement, inPrintMode, spellCheckedElements) {
for (var i = 0; i < elements.length; ++i) {
windowUtils().checkAndClearPaintedState(elements[i]);
}
elements = getNoDisplayListElements(contentRootElement);
for (var i = 0; i < elements.length; ++i) {
windowUtils().checkAndClearDisplayListState(elements[i]);
}
elements = getDisplayListElements(contentRootElement);
for (var i = 0; i < elements.length; ++i) {
windowUtils().checkAndClearDisplayListState(elements[i]);
}
var notification = content.document.createEvent("Events");
notification.initEvent("MozReftestInvalidate", true, false);
contentRootElement.dispatchEvent(notification);
@ -697,6 +711,23 @@ function WaitForTestEnd(contentRootElement, inPrintMode, spellCheckedElements) {
SendFailedNoPaint();
}
}
// We only support retained display lists in the content process
// right now, so don't fail reftest-no-display-list tests when
// we don't have e10s.
if (gBrowserIsRemote) {
elements = getNoDisplayListElements(contentRootElement);
for (var i = 0; i < elements.length; ++i) {
if (windowUtils().checkAndClearDisplayListState(elements[i])) {
SendFailedNoDisplayList();
}
}
elements = getDisplayListElements(contentRootElement);
for (var i = 0; i < elements.length; ++i) {
if (!windowUtils().checkAndClearDisplayListState(elements[i])) {
SendFailedDisplayList();
}
}
}
CheckLayerAssertions(contentRootElement);
}
LogInfo("MakeProgress: Completed");
@ -1159,6 +1190,16 @@ function SendFailedNoPaint()
sendAsyncMessage("reftest:FailedNoPaint");
}
function SendFailedNoDisplayList()
{
sendAsyncMessage("reftest:FailedNoDisplayList");
}
function SendFailedDisplayList()
{
sendAsyncMessage("reftest:FailedDisplayList");
}
function SendFailedOpaqueLayer(why)
{
sendAsyncMessage("reftest:FailedOpaqueLayer", { why: why });

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

@ -1050,7 +1050,7 @@ function RecordResult(testRunTime, errorMsg, typeSpecificResults)
differences >= g.urls[0].fuzzyMinPixels;
}
var failedExtraCheck = g.failedNoPaint || g.failedOpaqueLayer || g.failedAssignedLayer;
var failedExtraCheck = g.failedNoPaint || g.failedNoDisplayList || g.failedDisplayList || g.failedOpaqueLayer || g.failedAssignedLayer;
// whether the comparison result matches what is in the manifest
var test_passed = (equal == (g.urls[0].type == TYPE_REFTEST_EQUAL)) && !failedExtraCheck;
@ -1087,6 +1087,12 @@ function RecordResult(testRunTime, errorMsg, typeSpecificResults)
if (g.failedNoPaint) {
failures.push("failed reftest-no-paint");
}
if (g.failedNoDisplayList) {
failures.push("failed reftest-no-display-list");
}
if (g.failedDisplayList) {
failures.push("failed reftest-display-list");
}
// The g.failed*Messages arrays will contain messages from both the test and the reference.
if (g.failedOpaqueLayer) {
failures.push("failed reftest-opaque-layer: " + g.failedOpaqueLayerMessages.join(", "));
@ -1247,6 +1253,8 @@ function FinishTestItem()
// and tests will continue.
SendClear();
g.failedNoPaint = false;
g.failedNoDisplayList = false;
g.failedDisplayList = false;
g.failedOpaqueLayer = false;
g.failedOpaqueLayerMessages = [];
g.failedAssignedLayer = false;
@ -1331,6 +1339,14 @@ function RegisterMessageListenersAndLoadContentScript()
"reftest:FailedNoPaint",
function (m) { RecvFailedNoPaint(); }
);
g.browserMessageManager.addMessageListener(
"reftest:FailedNoDisplayList",
function (m) { RecvFailedNoDisplayList(); }
);
g.browserMessageManager.addMessageListener(
"reftest:FailedDisplayList",
function (m) { RecvFailedDisplayList(); }
);
g.browserMessageManager.addMessageListener(
"reftest:FailedOpaqueLayer",
function (m) { RecvFailedOpaqueLayer(m.json.why); }
@ -1403,6 +1419,16 @@ function RecvFailedNoPaint()
g.failedNoPaint = true;
}
function RecvFailedNoDisplayList()
{
g.failedNoDisplayList = true;
}
function RecvFailedDisplayList()
{
g.failedDisplayList = true;
}
function RecvFailedOpaqueLayer(why) {
g.failedOpaqueLayer = true;
g.failedOpaqueLayerMessages.push(why);