Bug 1854550 - pt 10. Allow LOG() with zero extra arguments r=glandium
[gecko.git] / dom / base / PlacesObservers.cpp
blobbb92e4a0725adefceec0e925b46c647028a8ac24
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PlacesObservers.h"
9 #include "PlacesWeakCallbackWrapper.h"
10 #include "nsIWeakReferenceUtils.h"
11 #include "nsIXPConnect.h"
12 #include "mozilla/ClearOnShutdown.h"
14 namespace mozilla::dom {
16 template <class T>
17 struct Flagged {
18 Flagged(uint32_t aFlags, T&& aValue)
19 : flags(aFlags), value(std::forward<T>(aValue)) {}
20 Flagged(Flagged&& aOther)
21 : Flagged(std::move(aOther.flags), std::move(aOther.value)) {}
22 Flagged(const Flagged& aOther) = default;
23 ~Flagged() = default;
25 uint32_t flags;
26 T value;
29 template <class T>
30 using FlaggedArray = nsTArray<Flagged<T>>;
32 template <class T>
33 struct ListenerCollection {
34 static StaticAutoPtr<FlaggedArray<T>> gListeners;
35 static StaticAutoPtr<FlaggedArray<T>> gListenersToRemove;
37 static FlaggedArray<T>* GetListeners(bool aDoNotInit = false) {
38 MOZ_ASSERT(NS_IsMainThread());
39 if (!gListeners && !aDoNotInit) {
40 gListeners = new FlaggedArray<T>();
41 ClearOnShutdown(&gListeners);
43 return gListeners;
46 static FlaggedArray<T>* GetListenersToRemove(bool aDoNotInit = false) {
47 MOZ_ASSERT(NS_IsMainThread());
48 if (!gListenersToRemove && !aDoNotInit) {
49 gListenersToRemove = new FlaggedArray<T>();
50 ClearOnShutdown(&gListenersToRemove);
52 return gListenersToRemove;
56 template <class T>
57 StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListeners;
58 template <class T>
59 StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListenersToRemove;
61 using JSListeners = ListenerCollection<RefPtr<PlacesEventCallback>>;
62 using WeakJSListeners = ListenerCollection<WeakPtr<PlacesWeakCallbackWrapper>>;
63 using WeakNativeListeners =
64 ListenerCollection<WeakPtr<places::INativePlacesEventCallback>>;
66 // Even if NotifyListeners is called any timing, we mange the notifications with
67 // adding to this queue, then sending in sequence. This avoids sending nested
68 // notifications while previous ones are still being sent.
69 static nsTArray<Sequence<OwningNonNull<PlacesEvent>>> gNotificationQueue;
71 uint32_t GetEventTypeFlag(PlacesEventType aEventType) {
72 if (aEventType == PlacesEventType::None) {
73 return 0;
75 return 1 << ((uint32_t)aEventType - 1);
78 uint32_t GetFlagsForEventTypes(const nsTArray<PlacesEventType>& aEventTypes) {
79 uint32_t flags = 0;
80 for (PlacesEventType eventType : aEventTypes) {
81 flags |= GetEventTypeFlag(eventType);
83 return flags;
86 uint32_t GetFlagsForEvents(
87 const nsTArray<OwningNonNull<PlacesEvent>>& aEvents) {
88 uint32_t flags = 0;
89 for (const PlacesEvent& event : aEvents) {
90 flags |= GetEventTypeFlag(event.Type());
92 return flags;
95 template <class TWrapped, class TUnwrapped, class TListenerCollection>
96 MOZ_CAN_RUN_SCRIPT void CallListeners(
97 uint32_t aEventFlags, const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
98 unsigned long aListenersLengthToCall,
99 const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener,
100 const std::function<void(TUnwrapped&,
101 const Sequence<OwningNonNull<PlacesEvent>>&)>&
102 aCallListener) {
103 auto& listeners = *TListenerCollection::GetListeners();
104 for (uint32_t i = 0; i < aListenersLengthToCall; i++) {
105 Flagged<TWrapped>& listener = listeners[i];
106 TUnwrapped unwrapped = aUnwrapListener(listener.value);
107 if (!unwrapped) {
108 continue;
111 if ((listener.flags & aEventFlags) == aEventFlags) {
112 aCallListener(unwrapped, aEvents);
113 } else if (listener.flags & aEventFlags) {
114 Sequence<OwningNonNull<PlacesEvent>> filtered;
115 for (const OwningNonNull<PlacesEvent>& event : aEvents) {
116 if (listener.flags & GetEventTypeFlag(event->Type())) {
117 bool success = !!filtered.AppendElement(event, fallible);
118 MOZ_RELEASE_ASSERT(success);
121 aCallListener(unwrapped, filtered);
126 void PlacesObservers::AddListener(GlobalObject& aGlobal,
127 const nsTArray<PlacesEventType>& aEventTypes,
128 PlacesEventCallback& aCallback,
129 ErrorResult& rv) {
130 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
132 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
133 JSListeners::GetListeners();
134 Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
135 listeners->AppendElement(pair);
138 void PlacesObservers::AddListener(GlobalObject& aGlobal,
139 const nsTArray<PlacesEventType>& aEventTypes,
140 PlacesWeakCallbackWrapper& aCallback,
141 ErrorResult& rv) {
142 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
144 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
145 WeakJSListeners::GetListeners();
146 WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
147 MOZ_ASSERT(weakCb.get());
148 Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, std::move(weakCb));
149 listeners->AppendElement(flagged);
152 void PlacesObservers::AddListener(
153 const nsTArray<PlacesEventType>& aEventTypes,
154 places::INativePlacesEventCallback* aCallback) {
155 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
157 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
158 WeakNativeListeners::GetListeners();
159 Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
160 listeners->AppendElement(pair);
163 void PlacesObservers::RemoveListener(
164 GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
165 PlacesEventCallback& aCallback, ErrorResult& rv) {
166 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
167 if (!gNotificationQueue.IsEmpty()) {
168 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
169 JSListeners::GetListenersToRemove();
170 Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
171 listeners->AppendElement(pair);
172 } else {
173 RemoveListener(flags, aCallback);
177 void PlacesObservers::RemoveListener(
178 GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
179 PlacesWeakCallbackWrapper& aCallback, ErrorResult& rv) {
180 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
181 if (!gNotificationQueue.IsEmpty()) {
182 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
183 WeakJSListeners::GetListenersToRemove();
184 WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
185 MOZ_ASSERT(weakCb.get());
186 Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags,
187 std::move(weakCb));
188 listeners->AppendElement(flagged);
189 } else {
190 RemoveListener(flags, aCallback);
194 void PlacesObservers::RemoveListener(
195 const nsTArray<PlacesEventType>& aEventTypes,
196 places::INativePlacesEventCallback* aCallback) {
197 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
198 if (!gNotificationQueue.IsEmpty()) {
199 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
200 WeakNativeListeners::GetListenersToRemove();
201 Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
202 listeners->AppendElement(pair);
203 } else {
204 RemoveListener(flags, aCallback);
208 void PlacesObservers::RemoveListener(uint32_t aFlags,
209 PlacesEventCallback& aCallback) {
210 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
211 JSListeners::GetListeners(/* aDoNotInit: */ true);
212 if (!listeners) {
213 return;
215 for (uint32_t i = 0; i < listeners->Length(); i++) {
216 Flagged<RefPtr<PlacesEventCallback>>& l = listeners->ElementAt(i);
217 if (!(*l.value == aCallback)) {
218 continue;
220 if (l.flags == (aFlags & l.flags)) {
221 listeners->RemoveElementAt(i);
222 i--;
223 } else {
224 l.flags &= ~aFlags;
229 void PlacesObservers::RemoveListener(uint32_t aFlags,
230 PlacesWeakCallbackWrapper& aCallback) {
231 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
232 WeakJSListeners::GetListeners(/* aDoNotInit: */ true);
233 if (!listeners) {
234 return;
236 for (uint32_t i = 0; i < listeners->Length(); i++) {
237 Flagged<WeakPtr<PlacesWeakCallbackWrapper>>& l = listeners->ElementAt(i);
238 RefPtr<PlacesWeakCallbackWrapper> unwrapped = l.value.get();
239 if (unwrapped != &aCallback) {
240 continue;
242 if (l.flags == (aFlags & l.flags)) {
243 listeners->RemoveElementAt(i);
244 i--;
245 } else {
246 l.flags &= ~aFlags;
251 void PlacesObservers::RemoveListener(
252 uint32_t aFlags, places::INativePlacesEventCallback* aCallback) {
253 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
254 WeakNativeListeners::GetListeners(/* aDoNotInit: */ true);
255 if (!listeners) {
256 return;
258 for (uint32_t i = 0; i < listeners->Length(); i++) {
259 Flagged<WeakPtr<places::INativePlacesEventCallback>>& l =
260 listeners->ElementAt(i);
261 RefPtr<places::INativePlacesEventCallback> unwrapped = l.value.get();
262 if (unwrapped != aCallback) {
263 continue;
265 if (l.flags == (aFlags & l.flags)) {
266 listeners->RemoveElementAt(i);
267 i--;
268 } else {
269 l.flags &= ~aFlags;
274 template <class TWrapped, class TUnwrapped, class TListenerCollection>
275 void CleanupListeners(
276 const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener,
277 const std::function<void(Flagged<TWrapped>&)>& aRemoveListener) {
278 auto& listeners = *TListenerCollection::GetListeners();
279 for (uint32_t i = 0; i < listeners.Length(); i++) {
280 Flagged<TWrapped>& listener = listeners[i];
281 TUnwrapped unwrapped = aUnwrapListener(listener.value);
282 if (!unwrapped) {
283 listeners.RemoveElementAt(i);
284 i--;
288 auto& listenersToRemove = *TListenerCollection::GetListenersToRemove();
289 for (auto& listener : listenersToRemove) {
290 aRemoveListener(listener);
292 listenersToRemove.Clear();
295 void PlacesObservers::NotifyListeners(
296 GlobalObject& aGlobal, const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
297 ErrorResult& rv) {
298 NotifyListeners(aEvents);
301 void PlacesObservers::NotifyListeners(
302 const Sequence<OwningNonNull<PlacesEvent>>& aEvents) {
303 MOZ_ASSERT(aEvents.Length() > 0, "Must pass a populated array of events");
304 if (aEvents.Length() == 0) {
305 return;
308 #ifdef DEBUG
309 if (!gNotificationQueue.IsEmpty()) {
310 NS_WARNING(
311 "Avoid nested Places notifications if possible, the order of events "
312 "cannot be guaranteed");
313 nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
314 Unused << xpc->DebugDumpJSStack(false, false, false);
316 #endif
318 gNotificationQueue.AppendElement(aEvents);
320 // If gNotificationQueue has only the events we added now, start to notify.
321 // Otherwise, as it already started the notification processing,
322 // rely on the processing.
323 if (gNotificationQueue.Length() == 1) {
324 NotifyNext();
328 void PlacesObservers::NotifyNext() {
329 auto events = gNotificationQueue[0];
330 uint32_t flags = GetFlagsForEvents(events);
332 // Send up to the number of current listeners, to avoid handling listeners
333 // added during this notification.
334 unsigned long jsListenersLength = JSListeners::GetListeners()->Length();
335 unsigned long weakNativeListenersLength =
336 WeakNativeListeners::GetListeners()->Length();
337 unsigned long weakJSListenersLength =
338 WeakJSListeners::GetListeners()->Length();
340 CallListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>,
341 JSListeners>(
342 flags, events, jsListenersLength, [](auto& cb) { return cb; },
343 // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
344 // some internals of the std::function implementation that we can't
345 // annotate. We handle this by annotating CallListeners and making sure
346 // it holds a strong ref to the callback.
347 [&](auto& cb, const auto& events)
348 MOZ_CAN_RUN_SCRIPT_BOUNDARY { MOZ_KnownLive(cb)->Call(events); });
350 CallListeners<WeakPtr<places::INativePlacesEventCallback>,
351 RefPtr<places::INativePlacesEventCallback>,
352 WeakNativeListeners>(
353 flags, events, weakNativeListenersLength,
354 [](auto& cb) { return cb.get(); },
355 [&](auto& cb, const Sequence<OwningNonNull<PlacesEvent>>& events) {
356 cb->HandlePlacesEvent(events);
359 CallListeners<WeakPtr<PlacesWeakCallbackWrapper>,
360 RefPtr<PlacesWeakCallbackWrapper>, WeakJSListeners>(
361 flags, events, weakJSListenersLength, [](auto& cb) { return cb.get(); },
362 // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
363 // some internals of the std::function implementation that we can't
364 // annotate. We handle this by annotating CallListeners and making sure
365 // it holds a strong ref to the callback.
366 [&](auto& cb, const auto& events) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
367 RefPtr<PlacesEventCallback> callback(cb->mCallback);
368 callback->Call(events);
371 gNotificationQueue.RemoveElementAt(0);
373 CleanupListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>,
374 JSListeners>(
375 [](auto& cb) { return cb; },
376 [&](auto& cb) { RemoveListener(cb.flags, *cb.value); });
377 CleanupListeners<WeakPtr<PlacesWeakCallbackWrapper>,
378 RefPtr<PlacesWeakCallbackWrapper>, WeakJSListeners>(
379 [](auto& cb) { return cb.get(); },
380 [&](auto& cb) { RemoveListener(cb.flags, *cb.value.get()); });
381 CleanupListeners<WeakPtr<places::INativePlacesEventCallback>,
382 RefPtr<places::INativePlacesEventCallback>,
383 WeakNativeListeners>(
384 [](auto& cb) { return cb.get(); },
385 [&](auto& cb) { RemoveListener(cb.flags, cb.value.get()); });
387 if (!gNotificationQueue.IsEmpty()) {
388 NotifyNext();
392 } // namespace mozilla::dom