Commit b36af58a authored by Stefan Zager's avatar Stefan Zager Committed by Commit Bot

[IntersectionObserver] Refactor to avoid ancestor frame tree walk

Previously, the dirty flags to IntersectionObservation::Compute were
computed by doing a walk up the ancestor tree. This is silly, since
the IntersectionObserver algorithm runs in a top-down tree walk. This
CL propagates ancestor frame flags down to child frames to avoid the
tree walk.

R=chrishtr@chromium.org

Change-Id: I09f95696ce4d22c15ee76bc0a0ecc4e12778284c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1501492
Commit-Queue: Stefan Zager <szager@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#637829}
parent 2e25175f
...@@ -15,7 +15,11 @@ struct IntrinsicSizingInfo; ...@@ -15,7 +15,11 @@ struct IntrinsicSizingInfo;
class CORE_EXPORT FrameView : public EmbeddedContentView { class CORE_EXPORT FrameView : public EmbeddedContentView {
public: public:
~FrameView() override = default; ~FrameView() override = default;
virtual void UpdateViewportIntersectionsForSubtree() = 0;
// parent_flags is the result of calling GetIntersectionObservationFlags on
// the LocalFrameView parent of this FrameView (if any). It contains dirty
// bits based on whether geometry may have changed in the parent frame.
virtual void UpdateViewportIntersectionsForSubtree(unsigned parent_flags) = 0;
virtual bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const = 0; virtual bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const = 0;
virtual bool HasIntrinsicSizingInfo() const = 0; virtual bool HasIntrinsicSizingInfo() const = 0;
......
...@@ -1068,7 +1068,7 @@ void LocalFrameView::RunIntersectionObserverSteps() { ...@@ -1068,7 +1068,7 @@ void LocalFrameView::RunIntersectionObserverSteps() {
"LocalFrameView::UpdateViewportIntersectionsForSubtree"); "LocalFrameView::UpdateViewportIntersectionsForSubtree");
SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(), SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
LocalFrameUkmAggregator::kIntersectionObservation); LocalFrameUkmAggregator::kIntersectionObservation);
UpdateViewportIntersectionsForSubtree(); UpdateViewportIntersectionsForSubtree(0);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK(was_dirty || !NeedsLayout()); DCHECK(was_dirty || !NeedsLayout());
#endif #endif
...@@ -3869,7 +3869,8 @@ void LocalFrameView::CollectAnnotatedRegions( ...@@ -3869,7 +3869,8 @@ void LocalFrameView::CollectAnnotatedRegions(
CollectAnnotatedRegions(*curr, regions); CollectAnnotatedRegions(*curr, regions);
} }
void LocalFrameView::UpdateViewportIntersectionsForSubtree() { void LocalFrameView::UpdateViewportIntersectionsForSubtree(
unsigned parent_flags) {
// TODO(dcheng): Since LocalFrameView tree updates are deferred, FrameViews // TODO(dcheng): Since LocalFrameView tree updates are deferred, FrameViews
// might still be in the LocalFrameView hierarchy even though the associated // might still be in the LocalFrameView hierarchy even though the associated
// Document is already detached. Investigate if this check and a similar check // Document is already detached. Investigate if this check and a similar check
...@@ -3878,28 +3879,31 @@ void LocalFrameView::UpdateViewportIntersectionsForSubtree() { ...@@ -3878,28 +3879,31 @@ void LocalFrameView::UpdateViewportIntersectionsForSubtree() {
if (!GetFrame().GetDocument()->IsActive()) if (!GetFrame().GetDocument()->IsActive())
return; return;
unsigned flags = GetIntersectionObservationFlags(parent_flags);
if (!NeedsLayout()) { if (!NeedsLayout()) {
// Notify javascript IntersectionObservers // Notify javascript IntersectionObservers
if (GetFrame().GetDocument()->GetIntersectionObserverController()) { if (GetFrame().GetDocument()->GetIntersectionObserverController()) {
GetFrame() GetFrame()
.GetDocument() .GetDocument()
->GetIntersectionObserverController() ->GetIntersectionObserverController()
->ComputeTrackedIntersectionObservations(); ->ComputeTrackedIntersectionObservations(flags);
} }
intersection_observation_state_ = kNotNeeded;
} }
for (Frame* child = frame_->Tree().FirstChild(); child; for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) { child = child->Tree().NextSibling()) {
child->View()->UpdateViewportIntersectionsForSubtree(); child->View()->UpdateViewportIntersectionsForSubtree(flags);
} }
for (HTMLPortalElement* portal : for (HTMLPortalElement* portal :
DocumentPortals::From(*frame_->GetDocument()).GetPortals()) { DocumentPortals::From(*frame_->GetDocument()).GetPortals()) {
if (portal->ContentFrame()) if (portal->ContentFrame()) {
portal->ContentFrame()->View()->UpdateViewportIntersectionsForSubtree(); portal->ContentFrame()->View()->UpdateViewportIntersectionsForSubtree(
flags);
}
} }
intersection_observation_state_ = kNotNeeded;
} }
void LocalFrameView::UpdateThrottlingStatusForSubtree() { void LocalFrameView::UpdateThrottlingStatusForSubtree() {
...@@ -4044,7 +4048,8 @@ void LocalFrameView::SetPaintArtifactCompositorNeedsUpdate() const { ...@@ -4044,7 +4048,8 @@ void LocalFrameView::SetPaintArtifactCompositorNeedsUpdate() const {
root->paint_artifact_compositor_->SetNeedsUpdate(); root->paint_artifact_compositor_->SetNeedsUpdate();
} }
unsigned LocalFrameView::GetIntersectionObservationFlags() const { unsigned LocalFrameView::GetIntersectionObservationFlags(
unsigned parent_flags) const {
unsigned flags = 0; unsigned flags = 0;
const LocalFrame& target_frame = GetFrame(); const LocalFrame& target_frame = GetFrame();
...@@ -4057,21 +4062,15 @@ unsigned LocalFrameView::GetIntersectionObservationFlags() const { ...@@ -4057,21 +4062,15 @@ unsigned LocalFrameView::GetIntersectionObservationFlags() const {
// Observers with explicit roots only need to be checked on the same frame, // Observers with explicit roots only need to be checked on the same frame,
// since in this case target and root must be in the same document. // since in this case target and root must be in the same document.
if (intersection_observation_state_ != kNotNeeded) if (intersection_observation_state_ != kNotNeeded) {
flags |= IntersectionObservation::kExplicitRootObserversNeedUpdate; flags |= (IntersectionObservation::kExplicitRootObserversNeedUpdate |
IntersectionObservation::kImplicitRootObserversNeedUpdate);
}
// For observers with implicit roots, we need to check state on the whole // For observers with implicit roots, we need to check state on the whole
// local frame tree. // local frame tree, as passed down from the parent.
const LocalFrameView* local_root_view = target_frame.LocalFrameRoot().View(); flags |= (parent_flags &
for (const LocalFrameView* view = this; view; IntersectionObservation::kImplicitRootObserversNeedUpdate);
view = view->ParentFrameView()) {
if (view->intersection_observation_state_ != kNotNeeded) {
flags |= IntersectionObservation::kImplicitRootObserversNeedUpdate;
break;
}
if (view == local_root_view)
break;
}
return flags; return flags;
} }
......
...@@ -218,7 +218,7 @@ class CORE_EXPORT LocalFrameView final ...@@ -218,7 +218,7 @@ class CORE_EXPORT LocalFrameView final
// Get the InstersectionObservation::ComputeFlags for target elements in this // Get the InstersectionObservation::ComputeFlags for target elements in this
// view. // view.
unsigned GetIntersectionObservationFlags() const; unsigned GetIntersectionObservationFlags(unsigned parent_flags) const;
void SetPaintArtifactCompositorNeedsUpdate() const; void SetPaintArtifactCompositorNeedsUpdate() const;
...@@ -867,7 +867,8 @@ class CORE_EXPORT LocalFrameView final ...@@ -867,7 +867,8 @@ class CORE_EXPORT LocalFrameView final
template <typename Function> template <typename Function>
void ForAllNonThrottledLocalFrameViews(const Function&); void ForAllNonThrottledLocalFrameViews(const Function&);
void UpdateViewportIntersectionsForSubtree() override; void UpdateViewportIntersectionsForSubtree(unsigned parent_flags) override;
void UpdateThrottlingStatusForSubtree(); void UpdateThrottlingStatusForSubtree();
void NotifyResizeObservers(); void NotifyResizeObservers();
......
...@@ -83,7 +83,13 @@ RemoteFrameView* RemoteFrameView::Create(RemoteFrame* remote_frame) { ...@@ -83,7 +83,13 @@ RemoteFrameView* RemoteFrameView::Create(RemoteFrame* remote_frame) {
return view; return view;
} }
void RemoteFrameView::UpdateViewportIntersectionsForSubtree() { void RemoteFrameView::UpdateViewportIntersectionsForSubtree(
unsigned parent_flags) {
if (!(parent_flags &
IntersectionObservation::kImplicitRootObserversNeedUpdate)) {
return;
}
LayoutEmbeddedContent* owner = remote_frame_->OwnerLayoutObject(); LayoutEmbeddedContent* owner = remote_frame_->OwnerLayoutObject();
if (!owner) if (!owner)
return; return;
......
...@@ -58,7 +58,7 @@ class RemoteFrameView final : public GarbageCollectedFinalized<RemoteFrameView>, ...@@ -58,7 +58,7 @@ class RemoteFrameView final : public GarbageCollectedFinalized<RemoteFrameView>,
void Show() override; void Show() override;
void SetParentVisible(bool) override; void SetParentVisible(bool) override;
void UpdateViewportIntersectionsForSubtree() override; void UpdateViewportIntersectionsForSubtree(unsigned parent_flags) override;
bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const override; bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const override;
......
...@@ -63,14 +63,12 @@ void IntersectionObserverController::DeliverIntersectionObservations() { ...@@ -63,14 +63,12 @@ void IntersectionObserverController::DeliverIntersectionObservations() {
intersection_observers_being_invoked_.clear(); intersection_observers_being_invoked_.clear();
} }
void IntersectionObserverController::ComputeTrackedIntersectionObservations() { void IntersectionObserverController::ComputeTrackedIntersectionObservations(
unsigned flags) {
if (Document* document = To<Document>(GetExecutionContext())) { if (Document* document = To<Document>(GetExecutionContext())) {
TRACE_EVENT0("blink", TRACE_EVENT0("blink",
"IntersectionObserverController::" "IntersectionObserverController::"
"computeTrackedIntersectionObservations"); "computeTrackedIntersectionObservations");
unsigned flags;
if (LocalFrameView* target_view = document->View())
flags = target_view->GetIntersectionObservationFlags();
for (auto& element : tracked_observation_targets_) for (auto& element : tracked_observation_targets_)
element->ComputeIntersectionObservations(flags); element->ComputeIntersectionObservations(flags);
} }
......
...@@ -33,7 +33,11 @@ class IntersectionObserverController ...@@ -33,7 +33,11 @@ class IntersectionObserverController
void ScheduleIntersectionObserverForDelivery(IntersectionObserver&); void ScheduleIntersectionObserverForDelivery(IntersectionObserver&);
void DeliverIntersectionObservations(); void DeliverIntersectionObservations();
void ComputeTrackedIntersectionObservations();
// The flags argument is composed of values from
// IntersectionObservation::ComputeFlags. They are dirty bits that control
// whether an IntersectionObserver needs to do any work.
void ComputeTrackedIntersectionObservations(unsigned flags);
void AddTrackedTarget(Element&); void AddTrackedTarget(Element&);
void RemoveTrackedTarget(Element&); void RemoveTrackedTarget(Element&);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment