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/. */
6
7 #include "PlacesObservers.h"
8
9 #include "PlacesWeakCallbackWrapper.h"
10 #include "nsIWeakReferenceUtils.h"
11 #include "mozilla/ClearOnShutdown.h"
12
13 namespace mozilla::dom {
14
15 template <class T>
16 struct Flagged {
Flaggedmozilla::dom::Flagged17 Flagged(uint32_t aFlags, T&& aValue)
18 : flags(aFlags), value(std::forward<T>(aValue)) {}
Flaggedmozilla::dom::Flagged19 Flagged(Flagged&& aOther)
20 : Flagged(std::move(aOther.flags), std::move(aOther.value)) {}
21 Flagged(const Flagged& aOther) = default;
22 ~Flagged() = default;
23
24 uint32_t flags;
25 T value;
26 };
27
28 template <class T>
29 using FlaggedArray = nsTArray<Flagged<T>>;
30
31 template <class T>
32 struct ListenerCollection {
33 static StaticAutoPtr<FlaggedArray<T>> gListeners;
34 static StaticAutoPtr<FlaggedArray<T>> gListenersToRemove;
35
GetListenersmozilla::dom::ListenerCollection36 static FlaggedArray<T>* GetListeners(bool aDoNotInit = false) {
37 MOZ_ASSERT(NS_IsMainThread());
38 if (!gListeners && !aDoNotInit) {
39 gListeners = new FlaggedArray<T>();
40 ClearOnShutdown(&gListeners);
41 }
42 return gListeners;
43 }
44
GetListenersToRemovemozilla::dom::ListenerCollection45 static FlaggedArray<T>* GetListenersToRemove(bool aDoNotInit = false) {
46 MOZ_ASSERT(NS_IsMainThread());
47 if (!gListenersToRemove && !aDoNotInit) {
48 gListenersToRemove = new FlaggedArray<T>();
49 ClearOnShutdown(&gListenersToRemove);
50 }
51 return gListenersToRemove;
52 }
53 };
54
55 template <class T>
56 StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListeners;
57 template <class T>
58 StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListenersToRemove;
59
60 typedef ListenerCollection<RefPtr<PlacesEventCallback>> JSListeners;
61 typedef ListenerCollection<WeakPtr<PlacesWeakCallbackWrapper>> WeakJSListeners;
62 typedef ListenerCollection<WeakPtr<places::INativePlacesEventCallback>>
63 WeakNativeListeners;
64
65 static bool gCallingListeners = false;
66
GetEventTypeFlag(PlacesEventType aEventType)67 uint32_t GetEventTypeFlag(PlacesEventType aEventType) {
68 if (aEventType == PlacesEventType::None) {
69 return 0;
70 }
71 return 1 << ((uint32_t)aEventType - 1);
72 }
73
GetFlagsForEventTypes(const nsTArray<PlacesEventType> & aEventTypes)74 uint32_t GetFlagsForEventTypes(const nsTArray<PlacesEventType>& aEventTypes) {
75 uint32_t flags = 0;
76 for (PlacesEventType eventType : aEventTypes) {
77 flags |= GetEventTypeFlag(eventType);
78 }
79 return flags;
80 }
81
GetFlagsForEvents(const nsTArray<OwningNonNull<PlacesEvent>> & aEvents)82 uint32_t GetFlagsForEvents(
83 const nsTArray<OwningNonNull<PlacesEvent>>& aEvents) {
84 uint32_t flags = 0;
85 for (const PlacesEvent& event : aEvents) {
86 flags |= GetEventTypeFlag(event.Type());
87 }
88 return flags;
89 }
90
91 template <class TWrapped, class TUnwrapped>
CallListeners(uint32_t aEventFlags,FlaggedArray<TWrapped> & aListeners,const Sequence<OwningNonNull<PlacesEvent>> & aEvents,const std::function<TUnwrapped (TWrapped &)> & aUnwrapListener,const std::function<void (TUnwrapped &,const Sequence<OwningNonNull<PlacesEvent>> &)> & aCallListener)92 MOZ_CAN_RUN_SCRIPT void CallListeners(
93 uint32_t aEventFlags, FlaggedArray<TWrapped>& aListeners,
94 const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
95 const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener,
96 const std::function<void(TUnwrapped&,
97 const Sequence<OwningNonNull<PlacesEvent>>&)>&
98 aCallListener) {
99 for (uint32_t i = 0; i < aListeners.Length(); i++) {
100 Flagged<TWrapped>& l = aListeners[i];
101 TUnwrapped unwrapped = aUnwrapListener(l.value);
102 if (!unwrapped) {
103 aListeners.RemoveElementAt(i);
104 i--;
105 continue;
106 }
107
108 if ((l.flags & aEventFlags) == aEventFlags) {
109 aCallListener(unwrapped, aEvents);
110 } else if (l.flags & aEventFlags) {
111 Sequence<OwningNonNull<PlacesEvent>> filtered;
112 for (const OwningNonNull<PlacesEvent>& event : aEvents) {
113 if (l.flags & GetEventTypeFlag(event->Type())) {
114 bool success = !!filtered.AppendElement(event, fallible);
115 MOZ_RELEASE_ASSERT(success);
116 }
117 }
118 aCallListener(unwrapped, filtered);
119 }
120 }
121 }
122
AddListener(GlobalObject & aGlobal,const nsTArray<PlacesEventType> & aEventTypes,PlacesEventCallback & aCallback,ErrorResult & rv)123 void PlacesObservers::AddListener(GlobalObject& aGlobal,
124 const nsTArray<PlacesEventType>& aEventTypes,
125 PlacesEventCallback& aCallback,
126 ErrorResult& rv) {
127 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
128
129 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
130 JSListeners::GetListeners();
131 Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
132 listeners->AppendElement(pair);
133 }
134
AddListener(GlobalObject & aGlobal,const nsTArray<PlacesEventType> & aEventTypes,PlacesWeakCallbackWrapper & aCallback,ErrorResult & rv)135 void PlacesObservers::AddListener(GlobalObject& aGlobal,
136 const nsTArray<PlacesEventType>& aEventTypes,
137 PlacesWeakCallbackWrapper& aCallback,
138 ErrorResult& rv) {
139 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
140
141 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
142 WeakJSListeners::GetListeners();
143 WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
144 MOZ_ASSERT(weakCb.get());
145 Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, std::move(weakCb));
146 listeners->AppendElement(flagged);
147 }
148
AddListener(const nsTArray<PlacesEventType> & aEventTypes,places::INativePlacesEventCallback * aCallback)149 void PlacesObservers::AddListener(
150 const nsTArray<PlacesEventType>& aEventTypes,
151 places::INativePlacesEventCallback* aCallback) {
152 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
153
154 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
155 WeakNativeListeners::GetListeners();
156 Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
157 listeners->AppendElement(pair);
158 }
159
RemoveListener(GlobalObject & aGlobal,const nsTArray<PlacesEventType> & aEventTypes,PlacesEventCallback & aCallback,ErrorResult & rv)160 void PlacesObservers::RemoveListener(
161 GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
162 PlacesEventCallback& aCallback, ErrorResult& rv) {
163 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
164 if (gCallingListeners) {
165 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
166 JSListeners::GetListenersToRemove();
167 Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
168 listeners->AppendElement(pair);
169 } else {
170 RemoveListener(flags, aCallback);
171 }
172 }
173
RemoveListener(GlobalObject & aGlobal,const nsTArray<PlacesEventType> & aEventTypes,PlacesWeakCallbackWrapper & aCallback,ErrorResult & rv)174 void PlacesObservers::RemoveListener(
175 GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes,
176 PlacesWeakCallbackWrapper& aCallback, ErrorResult& rv) {
177 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
178 if (gCallingListeners) {
179 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
180 WeakJSListeners::GetListenersToRemove();
181 WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
182 MOZ_ASSERT(weakCb.get());
183 Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags,
184 std::move(weakCb));
185 listeners->AppendElement(flagged);
186 } else {
187 RemoveListener(flags, aCallback);
188 }
189 }
190
RemoveListener(const nsTArray<PlacesEventType> & aEventTypes,places::INativePlacesEventCallback * aCallback)191 void PlacesObservers::RemoveListener(
192 const nsTArray<PlacesEventType>& aEventTypes,
193 places::INativePlacesEventCallback* aCallback) {
194 uint32_t flags = GetFlagsForEventTypes(aEventTypes);
195 if (gCallingListeners) {
196 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
197 WeakNativeListeners::GetListenersToRemove();
198 Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
199 listeners->AppendElement(pair);
200 } else {
201 RemoveListener(flags, aCallback);
202 }
203 }
204
RemoveListener(uint32_t aFlags,PlacesEventCallback & aCallback)205 void PlacesObservers::RemoveListener(uint32_t aFlags,
206 PlacesEventCallback& aCallback) {
207 FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
208 JSListeners::GetListeners(/* aDoNotInit: */ true);
209 if (!listeners) {
210 return;
211 }
212 for (uint32_t i = 0; i < listeners->Length(); i++) {
213 Flagged<RefPtr<PlacesEventCallback>>& l = listeners->ElementAt(i);
214 if (!(*l.value == aCallback)) {
215 continue;
216 }
217 if (l.flags == (aFlags & l.flags)) {
218 listeners->RemoveElementAt(i);
219 i--;
220 } else {
221 l.flags &= ~aFlags;
222 }
223 }
224 }
225
RemoveListener(uint32_t aFlags,PlacesWeakCallbackWrapper & aCallback)226 void PlacesObservers::RemoveListener(uint32_t aFlags,
227 PlacesWeakCallbackWrapper& aCallback) {
228 FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
229 WeakJSListeners::GetListeners(/* aDoNotInit: */ true);
230 if (!listeners) {
231 return;
232 }
233 for (uint32_t i = 0; i < listeners->Length(); i++) {
234 Flagged<WeakPtr<PlacesWeakCallbackWrapper>>& l = listeners->ElementAt(i);
235 RefPtr<PlacesWeakCallbackWrapper> unwrapped = l.value.get();
236 if (unwrapped != &aCallback) {
237 continue;
238 }
239 if (l.flags == (aFlags & l.flags)) {
240 listeners->RemoveElementAt(i);
241 i--;
242 } else {
243 l.flags &= ~aFlags;
244 }
245 }
246 }
247
RemoveListener(uint32_t aFlags,places::INativePlacesEventCallback * aCallback)248 void PlacesObservers::RemoveListener(
249 uint32_t aFlags, places::INativePlacesEventCallback* aCallback) {
250 FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
251 WeakNativeListeners::GetListeners(/* aDoNotInit: */ true);
252 if (!listeners) {
253 return;
254 }
255 for (uint32_t i = 0; i < listeners->Length(); i++) {
256 Flagged<WeakPtr<places::INativePlacesEventCallback>>& l =
257 listeners->ElementAt(i);
258 RefPtr<places::INativePlacesEventCallback> unwrapped = l.value.get();
259 if (unwrapped != aCallback) {
260 continue;
261 }
262 if (l.flags == (aFlags & l.flags)) {
263 listeners->RemoveElementAt(i);
264 i--;
265 } else {
266 l.flags &= ~aFlags;
267 }
268 }
269 }
270
NotifyListeners(GlobalObject & aGlobal,const Sequence<OwningNonNull<PlacesEvent>> & aEvents,ErrorResult & rv)271 void PlacesObservers::NotifyListeners(
272 GlobalObject& aGlobal, const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
273 ErrorResult& rv) {
274 NotifyListeners(aEvents);
275 }
276
NotifyListeners(const Sequence<OwningNonNull<PlacesEvent>> & aEvents)277 void PlacesObservers::NotifyListeners(
278 const Sequence<OwningNonNull<PlacesEvent>>& aEvents) {
279 MOZ_ASSERT(aEvents.Length() > 0, "Must pass a populated array of events");
280 MOZ_RELEASE_ASSERT(!gCallingListeners);
281 gCallingListeners = true;
282
283 if (aEvents.Length() > 0) {
284 uint32_t flags = GetFlagsForEvents(aEvents);
285
286 CallListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>>(
287 flags, *JSListeners::GetListeners(), aEvents,
288 [](auto& cb) { return cb; },
289 // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
290 // some internals of the std::function implementation that we can't
291 // annotate. We handle this by annotating CallListeners and making sure
292 // it holds a strong ref to the callback.
293 [&](auto& cb, const auto& events)
294 MOZ_CAN_RUN_SCRIPT_BOUNDARY { MOZ_KnownLive(cb)->Call(events); });
295
296 CallListeners<WeakPtr<places::INativePlacesEventCallback>,
297 RefPtr<places::INativePlacesEventCallback>>(
298 flags, *WeakNativeListeners::GetListeners(), aEvents,
299 [](auto& cb) { return cb.get(); },
300 [&](auto& cb, const Sequence<OwningNonNull<PlacesEvent>>& events) {
301 cb->HandlePlacesEvent(events);
302 });
303
304 CallListeners<WeakPtr<PlacesWeakCallbackWrapper>,
305 RefPtr<PlacesWeakCallbackWrapper>>(
306 flags, *WeakJSListeners::GetListeners(), aEvents,
307 [](auto& cb) { return cb.get(); },
308 // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from
309 // some internals of the std::function implementation that we can't
310 // annotate. We handle this by annotating CallListeners and making sure
311 // it holds a strong ref to the callback.
312 [&](auto& cb, const auto& events) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
313 RefPtr<PlacesEventCallback> callback(cb->mCallback);
314 callback->Call(events);
315 });
316 }
317
318 auto& listenersToRemove = *JSListeners::GetListenersToRemove();
319 if (listenersToRemove.Length() > 0) {
320 for (auto& listener : listenersToRemove) {
321 RemoveListener(listener.flags, *listener.value);
322 }
323 }
324 listenersToRemove.Clear();
325
326 auto& weakListenersToRemove = *WeakJSListeners::GetListenersToRemove();
327 if (weakListenersToRemove.Length() > 0) {
328 for (auto& listener : weakListenersToRemove) {
329 RemoveListener(listener.flags, *listener.value.get());
330 }
331 }
332 weakListenersToRemove.Clear();
333
334 auto& nativeListenersToRemove = *WeakNativeListeners::GetListenersToRemove();
335 if (nativeListenersToRemove.Length() > 0) {
336 for (auto& listener : nativeListenersToRemove) {
337 RemoveListener(listener.flags, listener.value.get());
338 }
339 }
340 nativeListenersToRemove.Clear();
341
342 gCallingListeners = false;
343 }
344
345 } // namespace mozilla::dom
346