Commit 9c1e0c93 authored by Stefan Zager's avatar Stefan Zager Committed by Commit Bot

[IntersectionObserver] Refactor IntersectionGeometry

IntersectionGeometry is designed to be consumed by blink-internal
code using IntersectionObserver; IntersectionObserverEntry is
designed to implement the IDL for javascript. This CL divides the
classes along those lines, and also makes IntersectionGeometry a
true POD type by eliminating LayoutObject* members.

R=chrishtr@chromium.org

Change-Id: I5bb37aef8e5962cca813efc37f0f0d2c2c6798c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1501312Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Commit-Queue: Stefan Zager <szager@chromium.org>
Cr-Commit-Position: refs/heads/master@{#637783}
parent 3f72bca7
......@@ -38,17 +38,17 @@ namespace {
// could break their functionality, so these heuristics are used to recognize
// likely hidden frames and immediately load them so that they can function
// properly.
bool IsFrameProbablyHidden(const DOMRectReadOnly& bounding_client_rect,
bool IsFrameProbablyHidden(const LayoutRect& bounding_client_rect,
const Element& element) {
// Tiny frames that are 4x4 or smaller are likely not intended to be seen by
// the user. Note that this condition includes frames marked as
// "display:none", since those frames would have dimensions of 0x0.
if (bounding_client_rect.width() < 4.1 || bounding_client_rect.height() < 4.1)
if (bounding_client_rect.Width() < 4.1 || bounding_client_rect.Height() < 4.1)
return true;
// Frames that are positioned completely off the page above or to the left are
// likely never intended to be visible to the user.
if (bounding_client_rect.right() < 0.0 || bounding_client_rect.bottom() < 0.0)
if (bounding_client_rect.MaxX() < 0.0 || bounding_client_rect.MaxY() < 0.0)
return true;
const ComputedStyle* style = element.GetComputedStyle();
......@@ -142,7 +142,7 @@ void LazyLoadFrameObserver::LoadIfHiddenOrNearViewport(
if (entries.back()->isIntersecting()) {
RecordInitialDeferralAction(
FrameInitialDeferralAction::kLoadedNearOrInViewport);
} else if (IsFrameProbablyHidden(*entries.back()->boundingClientRect(),
} else if (IsFrameProbablyHidden(entries.back()->GetGeometry().TargetRect(),
*element_)) {
RecordInitialDeferralAction(FrameInitialDeferralAction::kLoadedHidden);
} else {
......@@ -206,7 +206,8 @@ void LazyLoadFrameObserver::RecordMetricsOnVisibilityChanged(
DCHECK(!entries.IsEmpty());
DCHECK_EQ(element_, entries.back()->target());
if (IsFrameProbablyHidden(*entries.back()->boundingClientRect(), *element_)) {
if (IsFrameProbablyHidden(entries.back()->GetGeometry().TargetRect(),
*element_)) {
visibility_metrics_observer_->disconnect();
visibility_metrics_observer_.Clear();
return;
......
......@@ -147,19 +147,15 @@ void MediaCustomControlsFullscreenDetector::OnIntersectionChanged(
// We only want a single notification, then stop observing.
viewport_intersection_observer_->disconnect();
const Member<IntersectionObserverEntry>& last_entry = entries.back();
const IntersectionGeometry& geometry = entries.back()->GetGeometry();
// Target and intersection rects must be converted from CSS to device pixels.
float zoom = VideoElement().GetLayoutObject()->StyleRef().EffectiveZoom();
DOMRectReadOnly* target_rect = last_entry->boundingClientRect();
FloatSize target_size(target_rect->width(), target_rect->height());
LayoutSize target_size = geometry.TargetRect().Size();
target_size.Scale(zoom);
DOMRectReadOnly* intersection_rect = last_entry->intersectionRect();
FloatSize intersection_size(intersection_rect->width(),
intersection_rect->height());
LayoutSize intersection_size = geometry.IntersectionRect().Size();
intersection_size.Scale(zoom);
DOMRectReadOnly* root_rect = last_entry->rootBounds();
FloatSize root_size(root_rect->width(), root_rect->height());
LayoutSize root_size = geometry.RootRect().Size();
bool is_dominant = ComputeIsDominantVideoForTests(
RoundedIntSize(target_size), RoundedIntSize(root_size),
......
......@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_GEOMETRY_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_GEOMETRY_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/geometry/length.h"
......@@ -14,7 +15,6 @@
namespace blink {
class Element;
class IntersectionObserverEntry;
class LayoutObject;
// Computes the intersection between an ancestor (root) element and a
......@@ -24,9 +24,7 @@ class LayoutObject;
//
// If the root argument to the constructor is null, computes the intersection
// of the target with the top-level frame viewport (AKA the "implicit root").
class IntersectionGeometry {
STACK_ALLOCATED();
class CORE_EXPORT IntersectionGeometry {
public:
enum Flags {
// These flags should passed to the constructor
......@@ -44,6 +42,7 @@ class IntersectionGeometry {
const Vector<Length>& root_margin,
const Vector<float>& thresholds,
unsigned flags);
IntersectionGeometry(const IntersectionGeometry&) = default;
~IntersectionGeometry();
bool ShouldReportRootBounds() const {
......@@ -56,23 +55,10 @@ class IntersectionGeometry {
return flags_ & kShouldTrackFractionOfRoot;
}
LayoutObject* Root() const { return root_; }
LayoutObject* Target() const { return target_; }
// Client rect in the coordinate system of the frame containing target.
// These are all in CSS pixels
LayoutRect TargetRect() const { return target_rect_; }
// Target rect in CSS pixels
LayoutRect UnZoomedTargetRect() const;
// Client rect in the coordinate system of the frame containing target.
LayoutRect IntersectionRect() const { return intersection_rect_; }
// Intersection rect in CSS pixels
LayoutRect UnZoomedIntersectionRect() const;
// Client rect in the coordinate system of the frame containing root.
LayoutRect RootRect() const { return root_rect_; }
// Root rect in CSS pixels
LayoutRect UnZoomedRootRect() const;
IntRect IntersectionIntRect() const {
return PixelSnappedIntRect(intersection_rect_);
......@@ -87,26 +73,22 @@ class IntersectionGeometry {
bool IsIntersecting() const { return threshold_index_ > 0; }
bool IsVisible() const { return flags_ & kIsVisible; }
IntersectionObserverEntry* CreateEntry(Element* target,
DOMHighResTimeStamp timestamp);
private:
void ComputeGeometry();
bool CanComputeGeometry(Element* root, Element& target) const;
void InitializeTargetRect();
void InitializeRootRect();
bool ClipToRoot();
void MapTargetRectToTargetFrameCoordinates();
void MapRootRectToRootFrameCoordinates();
void MapIntersectionRectToTargetFrameCoordinates();
void ApplyRootMargin();
unsigned FirstThresholdGreaterThan(float ratio) const;
void ComputeVisibility();
LayoutObject* root_;
LayoutObject* target_;
const Vector<Length>& root_margin_;
const Vector<float>& thresholds_;
void ComputeGeometry(Element* root_element,
Element& target_element,
const Vector<Length>& root_margin,
const Vector<float>& thresholds);
LayoutRect InitializeTargetRect(LayoutObject* target);
LayoutRect InitializeRootRect(LayoutObject* root,
const Vector<Length>& margin);
void ApplyRootMargin(LayoutRect& rect, const Vector<Length>& margin);
bool ClipToRoot(LayoutObject* root,
LayoutObject* target,
const LayoutRect& root_rect,
LayoutRect& intersection_rect);
unsigned FirstThresholdGreaterThan(float ratio,
const Vector<float>& thresholds) const;
LayoutRect target_rect_;
LayoutRect intersection_rect_;
LayoutRect root_rect_;
......
......@@ -76,10 +76,9 @@ void IntersectionObservation::Compute(unsigned flags) {
if (last_threshold_index_ != geometry.ThresholdIndex() ||
last_is_visible_ != geometry.IsVisible()) {
entries_.push_back(geometry.CreateEntry(Target(), timestamp));
To<Document>(Observer()->GetExecutionContext())
->EnsureIntersectionObserverController()
.ScheduleIntersectionObserverForDelivery(*Observer());
entries_.push_back(MakeGarbageCollected<IntersectionObserverEntry>(
geometry, timestamp, Target()));
Observer()->SetNeedsDelivery();
SetLastThresholdIndex(geometry.ThresholdIndex());
SetWasVisible(geometry.IsVisible());
}
......
......@@ -20,7 +20,6 @@
#include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_init.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
......@@ -230,7 +229,8 @@ IntersectionObserver::IntersectionObserver(
root_is_implicit_(root ? 0 : 1),
track_visibility_(track_visibility ? 1 : 0),
track_fraction_of_root_(semantics == kFractionOfRoot),
always_report_root_bounds_(always_report_root_bounds ? 1 : 0) {
always_report_root_bounds_(always_report_root_bounds ? 1 : 0),
needs_delivery_(0) {
switch (root_margin.size()) {
case 0:
break;
......@@ -331,6 +331,7 @@ void IntersectionObserver::disconnect(ExceptionState& exception_state) {
HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords(
ExceptionState& exception_state) {
needs_delivery_ = 0;
HeapVector<Member<IntersectionObserverEntry>> entries;
for (auto& observation : observations_)
observation->TakeRecords(entries);
......@@ -369,14 +370,19 @@ DOMHighResTimeStamp IntersectionObserver::GetTimeStamp() const {
return -1;
}
unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const {
unsigned result = 0;
while (result < thresholds_.size() && thresholds_[result] <= ratio)
++result;
return result;
void IntersectionObserver::SetNeedsDelivery() {
if (needs_delivery_)
return;
needs_delivery_ = 1;
To<Document>(GetExecutionContext())
->EnsureIntersectionObserverController()
.ScheduleIntersectionObserverForDelivery(*this);
}
void IntersectionObserver::Deliver() {
if (!needs_delivery_)
return;
needs_delivery_ = 0;
HeapVector<Member<IntersectionObserverEntry>> entries;
for (auto& observation : observations_)
observation->TakeRecords(entries);
......
......@@ -124,7 +124,7 @@ class CORE_EXPORT IntersectionObserver final
const Length& RightMargin() const { return right_margin_; }
const Length& BottomMargin() const { return bottom_margin_; }
const Length& LeftMargin() const { return left_margin_; }
unsigned FirstThresholdGreaterThan(float ratio) const;
void SetNeedsDelivery();
void Deliver();
// Returns false if this observer has an explicit root element which has been
......@@ -156,6 +156,7 @@ class CORE_EXPORT IntersectionObserver final
unsigned track_visibility_ : 1;
unsigned track_fraction_of_root_ : 1;
unsigned always_report_root_bounds_ : 1;
unsigned needs_delivery_ : 1;
};
} // namespace blink
......
......@@ -56,7 +56,8 @@ void IntersectionObserverController::DeliverIntersectionObservations() {
// TODO(yukishiino): Remove this CHECK once https://crbug.com/809784 gets
// resolved.
CHECK(!context->IsContextDestroyed());
pending_intersection_observers_.swap(intersection_observers_being_invoked_);
CopyToVector(pending_intersection_observers_,
intersection_observers_being_invoked_);
for (auto& observer : intersection_observers_being_invoked_)
observer->Deliver();
intersection_observers_being_invoked_.clear();
......
......@@ -55,7 +55,7 @@ class IntersectionObserverController
pending_intersection_observers_;
// TODO(https://crbug.com/796145): Remove this hack once on-stack objects
// get supported by either of wrapper-tracing or unified GC.
HeapHashSet<TraceWrapperMember<IntersectionObserver>>
HeapVector<TraceWrapperMember<IntersectionObserver>>
intersection_observers_being_invoked_;
};
......
......@@ -3,37 +3,34 @@
// found in the LICENSE file.
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
#include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
#include "third_party/blink/renderer/core/dom/element.h"
namespace blink {
IntersectionObserverEntry::IntersectionObserverEntry(
const IntersectionGeometry& geometry,
DOMHighResTimeStamp time,
double intersection_ratio,
const FloatRect& bounding_client_rect,
const FloatRect* root_bounds,
const FloatRect& intersection_rect,
bool is_intersecting,
bool is_visible,
Element* target)
: time_(time),
intersection_ratio_(intersection_ratio),
bounding_client_rect_(
DOMRectReadOnly::FromFloatRect(bounding_client_rect)),
root_bounds_(root_bounds ? DOMRectReadOnly::FromFloatRect(*root_bounds)
: nullptr),
intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)),
target_(target),
is_intersecting_(is_intersecting),
is_visible_(is_visible)
{}
: geometry_(geometry), time_(time), target_(target) {}
DOMRectReadOnly* IntersectionObserverEntry::boundingClientRect() const {
return DOMRectReadOnly::FromFloatRect(FloatRect(geometry_.TargetRect()));
}
DOMRectReadOnly* IntersectionObserverEntry::rootBounds() const {
if (geometry_.ShouldReportRootBounds())
return DOMRectReadOnly::FromFloatRect(FloatRect(geometry_.RootRect()));
return nullptr;
}
DOMRectReadOnly* IntersectionObserverEntry::intersectionRect() const {
return DOMRectReadOnly::FromFloatRect(
FloatRect(geometry_.IntersectionRect()));
}
void IntersectionObserverEntry::Trace(blink::Visitor* visitor) {
visitor->Trace(bounding_client_rect_);
visitor->Trace(root_bounds_);
visitor->Trace(intersection_rect_);
visitor->Trace(target_);
ScriptWrappable::Trace(visitor);
}
......
......@@ -6,48 +6,42 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVER_ENTRY_H_
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
#include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_geometry.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/geometry/int_rect.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink {
class DOMRectReadOnly;
class Element;
class CORE_EXPORT IntersectionObserverEntry final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
IntersectionObserverEntry(DOMHighResTimeStamp timestamp,
double intersection_ratio,
const FloatRect& bounding_client_rect,
const FloatRect* root_bounds,
const FloatRect& intersection_rect,
bool is_intersecting,
bool is_visible,
Element*);
IntersectionObserverEntry(const IntersectionGeometry& geometry,
DOMHighResTimeStamp timestamp,
Element* target);
// IDL interface
double time() const { return time_; }
double intersectionRatio() const { return intersection_ratio_; }
DOMRectReadOnly* boundingClientRect() const { return bounding_client_rect_; }
DOMRectReadOnly* rootBounds() const { return root_bounds_; }
DOMRectReadOnly* intersectionRect() const { return intersection_rect_; }
bool isIntersecting() const { return is_intersecting_; }
bool isVisible() const { return is_visible_; }
double intersectionRatio() const { return geometry_.IntersectionRatio(); }
DOMRectReadOnly* boundingClientRect() const;
DOMRectReadOnly* rootBounds() const;
DOMRectReadOnly* intersectionRect() const;
bool isIntersecting() const { return geometry_.IsIntersecting(); }
bool isVisible() const { return geometry_.IsVisible(); }
Element* target() const { return target_.Get(); }
// blink-internal interface
const IntersectionGeometry& GetGeometry() const { return geometry_; }
void Trace(blink::Visitor*) override;
private:
IntersectionGeometry geometry_;
DOMHighResTimeStamp time_;
double intersection_ratio_;
Member<DOMRectReadOnly> bounding_client_rect_;
Member<DOMRectReadOnly> root_bounds_;
Member<DOMRectReadOnly> intersection_rect_;
Member<Element> target_;
bool is_intersecting_;
bool is_visible_;
};
} // namespace blink
......
......@@ -38,11 +38,11 @@ class TestIntersectionObserverDelegate : public IntersectionObserverDelegate {
FloatRect LastIntersectionRect() const {
if (entries_.IsEmpty())
return FloatRect();
const IntersectionObserverEntry* entry = entries_.back();
return FloatRect(entry->intersectionRect()->x(),
entry->intersectionRect()->y(),
entry->intersectionRect()->width(),
entry->intersectionRect()->height());
const IntersectionGeometry& geometry = entries_.back()->GetGeometry();
return FloatRect(geometry.IntersectionRect().X(),
geometry.IntersectionRect().Y(),
geometry.IntersectionRect().Width(),
geometry.IntersectionRect().Height());
}
void Trace(blink::Visitor* visitor) override {
......
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