Skip to content

Commit 3b6cf30

Browse files
Get rid of redundant intersection computation
Currently, we compute the intersection of a frame with the top-level viewport in two places: once to control throttling of off-screen iframes, and again to propagate viewport intersection information to OOPIF's, to use for running the IntersectionObserver algorithm. With this CL, the viewport intersection is computed once for both consumers. Cherry-picked from: https://chromium-review.googlesource.com/c/chromium/src/+/1504503 BUG=926539 [email protected] Change-Id: Ia7b964a59647d5efb05279f28652e07031181872 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1518715 Reviewed-by: Stefan Zager <[email protected]> Cr-Commit-Position: refs/branch-heads/3729@{#54} Cr-Branched-From: d4a8972-refs/heads/master@{#638880}
1 parent b760288 commit 3b6cf30

File tree

6 files changed

+83
-113
lines changed

6 files changed

+83
-113
lines changed

content/browser/site_per_process_browsertest.cc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12247,22 +12247,21 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
1224712247
// case, the raster area of the large iframe should be restricted to
1224812248
// approximately the area of the smaller iframe in which it is embedded.
1224912249
int view_height = child->current_frame_host()
12250-
->GetRenderWidgetHost()
12251-
->GetView()
12252-
->GetViewBounds()
12253-
.height() *
12254-
scale_factor +
12255-
5;
12250+
->GetRenderWidgetHost()
12251+
->GetView()
12252+
->GetViewBounds()
12253+
.height() *
12254+
scale_factor;
1225612255
int expected_height = view_height * 13 / 10;
1225712256
int expected_offset =
1225812257
(5000 * scale_factor) - (expected_height - 185 * scale_factor) / 2;
1225912258

1226012259
// Allow a small amount for rounding differences from applying page and
1226112260
// device scale factors at different times.
12262-
EXPECT_GE(compositing_rect.height(), expected_height - 2);
12263-
EXPECT_LE(compositing_rect.height(), expected_height + 2);
12264-
EXPECT_GE(compositing_rect.y(), expected_offset - 2);
12265-
EXPECT_LE(compositing_rect.y(), expected_offset + 2);
12261+
EXPECT_GE(compositing_rect.height(), expected_height - 3);
12262+
EXPECT_LE(compositing_rect.height(), expected_height + 3);
12263+
EXPECT_GE(compositing_rect.y(), expected_offset - 3);
12264+
EXPECT_LE(compositing_rect.y(), expected_offset + 3);
1226612265
}
1226712266

1226812267
// Verify that OOPIF select element popup menu coordinates account for scroll

third_party/blink/renderer/core/frame/remote_frame_view.cc

Lines changed: 35 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ void RemoteFrameView::AttachToLayout() {
6565
SetParentVisible(true);
6666
UpdateVisibility(true);
6767

68-
SetupRenderThrottling();
6968
subtree_throttled_ = ParentFrameView()->CanThrottleRendering();
7069

7170
FrameRectsChanged();
@@ -90,68 +89,54 @@ void RemoteFrameView::UpdateViewportIntersectionsForSubtree(
9089
return;
9190
}
9291

93-
LayoutEmbeddedContent* owner = remote_frame_->OwnerLayoutObject();
92+
// This should only run in child frames.
93+
HTMLFrameOwnerElement* owner_element = remote_frame_->DeprecatedLocalOwner();
94+
DCHECK(owner_element);
95+
LayoutEmbeddedContent* owner = owner_element->GetLayoutEmbeddedContent();
9496
if (!owner)
9597
return;
96-
97-
LocalFrameView* local_root_view = ParentLocalRootFrameView();
98-
if (!local_root_view)
99-
return;
100-
10198
IntRect viewport_intersection;
10299
bool occluded_or_obscured = false;
103-
DocumentLifecycle::LifecycleState parent_state =
104-
owner->GetDocument().Lifecycle().GetState();
105100

106101
// If the parent LocalFrameView is throttled and out-of-date, then we can't
107102
// get any useful information.
103+
DocumentLifecycle::LifecycleState parent_state =
104+
owner_element->GetDocument().Lifecycle().GetState();
108105
if (parent_state >= DocumentLifecycle::kLayoutClean) {
109-
// Start with rect in remote frame's coordinate space. Then
110-
// mapToVisualRectInAncestorSpace will move it to the local root's
111-
// coordinate space and account for any clip from containing elements such
112-
// as a scrollable div. Passing nullptr as an argument to
113-
// mapToVisualRectInAncestorSpace causes it to be clipped to the viewport,
114-
// even if there are RemoteFrame ancestors in the frame tree.
115-
LayoutRect rect(0, 0, frame_rect_.Width(), frame_rect_.Height());
116-
rect.Move(owner->PhysicalContentBoxOffset());
117-
if (owner->MapToVisualRectInAncestorSpace(nullptr, rect,
118-
kUseGeometryMapper)) {
119-
IntRect root_visible_rect(IntPoint(), local_root_view->Size());
120-
IntRect intersected_rect = EnclosingIntRect(rect);
121-
intersected_rect.Intersect(root_visible_rect);
122-
123-
// Translate the intersection rect from the root frame's coordinate space
124-
// to the remote frame's coordinate space.
125-
FloatRect viewport_intersection_float =
126-
remote_frame_->OwnerLayoutObject()
127-
->AncestorToLocalQuad(
128-
local_root_view->GetLayoutView(), FloatQuad(intersected_rect),
129-
kTraverseDocumentBoundaries | kUseTransforms)
130-
.BoundingBox();
131-
viewport_intersection_float.Move(
132-
-remote_frame_->OwnerLayoutObject()->PhysicalContentBoxOffset());
133-
viewport_intersection = EnclosingIntRect(viewport_intersection_float);
106+
unsigned geometry_flags =
107+
IntersectionGeometry::kShouldUseReplacedContentRect;
108+
if (parent_state >= DocumentLifecycle::kPrePaintClean &&
109+
RuntimeEnabledFeatures::IntersectionObserverV2Enabled()) {
110+
geometry_flags |= IntersectionGeometry::kShouldComputeVisibility;
134111
}
135-
}
136-
137-
if (parent_state >= DocumentLifecycle::kPrePaintClean &&
138-
RuntimeEnabledFeatures::IntersectionObserverV2Enabled()) {
139-
// TODO(layout-dev): As an optimization, we should only check for
140-
// occlusion and effects if the remote frame needs it, i.e., if it has at
141-
// least one active IntersectionObserver with trackVisibility:true.
142-
if (owner->GetDocument()
143-
.GetFrame()
144-
->LocalFrameRoot()
145-
.MayBeOccludedOrObscuredByRemoteAncestor() ||
146-
owner->HasDistortingVisualEffects()) {
147-
occluded_or_obscured = true;
112+
IntersectionGeometry geometry(nullptr, *owner_element, {},
113+
{IntersectionObserver::kMinimumThreshold},
114+
geometry_flags);
115+
// geometry.IntersectionRect() is in absolute coordinates of the owning
116+
// document. Map it down to absolute coordinates in the child document.
117+
LayoutRect intersection_rect = LayoutRect(
118+
owner
119+
->AncestorToLocalQuad(
120+
nullptr, FloatQuad(FloatRect(geometry.IntersectionRect())),
121+
kUseTransforms)
122+
.BoundingBox());
123+
// Map from the box coordinates of the owner to the inner frame.
124+
intersection_rect.Move(-owner->PhysicalContentBoxOffset());
125+
// Don't let EnclosingIntRect turn an empty rect into a non-empty one.
126+
if (intersection_rect.IsEmpty()) {
127+
viewport_intersection =
128+
IntRect(FlooredIntPoint(intersection_rect.Location()), IntSize());
148129
} else {
149-
HitTestResult result(owner->HitTestForOcclusion());
150-
occluded_or_obscured =
151-
result.InnerNode() && result.InnerNode() != owner->GetNode();
130+
viewport_intersection = EnclosingIntRect(intersection_rect);
152131
}
132+
occluded_or_obscured = !geometry.IsVisible();
153133
}
154134

135+
// TODO(szager): There are some redundant IPC's here; clean them up.
136+
bool is_visible_for_throttling = !viewport_intersection.IsEmpty();
137+
UpdateVisibility(is_visible_for_throttling);
138+
UpdateRenderThrottlingStatus(!is_visible_for_throttling, subtree_throttled_);
139+
155140
if (viewport_intersection == last_viewport_intersection_ &&
156141
occluded_or_obscured == last_occluded_or_obscured_) {
157142
return;
@@ -341,29 +326,6 @@ void RemoteFrameView::UpdateVisibility(bool scroll_visible) {
341326
remote_frame_->Client()->VisibilityChanged(visibility);
342327
}
343328

344-
void RemoteFrameView::OnViewportIntersectionChanged(
345-
const HeapVector<Member<IntersectionObserverEntry>>& entries) {
346-
bool is_visible = entries.back()->intersectionRatio() > 0;
347-
UpdateVisibility(is_visible);
348-
UpdateRenderThrottlingStatus(!is_visible, subtree_throttled_);
349-
}
350-
351-
void RemoteFrameView::SetupRenderThrottling() {
352-
if (visibility_observer_)
353-
return;
354-
355-
Element* target_element = GetFrame().DeprecatedLocalOwner();
356-
if (!target_element)
357-
return;
358-
359-
visibility_observer_ = IntersectionObserver::Create(
360-
{}, {IntersectionObserver::kMinimumThreshold},
361-
&target_element->GetDocument(),
362-
WTF::BindRepeating(&RemoteFrameView::OnViewportIntersectionChanged,
363-
WrapWeakPersistent(this)));
364-
visibility_observer_->observe(target_element);
365-
}
366-
367329
void RemoteFrameView::UpdateRenderThrottlingStatus(bool hidden,
368330
bool subtree_throttled) {
369331
TRACE_EVENT0("blink", "RemoteFrameView::UpdateRenderThrottlingStatus");

third_party/blink/renderer/core/frame/remote_frame_view.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ class RemoteFrameView final : public GarbageCollectedFinalized<RemoteFrameView>,
8888
const HeapVector<Member<IntersectionObserverEntry>>& entries);
8989
void UpdateRenderThrottlingStatus(bool hidden, bool subtree_throttled);
9090
bool CanThrottleRendering() const;
91-
void SetupRenderThrottling();
9291
void UpdateVisibility(bool scroll_visible);
9392

9493
// The properties and handling of the cycle between RemoteFrame

third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
1212
#include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
1313
#include "third_party/blink/renderer/core/layout/layout_box.h"
14+
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
1415
#include "third_party/blink/renderer/core/layout/layout_inline.h"
1516
#include "third_party/blink/renderer/core/layout/layout_view.h"
1617
#include "third_party/blink/renderer/core/page/page.h"
@@ -74,16 +75,17 @@ bool ComputeIsVisible(LayoutObject* target, const LayoutRect& rect) {
7475
return false;
7576
// TODO(layout-dev): This should hit-test the intersection rect, not the
7677
// target rect; it's not helpful to know that the portion of the target that
77-
// is clipped is also occluded. To do that, the intersection rect must be
78-
// mapped down to the local space of the target element.
78+
// is clipped is also occluded.
7979
HitTestResult result(target->HitTestForOcclusion(rect));
8080
return (!result.InnerNode() || result.InnerNode() == target->GetNode());
8181
}
8282

8383
static const unsigned kConstructorFlagsMask =
8484
IntersectionGeometry::kShouldReportRootBounds |
8585
IntersectionGeometry::kShouldComputeVisibility |
86-
IntersectionGeometry::kShouldTrackFractionOfRoot;
86+
IntersectionGeometry::kShouldTrackFractionOfRoot |
87+
IntersectionGeometry::kShouldUseReplacedContentRect |
88+
IntersectionGeometry::kShouldConvertToCSSPixels;
8789

8890
} // namespace
8991

@@ -127,20 +129,21 @@ void IntersectionGeometry::ComputeGeometry(Element* root_element,
127129

128130
DCHECK(!target_element.GetDocument().View()->NeedsLayout());
129131

130-
LayoutRect target_rect = InitializeTargetRect(target);
131-
LayoutRect intersection_rect = target_rect;
132-
LayoutRect root_rect = InitializeRootRect(root, root_margin);
133-
bool does_intersect = ClipToRoot(root, target, root_rect, intersection_rect);
134-
MapRectUpToDocument(target_rect, *target);
132+
target_rect_ = InitializeTargetRect(target);
133+
intersection_rect_ = target_rect_;
134+
root_rect_ = InitializeRootRect(root, root_margin);
135+
bool does_intersect =
136+
ClipToRoot(root, target, root_rect_, intersection_rect_);
137+
MapRectUpToDocument(target_rect_, *target);
135138
if (does_intersect) {
136139
if (RootIsImplicit())
137-
MapRectDownToDocument(intersection_rect, target->GetDocument());
140+
MapRectDownToDocument(intersection_rect_, target->GetDocument());
138141
else
139-
MapRectUpToDocument(intersection_rect, *root);
142+
MapRectUpToDocument(intersection_rect_, *root);
140143
} else {
141-
intersection_rect = LayoutRect();
144+
intersection_rect_ = LayoutRect();
142145
}
143-
MapRectUpToDocument(root_rect, *root);
146+
MapRectUpToDocument(root_rect_, *root);
144147

145148
// Some corner cases for threshold index:
146149
// - If target rect is zero area, because it has zero width and/or zero
@@ -159,11 +162,11 @@ void IntersectionGeometry::ComputeGeometry(Element* root_element,
159162

160163
if (does_intersect) {
161164
const LayoutRect comparison_rect =
162-
ShouldTrackFractionOfRoot() ? root_rect : target_rect;
165+
ShouldTrackFractionOfRoot() ? root_rect_ : target_rect_;
163166
if (comparison_rect.IsEmpty()) {
164167
intersection_ratio_ = 1;
165168
} else {
166-
const LayoutSize& intersection_size = intersection_rect.Size();
169+
const LayoutSize& intersection_size = intersection_rect_.Size();
167170
const float intersection_area = intersection_size.Width().ToFloat() *
168171
intersection_size.Height().ToFloat();
169172
const LayoutSize& comparison_size = comparison_rect.Size();
@@ -178,22 +181,27 @@ void IntersectionGeometry::ComputeGeometry(Element* root_element,
178181
threshold_index_ = 0;
179182
}
180183
if (IsIntersecting() && ShouldComputeVisibility() &&
181-
ComputeIsVisible(target, target_rect))
184+
ComputeIsVisible(target, target_rect_))
182185
flags_ |= kIsVisible;
183186

184-
// Convert to un-zoomed CSS pixels
185-
FloatRect target_float_rect(target_rect);
186-
AdjustForAbsoluteZoom::AdjustFloatRect(target_float_rect, *target);
187-
target_rect_ = LayoutRect(target_float_rect);
188-
FloatRect intersection_float_rect(intersection_rect);
189-
AdjustForAbsoluteZoom::AdjustFloatRect(intersection_float_rect, *target);
190-
intersection_rect_ = LayoutRect(intersection_float_rect);
191-
FloatRect root_float_rect(root_rect);
192-
AdjustForAbsoluteZoom::AdjustFloatRect(root_float_rect, *root);
193-
root_rect_ = LayoutRect(root_float_rect);
187+
if (flags_ & kShouldConvertToCSSPixels) {
188+
FloatRect target_float_rect(target_rect_);
189+
AdjustForAbsoluteZoom::AdjustFloatRect(target_float_rect, *target);
190+
target_rect_ = LayoutRect(target_float_rect);
191+
FloatRect intersection_float_rect(intersection_rect_);
192+
AdjustForAbsoluteZoom::AdjustFloatRect(intersection_float_rect, *target);
193+
intersection_rect_ = LayoutRect(intersection_float_rect);
194+
FloatRect root_float_rect(root_rect_);
195+
AdjustForAbsoluteZoom::AdjustFloatRect(root_float_rect, *root);
196+
root_rect_ = LayoutRect(root_float_rect);
197+
}
194198
}
195199

196200
LayoutRect IntersectionGeometry::InitializeTargetRect(LayoutObject* target) {
201+
if ((flags_ & kShouldUseReplacedContentRect) &&
202+
target->IsLayoutEmbeddedContent()) {
203+
return ToLayoutEmbeddedContent(target)->ReplacedContentRect();
204+
}
197205
if (target->IsBox())
198206
return LayoutRect(ToLayoutBoxModelObject(target)->BorderBoundingBox());
199207
if (target->IsLayoutInline())

third_party/blink/renderer/core/intersection_observer/intersection_geometry.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,20 @@ class CORE_EXPORT IntersectionGeometry {
3131
kShouldReportRootBounds = 1 << 0,
3232
kShouldComputeVisibility = 1 << 1,
3333
kShouldTrackFractionOfRoot = 1 << 2,
34+
kShouldUseReplacedContentRect = 1 << 3,
35+
kShouldConvertToCSSPixels = 1 << 4,
3436

3537
// These flags will be computed
36-
kRootIsImplicit = 1 << 3,
37-
kIsVisible = 1 << 4
38+
kRootIsImplicit = 1 << 5,
39+
kIsVisible = 1 << 6
3840
};
3941

4042
IntersectionGeometry(Element* root,
4143
Element& target,
4244
const Vector<Length>& root_margin,
4345
const Vector<float>& thresholds,
4446
unsigned flags);
47+
4548
IntersectionGeometry(const IntersectionGeometry&) = default;
4649
~IntersectionGeometry();
4750

@@ -55,7 +58,6 @@ class CORE_EXPORT IntersectionGeometry {
5558
return flags_ & kShouldTrackFractionOfRoot;
5659
}
5760

58-
// These are all in CSS pixels
5961
LayoutRect TargetRect() const { return target_rect_; }
6062
LayoutRect IntersectionRect() const { return intersection_rect_; }
6163
LayoutRect RootRect() const { return root_rect_; }

third_party/blink/renderer/core/intersection_observer/intersection_observation.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ void IntersectionObservation::Compute(unsigned flags) {
6060
bool report_root_bounds = observer_->AlwaysReportRootBounds() ||
6161
(flags & kReportImplicitRootBounds) ||
6262
!observer_->RootIsImplicit();
63-
unsigned geometry_flags = 0;
63+
unsigned geometry_flags = IntersectionGeometry::kShouldConvertToCSSPixels;
6464
if (report_root_bounds)
6565
geometry_flags |= IntersectionGeometry::kShouldReportRootBounds;
6666
if (Observer()->trackVisibility())

0 commit comments

Comments
 (0)