Bug 1311375 - promise reject when there is an unsettled presentation start procedure. r=smaug
authorShih-Chiang Chien <schien@mozilla.com>
Mon, 31 Oct 2016 15:49:32 +0800
changeset 347148 7cecd173e205ecd2becbfa03378f4cc168eef047
parent 347147 c28c8e058e17cbdd428172894b53e2b81bec53b4
child 347149 dec79837f59068107eb055818473bbefd206ac85
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1311375
milestone52.0a1
Bug 1311375 - promise reject when there is an unsettled presentation start procedure. r=smaug MozReview-Commit-ID: AL0QeyXBwzb
dom/presentation/Presentation.cpp
dom/presentation/Presentation.h
dom/presentation/PresentationCallbacks.cpp
dom/presentation/PresentationRequest.cpp
dom/presentation/PresentationRequest.h
dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js
--- a/dom/presentation/Presentation.cpp
+++ b/dom/presentation/Presentation.cpp
@@ -156,16 +156,28 @@ Presentation::GetReceiver()
     MOZ_ASSERT(mReceiver);
     return nullptr;
   }
 
   RefPtr<PresentationReceiver> receiver = mReceiver;
   return receiver.forget();
 }
 
+void
+Presentation::SetStartSessionUnsettled(bool aIsUnsettled)
+{
+  mStartSessionUnsettled = aIsUnsettled;
+}
+
+bool
+Presentation::IsStartSessionUnsettled() const
+{
+  return mStartSessionUnsettled;
+}
+
 bool
 Presentation::IsInPresentedContent() const
 {
   if (!mWindow) {
     return false;
   }
 
   nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell();
--- a/dom/presentation/Presentation.h
+++ b/dom/presentation/Presentation.h
@@ -42,24 +42,29 @@ public:
 
   // WebIDL (public APIs)
   void SetDefaultRequest(PresentationRequest* aRequest);
 
   already_AddRefed<PresentationRequest> GetDefaultRequest() const;
 
   already_AddRefed<PresentationReceiver> GetReceiver();
 
+  // For bookkeeping unsettled start session request
+  void SetStartSessionUnsettled(bool aIsUnsettled);
+  bool IsStartSessionUnsettled() const;
+
 private:
   explicit Presentation(nsPIDOMWindowInner* aWindow);
 
   virtual ~Presentation();
 
   bool IsInPresentedContent() const;
 
   RefPtr<PresentationRequest> mDefaultRequest;
   RefPtr<PresentationReceiver> mReceiver;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
+  bool mStartSessionUnsettled = false;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Presentation_h
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -50,30 +50,31 @@ PresentationRequesterCallback::NotifySuc
   if (aUrl.IsEmpty()) {
     return NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   RefPtr<PresentationConnection> connection =
     PresentationConnection::Create(mRequest->GetOwner(), mSessionId, aUrl,
                                    nsIPresentationService::ROLE_CONTROLLER);
   if (NS_WARN_IF(!connection)) {
-    mPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
-    return NS_OK;
+    return NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
+  mRequest->NotifyPromiseSettled();
   mPromise->MaybeResolve(connection);
 
   return mRequest->DispatchConnectionAvailableEvent(connection);
 }
 
 NS_IMETHODIMP
 PresentationRequesterCallback::NotifyError(nsresult aError)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  mRequest->NotifyPromiseSettled();
   mPromise->MaybeReject(aError);
   return NS_OK;
 }
 
 /*
  * Implementation of PresentationRequesterCallback
  */
 
--- a/dom/presentation/PresentationRequest.cpp
+++ b/dom/presentation/PresentationRequest.cpp
@@ -4,30 +4,33 @@
  * 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 "PresentationRequest.h"
 
 #include "AvailabilityCollection.h"
 #include "ControllerConnectionCollection.h"
 #include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/PresentationRequestBinding.h"
 #include "mozilla/dom/PresentationConnectionAvailableEvent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/Move.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsContentSecurityManager.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsGlobalWindow.h"
 #include "nsIDocument.h"
 #include "nsIPresentationService.h"
 #include "nsIURI.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
 #include "nsSandboxFlags.h"
 #include "nsServiceManagerUtils.h"
+#include "Presentation.h"
 #include "PresentationAvailability.h"
 #include "PresentationCallbacks.h"
 #include "PresentationLog.h"
 #include "PresentationTransportBuilderConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -173,16 +176,32 @@ PresentationRequest::StartWithDevice(con
     return promise.forget();
   }
 
   if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
+  RefPtr<Navigator> navigator =
+    nsGlobalWindow::Cast(GetOwner())->GetNavigator(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  RefPtr<Presentation> presentation = navigator->GetPresentation(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  if (presentation->IsStartSessionUnsettled()) {
+    promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
+    return promise.forget();
+  }
+
   // Generate a session ID.
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1");
   if(NS_WARN_IF(!uuidgen)) {
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return promise.forget();
   }
 
@@ -195,16 +214,18 @@ PresentationRequest::StartWithDevice(con
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if(NS_WARN_IF(!service)) {
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return promise.forget();
   }
 
+  presentation->SetStartSessionUnsettled(true);
+
   // Get xul:browser element in parent process or nsWindowRoot object in child
   // process. If it's in child process, the corresponding xul:browser element
   // will be obtained at PresentationRequestParent::DoRequest in its parent
   // process.
   nsCOMPtr<nsIDOMEventTarget> handler =
     do_QueryInterface(GetOwner()->GetChromeEventHandler());
   nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
   nsCOMPtr<nsIPresentationServiceCallback> callback =
@@ -217,16 +238,17 @@ PresentationRequest::StartWithDevice(con
                              aDeviceId,
                              GetOwner()->WindowID(),
                              handler,
                              principal,
                              callback,
                              constructor);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
+    NotifyPromiseSettled();
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 PresentationRequest::Reconnect(const nsAString& aPresentationId,
                                ErrorResult& aRv)
@@ -432,16 +454,39 @@ PresentationRequest::DispatchConnectionA
   }
   event->SetTrusted(true);
 
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this, event);
   return asyncDispatcher->PostDOMEvent();
 }
 
+void
+PresentationRequest::NotifyPromiseSettled()
+{
+  PRES_DEBUG("%s\n", __func__);
+
+  if (!GetOwner()) {
+    return;
+  }
+
+  ErrorResult rv;
+  RefPtr<Navigator> navigator =
+    nsGlobalWindow::Cast(GetOwner())->GetNavigator(rv);
+  if (!navigator) {
+    return;
+  }
+
+  RefPtr<Presentation> presentation = navigator->GetPresentation(rv);
+
+  if (presentation) {
+    presentation->SetStartSessionUnsettled(false);
+  }
+}
+
 bool
 PresentationRequest::IsProhibitMixedSecurityContexts(nsIDocument* aDocument)
 {
   MOZ_ASSERT(aDocument);
 
   if (nsContentUtils::IsChromeDoc(aDocument)) {
     return true;
   }
--- a/dom/presentation/PresentationRequest.h
+++ b/dom/presentation/PresentationRequest.h
@@ -47,16 +47,18 @@ public:
                                       ErrorResult& aRv);
 
   already_AddRefed<Promise> GetAvailability(ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(connectionavailable);
 
   nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection);
 
+  void NotifyPromiseSettled();
+
 private:
   PresentationRequest(nsPIDOMWindowInner* aWindow,
                       nsTArray<nsString>&& aUrls);
 
   ~PresentationRequest();
 
   bool Init();
 
--- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js
+++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js
@@ -123,16 +123,23 @@ function testStartConnection() {
         presentationId = connection.id;
         aResolve();
       };
     }).catch((aError) => {
       ok(false, "Sender: Error occurred when establishing a connection: " + aError);
       teardown();
       aReject();
     });
+
+    let request2 = new PresentationRequest("/");
+    request2.start().then(() => {
+      ok(false, "Sender: session start should fail while there is an unsettled promise.");
+    }).catch((aError) => {
+      is(aError.name, "OperationError", "Expect to get OperationError.");
+    });
   });
 }
 
 function testSendMessage() {
   return new Promise(function(aResolve, aReject) {
     info('Sender: --- testSendMessage ---');
     gScript.addMessageListener('trigger-message-from-sender', function triggerMessageFromSenderHandler() {
       debug('Got message: trigger-message-from-sender');