Commit 61b31080 authored by Stefan Zager's avatar Stefan Zager Committed by Commit Bot

[IntersectionObserver] Reland: Fix the concept of "tracking document"

Re-landing after revert:

https://chromium-review.googlesource.com/c/chromium/src/+/1200388

... but leave the flaky tests disabled.

R=chrishtr@chromium.org
BUG=879798

Change-Id: Ia588f9d82665a533b4e1bd2f77809692ded0cb41
Reviewed-on: https://chromium-review.googlesource.com/1215370
Commit-Queue: Stefan Zager <szager@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarStefan Zager <szager@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590155}
parent ad39c988
<!DOCTYPE html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="./resources/intersection-observer-test-utils.js"></script>
<script>
var entries = [];
var popup, target;
function waitForPopupNotification(f) {
popup.requestAnimationFrame(function() {
popup.setTimeout(function() { popup.setTimeout(f); });
});
}
async_test((t) => {
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes);
});
popup = window.open();
target = popup.document.createElement('div');
target.style.width = "100px";
target.style.height = "100px";
observer.observe(target);
waitForPopupNotification(() => {
assert_equals(entries.length, 1, "Initial notification for detached target.");
assert_equals(entries[0].isIntersecting, false, "not intersecting");
popup.document.body.appendChild(target);
waitForPopupNotification(() => {
assert_equals(entries.length, 2, "Notification after insertion into popup.");
assert_equals(entries[1].isIntersecting, true, "intersecting");
t.done();
});
});
}, "IntersectionObserver with target in a different window.");
</script>
...@@ -121,6 +121,7 @@ ...@@ -121,6 +121,7 @@
#include "third_party/blink/renderer/core/html/parser/nesting_level_incrementer.h" #include "third_party/blink/renderer/core/html/parser/nesting_level_incrementer.h"
#include "third_party/blink/renderer/core/input/event_handler.h" #include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h" #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/invisible_dom/activate_invisible_event.h" #include "third_party/blink/renderer/core/invisible_dom/activate_invisible_event.h"
#include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h" #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h" #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
...@@ -2045,10 +2046,12 @@ Node::InsertionNotificationRequest Element::InsertedInto( ...@@ -2045,10 +2046,12 @@ Node::InsertionNotificationRequest Element::InsertedInto(
if (HasRareData()) { if (HasRareData()) {
ElementRareData* rare_data = GetElementRareData(); ElementRareData* rare_data = GetElementRareData();
if (rare_data->IntersectionObserverData()) if (rare_data->IntersectionObserverData() &&
rare_data->IntersectionObserverData()->ActivateValidIntersectionObservers( rare_data->IntersectionObserverData()->HasObservations()) {
GetDocument().EnsureIntersectionObserverController().AddTrackedTarget(
*this); *this);
} }
}
if (isConnected()) { if (isConnected()) {
if (GetCustomElementState() == CustomElementState::kCustom) if (GetCustomElementState() == CustomElementState::kCustom)
...@@ -2148,10 +2151,11 @@ void Element::RemovedFrom(ContainerNode& insertion_point) { ...@@ -2148,10 +2151,11 @@ void Element::RemovedFrom(ContainerNode& insertion_point) {
if (ElementAnimations* element_animations = data->GetElementAnimations()) if (ElementAnimations* element_animations = data->GetElementAnimations())
element_animations->CssAnimations().Cancel(); element_animations->CssAnimations().Cancel();
if (data->IntersectionObserverData()) if (data->IntersectionObserverData()) {
data->IntersectionObserverData()->DeactivateAllIntersectionObservers( GetDocument().EnsureIntersectionObserverController().RemoveTrackedTarget(
*this); *this);
} }
}
if (GetDocument().GetFrame()) if (GetDocument().GetFrame())
GetDocument().GetFrame()->GetEventHandler().ElementRemoved(this); GetDocument().GetFrame()->GetEventHandler().ElementRemoved(this);
...@@ -3647,6 +3651,12 @@ ElementIntersectionObserverData& Element::EnsureIntersectionObserverData() { ...@@ -3647,6 +3651,12 @@ ElementIntersectionObserverData& Element::EnsureIntersectionObserverData() {
return EnsureElementRareData().EnsureIntersectionObserverData(); return EnsureElementRareData().EnsureIntersectionObserverData();
} }
void Element::ComputeIntersectionObservations(
bool should_report_implicit_root_bounds) {
if (ElementIntersectionObserverData* data = IntersectionObserverData())
data->ComputeObservations(should_report_implicit_root_bounds);
}
HeapHashMap<TraceWrapperMember<ResizeObserver>, Member<ResizeObservation>>* HeapHashMap<TraceWrapperMember<ResizeObserver>, Member<ResizeObservation>>*
Element::ResizeObserverData() const { Element::ResizeObserverData() const {
if (HasRareData()) if (HasRareData())
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/css/css_primitive_value.h" #include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/css_selector.h" #include "third_party/blink/renderer/core/css/css_selector.h"
#include "third_party/blink/renderer/core/dom/container_node.h" #include "third_party/blink/renderer/core/dom/container_node.h"
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
#include "third_party/blink/renderer/core/dom/element_data.h" #include "third_party/blink/renderer/core/dom/element_data.h"
#include "third_party/blink/renderer/core/dom/names_map.h" #include "third_party/blink/renderer/core/dom/names_map.h"
#include "third_party/blink/renderer/core/dom/whitespace_attacher.h" #include "third_party/blink/renderer/core/dom/whitespace_attacher.h"
...@@ -897,6 +898,7 @@ class CORE_EXPORT Element : public ContainerNode { ...@@ -897,6 +898,7 @@ class CORE_EXPORT Element : public ContainerNode {
ElementIntersectionObserverData* IntersectionObserverData() const; ElementIntersectionObserverData* IntersectionObserverData() const;
ElementIntersectionObserverData& EnsureIntersectionObserverData(); ElementIntersectionObserverData& EnsureIntersectionObserverData();
void ComputeIntersectionObservations(bool);
HeapHashMap<TraceWrapperMember<ResizeObserver>, Member<ResizeObservation>>* HeapHashMap<TraceWrapperMember<ResizeObserver>, Member<ResizeObservation>>*
ResizeObserverData() const; ResizeObserverData() const;
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/context_lifecycle_notifier.h" #include "third_party/blink/renderer/core/dom/context_lifecycle_notifier.h"
#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h" #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/loader/fetch/access_control_status.h" #include "third_party/blink/renderer/platform/loader/fetch/access_control_status.h"
#include "third_party/blink/renderer/platform/supplementable.h" #include "third_party/blink/renderer/platform/supplementable.h"
...@@ -61,7 +62,6 @@ class EventTarget; ...@@ -61,7 +62,6 @@ class EventTarget;
class FrameOrWorkerScheduler; class FrameOrWorkerScheduler;
class InterfaceInvalidator; class InterfaceInvalidator;
class KURL; class KURL;
class LocalDOMWindow;
class PausableObject; class PausableObject;
class PublicURLManager; class PublicURLManager;
class ResourceFetcher; class ResourceFetcher;
......
...@@ -21,16 +21,6 @@ IntersectionObservation* ElementIntersectionObserverData::GetObservationFor( ...@@ -21,16 +21,6 @@ IntersectionObservation* ElementIntersectionObserverData::GetObservationFor(
return i->value; return i->value;
} }
void ElementIntersectionObserverData::AddObserver(
IntersectionObserver& observer) {
intersection_observers_.insert(&observer);
}
void ElementIntersectionObserverData::RemoveObserver(
IntersectionObserver& observer) {
intersection_observers_.erase(&observer);
}
void ElementIntersectionObserverData::AddObservation( void ElementIntersectionObserverData::AddObservation(
IntersectionObservation& observation) { IntersectionObservation& observation) {
DCHECK(observation.Observer()); DCHECK(observation.Observer());
...@@ -42,28 +32,13 @@ void ElementIntersectionObserverData::RemoveObservation( ...@@ -42,28 +32,13 @@ void ElementIntersectionObserverData::RemoveObservation(
intersection_observations_.erase(&observer); intersection_observations_.erase(&observer);
} }
void ElementIntersectionObserverData::ActivateValidIntersectionObservers( void ElementIntersectionObserverData::ComputeObservations(
Node& node) { bool should_report_implicit_root_bounds) {
for (auto& observer : intersection_observers_) {
Document* document = observer->TrackingDocument();
if (!document)
continue;
document->EnsureIntersectionObserverController().AddTrackedObserver(
*observer);
}
for (auto& observation : intersection_observations_) for (auto& observation : intersection_observations_)
observation.value->UpdateShouldReportRootBoundsAfterDomChange(); observation.value->Compute(should_report_implicit_root_bounds);
}
void ElementIntersectionObserverData::DeactivateAllIntersectionObservers(
Node& node) {
node.GetDocument()
.EnsureIntersectionObserverController()
.RemoveTrackedObserversForRoot(node);
} }
void ElementIntersectionObserverData::Trace(blink::Visitor* visitor) { void ElementIntersectionObserverData::Trace(blink::Visitor* visitor) {
visitor->Trace(intersection_observers_);
visitor->Trace(intersection_observations_); visitor->Trace(intersection_observations_);
} }
......
...@@ -5,13 +5,13 @@ ...@@ -5,13 +5,13 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_ELEMENT_INTERSECTION_OBSERVER_DATA_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_ELEMENT_INTERSECTION_OBSERVER_DATA_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_ELEMENT_INTERSECTION_OBSERVER_DATA_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_ELEMENT_INTERSECTION_OBSERVER_DATA_H_
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
#include "third_party/blink/renderer/platform/bindings/name_client.h" #include "third_party/blink/renderer/platform/bindings/name_client.h"
#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink { namespace blink {
class Node;
class IntersectionObservation; class IntersectionObservation;
class IntersectionObserver; class IntersectionObserver;
...@@ -22,12 +22,10 @@ class ElementIntersectionObserverData ...@@ -22,12 +22,10 @@ class ElementIntersectionObserverData
ElementIntersectionObserverData(); ElementIntersectionObserverData();
IntersectionObservation* GetObservationFor(IntersectionObserver&); IntersectionObservation* GetObservationFor(IntersectionObserver&);
void AddObserver(IntersectionObserver&);
void RemoveObserver(IntersectionObserver&);
void AddObservation(IntersectionObservation&); void AddObservation(IntersectionObservation&);
void RemoveObservation(IntersectionObserver&); void RemoveObservation(IntersectionObserver&);
void ActivateValidIntersectionObservers(Node&); bool HasObservations() const { return !intersection_observations_.IsEmpty(); }
void DeactivateAllIntersectionObservers(Node&); void ComputeObservations(bool);
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
const char* NameInHeapSnapshot() const override { const char* NameInHeapSnapshot() const override {
...@@ -35,8 +33,6 @@ class ElementIntersectionObserverData ...@@ -35,8 +33,6 @@ class ElementIntersectionObserverData
} }
private: private:
// IntersectionObservers for which the Node owning this data is root.
HeapHashSet<WeakMember<IntersectionObserver>> intersection_observers_;
// IntersectionObservations for which the Node owning this data is target. // IntersectionObservations for which the Node owning this data is target.
HeapHashMap<TraceWrapperMember<IntersectionObserver>, HeapHashMap<TraceWrapperMember<IntersectionObserver>,
Member<IntersectionObservation>> Member<IntersectionObservation>>
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/dom/element_rare_data.h" #include "third_party/blink/renderer/core/dom/element_rare_data.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h" #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/intersection_geometry.h" #include "third_party/blink/renderer/core/layout/intersection_geometry.h"
#include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_object.h"
...@@ -34,30 +35,42 @@ bool IsOccluded(const Element& element, const IntersectionGeometry& geometry) { ...@@ -34,30 +35,42 @@ bool IsOccluded(const Element& element, const IntersectionGeometry& geometry) {
} // namespace } // namespace
// Minimum time, in milliseconds, between observations. See:
// http://szager-chromium.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility
const DOMHighResTimeStamp IntersectionObservation::s_v2_throttle_delay_ = 100;
IntersectionObservation::IntersectionObservation(IntersectionObserver& observer, IntersectionObservation::IntersectionObservation(IntersectionObserver& observer,
Element& target) Element& target)
: observer_(observer), : observer_(observer),
target_(&target), target_(&target),
// Note that the spec says the initial value of m_lastThresholdIndex last_run_time_(-s_v2_throttle_delay_),
// should be -1, but since m_lastThresholdIndex is unsigned, we use a
// different sentinel value.
last_is_visible_(false), last_is_visible_(false),
last_threshold_index_(kMaxThresholdIndex - 1) { // Note that the spec says the initial value of last_threshold_index_
UpdateShouldReportRootBoundsAfterDomChange(); // should be -1, but since last_threshold_index_ is unsigned, we use a
} // different sentinel value.
last_threshold_index_(kMaxThresholdIndex - 1) {}
void IntersectionObservation::ComputeIntersectionObservations( void IntersectionObservation::Compute(bool should_report_implicit_root_bounds) {
DOMHighResTimeStamp timestamp) {
DCHECK(Observer()); DCHECK(Observer());
if (!target_) if (!target_ || !observer_->RootIsValid() | !observer_->GetExecutionContext())
return; return;
DOMHighResTimeStamp timestamp = observer_->GetTimeStamp();
if (timestamp == -1)
return;
if (observer_->trackVisibility() &&
IntersectionObserver::V2ThrottleDelayEnabled() &&
timestamp - last_run_time_ < s_v2_throttle_delay_) {
return;
}
Vector<Length> root_margin(4); Vector<Length> root_margin(4);
root_margin[0] = observer_->TopMargin(); root_margin[0] = observer_->TopMargin();
root_margin[1] = observer_->RightMargin(); root_margin[1] = observer_->RightMargin();
root_margin[2] = observer_->BottomMargin(); root_margin[2] = observer_->BottomMargin();
root_margin[3] = observer_->LeftMargin(); root_margin[3] = observer_->LeftMargin();
bool should_report_root_bounds =
should_report_implicit_root_bounds || !observer_->RootIsImplicit();
IntersectionGeometry geometry(observer_->root(), *Target(), root_margin, IntersectionGeometry geometry(observer_->root(), *Target(), root_margin,
should_report_root_bounds_); should_report_root_bounds);
geometry.ComputeGeometry(); geometry.ComputeGeometry();
// Some corner cases for threshold index: // Some corner cases for threshold index:
...@@ -101,51 +114,43 @@ void IntersectionObservation::ComputeIntersectionObservations( ...@@ -101,51 +114,43 @@ void IntersectionObservation::ComputeIntersectionObservations(
} }
// TODO(tkent): We can't use CHECK_LT due to a compile error. // TODO(tkent): We can't use CHECK_LT due to a compile error.
CHECK(new_threshold_index < kMaxThresholdIndex); CHECK(new_threshold_index < kMaxThresholdIndex - 1);
if (last_threshold_index_ != new_threshold_index || if (last_threshold_index_ != new_threshold_index ||
last_is_visible_ != is_visible) { last_is_visible_ != is_visible) {
FloatRect snapped_root_bounds(geometry.RootRect()); FloatRect snapped_root_bounds(geometry.RootRect());
FloatRect* root_bounds_pointer = FloatRect* root_bounds_pointer =
should_report_root_bounds_ ? &snapped_root_bounds : nullptr; should_report_root_bounds ? &snapped_root_bounds : nullptr;
IntersectionObserverEntry* new_entry = new IntersectionObserverEntry( IntersectionObserverEntry* new_entry = new IntersectionObserverEntry(
timestamp, new_visible_ratio, FloatRect(geometry.TargetRect()), timestamp, new_visible_ratio, FloatRect(geometry.TargetRect()),
root_bounds_pointer, FloatRect(geometry.IntersectionRect()), root_bounds_pointer, FloatRect(geometry.IntersectionRect()),
new_threshold_index > 0, is_visible, Target()); new_threshold_index > 0, is_visible, Target());
Observer()->EnqueueIntersectionObserverEntry(*new_entry); entries_.push_back(new_entry);
ToDocument(Observer()->GetExecutionContext())
->EnsureIntersectionObserverController()
.ScheduleIntersectionObserverForDelivery(*Observer());
SetLastThresholdIndex(new_threshold_index); SetLastThresholdIndex(new_threshold_index);
SetWasVisible(is_visible); SetWasVisible(is_visible);
} }
} }
void IntersectionObservation::TakeRecords(
HeapVector<Member<IntersectionObserverEntry>>& entries) {
entries.AppendVector(entries_);
entries_.clear();
}
void IntersectionObservation::Disconnect() { void IntersectionObservation::Disconnect() {
DCHECK(Observer()); DCHECK(Observer());
if (target_) if (target_)
Target()->EnsureIntersectionObserverData().RemoveObservation(*Observer()); Target()->EnsureIntersectionObserverData().RemoveObservation(*Observer());
entries_.clear();
observer_.Clear(); observer_.Clear();
} }
void IntersectionObservation::UpdateShouldReportRootBoundsAfterDomChange() {
if (!Observer()->RootIsImplicit()) {
should_report_root_bounds_ = true;
return;
}
should_report_root_bounds_ = false;
LocalFrame* target_frame = Target()->GetDocument().GetFrame();
if (!target_frame)
return;
Frame& root_frame = target_frame->Tree().Top();
if (&root_frame == target_frame) {
should_report_root_bounds_ = true;
} else {
should_report_root_bounds_ =
target_frame->GetSecurityContext()->GetSecurityOrigin()->CanAccess(
root_frame.GetSecurityContext()->GetSecurityOrigin());
}
}
void IntersectionObservation::Trace(blink::Visitor* visitor) { void IntersectionObservation::Trace(blink::Visitor* visitor) {
visitor->Trace(observer_); visitor->Trace(observer_);
visitor->Trace(entries_);
visitor->Trace(target_); visitor->Trace(target_);
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVATION_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVATION_H_
#include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h" #include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink { namespace blink {
...@@ -25,9 +26,11 @@ class IntersectionObservation final ...@@ -25,9 +26,11 @@ class IntersectionObservation final
IntersectionObserver* Observer() const { return observer_.Get(); } IntersectionObserver* Observer() const { return observer_.Get(); }
Element* Target() const { return target_; } Element* Target() const { return target_; }
unsigned LastThresholdIndex() const { return last_threshold_index_; } unsigned LastThresholdIndex() const { return last_threshold_index_; }
void ComputeIntersectionObservations(DOMHighResTimeStamp); // If the parameter is true and the observer doesn't have an explicit root,
// then any notifications generated will contain root bounds geometry.
void Compute(bool should_report_implicit_root_bounds);
void TakeRecords(HeapVector<Member<IntersectionObserverEntry>>&);
void Disconnect(); void Disconnect();
void UpdateShouldReportRootBoundsAfterDomChange();
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
...@@ -37,13 +40,18 @@ class IntersectionObservation final ...@@ -37,13 +40,18 @@ class IntersectionObservation final
last_is_visible_ = last_is_visible ? 1 : 0; last_is_visible_ = last_is_visible ? 1 : 0;
} }
// If trackVisibility is true, don't compute observations more frequently
// than this many milliseconds.
static const DOMHighResTimeStamp s_v2_throttle_delay_;
Member<IntersectionObserver> observer_; Member<IntersectionObserver> observer_;
WeakMember<Element> target_; WeakMember<Element> target_;
HeapVector<Member<IntersectionObserverEntry>> entries_;
DOMHighResTimeStamp last_run_time_;
unsigned should_report_root_bounds_ : 1;
unsigned last_is_visible_ : 1; unsigned last_is_visible_ : 1;
unsigned last_threshold_index_ : 29; unsigned last_threshold_index_ : 31;
static const unsigned kMaxThresholdIndex = (unsigned)0x20000000; static const unsigned kMaxThresholdIndex = (unsigned)0x80000000;
}; };
} // namespace blink } // namespace blink
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_init.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/layout/layout_view.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h" #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/window_performance.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/timer.h" #include "third_party/blink/renderer/platform/timer.h"
...@@ -136,15 +135,6 @@ void ParseThresholds(const DoubleOrDoubleSequence& threshold_parameter, ...@@ -136,15 +135,6 @@ void ParseThresholds(const DoubleOrDoubleSequence& threshold_parameter,
} // anonymous namespace } // anonymous namespace
// Minimum time, in milliseconds, between observations. See:
// http://szager-chromium.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility
const DOMHighResTimeStamp IntersectionObserver::s_v2_throttle_delay_ = 100;
static bool v2_throttle_delay_enabled = true;
void IntersectionObserver::SetV2ThrottleDelayEnabledForTesting(bool enabled) {
v2_throttle_delay_enabled = enabled;
}
IntersectionObserver* IntersectionObserver::Create( IntersectionObserver* IntersectionObserver::Create(
const IntersectionObserverInit& observer_init, const IntersectionObserverInit& observer_init,
IntersectionObserverDelegate& delegate, IntersectionObserverDelegate& delegate,
...@@ -206,7 +196,6 @@ IntersectionObserver::IntersectionObserver( ...@@ -206,7 +196,6 @@ IntersectionObserver::IntersectionObserver(
right_margin_(kFixed), right_margin_(kFixed),
bottom_margin_(kFixed), bottom_margin_(kFixed),
left_margin_(kFixed), left_margin_(kFixed),
last_run_time_(-s_v2_throttle_delay_),
root_is_implicit_(root ? 0 : 1), root_is_implicit_(root ? 0 : 1),
track_visibility_(track_visibility ? 1 : 0) { track_visibility_(track_visibility ? 1 : 0) {
switch (root_margin.size()) { switch (root_margin.size()) {
...@@ -235,10 +224,6 @@ IntersectionObserver::IntersectionObserver( ...@@ -235,10 +224,6 @@ IntersectionObserver::IntersectionObserver(
NOTREACHED(); NOTREACHED();
break; break;
} }
if (root)
root->EnsureIntersectionObserverData().AddObserver(*this);
if (Document* document = TrackingDocument())
document->EnsureIntersectionObserverController().AddTrackedObserver(*this);
} }
void IntersectionObserver::ClearWeakMembers(Visitor* visitor) { void IntersectionObserver::ClearWeakMembers(Visitor* visitor) {
...@@ -253,16 +238,6 @@ bool IntersectionObserver::RootIsValid() const { ...@@ -253,16 +238,6 @@ bool IntersectionObserver::RootIsValid() const {
return RootIsImplicit() || root(); return RootIsImplicit() || root();
} }
Document* IntersectionObserver::TrackingDocument() const {
if (RootIsImplicit()) {
if (!delegate_->GetExecutionContext())
return nullptr;
return ToDocument(delegate_->GetExecutionContext());
}
DCHECK(root());
return &root()->GetDocument();
}
void IntersectionObserver::observe(Element* target, void IntersectionObserver::observe(Element* target,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (!RootIsValid()) if (!RootIsValid())
...@@ -282,12 +257,21 @@ void IntersectionObserver::observe(Element* target, ...@@ -282,12 +257,21 @@ void IntersectionObserver::observe(Element* target,
new IntersectionObservation(*this, *target); new IntersectionObservation(*this, *target);
target->EnsureIntersectionObserverData().AddObservation(*observation); target->EnsureIntersectionObserverData().AddObservation(*observation);
observations_.insert(observation); observations_.insert(observation);
if (target->isConnected()) {
target->GetDocument()
.EnsureIntersectionObserverController()
.AddTrackedTarget(*target);
if (LocalFrameView* frame_view = target_frame->View()) { if (LocalFrameView* frame_view = target_frame->View()) {
// The IntersectionObsever spec requires that at least one observation // The IntersectionObsever spec requires that at least one observation
// be recorded afer observe() is called, even if the frame is throttled. // be recorded after observe() is called, even if the frame is throttled.
frame_view->SetNeedsIntersectionObservation(LocalFrameView::kRequired); frame_view->SetNeedsIntersectionObservation(LocalFrameView::kRequired);
frame_view->ScheduleAnimation(); frame_view->ScheduleAnimation();
} }
} else {
// The IntersectionObsever spec requires that at least one observation
// be recorded after observe() is called, even if the target is detached.
observation->Compute(false);
}
} }
void IntersectionObserver::unobserve(Element* target, void IntersectionObserver::unobserve(Element* target,
...@@ -302,46 +286,19 @@ void IntersectionObserver::unobserve(Element* target, ...@@ -302,46 +286,19 @@ void IntersectionObserver::unobserve(Element* target,
observation->Disconnect(); observation->Disconnect();
observations_.erase(observation); observations_.erase(observation);
for (size_t i = 0; i < entries_.size(); ++i) {
if (entries_[i]->target() == target) {
entries_.EraseAt(i);
--i;
}
}
}
void IntersectionObserver::ComputeIntersectionObservations() {
if (!RootIsValid() || !delegate_->GetExecutionContext())
return;
Document* delegate_document = ToDocument(delegate_->GetExecutionContext());
if (!delegate_document || delegate_document->IsStopped())
return;
LocalDOMWindow* delegate_dom_window = delegate_document->domWindow();
if (!delegate_dom_window)
return;
DOMHighResTimeStamp timestamp =
DOMWindowPerformance::performance(*delegate_dom_window)->now();
if (track_visibility_ && v2_throttle_delay_enabled &&
timestamp - last_run_time_ < s_v2_throttle_delay_) {
return;
}
last_run_time_ = timestamp;
for (auto& observation : observations_)
observation->ComputeIntersectionObservations(last_run_time_);
} }
void IntersectionObserver::disconnect(ExceptionState& exception_state) { void IntersectionObserver::disconnect(ExceptionState& exception_state) {
for (auto& observation : observations_) for (auto& observation : observations_)
observation->Disconnect(); observation->Disconnect();
observations_.clear(); observations_.clear();
entries_.clear();
} }
HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords(
ExceptionState& exception_state) { ExceptionState& exception_state) {
HeapVector<Member<IntersectionObserverEntry>> entries; HeapVector<Member<IntersectionObserverEntry>> entries;
entries.swap(entries_); for (auto& observation : observations_)
observation->TakeRecords(entries);
return entries; return entries;
} }
...@@ -365,13 +322,12 @@ String IntersectionObserver::rootMargin() const { ...@@ -365,13 +322,12 @@ String IntersectionObserver::rootMargin() const {
return string_builder.ToString(); return string_builder.ToString();
} }
void IntersectionObserver::EnqueueIntersectionObserverEntry( DOMHighResTimeStamp IntersectionObserver::GetTimeStamp() const {
IntersectionObserverEntry& entry) { if (Document* document = ToDocument(delegate_->GetExecutionContext())) {
DCHECK(delegate_->GetExecutionContext()); if (LocalDOMWindow* dom_window = document->domWindow())
entries_.push_back(&entry); return DOMWindowPerformance::performance(*dom_window)->now();
ToDocument(delegate_->GetExecutionContext()) }
->EnsureIntersectionObserverController() return -1;
.ScheduleIntersectionObserverForDelivery(*this);
} }
unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const { unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const {
...@@ -382,11 +338,10 @@ unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const { ...@@ -382,11 +338,10 @@ unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const {
} }
void IntersectionObserver::Deliver() { void IntersectionObserver::Deliver() {
if (entries_.IsEmpty())
return;
HeapVector<Member<IntersectionObserverEntry>> entries; HeapVector<Member<IntersectionObserverEntry>> entries;
entries.swap(entries_); for (auto& observation : observations_)
observation->TakeRecords(entries);
if (entries.size())
delegate_->Deliver(entries, *this); delegate_->Deliver(entries, *this);
} }
...@@ -399,9 +354,18 @@ void IntersectionObserver::Trace(blink::Visitor* visitor) { ...@@ -399,9 +354,18 @@ void IntersectionObserver::Trace(blink::Visitor* visitor) {
IntersectionObserver, &IntersectionObserver::ClearWeakMembers>(this); IntersectionObserver, &IntersectionObserver::ClearWeakMembers>(this);
visitor->Trace(delegate_); visitor->Trace(delegate_);
visitor->Trace(observations_); visitor->Trace(observations_);
visitor->Trace(entries_);
ScriptWrappable::Trace(visitor); ScriptWrappable::Trace(visitor);
ContextClient::Trace(visitor); ContextClient::Trace(visitor);
} }
static bool v2_throttle_delay_enabled = true;
bool IntersectionObserver::V2ThrottleDelayEnabled() {
return v2_throttle_delay_enabled;
}
void IntersectionObserver::SetV2ThrottleDelayEnabledForTesting(bool enabled) {
v2_throttle_delay_enabled = enabled;
}
} // namespace blink } // namespace blink
...@@ -73,24 +73,17 @@ class CORE_EXPORT IntersectionObserver final ...@@ -73,24 +73,17 @@ class CORE_EXPORT IntersectionObserver final
// root just because m_root is null. Hence m_rootIsImplicit. // root just because m_root is null. Hence m_rootIsImplicit.
bool RootIsImplicit() const { return root_is_implicit_; } bool RootIsImplicit() const { return root_is_implicit_; }
// This is the document which is responsible for running DOMHighResTimeStamp GetTimeStamp() const;
// computeIntersectionObservations at frame generation time.
// This can return nullptr when no tracking document is available.
Document* TrackingDocument() const;
const Length& TopMargin() const { return top_margin_; } const Length& TopMargin() const { return top_margin_; }
const Length& RightMargin() const { return right_margin_; } const Length& RightMargin() const { return right_margin_; }
const Length& BottomMargin() const { return bottom_margin_; } const Length& BottomMargin() const { return bottom_margin_; }
const Length& LeftMargin() const { return left_margin_; } const Length& LeftMargin() const { return left_margin_; }
void ComputeIntersectionObservations();
void EnqueueIntersectionObserverEntry(IntersectionObserverEntry&);
unsigned FirstThresholdGreaterThan(float ratio) const; unsigned FirstThresholdGreaterThan(float ratio) const;
void Deliver(); void Deliver();
bool HasEntries() const { return entries_.size(); }
const HeapLinkedHashSet<WeakMember<IntersectionObservation>>& Observations() // Returns false if this observer has an explicit root element which has been
const { // deleted; true otherwise.
return observations_; bool RootIsValid() const;
}
// ScriptWrappable override: // ScriptWrappable override:
bool HasPendingActivity() const override; bool HasPendingActivity() const override;
...@@ -99,6 +92,7 @@ class CORE_EXPORT IntersectionObserver final ...@@ -99,6 +92,7 @@ class CORE_EXPORT IntersectionObserver final
// Enable/disable throttling of visibility checking, so we don't have to add // Enable/disable throttling of visibility checking, so we don't have to add
// 100ms sleep() calls to tests. // 100ms sleep() calls to tests.
static bool V2ThrottleDelayEnabled();
static void SetV2ThrottleDelayEnabledForTesting(bool); static void SetV2ThrottleDelayEnabledForTesting(bool);
private: private:
...@@ -109,24 +103,14 @@ class CORE_EXPORT IntersectionObserver final ...@@ -109,24 +103,14 @@ class CORE_EXPORT IntersectionObserver final
bool track_visibility); bool track_visibility);
void ClearWeakMembers(Visitor*); void ClearWeakMembers(Visitor*);
// Returns false if this observer has an explicit root element which has been
// deleted; true otherwise.
bool RootIsValid() const;
// If trackVisibility is true, don't compute observations more frequently
// than this many milliseconds.
static const DOMHighResTimeStamp s_v2_throttle_delay_;
const TraceWrapperMember<IntersectionObserverDelegate> delegate_; const TraceWrapperMember<IntersectionObserverDelegate> delegate_;
WeakMember<Element> root_; WeakMember<Element> root_;
HeapLinkedHashSet<WeakMember<IntersectionObservation>> observations_; HeapLinkedHashSet<WeakMember<IntersectionObservation>> observations_;
HeapVector<Member<IntersectionObserverEntry>> entries_;
Vector<float> thresholds_; Vector<float> thresholds_;
Length top_margin_; Length top_margin_;
Length right_margin_; Length right_margin_;
Length bottom_margin_; Length bottom_margin_;
Length left_margin_; Length left_margin_;
DOMHighResTimeStamp last_run_time_;
unsigned root_is_implicit_ : 1; unsigned root_is_implicit_ : 1;
unsigned track_visibility_ : 1; unsigned track_visibility_ : 1;
}; };
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
namespace blink { namespace blink {
...@@ -74,32 +76,39 @@ void IntersectionObserverController::DeliverIntersectionObservations() { ...@@ -74,32 +76,39 @@ void IntersectionObserverController::DeliverIntersectionObservations() {
} }
void IntersectionObserverController::ComputeTrackedIntersectionObservations() { void IntersectionObserverController::ComputeTrackedIntersectionObservations() {
if (!GetExecutionContext()) if (Document* document = ToDocument(GetExecutionContext())) {
return; TRACE_EVENT0("blink",
TRACE_EVENT0( "IntersectionObserverController::"
"blink", "computeTrackedIntersectionObservations");
"IntersectionObserverController::computeTrackedIntersectionObservations"); bool should_report_implicit_root_bounds = false;
for (auto& observer : tracked_intersection_observers_) if (LocalFrame* target_frame = document->GetFrame()) {
observer->ComputeIntersectionObservations(); Frame& root_frame = target_frame->Tree().Top();
if (&root_frame == target_frame) {
should_report_implicit_root_bounds = true;
} else {
should_report_implicit_root_bounds =
target_frame->GetSecurityContext()->GetSecurityOrigin()->CanAccess(
root_frame.GetSecurityContext()->GetSecurityOrigin());
}
}
for (auto& element : tracked_observation_targets_) {
element->ComputeIntersectionObservations(
should_report_implicit_root_bounds);
}
}
} }
void IntersectionObserverController::AddTrackedObserver( void IntersectionObserverController::AddTrackedTarget(Element& target) {
IntersectionObserver& observer) { tracked_observation_targets_.insert(&target);
tracked_intersection_observers_.insert(&observer);
} }
void IntersectionObserverController::RemoveTrackedObserversForRoot( void IntersectionObserverController::RemoveTrackedTarget(Element& target) {
const Node& root) { target.ComputeIntersectionObservations(false);
HeapVector<Member<IntersectionObserver>> to_remove; tracked_observation_targets_.erase(&target);
for (auto& observer : tracked_intersection_observers_) {
if (observer->root() == &root)
to_remove.push_back(observer);
}
tracked_intersection_observers_.RemoveAll(to_remove);
} }
void IntersectionObserverController::Trace(blink::Visitor* visitor) { void IntersectionObserverController::Trace(blink::Visitor* visitor) {
visitor->Trace(tracked_intersection_observers_); visitor->Trace(tracked_observation_targets_);
visitor->Trace(pending_intersection_observers_); visitor->Trace(pending_intersection_observers_);
visitor->Trace(intersection_observers_being_invoked_); visitor->Trace(intersection_observers_being_invoked_);
PausableObject::Trace(visitor); PausableObject::Trace(visitor);
......
...@@ -34,8 +34,8 @@ class IntersectionObserverController ...@@ -34,8 +34,8 @@ class IntersectionObserverController
void ScheduleIntersectionObserverForDelivery(IntersectionObserver&); void ScheduleIntersectionObserverForDelivery(IntersectionObserver&);
void DeliverIntersectionObservations(); void DeliverIntersectionObservations();
void ComputeTrackedIntersectionObservations(); void ComputeTrackedIntersectionObservations();
void AddTrackedObserver(IntersectionObserver&); void AddTrackedTarget(Element&);
void RemoveTrackedObserversForRoot(const Node&); void RemoveTrackedTarget(Element&);
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
const char* NameInHeapSnapshot() const override { const char* NameInHeapSnapshot() const override {
...@@ -47,8 +47,9 @@ class IntersectionObserverController ...@@ -47,8 +47,9 @@ class IntersectionObserverController
void PostTaskToDeliverObservations(); void PostTaskToDeliverObservations();
private: private:
// IntersectionObservers for which this is the tracking document. // Elements in this document which are the target of an
HeapHashSet<WeakMember<IntersectionObserver>> tracked_intersection_observers_; // IntersectionObservation.
HeapHashSet<WeakMember<Element>> tracked_observation_targets_;
// IntersectionObservers for which this is the execution context of the // IntersectionObservers for which this is the execution context of the
// callback. // callback.
HeapHashSet<TraceWrapperMember<IntersectionObserver>> HeapHashSet<TraceWrapperMember<IntersectionObserver>>
......
...@@ -99,6 +99,51 @@ TEST_F(IntersectionObserverTest, ObserveSchedulesFrame) { ...@@ -99,6 +99,51 @@ TEST_F(IntersectionObserverTest, ObserveSchedulesFrame) {
EXPECT_TRUE(Compositor().NeedsBeginFrame()); EXPECT_TRUE(Compositor().NeedsBeginFrame());
} }
TEST_F(IntersectionObserverTest, NotificationSentWhenRootRemoved) {
SimRequest main_resource("https://example.com/", "text/html");
LoadURL("https://example.com/");
main_resource.Complete(R"HTML(
<style>
#target {
width: 100px;
height: 100px;
}
</style>
<div id='root'>
<div id='target'></div>
</div>
)HTML");
Element* root = GetDocument().getElementById("root");
ASSERT_TRUE(root);
IntersectionObserverInit observer_init;
observer_init.setRoot(root);
DummyExceptionStateForTesting exception_state;
TestIntersectionObserverDelegate* observer_delegate =
new TestIntersectionObserverDelegate(GetDocument());
IntersectionObserver* observer = IntersectionObserver::Create(
observer_init, *observer_delegate, exception_state);
ASSERT_FALSE(exception_state.HadException());
Element* target = GetDocument().getElementById("target");
ASSERT_TRUE(target);
observer->observe(target, exception_state);
Compositor().BeginFrame();
test::RunPendingTasks();
ASSERT_FALSE(Compositor().NeedsBeginFrame());
EXPECT_EQ(observer_delegate->CallCount(), 1);
EXPECT_EQ(observer_delegate->EntryCount(), 1);
EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
root->remove();
Compositor().BeginFrame();
test::RunPendingTasks();
ASSERT_FALSE(Compositor().NeedsBeginFrame());
EXPECT_EQ(observer_delegate->CallCount(), 2);
EXPECT_EQ(observer_delegate->EntryCount(), 2);
EXPECT_FALSE(observer_delegate->LastEntry()->isIntersecting());
}
TEST_F(IntersectionObserverTest, ResumePostsTask) { TEST_F(IntersectionObserverTest, ResumePostsTask) {
WebView().Resize(WebSize(800, 600)); WebView().Resize(WebSize(800, 600));
SimRequest main_resource("https://example.com/", "text/html"); SimRequest main_resource("https://example.com/", "text/html");
......
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