Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et ft=cpp : */
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 file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_dom_PresentationServiceBase_h
8 : #define mozilla_dom_PresentationServiceBase_h
9 :
10 : #include "mozilla/Unused.h"
11 : #include "nsClassHashtable.h"
12 : #include "nsCOMArray.h"
13 : #include "nsIPresentationListener.h"
14 : #include "nsIPresentationService.h"
15 : #include "nsRefPtrHashtable.h"
16 : #include "nsString.h"
17 : #include "nsTArray.h"
18 :
19 : namespace mozilla {
20 : namespace dom {
21 :
22 : template<class T>
23 : class PresentationServiceBase
24 : {
25 : public:
26 1 : PresentationServiceBase() = default;
27 :
28 : already_AddRefed<T>
29 0 : GetSessionInfo(const nsAString& aSessionId, const uint8_t aRole)
30 : {
31 0 : MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
32 : aRole == nsIPresentationService::ROLE_RECEIVER);
33 :
34 0 : RefPtr<T> info;
35 0 : if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
36 0 : return mSessionInfoAtController.Get(aSessionId, getter_AddRefs(info)) ?
37 0 : info.forget() : nullptr;
38 : } else {
39 0 : return mSessionInfoAtReceiver.Get(aSessionId, getter_AddRefs(info)) ?
40 0 : info.forget() : nullptr;
41 : }
42 : }
43 :
44 : protected:
45 : class SessionIdManager final
46 : {
47 : public:
48 2 : explicit SessionIdManager()
49 2 : {
50 2 : MOZ_COUNT_CTOR(SessionIdManager);
51 2 : }
52 :
53 0 : ~SessionIdManager()
54 : {
55 0 : MOZ_COUNT_DTOR(SessionIdManager);
56 0 : }
57 :
58 0 : nsresult GetWindowId(const nsAString& aSessionId, uint64_t* aWindowId)
59 : {
60 0 : MOZ_ASSERT(NS_IsMainThread());
61 :
62 0 : if (mRespondingWindowIds.Get(aSessionId, aWindowId)) {
63 0 : return NS_OK;
64 : }
65 0 : return NS_ERROR_NOT_AVAILABLE;
66 : }
67 :
68 0 : nsresult GetSessionIds(uint64_t aWindowId, nsTArray<nsString>& aSessionIds)
69 : {
70 0 : MOZ_ASSERT(NS_IsMainThread());
71 :
72 : nsTArray<nsString>* sessionIdArray;
73 0 : if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
74 0 : return NS_ERROR_INVALID_ARG;
75 : }
76 :
77 0 : aSessionIds.Assign(*sessionIdArray);
78 0 : return NS_OK;
79 : }
80 :
81 0 : void AddSessionId(uint64_t aWindowId, const nsAString& aSessionId)
82 : {
83 0 : MOZ_ASSERT(NS_IsMainThread());
84 :
85 0 : if (NS_WARN_IF(aWindowId == 0)) {
86 0 : return;
87 : }
88 :
89 : nsTArray<nsString>* sessionIdArray;
90 0 : if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
91 0 : sessionIdArray = new nsTArray<nsString>();
92 0 : mRespondingSessionIds.Put(aWindowId, sessionIdArray);
93 : }
94 :
95 0 : sessionIdArray->AppendElement(nsString(aSessionId));
96 0 : mRespondingWindowIds.Put(aSessionId, aWindowId);
97 : }
98 :
99 0 : void RemoveSessionId(const nsAString& aSessionId)
100 : {
101 0 : MOZ_ASSERT(NS_IsMainThread());
102 :
103 0 : uint64_t windowId = 0;
104 0 : if (mRespondingWindowIds.Get(aSessionId, &windowId)) {
105 0 : mRespondingWindowIds.Remove(aSessionId);
106 : nsTArray<nsString>* sessionIdArray;
107 0 : if (mRespondingSessionIds.Get(windowId, &sessionIdArray)) {
108 0 : sessionIdArray->RemoveElement(nsString(aSessionId));
109 0 : if (sessionIdArray->IsEmpty()) {
110 0 : mRespondingSessionIds.Remove(windowId);
111 : }
112 : }
113 : }
114 0 : }
115 :
116 0 : nsresult UpdateWindowId(const nsAString& aSessionId, const uint64_t aWindowId)
117 : {
118 0 : MOZ_ASSERT(NS_IsMainThread());
119 :
120 0 : RemoveSessionId(aSessionId);
121 0 : AddSessionId(aWindowId, aSessionId);
122 0 : return NS_OK;
123 : }
124 :
125 0 : void Clear()
126 : {
127 0 : mRespondingSessionIds.Clear();
128 0 : mRespondingWindowIds.Clear();
129 0 : }
130 :
131 : private:
132 : nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mRespondingSessionIds;
133 : nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds;
134 : };
135 :
136 : class AvailabilityManager final
137 : {
138 : public:
139 1 : explicit AvailabilityManager()
140 1 : {
141 1 : MOZ_COUNT_CTOR(AvailabilityManager);
142 1 : }
143 :
144 0 : ~AvailabilityManager()
145 : {
146 0 : MOZ_COUNT_DTOR(AvailabilityManager);
147 0 : }
148 :
149 0 : void AddAvailabilityListener(
150 : const nsTArray<nsString>& aAvailabilityUrls,
151 : nsIPresentationAvailabilityListener* aListener)
152 : {
153 0 : nsTArray<nsString> dummy;
154 0 : AddAvailabilityListener(aAvailabilityUrls, aListener, dummy);
155 0 : }
156 :
157 0 : void AddAvailabilityListener(
158 : const nsTArray<nsString>& aAvailabilityUrls,
159 : nsIPresentationAvailabilityListener* aListener,
160 : nsTArray<nsString>& aAddedUrls)
161 : {
162 0 : if (!aListener) {
163 0 : MOZ_ASSERT(false, "aListener should not be null.");
164 : return;
165 : }
166 :
167 0 : if (aAvailabilityUrls.IsEmpty()) {
168 0 : MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
169 : return;
170 : }
171 :
172 0 : aAddedUrls.Clear();
173 0 : nsTArray<nsString> knownAvailableUrls;
174 0 : for (const auto& url : aAvailabilityUrls) {
175 : AvailabilityEntry* entry;
176 0 : if (!mAvailabilityUrlTable.Get(url, &entry)) {
177 0 : entry = new AvailabilityEntry();
178 0 : mAvailabilityUrlTable.Put(url, entry);
179 0 : aAddedUrls.AppendElement(url);
180 : }
181 0 : if (!entry->mListeners.Contains(aListener)) {
182 0 : entry->mListeners.AppendElement(aListener);
183 : }
184 0 : if (entry->mAvailable) {
185 0 : knownAvailableUrls.AppendElement(url);
186 : }
187 : }
188 :
189 0 : if (!knownAvailableUrls.IsEmpty()) {
190 : Unused <<
191 0 : NS_WARN_IF(
192 : NS_FAILED(aListener->NotifyAvailableChange(knownAvailableUrls,
193 : true)));
194 : } else {
195 : // If we can't find any known available url and there is no newly
196 : // added url, we still need to notify the listener of the result.
197 : // So, the promise returned by |getAvailability| can be resolved.
198 0 : if (aAddedUrls.IsEmpty()) {
199 : Unused <<
200 0 : NS_WARN_IF(
201 : NS_FAILED(aListener->NotifyAvailableChange(aAvailabilityUrls,
202 : false)));
203 : }
204 : }
205 0 : }
206 :
207 0 : void RemoveAvailabilityListener(
208 : const nsTArray<nsString>& aAvailabilityUrls,
209 : nsIPresentationAvailabilityListener* aListener)
210 : {
211 0 : nsTArray<nsString> dummy;
212 0 : RemoveAvailabilityListener(aAvailabilityUrls, aListener, dummy);
213 0 : }
214 :
215 0 : void RemoveAvailabilityListener(
216 : const nsTArray<nsString>& aAvailabilityUrls,
217 : nsIPresentationAvailabilityListener* aListener,
218 : nsTArray<nsString>& aRemovedUrls)
219 : {
220 0 : if (!aListener) {
221 0 : MOZ_ASSERT(false, "aListener should not be null.");
222 : return;
223 : }
224 :
225 0 : if (aAvailabilityUrls.IsEmpty()) {
226 0 : MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
227 : return;
228 : }
229 :
230 0 : aRemovedUrls.Clear();
231 0 : for (const auto& url : aAvailabilityUrls) {
232 : AvailabilityEntry* entry;
233 0 : if (mAvailabilityUrlTable.Get(url, &entry)) {
234 0 : entry->mListeners.RemoveElement(aListener);
235 0 : if (entry->mListeners.IsEmpty()) {
236 0 : mAvailabilityUrlTable.Remove(url);
237 0 : aRemovedUrls.AppendElement(url);
238 : }
239 : }
240 : }
241 0 : }
242 :
243 0 : nsresult DoNotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls,
244 : bool aAvailable)
245 : {
246 : typedef nsClassHashtable<nsISupportsHashKey,
247 : nsTArray<nsString>> ListenerToUrlsMap;
248 0 : ListenerToUrlsMap availabilityListenerTable;
249 : // Create a mapping from nsIPresentationAvailabilityListener to
250 : // availabilityUrls.
251 0 : for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) {
252 0 : if (aAvailabilityUrls.Contains(it.Key())) {
253 0 : AvailabilityEntry* entry = it.UserData();
254 0 : entry->mAvailable = aAvailable;
255 :
256 0 : for (uint32_t i = 0; i < entry->mListeners.Length(); ++i) {
257 : nsIPresentationAvailabilityListener* listener =
258 0 : entry->mListeners.ObjectAt(i);
259 : nsTArray<nsString>* urlArray;
260 0 : if (!availabilityListenerTable.Get(listener, &urlArray)) {
261 0 : urlArray = new nsTArray<nsString>();
262 0 : availabilityListenerTable.Put(listener, urlArray);
263 : }
264 0 : urlArray->AppendElement(it.Key());
265 : }
266 : }
267 : }
268 :
269 0 : for (auto it = availabilityListenerTable.Iter(); !it.Done(); it.Next()) {
270 : auto listener =
271 0 : static_cast<nsIPresentationAvailabilityListener*>(it.Key());
272 :
273 : Unused <<
274 0 : NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(*it.UserData(),
275 : aAvailable)));
276 : }
277 0 : return NS_OK;
278 : }
279 :
280 0 : void GetAvailbilityUrlByAvailability(nsTArray<nsString>& aOutArray,
281 : bool aAvailable)
282 : {
283 0 : aOutArray.Clear();
284 :
285 0 : for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) {
286 0 : if (it.UserData()->mAvailable == aAvailable) {
287 0 : aOutArray.AppendElement(it.Key());
288 : }
289 : }
290 0 : }
291 :
292 0 : void Clear()
293 : {
294 0 : mAvailabilityUrlTable.Clear();
295 0 : }
296 :
297 : private:
298 0 : struct AvailabilityEntry
299 : {
300 0 : explicit AvailabilityEntry()
301 0 : : mAvailable(false)
302 0 : {}
303 :
304 : bool mAvailable;
305 : nsCOMArray<nsIPresentationAvailabilityListener> mListeners;
306 : };
307 :
308 : nsClassHashtable<nsStringHashKey, AvailabilityEntry> mAvailabilityUrlTable;
309 : };
310 :
311 0 : virtual ~PresentationServiceBase() = default;
312 :
313 0 : void Shutdown()
314 : {
315 0 : mRespondingListeners.Clear();
316 0 : mControllerSessionIdManager.Clear();
317 0 : mReceiverSessionIdManager.Clear();
318 0 : }
319 :
320 0 : nsresult GetWindowIdBySessionIdInternal(const nsAString& aSessionId,
321 : uint8_t aRole,
322 : uint64_t* aWindowId)
323 : {
324 0 : MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
325 : aRole == nsIPresentationService::ROLE_RECEIVER);
326 :
327 0 : if (NS_WARN_IF(!aWindowId)) {
328 0 : return NS_ERROR_INVALID_POINTER;
329 : }
330 :
331 0 : if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
332 0 : return mControllerSessionIdManager.GetWindowId(aSessionId, aWindowId);
333 : }
334 :
335 0 : return mReceiverSessionIdManager.GetWindowId(aSessionId, aWindowId);
336 : }
337 :
338 0 : void AddRespondingSessionId(uint64_t aWindowId,
339 : const nsAString& aSessionId,
340 : uint8_t aRole)
341 : {
342 0 : MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
343 : aRole == nsIPresentationService::ROLE_RECEIVER);
344 :
345 0 : if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
346 0 : mControllerSessionIdManager.AddSessionId(aWindowId, aSessionId);
347 : } else {
348 0 : mReceiverSessionIdManager.AddSessionId(aWindowId, aSessionId);
349 : }
350 0 : }
351 :
352 0 : void RemoveRespondingSessionId(const nsAString& aSessionId,
353 : uint8_t aRole)
354 : {
355 0 : MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
356 : aRole == nsIPresentationService::ROLE_RECEIVER);
357 :
358 0 : if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
359 0 : mControllerSessionIdManager.RemoveSessionId(aSessionId);
360 : } else {
361 0 : mReceiverSessionIdManager.RemoveSessionId(aSessionId);
362 : }
363 0 : }
364 :
365 0 : nsresult UpdateWindowIdBySessionIdInternal(const nsAString& aSessionId,
366 : uint8_t aRole,
367 : const uint64_t aWindowId)
368 : {
369 0 : MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
370 : aRole == nsIPresentationService::ROLE_RECEIVER);
371 :
372 0 : if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
373 0 : return mControllerSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
374 : }
375 :
376 0 : return mReceiverSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
377 : }
378 :
379 : // Store the responding listener based on the window ID of the (in-process or
380 : // OOP) receiver page.
381 : nsRefPtrHashtable<nsUint64HashKey, nsIPresentationRespondingListener>
382 : mRespondingListeners;
383 :
384 : // Store the mapping between the window ID of the in-process and OOP page and the ID
385 : // of the responding session. It's used for both controller and receiver page
386 : // to retrieve the correspondent session ID. Besides, also keep the mapping
387 : // between the responding session ID and the window ID to help look up the
388 : // window ID.
389 : SessionIdManager mControllerSessionIdManager;
390 : SessionIdManager mReceiverSessionIdManager;
391 :
392 : nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtController;
393 : nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtReceiver;
394 :
395 : AvailabilityManager mAvailabilityManager;
396 : };
397 :
398 : } // namespace dom
399 : } // namespace mozilla
400 :
401 : #endif // mozilla_dom_PresentationServiceBase_h
|