Bug 1258602 - Part1: Add PresentationConnectionList, r=smaug
authorKershaw Chang <kechang@mozilla.com>
Sun, 29 May 2016 19:59:00 +0200
changeset 338555 54ca84566554be8fd282e3ffc633e66bc0c39f94
parent 338554 8895926c8142c6387958ccf4ae741fb201836716
child 338556 e0f15af55efbef722c2a6981203a0d3a06892577
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1258602
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1258602 - Part1: Add PresentationConnectionList, r=smaug
dom/presentation/PresentationCallbacks.cpp
dom/presentation/PresentationConnection.cpp
dom/presentation/PresentationConnection.h
dom/presentation/PresentationConnectionList.cpp
dom/presentation/PresentationConnectionList.h
dom/presentation/PresentationReceiver.cpp
dom/presentation/PresentationReceiver.h
dom/presentation/PresentationSessionInfo.h
dom/presentation/interfaces/nsIPresentationListener.idl
dom/presentation/moz.build
dom/webidl/PresentationConnection.webidl
dom/webidl/PresentationConnectionList.webidl
dom/webidl/PresentationReceiver.webidl
dom/webidl/moz.build
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -45,18 +45,17 @@ NS_IMETHODIMP
 PresentationRequesterCallback::NotifySuccess()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // At the sender side, this function must get called after the transport
   // channel is ready. So we simply set the connection state as connected.
   RefPtr<PresentationConnection> connection =
     PresentationConnection::Create(mRequest->GetOwner(), mSessionId,
-                                   nsIPresentationService::ROLE_CONTROLLER,
-                                   PresentationConnectionState::Connected);
+                                   nsIPresentationService::ROLE_CONTROLLER);
   if (NS_WARN_IF(!connection)) {
     mPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return NS_OK;
   }
 
   mPromise->MaybeResolve(connection);
 
   return mRequest->DispatchConnectionAvailableEvent(connection);
--- a/dom/presentation/PresentationConnection.cpp
+++ b/dom/presentation/PresentationConnection.cpp
@@ -8,63 +8,67 @@
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPresentationService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringStream.h"
 #include "PresentationConnection.h"
+#include "PresentationConnectionList.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationConnection)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningConnectionList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
   tmp->Shutdown();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningConnectionList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(PresentationConnection, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(PresentationConnection, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnection)
   NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
                                                const nsAString& aId,
                                                const uint8_t aRole,
-                                               PresentationConnectionState aState)
+                                               PresentationConnectionList* aList)
   : DOMEventTargetHelper(aWindow)
   , mId(aId)
-  , mState(aState)
+  , mState(PresentationConnectionState::Connecting)
+  , mOwningConnectionList(aList)
 {
   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
              aRole == nsIPresentationService::ROLE_RECEIVER);
   mRole = aRole;
 }
 
 /* virtual */ PresentationConnection::~PresentationConnection()
 {
 }
 
 /* static */ already_AddRefed<PresentationConnection>
 PresentationConnection::Create(nsPIDOMWindowInner* aWindow,
                                const nsAString& aId,
                                const uint8_t aRole,
-                               PresentationConnectionState aState)
+                               PresentationConnectionList* aList)
 {
   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
              aRole == nsIPresentationService::ROLE_RECEIVER);
   RefPtr<PresentationConnection> connection =
-    new PresentationConnection(aWindow, aId, aRole, aState);
+    new PresentationConnection(aWindow, aId, aRole, aList);
   return NS_WARN_IF(!connection->Init()) ? nullptr : connection.forget();
 }
 
 bool
 PresentationConnection::Init()
 {
   if (NS_WARN_IF(mId.IsEmpty())) {
     return false;
@@ -181,16 +185,19 @@ PresentationConnection::NotifyStateChang
                                           uint16_t aState)
 {
   if (!aSessionId.Equals(mId)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   PresentationConnectionState state;
   switch (aState) {
+    case nsIPresentationSessionListener::STATE_CONNECTING:
+      state = PresentationConnectionState::Connecting;
+      break;
     case nsIPresentationSessionListener::STATE_CONNECTED:
       state = PresentationConnectionState::Connected;
       break;
     case nsIPresentationSessionListener::STATE_CLOSED:
       state = PresentationConnectionState::Closed;
       break;
     case nsIPresentationSessionListener::STATE_TERMINATED:
       state = PresentationConnectionState::Terminated;
@@ -215,17 +222,26 @@ PresentationConnection::NotifyStateChang
     }
 
     nsresult rv = service->UnregisterSessionListener(mId, mRole);
     if(NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  return DispatchStateChangeEvent();
+  nsresult rv = DispatchStateChangeEvent();
+  if(NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+  }
+
+  if (mOwningConnectionList) {
+    mOwningConnectionList->NotifyStateChange(aSessionId, this);
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationConnection::NotifyMessage(const nsAString& aSessionId,
                                       const nsACString& aData)
 {
   if (!aSessionId.Equals(mId)) {
     return NS_ERROR_INVALID_ARG;
--- a/dom/presentation/PresentationConnection.h
+++ b/dom/presentation/PresentationConnection.h
@@ -9,29 +9,32 @@
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/PresentationConnectionBinding.h"
 #include "nsIPresentationListener.h"
 
 namespace mozilla {
 namespace dom {
 
+class PresentationConnectionList;
+
 class PresentationConnection final : public DOMEventTargetHelper
                                    , public nsIPresentationSessionListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnection,
                                            DOMEventTargetHelper)
   NS_DECL_NSIPRESENTATIONSESSIONLISTENER
 
-  static already_AddRefed<PresentationConnection> Create(nsPIDOMWindowInner* aWindow,
-                                                         const nsAString& aId,
-                                                         const uint8_t aRole,
-                                                         PresentationConnectionState aState);
+  static already_AddRefed<PresentationConnection>
+  Create(nsPIDOMWindowInner* aWindow,
+         const nsAString& aId,
+         const uint8_t aRole,
+         PresentationConnectionList* aList = nullptr);
 
   virtual void DisconnectFromOwner() override;
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL (public APIs)
   void GetId(nsAString& aId) const;
@@ -47,29 +50,30 @@ public:
 
   IMPL_EVENT_HANDLER(statechange);
   IMPL_EVENT_HANDLER(message);
 
 private:
   PresentationConnection(nsPIDOMWindowInner* aWindow,
                          const nsAString& aId,
                          const uint8_t aRole,
-                         PresentationConnectionState aState);
+                         PresentationConnectionList* aList);
 
   ~PresentationConnection();
 
   bool Init();
 
   void Shutdown();
 
   nsresult DispatchStateChangeEvent();
 
   nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
 
   nsString mId;
   uint8_t mRole;
   PresentationConnectionState mState;
+  RefPtr<PresentationConnectionList> mOwningConnectionList;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PresentationConnection_h
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationConnectionList.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "PresentationConnectionList.h"
+
+#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/dom/PresentationConnectionAvailableEvent.h"
+#include "mozilla/dom/PresentationConnectionListBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "PresentationConnection.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(PresentationConnectionList, DOMEventTargetHelper,
+                                   mGetConnectionListPromise,
+                                   mConnections)
+
+NS_IMPL_ADDREF_INHERITED(PresentationConnectionList, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(PresentationConnectionList, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnectionList)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+PresentationConnectionList::PresentationConnectionList(nsPIDOMWindowInner* aWindow,
+                                                       Promise* aPromise)
+  : DOMEventTargetHelper(aWindow)
+  , mGetConnectionListPromise(aPromise)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aPromise);
+}
+
+/* virtual */ JSObject*
+PresentationConnectionList::WrapObject(JSContext* aCx,
+                                       JS::Handle<JSObject*> aGivenProto)
+{
+  return PresentationConnectionListBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+PresentationConnectionList::GetConnections(
+  nsTArray<RefPtr<PresentationConnection>>& aConnections) const
+{
+  aConnections = mConnections;
+}
+
+nsresult
+PresentationConnectionList::DispatchConnectionAvailableEvent(
+  PresentationConnection* aConnection)
+{
+  PresentationConnectionAvailableEventInit init;
+  init.mConnection = aConnection;
+
+  RefPtr<PresentationConnectionAvailableEvent> event =
+    PresentationConnectionAvailableEvent::Constructor(
+      this,
+      NS_LITERAL_STRING("connectionavailable"),
+      init);
+
+  if (NS_WARN_IF(!event)) {
+    return NS_ERROR_FAILURE;
+  }
+  event->SetTrusted(true);
+
+  RefPtr<AsyncEventDispatcher> asyncDispatcher =
+    new AsyncEventDispatcher(this, event);
+  return asyncDispatcher->PostDOMEvent();
+}
+
+PresentationConnectionList::ConnectionArrayIndex
+PresentationConnectionList::FindConnectionById(
+  const nsAString& aId)
+{
+  for (ConnectionArrayIndex i = 0; i < mConnections.Length(); i++) {
+    nsAutoString id;
+    mConnections[i]->GetId(id);
+    if (id == nsAutoString(aId)) {
+      return i;
+    }
+  }
+
+  return mConnections.NoIndex;
+}
+
+void
+PresentationConnectionList::NotifyStateChange(const nsAString& aSessionId,
+                                              PresentationConnection* aConnection)
+{
+  if (!aConnection) {
+    MOZ_ASSERT(false, "PresentationConnection can not be null.");
+    return;
+  }
+
+  bool connectionFound =
+    FindConnectionById(aSessionId) != mConnections.NoIndex ? true : false;
+
+  PresentationConnectionListBinding::ClearCachedConnectionsValue(this);
+  switch (aConnection->State()) {
+    case PresentationConnectionState::Connected:
+      if (!connectionFound) {
+        mConnections.AppendElement(aConnection);
+        if (mGetConnectionListPromise) {
+          mGetConnectionListPromise->MaybeResolve(this);
+          mGetConnectionListPromise = nullptr;
+          return;
+        }
+      }
+      DispatchConnectionAvailableEvent(aConnection);
+      break;
+    case PresentationConnectionState::Terminated:
+      if (connectionFound) {
+        mConnections.RemoveElement(aConnection);
+      }
+      break;
+    default:
+      break;
+    }
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/presentation/PresentationConnectionList.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_PresentationConnectionList_h
+#define mozilla_dom_PresentationConnectionList_h
+
+#include "mozilla/DOMEventTargetHelper.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class PresentationConnection;
+class Promise;
+
+class PresentationConnectionList final : public DOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnectionList,
+                                           DOMEventTargetHelper)
+
+  PresentationConnectionList(nsPIDOMWindowInner* aWindow,
+                             Promise* aPromise);
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  void GetConnections(nsTArray<RefPtr<PresentationConnection>>& aConnections) const;
+
+  void NotifyStateChange(const nsAString& aSessionId, PresentationConnection* aConnection);
+
+  IMPL_EVENT_HANDLER(connectionavailable);
+
+private:
+  virtual ~PresentationConnectionList() = default;
+
+  nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection);
+
+  typedef nsTArray<RefPtr<PresentationConnection>> ConnectionArray;
+  typedef ConnectionArray::index_type ConnectionArrayIndex;
+
+  ConnectionArrayIndex FindConnectionById(const nsAString& aId);
+
+  RefPtr<Promise> mGetConnectionListPromise;
+
+  // This array stores only non-terminsted connections.
+  ConnectionArray mConnections;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PresentationConnectionList_h
--- a/dom/presentation/PresentationReceiver.cpp
+++ b/dom/presentation/PresentationReceiver.cpp
@@ -1,191 +1,166 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
-#include "mozilla/AsyncEventDispatcher.h"
+#include "PresentationReceiver.h"
+
 #include "mozilla/dom/PresentationReceiverBinding.h"
 #include "mozilla/dom/Promise.h"
-#include "nsCycleCollectionParticipant.h"
 #include "nsIPresentationService.h"
 #include "nsServiceManagerUtils.h"
-#include "PresentationReceiver.h"
+#include "nsThreadUtils.h"
 #include "PresentationConnection.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
+#include "PresentationConnectionList.h"
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationReceiver)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationReceiver, DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnections)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingGetConnectionPromises)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+namespace mozilla {
+namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationReceiver, DOMEventTargetHelper)
-  tmp->Shutdown();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConnections)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingGetConnectionPromises)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PresentationReceiver,
+                                      mOwner,
+                                      mGetConnectionListPromise,
+                                      mConnectionList)
 
-NS_IMPL_ADDREF_INHERITED(PresentationReceiver, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(PresentationReceiver, DOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PresentationReceiver)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PresentationReceiver)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationReceiver)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PresentationReceiver)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIPresentationRespondingListener)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+NS_INTERFACE_MAP_END
 
 /* static */ already_AddRefed<PresentationReceiver>
 PresentationReceiver::Create(nsPIDOMWindowInner* aWindow)
 {
   RefPtr<PresentationReceiver> receiver = new PresentationReceiver(aWindow);
   return NS_WARN_IF(!receiver->Init()) ? nullptr : receiver.forget();
 }
 
 PresentationReceiver::PresentationReceiver(nsPIDOMWindowInner* aWindow)
-  : DOMEventTargetHelper(aWindow)
+  : mOwner(aWindow)
 {
+  MOZ_ASSERT(aWindow);
 }
 
 PresentationReceiver::~PresentationReceiver()
 {
   Shutdown();
 }
 
 bool
 PresentationReceiver::Init()
 {
-  if (NS_WARN_IF(!GetOwner())) {
+  if (NS_WARN_IF(!mOwner)) {
     return false;
   }
-  mWindowId = GetOwner()->WindowID();
-
-  nsCOMPtr<nsIPresentationService> service =
-    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
-  if (NS_WARN_IF(!service)) {
-    return false;
-  }
-
-  // Register listener for incoming sessions.
-  nsresult rv = service->RegisterRespondingListener(mWindowId, this);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
+  mWindowId = mOwner->WindowID();
 
   return true;
 }
 
 void PresentationReceiver::Shutdown()
 {
-  mConnections.Clear();
-  mPendingGetConnectionPromises.Clear();
-
   // Unregister listener for incoming sessions.
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return;
   }
 
   nsresult rv = service->UnregisterRespondingListener(mWindowId);
   NS_WARN_IF(NS_FAILED(rv));
 }
 
-/* virtual */ void
-PresentationReceiver::DisconnectFromOwner()
-{
-  Shutdown();
-  DOMEventTargetHelper::DisconnectFromOwner();
-}
-
 /* virtual */ JSObject*
 PresentationReceiver::WrapObject(JSContext* aCx,
                                  JS::Handle<JSObject*> aGivenProto)
 {
   return PresentationReceiverBinding::Wrap(aCx, this, aGivenProto);
 }
 
-already_AddRefed<Promise>
-PresentationReceiver::GetConnection(ErrorResult& aRv)
+NS_IMETHODIMP
+PresentationReceiver::NotifySessionConnect(uint64_t aWindowId,
+                                           const nsAString& aSessionId)
 {
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
-  if (NS_WARN_IF(!global)) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+  if (NS_WARN_IF(!mOwner)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_WARN_IF(aWindowId != mWindowId)) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  RefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
+  if (NS_WARN_IF(!mConnectionList)) {
+    return NS_ERROR_FAILURE;
   }
 
-  // If there's no existing connection, leave the promise pending until a
-  // connecting request arrives from the controlling browsing context (sender).
-  // http://w3c.github.io/presentation-api/#dom-presentation-getconnection
-  if (!mConnections.IsEmpty()) {
-    promise->MaybeResolve(mConnections[0]);
-  } else {
-    mPendingGetConnectionPromises.AppendElement(promise);
+  RefPtr<PresentationConnection> connection =
+    PresentationConnection::Create(mOwner, aSessionId,
+                                   nsIPresentationService::ROLE_RECEIVER,
+                                   mConnectionList);
+  if (NS_WARN_IF(!connection)) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return promise.forget();
+  return NS_OK;
 }
 
 already_AddRefed<Promise>
-PresentationReceiver::GetConnections(ErrorResult& aRv) const
+PresentationReceiver::GetConnectionList(ErrorResult& aRv)
 {
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mOwner);
   if (NS_WARN_IF(!global)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
-  RefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
+  if (!mGetConnectionListPromise) {
+    mGetConnectionListPromise = Promise::Create(global, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    RefPtr<PresentationReceiver> self = this;
+    nsresult rv =
+      NS_DispatchToMainThread(NS_NewRunnableFunction([self] () -> void {
+        self->CreateConnectionList();
+      }));
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return nullptr;
+    }
   }
 
-  promise->MaybeResolve(mConnections);
+  RefPtr<Promise> promise = mGetConnectionListPromise;
   return promise.forget();
 }
 
-NS_IMETHODIMP
-PresentationReceiver::NotifySessionConnect(uint64_t aWindowId,
-                                           const nsAString& aSessionId)
+void
+PresentationReceiver::CreateConnectionList()
 {
-  if (NS_WARN_IF(!GetOwner())) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(mGetConnectionListPromise);
 
-  if (NS_WARN_IF(aWindowId != GetOwner()->WindowID())) {
-    return NS_ERROR_INVALID_ARG;
+  if (mConnectionList) {
+    return;
   }
 
-  RefPtr<PresentationConnection> connection =
-    PresentationConnection::Create(GetOwner(), aSessionId,
-                                   nsIPresentationService::ROLE_RECEIVER,
-                                   PresentationConnectionState::Closed);
-  if (NS_WARN_IF(!connection)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-  mConnections.AppendElement(connection);
+  mConnectionList = new PresentationConnectionList(mOwner,
+                                                   mGetConnectionListPromise);
 
-  // Resolve pending |GetConnection| promises if any.
-  if (!mPendingGetConnectionPromises.IsEmpty()) {
-    for(uint32_t i = 0; i < mPendingGetConnectionPromises.Length(); i++) {
-      mPendingGetConnectionPromises[i]->MaybeResolve(connection);
-    }
-    mPendingGetConnectionPromises.Clear();
+  // Register listener for incoming sessions.
+  nsCOMPtr<nsIPresentationService> service =
+    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!service)) {
+    mGetConnectionListPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
+    return;
   }
 
-  return DispatchConnectionAvailableEvent();
+  nsresult rv = service->RegisterRespondingListener(mWindowId, this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mGetConnectionListPromise->MaybeReject(rv);
+  }
 }
 
-nsresult
-PresentationReceiver::DispatchConnectionAvailableEvent()
-{
-  RefPtr<AsyncEventDispatcher> asyncDispatcher =
-    new AsyncEventDispatcher(this, NS_LITERAL_STRING("connectionavailable"), false);
-  return asyncDispatcher->PostDOMEvent();
-}
+} // namespace dom
+} // namespace mozilla
--- a/dom/presentation/PresentationReceiver.h
+++ b/dom/presentation/PresentationReceiver.h
@@ -2,63 +2,63 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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_PresentationReceiver_h
 #define mozilla_dom_PresentationReceiver_h
 
-#include "mozilla/DOMEventTargetHelper.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsIPresentationListener.h"
 
 namespace mozilla {
 namespace dom {
 
+class PresentationConnection;
+class PresentationConnectionList;
 class Promise;
-class PresentationConnection;
 
-class PresentationReceiver final : public DOMEventTargetHelper
-                                 , public nsIPresentationRespondingListener
+class PresentationReceiver final : public nsIPresentationRespondingListener
+                                 , public nsWrapperCache
 {
 public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationReceiver,
-                                           DOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PresentationReceiver)
   NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER
 
   static already_AddRefed<PresentationReceiver> Create(nsPIDOMWindowInner* aWindow);
 
-  virtual void DisconnectFromOwner() override;
-
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
-  // WebIDL (public APIs)
-  already_AddRefed<Promise> GetConnection(ErrorResult& aRv);
+  nsISupports* GetParentObject() const
+  {
+    return mOwner;
+  }
 
-  already_AddRefed<Promise> GetConnections(ErrorResult& aRv) const;
-
-  IMPL_EVENT_HANDLER(connectionavailable);
+  // WebIDL (public APIs)
+  already_AddRefed<Promise> GetConnectionList(ErrorResult& aRv);
 
 private:
   explicit PresentationReceiver(nsPIDOMWindowInner* aWindow);
 
-  ~PresentationReceiver();
+  virtual ~PresentationReceiver();
 
   bool Init();
 
   void Shutdown();
 
-  nsresult DispatchConnectionAvailableEvent();
+  void CreateConnectionList();
 
   // Store the inner window ID for |UnregisterRespondingListener| call in
   // |Shutdown| since the inner window may not exist at that moment.
   uint64_t mWindowId;
 
-  nsTArray<RefPtr<PresentationConnection>> mConnections;
-  nsTArray<RefPtr<Promise>> mPendingGetConnectionPromises;
+  nsCOMPtr<nsPIDOMWindowInner> mOwner;
+  RefPtr<Promise> mGetConnectionListPromise;
+  RefPtr<PresentationConnectionList> mConnectionList;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PresentationReceiver_h
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -39,17 +39,17 @@ public:
   PresentationSessionInfo(const nsAString& aUrl,
                           const nsAString& aSessionId,
                           const uint8_t aRole,
                           nsIPresentationServiceCallback* aCallback)
     : mUrl(aUrl)
     , mSessionId(aSessionId)
     , mIsResponderReady(false)
     , mIsTransportReady(false)
-    , mState(nsIPresentationSessionListener::STATE_CLOSED)
+    , mState(nsIPresentationSessionListener::STATE_CONNECTING)
     , mCallback(aCallback)
   {
     MOZ_ASSERT(!mUrl.IsEmpty());
     MOZ_ASSERT(!mSessionId.IsEmpty());
     MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
                aRole == nsIPresentationService::ROLE_RECEIVER);
     mRole = aRole;
   }
--- a/dom/presentation/interfaces/nsIPresentationListener.idl
+++ b/dom/presentation/interfaces/nsIPresentationListener.idl
@@ -11,19 +11,20 @@ interface nsIPresentationAvailabilityLis
    * Called when device availability changes.
    */
   void notifyAvailableChange(in bool available);
 };
 
 [scriptable, uuid(7dd48df8-8f8c-48c7-ac37-7b9fd1acf2f8)]
 interface nsIPresentationSessionListener : nsISupports
 {
-  const unsigned short STATE_CONNECTED = 0;
-  const unsigned short STATE_CLOSED = 1;
-  const unsigned short STATE_TERMINATED = 2;
+  const unsigned short STATE_CONNECTING = 0;
+  const unsigned short STATE_CONNECTED = 1;
+  const unsigned short STATE_CLOSED = 2;
+  const unsigned short STATE_TERMINATED = 3;
 
   /*
    * Called when session state changes.
    */
   void notifyStateChange(in DOMString sessionId,
                          in unsigned short state);
 
   /*
--- a/dom/presentation/moz.build
+++ b/dom/presentation/moz.build
@@ -13,16 +13,17 @@ MOCHITEST_CHROME_MANIFESTS += ['tests/mo
 EXPORTS.mozilla.dom += [
     'ipc/PresentationChild.h',
     'ipc/PresentationIPCService.h',
     'ipc/PresentationParent.h',
     'Presentation.h',
     'PresentationAvailability.h',
     'PresentationCallbacks.h',
     'PresentationConnection.h',
+    'PresentationConnectionList.h',
     'PresentationDeviceManager.h',
     'PresentationReceiver.h',
     'PresentationRequest.h',
     'PresentationService.h',
     'PresentationServiceBase.h',
     'PresentationSessionInfo.h',
     'PresentationTCPSessionTransport.h',
 ]
@@ -30,16 +31,17 @@ EXPORTS.mozilla.dom += [
 UNIFIED_SOURCES += [
     'ipc/PresentationChild.cpp',
     'ipc/PresentationIPCService.cpp',
     'ipc/PresentationParent.cpp',
     'Presentation.cpp',
     'PresentationAvailability.cpp',
     'PresentationCallbacks.cpp',
     'PresentationConnection.cpp',
+    'PresentationConnectionList.cpp',
     'PresentationDeviceManager.cpp',
     'PresentationReceiver.cpp',
     'PresentationRequest.cpp',
     'PresentationService.cpp',
     'PresentationServiceBase.cpp',
     'PresentationSessionInfo.cpp',
     'PresentationSessionRequest.cpp',
     'PresentationTCPSessionTransport.cpp',
--- a/dom/webidl/PresentationConnection.webidl
+++ b/dom/webidl/PresentationConnection.webidl
@@ -1,16 +1,19 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
 enum PresentationConnectionState
 {
+  // The initial state when a PresentationConnection is ceated.
+  "connecting",
+
   // Existing presentation, and the communication channel is active.
   "connected",
 
   // Existing presentation, but the communication channel is inactive.
   "closed",
 
   // The presentation is nonexistent anymore. It could be terminated manually,
   // or either controlling or receiving browsing context is no longer available.
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PresentationConnectionList.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[Pref="dom.presentation.enabled",
+ Func="Navigator::HasPresentationSupport"]
+interface PresentationConnectionList : EventTarget {
+  /*
+   * Return the non-terminated set of presentation connections in the
+   * set of presentation controllers.
+   * TODO: Use FrozenArray once available. (Bug 1236777)
+   * readonly attribute FrozenArray<PresentationConnection> connections;
+   */
+  [Frozen, Cached, Pure]
+  readonly attribute sequence<PresentationConnection> connections;
+
+  /*
+   * It is called when an incoming connection is connected.
+   */
+  attribute EventHandler onconnectionavailable;
+};
--- a/dom/webidl/PresentationReceiver.webidl
+++ b/dom/webidl/PresentationReceiver.webidl
@@ -1,27 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
 [Pref="dom.presentation.enabled",
  Func="Navigator::HasPresentationSupport"]
-interface PresentationReceiver : EventTarget {
-  /*
-   * Get the first connected presentation connection in a receiving browsing
-   * context.
-   */
-  [Throws]
-  Promise<PresentationConnection> getConnection();
-
+interface PresentationReceiver {
   /*
-   * Get all connected presentation connections in a receiving browsing context.
+   * Get a list which contains all connected presentation connections
+   * in a receiving browsing context.
    */
-  [Throws]
-  Promise<sequence<PresentationConnection>> getConnections();
-
-  /*
-   * It is called when an incoming connection is connecting.
-   */
-  attribute EventHandler onconnectionavailable;
-};
+  [SameObject, Throws]
+  readonly attribute Promise<PresentationConnectionList> connectionList;
+};
\ No newline at end of file
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -373,16 +373,17 @@ WEBIDL_FILES = [
     'PluginArray.webidl',
     'PointerEvent.webidl',
     'PopupBoxObject.webidl',
     'Position.webidl',
     'PositionError.webidl',
     'Presentation.webidl',
     'PresentationAvailability.webidl',
     'PresentationConnection.webidl',
+    'PresentationConnectionList.webidl',
     'PresentationDeviceInfoManager.webidl',
     'PresentationReceiver.webidl',
     'PresentationRequest.webidl',
     'ProcessingInstruction.webidl',
     'ProfileTimelineMarker.webidl',
     'RadioNodeList.webidl',
     'Range.webidl',
     'Rect.webidl',