Commit e23e774e authored by Victor Costan's avatar Victor Costan Committed by Commit Bot

Blink: Introduce LifecycleNotifier::ForEachObserver().

LifecycleNotifier currently exposes its internal state (observers_,
iteration_state_) to subclasses, and relies on the subclasses to
correctly update iteration_state_ while iterating over observers_, like
PageVisibilityNotifier::NotifyPageVisibilityChanged() does. Sadly, it's
easy to get this wrong, as shown by SynchronousMutationNotifier (does
not update iteration_state_ at all) and ContextLifecycleNotifier (only
updates iteration_state_ in some cases).

This CL makes the LifecycleNotifier state private, and adds a
ForEachObserver() method that safely iterates over observers_,
encapsulating the needed changes to iteration_state_ and a cast from
LifecycleObserverBase* to the desired observer type.

Change-Id: Ibb23f2c7e2bba9bdf08d49e8569a354934ed4e11
Reviewed-on: https://chromium-review.googlesource.com/1132674Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Commit-Queue: Victor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#574076}
parent 0439ecc7
...@@ -33,67 +33,58 @@ ...@@ -33,67 +33,58 @@
namespace blink { namespace blink {
void ContextLifecycleNotifier::NotifyResumingPausableObjects() { void ContextLifecycleNotifier::NotifyResumingPausableObjects() {
base::AutoReset<IterationState> scope(&iteration_state_, kAllowingNone); ForEachObserver([&](ContextLifecycleObserver* observer) {
for (LifecycleObserverBase* observer_base : observers_) {
ContextLifecycleObserver* observer =
static_cast<ContextLifecycleObserver*>(observer_base);
if (observer->ObserverType() != if (observer->ObserverType() !=
ContextLifecycleObserver::kPausableObjectType) ContextLifecycleObserver::kPausableObjectType)
continue; return;
PausableObject* pausable_object = static_cast<PausableObject*>(observer); PausableObject* pausable_object = static_cast<PausableObject*>(observer);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK_EQ(pausable_object->GetExecutionContext(), Context()); DCHECK_EQ(pausable_object->GetExecutionContext(), Context());
DCHECK(pausable_object->PauseIfNeededCalled()); DCHECK(pausable_object->PauseIfNeededCalled());
#endif #endif
pausable_object->Unpause(); pausable_object->Unpause();
} });
} }
void ContextLifecycleNotifier::NotifySuspendingPausableObjects() { void ContextLifecycleNotifier::NotifySuspendingPausableObjects() {
base::AutoReset<IterationState> scope(&iteration_state_, kAllowingNone); ForEachObserver([&](ContextLifecycleObserver* observer) {
for (LifecycleObserverBase* observer_base : observers_) {
ContextLifecycleObserver* observer =
static_cast<ContextLifecycleObserver*>(observer_base);
if (observer->ObserverType() != if (observer->ObserverType() !=
ContextLifecycleObserver::kPausableObjectType) ContextLifecycleObserver::kPausableObjectType)
continue; return;
PausableObject* pausable_object = static_cast<PausableObject*>(observer); PausableObject* pausable_object = static_cast<PausableObject*>(observer);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK_EQ(pausable_object->GetExecutionContext(), Context()); DCHECK_EQ(pausable_object->GetExecutionContext(), Context());
DCHECK(pausable_object->PauseIfNeededCalled()); DCHECK(pausable_object->PauseIfNeededCalled());
#endif #endif
pausable_object->Pause(); pausable_object->Pause();
} });
} }
unsigned ContextLifecycleNotifier::PausableObjectCount() const { unsigned ContextLifecycleNotifier::PausableObjectCount() const {
DCHECK(!IsIteratingOverObservers()); DCHECK(!IsIteratingOverObservers());
unsigned pausable_objects = 0; unsigned pausable_objects = 0;
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](ContextLifecycleObserver* observer) {
ContextLifecycleObserver* observer =
static_cast<ContextLifecycleObserver*>(observer_base);
if (observer->ObserverType() != if (observer->ObserverType() !=
ContextLifecycleObserver::kPausableObjectType) ContextLifecycleObserver::kPausableObjectType)
continue; return;
pausable_objects++; pausable_objects++;
} });
return pausable_objects; return pausable_objects;
} }
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
bool ContextLifecycleNotifier::Contains(PausableObject* object) const { bool ContextLifecycleNotifier::Contains(PausableObject* object) const {
DCHECK(!IsIteratingOverObservers()); DCHECK(!IsIteratingOverObservers());
for (LifecycleObserverBase* observer_base : observers_) { bool found = false;
ContextLifecycleObserver* observer = ForEachObserver([&](ContextLifecycleObserver* observer) {
static_cast<ContextLifecycleObserver*>(observer_base);
if (observer->ObserverType() != if (observer->ObserverType() !=
ContextLifecycleObserver::kPausableObjectType) ContextLifecycleObserver::kPausableObjectType)
continue; return;
PausableObject* pausable_object = static_cast<PausableObject*>(observer); PausableObject* pausable_object = static_cast<PausableObject*>(observer);
if (pausable_object == object) if (pausable_object == object)
return true; found = true;
} });
return false; return found;
} }
#endif #endif
......
...@@ -13,40 +13,32 @@ SynchronousMutationNotifier::SynchronousMutationNotifier() = default; ...@@ -13,40 +13,32 @@ SynchronousMutationNotifier::SynchronousMutationNotifier() = default;
void SynchronousMutationNotifier::NotifyChangeChildren( void SynchronousMutationNotifier::NotifyChangeChildren(
const ContainerNode& container) { const ContainerNode& container) {
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](SynchronousMutationObserver* observer) {
SynchronousMutationObserver* observer =
static_cast<SynchronousMutationObserver*>(observer_base);
observer->DidChangeChildren(container); observer->DidChangeChildren(container);
} });
} }
void SynchronousMutationNotifier::NotifyMergeTextNodes( void SynchronousMutationNotifier::NotifyMergeTextNodes(
const Text& node, const Text& node,
const NodeWithIndex& node_to_be_removed_with_index, const NodeWithIndex& node_to_be_removed_with_index,
unsigned old_length) { unsigned old_length) {
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](SynchronousMutationObserver* observer) {
SynchronousMutationObserver* observer =
static_cast<SynchronousMutationObserver*>(observer_base);
observer->DidMergeTextNodes(node, node_to_be_removed_with_index, observer->DidMergeTextNodes(node, node_to_be_removed_with_index,
old_length); old_length);
} });
} }
void SynchronousMutationNotifier::NotifyMoveTreeToNewDocument( void SynchronousMutationNotifier::NotifyMoveTreeToNewDocument(
const Node& root) { const Node& root) {
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](SynchronousMutationObserver* observer) {
SynchronousMutationObserver* observer =
static_cast<SynchronousMutationObserver*>(observer_base);
observer->DidMoveTreeToNewDocument(root); observer->DidMoveTreeToNewDocument(root);
} });
} }
void SynchronousMutationNotifier::NotifySplitTextNode(const Text& node) { void SynchronousMutationNotifier::NotifySplitTextNode(const Text& node) {
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](SynchronousMutationObserver* observer) {
SynchronousMutationObserver* observer =
static_cast<SynchronousMutationObserver*>(observer_base);
observer->DidSplitTextNode(node); observer->DidSplitTextNode(node);
} });
} }
void SynchronousMutationNotifier::NotifyUpdateCharacterData( void SynchronousMutationNotifier::NotifyUpdateCharacterData(
...@@ -54,29 +46,23 @@ void SynchronousMutationNotifier::NotifyUpdateCharacterData( ...@@ -54,29 +46,23 @@ void SynchronousMutationNotifier::NotifyUpdateCharacterData(
unsigned offset, unsigned offset,
unsigned old_length, unsigned old_length,
unsigned new_length) { unsigned new_length) {
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](SynchronousMutationObserver* observer) {
SynchronousMutationObserver* observer =
static_cast<SynchronousMutationObserver*>(observer_base);
observer->DidUpdateCharacterData(character_data, offset, old_length, observer->DidUpdateCharacterData(character_data, offset, old_length,
new_length); new_length);
} });
} }
void SynchronousMutationNotifier::NotifyNodeChildrenWillBeRemoved( void SynchronousMutationNotifier::NotifyNodeChildrenWillBeRemoved(
ContainerNode& container) { ContainerNode& container) {
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](SynchronousMutationObserver* observer) {
SynchronousMutationObserver* observer =
static_cast<SynchronousMutationObserver*>(observer_base);
observer->NodeChildrenWillBeRemoved(container); observer->NodeChildrenWillBeRemoved(container);
} });
} }
void SynchronousMutationNotifier::NotifyNodeWillBeRemoved(Node& node) { void SynchronousMutationNotifier::NotifyNodeWillBeRemoved(Node& node) {
for (LifecycleObserverBase* observer_base : observers_) { ForEachObserver([&](SynchronousMutationObserver* observer) {
SynchronousMutationObserver* observer =
static_cast<SynchronousMutationObserver*>(observer_base);
observer->NodeWillBeRemoved(node); observer->NodeWillBeRemoved(node);
} });
} }
} // namespace blink } // namespace blink
...@@ -31,12 +31,9 @@ ...@@ -31,12 +31,9 @@
namespace blink { namespace blink {
void PageVisibilityNotifier::NotifyPageVisibilityChanged() { void PageVisibilityNotifier::NotifyPageVisibilityChanged() {
base::AutoReset<IterationState> scope(&iteration_state_, kAllowingNone); ForEachObserver([](PageVisibilityObserver* observer) {
for (LifecycleObserverBase* observer_base : observers_) {
PageVisibilityObserver* observer =
static_cast<PageVisibilityObserver*>(observer_base);
observer->PageVisibilityChanged(); observer->PageVisibilityChanged();
} });
} }
} // namespace blink } // namespace blink
...@@ -63,6 +63,25 @@ class LifecycleNotifier : public GarbageCollectedMixin { ...@@ -63,6 +63,25 @@ class LifecycleNotifier : public GarbageCollectedMixin {
T* Context() { return static_cast<T*>(this); } T* Context() { return static_cast<T*>(this); }
// Safely iterate over the registered lifecycle observers.
//
// Adding or removing observers is not allowed during iteration. The callable
// will only be called synchronously inside ForEachObserver().
//
// Sample usage:
// ForEachObserver([](ObserverType* observer) {
// observer->SomeMethod();
// });
template <typename ForEachCallable>
void ForEachObserver(const ForEachCallable& callable) const {
base::AutoReset<IterationState> scope(&iteration_state_, kAllowingNone);
for (LifecycleObserverBase* observer_base : observers_) {
Observer* observer = static_cast<Observer*>(observer_base);
callable(observer);
}
}
private:
using ObserverSet = HeapHashSet<WeakMember<LifecycleObserverBase>>; using ObserverSet = HeapHashSet<WeakMember<LifecycleObserverBase>>;
enum IterationState { enum IterationState {
...@@ -74,7 +93,7 @@ class LifecycleNotifier : public GarbageCollectedMixin { ...@@ -74,7 +93,7 @@ class LifecycleNotifier : public GarbageCollectedMixin {
// Iteration state is recorded while iterating the observer set, // Iteration state is recorded while iterating the observer set,
// optionally barring add or remove mutations. // optionally barring add or remove mutations.
IterationState iteration_state_; mutable IterationState iteration_state_;
ObserverSet observers_; ObserverSet observers_;
}; };
......
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