Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef BASE_SCOPED_OBSERVATION_H_ |
| 6 | #define BASE_SCOPED_OBSERVATION_H_ |
| 7 | |
| 8 | #include <stddef.h> |
| 9 | |
David Sanders | 8cfb63a | 2022-04-14 19:36:30 | [diff] [blame] | 10 | #include "base/check.h" |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 11 | #include "base/check_op.h" |
Keishi Hattori | 44d4f4e9 | 2022-06-16 06:01:20 | [diff] [blame] | 12 | #include "base/memory/raw_ptr.h" |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 13 | #include "base/scoped_observation_traits.h" |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 14 | |
Sigurdur Asgeirsson | 80f6e8f | 2020-11-04 20:14:15 | [diff] [blame] | 15 | namespace base { |
| 16 | |
Christian Flach | 0572efd | 2022-09-21 22:19:21 | [diff] [blame] | 17 | // `ScopedObservation` is used to keep track of a singular observation, i.e., |
| 18 | // where an observer observes a single source only. Use |
| 19 | // `base::ScopedMultiSourceObservation` for objects that observe multiple |
Sigurdur Asgeirsson | d272114c | 2021-06-08 12:37:32 | [diff] [blame] | 20 | // sources. |
Sigurdur Asgeirsson | 1103165 | 2020-11-12 17:44:25 | [diff] [blame] | 21 | // |
Christian Flach | 0572efd | 2022-09-21 22:19:21 | [diff] [blame] | 22 | // When a `ScopedObservation` is destroyed, it unregisters the observer from the |
| 23 | // observable if it was currently observing something. Otherwise it does |
| 24 | // nothing. |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 25 | // |
Christian Flach | 0572efd | 2022-09-21 22:19:21 | [diff] [blame] | 26 | // Using a `ScopedObservation` instead of manually observing and unobserving has |
| 27 | // the following benefits: |
| 28 | // - The observer cannot accidentally forget to stop observing when it is |
| 29 | // destroyed. |
| 30 | // - By calling `Reset`, an ongoing observation can be stopped before the |
| 31 | // `ScopedObservation` is destroyed. If nothing was currently observed, then |
| 32 | // calling `Reset` does nothing. This can be useful for when the observable is |
| 33 | // destroyed before the observer is destroyed, because it prevents the |
| 34 | // observer from accidentally unregistering itself from the destroyed |
| 35 | // observable a second time when it itself is destroyed. Without |
| 36 | // `ScopedObservation`, one might need to keep track of whether one has |
| 37 | // already stopped observing in a separate boolean. |
| 38 | // |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 39 | // A complete usage example can be found below. |
Christian Flach | 0572efd | 2022-09-21 22:19:21 | [diff] [blame] | 40 | // |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 41 | // `observer.h`: |
| 42 | // class Observer { |
Christian Flach | 0572efd | 2022-09-21 22:19:21 | [diff] [blame] | 43 | // public: |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 44 | // virtual ~Observer() {} |
| 45 | // |
| 46 | // virtual void OnEvent() {} |
| 47 | // }; |
| 48 | // |
| 49 | // `source.h`: |
| 50 | // class Observer; |
| 51 | // class Source { |
| 52 | // public: |
| 53 | // void AddObserver(Observer* observer); |
| 54 | // void RemoveObserver(Observer* observer); |
| 55 | // }; |
| 56 | // |
| 57 | // `observer_impl.h`: |
| 58 | // #include "observer.h" |
| 59 | // |
| 60 | // class Source; |
| 61 | // |
| 62 | // class ObserverImpl: public Observer { |
| 63 | // public: |
| 64 | // ObserverImpl(Source* source); |
Christian Flach | 0572efd | 2022-09-21 22:19:21 | [diff] [blame] | 65 | // // Note how there is no need to stop observing in the destructor. |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 66 | // ~ObserverImpl() override {} |
| 67 | // |
| 68 | // void OnEvent() override { |
| 69 | // ... |
| 70 | // } |
| 71 | // |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 72 | // private: |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 73 | // // Note that |obs_| can be instantiated with forward-declared Source. |
| 74 | // base::ScopedObservation<Source, Observer> obs_{this}; |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 75 | // }; |
| 76 | // |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 77 | // `observer_impl.cc`: |
| 78 | // #include "observer_impl.h" |
| 79 | // #include "source.h" |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 80 | // |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 81 | // ObserverImpl::ObserverImpl(Source* source) { |
| 82 | // // After the call |this| starts listening to events from |source|. |
| 83 | // obs_.Observe(source); |
| 84 | // } |
| 85 | // |
| 86 | //////////////////////////////////////////////////////////////////////////////// |
| 87 | // |
| 88 | // By default `ScopedObservation` only works with sources that expose |
| 89 | // `AddObserver` and `RemoveObserver`. However, it's also possible to |
| 90 | // adapt it to custom function names (say `AddFoo` and `RemoveFoo` accordingly) |
Andrew Rayskiy | 3cf07339 | 2022-11-11 00:31:49 | [diff] [blame] | 91 | // by tailoring ScopedObservationTraits<> for the given Source and Observer -- |
| 92 | // see `base/scoped_observation_traits.h` for details. |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 93 | // |
| 94 | |
Andrew Rayskiy | 3cf07339 | 2022-11-11 00:31:49 | [diff] [blame] | 95 | template <class Source, class Observer> |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 96 | class ScopedObservation { |
| 97 | public: |
| 98 | explicit ScopedObservation(Observer* observer) : observer_(observer) {} |
| 99 | ScopedObservation(const ScopedObservation&) = delete; |
| 100 | ScopedObservation& operator=(const ScopedObservation&) = delete; |
Sigurdur Asgeirsson | c7a6ac2 | 2020-11-23 17:59:19 | [diff] [blame] | 101 | ~ScopedObservation() { Reset(); } |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 102 | |
| 103 | // Adds the object passed to the constructor as an observer on |source|. |
| 104 | // IsObserving() must be false. |
| 105 | void Observe(Source* source) { |
Anthony Vallee-Dubois | 331683b | 2023-02-17 17:06:46 | [diff] [blame] | 106 | DCHECK_EQ(source_, nullptr); |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 107 | source_ = source; |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 108 | Traits::AddObserver(source_, observer_); |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 109 | } |
| 110 | |
Sigurdur Asgeirsson | c7a6ac2 | 2020-11-23 17:59:19 | [diff] [blame] | 111 | // Remove the object passed to the constructor as an observer from |source_| |
| 112 | // if currently observing. Does nothing otherwise. |
| 113 | void Reset() { |
Sigurdur Asgeirsson | 518e208 | 2020-12-09 13:00:27 | [diff] [blame] | 114 | if (source_) { |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 115 | Traits::RemoveObserver(source_, observer_); |
Sigurdur Asgeirsson | 518e208 | 2020-12-09 13:00:27 | [diff] [blame] | 116 | source_ = nullptr; |
| 117 | } |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | // Returns true if any source is being observed. |
| 121 | bool IsObserving() const { return source_ != nullptr; } |
| 122 | |
| 123 | // Returns true if |source| is being observed. |
Sigurdur Asgeirsson | 61cc2154 | 2020-11-19 00:12:07 | [diff] [blame] | 124 | bool IsObservingSource(Source* source) const { |
| 125 | DCHECK(source); |
| 126 | return source_ == source; |
| 127 | } |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 128 | |
| 129 | private: |
Andrew Rayskiy | 3cf07339 | 2022-11-11 00:31:49 | [diff] [blame] | 130 | using Traits = ScopedObservationTraits<Source, Observer>; |
Andrew Rayskiy | 8fdfb67 | 2022-11-04 00:26:30 | [diff] [blame] | 131 | |
Paul Semel | 8311c1a3 | 2023-05-04 17:12:46 | [diff] [blame] | 132 | const raw_ptr<Observer, DanglingUntriaged> observer_; |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 133 | |
| 134 | // The observed source, if any. |
Paul Semel | d40aad18 | 2023-06-06 17:39:30 | [diff] [blame] | 135 | raw_ptr<Source, LeakedDanglingUntriaged> source_ = nullptr; |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 136 | }; |
| 137 | |
Sigurdur Asgeirsson | 80f6e8f | 2020-11-04 20:14:15 | [diff] [blame] | 138 | } // namespace base |
| 139 | |
Sigurdur Asgeirsson | 046f5c7 | 2020-11-04 16:48:43 | [diff] [blame] | 140 | #endif // BASE_SCOPED_OBSERVATION_H_ |