• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2013
2 // Google Inc. All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //    * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //    * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //    * Neither the name of Google Inc. nor the name Chromium Embedded
15 // Framework nor the names of its contributors may be used to endorse
16 // or promote products derived from this software without specific prior
17 // written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 // OVERVIEW:
32 //
33 // A container for a list of callbacks. Provides callers the ability to manually
34 // or automatically unregister callbacks at any time, including during callback
35 // notification.
36 //
37 // TYPICAL USAGE:
38 //
39 // class MyWidget {
40 //  public:
41 //   using CallbackList = base::RepeatingCallbackList<void(const Foo&)>;
42 //
43 //   // Registers |cb| to be called whenever NotifyFoo() is executed.
44 //   CallbackListSubscription RegisterCallback(CallbackList::CallbackType cb) {
45 //     return callback_list_.Add(std::move(cb));
46 //   }
47 //
48 //  private:
49 //   // Calls all registered callbacks, with |foo| as the supplied arg.
50 //   void NotifyFoo(const Foo& foo) {
51 //     callback_list_.Notify(foo);
52 //   }
53 //
54 //   CallbackList callback_list_;
55 // };
56 //
57 //
58 // class MyWidgetListener {
59 //  private:
60 //   void OnFoo(const Foo& foo) {
61 //     // Called whenever MyWidget::NotifyFoo() is executed, unless
62 //     // |foo_subscription_| has been destroyed.
63 //   }
64 //
65 //   // Automatically deregisters the callback when deleted (e.g. in
66 //   // ~MyWidgetListener()).  Unretained(this) is safe here since the
67 //   // ScopedClosureRunner does not outlive |this|.
68 //   CallbackListSubscription foo_subscription_ =
69 //       MyWidget::Get()->RegisterCallback(
70 //           base::BindRepeating(&MyWidgetListener::OnFoo,
71 //                               base::Unretained(this)));
72 // };
73 //
74 // UNSUPPORTED:
75 //
76 // * Destroying the CallbackList during callback notification.
77 //
78 // This is possible to support, but not currently necessary.
79 
80 #ifndef CEF_INCLUDE_BASE_CEF_CALLBACK_LIST_H_
81 #define CEF_INCLUDE_BASE_CEF_CALLBACK_LIST_H_
82 #pragma once
83 
84 #if defined(USING_CHROMIUM_INCLUDES)
85 // When building CEF include the Chromium header directly.
86 #include "base/callback_list.h"
87 #else  // !USING_CHROMIUM_INCLUDES
88 // The following is substantially similar to the Chromium implementation.
89 // If the Chromium implementation diverges the below implementation should be
90 // updated to match.
91 
92 #include <algorithm>
93 #include <list>
94 #include <memory>
95 #include <utility>
96 
97 #include "include/base/cef_auto_reset.h"
98 #include "include/base/cef_bind.h"
99 #include "include/base/cef_callback.h"
100 #include "include/base/cef_callback_helpers.h"
101 #include "include/base/cef_compiler_specific.h"
102 #include "include/base/cef_logging.h"
103 #include "include/base/cef_weak_ptr.h"
104 
105 namespace base {
106 namespace internal {
107 template <typename CallbackListImpl>
108 class CallbackListBase;
109 }  // namespace internal
110 
111 template <typename Signature>
112 class OnceCallbackList;
113 
114 template <typename Signature>
115 class RepeatingCallbackList;
116 
117 // A trimmed-down version of ScopedClosureRunner that can be used to guarantee a
118 // closure is run on destruction. This is designed to be used by
119 // CallbackListBase to run CancelCallback() when this subscription dies;
120 // consumers can avoid callbacks on dead objects by ensuring the subscription
121 // returned by CallbackListBase::Add() does not outlive the bound object in the
122 // callback. A typical way to do this is to bind a callback to a member function
123 // on `this` and store the returned subscription as a member variable.
124 class CallbackListSubscription {
125  public:
126   CallbackListSubscription();
127   CallbackListSubscription(CallbackListSubscription&& subscription);
128   CallbackListSubscription& operator=(CallbackListSubscription&& subscription);
129   ~CallbackListSubscription();
130 
131   explicit operator bool() const { return !!closure_; }
132 
133  private:
134   template <typename T>
135   friend class internal::CallbackListBase;
136 
137   explicit CallbackListSubscription(base::OnceClosure closure);
138 
139   void Run();
140 
141   OnceClosure closure_;
142 };
143 
144 namespace internal {
145 
146 // From base/stl_util.h.
147 template <class T, class Allocator, class Predicate>
EraseIf(std::list<T,Allocator> & container,Predicate pred)148 size_t EraseIf(std::list<T, Allocator>& container, Predicate pred) {
149   size_t old_size = container.size();
150   container.remove_if(pred);
151   return old_size - container.size();
152 }
153 
154 // A traits class to break circular type dependencies between CallbackListBase
155 // and its subclasses.
156 template <typename CallbackList>
157 struct CallbackListTraits;
158 
159 // NOTE: It's important that Callbacks provide iterator stability when items are
160 // added to the end, so e.g. a std::vector<> is not suitable here.
161 template <typename Signature>
162 struct CallbackListTraits<OnceCallbackList<Signature>> {
163   using CallbackType = OnceCallback<Signature>;
164   using Callbacks = std::list<CallbackType>;
165 };
166 template <typename Signature>
167 struct CallbackListTraits<RepeatingCallbackList<Signature>> {
168   using CallbackType = RepeatingCallback<Signature>;
169   using Callbacks = std::list<CallbackType>;
170 };
171 
172 template <typename CallbackListImpl>
173 class CallbackListBase {
174  public:
175   using CallbackType =
176       typename CallbackListTraits<CallbackListImpl>::CallbackType;
177   static_assert(IsBaseCallback<CallbackType>::value, "");
178 
179   // TODO(crbug.com/1103086): Update references to use this directly and by
180   // value, then remove.
181   using Subscription = CallbackListSubscription;
182 
183   CallbackListBase() = default;
184   CallbackListBase(const CallbackListBase&) = delete;
185   CallbackListBase& operator=(const CallbackListBase&) = delete;
186 
187   ~CallbackListBase() {
188     // Destroying the list during iteration is unsupported and will cause a UAF.
189     CHECK(!iterating_);
190   }
191 
192   // Registers |cb| for future notifications. Returns a CallbackListSubscription
193   // whose destruction will cancel |cb|.
194   CallbackListSubscription Add(CallbackType cb) WARN_UNUSED_RESULT {
195     DCHECK(!cb.is_null());
196     return CallbackListSubscription(base::BindOnce(
197         &CallbackListBase::CancelCallback, weak_ptr_factory_.GetWeakPtr(),
198         callbacks_.insert(callbacks_.end(), std::move(cb))));
199   }
200 
201   // Registers |cb| for future notifications. Provides no way for the caller to
202   // cancel, so this is only safe for cases where the callback is guaranteed to
203   // live at least as long as this list (e.g. if it's bound on the same object
204   // that owns the list).
205   // TODO(pkasting): Attempt to use Add() instead and see if callers can relax
206   // other lifetime/ordering mechanisms as a result.
207   void AddUnsafe(CallbackType cb) {
208     DCHECK(!cb.is_null());
209     callbacks_.push_back(std::move(cb));
210   }
211 
212   // Registers |removal_callback| to be run after elements are removed from the
213   // list of registered callbacks.
214   void set_removal_callback(const RepeatingClosure& removal_callback) {
215     removal_callback_ = removal_callback;
216   }
217 
218   // Returns whether the list of registered callbacks is empty (from an external
219   // perspective -- meaning no remaining callbacks are live).
220   bool empty() const {
221     return std::all_of(callbacks_.cbegin(), callbacks_.cend(),
222                        [](const auto& callback) { return callback.is_null(); });
223   }
224 
225   // Calls all registered callbacks that are not canceled beforehand. If any
226   // callbacks are unregistered, notifies any registered removal callback at the
227   // end.
228   //
229   // Arguments must be copyable, since they must be supplied to all callbacks.
230   // Move-only types would be destructively modified by passing them to the
231   // first callback and not reach subsequent callbacks as intended.
232   //
233   // Notify() may be called re-entrantly, in which case the nested call
234   // completes before the outer one continues. Callbacks are only ever added at
235   // the end and canceled callbacks are not pruned from the list until the
236   // outermost iteration completes, so existing iterators should never be
237   // invalidated. However, this does mean that a callback added during a nested
238   // call can be notified by outer calls -- meaning it will be notified about
239   // things that happened before it was added -- if its subscription outlives
240   // the reentrant Notify() call.
241   template <typename... RunArgs>
242   void Notify(RunArgs&&... args) {
243     if (empty())
244       return;  // Nothing to do.
245 
246     {
247       AutoReset<bool> iterating(&iterating_, true);
248 
249       // Skip any callbacks that are canceled during iteration.
250       // NOTE: Since RunCallback() may call Add(), it's not safe to cache the
251       // value of callbacks_.end() across loop iterations.
252       const auto next_valid = [this](const auto it) {
253         return std::find_if_not(it, callbacks_.end(), [](const auto& callback) {
254           return callback.is_null();
255         });
256       };
257       for (auto it = next_valid(callbacks_.begin()); it != callbacks_.end();
258            it = next_valid(it))
259         // NOTE: Intentionally does not call std::forward<RunArgs>(args)...,
260         // since that would allow move-only arguments.
261         static_cast<CallbackListImpl*>(this)->RunCallback(it++, args...);
262     }
263 
264     // Re-entrant invocations shouldn't prune anything from the list. This can
265     // invalidate iterators from underneath higher call frames. It's safe to
266     // simply do nothing, since the outermost frame will continue through here
267     // and prune all null callbacks below.
268     if (iterating_)
269       return;
270 
271     // Any null callbacks remaining in the list were canceled due to
272     // Subscription destruction during iteration, and can safely be erased now.
273     const size_t erased_callbacks =
274         EraseIf(callbacks_, [](const auto& cb) { return cb.is_null(); });
275 
276     // Run |removal_callback_| if any callbacks were canceled. Note that we
277     // cannot simply compare list sizes before and after iterating, since
278     // notification may result in Add()ing new callbacks as well as canceling
279     // them. Also note that if this is a OnceCallbackList, the OnceCallbacks
280     // that were executed above have all been removed regardless of whether
281     // they're counted in |erased_callbacks_|.
282     if (removal_callback_ &&
283         (erased_callbacks || IsOnceCallback<CallbackType>::value))
284       removal_callback_.Run();  // May delete |this|!
285   }
286 
287  protected:
288   using Callbacks = typename CallbackListTraits<CallbackListImpl>::Callbacks;
289 
290   // Holds non-null callbacks, which will be called during Notify().
291   Callbacks callbacks_;
292 
293  private:
294   // Cancels the callback pointed to by |it|, which is guaranteed to be valid.
295   void CancelCallback(const typename Callbacks::iterator& it) {
296     if (static_cast<CallbackListImpl*>(this)->CancelNullCallback(it))
297       return;
298 
299     if (iterating_) {
300       // Calling erase() here is unsafe, since the loop in Notify() may be
301       // referencing this same iterator, e.g. if adjacent callbacks'
302       // Subscriptions are both destroyed when the first one is Run().  Just
303       // reset the callback and let Notify() clean it up at the end.
304       it->Reset();
305     } else {
306       callbacks_.erase(it);
307       if (removal_callback_)
308         removal_callback_.Run();  // May delete |this|!
309     }
310   }
311 
312   // Set while Notify() is traversing |callbacks_|.  Used primarily to avoid
313   // invalidating iterators that may be in use.
314   bool iterating_ = false;
315 
316   // Called after elements are removed from |callbacks_|.
317   RepeatingClosure removal_callback_;
318 
319   WeakPtrFactory<CallbackListBase> weak_ptr_factory_{this};
320 };
321 
322 }  // namespace internal
323 
324 template <typename Signature>
325 class OnceCallbackList
326     : public internal::CallbackListBase<OnceCallbackList<Signature>> {
327  private:
328   friend internal::CallbackListBase<OnceCallbackList>;
329   using Traits = internal::CallbackListTraits<OnceCallbackList>;
330 
331   // Runs the current callback, which may cancel it or any other callbacks.
332   template <typename... RunArgs>
333   void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) {
334     // OnceCallbacks still have Subscriptions with outstanding iterators;
335     // splice() removes them from |callbacks_| without invalidating those.
336     null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it);
337 
338     // NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see
339     // comments in Notify().
340     std::move(*it).Run(args...);
341   }
342 
343   // If |it| refers to an already-canceled callback, does any necessary cleanup
344   // and returns true.  Otherwise returns false.
345   bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
346     if (it->is_null()) {
347       null_callbacks_.erase(it);
348       return true;
349     }
350     return false;
351   }
352 
353   // Holds null callbacks whose Subscriptions are still alive, so the
354   // Subscriptions will still contain valid iterators.  Only needed for
355   // OnceCallbacks, since RepeatingCallbacks are not canceled except by
356   // Subscription destruction.
357   typename Traits::Callbacks null_callbacks_;
358 };
359 
360 template <typename Signature>
361 class RepeatingCallbackList
362     : public internal::CallbackListBase<RepeatingCallbackList<Signature>> {
363  private:
364   friend internal::CallbackListBase<RepeatingCallbackList>;
365   using Traits = internal::CallbackListTraits<RepeatingCallbackList>;
366   // Runs the current callback, which may cancel it or any other callbacks.
367   template <typename... RunArgs>
368   void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) {
369     // NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see
370     // comments in Notify().
371     it->Run(args...);
372   }
373 
374   // If |it| refers to an already-canceled callback, does any necessary cleanup
375   // and returns true.  Otherwise returns false.
376   bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
377     // Because at most one Subscription can point to a given callback, and
378     // RepeatingCallbacks are only reset by CancelCallback(), no one should be
379     // able to request cancellation of a canceled RepeatingCallback.
380     DCHECK(!it->is_null());
381     return false;
382   }
383 };
384 
385 // Syntactic sugar to parallel that used for Callbacks.
386 // ClosureList explicitly not provided since it is not used, and CallbackList
387 // is deprecated. {Once,Repeating}ClosureList should instead be used.
388 using OnceClosureList = OnceCallbackList<void()>;
389 using RepeatingClosureList = RepeatingCallbackList<void()>;
390 
391 }  // namespace base
392 
393 #endif  // !USING_CHROMIUM_INCLUDES
394 
395 #endif  // CEF_INCLUDE_BASE_CEF_CALLBACK_LIST_H_
396