Bug 1731009 - Properly check whether the given nsIFrame is inside an OOP Iframe visible rect or not even if the frame size is (0x0). r=tnikkel

Before this change, in nsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess
we call BaseRect::IsEmpty() for the returned value of GetFrameVisibleRectOnScreen,
unfortunately if the returned value size is (0x0), BaseRect::IsEmpty() returns
true thus we misthink the given nsIFrame is out of the visible area of the OOP
iframe where the nsIFrame lives.

Note that we have already done the same check for in-process cases in
IsFrameScrolledOutOfView [1].

The test case in this change fails without this change, suceeds with
the change. Though, to be honest, I don't know the reason those styles
, `display: grid`, etc. generate (0x0) sized frame even if decendants have
sized.

[1] https://searchfox.org/mozilla-central/rev/15de05f0e6d841cbc2ac66f8dcad72ebdada47f6/layout/generic/nsIFrame.cpp#11090-11094

Differential Revision: https://phabricator.services.mozilla.com/D126312
This commit is contained in:
Hiroyuki Ikezoe 2021-09-22 22:10:37 +00:00
Родитель 26910d70ce
Коммит 3608e394a5
4 изменённых файлов: 97 добавлений и 13 удалений

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

@ -22,7 +22,6 @@ support-files =
mozilla/file_disable_animations_api_implicit_keyframes.html
mozilla/file_disable_animations_api_timelines.html
mozilla/file_discrete_animations.html
mozilla/file_restyles.html
mozilla/file_transition_finish_on_compositor.html
../../../layout/style/test/property_database.js
testcommon.js
@ -53,6 +52,9 @@ skip-if = (os == 'win' && bits == 64) # Bug 1363957
skip-if =
fission && xorigin && os == "linux" && !debug # Bug 1716403 - New fission platform triage
[mozilla/test_restyles.html]
support-files =
mozilla/file_restyles.html
mozilla/empty.html
skip-if =
os == 'win' && !webrender # Bug 1663509
[mozilla/test_restyling_xhr_doc.html]

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

@ -0,0 +1,2 @@
<!DOCTYPE html>
<script src="../testcommon.js"></script>

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

@ -2165,6 +2165,60 @@ waitForAllPaints(() => {
await ensureElementRemoval(div);
});
add_task_if_omta_enabled(async function transform_animation_on_collapsed_element() {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
// Load a cross origin iframe.
const targetURL = SimpleTest.getTestFileURL("empty.html")
.replace(window.location.origin, "http://example.com/");
iframe.src = targetURL;
await new Promise(resolve => {
iframe.onload = resolve;
});
await SpecialPowers.spawn(iframe, [MS_PER_SEC], async (MS_PER_SEC) => {
// Create a flex item with "preserve-3d" having an abs-pos child inside
// a grid container.
// These styles make the the flex item size (0x0).
const gridContainer = content.document.createElement("div");
gridContainer.style.display = "grid";
gridContainer.style.placeItems = "center";
const target = content.document.createElement("div");
target.style.display = "flex";
target.style.transformStyle = "preserve-3d";
gridContainer.appendChild(target);
const child = content.document.createElement("div");
child.style.position = "absolute";
child.style.transform = "rotateY(0deg)";
child.style.width = "100px";
child.style.height = "100px";
child.style.backgroundColor = "green";
target.appendChild(child);
content.document.body.appendChild(gridContainer);
const animation =
target.animate({ transform: [ "rotateY(0deg)", "rotateY(360deg)" ] },
{ duration: 100 * MS_PER_SEC,
id: "test",
easing: 'step-end' });
await content.wrappedJSObject.waitForAnimationReadyToRestyle(animation);
ok(SpecialPowers.wrap(animation).isRunningOnCompositor,
'transform animation on a collapsed element should run on the ' +
'compositor');
const markers = await content.wrappedJSObject.observeStyling(5);
is(markers.length, 0,
'transform animation on a collapsed element animation should not ' +
'update styles on the main thread');
});
await ensureElementRemoval(iframe);
});
});
</script>

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

@ -9714,32 +9714,40 @@ ComputedStyle* nsLayoutUtils::StyleForScrollbar(nsIFrame* aScrollbarPart) {
return style.get();
}
// NOTE: Returns Nothing() if |aFrame| is not in out-of-process or if we haven't
// received enough information from APZ.
static Maybe<ScreenRect> GetFrameVisibleRectOnScreen(const nsIFrame* aFrame) {
enum class FramePosition : uint8_t {
Unknown,
InView,
OutOfView,
};
// NOTE: Returns a pair of Nothing() and `FramePosition::Unknown` if |aFrame|
// is not in out-of-process or if we haven't received enough information from
// APZ.
static std::pair<Maybe<ScreenRect>, FramePosition> GetFrameVisibleRectOnScreen(
const nsIFrame* aFrame) {
// We actually want the in-process top prescontext here.
nsPresContext* topContextInProcess =
aFrame->PresContext()->GetInProcessRootContentDocumentPresContext();
if (!topContextInProcess) {
// We are in chrome process.
return Nothing();
return std::make_pair(Nothing(), FramePosition::Unknown);
}
if (topContextInProcess->Document()->IsTopLevelContentDocument()) {
// We are in the top of content document.
return Nothing();
return std::make_pair(Nothing(), FramePosition::Unknown);
}
nsIDocShell* docShell = topContextInProcess->GetDocShell();
BrowserChild* browserChild = BrowserChild::GetFrom(docShell);
if (!browserChild) {
// We are not in out-of-process iframe.
return Nothing();
return std::make_pair(Nothing(), FramePosition::Unknown);
}
if (!browserChild->GetEffectsInfo().IsVisible()) {
// There is no visible rect on this iframe at all.
return Some(ScreenRect());
return std::make_pair(Some(ScreenRect()), FramePosition::Unknown);
}
Maybe<ScreenRect> visibleRect =
@ -9747,7 +9755,7 @@ static Maybe<ScreenRect> GetFrameVisibleRectOnScreen(const nsIFrame* aFrame) {
if (!visibleRect) {
// We are unsure if we haven't received the transformed rectangle of the
// iframe's visible area.
return Nothing();
return std::make_pair(Nothing(), FramePosition::Unknown);
}
nsIFrame* rootFrame = topContextInProcess->PresShell()->GetRootFrame();
@ -9762,24 +9770,42 @@ static Maybe<ScreenRect> GetFrameVisibleRectOnScreen(const nsIFrame* aFrame) {
rectInLayoutDevicePixel),
PixelCastJustification::ContentProcessIsLayerInUiProcess);
return Some(visibleRect->Intersect(transformedToRoot));
FramePosition position = FramePosition::Unknown;
// we need to check whether the transformed rect is outside the iframe
// visible rect or not because in some cases the rect size is (0x0), thus
// the intersection between the transformed rect and the iframe visible rect
// would also be (0x0), then we can't tell whether the given nsIFrame is
// inside the iframe visible rect or not by calling BaseRect::IsEmpty for the
// intersection.
if (transformedToRoot.x > visibleRect->XMost() ||
transformedToRoot.y > visibleRect->YMost() ||
visibleRect->x > transformedToRoot.XMost() ||
visibleRect->y > transformedToRoot.YMost()) {
position = FramePosition::OutOfView;
} else {
position = FramePosition::InView;
}
return std::make_pair(Some(visibleRect->Intersect(transformedToRoot)),
position);
}
// static
bool nsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess(
const nsIFrame* aFrame) {
Maybe<ScreenRect> visibleRect = GetFrameVisibleRectOnScreen(aFrame);
auto [visibleRect, framePosition] = GetFrameVisibleRectOnScreen(aFrame);
if (visibleRect.isNothing()) {
return false;
}
return visibleRect->IsEmpty();
return visibleRect->IsEmpty() && framePosition != FramePosition::InView;
}
// static
bool nsLayoutUtils::FrameIsMostlyScrolledOutOfViewInCrossProcess(
const nsIFrame* aFrame, nscoord aMargin) {
Maybe<ScreenRect> visibleRect = GetFrameVisibleRectOnScreen(aFrame);
auto [visibleRect, framePosition] = GetFrameVisibleRectOnScreen(aFrame);
(void)framePosition;
if (visibleRect.isNothing()) {
return false;
}