Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / base / PlacesObservers.cpp
blobb5211733ea05934dd9ac552ff3a44fc3f2cff91d
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 = 0;
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 StaticRefPtr<PlacesEventCounts> PlacesObservers::sCounts;
127 static void EnsureCountsInitialized() {
128 if (!PlacesObservers::sCounts) {
129 PlacesObservers::sCounts = new PlacesEventCounts();
130 ClearOnShutdown(&PlacesObservers::sCounts);
134 void PlacesObservers::AddListener(GlobalObject& aGlobal,
135 const nsTArray<PlacesEventType>& aEventTypes,
136 PlacesEventCallback& aCallback,
137 ErrorResult& rv) {
138 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
140 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
141 JSListeners::GetListeners();
142 Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
143 listeners->AppendElement(pair);
146 void PlacesObservers::AddListener(GlobalObject& aGlobal,
147 const nsTArray<PlacesEventType>& aEventTypes,
148 PlacesWeakCallbackWrapper& aCallback,
149 ErrorResult& rv) {
150 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
152 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
153 WeakJSListeners::GetListeners();
154 WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
155 MOZ_ASSERT(weakCb.get());
156 Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, std::move(weakCb));
157 listeners->AppendElement(flagged);
160 void PlacesObservers::AddListener(
161 const nsTArray<PlacesEventType>& aEventTypes,
162 places::INativePlacesEventCallback* aCallback) {
163 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
165 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
166 WeakNativeListeners::GetListeners();
167 Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
168 listeners->AppendElement(pair);
171 void PlacesObservers::RemoveListener(
172 GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
173 PlacesEventCallback& aCallback, ErrorResult& rv) {
174 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
175 if (!gNotificationQueue.IsEmpty()) {
176 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
177 JSListeners::GetListenersToRemove();
178 Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
179 listeners->AppendElement(pair);
180 } else {
181 RemoveListener(flags, aCallback);
185 void PlacesObservers::RemoveListener(
186 GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
187 PlacesWeakCallbackWrapper& aCallback, ErrorResult& rv) {
188 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
189 if (!gNotificationQueue.IsEmpty()) {
190 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
191 WeakJSListeners::GetListenersToRemove();
192 WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
193 MOZ_ASSERT(weakCb.get());
194 Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags,
195 std::move(weakCb));
196 listeners->AppendElement(flagged);
197 } else {
198 RemoveListener(flags, aCallback);
202 void PlacesObservers::RemoveListener(
203 const nsTArray<PlacesEventType>& aEventTypes,
204 places::INativePlacesEventCallback* aCallback) {
205 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
206 if (!gNotificationQueue.IsEmpty()) {
207 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
208 WeakNativeListeners::GetListenersToRemove();
209 Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
210 listeners->AppendElement(pair);
211 } else {
212 RemoveListener(flags, aCallback);
216 void PlacesObservers::RemoveListener(uint32_t aFlags,
217 PlacesEventCallback& aCallback) {
218 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
219 JSListeners::GetListeners(/* aDoNotInit: */ true);
220 if (!listeners) {
221 return;
223 for (uint32_t i = 0; i < listeners->Length(); i++) {
224 Flagged<RefPtr<PlacesEventCallback>>& l = listeners->ElementAt(i);
225 if (!(*l.value == aCallback)) {
226 continue;
228 if (l.flags == (aFlags & l.flags)) {
229 listeners->RemoveElementAt(i);
230 i--;
231 } else {
232 l.flags &= ~aFlags;
237 void PlacesObservers::RemoveListener(uint32_t aFlags,
238 PlacesWeakCallbackWrapper& aCallback) {
239 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
240 WeakJSListeners::GetListeners(/* aDoNotInit: */ true);
241 if (!listeners) {
242 return;
244 for (uint32_t i = 0; i < listeners->Length(); i++) {
245 Flagged<WeakPtr<PlacesWeakCallbackWrapper>>& l = listeners->ElementAt(i);
246 RefPtr<PlacesWeakCallbackWrapper> unwrapped = l.value.get();
247 if (unwrapped != &aCallback) {
248 continue;
250 if (l.flags == (aFlags & l.flags)) {
251 listeners->RemoveElementAt(i);
252 i--;
253 } else {
254 l.flags &= ~aFlags;
259 void PlacesObservers::RemoveListener(
260 uint32_t aFlags, places::INativePlacesEventCallback* aCallback) {
261 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
262 WeakNativeListeners::GetListeners(/* aDoNotInit: */ true);
263 if (!listeners) {
264 return;
266 for (uint32_t i = 0; i < listeners->Length(); i++) {
267 Flagged<WeakPtr<places::INativePlacesEventCallback>>& l =
268 listeners->ElementAt(i);
269 RefPtr<places::INativePlacesEventCallback> unwrapped = l.value.get();
270 if (unwrapped != aCallback) {
271 continue;
273 if (l.flags == (aFlags & l.flags)) {
274 listeners->RemoveElementAt(i);
275 i--;
276 } else {
277 l.flags &= ~aFlags;
282 template <class TWrapped, class TUnwrapped, class TListenerCollection>
283 void CleanupListeners(
284 const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener,
285 const std::function<void(Flagged<TWrapped>&)>& aRemoveListener) {
286 auto& listeners = *TListenerCollection::GetListeners();
287 for (uint32_t i = 0; i < listeners.Length(); i++) {
288 Flagged<TWrapped>& listener = listeners[i];
289 TUnwrapped unwrapped = aUnwrapListener(listener.value);
290 if (!unwrapped) {
291 listeners.RemoveElementAt(i);
292 i--;
296 auto& listenersToRemove = *TListenerCollection::GetListenersToRemove();
297 for (auto& listener : listenersToRemove) {
298 aRemoveListener(listener);
300 listenersToRemove.Clear();
303 void PlacesObservers::NotifyListeners(
304 GlobalObject& aGlobal, const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
305 ErrorResult& rv) {
306 NotifyListeners(aEvents);
309 void PlacesObservers::NotifyListeners(
310 const Sequence<OwningNonNull<PlacesEvent>>& aEvents) {
311 MOZ_ASSERT(aEvents.Length() > 0, "Must pass a populated array of events");
312 if (aEvents.Length() == 0) {
313 return;
315 EnsureCountsInitialized();
316 for (const auto& event : aEvents) {
317 DebugOnly<nsresult> rv = sCounts->Increment(event->Type());
318 MOZ_ASSERT(NS_SUCCEEDED(rv));
320 #ifdef DEBUG
321 if (!gNotificationQueue.IsEmpty()) {
322 NS_WARNING(
323 "Avoid nested Places notifications if possible, the order of events "
324 "cannot be guaranteed");
325 nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
326 Unused << xpc->DebugDumpJSStack(false, false, false);
328 #endif
330 gNotificationQueue.AppendElement(aEvents);
332 // If gNotificationQueue has only the events we added now, start to notify.
333 // Otherwise, as it already started the notification processing,
334 // rely on the processing.
335 if (gNotificationQueue.Length() == 1) {
336 NotifyNext();
340 void PlacesObservers::NotifyNext() {
341 auto events = gNotificationQueue[0];
342 uint32_t flags = GetFlagsForEvents(events);
344 // Send up to the number of current listeners, to avoid handling listeners
345 // added during this notification.
346 unsigned long jsListenersLength = JSListeners::GetListeners()->Length();
347 unsigned long weakNativeListenersLength =
348 WeakNativeListeners::GetListeners()->Length();
349 unsigned long weakJSListenersLength =
350 WeakJSListeners::GetListeners()->Length();
352 CallListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>,
353 JSListeners>(
354 flags, events, jsListenersLength, [](auto& cb) { return cb; },
355 // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
356 // some internals of the std::function implementation that we can't
357 // annotate. We handle this by annotating CallListeners and making sure
358 // it holds a strong ref to the callback.
359 [&](auto& cb, const auto& events)
360 MOZ_CAN_RUN_SCRIPT_BOUNDARY { MOZ_KnownLive(cb)->Call(events); });
362 CallListeners<WeakPtr<places::INativePlacesEventCallback>,
363 RefPtr<places::INativePlacesEventCallback>,
364 WeakNativeListeners>(
365 flags, events, weakNativeListenersLength,
366 [](auto& cb) { return cb.get(); },
367 [&](auto& cb, const Sequence<OwningNonNull<PlacesEvent>>& events) {
368 cb->HandlePlacesEvent(events);
371 CallListeners<WeakPtr<PlacesWeakCallbackWrapper>,
372 RefPtr<PlacesWeakCallbackWrapper>, WeakJSListeners>(
373 flags, events, weakJSListenersLength, [](auto& cb) { return cb.get(); },
374 // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
375 // some internals of the std::function implementation that we can't
376 // annotate. We handle this by annotating CallListeners and making sure
377 // it holds a strong ref to the callback.
378 [&](auto& cb, const auto& events) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
379 RefPtr<PlacesEventCallback> callback(cb->mCallback);
380 callback->Call(events);
383 gNotificationQueue.RemoveElementAt(0);
385 CleanupListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>,
386 JSListeners>(
387 [](auto& cb) { return cb; },
388 [&](auto& cb) { RemoveListener(cb.flags, *cb.value); });
389 CleanupListeners<WeakPtr<PlacesWeakCallbackWrapper>,
390 RefPtr<PlacesWeakCallbackWrapper>, WeakJSListeners>(
391 [](auto& cb) { return cb.get(); },
392 [&](auto& cb) { RemoveListener(cb.flags, *cb.value.get()); });
393 CleanupListeners<WeakPtr<places::INativePlacesEventCallback>,
394 RefPtr<places::INativePlacesEventCallback>,
395 WeakNativeListeners>(
396 [](auto& cb) { return cb.get(); },
397 [&](auto& cb) { RemoveListener(cb.flags, cb.value.get()); });
399 if (!gNotificationQueue.IsEmpty()) {
400 NotifyNext();
404 already_AddRefed<PlacesEventCounts> PlacesObservers::Counts(
405 const GlobalObject& global) {
406 EnsureCountsInitialized();
407 return do_AddRef(sCounts);
410 } // namespace mozilla::dom