Bug 1541705 - Try to see whether the target frame is scrolled out of view in out-of-process iframe if we couldn't walk up the frame tree. r=surkov

Differential Revision: https://phabricator.services.mozilla.com/D44423

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Hiroyuki Ikezoe 2019-09-12 08:12:49 +00:00
Родитель 8c9bd09b43
Коммит 6f114d0542
4 изменённых файлов: 135 добавлений и 1 удалений

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

@ -362,12 +362,12 @@ uint64_t Accessible::VisibilityState() const {
// If contained by scrollable frame then check that at least 12 pixels
// around the object is visible, otherwise the object is offscreen.
nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
if (scrollableFrame) {
nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
nsRect frameRect = nsLayoutUtils::TransformFrameRectToAncestor(
frame, frame->GetRectRelativeToSelf(), parentFrame);
if (!scrollPortRect.Contains(frameRect)) {
const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
scrollPortRect.Deflate(kMinPixels, kMinPixels);
if (!scrollPortRect.Intersects(frameRect)) return states::OFFSCREEN;
}
@ -375,6 +375,14 @@ uint64_t Accessible::VisibilityState() const {
if (!parentFrame) {
parentFrame = nsLayoutUtils::GetCrossDocParentFrame(curFrame);
// Even if we couldn't find the parent frame, it might mean we are in an
// out-of-process iframe, try to see if |frame| is scrolled out in an
// scrollable frame in a cross-process ancestor document.
if (!parentFrame &&
nsLayoutUtils::FrameIsMostlyScrolledOutOfViewInCrossProcess(
frame, kMinPixels)) {
return states::OFFSCREEN;
}
}
curFrame = parentFrame;

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

@ -7,3 +7,5 @@ support-files =
[browser_test_link.js]
skip-if = verify
[browser_test_visibility.js]
[browser_offscreen_element_in_out_of_process_iframe.js]
skip-if = (webrender && os == 'win') # bug 1580706

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

@ -0,0 +1,90 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const parentURL =
"data:text/html;charset=utf-8," +
'<div id="scroller" style="width: 300px; height: 300px; overflow-y: scroll; overflow-x: hidden;">' +
' <div style="width: 100%; height: 1000px;"></div>' +
' <iframe frameborder="0"/>' +
"</div>";
const iframeURL =
"data:text/html;charset=utf-8," +
"<style>" +
" html,body {" +
" /* Convenient for calculation of element positions */" +
" margin: 0;" +
" padding: 0;" +
" }" +
"</style>" +
'<div id="target" style="width: 100px; height: 100px;">target</div>';
add_task(async function() {
const win = await BrowserTestUtils.openNewBrowserWindow({
fission: true,
});
try {
const browser = win.gBrowser.selectedTab.linkedBrowser;
BrowserTestUtils.loadURI(browser, parentURL);
await BrowserTestUtils.browserLoaded(browser, false, parentURL);
async function setup(url) {
const iframe = content.document.querySelector("iframe");
iframe.contentWindow.location = url;
await new Promise(resolve => {
iframe.addEventListener("load", resolve, { once: true });
});
return iframe.browsingContext;
}
async function scrollTo(x, y) {
await SpecialPowers.spawn(browser, [x, y], async (scrollX, scrollY) => {
const scroller = content.document.getElementById("scroller");
scroller.scrollTo(scrollX, scrollY);
await new Promise(resolve => {
scroller.addEventListener("scroll", resolve, { once: true });
});
});
await waitForIFrameUpdates();
}
// Setup an out-of-process iframe which is initially scrolled out.
const iframe = await SpecialPowers.spawn(browser, [iframeURL], setup);
await waitForIFrameUpdates();
await spawnTestStates(
iframe,
"target",
nsIAccessibleStates.STATE_OFFSCREEN
);
// Scroll the iframe into view and the target element is also visible but
// the visible area height is 11px.
await scrollTo(0, 711);
await spawnTestStates(
iframe,
"target",
nsIAccessibleStates.STATE_OFFSCREEN
);
// Scroll to a position where the visible height is 13px.
await scrollTo(0, 713);
await spawnTestStates(iframe, "target", 0);
// Scroll the iframe out again.
await scrollTo(0, 0);
await spawnTestStates(
iframe,
"target",
nsIAccessibleStates.STATE_OFFSCREEN
);
} finally {
await BrowserTestUtils.closeWindow(win);
}
});

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

@ -4,6 +4,8 @@
"use strict";
/* exported waitForIFrameUpdates, spawnTestStates */
// Load the shared-head file first.
/* import-globals-from ../shared-head.js */
Services.scriptloader.loadSubScript(
@ -17,3 +19,35 @@ loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);
// A utility function to make sure the information of scroll position or visible
// area changes reach to out-of-process iframes.
async function waitForIFrameUpdates() {
// Wait for two frames since the information is notified via asynchronous IPC
// calls.
await new Promise(resolve => requestAnimationFrame(resolve));
await new Promise(resolve => requestAnimationFrame(resolve));
}
// A utility function to test the state of |elementId| element in out-of-process
// |browsingContext|.
async function spawnTestStates(browsingContext, elementId, expectedStates) {
function testStates(id, expected) {
const acc = SpecialPowers.Cc[
"@mozilla.org/accessibilityService;1"
].getService(SpecialPowers.Ci.nsIAccessibilityService);
const target = content.document.getElementById(id);
let state = {};
acc.getAccessibleFor(target).getState(state, {});
if (expected === 0) {
Assert.equal(state.value, expected);
} else {
Assert.ok(state.value & expected);
}
}
await SpecialPowers.spawn(
browsingContext,
[elementId, expectedStates],
testStates
);
}