Bug 1712400 - Account for the resolution in the enclosing document when positioning and sizing the drag preview image when dragging content in a nested content process. r=hiro
authorBotond Ballo <botond@mozilla.com>
Sat, 10 Jul 2021 17:23:20 +0000
changeset 585258 65ecc7f9c8ee8c1cac4d12238f42b626b883b3d7
parent 585257 8e487542c42862d38349d6d9149cd816c04da87e
child 585259 e5591aaa2cdc8ace99686b1e09bcd5d4c7c39fa6
push id38602
push userdluca@mozilla.com
push dateSun, 11 Jul 2021 09:36:42 +0000
treeherdermozilla-central@e5591aaa2cdc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro
bugs1712400
milestone91.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1712400 - Account for the resolution in the enclosing document when positioning and sizing the drag preview image when dragging content in a nested content process. r=hiro Differential Revision: https://phabricator.services.mozilla.com/D119358
layout/base/PresShell.cpp
layout/base/Units.h
layout/base/ViewportUtils.cpp
layout/base/ViewportUtils.h
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4820,16 +4820,25 @@ UniquePtr<RangePaintInfo> PresShell::Cre
   // case with nested async zooms (even though that never actually happens),
   // because it fell out of the implementation for free.
   //
   // TODO: Do we need to do the same for ancestor transforms?
   for (nsPresContext* ctx = GetPresContext(); ctx;
        ctx = ctx->GetParentPresContext()) {
     PresShell* shell = ctx->PresShell();
     float resolution = shell->GetResolution();
+
+    // If we are at the root document in the process, try to see if documents
+    // in enclosing processes have a resolution and include that as well.
+    if (!ctx->GetParentPresContext()) {
+      // xScale is an arbitrary choice. Outside of edge cases involving CSS
+      // transforms, xScale == yScale so it doesn't matter.
+      resolution *= ViewportUtils::TryInferEnclosingResolution(shell).xScale;
+    }
+
     if (resolution == 1.0) {
       continue;
     }
 
     info->mResolution *= resolution;
     nsIFrame* rootScrollFrame = shell->GetRootScrollFrame();
     ViewID zoomedId =
         nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent());
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -233,16 +233,17 @@ typedef gfx::ScaleFactors2D<ScreenPixel,
 typedef gfx::ScaleFactors2D<ScreenPixel, ParentLayerPixel>
     ScreenToParentLayerScale2D;
 typedef gfx::ScaleFactors2D<ParentLayerPixel, LayerPixel>
     ParentLayerToLayerScale2D;
 typedef gfx::ScaleFactors2D<ParentLayerPixel, ScreenPixel>
     ParentLayerToScreenScale2D;
 typedef gfx::ScaleFactors2D<ParentLayerPixel, ParentLayerPixel>
     ParentLayerToParentLayerScale2D;
+typedef gfx::ScaleFactors2D<gfx::UnknownUnits, gfx::UnknownUnits> Scale2D;
 
 typedef gfx::Matrix4x4Typed<CSSPixel, CSSPixel> CSSToCSSMatrix4x4;
 typedef gfx::Matrix4x4Typed<LayoutDevicePixel, LayoutDevicePixel>
     LayoutDeviceToLayoutDeviceMatrix4x4;
 typedef gfx::Matrix4x4Typed<LayoutDevicePixel, ParentLayerPixel>
     LayoutDeviceToParentLayerMatrix4x4;
 typedef gfx::Matrix4x4Typed<LayerPixel, ParentLayerPixel>
     LayerToParentLayerMatrix4x4;
--- a/layout/base/ViewportUtils.cpp
+++ b/layout/base/ViewportUtils.cpp
@@ -1,15 +1,17 @@
 /* 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/. */
 
+#include "Units.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/ViewportFrame.h"
 #include "mozilla/ViewportUtils.h"
+#include "mozilla/dom/BrowserChild.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layers/ScrollableLayerGuid.h"
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsIScrollableFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsQueryFrame.h"
@@ -142,16 +144,30 @@ CSSRect ViewportUtils::DocumentRelativeL
                                                       PresShell* aShell) {
   ScrollableLayerGuid::ViewID targetScrollId =
       nsLayoutUtils::ScrollIdForRootScrollFrame(aShell->GetPresContext());
   auto visualToLayout =
       ViewportUtils::GetVisualToLayoutTransform(targetScrollId);
   return visualToLayout.Inverse().TransformBounds(aRect);
 }
 
+template <class SourceUnits, class DestUnits>
+gfx::PointTyped<DestUnits> TransformPointOrRect(
+    const gfx::Matrix4x4Typed<SourceUnits, DestUnits>& aMatrix,
+    const gfx::PointTyped<SourceUnits>& aPoint) {
+  return aMatrix.TransformPoint(aPoint);
+}
+
+template <class SourceUnits, class DestUnits>
+gfx::RectTyped<DestUnits> TransformPointOrRect(
+    const gfx::Matrix4x4Typed<SourceUnits, DestUnits>& aMatrix,
+    const gfx::RectTyped<SourceUnits>& aRect) {
+  return aMatrix.TransformBounds(aRect);
+}
+
 template <class LDPointOrRect>
 LDPointOrRect ConvertToScreenRelativeVisual(const LDPointOrRect& aInput,
                                             nsPresContext* aCtx) {
   MOZ_ASSERT(aCtx);
 
   LDPointOrRect layoutToVisual(aInput);
   nsIFrame* prevRootFrame = nullptr;
   nsPresContext* prevCtx = nullptr;
@@ -172,22 +188,38 @@ LDPointOrRect ConvertToScreenRelativeVis
       layoutToVisual =
           ViewportUtils::DocumentRelativeLayoutToVisual(layoutToVisual, shell);
     }
 
     prevRootFrame = rootFrame;
     prevCtx = ctx;
   }
 
+  // If we're in a nested content process, the above traversal will not have
+  // encountered the APZ zoom root. The translation part of the layout-to-visual
+  // transform will be included in |rootScreenRect.TopLeft()|, added below
+  // (that ultimately comes from nsIWidget::WidgetToScreenOffset(), which for an
+  // OOP iframe's widget includes this translation), but the scale part needs to
+  // be computed and added separately.
+  Scale2D enclosingResolution =
+      ViewportUtils::TryInferEnclosingResolution(prevCtx->GetPresShell());
+  if (enclosingResolution != Scale2D{1.0f, 1.0f}) {
+    layoutToVisual = TransformPointOrRect(
+        LayoutDeviceToLayoutDeviceMatrix4x4::Scaling(
+            enclosingResolution.xScale, enclosingResolution.yScale, 1.0f),
+        layoutToVisual);
+  }
+
   // Then we do the conversion from the rootmost presContext's root frame (in
   // visual space) to screen space.
   LayoutDeviceIntRect rootScreenRect =
       LayoutDeviceIntRect::FromAppUnitsToNearest(
           prevRootFrame->GetScreenRectInAppUnits(),
           prevCtx->AppUnitsPerDevPixel());
+
   return layoutToVisual + rootScreenRect.TopLeft();
 }
 
 LayoutDevicePoint ViewportUtils::ToScreenRelativeVisual(
     const LayoutDevicePoint& aPt, nsPresContext* aCtx) {
   return ConvertToScreenRelativeVisual(aPt, aCtx);
 }
 
@@ -222,9 +254,45 @@ const nsIFrame* ViewportUtils::IsZoomedC
       if (viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
         return viewportFrame->PresShell()->GetRootScrollFrame();
       }
     }
   }
   return nullptr;
 }
 
+Scale2D ViewportUtils::TryInferEnclosingResolution(PresShell* aShell) {
+  MOZ_ASSERT(aShell && aShell->GetPresContext());
+  MOZ_ASSERT(!aShell->GetPresContext()->GetParentPresContext(),
+             "TryInferEnclosingResolution can only be called for a root pres "
+             "shell within a process");
+  if (dom::BrowserChild* bc = dom::BrowserChild::GetFrom(aShell)) {
+    if (!bc->IsTopLevel()) {
+      // The enclosing resolution is not directly available in the BrowserChild.
+      // The closest thing available is GetChildToParentConversionMatrix(),
+      // which also includes any enclosing CSS transforms.
+      // The behaviour implemented here will not provide an accurate answer
+      // in the presence of CSS transforms, but it tries to do something
+      // reasonable:
+      //  - If there are no enclosing CSS transforms, it will return the
+      //    resolution.
+      //  - If the enclosing transforms contain scales and translations only,
+      //    it will return the resolution times the CSS transform scale
+      //    (choosing the x-scale if they are different).
+      //  - Otherwise, it will return the resolution times a scale component
+      //    of the transform as returned by Matrix4x4Typed::Decompose().
+      //  - If the enclosing transform is sufficiently complex that
+      //    Decompose() returns false, give up and return 1.0.
+      gfx::Point3DTyped<gfx::UnknownUnits> translation;
+      gfx::Quaternion rotation;
+      gfx::Point3DTyped<gfx::UnknownUnits> scale;
+      // Need to call ToUnknownMatrix() because Decompose() doesn't properly
+      // support typed units.
+      if (bc->GetChildToParentConversionMatrix().ToUnknownMatrix().Decompose(
+              translation, rotation, scale)) {
+        return {scale.x, scale.y};
+      }
+    }
+  }
+  return {1.0f, 1.0f};
+}
+
 }  // namespace mozilla
--- a/layout/base/ViewportUtils.h
+++ b/layout/base/ViewportUtils.h
@@ -4,16 +4,17 @@
 
 #ifndef mozilla_ViewportUtils_h
 #define mozilla_ViewportUtils_h
 
 #include "Units.h"
 #include "mozilla/layers/ScrollableLayerGuid.h"
 
 class nsIFrame;
+class nsPresContext;
 
 namespace mozilla {
 
 class PresShell;
 
 class ViewportUtils {
  public:
   /* Return a transform to be applied to the coordinates of input events
@@ -88,16 +89,31 @@ class ViewportUtils {
    * parent frame is not, thereby making |aFrame| a root of a subtree of
    * frames representing content that is zoomed in. The value returned in such
    * cases is the root scroll frame inside the async zoom container.
 
    * Callers use this to identify points during frame tree traversal where the
    * visual-to-layout transform needs to be applied.
    */
   static const nsIFrame* IsZoomedContentRoot(const nsIFrame* aFrame);
+
+  /**
+   * If |aShell| is in a nested content process, try to infer the resolution
+   * at which the enclosing root content
+   * document has been painted.
+   *
+   * Otherwise (if |aShell| is in the chrome or top-level content process),
+   * this function returns 1.0.
+   *
+   * |aShell| must be the root pres shell in its process.
+   *
+   * This function may not return an accurate answer if there is also
+   * a CSS transform enclosing the iframe.
+   */
+  static Scale2D TryInferEnclosingResolution(PresShell* aShell);
 };
 
 // Forward declare explicit instantiations of GetVisualToLayoutTransform() for
 // CSSPixel and LayoutDevicePixel, the only two types it gets used with.
 // These declarations promise to callers in any translation unit that _some_
 // translation unit (in this case, ViewportUtils.cpp) will contain the
 // definitions of these instantiations. This allows us to keep the definition
 // out-of-line in the source.