Bug 1790253: Check whether elements having 'display: contents' style is apz aware. r=botond,smaug,emilio

Differential Revision: https://phabricator.services.mozilla.com/D159378
This commit is contained in:
Daisuke Akatsuka 2022-12-19 08:41:20 +00:00
parent 5804f40537
commit 6f6246b2a2
5 changed files with 284 additions and 6 deletions

View File

@ -115,6 +115,9 @@ support-files =
engine2/manifest.json
[browser_searchFindMoreLink.js]
[browser_searchRestoreDefaults.js]
[browser_searchScroll.js]
support-files =
!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
[browser_searchShowSuggestionsFirst.js]
[browser_searchsuggestions.js]
[browser_security-1.js]

View File

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
this
);
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
const { SearchTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/SearchTestUtils.sys.mjs"
);
const { SearchUtils } = ChromeUtils.importESModule(
"resource://gre/modules/SearchUtils.sys.mjs"
);
AddonTestUtils.initMochitest(this);
SearchTestUtils.init(this);
add_setup(async function() {
await SpecialPowers.pushPrefEnv({
set: [["dom.w3c_touch_events.enabled", 0]],
});
});
add_task(async function test_scroll() {
info("Open preferences page for search");
await openPreferencesViaOpenPreferencesAPI("search", { leaveOpen: true });
const doc = gBrowser.selectedBrowser.contentDocument;
const tree = doc.querySelector("#engineList");
info("Add engines to make the tree scrollable");
for (let i = 0, n = parseInt(tree.getAttribute("rows")); i < n; i++) {
let extension = await SearchTestUtils.installSearchExtension({
id: `${i}@tests.mozilla.org`,
name: `example${i}`,
version: "1.0",
keyword: `example${i}`,
});
await AddonTestUtils.waitForSearchProviderStartup(extension);
}
info("Make tree element move into viewport");
const mainContent = doc.querySelector(".main-content");
const readyForScrollIntoView = new Promise(r => {
mainContent.addEventListener("scroll", r, { once: true });
});
tree.scrollIntoView();
await readyForScrollIntoView;
const previousScroll = mainContent.scrollTop;
await promiseMoveMouseAndScrollWheelOver(tree, 1, 1, false);
Assert.equal(
previousScroll,
mainContent.scrollTop,
"Container element does not scroll"
);
info("Clean up");
gBrowser.removeCurrentTab();
});

View File

@ -2833,18 +2833,37 @@ class AutoSaveRestoreContainsBlendMode {
}
};
static bool IsFrameOrAncestorApzAware(nsIFrame* aFrame) {
nsIContent* node = aFrame->GetContent();
if (!node) {
return false;
}
do {
if (node->IsNodeApzAware()) {
return true;
}
nsIContent* shadowRoot = node->GetShadowRoot();
if (shadowRoot && shadowRoot->IsNodeApzAware()) {
return true;
}
// Even if the node owning aFrame doesn't have apz-aware event listeners
// itself, its shadow root or display: contents ancestors (which have no
// frames) might, so we need to account for them too.
} while ((node = node->GetFlattenedTreeParent()) && node->IsElement() &&
node->AsElement()->IsDisplayContents());
return false;
}
static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame) {
if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
return;
}
nsIContent* content = aFrame->GetContent();
if (!content) {
return;
}
if (content->IsNodeApzAware()) {
if (IsFrameOrAncestorApzAware(aFrame)) {
aBuilder->SetAncestorHasApzAwareEventHandler(true);
}
}

View File

@ -154,3 +154,5 @@ support-files =
file_reframe_for_lazy_load_image.html
[test_bug1655135.html]
[test_bug1756831.html]
[test_scroll_on_display_contents.html]
support-files = !/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js

View File

@ -0,0 +1,187 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1790253
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1790253</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790253">Mozilla Bug 1790253</a>
<p id="display"></p>
<style>
.container {
height: 200px;
width: 200px;
overflow: scroll;
background-color: gray;
}
</style>
<script>
SimpleTest.waitForExplicitFinish();
document.addEventListener("DOMContentLoaded", async () => {
await testShadowRoot();
await testShadowRootInDisplayContent();
await testNestedShadowRoot();
await testDisplayContent();
await testNestedDisplayContent();
SimpleTest.finish();
});
async function testShadowRoot() {
// Structure:
// <div class="container">
// #ShadowRoot - Listener
// <div style="height: 300px; background: linear-gradient(#e66465, #9198e5);">
const container = document.createElement("div");
container.classList.add("container");
container.attachShadow({ mode: "open" });
container.shadowRoot.innerHTML = `
<div style="height: 300px; background: linear-gradient(#e66465, #9198e5);"></div>
`;
container.shadowRoot.addEventListener("wheel", e => { e.preventDefault(); });
document.body.append(container);
await doTest(container);
container.remove();
}
async function testShadowRootInDisplayContent() {
// Structure:
// <div class="container">
// <div style="display: contents">
// #ShadowRoot - Listener
// <div style="display: contents">
// <div style="display: contents">
// <div style="height: 300px; background: linear-gradient(#e66465, #9198e5);">
const container = document.createElement("div");
container.classList.add("container");
container.innerHTML = `
<div style="display: contents"></div>
`;
const displayContent = container.querySelector("div");
displayContent.attachShadow({ mode: "open" });
displayContent.shadowRoot.innerHTML = `
<div style="display: contents">
<div style="display: contents">
<div style="height: 300px; background: linear-gradient(#e66465, #9198e5);"></div>
</div>
</div>
`;
displayContent.shadowRoot.addEventListener("wheel", e => { e.preventDefault(); });
document.body.append(container);
await doTest(container);
container.remove();
}
async function testNestedShadowRoot() {
// Structure:
// <div class="container">
// <div style="display: contents">
// #ShadowRoot - Listener
// <div style="display: contents">
// #ShadowRoot
// <div style="display: contents">
// <div style="display: contents">
// <div style="height: 300px; background: linear-gradient(#e66465, #9198e5);">
const container = document.createElement("div");
container.classList.add("container");
container.innerHTML = `
<div style="display: contents"></div>
`;
const firstDisplayContent = container.querySelector("div");
firstDisplayContent.attachShadow({ mode: "open" });
firstDisplayContent.shadowRoot.innerHTML = `
<div style="display: contents"></div>
`;
firstDisplayContent.shadowRoot.addEventListener("wheel", e => { e.preventDefault(); });
const secondDisplayContent = firstDisplayContent.shadowRoot.querySelector("div");
secondDisplayContent.attachShadow({ mode: "open" });
firstDisplayContent.shadowRoot.innerHTML = `
<div style="display: contents">
<div style="display: contents">
<div style="height: 300px; background: linear-gradient(#e66465, #9198e5);"></div>
</div>
</div>
`;
document.body.append(container);
await doTest(container);
container.remove();
}
async function testDisplayContent() {
// Structure:
// <div class="container">
// <div style="display: contents"> - Listener
// <div style="height: 300px; background: linear-gradient(#e66465, #9198e5);">
const container = document.createElement("div");
container.classList.add("container");
container.innerHTML = `
<div style="display: contents">
<div style="height: 300px; background: linear-gradient(#e66465, #9198e5);"></div>
</div>
`;
const displayContent = container.querySelector("div");
displayContent.addEventListener("wheel", e => { e.preventDefault(); });
document.body.append(container);
await doTest(container);
container.remove();
}
async function testNestedDisplayContent() {
// Structure:
// <div class="container">
// <div style="display: contents"> - Listener
// <div style="display: contents">
// <div style="height: 300px; background: linear-gradient(#e66465, #9198e5);">
const container = document.createElement("div");
container.classList.add("container");
container.innerHTML = `
<div style="display: contents">
<div style="display: contents">
<div style="height: 300px; background: linear-gradient(#e66465, #9198e5);"></div>
</div>
</div>
`;
const displayContent = container.querySelector("div");
displayContent.addEventListener("wheel", e => { e.preventDefault(); });
document.body.append(container);
await doTest(container);
container.remove();
}
async function doTest(target) {
await promiseAllPaintsDone();
const previousScroll = target.scrollTop;
await promiseMoveMouseAndScrollWheelOver(target, 1, 1, false);
await promiseApzFlushedRepaints();
is(previousScroll, target.scrollTop, "The target should not be scrolled");
}
</script>
</body>
</html>