diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index a4cc42e1ad16..08e2c200b3a8 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc @@ -12247,22 +12247,21 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, // case, the raster area of the large iframe should be restricted to // approximately the area of the smaller iframe in which it is embedded. int view_height = child->current_frame_host() - ->GetRenderWidgetHost() - ->GetView() - ->GetViewBounds() - .height() * - scale_factor + - 5; + ->GetRenderWidgetHost() + ->GetView() + ->GetViewBounds() + .height() * + scale_factor; int expected_height = view_height * 13 / 10; int expected_offset = (5000 * scale_factor) - (expected_height - 185 * scale_factor) / 2; // Allow a small amount for rounding differences from applying page and // device scale factors at different times. - EXPECT_GE(compositing_rect.height(), expected_height - 2); - EXPECT_LE(compositing_rect.height(), expected_height + 2); - EXPECT_GE(compositing_rect.y(), expected_offset - 2); - EXPECT_LE(compositing_rect.y(), expected_offset + 2); + EXPECT_GE(compositing_rect.height(), expected_height - 3); + EXPECT_LE(compositing_rect.height(), expected_height + 3); + EXPECT_GE(compositing_rect.y(), expected_offset - 3); + EXPECT_LE(compositing_rect.y(), expected_offset + 3); } // Verify that OOPIF select element popup menu coordinates account for scroll 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 f60cd4b1bf2f..e039f3cc7b21 100644 --- a/third_party/blink/renderer/core/frame/remote_frame_view.cc +++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc @@ -65,7 +65,6 @@ void RemoteFrameView::AttachToLayout() { SetParentVisible(true); UpdateVisibility(true); - SetupRenderThrottling(); subtree_throttled_ = ParentFrameView()->CanThrottleRendering(); FrameRectsChanged(); @@ -90,68 +89,54 @@ void RemoteFrameView::UpdateViewportIntersectionsForSubtree( return; } - LayoutEmbeddedContent* owner = remote_frame_->OwnerLayoutObject(); + // This should only run in child frames. + HTMLFrameOwnerElement* owner_element = remote_frame_->DeprecatedLocalOwner(); + DCHECK(owner_element); + LayoutEmbeddedContent* owner = owner_element->GetLayoutEmbeddedContent(); if (!owner) return; - - LocalFrameView* local_root_view = ParentLocalRootFrameView(); - if (!local_root_view) - return; - IntRect viewport_intersection; bool occluded_or_obscured = false; - DocumentLifecycle::LifecycleState parent_state = - owner->GetDocument().Lifecycle().GetState(); // If the parent LocalFrameView is throttled and out-of-date, then we can't // get any useful information. + DocumentLifecycle::LifecycleState parent_state = + owner_element->GetDocument().Lifecycle().GetState(); if (parent_state >= DocumentLifecycle::kLayoutClean) { - // Start with rect in remote frame's coordinate space. Then - // mapToVisualRectInAncestorSpace will move it to the local root's - // coordinate space and account for any clip from containing elements such - // as a scrollable div. Passing nullptr as an argument to - // mapToVisualRectInAncestorSpace causes it to be clipped to the viewport, - // even if there are RemoteFrame ancestors in the frame tree. - LayoutRect rect(0, 0, frame_rect_.Width(), frame_rect_.Height()); - rect.Move(owner->PhysicalContentBoxOffset()); - if (owner->MapToVisualRectInAncestorSpace(nullptr, rect, - kUseGeometryMapper)) { - IntRect root_visible_rect(IntPoint(), local_root_view->Size()); - IntRect intersected_rect = EnclosingIntRect(rect); - intersected_rect.Intersect(root_visible_rect); - - // Translate the intersection rect from the root frame's coordinate space - // to the remote frame's coordinate space. - FloatRect viewport_intersection_float = - remote_frame_->OwnerLayoutObject() - ->AncestorToLocalQuad( - local_root_view->GetLayoutView(), FloatQuad(intersected_rect), - kTraverseDocumentBoundaries | kUseTransforms) - .BoundingBox(); - viewport_intersection_float.Move( - -remote_frame_->OwnerLayoutObject()->PhysicalContentBoxOffset()); - viewport_intersection = EnclosingIntRect(viewport_intersection_float); + unsigned geometry_flags = + IntersectionGeometry::kShouldUseReplacedContentRect; + if (parent_state >= DocumentLifecycle::kPrePaintClean && + RuntimeEnabledFeatures::IntersectionObserverV2Enabled()) { + geometry_flags |= IntersectionGeometry::kShouldComputeVisibility; } - } - - if (parent_state >= DocumentLifecycle::kPrePaintClean && - RuntimeEnabledFeatures::IntersectionObserverV2Enabled()) { - // TODO(layout-dev): As an optimization, we should only check for - // occlusion and effects if the remote frame needs it, i.e., if it has at - // least one active IntersectionObserver with trackVisibility:true. - if (owner->GetDocument() - .GetFrame() - ->LocalFrameRoot() - .MayBeOccludedOrObscuredByRemoteAncestor() || - owner->HasDistortingVisualEffects()) { - occluded_or_obscured = true; + IntersectionGeometry geometry(nullptr, *owner_element, {}, + {IntersectionObserver::kMinimumThreshold}, + geometry_flags); + // geometry.IntersectionRect() is in absolute coordinates of the owning + // document. Map it down to absolute coordinates in the child document. + LayoutRect intersection_rect = LayoutRect( + owner + ->AncestorToLocalQuad( + nullptr, FloatQuad(FloatRect(geometry.IntersectionRect())), + kUseTransforms) + .BoundingBox()); + // Map from the box coordinates of the owner to the inner frame. + intersection_rect.Move(-owner->PhysicalContentBoxOffset()); + // Don't let EnclosingIntRect turn an empty rect into a non-empty one. + if (intersection_rect.IsEmpty()) { + viewport_intersection = + IntRect(FlooredIntPoint(intersection_rect.Location()), IntSize()); } else { - HitTestResult result(owner->HitTestForOcclusion()); - occluded_or_obscured = - result.InnerNode() && result.InnerNode() != owner->GetNode(); + viewport_intersection = EnclosingIntRect(intersection_rect); } + occluded_or_obscured = !geometry.IsVisible(); } + // TODO(szager): There are some redundant IPC's here; clean them up. + bool is_visible_for_throttling = !viewport_intersection.IsEmpty(); + UpdateVisibility(is_visible_for_throttling); + UpdateRenderThrottlingStatus(!is_visible_for_throttling, subtree_throttled_); + if (viewport_intersection == last_viewport_intersection_ && occluded_or_obscured == last_occluded_or_obscured_) { return; @@ -341,29 +326,6 @@ void RemoteFrameView::UpdateVisibility(bool scroll_visible) { remote_frame_->Client()->VisibilityChanged(visibility); } -void RemoteFrameView::OnViewportIntersectionChanged( - const HeapVector>& entries) { - bool is_visible = entries.back()->intersectionRatio() > 0; - UpdateVisibility(is_visible); - UpdateRenderThrottlingStatus(!is_visible, subtree_throttled_); -} - -void RemoteFrameView::SetupRenderThrottling() { - if (visibility_observer_) - return; - - Element* target_element = GetFrame().DeprecatedLocalOwner(); - if (!target_element) - return; - - visibility_observer_ = IntersectionObserver::Create( - {}, {IntersectionObserver::kMinimumThreshold}, - &target_element->GetDocument(), - WTF::BindRepeating(&RemoteFrameView::OnViewportIntersectionChanged, - WrapWeakPersistent(this))); - visibility_observer_->observe(target_element); -} - void RemoteFrameView::UpdateRenderThrottlingStatus(bool hidden, bool subtree_throttled) { TRACE_EVENT0("blink", "RemoteFrameView::UpdateRenderThrottlingStatus"); 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 ae5db5af0d57..c9980f108e42 100644 --- a/third_party/blink/renderer/core/frame/remote_frame_view.h +++ b/third_party/blink/renderer/core/frame/remote_frame_view.h @@ -88,7 +88,6 @@ class RemoteFrameView final : public GarbageCollectedFinalized, const HeapVector>& entries); void UpdateRenderThrottlingStatus(bool hidden, bool subtree_throttled); bool CanThrottleRendering() const; - void SetupRenderThrottling(); void UpdateVisibility(bool scroll_visible); // The properties and handling of the cycle between RemoteFrame diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc index 6cc4e04f65b5..1bc8013e585b 100644 --- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc +++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc @@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h" #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h" #include "third_party/blink/renderer/core/layout/layout_box.h" +#include "third_party/blink/renderer/core/layout/layout_embedded_content.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/page/page.h" @@ -74,8 +75,7 @@ bool ComputeIsVisible(LayoutObject* target, const LayoutRect& rect) { return false; // TODO(layout-dev): This should hit-test the intersection rect, not the // target rect; it's not helpful to know that the portion of the target that - // is clipped is also occluded. To do that, the intersection rect must be - // mapped down to the local space of the target element. + // is clipped is also occluded. HitTestResult result(target->HitTestForOcclusion(rect)); return (!result.InnerNode() || result.InnerNode() == target->GetNode()); } @@ -83,7 +83,9 @@ bool ComputeIsVisible(LayoutObject* target, const LayoutRect& rect) { static const unsigned kConstructorFlagsMask = IntersectionGeometry::kShouldReportRootBounds | IntersectionGeometry::kShouldComputeVisibility | - IntersectionGeometry::kShouldTrackFractionOfRoot; + IntersectionGeometry::kShouldTrackFractionOfRoot | + IntersectionGeometry::kShouldUseReplacedContentRect | + IntersectionGeometry::kShouldConvertToCSSPixels; } // namespace @@ -127,20 +129,21 @@ void IntersectionGeometry::ComputeGeometry(Element* root_element, DCHECK(!target_element.GetDocument().View()->NeedsLayout()); - LayoutRect target_rect = InitializeTargetRect(target); - LayoutRect intersection_rect = target_rect; - LayoutRect root_rect = InitializeRootRect(root, root_margin); - bool does_intersect = ClipToRoot(root, target, root_rect, intersection_rect); - MapRectUpToDocument(target_rect, *target); + target_rect_ = InitializeTargetRect(target); + intersection_rect_ = target_rect_; + root_rect_ = InitializeRootRect(root, root_margin); + bool does_intersect = + ClipToRoot(root, target, root_rect_, intersection_rect_); + MapRectUpToDocument(target_rect_, *target); if (does_intersect) { if (RootIsImplicit()) - MapRectDownToDocument(intersection_rect, target->GetDocument()); + MapRectDownToDocument(intersection_rect_, target->GetDocument()); else - MapRectUpToDocument(intersection_rect, *root); + MapRectUpToDocument(intersection_rect_, *root); } else { - intersection_rect = LayoutRect(); + intersection_rect_ = LayoutRect(); } - MapRectUpToDocument(root_rect, *root); + MapRectUpToDocument(root_rect_, *root); // Some corner cases for threshold index: // - If target rect is zero area, because it has zero width and/or zero @@ -159,11 +162,11 @@ void IntersectionGeometry::ComputeGeometry(Element* root_element, if (does_intersect) { const LayoutRect comparison_rect = - ShouldTrackFractionOfRoot() ? root_rect : target_rect; + ShouldTrackFractionOfRoot() ? root_rect_ : target_rect_; if (comparison_rect.IsEmpty()) { intersection_ratio_ = 1; } else { - const LayoutSize& intersection_size = intersection_rect.Size(); + const LayoutSize& intersection_size = intersection_rect_.Size(); const float intersection_area = intersection_size.Width().ToFloat() * intersection_size.Height().ToFloat(); const LayoutSize& comparison_size = comparison_rect.Size(); @@ -178,22 +181,27 @@ void IntersectionGeometry::ComputeGeometry(Element* root_element, threshold_index_ = 0; } if (IsIntersecting() && ShouldComputeVisibility() && - ComputeIsVisible(target, target_rect)) + ComputeIsVisible(target, target_rect_)) flags_ |= kIsVisible; - // Convert to un-zoomed CSS pixels - FloatRect target_float_rect(target_rect); - AdjustForAbsoluteZoom::AdjustFloatRect(target_float_rect, *target); - target_rect_ = LayoutRect(target_float_rect); - FloatRect intersection_float_rect(intersection_rect); - AdjustForAbsoluteZoom::AdjustFloatRect(intersection_float_rect, *target); - intersection_rect_ = LayoutRect(intersection_float_rect); - FloatRect root_float_rect(root_rect); - AdjustForAbsoluteZoom::AdjustFloatRect(root_float_rect, *root); - root_rect_ = LayoutRect(root_float_rect); + if (flags_ & kShouldConvertToCSSPixels) { + FloatRect target_float_rect(target_rect_); + AdjustForAbsoluteZoom::AdjustFloatRect(target_float_rect, *target); + target_rect_ = LayoutRect(target_float_rect); + FloatRect intersection_float_rect(intersection_rect_); + AdjustForAbsoluteZoom::AdjustFloatRect(intersection_float_rect, *target); + intersection_rect_ = LayoutRect(intersection_float_rect); + FloatRect root_float_rect(root_rect_); + AdjustForAbsoluteZoom::AdjustFloatRect(root_float_rect, *root); + root_rect_ = LayoutRect(root_float_rect); + } } LayoutRect IntersectionGeometry::InitializeTargetRect(LayoutObject* target) { + if ((flags_ & kShouldUseReplacedContentRect) && + target->IsLayoutEmbeddedContent()) { + return ToLayoutEmbeddedContent(target)->ReplacedContentRect(); + } if (target->IsBox()) return LayoutRect(ToLayoutBoxModelObject(target)->BorderBoundingBox()); if (target->IsLayoutInline()) diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h index 5296d0b193a7..4a06e107e770 100644 --- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h +++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h @@ -31,10 +31,12 @@ class CORE_EXPORT IntersectionGeometry { kShouldReportRootBounds = 1 << 0, kShouldComputeVisibility = 1 << 1, kShouldTrackFractionOfRoot = 1 << 2, + kShouldUseReplacedContentRect = 1 << 3, + kShouldConvertToCSSPixels = 1 << 4, // These flags will be computed - kRootIsImplicit = 1 << 3, - kIsVisible = 1 << 4 + kRootIsImplicit = 1 << 5, + kIsVisible = 1 << 6 }; IntersectionGeometry(Element* root, @@ -42,6 +44,7 @@ class CORE_EXPORT IntersectionGeometry { const Vector& root_margin, const Vector& thresholds, unsigned flags); + IntersectionGeometry(const IntersectionGeometry&) = default; ~IntersectionGeometry(); @@ -55,7 +58,6 @@ class CORE_EXPORT IntersectionGeometry { return flags_ & kShouldTrackFractionOfRoot; } - // These are all in CSS pixels LayoutRect TargetRect() const { return target_rect_; } LayoutRect IntersectionRect() const { return intersection_rect_; } LayoutRect RootRect() const { return root_rect_; } diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc index 3e0a98040bc8..68adefc19b38 100644 --- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc +++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc @@ -60,7 +60,7 @@ void IntersectionObservation::Compute(unsigned flags) { bool report_root_bounds = observer_->AlwaysReportRootBounds() || (flags & kReportImplicitRootBounds) || !observer_->RootIsImplicit(); - unsigned geometry_flags = 0; + unsigned geometry_flags = IntersectionGeometry::kShouldConvertToCSSPixels; if (report_root_bounds) geometry_flags |= IntersectionGeometry::kShouldReportRootBounds; if (Observer()->trackVisibility())