summaryrefslogtreecommitdiffstats
path: root/dom/presentation/PresentationServiceBase.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/presentation/PresentationServiceBase.h356
1 files changed, 356 insertions, 0 deletions
diff --git a/dom/presentation/PresentationServiceBase.h b/dom/presentation/PresentationServiceBase.h
new file mode 100644
index 0000000000..a997d5db89
--- /dev/null
+++ b/dom/presentation/PresentationServiceBase.h
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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"
+#include "nsDataHashtable.h"
+#include "nsThread.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); }
+
+ MOZ_COUNTED_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);
+ }
+ }
+ }
+ }
+
+ void UpdateWindowId(const nsAString& aSessionId, const uint64_t aWindowId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RemoveSessionId(aSessionId);
+ AddSessionId(aWindowId, aSessionId);
+ }
+
+ 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); }
+
+ MOZ_COUNTED_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);
+ }
+ }
+ }
+ }
+
+ void 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)));
+ }
+ }
+
+ 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);
+ }
+ }
+
+ void 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) {
+ mControllerSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
+ } else {
+ 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