Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/dom/PresentationServiceBase.h
Line
Count
Source (jump to first uncovered line)
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 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
0
  PresentationServiceBase() = default;
27
28
  already_AddRefed<T>
29
  GetSessionInfo(const nsAString& aSessionId, const uint8_t aRole)
30
0
  {
31
0
    MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
32
0
               aRole == nsIPresentationService::ROLE_RECEIVER);
33
0
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
0
    } else {
39
0
      return mSessionInfoAtReceiver.Get(aSessionId, getter_AddRefs(info)) ?
40
0
             info.forget() : nullptr;
41
0
    }
42
0
  }
43
44
protected:
45
  class SessionIdManager final
46
  {
47
  public:
48
    explicit SessionIdManager()
49
0
    {
50
0
      MOZ_COUNT_CTOR(SessionIdManager);
51
0
    }
52
53
    ~SessionIdManager()
54
0
    {
55
0
      MOZ_COUNT_DTOR(SessionIdManager);
56
0
    }
57
58
    nsresult GetWindowId(const nsAString& aSessionId, uint64_t* aWindowId)
59
0
    {
60
0
      MOZ_ASSERT(NS_IsMainThread());
61
0
62
0
      if (mRespondingWindowIds.Get(aSessionId, aWindowId)) {
63
0
        return NS_OK;
64
0
      }
65
0
      return NS_ERROR_NOT_AVAILABLE;
66
0
    }
67
68
    nsresult GetSessionIds(uint64_t aWindowId, nsTArray<nsString>& aSessionIds)
69
0
    {
70
0
      MOZ_ASSERT(NS_IsMainThread());
71
0
72
0
      nsTArray<nsString>* sessionIdArray;
73
0
      if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
74
0
        return NS_ERROR_INVALID_ARG;
75
0
      }
76
0
77
0
      aSessionIds.Assign(*sessionIdArray);
78
0
      return NS_OK;
79
0
    }
80
81
    void AddSessionId(uint64_t aWindowId, const nsAString& aSessionId)
82
0
    {
83
0
      MOZ_ASSERT(NS_IsMainThread());
84
0
85
0
      if (NS_WARN_IF(aWindowId == 0)) {
86
0
        return;
87
0
      }
88
0
89
0
      nsTArray<nsString>* sessionIdArray;
90
0
      if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
91
0
        sessionIdArray = new nsTArray<nsString>();
92
0
        mRespondingSessionIds.Put(aWindowId, sessionIdArray);
93
0
      }
94
0
95
0
      sessionIdArray->AppendElement(nsString(aSessionId));
96
0
      mRespondingWindowIds.Put(aSessionId, aWindowId);
97
0
    }
98
99
    void RemoveSessionId(const nsAString& aSessionId)
100
0
    {
101
0
      MOZ_ASSERT(NS_IsMainThread());
102
0
103
0
      uint64_t windowId = 0;
104
0
      if (mRespondingWindowIds.Get(aSessionId, &windowId)) {
105
0
        mRespondingWindowIds.Remove(aSessionId);
106
0
        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
0
          }
112
0
        }
113
0
      }
114
0
    }
115
116
    nsresult UpdateWindowId(const nsAString& aSessionId, const uint64_t aWindowId)
117
0
    {
118
0
      MOZ_ASSERT(NS_IsMainThread());
119
0
120
0
      RemoveSessionId(aSessionId);
121
0
      AddSessionId(aWindowId, aSessionId);
122
0
      return NS_OK;
123
0
    }
124
125
    void Clear()
126
0
    {
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
    explicit AvailabilityManager()
140
0
    {
141
0
      MOZ_COUNT_CTOR(AvailabilityManager);
142
0
    }
143
144
    ~AvailabilityManager()
145
0
    {
146
0
      MOZ_COUNT_DTOR(AvailabilityManager);
147
0
    }
148
149
    void AddAvailabilityListener(
150
                               const nsTArray<nsString>& aAvailabilityUrls,
151
                               nsIPresentationAvailabilityListener* aListener)
152
0
    {
153
0
      nsTArray<nsString> dummy;
154
0
      AddAvailabilityListener(aAvailabilityUrls, aListener, dummy);
155
0
    }
156
157
    void AddAvailabilityListener(
158
                               const nsTArray<nsString>& aAvailabilityUrls,
159
                               nsIPresentationAvailabilityListener* aListener,
160
                               nsTArray<nsString>& aAddedUrls)
161
0
    {
162
0
      if (!aListener) {
163
0
        MOZ_ASSERT(false, "aListener should not be null.");
164
0
        return;
165
0
      }
166
0
167
0
      if (aAvailabilityUrls.IsEmpty()) {
168
0
        MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
169
0
        return;
170
0
      }
171
0
172
0
      aAddedUrls.Clear();
173
0
      nsTArray<nsString> knownAvailableUrls;
174
0
      for (const auto& url : aAvailabilityUrls) {
175
0
        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
0
        }
181
0
        if (!entry->mListeners.Contains(aListener)) {
182
0
          entry->mListeners.AppendElement(aListener);
183
0
        }
184
0
        if (entry->mAvailable) {
185
0
          knownAvailableUrls.AppendElement(url);
186
0
        }
187
0
      }
188
0
189
0
      if (!knownAvailableUrls.IsEmpty()) {
190
0
        Unused <<
191
0
          NS_WARN_IF(
192
0
            NS_FAILED(aListener->NotifyAvailableChange(knownAvailableUrls,
193
0
                                                       true)));
194
0
      } else {
195
0
        // If we can't find any known available url and there is no newly
196
0
        // added url, we still need to notify the listener of the result.
197
0
        // So, the promise returned by |getAvailability| can be resolved.
198
0
        if (aAddedUrls.IsEmpty()) {
199
0
          Unused <<
200
0
            NS_WARN_IF(
201
0
              NS_FAILED(aListener->NotifyAvailableChange(aAvailabilityUrls,
202
0
                                                         false)));
203
0
        }
204
0
      }
205
0
    }
206
207
    void RemoveAvailabilityListener(
208
                               const nsTArray<nsString>& aAvailabilityUrls,
209
                               nsIPresentationAvailabilityListener* aListener)
210
0
    {
211
0
      nsTArray<nsString> dummy;
212
0
      RemoveAvailabilityListener(aAvailabilityUrls, aListener, dummy);
213
0
    }
214
215
    void RemoveAvailabilityListener(
216
                               const nsTArray<nsString>& aAvailabilityUrls,
217
                               nsIPresentationAvailabilityListener* aListener,
218
                               nsTArray<nsString>& aRemovedUrls)
219
0
    {
220
0
      if (!aListener) {
221
0
        MOZ_ASSERT(false, "aListener should not be null.");
222
0
        return;
223
0
      }
224
0
225
0
      if (aAvailabilityUrls.IsEmpty()) {
226
0
        MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
227
0
        return;
228
0
      }
229
0
230
0
      aRemovedUrls.Clear();
231
0
      for (const auto& url : aAvailabilityUrls) {
232
0
        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
0
          }
239
0
        }
240
0
      }
241
0
    }
242
243
    nsresult DoNotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls,
244
                                     bool aAvailable)
245
0
    {
246
0
      typedef nsClassHashtable<nsISupportsHashKey,
247
0
                               nsTArray<nsString>> ListenerToUrlsMap;
248
0
      ListenerToUrlsMap availabilityListenerTable;
249
0
      // Create a mapping from nsIPresentationAvailabilityListener to
250
0
      // 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
0
256
0
          for (uint32_t i = 0; i < entry->mListeners.Length(); ++i) {
257
0
            nsIPresentationAvailabilityListener* listener =
258
0
              entry->mListeners.ObjectAt(i);
259
0
            nsTArray<nsString>* urlArray;
260
0
            if (!availabilityListenerTable.Get(listener, &urlArray)) {
261
0
              urlArray = new nsTArray<nsString>();
262
0
              availabilityListenerTable.Put(listener, urlArray);
263
0
            }
264
0
            urlArray->AppendElement(it.Key());
265
0
          }
266
0
        }
267
0
      }
268
0
269
0
      for (auto it = availabilityListenerTable.Iter(); !it.Done(); it.Next()) {
270
0
        auto listener =
271
0
          static_cast<nsIPresentationAvailabilityListener*>(it.Key());
272
0
273
0
        Unused <<
274
0
          NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(*it.UserData(),
275
0
                                                               aAvailable)));
276
0
      }
277
0
      return NS_OK;
278
0
    }
279
280
    void GetAvailbilityUrlByAvailability(nsTArray<nsString>& aOutArray,
281
                                         bool aAvailable)
282
0
    {
283
0
      aOutArray.Clear();
284
0
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
0
        }
289
0
      }
290
0
    }
291
292
    void Clear()
293
0
    {
294
0
      mAvailabilityUrlTable.Clear();
295
0
    }
296
297
  private:
298
    struct AvailabilityEntry
299
    {
300
      explicit AvailabilityEntry()
301
        : 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
  void Shutdown()
314
0
  {
315
0
    mRespondingListeners.Clear();
316
0
    mControllerSessionIdManager.Clear();
317
0
    mReceiverSessionIdManager.Clear();
318
0
  }
319
320
  nsresult GetWindowIdBySessionIdInternal(const nsAString& aSessionId,
321
                                          uint8_t aRole,
322
                                          uint64_t* aWindowId)
323
0
  {
324
0
    MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
325
0
               aRole == nsIPresentationService::ROLE_RECEIVER);
326
0
327
0
    if (NS_WARN_IF(!aWindowId)) {
328
0
      return NS_ERROR_INVALID_POINTER;
329
0
    }
330
0
331
0
    if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
332
0
      return mControllerSessionIdManager.GetWindowId(aSessionId, aWindowId);
333
0
    }
334
0
335
0
    return mReceiverSessionIdManager.GetWindowId(aSessionId, aWindowId);
336
0
  }
337
338
  void AddRespondingSessionId(uint64_t aWindowId,
339
                              const nsAString& aSessionId,
340
                              uint8_t aRole)
341
0
  {
342
0
    MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
343
0
               aRole == nsIPresentationService::ROLE_RECEIVER);
344
0
345
0
    if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
346
0
      mControllerSessionIdManager.AddSessionId(aWindowId, aSessionId);
347
0
    } else {
348
0
      mReceiverSessionIdManager.AddSessionId(aWindowId, aSessionId);
349
0
    }
350
0
  }
351
352
  void RemoveRespondingSessionId(const nsAString& aSessionId,
353
                                 uint8_t aRole)
354
0
  {
355
0
    MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
356
0
               aRole == nsIPresentationService::ROLE_RECEIVER);
357
0
358
0
    if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
359
0
      mControllerSessionIdManager.RemoveSessionId(aSessionId);
360
0
    } else {
361
0
      mReceiverSessionIdManager.RemoveSessionId(aSessionId);
362
0
    }
363
0
  }
364
365
  nsresult UpdateWindowIdBySessionIdInternal(const nsAString& aSessionId,
366
                                             uint8_t aRole,
367
                                             const uint64_t aWindowId)
368
0
  {
369
0
    MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
370
0
               aRole == nsIPresentationService::ROLE_RECEIVER);
371
0
372
0
    if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
373
0
      return mControllerSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
374
0
    }
375
0
376
0
    return mReceiverSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
377
0
  }
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