Bug 1715179 - Handle `overflow: visible` case in OOP iframes. r=botond

Differential Revision: https://phabricator.services.mozilla.com/D193755
This commit is contained in:
Hiroyuki Ikezoe 2023-12-04 09:31:23 +00:00
Родитель 80e4683e9e
Коммит d3d4a5137d
4 изменённых файлов: 106 добавлений и 2 удалений

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

@ -79,6 +79,7 @@ async function test(aTestFile) {
let resolution = await getResolution();
ok(resolution > 0,
"The initial_resolution is " + resolution + ", which is some sane value");
let initial_resolution = resolution;
let pos = await targetElementPosition();
const iframe = document.querySelector("iframe");
@ -97,11 +98,18 @@ async function test(aTestFile) {
let zoomedOutState = cloneVisualViewport();
// Reset the scale to the initial one since in the `visible: overflow` case
// the second double-tap doesn't restore to the initial scale.
SpecialPowers.DOMWindowUtils.setResolutionAndScaleTo(initial_resolution);
await promiseApzFlushedRepaints();
// Now load the document in an OOP iframe.
iframeURL = iframeURL.replace(window.location.origin, "https://example.com");
await setupIframe(iframeURL);
pos = await targetElementPosition();
await doubleTapOn(iframe, pos.x + 10, pos.y + 10, useTouchpad);
prev_resolution = resolution;
resolution = await getResolution();
ok(resolution > prev_resolution, "The first double-tap has increased the resolution to " + resolution);
@ -132,6 +140,10 @@ waitUntilApzStable()
.then(async () => test("helper_doubletap_zoom_oopif_subframe-1.html"))
// A test case where the layout scroll offset in the iframe isn't zero.
.then(async () => test("helper_doubletap_zoom_oopif_subframe-2.html#target"))
// A test case where the double-tap-to-zoom target element is `overflow: visible`.
.then(async () => test("helper_doubletap_zoom_oopif_subframe-3.html#target"))
// Similar to above but the target element has positive `margin-top`.
.then(async () => test("helper_doubletap_zoom_oopif_subframe-4.html#target"))
.then(subtestDone, subtestFailed);
</script>

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

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<script src="apz_test_native_event_utils.js"></script>
<script src="apz_test_utils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<style>
.container {
background-color: #eee;
width: 200px;
height: 50px;
border: 1px dotted black;
overflow: visible;
}
</style>
<div class="container"></div>
<script>
let text = "";
for (let i = 0; i < 200; i++) {
text += "Text text text text text text text text text text text text text text text text\n";
}
text += '<div id="target" style="width: 100%; height: 10px; background-color: red; position: relative; top: -20px; z-index: -1"></div>';
document.querySelector(".container").innerHTML = text;
// Silence SimpleTest warning about missing assertions by having it wait
// indefinitely. We don't need to give it an explicit finish because the
// entire window this test runs in will be closed after the main browser test
// finished.
SimpleTest.waitForExplicitFinish();
</script>
</html>

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

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<script src="apz_test_native_event_utils.js"></script>
<script src="apz_test_utils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<style>
.container {
background-color: #eee;
width: 200px;
height: 50px;
border: 1px dotted black;
overflow: visible;
margin-top: 200px;
}
</style>
<div class="container"></div>
<script>
let text = '<div id="target" style="width: 100%; height: 10px; background-color: red; position: relative; top: 60px; z-index: -1"></div>';
for (let i = 0; i < 200; i++) {
text += "Text text text text text text text text text text text text text text text text\n";
}
document.querySelector(".container").innerHTML = text;
// Silence SimpleTest warning about missing assertions by having it wait
// indefinitely. We don't need to give it an explicit finish because the
// entire window this test runs in will be closed after the main browser test
// finished.
SimpleTest.waitForExplicitFinish();
</script>
</html>

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

@ -11,6 +11,8 @@
#include "mozilla/PresShell.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/EffectsInfo.h"
#include "mozilla/dom/BrowserChild.h"
#include "nsCOMPtr.h"
#include "nsIContent.h"
#include "mozilla/dom/Document.h"
@ -104,9 +106,10 @@ static CSSRect GetBoundingContentRect(
const nsIScrollableFrame* aRootScrollFrame,
const DoubleTapToZoomMetrics& aMetrics,
mozilla::Maybe<CSSRect>* aOutNearestScrollClip = nullptr) {
CSSRect result = nsLayoutUtils::GetBoundingContentRect(
aElement, aRootScrollFrame, aOutNearestScrollClip);
if (aInProcessRootContentDocument->IsTopLevelContentDocument()) {
return nsLayoutUtils::GetBoundingContentRect(aElement, aRootScrollFrame,
aOutNearestScrollClip);
return result;
}
nsIFrame* frame = aElement->GetPrimaryFrame();
@ -114,6 +117,24 @@ static CSSRect GetBoundingContentRect(
return CSSRect();
}
// If the nearest scroll frame is |aRootScrollFrame|,
// nsLayoutUtils::GetBoundingContentRect doesn't set |aOutNearestScrollClip|,
// thus in the cases of OOP iframs, we need to use the visible rect of the
// iframe as the nearest scroll clip.
if (aOutNearestScrollClip && aOutNearestScrollClip->isNothing()) {
if (dom::BrowserChild* browserChild =
dom::BrowserChild::GetFrom(frame->PresShell())) {
const dom::EffectsInfo& effectsInfo = browserChild->GetEffectsInfo();
if (effectsInfo.IsVisible()) {
*aOutNearestScrollClip =
effectsInfo.mVisibleRect.map([&aMetrics](const nsRect& aRect) {
return aMetrics.mTransformMatrix.TransformBounds(
CSSRect::FromAppUnits(aRect));
});
}
}
}
// In the case of an element inside an OOP iframe, |aMetrics.mTransformMatrix|
// includes the translation information about the root layout scroll offset,
// thus we use nsIFrame::GetBoundingClientRect rather than
@ -301,6 +322,15 @@ ZoomTarget CalculateRectToZoomTo(
res == nsLayoutUtils::NONINVERTIBLE_TRANSFORM);
if (res == nsLayoutUtils::TRANSFORM_SUCCEEDED) {
CSSRect overflowRectCSS = CSSRect::FromAppUnits(overflowRect);
// In the case of OOP iframes, above |overflowRectCSS| in the iframe
// documents coords, we need to convert it into the top level coords.
if (!aInProcessRootContentDocument->IsTopLevelContentDocument()) {
overflowRectCSS.MoveBy(
CSSPoint::FromAppUnits(-rootScrollFrame->GetScrollPosition()));
overflowRectCSS =
aMetrics.mTransformMatrix.TransformBounds(overflowRectCSS);
}
if (nearestScrollClip.isSome()) {
overflowRectCSS = nearestScrollClip->Intersect(overflowRectCSS);
}