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 @@
#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/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/layout/adjust_for_absolute_zoom.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
......@@ -2045,9 +2046,11 @@ Node::InsertionNotificationRequest Element::InsertedInto(
if (HasRareData()) {
ElementRareData* rare_data = GetElementRareData();
if (rare_data->IntersectionObserverData())
rare_data->IntersectionObserverData()->ActivateValidIntersectionObservers(
if (rare_data->IntersectionObserverData() &&
rare_data->IntersectionObserverData()->HasObservations()) {
GetDocument().EnsureIntersectionObserverController().AddTrackedTarget(
*this);
}
}
if (isConnected()) {
......@@ -2148,9 +2151,10 @@ void Element::RemovedFrom(ContainerNode& insertion_point) {
if (ElementAnimations* element_animations = data->GetElementAnimations())
element_animations->CssAnimations().Cancel();
if (data->IntersectionObserverData())
data->IntersectionObserverData()->DeactivateAllIntersectionObservers(
if (data->IntersectionObserverData()) {
GetDocument().EnsureIntersectionObserverController().RemoveTrackedTarget(
*this);
}
}
if (GetDocument().GetFrame())
......@@ -3647,6 +3651,12 @@ ElementIntersectionObserverData& Element::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>>*
Element::ResizeObserverData() const {
if (HasRareData())
......
......@@ -30,6 +30,7 @@
#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/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/names_map.h"
#include "third_party/blink/renderer/core/dom/whitespace_attacher.h"
......@@ -897,6 +898,7 @@ class CORE_EXPORT Element : public ContainerNode {
ElementIntersectionObserverData* IntersectionObserverData() const;
ElementIntersectionObserverData& EnsureIntersectionObserverData();
void ComputeIntersectionObservations(bool);
HeapHashMap<TraceWrapperMember<ResizeObserver>, Member<ResizeObservation>>*
ResizeObserverData() const;
......
......@@ -36,6 +36,7 @@
#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_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/loader/fetch/access_control_status.h"
#include "third_party/blink/renderer/platform/supplementable.h"
......@@ -61,7 +62,6 @@ class EventTarget;
class FrameOrWorkerScheduler;
class InterfaceInvalidator;
class KURL;
class LocalDOMWindow;
class PausableObject;
class PublicURLManager;
class ResourceFetcher;
......
......@@ -21,16 +21,6 @@ IntersectionObservation* ElementIntersectionObserverData::GetObservationFor(
return i->value;
}
void ElementIntersectionObserverData::AddObserver(
IntersectionObserver& observer) {
intersection_observers_.insert(&observer);
}
void ElementIntersectionObserverData::RemoveObserver(
IntersectionObserver& observer) {
intersection_observers_.erase(&observer);
}
void ElementIntersectionObserverData::AddObservation(
IntersectionObservation& observation) {
DCHECK(observation.Observer());
......@@ -42,28 +32,13 @@ void ElementIntersectionObserverData::RemoveObservation(
intersection_observations_.erase(&observer);
}
void ElementIntersectionObserverData::ActivateValidIntersectionObservers(
Node& node) {
for (auto& observer : intersection_observers_) {
Document* document = observer->TrackingDocument();
if (!document)
continue;
document->EnsureIntersectionObserverController().AddTrackedObserver(
*observer);
}
void ElementIntersectionObserverData::ComputeObservations(
bool should_report_implicit_root_bounds) {
for (auto& observation : intersection_observations_)
observation.value->UpdateShouldReportRootBoundsAfterDomChange();
}
void ElementIntersectionObserverData::DeactivateAllIntersectionObservers(
Node& node) {
node.GetDocument()
.EnsureIntersectionObserverController()
.RemoveTrackedObserversForRoot(node);
observation.value->Compute(should_report_implicit_root_bounds);
}
void ElementIntersectionObserverData::Trace(blink::Visitor* visitor) {
visitor->Trace(intersection_observers_);
visitor->Trace(intersection_observations_);
}
......
......@@ -5,13 +5,13 @@
#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_
#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/trace_wrapper_member.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink {
class Node;
class IntersectionObservation;
class IntersectionObserver;
......@@ -22,12 +22,10 @@ class ElementIntersectionObserverData
ElementIntersectionObserverData();
IntersectionObservation* GetObservationFor(IntersectionObserver&);
void AddObserver(IntersectionObserver&);
void RemoveObserver(IntersectionObserver&);
void AddObservation(IntersectionObservation&);
void RemoveObservation(IntersectionObserver&);
void ActivateValidIntersectionObservers(Node&);
void DeactivateAllIntersectionObservers(Node&);
bool HasObservations() const { return !intersection_observations_.IsEmpty(); }
void ComputeObservations(bool);
void Trace(blink::Visitor*);
const char* NameInHeapSnapshot() const override {
......@@ -35,8 +33,6 @@ class ElementIntersectionObserverData
}
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.
HeapHashMap<TraceWrapperMember<IntersectionObserver>,
Member<IntersectionObservation>>
......
......@@ -7,6 +7,7 @@
#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/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/intersection_geometry.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
......@@ -34,30 +35,42 @@ bool IsOccluded(const Element& element, const IntersectionGeometry& geometry) {
} // 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,
Element& target)
: observer_(observer),
target_(&target),
// Note that the spec says the initial value of m_lastThresholdIndex
// should be -1, but since m_lastThresholdIndex is unsigned, we use a
// different sentinel value.
last_run_time_(-s_v2_throttle_delay_),
last_is_visible_(false),
last_threshold_index_(kMaxThresholdIndex - 1) {
UpdateShouldReportRootBoundsAfterDomChange();
}
// Note that the spec says the initial value of last_threshold_index_
// should be -1, but since last_threshold_index_ is unsigned, we use a
// different sentinel value.
last_threshold_index_(kMaxThresholdIndex - 1) {}
void IntersectionObservation::ComputeIntersectionObservations(
DOMHighResTimeStamp timestamp) {
void IntersectionObservation::Compute(bool should_report_implicit_root_bounds) {
DCHECK(Observer());
if (!target_)
if (!target_ || !observer_->RootIsValid() | !observer_->GetExecutionContext())
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);
root_margin[0] = observer_->TopMargin();
root_margin[1] = observer_->RightMargin();
root_margin[2] = observer_->BottomMargin();
root_margin[3] = observer_->LeftMargin();
bool should_report_root_bounds =
should_report_implicit_root_bounds || !observer_->RootIsImplicit();
IntersectionGeometry geometry(observer_->root(), *Target(), root_margin,
should_report_root_bounds_);
should_report_root_bounds);
geometry.ComputeGeometry();
// Some corner cases for threshold index:
......@@ -101,51 +114,43 @@ void IntersectionObservation::ComputeIntersectionObservations(
}
// 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 ||
last_is_visible_ != is_visible) {
FloatRect snapped_root_bounds(geometry.RootRect());
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(
timestamp, new_visible_ratio, FloatRect(geometry.TargetRect()),
root_bounds_pointer, FloatRect(geometry.IntersectionRect()),
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);
SetWasVisible(is_visible);
}
}
void IntersectionObservation::TakeRecords(
HeapVector<Member<IntersectionObserverEntry>>& entries) {
entries.AppendVector(entries_);
entries_.clear();
}
void IntersectionObservation::Disconnect() {
DCHECK(Observer());
if (target_)
Target()->EnsureIntersectionObserverData().RemoveObservation(*Observer());
entries_.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) {
visitor->Trace(observer_);
visitor->Trace(entries_);
visitor->Trace(target_);
}
......
......@@ -6,6 +6,7 @@
#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/intersection_observer/intersection_observer_entry.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink {
......@@ -25,9 +26,11 @@ class IntersectionObservation final
IntersectionObserver* Observer() const { return observer_.Get(); }
Element* Target() const { return target_; }
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 UpdateShouldReportRootBoundsAfterDomChange();
void Trace(blink::Visitor*);
......@@ -37,13 +40,18 @@ class IntersectionObservation final
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_;
WeakMember<Element> target_;
HeapVector<Member<IntersectionObserverEntry>> entries_;
DOMHighResTimeStamp last_run_time_;
unsigned should_report_root_bounds_ : 1;
unsigned last_is_visible_ : 1;
unsigned last_threshold_index_ : 29;
static const unsigned kMaxThresholdIndex = (unsigned)0x20000000;
unsigned last_threshold_index_ : 31;
static const unsigned kMaxThresholdIndex = (unsigned)0x80000000;
};
} // namespace blink
......
......@@ -24,7 +24,6 @@
#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"
#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/timer.h"
......@@ -136,15 +135,6 @@ void ParseThresholds(const DoubleOrDoubleSequence& threshold_parameter,
} // 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(
const IntersectionObserverInit& observer_init,
IntersectionObserverDelegate& delegate,
......@@ -206,7 +196,6 @@ IntersectionObserver::IntersectionObserver(
right_margin_(kFixed),
bottom_margin_(kFixed),
left_margin_(kFixed),
last_run_time_(-s_v2_throttle_delay_),
root_is_implicit_(root ? 0 : 1),
track_visibility_(track_visibility ? 1 : 0) {
switch (root_margin.size()) {
......@@ -235,10 +224,6 @@ IntersectionObserver::IntersectionObserver(
NOTREACHED();
break;
}
if (root)
root->EnsureIntersectionObserverData().AddObserver(*this);
if (Document* document = TrackingDocument())
document->EnsureIntersectionObserverController().AddTrackedObserver(*this);
}
void IntersectionObserver::ClearWeakMembers(Visitor* visitor) {
......@@ -253,16 +238,6 @@ bool IntersectionObserver::RootIsValid() const {
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,
ExceptionState& exception_state) {
if (!RootIsValid())
......@@ -282,11 +257,20 @@ void IntersectionObserver::observe(Element* target,
new IntersectionObservation(*this, *target);
target->EnsureIntersectionObserverData().AddObservation(*observation);
observations_.insert(observation);
if (LocalFrameView* frame_view = target_frame->View()) {
if (target->isConnected()) {
target->GetDocument()
.EnsureIntersectionObserverController()
.AddTrackedTarget(*target);
if (LocalFrameView* frame_view = target_frame->View()) {
// The IntersectionObsever spec requires that at least one observation
// be recorded after observe() is called, even if the frame is throttled.
frame_view->SetNeedsIntersectionObservation(LocalFrameView::kRequired);
frame_view->ScheduleAnimation();
}
} else {
// The IntersectionObsever spec requires that at least one observation
// be recorded afer observe() is called, even if the frame is throttled.
frame_view->SetNeedsIntersectionObservation(LocalFrameView::kRequired);
frame_view->ScheduleAnimation();
// be recorded after observe() is called, even if the target is detached.
observation->Compute(false);
}
}
......@@ -302,46 +286,19 @@ void IntersectionObserver::unobserve(Element* target,
observation->Disconnect();
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) {
for (auto& observation : observations_)
observation->Disconnect();
observations_.clear();
entries_.clear();
}
HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords(
ExceptionState& exception_state) {
HeapVector<Member<IntersectionObserverEntry>> entries;
entries.swap(entries_);
for (auto& observation : observations_)
observation->TakeRecords(entries);
return entries;
}
......@@ -365,13 +322,12 @@ String IntersectionObserver::rootMargin() const {
return string_builder.ToString();
}
void IntersectionObserver::EnqueueIntersectionObserverEntry(
IntersectionObserverEntry& entry) {
DCHECK(delegate_->GetExecutionContext());
entries_.push_back(&entry);
ToDocument(delegate_->GetExecutionContext())
->EnsureIntersectionObserverController()
.ScheduleIntersectionObserverForDelivery(*this);
DOMHighResTimeStamp IntersectionObserver::GetTimeStamp() const {
if (Document* document = ToDocument(delegate_->GetExecutionContext())) {
if (LocalDOMWindow* dom_window = document->domWindow())
return DOMWindowPerformance::performance(*dom_window)->now();
}
return -1;
}
unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const {
......@@ -382,12 +338,11 @@ unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const {
}
void IntersectionObserver::Deliver() {
if (entries_.IsEmpty())
return;
HeapVector<Member<IntersectionObserverEntry>> entries;
entries.swap(entries_);
delegate_->Deliver(entries, *this);
for (auto& observation : observations_)
observation->TakeRecords(entries);
if (entries.size())
delegate_->Deliver(entries, *this);
}
bool IntersectionObserver::HasPendingActivity() const {
......@@ -399,9 +354,18 @@ void IntersectionObserver::Trace(blink::Visitor* visitor) {
IntersectionObserver, &IntersectionObserver::ClearWeakMembers>(this);
visitor->Trace(delegate_);
visitor->Trace(observations_);
visitor->Trace(entries_);
ScriptWrappable::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
......@@ -73,24 +73,17 @@ class CORE_EXPORT IntersectionObserver final
// root just because m_root is null. Hence m_rootIsImplicit.
bool RootIsImplicit() const { return root_is_implicit_; }
// This is the document which is responsible for running
// computeIntersectionObservations at frame generation time.
// This can return nullptr when no tracking document is available.
Document* TrackingDocument() const;
DOMHighResTimeStamp GetTimeStamp() const;
const Length& TopMargin() const { return top_margin_; }
const Length& RightMargin() const { return right_margin_; }
const Length& BottomMargin() const { return bottom_margin_; }
const Length& LeftMargin() const { return left_margin_; }
void ComputeIntersectionObservations();
void EnqueueIntersectionObserverEntry(IntersectionObserverEntry&);
unsigned FirstThresholdGreaterThan(float ratio) const;
void Deliver();
bool HasEntries() const { return entries_.size(); }
const HeapLinkedHashSet<WeakMember<IntersectionObservation>>& Observations()
const {
return observations_;
}
// Returns false if this observer has an explicit root element which has been
// deleted; true otherwise.
bool RootIsValid() const;
// ScriptWrappable override:
bool HasPendingActivity() const override;
......@@ -99,6 +92,7 @@ class CORE_EXPORT IntersectionObserver final
// Enable/disable throttling of visibility checking, so we don't have to add
// 100ms sleep() calls to tests.
static bool V2ThrottleDelayEnabled();
static void SetV2ThrottleDelayEnabledForTesting(bool);
private:
......@@ -109,24 +103,14 @@ class CORE_EXPORT IntersectionObserver final
bool track_visibility);
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_;
WeakMember<Element> root_;
HeapLinkedHashSet<WeakMember<IntersectionObservation>> observations_;
HeapVector<Member<IntersectionObserverEntry>> entries_;
Vector<float> thresholds_;
Length top_margin_;
Length right_margin_;
Length bottom_margin_;
Length left_margin_;
DOMHighResTimeStamp last_run_time_;
unsigned root_is_implicit_ : 1;
unsigned track_visibility_ : 1;
};
......
......@@ -7,6 +7,8 @@
#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/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"
namespace blink {
......@@ -74,32 +76,39 @@ void IntersectionObserverController::DeliverIntersectionObservations() {
}
void IntersectionObserverController::ComputeTrackedIntersectionObservations() {
if (!GetExecutionContext())
return;
TRACE_EVENT0(
"blink",
"IntersectionObserverController::computeTrackedIntersectionObservations");
for (auto& observer : tracked_intersection_observers_)
observer->ComputeIntersectionObservations();
if (Document* document = ToDocument(GetExecutionContext())) {
TRACE_EVENT0("blink",
"IntersectionObserverController::"
"computeTrackedIntersectionObservations");
bool should_report_implicit_root_bounds = false;
if (LocalFrame* target_frame = document->GetFrame()) {
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(
IntersectionObserver& observer) {
tracked_intersection_observers_.insert(&observer);
void IntersectionObserverController::AddTrackedTarget(Element& target) {
tracked_observation_targets_.insert(&target);
}
void IntersectionObserverController::RemoveTrackedObserversForRoot(
const Node& root) {
HeapVector<Member<IntersectionObserver>> to_remove;
for (auto& observer : tracked_intersection_observers_) {
if (observer->root() == &root)
to_remove.push_back(observer);
}
tracked_intersection_observers_.RemoveAll(to_remove);
void IntersectionObserverController::RemoveTrackedTarget(Element& target) {
target.ComputeIntersectionObservations(false);
tracked_observation_targets_.erase(&target);
}
void IntersectionObserverController::Trace(blink::Visitor* visitor) {
visitor->Trace(tracked_intersection_observers_);
visitor->Trace(tracked_observation_targets_);
visitor->Trace(pending_intersection_observers_);
visitor->Trace(intersection_observers_being_invoked_);
PausableObject::Trace(visitor);
......
......@@ -34,8 +34,8 @@ class IntersectionObserverController
void ScheduleIntersectionObserverForDelivery(IntersectionObserver&);
void DeliverIntersectionObservations();
void ComputeTrackedIntersectionObservations();
void AddTrackedObserver(IntersectionObserver&);
void RemoveTrackedObserversForRoot(const Node&);
void AddTrackedTarget(Element&);
void RemoveTrackedTarget(Element&);
void Trace(blink::Visitor*) override;
const char* NameInHeapSnapshot() const override {
......@@ -47,8 +47,9 @@ class IntersectionObserverController
void PostTaskToDeliverObservations();
private:
// IntersectionObservers for which this is the tracking document.
HeapHashSet<WeakMember<IntersectionObserver>> tracked_intersection_observers_;
// Elements in this document which are the target of an
// IntersectionObservation.
HeapHashSet<WeakMember<Element>> tracked_observation_targets_;
// IntersectionObservers for which this is the execution context of the
// callback.
HeapHashSet<TraceWrapperMember<IntersectionObserver>>
......
......@@ -99,6 +99,51 @@ TEST_F(IntersectionObserverTest, ObserveSchedulesFrame) {
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) {
WebView().Resize(WebSize(800, 600));
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