Bug 1258602 - Part3: Changes for making the initial state to “connecting”, r=smaug
authorKershaw Chang <kechang@mozilla.com>
Sun, 29 May 2016 23:01:00 +0200
changeset 338557 3da545fd691fa27d09ab8865b1fa2916418cad7f
parent 338556 e0f15af55efbef722c2a6981203a0d3a06892577
child 338558 f5804d79ba15e4e16aaa004cd37331a4b2233a9f
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 - Part3: Changes for making the initial state to “connecting”, r=smaug
dom/presentation/PresentationService.cpp
dom/presentation/PresentationService.h
dom/presentation/PresentationSessionInfo.cpp
dom/presentation/PresentationSessionInfo.h
dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.html
dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html
dom/presentation/tests/mochitest/test_presentation_dc_sender.html
dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html
dom/presentation/tests/mochitest/test_presentation_tcp_sender.html
dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html
dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -35,47 +35,54 @@ namespace dom {
 class PresentationDeviceRequest final : public nsIPresentationDeviceRequest
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONDEVICEREQUEST
 
   PresentationDeviceRequest(const nsAString& aRequestUrl,
                             const nsAString& aId,
-                            const nsAString& aOrigin);
+                            const nsAString& aOrigin,
+                            uint64_t aWindowId,
+                            nsIPresentationServiceCallback* aCallback);
 
 private:
-  virtual ~PresentationDeviceRequest();
+  virtual ~PresentationDeviceRequest() = default;
+  nsresult CreateSessionInfo(nsIPresentationDevice* aDevice);
 
   nsString mRequestUrl;
   nsString mId;
   nsString mOrigin;
+  uint64_t mWindowId;
+  nsCOMPtr<nsIPresentationServiceCallback> mCallback;
 };
 
 LazyLogModule gPresentationLog("Presentation");
 
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(PresentationDeviceRequest, nsIPresentationDeviceRequest)
 
-PresentationDeviceRequest::PresentationDeviceRequest(const nsAString& aRequestUrl,
-                                                     const nsAString& aId,
-                                                     const nsAString& aOrigin)
+PresentationDeviceRequest::PresentationDeviceRequest(
+                                      const nsAString& aRequestUrl,
+                                      const nsAString& aId,
+                                      const nsAString& aOrigin,
+                                      uint64_t aWindowId,
+                                      nsIPresentationServiceCallback* aCallback)
   : mRequestUrl(aRequestUrl)
   , mId(aId)
   , mOrigin(aOrigin)
+  , mWindowId(aWindowId)
+  , mCallback(aCallback)
 {
   MOZ_ASSERT(!mRequestUrl.IsEmpty());
   MOZ_ASSERT(!mId.IsEmpty());
   MOZ_ASSERT(!mOrigin.IsEmpty());
-}
-
-PresentationDeviceRequest::~PresentationDeviceRequest()
-{
+  MOZ_ASSERT(mCallback);
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::GetOrigin(nsAString& aOrigin)
 {
   aOrigin = mOrigin;
   return NS_OK;
 }
@@ -88,26 +95,38 @@ PresentationDeviceRequest::GetRequestURL
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::Select(nsIPresentationDevice* aDevice)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aDevice);
 
+  nsresult rv = CreateSessionInfo(aDevice);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mCallback->NotifyError(rv);
+    return rv;
+  }
+
+  return mCallback->NotifySuccess();
+}
+
+nsresult
+PresentationDeviceRequest::CreateSessionInfo(nsIPresentationDevice* aDevice)
+{
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  // Update device in the session info.
+  // Create the controlling session info
   RefPtr<PresentationSessionInfo> info =
     static_cast<PresentationService*>(service.get())->
-      GetSessionInfo(mId, nsIPresentationService::ROLE_CONTROLLER);
+      CreateControllingSessionInfo(mRequestUrl, mId, mWindowId);
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   info->SetDevice(aDevice);
 
   // Establish a control channel. If we failed to do so, the callback is called
   // with an error message.
   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
@@ -123,30 +142,17 @@ PresentationDeviceRequest::Select(nsIPre
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::Cancel()
 {
-  nsCOMPtr<nsIPresentationService> service =
-    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
-  if (NS_WARN_IF(!service)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  RefPtr<PresentationSessionInfo> info =
-    static_cast<PresentationService*>(service.get())->
-      GetSessionInfo(mId, nsIPresentationService::ROLE_CONTROLLER);
-  if (NS_WARN_IF(!info)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  return info->ReplyError(NS_ERROR_DOM_ABORT_ERR);
+  return mCallback->NotifyError(NS_ERROR_DOM_ABORT_ERR);
 }
 
 /*
  * Implementation of PresentationService
  */
 
 NS_IMPL_ISUPPORTS_INHERITED(PresentationService,
                             PresentationServiceBase,
@@ -398,60 +404,56 @@ PresentationService::StartSession(const 
                                   const nsAString& aDeviceId,
                                   uint64_t aWindowId,
                                   nsIPresentationServiceCallback* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(!aSessionId.IsEmpty());
 
-  // Create session info  and set the callback. The callback is called when the
-  // request is finished.
-  RefPtr<PresentationSessionInfo> info =
-    new PresentationControllingInfo(aUrl, aSessionId, aCallback);
-  mSessionInfoAtController.Put(aSessionId, info);
-
-  AddRespondingSessionId(aWindowId, aSessionId);
-
   nsCOMPtr<nsIPresentationDeviceRequest> request =
-    new PresentationDeviceRequest(aUrl, aSessionId, aOrigin);
+    new PresentationDeviceRequest(aUrl,
+                                  aSessionId,
+                                  aOrigin,
+                                  aWindowId,
+                                  aCallback);
 
   if (aDeviceId.IsVoid()) {
     // Pop up a prompt and ask user to select a device.
     nsCOMPtr<nsIPresentationDevicePrompt> prompt =
       do_GetService(PRESENTATION_DEVICE_PROMPT_CONTRACTID);
     if (NS_WARN_IF(!prompt)) {
-      return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+      return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
     }
 
     nsresult rv = prompt->PromptDeviceSelection(request);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+      return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
     }
 
     return NS_OK;
   }
 
   // Find the designated device from available device list.
   nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
     do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
   if (NS_WARN_IF(!deviceManager)) {
-    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+    return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   nsCOMPtr<nsIArray> devices;
   nsresult rv = deviceManager->GetAvailableDevices(getter_AddRefs(devices));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+    return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   nsCOMPtr<nsISimpleEnumerator> enumerator;
   rv = devices->Enumerate(getter_AddRefs(enumerator));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+    return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   NS_ConvertUTF16toUTF8 utf8DeviceId(aDeviceId);
   bool hasMore;
   while(NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore){
     nsCOMPtr<nsISupports> isupports;
     rv = enumerator->GetNext(getter_AddRefs(isupports));
 
@@ -461,17 +463,36 @@ PresentationService::StartSession(const 
     nsAutoCString id;
     if (NS_SUCCEEDED(device->GetId(id)) && id.Equals(utf8DeviceId)) {
       request->Select(device);
       return NS_OK;
     }
   }
 
   // Reject if designated device is not available.
-  return info->ReplyError(NS_ERROR_DOM_NOT_FOUND_ERR);
+  return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
+}
+
+already_AddRefed<PresentationSessionInfo>
+PresentationService::CreateControllingSessionInfo(const nsAString& aUrl,
+                                                  const nsAString& aSessionId,
+                                                  uint64_t aWindowId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (aSessionId.IsEmpty()) {
+    return nullptr;
+  }
+
+  RefPtr<PresentationSessionInfo> info =
+    new PresentationControllingInfo(aUrl, aSessionId);
+
+  mSessionInfoAtController.Put(aSessionId, info);
+  AddRespondingSessionId(aWindowId, aSessionId);
+  return info.forget();
 }
 
 NS_IMETHODIMP
 PresentationService::SendSessionMessage(const nsAString& aSessionId,
                                         uint8_t aRole,
                                         const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/presentation/PresentationService.h
+++ b/dom/presentation/PresentationService.h
@@ -14,16 +14,17 @@
 #include "PresentationSessionInfo.h"
 
 class nsIPresentationSessionRequest;
 class nsIURI;
 
 namespace mozilla {
 namespace dom {
 
+class PresentationDeviceRequest;
 class PresentationRespondingInfo;
 
 class PresentationService final : public nsIPresentationService
                                 , public nsIObserver
                                 , public PresentationServiceBase
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -49,23 +50,31 @@ public:
     }
   }
 
   bool IsSessionAccessible(const nsAString& aSessionId,
                            const uint8_t aRole,
                            base::ProcessId aProcessId);
 
 private:
+  friend class PresentationDeviceRequest;
+
   virtual ~PresentationService();
   void HandleShutdown();
   nsresult HandleDeviceChange();
   nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
   void NotifyAvailableChange(bool aIsAvailable);
   bool IsAppInstalled(nsIURI* aUri);
 
+  // This is meant to be called by PresentationDeviceRequest.
+  already_AddRefed<PresentationSessionInfo>
+  CreateControllingSessionInfo(const nsAString& aUrl,
+                               const nsAString& aSessionId,
+                               uint64_t aWindowId);
+
   bool mIsAvailable;
   nsTObserverArray<nsCOMPtr<nsIPresentationAvailabilityListener>> mAvailabilityListeners;
   nsRefPtrHashtable<nsStringHashKey, PresentationSessionInfo> mSessionInfoAtController;
   nsRefPtrHashtable<nsStringHashKey, PresentationSessionInfo> mSessionInfoAtReceiver;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -289,35 +289,24 @@ PresentationSessionInfo::Close(nsresult 
   Shutdown(aReason);
   return NS_OK;
 }
 
 nsresult
 PresentationSessionInfo::ReplySuccess()
 {
   SetState(nsIPresentationSessionListener::STATE_CONNECTED);
-
-  if (mCallback) {
-    NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess()));
-    SetCallback(nullptr);
-  }
-
   return NS_OK;
 }
 
 nsresult
 PresentationSessionInfo::ReplyError(nsresult aError)
 {
   Shutdown(aError);
 
-  if (mCallback) {
-    NS_WARN_IF(NS_FAILED(mCallback->NotifyError(aError)));
-    SetCallback(nullptr);
-  }
-
   // Remove itself since it never succeeds.
   return UntrackFromService();
 }
 
 /* virtual */ nsresult
 PresentationSessionInfo::UntrackFromService()
 {
   nsCOMPtr<nsIPresentationService> service =
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -33,24 +33,22 @@ class PresentationSessionInfo : public n
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK
   NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER
 
   PresentationSessionInfo(const nsAString& aUrl,
                           const nsAString& aSessionId,
-                          const uint8_t aRole,
-                          nsIPresentationServiceCallback* aCallback)
+                          const uint8_t aRole)
     : mUrl(aUrl)
     , mSessionId(aSessionId)
     , mIsResponderReady(false)
     , mIsTransportReady(false)
     , 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;
   }
 
@@ -66,21 +64,16 @@ public:
     return mSessionId;
   }
 
   uint8_t GetRole() const
   {
     return mRole;
   }
 
-  void SetCallback(nsIPresentationServiceCallback* aCallback)
-  {
-    mCallback = aCallback;
-  }
-
   nsresult SetListener(nsIPresentationSessionListener* aListener);
 
   void SetDevice(nsIPresentationDevice* aDevice)
   {
     mDevice = aDevice;
   }
 
   already_AddRefed<nsIPresentationDevice> GetDevice() const
@@ -147,17 +140,16 @@ protected:
   nsString mUrl;
   nsString mSessionId;
   // mRole should be nsIPresentationService::ROLE_CONTROLLER
   //              or nsIPresentationService::ROLE_RECEIVER.
   uint8_t mRole;
   bool mIsResponderReady;
   bool mIsTransportReady;
   uint32_t mState; // CONNECTED, CLOSED, TERMINATED
-  nsCOMPtr<nsIPresentationServiceCallback> mCallback;
   nsCOMPtr<nsIPresentationSessionListener> mListener;
   nsCOMPtr<nsIPresentationDevice> mDevice;
   nsCOMPtr<nsIPresentationSessionTransport> mTransport;
   nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
   nsCOMPtr<nsIPresentationSessionTransportBuilder> mBuilder;
 };
 
 // Session info with controlling browsing context (sender side) behaviors.
@@ -165,25 +157,21 @@ class PresentationControllingInfo final 
                                         , public nsIServerSocketListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
   NS_DECL_NSISERVERSOCKETLISTENER
 
   PresentationControllingInfo(const nsAString& aUrl,
-                              const nsAString& aSessionId,
-                              nsIPresentationServiceCallback* aCallback)
+                              const nsAString& aSessionId)
     : PresentationSessionInfo(aUrl,
                               aSessionId,
-                              nsIPresentationService::ROLE_CONTROLLER,
-                              aCallback)
-  {
-    MOZ_ASSERT(mCallback);
-  }
+                              nsIPresentationService::ROLE_CONTROLLER)
+  {}
 
   nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
 
 private:
   ~PresentationControllingInfo()
   {
     Shutdown(NS_OK);
   }
@@ -220,18 +208,17 @@ public:
   NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER
   NS_DECL_NSITIMERCALLBACK
 
   PresentationPresentingInfo(const nsAString& aUrl,
                              const nsAString& aSessionId,
                              nsIPresentationDevice* aDevice)
     : PresentationSessionInfo(aUrl,
                               aSessionId,
-                              nsIPresentationService::ROLE_RECEIVER,
-                              nullptr)
+                              nsIPresentationService::ROLE_RECEIVER)
   {
     MOZ_ASSERT(aDevice);
     SetDevice(aDevice);
   }
 
   nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
 
   nsresult NotifyResponderReady();
--- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.html
@@ -123,18 +123,22 @@ function testCreateRequest() {
 }
 
 function testStartConnection() {
   return new Promise(function(aResolve, aReject) {
     request.start().then((aConnection) => {
       connection = aConnection;
       ok(connection, "Sender: Connection should be available.");
       ok(connection.id, "Sender: Connection ID should be set.");
-      is(connection.state, "connected", "Sender: Connection state at sender side should be connected by default.");
-      aResolve();
+      is(connection.state, "connecting", "The initial state should be connecting.");
+      connection.onstatechange = function() {
+        connection.onstatechange = null;
+        is(connection.state, "connected", "Connection should be connected.");
+        aResolve();
+      };
     }).catch((aError) => {
       ok(false, "Sender: Error occurred when establishing a connection: " + aError);
       teardown();
       aReject();
     });
   });
 }
 
--- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html
@@ -129,18 +129,22 @@ function testCreateRequest() {
 
 function testStartConnection() {
   return new Promise(function(aResolve, aReject) {
     request.start()
     .then((aConnection) => {
       connection = aConnection;
       ok(connection, "Sender: Connection should be available.");
       ok(connection.id, "Sender: Connection ID should be set.");
-      is(connection.state, "connected", "Sender: Connection state at sender side should be connected by default.");
-      aResolve();
+      is(connection.state, "connecting", "The initial state should be connecting.");
+      connection.onstatechange = function() {
+        connection.onstatechange = null;
+        is(connection.state, "connected", "Connection should be connected.");
+        aResolve();
+      };
     })
     .catch((aError) => {
       ok(false, "Sender: Error occurred when establishing a connection: " + aError);
       teardown();
       aReject();
     });
   });
 }
--- a/dom/presentation/tests/mochitest/test_presentation_dc_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_dc_sender.html
@@ -95,31 +95,34 @@ function testStartConnection() {
     var connectionFromEvent;
     request.onconnectionavailable = function(aEvent) {
       request.onconnectionavailable = null;
       connectionFromEvent = aEvent.connection;
       ok(connectionFromEvent, "|connectionavailable| event is fired with a connection.");
 
       if (connection) {
         is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
-        aResolve();
       }
     };
 
     request.start().then(
       function(aConnection) {
         connection = aConnection;
         ok(connection, "Connection should be available.");
         ok(connection.id, "Connection ID should be set.");
-        is(connection.state, "connected", "Connection state at sender side should be connected by default.");
+        is(connection.state, "connecting", "The initial state should be connecting.");
 
         if (connectionFromEvent) {
           is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
+        }
+        connection.onstatechange = function() {
+          connection.onstatechange = null;
+          is(connection.state, "connected", "Connection should be connected.");
           aResolve();
-        }
+        };
       },
       function(aError) {
         ok(false, "Error occurred when establishing a connection: " + aError);
         teardown();
         aReject();
       }
     );
   });
--- a/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html
@@ -91,31 +91,34 @@ function testStartConnectionWithDevice()
     var connectionFromEvent;
     request.onconnectionavailable = function(aEvent) {
       request.onconnectionavailable = null;
       connectionFromEvent = aEvent.connection;
       ok(connectionFromEvent, "|connectionavailable| event is fired with a connection.");
 
       if (connection) {
         is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
-        aResolve();
       }
     };
 
     request.startWithDevice('id').then(
       function(aConnection) {
         connection = aConnection;
         ok(connection, "Connection should be available.");
         ok(connection.id, "Connection ID should be set.");
-        is(connection.state, "connected", "Connection state at sender side should be connected by default.");
+        is(connection.state, "connecting", "The initial state should be connecting.");
 
         if (connectionFromEvent) {
           is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
+        }
+        connection.onstatechange = function() {
+          connection.onstatechange = null;
+          is(connection.state, "connected", "Connection should be connected.");
           aResolve();
-        }
+        };
       },
       function(aError) {
         ok(false, "Error occurred when establishing a connection: " + aError);
         teardown();
         aReject();
       }
     );
   });
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html
@@ -90,31 +90,34 @@ function testStartConnection() {
     var connectionFromEvent;
     request.onconnectionavailable = function(aEvent) {
       request.onconnectionavailable = null;
       connectionFromEvent = aEvent.connection;
       ok(connectionFromEvent, "|connectionavailable| event is fired with a connection.");
 
       if (connection) {
         is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
-        aResolve();
       }
     };
 
     request.start().then(
       function(aConnection) {
         connection = aConnection;
         ok(connection, "Connection should be available.");
         ok(connection.id, "Connection ID should be set.");
-        is(connection.state, "connected", "Connection state at sender side should be connected by default.");
+        is(connection.state, "connecting", "The initial state should be connecting.");
 
         if (connectionFromEvent) {
           is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
+        }
+        connection.onstatechange = function() {
+          connection.onstatechange = null;
+          is(connection.state, "connected", "Connection should be connected.");
           aResolve();
-        }
+        };
       },
       function(aError) {
         ok(false, "Error occurred when establishing a connection: " + aError);
         teardown();
         aReject();
       }
     );
   });
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
@@ -83,18 +83,22 @@ function testStartConnection() {
 
     ok(!navigator.presentation.receiver, "Sender shouldn't get a presentation receiver instance.");
 
     navigator.presentation.defaultRequest.onconnectionavailable = function(aEvent) {
       navigator.presentation.defaultRequest.onconnectionavailable = null;
       connection = aEvent.connection;
       ok(connection, "|connectionavailable| event is fired with a connection.");
       ok(connection.id, "Connection ID should be set.");
-      is(connection.state, "connected", "Connection state at sender side should be connected by default.");
-      aResolve();
+      is(connection.state, "connecting", "The initial state should be connecting.");
+      connection.onstatechange = function() {
+        connection.onstatechange = null;
+        is(connection.state, "connected", "Connection should be connected.");
+        aResolve();
+      };
     };
 
     // Simulate the UA triggers |start()| of the default request.
     navigator.presentation.defaultRequest.start();
   });
 }
 
 function testTerminateConnection() {
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html
@@ -87,18 +87,22 @@ function testStartConnection() {
       info("Data notification is enabled for data transport channel.");
     });
 
     request.start().then(
       function(aConnection) {
         connection = aConnection;
         ok(connection, "Connection should be available.");
         ok(connection.id, "Connection ID should be set.");
-        is(connection.state, "connected", "Connection state at sender side should be connected by default.");
-        aResolve();
+        is(connection.state, "connecting", "The initial state should be connecting.");
+        connection.onstatechange = function() {
+          connection.onstatechange = null;
+          is(connection.state, "connected", "Connection should be connected.");
+          aResolve();
+        };
       },
       function(aError) {
         ok(false, "Error occurred when establishing a connection: " + aError);
         teardown();
         aReject();
       }
     );
   });
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
@@ -99,22 +99,27 @@ function testStartConnectionUnexpectedCo
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
       ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE);
     });
 
     request.start().then(
       function(aConnection) {
-        ok(false, "|start| shouldn't succeed in this case.");
-        aReject();
+        is(aConnection.state, "connecting", "The initial state should be connecting.");
+        aConnection.onstatechange = function() {
+          aConnection.onstatechange = null;
+          is(aConnection.state, "terminated", "Connection should be terminated.");
+          aResolve();
+        };
       },
       function(aError) {
-        is(aError.name, "OperationError", "OperationError is expected when a connection error happens during establishing a connection.");
-        aResolve();
+        ok(false, "Error occurred when establishing a connection: " + aError);
+        teardown();
+        aReject();
       }
     );
   });
 }
 
 function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
@@ -142,22 +147,27 @@ function testStartConnectionUnexpectedCo
     gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) {
       gScript.removeMessageListener('offer-sent', offerSentHandler);
       ok(aIsValid, "A valid offer is sent out.");
       gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK);
     });
 
     request.start().then(
       function(aConnection) {
-        ok(false, "|start| shouldn't succeed in this case.");
-        aReject();
+        is(aConnection.state, "connecting", "The initial state should be connecting.");
+        aConnection.onstatechange = function() {
+          aConnection.onstatechange = null;
+          is(aConnection.state, "terminated", "Connection should be terminated.");
+          aResolve();
+        };
       },
       function(aError) {
-        is(aError.name, "OperationError", "OperationError is expected when a connection closed during establishing a connection.");
-        aResolve();
+        ok(false, "Error occurred when establishing a connection: " + aError);
+        teardown();
+        aReject();
       }
     );
   });
 }
 
 function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
@@ -196,22 +206,27 @@ function testStartConnectionUnexpectedCo
 
     gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
       gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
       info("The data transport is closed. " + aReason);
     });
 
     request.start().then(
       function(aConnection) {
-        ok(false, "|start| shouldn't succeed in this case.");
-        aReject();
+        is(aConnection.state, "connecting", "The initial state should be connecting.");
+        aConnection.onstatechange = function() {
+          aConnection.onstatechange = null;
+          is(aConnection.state, "terminated", "Connection should be terminated.");
+          aResolve();
+        };
       },
       function(aError) {
-        is(aError.name, "OperationError", "OperationError is expected when a connection error happens during establishing a connection.");
-        aResolve();
+        ok(false, "Error occurred when establishing a connection: " + aError);
+        teardown();
+        aReject();
       }
     );
   });
 }
 
 function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
@@ -250,22 +265,27 @@ function testStartConnectionUnexpectedCo
 
     gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
       gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
       info("The data transport is closed. " + aReason);
     });
 
     request.start().then(
       function(aConnection) {
-        ok(false, "|start| shouldn't succeed in this case.");
-        aReject();
+        is(aConnection.state, "connecting", "The initial state should be connecting.");
+        aConnection.onstatechange = function() {
+          aConnection.onstatechange = null;
+          is(aConnection.state, "terminated", "Connection should be terminated.");
+          aResolve();
+        };
       },
       function(aError) {
-        is(aError.name, "OperationError", "OperationError is expected when a connection closed during establishing a connection.");
-        aResolve();
+        ok(false, "Error occurred when establishing a connection: " + aError);
+        teardown();
+        aReject();
       }
     );
   });
 }
 
 function testStartConnectionUnexpectedDataTransportClose() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
@@ -304,22 +324,27 @@ function testStartConnectionUnexpectedDa
 
     gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
       gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
       info("The data transport is closed. " + aReason);
     });
 
     request.start().then(
       function(aConnection) {
-        ok(false, "|start| shouldn't succeed in this case.");
-        aReject();
+        is(aConnection.state, "connecting", "The initial state should be connecting.");
+        aConnection.onstatechange = function() {
+          aConnection.onstatechange = null;
+          is(aConnection.state, "terminated", "Connection should be terminated.");
+          aResolve();
+        };
       },
       function(aError) {
-        is(aError.name, "OperationError", "OperationError is expected when a connection error happens during establishing a connection.");
-        aResolve();
+        ok(false, "Error occurred when establishing a connection: " + aError);
+        teardown();
+        aReject();
       }
     );
   });
 }
 
 function teardown() {
   gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
     gScript.removeMessageListener('teardown-complete', teardownCompleteHandler);