Bug 1311375 - promise reject when there is an unsettled presentation start procedure. r=smaug
MozReview-Commit-ID: AL0QeyXBwzb
--- 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');