blink: Raster OOPIF using its scaling factor to main frame space

For OOPIFs, the compositor viewport is based on the interest rect sent
from the parent which is in frame's space.  The OOPIF compositor doesn't
know the transform from the child frame's space to main frame space so
it will raster at the default page scale which wastes GPU memory.

This CL fixes that by propagating the transform scale obtained from
RemoteFrameView::GetCompositingScaleFactor() to the OOPIF renderer via
|VisualProperties::compositing_scale_factor|.

On a pathological test page with a 4K size OOPIF at 500% page zoom,
steady state GPU memory goes from ~1 GB to ~37 MB.  Improvements in real
world pages should be seen in Compositing.ResourcePoolMemoryUsage.OOPIF
UMA histogram.

Bug: 1047289
Change-Id: Ib090bfc03bd81ce6867ba8f7259b8a8f1e222a1a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2482422
Reviewed-by: Stefan Zager <szager@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835479}
diff --git a/content/browser/renderer_host/cross_process_frame_connector.cc b/content/browser/renderer_host/cross_process_frame_connector.cc
index 274c7d2..0d19335 100644
--- a/content/browser/renderer_host/cross_process_frame_connector.cc
+++ b/content/browser/renderer_host/cross_process_frame_connector.cc
@@ -170,6 +170,7 @@
                                     visual_properties.max_size_for_auto_resize);
   render_widget_host->SetVisualPropertiesFromParentFrame(
       visual_properties.page_scale_factor,
+      visual_properties.compositing_scale_factor,
       visual_properties.is_pinch_gesture_active,
       visual_properties.visible_viewport_size,
       visual_properties.compositor_viewport,
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 01e7f5e..5a80366 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -965,6 +965,9 @@
         properties_from_parent_local_root_.is_pinch_gesture_active;
   }
 
+  visual_properties.compositing_scale_factor =
+      properties_from_parent_local_root_.compositing_scale_factor;
+
   // The |visible_viewport_size| is affected by auto-resize which is magical and
   // tricky.
   //
@@ -2158,11 +2161,14 @@
 
 void RenderWidgetHostImpl::SetVisualPropertiesFromParentFrame(
     float page_scale_factor,
+    float compositing_scale_factor,
     bool is_pinch_gesture_active,
     const gfx::Size& visible_viewport_size,
     const gfx::Rect& compositor_viewport,
     std::vector<gfx::Rect> root_widget_window_segments) {
   properties_from_parent_local_root_.page_scale_factor = page_scale_factor;
+  properties_from_parent_local_root_.compositing_scale_factor =
+      compositing_scale_factor;
   properties_from_parent_local_root_.is_pinch_gesture_active =
       is_pinch_gesture_active;
   properties_from_parent_local_root_.visible_viewport_size =
@@ -2470,6 +2476,8 @@
              new_visual_properties.capture_sequence_number ||
          old_visual_properties->page_scale_factor !=
              new_visual_properties.page_scale_factor ||
+         old_visual_properties->compositing_scale_factor !=
+             new_visual_properties.compositing_scale_factor ||
          old_visual_properties->is_pinch_gesture_active !=
              new_visual_properties.is_pinch_gesture_active ||
          old_visual_properties->root_widget_window_segments !=
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index aa994c8..0791bef 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -625,6 +625,7 @@
   // propagation down the RenderWidget tree.
   void SetVisualPropertiesFromParentFrame(
       float page_scale_factor,
+      float compositing_scale_factor,
       bool is_pinch_gesture_active,
       const gfx::Size& visible_viewport_size,
       const gfx::Rect& compositor_viewport,
@@ -1166,6 +1167,10 @@
     // The page-scale factor of the main-frame.
     float page_scale_factor = 1.f;
 
+    // This represents the child frame's raster scale factor which takes into
+    // account the transform from child frame space to main frame space.
+    float compositing_scale_factor = 1.f;
+
     // True when the renderer is currently undergoing a pinch-zoom gesture.
     bool is_pinch_gesture_active = false;
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index e6d1890f..6e17229 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -48,6 +48,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "cc/base/math_util.h"
 #include "cc/input/touch_action.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/viz/common/features.h"
@@ -13120,6 +13121,94 @@
   EXPECT_GT(viewport_intersection.width(), 0);
 }
 
+// Test that the compositing scale factor for an out-of-process iframe are set
+// and updated correctly, including accounting for all intermediate transforms.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       CompositingScaleFactorInNestedFrameTest) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_scaled_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* child_b = root->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_b, embedded_test_server()->GetURL(
+                   "b.com", "/frame_tree/page_with_transformed_iframe.html")));
+
+  ASSERT_EQ(1U, child_b->child_count());
+  FrameTreeNode* child_c = child_b->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_c, embedded_test_server()->GetURL(
+                   "c.com", "/frame_tree/page_with_scaled_frame.html")));
+
+  ASSERT_EQ(1U, child_c->child_count());
+  FrameTreeNode* child_d = child_c->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_d, embedded_test_server()->GetURL("d.com", "/simple_page.html")));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C D\n"
+      "   +--Site B ------- proxies for A C D\n"
+      "        +--Site C -- proxies for A B D\n"
+      "             +--Site D -- proxies for A B C\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/\n"
+      "      C = http://c.com/\n"
+      "      D = http://d.com/",
+      DepictFrameTree(root));
+
+  // Wait for b.com's frame to have its compositing scale factor set to 0.5,
+  // which is the scale factor for b.com's iframe element in the main frame.
+  while (true) {
+    auto* rwh_b = child_b->current_frame_host()->GetRenderWidgetHost();
+    base::Optional<blink::VisualProperties> properties =
+        rwh_b->GetLastVisualPropertiesSentToRendererForTesting();
+    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.5f)) {
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Wait for c.com's frame to have its compositing scale factor set to 0.5,
+  // which is the accumulated scale factor of c.com to the main frame obtained
+  // by multiplying the scale factor of c.com's iframe element (1 since
+  // transform is rotation only without scale) with the scale factor of its
+  // parent frame b.com (0.5).
+  while (true) {
+    auto* rwh_c = child_c->current_frame_host()->GetRenderWidgetHost();
+    base::Optional<blink::VisualProperties> properties =
+        rwh_c->GetLastVisualPropertiesSentToRendererForTesting();
+    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.5f)) {
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Wait for d.com's frame to have its compositing scale factor set to 0.25,
+  // which is the accumulated scale factor of d.com to the main frame obtained
+  // by combining the scale factor of d.com's iframe element (0.5) with the
+  // scale factor of its parent d.com (0.5).
+  while (true) {
+    auto* rwh_d = child_d->current_frame_host()->GetRenderWidgetHost();
+    base::Optional<blink::VisualProperties> properties =
+        rwh_d->GetLastVisualPropertiesSentToRendererForTesting();
+    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.25f)) {
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
 // Verify that OOPIF select element popup menu coordinates account for scroll
 // offset in containers embedding frame.
 // TODO(crbug.com/859552): Reenable this.
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index a27d892..7417043f 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -504,6 +504,8 @@
   if (web_frame_) {
     pending_visual_properties_.compositor_viewport =
         web_frame_->GetCompositingRect();
+    pending_visual_properties_.compositing_scale_factor =
+        web_frame_->GetCompositingScaleFactor();
   }
 
   bool synchronized_props_changed =
@@ -524,6 +526,8 @@
           pending_visual_properties_.zoom_level ||
       sent_visual_properties_->page_scale_factor !=
           pending_visual_properties_.page_scale_factor ||
+      sent_visual_properties_->compositing_scale_factor !=
+          pending_visual_properties_.compositing_scale_factor ||
       sent_visual_properties_->is_pinch_gesture_active !=
           pending_visual_properties_.is_pinch_gesture_active ||
       sent_visual_properties_->visible_viewport_size !=
diff --git a/third_party/blink/common/frame/frame_visual_properties_mojom_traits.cc b/third_party/blink/common/frame/frame_visual_properties_mojom_traits.cc
index 4159de0f..c23ddd3 100644
--- a/third_party/blink/common/frame/frame_visual_properties_mojom_traits.cc
+++ b/third_party/blink/common/frame/frame_visual_properties_mojom_traits.cc
@@ -22,12 +22,14 @@
       !data.ReadScreenSpaceRect(&out->screen_space_rect) ||
       !data.ReadLocalFrameSize(&out->local_frame_size) ||
       !data.ReadRootWidgetWindowSegments(&out->root_widget_window_segments) ||
-      !data.ReadLocalSurfaceId(&out->local_surface_id))
+      !data.ReadLocalSurfaceId(&out->local_surface_id) ||
+      data.page_scale_factor() <= 0 || data.compositing_scale_factor() <= 0)
     return false;
   out->auto_resize_enabled = data.auto_resize_enabled();
   out->capture_sequence_number = data.capture_sequence_number();
   out->zoom_level = data.zoom_level();
   out->page_scale_factor = data.page_scale_factor();
+  out->compositing_scale_factor = data.compositing_scale_factor();
   out->is_pinch_gesture_active = data.is_pinch_gesture_active();
   return true;
 }
diff --git a/third_party/blink/common/widget/visual_properties_mojom_traits.cc b/third_party/blink/common/widget/visual_properties_mojom_traits.cc
index b8bea35..c295de9a 100644
--- a/third_party/blink/common/widget/visual_properties_mojom_traits.cc
+++ b/third_party/blink/common/widget/visual_properties_mojom_traits.cc
@@ -23,7 +23,8 @@
           &out->compositor_viewport_pixel_rect) ||
       !data.ReadBrowserControlsParams(&out->browser_controls_params) ||
       !data.ReadLocalSurfaceId(&out->local_surface_id) ||
-      !data.ReadRootWidgetWindowSegments(&out->root_widget_window_segments))
+      !data.ReadRootWidgetWindowSegments(&out->root_widget_window_segments) ||
+      data.page_scale_factor() <= 0 || data.compositing_scale_factor() <= 0)
     return false;
   out->auto_resize_enabled = data.auto_resize_enabled();
   out->scroll_focused_node_into_view = data.scroll_focused_node_into_view();
@@ -32,6 +33,7 @@
   out->capture_sequence_number = data.capture_sequence_number();
   out->zoom_level = data.zoom_level();
   out->page_scale_factor = data.page_scale_factor();
+  out->compositing_scale_factor = data.compositing_scale_factor();
   out->is_pinch_gesture_active = data.is_pinch_gesture_active();
   return true;
 }
diff --git a/third_party/blink/public/common/frame/frame_visual_properties.h b/third_party/blink/public/common/frame/frame_visual_properties.h
index 5e77fee..9e00399de 100644
--- a/third_party/blink/public/common/frame/frame_visual_properties.h
+++ b/third_party/blink/public/common/frame/frame_visual_properties.h
@@ -33,6 +33,7 @@
   uint32_t capture_sequence_number = 0u;
   double zoom_level = 0;
   float page_scale_factor = 1.f;
+  float compositing_scale_factor = 1.f;
   gfx::Size visible_viewport_size;
   gfx::Size min_size_for_auto_resize;
   gfx::Size max_size_for_auto_resize;
diff --git a/third_party/blink/public/common/frame/frame_visual_properties_mojom_traits.h b/third_party/blink/public/common/frame/frame_visual_properties_mojom_traits.h
index b028534..815a7d9 100644
--- a/third_party/blink/public/common/frame/frame_visual_properties_mojom_traits.h
+++ b/third_party/blink/public/common/frame/frame_visual_properties_mojom_traits.h
@@ -38,9 +38,16 @@
   }
 
   static double page_scale_factor(const blink::FrameVisualProperties& r) {
+    DCHECK_GT(r.page_scale_factor, 0);
     return r.page_scale_factor;
   }
 
+  static double compositing_scale_factor(
+      const blink::FrameVisualProperties& r) {
+    DCHECK_GT(r.compositing_scale_factor, 0);
+    return r.compositing_scale_factor;
+  }
+
   static const gfx::Size& visible_viewport_size(
       const blink::FrameVisualProperties& r) {
     return r.visible_viewport_size;
diff --git a/third_party/blink/public/common/widget/visual_properties.h b/third_party/blink/public/common/widget/visual_properties.h
index b31bf07..ea252f8 100644
--- a/third_party/blink/public/common/widget/visual_properties.h
+++ b/third_party/blink/public/common/widget/visual_properties.h
@@ -114,6 +114,10 @@
   // It needs to be shared with subframes.
   float page_scale_factor = 1.f;
 
+  // This represents the child frame's raster scale factor which takes into
+  // account the transform from child frame space to main frame space.
+  float compositing_scale_factor = 1.f;
+
   // The logical segments of the root widget, in widget-relative DIPs. This
   // property is set by the root RenderWidget in the renderer process, then
   // propagated to child local frame roots via RenderFrameProxy/
diff --git a/third_party/blink/public/common/widget/visual_properties_mojom_traits.h b/third_party/blink/public/common/widget/visual_properties_mojom_traits.h
index af3ed2319..589e895 100644
--- a/third_party/blink/public/common/widget/visual_properties_mojom_traits.h
+++ b/third_party/blink/public/common/widget/visual_properties_mojom_traits.h
@@ -79,9 +79,15 @@
   }
 
   static double page_scale_factor(const blink::VisualProperties& r) {
+    DCHECK_GT(r.page_scale_factor, 0);
     return r.page_scale_factor;
   }
 
+  static double compositing_scale_factor(const blink::VisualProperties& r) {
+    DCHECK_GT(r.compositing_scale_factor, 0);
+    return r.compositing_scale_factor;
+  }
+
   static const std::vector<gfx::Rect>& root_widget_window_segments(
       const blink::VisualProperties& r) {
     return r.root_widget_window_segments;
diff --git a/third_party/blink/public/mojom/frame/frame_visual_properties.mojom b/third_party/blink/public/mojom/frame/frame_visual_properties.mojom
index 4e96c94f..50a2802 100644
--- a/third_party/blink/public/mojom/frame/frame_visual_properties.mojom
+++ b/third_party/blink/public/mojom/frame/frame_visual_properties.mojom
@@ -35,6 +35,10 @@
   // It needs to be shared with subframes.
   float page_scale_factor = 1;
 
+  // This represents the child frame's raster scale factor which takes into
+  // account the transform from child frame space to main frame space.
+  float compositing_scale_factor = 1;
+
   // The size of the area of the widget that is visible to the user, in DIPs.
   // The visible area may be empty if the visible area does not intersect with
   // the widget, for example in the case of a child frame that is entirely
diff --git a/third_party/blink/public/mojom/widget/visual_properties.mojom b/third_party/blink/public/mojom/widget/visual_properties.mojom
index df46740..cc7c2b7 100644
--- a/third_party/blink/public/mojom/widget/visual_properties.mojom
+++ b/third_party/blink/public/mojom/widget/visual_properties.mojom
@@ -79,6 +79,10 @@
   // It needs to be shared with subframes.
   float page_scale_factor = 1;
 
+  // This represents the child frame's raster scale factor which takes into
+  // account the transform from child frame space to main frame space.
+  float compositing_scale_factor = 1;
+
   // The logical segments of the root widget, in widget-relative DIPs. This
   // property is set by the root RenderWidget in the renderer process, then
   // propagated to child local frame roots via RenderFrameProxy/
diff --git a/third_party/blink/public/web/web_remote_frame.h b/third_party/blink/public/web/web_remote_frame.h
index 494bb0b..27f81eb 100644
--- a/third_party/blink/public/web/web_remote_frame.h
+++ b/third_party/blink/public/web/web_remote_frame.h
@@ -158,8 +158,13 @@
 
   virtual void SetHadStickyUserActivationBeforeNavigation(bool value) = 0;
 
+  // Return the interest rect for compositing in the frame's space.
   virtual WebRect GetCompositingRect() = 0;
 
+  // Returns the ideal raster scale factor for the OOPIF's compositor so that it
+  // doesn't raster at a higher scale than it needs to.
+  virtual float GetCompositingScaleFactor() = 0;
+
   // Unique name is an opaque identifier for maintaining association with
   // session restore state for this frame.
   virtual WebString UniqueName() const = 0;
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index ede2eb1..e58e560 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -871,6 +871,14 @@
   widget_base_->SetVisibleViewportSizeInDIPs(
       visual_properties.visible_viewport_size);
 
+  // TODO(crbug.com/1155388): Popups are a single "global" object that don't
+  // inherit the scale factor of the frame containing the corresponding element
+  // so compositing_scale_factor is always 1 and has no effect.
+  float combined_scale_factor = visual_properties.page_scale_factor *
+                                visual_properties.compositing_scale_factor;
+  widget_base_->LayerTreeHost()->SetExternalPageScaleFactor(
+      combined_scale_factor, visual_properties.is_pinch_gesture_active);
+
   Resize(widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size));
 }
 
diff --git a/third_party/blink/renderer/core/frame/DEPS b/third_party/blink/renderer/core/frame/DEPS
index cae0b42..b822d76 100644
--- a/third_party/blink/renderer/core/frame/DEPS
+++ b/third_party/blink/renderer/core/frame/DEPS
@@ -26,6 +26,7 @@
     "+components/paint_preview/common/paint_preview_tracker.h",
   ],
   "remote_frame_view\.cc": [
+    "+cc/base/math_util.h",
     "+components/paint_preview/common/paint_preview_tracker.h",
     "+printing/buildflags/buildflags.h",
     "+printing/metafile_skia.h",
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 0fde7d3..22cbab5 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1104,6 +1104,9 @@
 void LocalFrameView::RunPostLifecycleSteps() {
   AllowThrottlingScope allow_throttling(*this);
   RunIntersectionObserverSteps();
+  ForAllRemoteFrameViews([](RemoteFrameView& frame_view) {
+    frame_view.UpdateCompositingScaleFactor();
+  });
 }
 
 void LocalFrameView::RunIntersectionObserverSteps() {
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index dab371e..e4ff2cf 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/frame/remote_frame_view.h"
 
+#include "cc/base/math_util.h"
 #include "components/paint_preview/common/paint_preview_tracker.h"
 #include "printing/buildflags/buildflags.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_element_type.mojom-blink.h"
@@ -21,6 +22,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
+#include "third_party/blink/renderer/platform/widget/frame_widget.h"
 
 #if BUILDFLAG(ENABLE_PRINTING)
 // nogncheck because dependency on //printing is conditional upon
@@ -178,6 +180,49 @@
     needs_frame_rect_propagation_ = true;
 }
 
+void RemoteFrameView::UpdateCompositingScaleFactor() {
+  float previous_scale_factor = compositing_scale_factor_;
+
+  LocalFrameView* local_root_view = ParentLocalRootFrameView();
+  LayoutEmbeddedContent* owner_layout_object =
+      remote_frame_->OwnerLayoutObject();
+  if (!local_root_view || !owner_layout_object)
+    return;
+
+  TransformState local_root_transform_state(
+      TransformState::kApplyTransformDirection);
+  local_root_transform_state.Move(
+      owner_layout_object->PhysicalContentBoxOffset());
+  owner_layout_object->MapLocalToAncestor(nullptr, local_root_transform_state,
+                                          kTraverseDocumentBoundaries);
+
+  float frame_to_local_root_scale_factor = 1.0f;
+  gfx::Transform local_root_transform = TransformationMatrix::ToTransform(
+      local_root_transform_state.AccumulatedTransform());
+  if (local_root_transform.HasPerspective()) {
+    frame_to_local_root_scale_factor =
+        cc::MathUtil::ComputeApproximateMaxScale(local_root_transform);
+  } else {
+    gfx::Vector2dF scale_components =
+        cc::MathUtil::ComputeTransform2dScaleComponents(
+            local_root_transform,
+            /*fallback_scale=*/1.0f);
+    frame_to_local_root_scale_factor =
+        std::max(scale_components.x(), scale_components.y());
+  }
+
+  // The compositing scale factor is calculated by multiplying the scale factor
+  // from the local root to main frame with the scale factor between child frame
+  // and local root.
+  FrameWidget* widget = local_root_view->GetFrame().GetWidgetForLocalRoot();
+  compositing_scale_factor_ =
+      widget->GetCompositingScaleFactor() * frame_to_local_root_scale_factor;
+  DCHECK_GE(compositing_scale_factor_, 0.0f);
+
+  if (compositing_scale_factor_ != previous_scale_factor)
+    remote_frame_->Client()->SynchronizeVisualProperties();
+}
+
 void RemoteFrameView::Dispose() {
   HTMLFrameOwnerElement* owner_element = remote_frame_->DeprecatedLocalOwner();
   // ownerElement can be null during frame swaps, because the
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index b8da01a1..a97ceb4 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -73,6 +73,9 @@
   void UpdateCompositingRect();
   IntRect GetCompositingRect() const { return compositing_rect_; }
 
+  void UpdateCompositingScaleFactor();
+  float GetCompositingScaleFactor() const { return compositing_scale_factor_; }
+
   uint32_t Print(const IntRect&, cc::PaintCanvas*) const;
   uint32_t CapturePaintPreview(const IntRect&, cc::PaintCanvas*) const;
 
@@ -99,6 +102,7 @@
   Member<RemoteFrame> remote_frame_;
   mojom::blink::ViewportIntersectionState last_intersection_state_;
   IntRect compositing_rect_;
+  float compositing_scale_factor_ = 1.0f;
 
   IntrinsicSizingInfo intrinsic_sizing_info_;
   bool has_intrinsic_sizing_info_ = false;
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc
index 1965604..fa6b82c 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -13727,4 +13727,80 @@
   web_view_helper.Reset();
 }
 
+TEST_F(WebFrameTest, RemoteFrameCompositingScaleFactor) {
+  frame_test_helpers::WebViewHelper web_view_helper;
+  web_view_helper.Initialize();
+
+  WebViewImpl* web_view = web_view_helper.GetWebView();
+  web_view->Resize(gfx::Size(800, 800));
+  InitializeWithHTML(*web_view->MainFrameImpl()->GetFrame(), R"HTML(
+<!DOCTYPE html>
+<style>
+  iframe {
+    width: 1600;
+    height: 1200;
+    transform-origin: top left;
+    transform: scale(0.5);
+    border: none;
+  }
+</style>
+<iframe></iframe>
+  )HTML");
+
+  WebRemoteFrameImpl* remote_frame = frame_test_helpers::CreateRemote();
+  web_view_helper.LocalMainFrame()->FirstChild()->Swap(remote_frame);
+  remote_frame->SetReplicatedOrigin(
+      WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
+
+  // Call directly into frame view since we need to RunPostLifecycleSteps() too.
+  web_view->MainFrameImpl()
+      ->GetFrame()
+      ->View()
+      ->UpdateAllLifecyclePhasesForTest();
+  RunPendingTasks();
+
+  // The compositing scale factor tells the OOPIF compositor to raster at a
+  // lower scale since the frame is scaled down in the parent webview.
+  EXPECT_EQ(remote_frame->GetCompositingRect(), WebRect(0, 0, 1600, 1200));
+  EXPECT_EQ(remote_frame->GetCompositingScaleFactor(), 0.5f);
+}
+
+TEST_F(WebFrameTest, RotatedRemoteFrameCompositingScaleFactor) {
+  frame_test_helpers::WebViewHelper web_view_helper;
+  web_view_helper.Initialize();
+
+  WebViewImpl* web_view = web_view_helper.GetWebView();
+  web_view->Resize(gfx::Size(800, 800));
+  InitializeWithHTML(*web_view->MainFrameImpl()->GetFrame(), R"HTML(
+<!DOCTYPE html>
+<style>
+  iframe {
+    width: 1600;
+    height: 1200;
+    transform-origin: top left;
+    transform: scale(0.5) rotate(45deg);
+    border: none;
+  }
+</style>
+<iframe></iframe>
+  )HTML");
+
+  WebRemoteFrameImpl* remote_frame = frame_test_helpers::CreateRemote();
+  web_view_helper.LocalMainFrame()->FirstChild()->Swap(remote_frame);
+  remote_frame->SetReplicatedOrigin(
+      WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
+
+  // Call directly into frame view since we need to RunPostLifecycleSteps() too.
+  web_view->MainFrameImpl()
+      ->GetFrame()
+      ->View()
+      ->UpdateAllLifecyclePhasesForTest();
+  RunPendingTasks();
+
+  // The compositing scale factor tells the OOPIF compositor to raster at a
+  // lower scale since the frame is scaled down in the parent webview.
+  EXPECT_EQ(remote_frame->GetCompositingRect(), WebRect(0, 0, 1600, 1200));
+  EXPECT_EQ(remote_frame->GetCompositingScaleFactor(), 0.5f);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 9bc6047..de64be40 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1419,16 +1419,20 @@
   // that it comes from the top level widget's page scale.
   if (!ForTopMostMainFrame()) {
     // The main frame controls the page scale factor, from blink. For other
-    // frame widgets, the page scale is received from its parent as part of
-    // the visual properties here. While blink doesn't need to know this
-    // page scale factor outside the main frame, the compositor does in
-    // order to produce its output at the correct scale.
+    // frame widgets, the page scale from pinch zoom and compositing scale is
+    // received from its parent as part of the visual properties here. While
+    // blink doesn't need to know this page scale factor outside the main frame,
+    // the compositor does in order to produce its output at the correct scale.
+    float combined_scale_factor = visual_properties.page_scale_factor *
+                                  visual_properties.compositing_scale_factor;
     widget_base_->LayerTreeHost()->SetExternalPageScaleFactor(
-        visual_properties.page_scale_factor,
-        visual_properties.is_pinch_gesture_active);
+        combined_scale_factor, visual_properties.is_pinch_gesture_active);
 
     NotifyPageScaleFactorChanged(visual_properties.page_scale_factor,
                                  visual_properties.is_pinch_gesture_active);
+
+    NotifyCompositingScaleFactorChanged(
+        visual_properties.compositing_scale_factor);
   } else {
     // Ensure the external scale factor in top-level widgets is reset as it may
     // be leftover from when a widget was nested and was promoted to top level
@@ -3626,6 +3630,23 @@
   Resize(widget_base_->DIPsToCeiledBlinkSpace(widget_size_in_dips));
 }
 
+float WebFrameWidgetImpl::GetCompositingScaleFactor() {
+  return compositing_scale_factor_;
+}
+
+void WebFrameWidgetImpl::NotifyCompositingScaleFactorChanged(
+    float compositing_scale_factor) {
+  compositing_scale_factor_ = compositing_scale_factor;
+
+  // Update the scale factor for remote frames which in turn depends on the
+  // compositing scale factor set in the widget.
+  ForEachRemoteFrameControlledByWidget(
+      WTF::BindRepeating([](RemoteFrame* remote_frame) {
+        if (remote_frame->View())
+          remote_frame->View()->UpdateCompositingScaleFactor();
+      }));
+}
+
 void WebFrameWidgetImpl::NotifyPageScaleFactorChanged(
     float page_scale_factor,
     bool is_pinch_gesture_active) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index 7eaf9854..a00cd0ed 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -273,6 +273,7 @@
                               const gfx::Range& replacement_range,
                               int relative_cursor_pos) override;
   void ImeFinishComposingTextForPlugin(bool keep_selection) override;
+  float GetCompositingScaleFactor() override;
 
   // WebFrameWidget overrides.
   void InitializeNonCompositing(WebNonCompositedWidgetClient* client) override;
@@ -803,6 +804,10 @@
   // The fullscreen granted status from the most recent VisualProperties update.
   bool IsFullscreenGranted();
 
+  // Set the compositing scale factor for this widget and notify remote frames
+  // to update their compositing scale factor.
+  void NotifyCompositingScaleFactorChanged(float compositing_scale_factor);
+
   void NotifyPageScaleFactorChanged(float page_scale_factor,
                                     bool is_pinch_gesture_active);
 
@@ -847,6 +852,10 @@
   // complicated inheritance structures.
   std::unique_ptr<WidgetBase> widget_base_;
 
+  // Compositing scale factor for all frames attached to this widget sent from
+  // the remote parent frame.
+  float compositing_scale_factor_ = 1.f;
+
   // The last seen page scale state, which comes from the main frame if we're
   // in a child frame. This state is propagated through the RenderWidget tree
   // passed to any new child RenderWidget.
diff --git a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
index 68fc6e271..334983d 100644
--- a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc
@@ -362,6 +362,10 @@
   return GetFrame()->View()->GetCompositingRect();
 }
 
+float WebRemoteFrameImpl::GetCompositingScaleFactor() {
+  return GetFrame()->View()->GetCompositingScaleFactor();
+}
+
 WebString WebRemoteFrameImpl::UniqueName() const {
   return GetFrame()->UniqueName();
 }
diff --git a/third_party/blink/renderer/core/frame/web_remote_frame_impl.h b/third_party/blink/renderer/core/frame/web_remote_frame_impl.h
index 7029d1c..9fe0cead 100644
--- a/third_party/blink/renderer/core/frame/web_remote_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_remote_frame_impl.h
@@ -114,6 +114,7 @@
   void SetHadStickyUserActivationBeforeNavigation(bool value) override;
   v8::Local<v8::Object> GlobalProxy() const override;
   WebRect GetCompositingRect() override;
+  float GetCompositingScaleFactor() override;
   WebString UniqueName() const override;
   void InitializeCoreFrame(Page&,
                            FrameOwner*,
diff --git a/third_party/blink/renderer/platform/widget/frame_widget.h b/third_party/blink/renderer/platform/widget/frame_widget.h
index be0bf2a..742ab741 100644
--- a/third_party/blink/renderer/platform/widget/frame_widget.h
+++ b/third_party/blink/renderer/platform/widget/frame_widget.h
@@ -253,6 +253,10 @@
   // Returns the FrameSinkId for this widget which is used for identifying
   // frames submitted from the compositor.
   virtual const viz::FrameSinkId& GetFrameSinkId() = 0;
+
+  // Returns the raster scale factor for the local root frame associated with
+  // this widget, taking into account its transform to main frame space.
+  virtual float GetCompositingScaleFactor() = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc
index 9ff5cb8..20e67bff 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.cc
+++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -397,13 +397,6 @@
       visual_properties.screen_info.device_scale_factor));
 
   client_->UpdateVisualProperties(visual_properties);
-
-  // FrameWidgets have custom code for external page scale factor.
-  if (!client_->FrameWidget()) {
-    LayerTreeHost()->SetExternalPageScaleFactor(
-        visual_properties.page_scale_factor,
-        visual_properties.is_pinch_gesture_active);
-  }
 }
 
 void WidgetBase::UpdateScreenRects(const gfx::Rect& widget_screen_rect,
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index ec65944..b1eab55 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -270,6 +270,7 @@
             'gfx::Vector2dF',
 
             # Chromium geometry operations.
+            'cc::MathUtil',
             'gfx::ToFlooredPoint',
 
             # Range type.