diff --git a/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif-2.html b/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif-2.html index 74707b0d8a98..b737cdf21176 100644 --- a/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif-2.html +++ b/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif-2.html @@ -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); diff --git a/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif_subframe-3.html b/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif_subframe-3.html new file mode 100644 index 000000000000..30b3ca930cb8 --- /dev/null +++ b/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif_subframe-3.html @@ -0,0 +1,31 @@ + + + + + + + +
+ + diff --git a/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif_subframe-4.html b/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif_subframe-4.html new file mode 100644 index 000000000000..b98f48450e03 --- /dev/null +++ b/gfx/layers/apz/test/mochitest/helper_doubletap_zoom_oopif_subframe-4.html @@ -0,0 +1,31 @@ + + + + + + + +
+ + diff --git a/gfx/layers/apz/util/DoubleTapToZoom.cpp b/gfx/layers/apz/util/DoubleTapToZoom.cpp index 23206bfd9c7e..cf64a310b763 100644 --- a/gfx/layers/apz/util/DoubleTapToZoom.cpp +++ b/gfx/layers/apz/util/DoubleTapToZoom.cpp @@ -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* 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); }