summaryrefslogtreecommitdiffstats
path: root/dom/presentation/PresentationServiceBase.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/presentation/PresentationServiceBase.h')
-rw-r--r--dom/presentation/PresentationServiceBase.h401
1 files changed, 401 insertions, 0 deletions
diff --git a/dom/presentation/PresentationServiceBase.h b/dom/presentation/PresentationServiceBase.h
new file mode 100644
index 000000000..227e95430
--- /dev/null
+++ b/dom/presentation/PresentationServiceBase.h
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PresentationServiceBase_h
+#define mozilla_dom_PresentationServiceBase_h
+
+#include "mozilla/Unused.h"
+#include "nsClassHashtable.h"
+#include "nsCOMArray.h"
+#include "nsIPresentationListener.h"
+#include "nsIPresentationService.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+template<class T>
+class PresentationServiceBase
+{
+public:
+ PresentationServiceBase() = default;
+
+ already_AddRefed<T>
+ GetSessionInfo(const nsAString& aSessionId, const uint8_t aRole)
+ {
+ MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+ aRole == nsIPresentationService::ROLE_RECEIVER);
+
+ RefPtr<T> info;
+ if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
+ return mSessionInfoAtController.Get(aSessionId, getter_AddRefs(info)) ?
+ info.forget() : nullptr;
+ } else {
+ return mSessionInfoAtReceiver.Get(aSessionId, getter_AddRefs(info)) ?
+ info.forget() : nullptr;
+ }
+ }
+
+protected:
+ class SessionIdManager final
+ {
+ public:
+ explicit SessionIdManager()
+ {
+ MOZ_COUNT_CTOR(SessionIdManager);
+ }
+
+ ~SessionIdManager()
+ {
+ MOZ_COUNT_DTOR(SessionIdManager);
+ }
+
+ nsresult GetWindowId(const nsAString& aSessionId, uint64_t* aWindowId)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mRespondingWindowIds.Get(aSessionId, aWindowId)) {
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult GetSessionIds(uint64_t aWindowId, nsTArray<nsString>& aSessionIds)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsTArray<nsString>* sessionIdArray;
+ if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ aSessionIds.Assign(*sessionIdArray);
+ return NS_OK;
+ }
+
+ void AddSessionId(uint64_t aWindowId, const nsAString& aSessionId)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (NS_WARN_IF(aWindowId == 0)) {
+ return;
+ }
+
+ nsTArray<nsString>* sessionIdArray;
+ if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
+ sessionIdArray = new nsTArray<nsString>();
+ mRespondingSessionIds.Put(aWindowId, sessionIdArray);
+ }
+
+ sessionIdArray->AppendElement(nsString(aSessionId));
+ mRespondingWindowIds.Put(aSessionId, aWindowId);
+ }
+
+ void RemoveSessionId(const nsAString& aSessionId)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ uint64_t windowId = 0;
+ if (mRespondingWindowIds.Get(aSessionId, &windowId)) {
+ mRespondingWindowIds.Remove(aSessionId);
+ nsTArray<nsString>* sessionIdArray;
+ if (mRespondingSessionIds.Get(windowId, &sessionIdArray)) {
+ sessionIdArray->RemoveElement(nsString(aSessionId));
+ if (sessionIdArray->IsEmpty()) {
+ mRespondingSessionIds.Remove(windowId);
+ }
+ }
+ }
+ }
+
+ nsresult UpdateWindowId(const nsAString& aSessionId, const uint64_t aWindowId)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RemoveSessionId(aSessionId);
+ AddSessionId(aWindowId, aSessionId);
+ return NS_OK;
+ }
+
+ void Clear()
+ {
+ mRespondingSessionIds.Clear();
+ mRespondingWindowIds.Clear();
+ }
+
+ private:
+ nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mRespondingSessionIds;
+ nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds;
+ };
+
+ class AvailabilityManager final
+ {
+ public:
+ explicit AvailabilityManager()
+ {
+ MOZ_COUNT_CTOR(AvailabilityManager);
+ }
+
+ ~AvailabilityManager()
+ {
+ MOZ_COUNT_DTOR(AvailabilityManager);
+ }
+
+ void AddAvailabilityListener(
+ const nsTArray<nsString>& aAvailabilityUrls,
+ nsIPresentationAvailabilityListener* aListener)
+ {
+ nsTArray<nsString> dummy;
+ AddAvailabilityListener(aAvailabilityUrls, aListener, dummy);
+ }
+
+ void AddAvailabilityListener(
+ const nsTArray<nsString>& aAvailabilityUrls,
+ nsIPresentationAvailabilityListener* aListener,
+ nsTArray<nsString>& aAddedUrls)
+ {
+ if (!aListener) {
+ MOZ_ASSERT(false, "aListener should not be null.");
+ return;
+ }
+
+ if (aAvailabilityUrls.IsEmpty()) {
+ MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
+ return;
+ }
+
+ aAddedUrls.Clear();
+ nsTArray<nsString> knownAvailableUrls;
+ for (const auto& url : aAvailabilityUrls) {
+ AvailabilityEntry* entry;
+ if (!mAvailabilityUrlTable.Get(url, &entry)) {
+ entry = new AvailabilityEntry();
+ mAvailabilityUrlTable.Put(url, entry);
+ aAddedUrls.AppendElement(url);
+ }
+ if (!entry->mListeners.Contains(aListener)) {
+ entry->mListeners.AppendElement(aListener);
+ }
+ if (entry->mAvailable) {
+ knownAvailableUrls.AppendElement(url);
+ }
+ }
+
+ if (!knownAvailableUrls.IsEmpty()) {
+ Unused <<
+ NS_WARN_IF(
+ NS_FAILED(aListener->NotifyAvailableChange(knownAvailableUrls,
+ true)));
+ } else {
+ // If we can't find any known available url and there is no newly
+ // added url, we still need to notify the listener of the result.
+ // So, the promise returned by |getAvailability| can be resolved.
+ if (aAddedUrls.IsEmpty()) {
+ Unused <<
+ NS_WARN_IF(
+ NS_FAILED(aListener->NotifyAvailableChange(aAvailabilityUrls,
+ false)));
+ }
+ }
+ }
+
+ void RemoveAvailabilityListener(
+ const nsTArray<nsString>& aAvailabilityUrls,
+ nsIPresentationAvailabilityListener* aListener)
+ {
+ nsTArray<nsString> dummy;
+ RemoveAvailabilityListener(aAvailabilityUrls, aListener, dummy);
+ }
+
+ void RemoveAvailabilityListener(
+ const nsTArray<nsString>& aAvailabilityUrls,
+ nsIPresentationAvailabilityListener* aListener,
+ nsTArray<nsString>& aRemovedUrls)
+ {
+ if (!aListener) {
+ MOZ_ASSERT(false, "aListener should not be null.");
+ return;
+ }
+
+ if (aAvailabilityUrls.IsEmpty()) {
+ MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
+ return;
+ }
+
+ aRemovedUrls.Clear();
+ for (const auto& url : aAvailabilityUrls) {
+ AvailabilityEntry* entry;
+ if (mAvailabilityUrlTable.Get(url, &entry)) {
+ entry->mListeners.RemoveElement(aListener);
+ if (entry->mListeners.IsEmpty()) {
+ mAvailabilityUrlTable.Remove(url);
+ aRemovedUrls.AppendElement(url);
+ }
+ }
+ }
+ }
+
+ nsresult DoNotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls,
+ bool aAvailable)
+ {
+ typedef nsClassHashtable<nsISupportsHashKey,
+ nsTArray<nsString>> ListenerToUrlsMap;
+ ListenerToUrlsMap availabilityListenerTable;
+ // Create a mapping from nsIPresentationAvailabilityListener to
+ // availabilityUrls.
+ for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) {
+ if (aAvailabilityUrls.Contains(it.Key())) {
+ AvailabilityEntry* entry = it.UserData();
+ entry->mAvailable = aAvailable;
+
+ for (uint32_t i = 0; i < entry->mListeners.Length(); ++i) {
+ nsIPresentationAvailabilityListener* listener =
+ entry->mListeners.ObjectAt(i);
+ nsTArray<nsString>* urlArray;
+ if (!availabilityListenerTable.Get(listener, &urlArray)) {
+ urlArray = new nsTArray<nsString>();
+ availabilityListenerTable.Put(listener, urlArray);
+ }
+ urlArray->AppendElement(it.Key());
+ }
+ }
+ }
+
+ for (auto it = availabilityListenerTable.Iter(); !it.Done(); it.Next()) {
+ auto listener =
+ static_cast<nsIPresentationAvailabilityListener*>(it.Key());
+
+ Unused <<
+ NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(*it.UserData(),
+ aAvailable)));
+ }
+ return NS_OK;
+ }
+
+ void GetAvailbilityUrlByAvailability(nsTArray<nsString>& aOutArray,
+ bool aAvailable)
+ {
+ aOutArray.Clear();
+
+ for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) {
+ if (it.UserData()->mAvailable == aAvailable) {
+ aOutArray.AppendElement(it.Key());
+ }
+ }
+ }
+
+ void Clear()
+ {
+ mAvailabilityUrlTable.Clear();
+ }
+
+ private:
+ struct AvailabilityEntry
+ {
+ explicit AvailabilityEntry()
+ : mAvailable(false)
+ {}
+
+ bool mAvailable;
+ nsCOMArray<nsIPresentationAvailabilityListener> mListeners;
+ };
+
+ nsClassHashtable<nsStringHashKey, AvailabilityEntry> mAvailabilityUrlTable;
+ };
+
+ virtual ~PresentationServiceBase() = default;
+
+ void Shutdown()
+ {
+ mRespondingListeners.Clear();
+ mControllerSessionIdManager.Clear();
+ mReceiverSessionIdManager.Clear();
+ }
+
+ nsresult GetWindowIdBySessionIdInternal(const nsAString& aSessionId,
+ uint8_t aRole,
+ uint64_t* aWindowId)
+ {
+ MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+ aRole == nsIPresentationService::ROLE_RECEIVER);
+
+ if (NS_WARN_IF(!aWindowId)) {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
+ return mControllerSessionIdManager.GetWindowId(aSessionId, aWindowId);
+ }
+
+ return mReceiverSessionIdManager.GetWindowId(aSessionId, aWindowId);
+ }
+
+ void AddRespondingSessionId(uint64_t aWindowId,
+ const nsAString& aSessionId,
+ uint8_t aRole)
+ {
+ MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+ aRole == nsIPresentationService::ROLE_RECEIVER);
+
+ if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
+ mControllerSessionIdManager.AddSessionId(aWindowId, aSessionId);
+ } else {
+ mReceiverSessionIdManager.AddSessionId(aWindowId, aSessionId);
+ }
+ }
+
+ void RemoveRespondingSessionId(const nsAString& aSessionId,
+ uint8_t aRole)
+ {
+ MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+ aRole == nsIPresentationService::ROLE_RECEIVER);
+
+ if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
+ mControllerSessionIdManager.RemoveSessionId(aSessionId);
+ } else {
+ mReceiverSessionIdManager.RemoveSessionId(aSessionId);
+ }
+ }
+
+ nsresult UpdateWindowIdBySessionIdInternal(const nsAString& aSessionId,
+ uint8_t aRole,
+ const uint64_t aWindowId)
+ {
+ MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
+ aRole == nsIPresentationService::ROLE_RECEIVER);
+
+ if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
+ return mControllerSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
+ }
+
+ return mReceiverSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
+ }
+
+ // Store the responding listener based on the window ID of the (in-process or
+ // OOP) receiver page.
+ nsRefPtrHashtable<nsUint64HashKey, nsIPresentationRespondingListener>
+ mRespondingListeners;
+
+ // Store the mapping between the window ID of the in-process and OOP page and the ID
+ // of the responding session. It's used for both controller and receiver page
+ // to retrieve the correspondent session ID. Besides, also keep the mapping
+ // between the responding session ID and the window ID to help look up the
+ // window ID.
+ SessionIdManager mControllerSessionIdManager;
+ SessionIdManager mReceiverSessionIdManager;
+
+ nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtController;
+ nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtReceiver;
+
+ AvailabilityManager mAvailabilityManager;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PresentationServiceBase_h