Bug 1288297 - Construct PresentationRequest with multiple URLs, r=smaug
authorKershaw Chang <kechang@mozilla.com>
Mon, 05 Sep 2016 01:17:00 +0200
changeset 312659 41bad373bd639cda96de080ca16e8e876be63463
parent 312658 38cad72a77a69b3b47eff8b70d64af4486253b25
child 312660 d0f9173a4daf66f0a2188a6b34e7b3f99d2085d8
push id81421
push usercbook@mozilla.com
push dateMon, 05 Sep 2016 12:45:50 +0000
treeherdermozilla-inbound@d0f9173a4daf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1288297
milestone51.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 1288297 - Construct PresentationRequest with multiple URLs, r=smaug
dom/presentation/AvailabilityCollection.cpp
dom/presentation/AvailabilityCollection.h
dom/presentation/PresentationAvailability.cpp
dom/presentation/PresentationAvailability.h
dom/presentation/PresentationCallbacks.cpp
dom/presentation/PresentationCallbacks.h
dom/presentation/PresentationRequest.cpp
dom/presentation/PresentationRequest.h
dom/presentation/PresentationService.cpp
dom/presentation/PresentationSessionInfo.cpp
dom/presentation/interfaces/nsIPresentationDevicePrompt.idl
dom/presentation/interfaces/nsIPresentationService.idl
dom/presentation/ipc/PPresentation.ipdl
dom/presentation/ipc/PPresentationRequest.ipdl
dom/presentation/ipc/PresentationChild.cpp
dom/presentation/ipc/PresentationChild.h
dom/presentation/ipc/PresentationIPCService.cpp
dom/presentation/ipc/PresentationParent.cpp
dom/presentation/tests/mochitest/test_presentation_dc_sender.html
dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
dom/webidl/PresentationRequest.webidl
mobile/android/components/PresentationDevicePrompt.js
--- a/dom/presentation/AvailabilityCollection.cpp
+++ b/dom/presentation/AvailabilityCollection.cpp
@@ -67,30 +67,30 @@ AvailabilityCollection::Remove(Presentat
     return;
   }
 
   WeakPtr<PresentationAvailability> availability = aAvailability;
   mAvailabilities.RemoveElement(availability);
 }
 
 already_AddRefed<PresentationAvailability>
-AvailabilityCollection::Find(const uint64_t aWindowId, const nsAString& aUrl)
+AvailabilityCollection::Find(const uint64_t aWindowId, const nsTArray<nsString>& aUrls)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Loop backwards to allow removing elements in the loop.
   for (int i = mAvailabilities.Length() - 1; i >= 0; --i) {
     WeakPtr<PresentationAvailability> availability = mAvailabilities[i];
     if (!availability) {
       // The availability object was destroyed. Remove it from the list.
       mAvailabilities.RemoveElementAt(i);
       continue;
     }
 
-    if (availability->Equals(aWindowId, aUrl)) {
+    if (availability->Equals(aWindowId, aUrls)) {
       RefPtr<PresentationAvailability> matchedAvailability = availability.get();
       return matchedAvailability.forget();
     }
   }
 
 
   return nullptr;
 }
--- a/dom/presentation/AvailabilityCollection.h
+++ b/dom/presentation/AvailabilityCollection.h
@@ -22,17 +22,17 @@ class AvailabilityCollection final
 public:
   static AvailabilityCollection* GetSingleton();
 
   void Add(PresentationAvailability* aAvailability);
 
   void Remove(PresentationAvailability* aAvailability);
 
   already_AddRefed<PresentationAvailability>
-  Find(const uint64_t aWindowId, const nsAString& aUrl);
+  Find(const uint64_t aWindowId, const nsTArray<nsString>& aUrls);
 
 private:
   friend class StaticAutoPtr<AvailabilityCollection>;
 
   AvailabilityCollection();
   virtual ~AvailabilityCollection();
 
   static StaticAutoPtr<AvailabilityCollection> sSingleton;
--- a/dom/presentation/PresentationAvailability.cpp
+++ b/dom/presentation/PresentationAvailability.cpp
@@ -32,30 +32,30 @@ NS_IMPL_ADDREF_INHERITED(PresentationAva
 NS_IMPL_RELEASE_INHERITED(PresentationAvailability, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationAvailability)
   NS_INTERFACE_MAP_ENTRY(nsIPresentationAvailabilityListener)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 /* static */ already_AddRefed<PresentationAvailability>
 PresentationAvailability::Create(nsPIDOMWindowInner* aWindow,
-                                 const nsAString& aUrl,
+                                 const nsTArray<nsString>& aUrls,
                                  RefPtr<Promise>& aPromise)
 {
   RefPtr<PresentationAvailability> availability =
-    new PresentationAvailability(aWindow, aUrl);
+    new PresentationAvailability(aWindow, aUrls);
   return NS_WARN_IF(!availability->Init(aPromise)) ? nullptr
                                                    : availability.forget();
 }
 
 PresentationAvailability::PresentationAvailability(nsPIDOMWindowInner* aWindow,
-                                                   const nsAString& aUrl)
+                                                   const nsTArray<nsString>& aUrls)
   : DOMEventTargetHelper(aWindow)
   , mIsAvailable(false)
-  , mUrl(aUrl)
+  , mUrls(aUrls)
 {
 }
 
 PresentationAvailability::~PresentationAvailability()
 {
   Shutdown();
 }
 
@@ -115,20 +115,25 @@ PresentationAvailability::DisconnectFrom
 PresentationAvailability::WrapObject(JSContext* aCx,
                                      JS::Handle<JSObject*> aGivenProto)
 {
   return PresentationAvailabilityBinding::Wrap(aCx, this, aGivenProto);
 }
 
 bool
 PresentationAvailability::Equals(const uint64_t aWindowID,
-                                 const nsAString& aUrl) const
+                                 const nsTArray<nsString>& aUrls) const
 {
   if (GetOwner() && GetOwner()->WindowID() == aWindowID &&
-      mUrl.Equals(aUrl)) {
+      mUrls.Length() == aUrls.Length()) {
+    for (const auto& url : aUrls) {
+      if (!mUrls.Contains(url)) {
+        return false;
+      }
+    }
     return true;
   }
 
   return false;
 }
 
 bool
 PresentationAvailability::IsCachedValueReady()
@@ -157,18 +162,17 @@ PresentationAvailability::NotifyAvailabl
                                     <bool>(this,
                                            &PresentationAvailability::UpdateAvailabilityAndDispatchEvent,
                                            aIsAvailable));
 }
 
 void
 PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable)
 {
-  PRES_DEBUG("%s:id[%s]\n", __func__,
-             NS_ConvertUTF16toUTF8(mUrl).get());
+  PRES_DEBUG("%s\n", __func__);
   bool isChanged = (aIsAvailable != mIsAvailable);
 
   mIsAvailable = aIsAvailable;
 
   if (!mPromises.IsEmpty()) {
     // Use the first availability change notification to resolve promise.
     do {
       nsTArray<RefPtr<Promise>> promises = Move(mPromises);
--- a/dom/presentation/PresentationAvailability.h
+++ b/dom/presentation/PresentationAvailability.h
@@ -24,50 +24,50 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationAvailability,
                                            DOMEventTargetHelper)
   NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PresentationAvailability)
 
   static already_AddRefed<PresentationAvailability>
   Create(nsPIDOMWindowInner* aWindow,
-         const nsAString& aUrl,
+         const nsTArray<nsString>& aUrls,
          RefPtr<Promise>& aPromise);
 
   virtual void DisconnectFromOwner() override;
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
-  bool Equals(const uint64_t aWindowID, const nsAString& aUrl) const;
+  bool Equals(const uint64_t aWindowID, const nsTArray<nsString>& aUrls) const;
 
   bool IsCachedValueReady();
 
   void EnqueuePromise(RefPtr<Promise>& aPromise);
 
   // WebIDL (public APIs)
   bool Value() const;
 
   IMPL_EVENT_HANDLER(change);
 
 private:
   explicit PresentationAvailability(nsPIDOMWindowInner* aWindow,
-                                    const nsAString& aUrl);
+                                    const nsTArray<nsString>& aUrls);
 
   virtual ~PresentationAvailability();
 
   bool Init(RefPtr<Promise>& aPromise);
 
   void Shutdown();
 
   void UpdateAvailabilityAndDispatchEvent(bool aIsAvailable);
 
   bool mIsAvailable;
 
   nsTArray<RefPtr<Promise>> mPromises;
 
-  nsString mUrl;
+  nsTArray<nsString> mUrls;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PresentationAvailability_h
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -20,41 +20,43 @@ using namespace mozilla::dom;
 
 /*
  * Implementation of PresentationRequesterCallback
  */
 
 NS_IMPL_ISUPPORTS(PresentationRequesterCallback, nsIPresentationServiceCallback)
 
 PresentationRequesterCallback::PresentationRequesterCallback(PresentationRequest* aRequest,
-                                                             const nsAString& aUrl,
                                                              const nsAString& aSessionId,
                                                              Promise* aPromise)
   : mRequest(aRequest)
-  , mUrl(aUrl)
   , mSessionId(aSessionId)
   , mPromise(aPromise)
 {
   MOZ_ASSERT(mRequest);
   MOZ_ASSERT(mPromise);
   MOZ_ASSERT(!mSessionId.IsEmpty());
 }
 
 PresentationRequesterCallback::~PresentationRequesterCallback()
 {
 }
 
 // nsIPresentationServiceCallback
 NS_IMETHODIMP
-PresentationRequesterCallback::NotifySuccess()
+PresentationRequesterCallback::NotifySuccess(const nsAString& aUrl)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (aUrl.IsEmpty()) {
+    return NotifyError(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
   RefPtr<PresentationConnection> connection =
-    PresentationConnection::Create(mRequest->GetOwner(), mSessionId, mUrl,
+    PresentationConnection::Create(mRequest->GetOwner(), mSessionId, aUrl,
                                    nsIPresentationService::ROLE_CONTROLLER);
   if (NS_WARN_IF(!connection)) {
     mPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return NS_OK;
   }
 
   mPromise->MaybeResolve(connection);
 
@@ -74,31 +76,30 @@ PresentationRequesterCallback::NotifyErr
  * Implementation of PresentationRequesterCallback
  */
 
 NS_IMPL_ISUPPORTS_INHERITED0(PresentationReconnectCallback,
                              PresentationRequesterCallback)
 
 PresentationReconnectCallback::PresentationReconnectCallback(
                                            PresentationRequest* aRequest,
-                                           const nsAString& aUrl,
                                            const nsAString& aSessionId,
                                            Promise* aPromise,
                                            PresentationConnection* aConnection)
-  : PresentationRequesterCallback(aRequest, aUrl, aSessionId, aPromise)
+  : PresentationRequesterCallback(aRequest, aSessionId, aPromise)
   , mConnection(aConnection)
 {
 }
 
 PresentationReconnectCallback::~PresentationReconnectCallback()
 {
 }
 
 NS_IMETHODIMP
-PresentationReconnectCallback::NotifySuccess()
+PresentationReconnectCallback::NotifySuccess(const nsAString& aUrl)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -111,17 +112,17 @@ PresentationReconnectCallback::NotifySuc
     mPromise->MaybeResolve(mConnection);
     rv = mRequest->DispatchConnectionAvailableEvent(mConnection);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else {
     // Use |PresentationRequesterCallback::NotifySuccess| to create a new
     // connection since we don't find one that can be reused.
-    rv = PresentationRequesterCallback::NotifySuccess();
+    rv = PresentationRequesterCallback::NotifySuccess(aUrl);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     rv = service->UpdateWindowIdBySessionId(mSessionId,
                                             nsIPresentationService::ROLE_CONTROLLER,
                                             mRequest->GetOwner()->WindowID());
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/presentation/PresentationCallbacks.h
+++ b/dom/presentation/PresentationCallbacks.h
@@ -26,37 +26,34 @@ class Promise;
 
 class PresentationRequesterCallback : public nsIPresentationServiceCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONSERVICECALLBACK
 
   PresentationRequesterCallback(PresentationRequest* aRequest,
-                                const nsAString& aUrl,
                                 const nsAString& aSessionId,
                                 Promise* aPromise);
 
 protected:
   virtual ~PresentationRequesterCallback();
 
   RefPtr<PresentationRequest> mRequest;
-  nsString mUrl;
   nsString mSessionId;
   RefPtr<Promise> mPromise;
 };
 
 class PresentationReconnectCallback final : public PresentationRequesterCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONSERVICECALLBACK
 
   PresentationReconnectCallback(PresentationRequest* aRequest,
-                                const nsAString& aUrl,
                                 const nsAString& aSessionId,
                                 Promise* aPromise,
                                 PresentationConnection* aConnection);
 
 private:
   virtual ~PresentationReconnectCallback();
 
   RefPtr<PresentationConnection> mConnection;
--- a/dom/presentation/PresentationRequest.cpp
+++ b/dom/presentation/PresentationRequest.cpp
@@ -7,16 +7,17 @@
 #include "PresentationRequest.h"
 
 #include "AvailabilityCollection.h"
 #include "ControllerConnectionCollection.h"
 #include "mozilla/BasePrincipal.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 "nsIDocument.h"
 #include "nsIPresentationService.h"
 #include "nsIURI.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
@@ -60,47 +61,61 @@ GetAbsoluteURL(const nsAString& aUrl,
   return NS_OK;
 }
 
 /* static */ already_AddRefed<PresentationRequest>
 PresentationRequest::Constructor(const GlobalObject& aGlobal,
                                  const nsAString& aUrl,
                                  ErrorResult& aRv)
 {
+  Sequence<nsString> urls;
+  urls.AppendElement(aUrl, fallible);
+  return Constructor(aGlobal, urls, aRv);
+}
+
+/* static */ already_AddRefed<PresentationRequest>
+PresentationRequest::Constructor(const GlobalObject& aGlobal,
+                                 const Sequence<nsString>& aUrls,
+                                 ErrorResult& aRv)
+{
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
-  // Ensure the URL is not empty.
-  if (NS_WARN_IF(aUrl.IsEmpty())) {
-    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+  if (aUrls.IsEmpty()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
   // Resolve relative URL to absolute URL
   nsCOMPtr<nsIURI> baseUri = window->GetDocBaseURI();
+  nsTArray<nsString> urls;
+  for (const auto& url : aUrls) {
+    nsAutoString absoluteUrl;
+    nsresult rv =
+      GetAbsoluteURL(url, baseUri, window->GetExtantDoc(), absoluteUrl);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+      return nullptr;
+    }
 
-  nsAutoString absoluteUrl;
-  nsresult rv = GetAbsoluteURL(aUrl, baseUri, window->GetExtantDoc(), absoluteUrl);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+    urls.AppendElement(absoluteUrl);
   }
 
   RefPtr<PresentationRequest> request =
-    new PresentationRequest(window, absoluteUrl);
+    new PresentationRequest(window, Move(urls));
   return NS_WARN_IF(!request->Init()) ? nullptr : request.forget();
 }
 
 PresentationRequest::PresentationRequest(nsPIDOMWindowInner* aWindow,
-                                         const nsAString& aUrl)
+                                         nsTArray<nsString>&& aUrls)
   : DOMEventTargetHelper(aWindow)
-  , mUrl(aUrl)
+  , mUrls(Move(aUrls))
 {
 }
 
 PresentationRequest::~PresentationRequest()
 {
 }
 
 bool
@@ -147,17 +162,17 @@ PresentationRequest::StartWithDevice(con
   }
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   if (IsProhibitMixedSecurityContexts(doc) &&
-      !IsPrioriAuthenticatedURL(mUrl)) {
+      !IsAllURLAuthenticated()) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
@@ -180,18 +195,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();
   }
 
   nsCOMPtr<nsIPresentationServiceCallback> callback =
-    new PresentationRequesterCallback(this, mUrl, id, promise);
-  rv = service->StartSession(mUrl, id, origin, aDeviceId, GetOwner()->WindowID(), callback);
+    new PresentationRequesterCallback(this, id, promise);
+  rv = service->StartSession(mUrls, id, origin, aDeviceId, GetOwner()->WindowID(), callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
@@ -211,17 +226,17 @@ PresentationRequest::Reconnect(const nsA
   }
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   if (IsProhibitMixedSecurityContexts(doc) &&
-      !IsPrioriAuthenticatedURL(mUrl)) {
+      !IsAllURLAuthenticated()) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
@@ -257,17 +272,17 @@ PresentationRequest::FindOrCreatePresent
     ControllerConnectionCollection::GetSingleton()->FindConnection(
       GetOwner()->WindowID(),
       aPresentationId,
       nsIPresentationService::ROLE_CONTROLLER);
 
   if (connection) {
     nsAutoString url;
     connection->GetUrl(url);
-    if (url.Equals(mUrl)) {
+    if (mUrls.Contains(url)) {
       switch (connection->State()) {
         case PresentationConnectionState::Closed:
           // We found the matched connection.
           break;
         case PresentationConnectionState::Connecting:
         case PresentationConnectionState::Connected:
           aPromise->MaybeResolve(connection);
           return;
@@ -288,36 +303,34 @@ PresentationRequest::FindOrCreatePresent
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if(NS_WARN_IF(!service)) {
     aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   nsCOMPtr<nsIPresentationServiceCallback> callback =
     new PresentationReconnectCallback(this,
-                                      mUrl,
                                       aPresentationId,
                                       aPromise,
                                       connection);
 
   nsresult rv =
-    service->ReconnectSession(mUrl,
+    service->ReconnectSession(mUrls,
                               aPresentationId,
                               nsIPresentationService::ROLE_CONTROLLER,
                               callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
   }
 }
 
 already_AddRefed<Promise>
 PresentationRequest::GetAvailability(ErrorResult& aRv)
 {
-  PRES_DEBUG("%s:id[%s]\n", __func__,
-             NS_ConvertUTF16toUTF8(mUrl).get());
+  PRES_DEBUG("%s\n", __func__);
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   if (NS_WARN_IF(!global)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
@@ -326,17 +339,17 @@ PresentationRequest::GetAvailability(Err
   }
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   if (IsProhibitMixedSecurityContexts(doc) &&
-      !IsPrioriAuthenticatedURL(mUrl)) {
+      !IsAllURLAuthenticated()) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
@@ -358,23 +371,22 @@ PresentationRequest::FindOrCreatePresent
 
   AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
   if (NS_WARN_IF(!collection)) {
     aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return;
   }
 
   RefPtr<PresentationAvailability> availability =
-    collection->Find(GetOwner()->WindowID(), mUrl);
+    collection->Find(GetOwner()->WindowID(), mUrls);
 
   if (!availability) {
-    availability = PresentationAvailability::Create(GetOwner(), mUrl, aPromise);
+    availability = PresentationAvailability::Create(GetOwner(), mUrls, aPromise);
   } else {
-    PRES_DEBUG(">resolve with same object:id[%s]\n",
-               NS_ConvertUTF16toUTF8(mUrl).get());
+    PRES_DEBUG(">resolve with same object\n");
 
     // Fetching cached available devices is asynchronous in our implementation,
     // we need to ensure the promise is resolved in order.
     if (availability->IsCachedValueReady()) {
       aPromise->MaybeResolve(availability);
       return;
     }
 
@@ -469,8 +481,20 @@ PresentationRequest::IsPrioriAuthenticat
   if (NS_WARN_IF(!csm)) {
     return false;
   }
 
   bool isTrustworthyOrigin = false;
   csm->IsOriginPotentiallyTrustworthy(principal, &isTrustworthyOrigin);
   return isTrustworthyOrigin;
 }
+
+bool
+PresentationRequest::IsAllURLAuthenticated()
+{
+  for (const auto& url : mUrls) {
+    if (!IsPrioriAuthenticatedURL(url)) {
+      return false;
+    }
+  }
+
+  return true;
+}
--- a/dom/presentation/PresentationRequest.h
+++ b/dom/presentation/PresentationRequest.h
@@ -2,35 +2,42 @@
 /* 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_PresentationRequest_h
 #define mozilla_dom_PresentationRequest_h
 
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
 class nsIDocument;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class PresentationAvailability;
 class PresentationConnection;
 
 class PresentationRequest final : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
-  static already_AddRefed<PresentationRequest> Constructor(const GlobalObject& aGlobal,
-                                                           const nsAString& aUrl,
-                                                           ErrorResult& aRv);
+  static already_AddRefed<PresentationRequest> Constructor(
+    const GlobalObject& aGlobal,
+    const nsAString& aUrl,
+    ErrorResult& aRv);
+
+  static already_AddRefed<PresentationRequest> Constructor(
+    const GlobalObject& aGlobal,
+    const Sequence<nsString>& aUrls,
+    ErrorResult& aRv);
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL (public APIs)
   already_AddRefed<Promise> Start(ErrorResult& aRv);
 
   already_AddRefed<Promise> StartWithDevice(const nsAString& aDeviceId,
@@ -42,32 +49,34 @@ public:
   already_AddRefed<Promise> GetAvailability(ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(connectionavailable);
 
   nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection);
 
 private:
   PresentationRequest(nsPIDOMWindowInner* aWindow,
-                      const nsAString& aUrl);
+                      nsTArray<nsString>&& aUrls);
 
   ~PresentationRequest();
 
   bool Init();
 
   void FindOrCreatePresentationConnection(const nsAString& aPresentationId,
                                           Promise* aPromise);
 
   void FindOrCreatePresentationAvailability(RefPtr<Promise>& aPromise);
 
   // Implement https://w3c.github.io/webappsec-mixed-content/#categorize-settings-object
   bool IsProhibitMixedSecurityContexts(nsIDocument* aDocument);
 
   // Implement https://w3c.github.io/webappsec-mixed-content/#a-priori-authenticated-url
   bool IsPrioriAuthenticatedURL(const nsAString& aUrl);
 
-  nsString mUrl;
+  bool IsAllURLAuthenticated();
+
+  nsTArray<nsString> mUrls;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PresentationRequest_h
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -54,110 +54,167 @@ IsSameDevice(nsIPresentationDevice* aDev
   aDeviceAnother->GetType(anotherType);
   if (!deviceType.Equals(anotherType)) {
     return false;
   }
 
   return true;
 }
 
+static nsresult
+ConvertURLArrayHelper(const nsTArray<nsString>& aUrls, nsIArray** aResult)
+{
+  if (!aResult) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  *aResult = nullptr;
+
+  nsresult rv;
+  nsCOMPtr<nsIMutableArray> urls =
+    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  for (const auto& url : aUrls) {
+    nsCOMPtr<nsISupportsString> isupportsString =
+      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = isupportsString->SetData(url);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = urls->AppendElement(isupportsString, false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  urls.forget(aResult);
+  return NS_OK;
+}
+
 /*
  * Implementation of PresentationDeviceRequest
  */
 
 class PresentationDeviceRequest final : public nsIPresentationDeviceRequest
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPRESENTATIONDEVICEREQUEST
 
-  PresentationDeviceRequest(const nsAString& aRequestUrl,
+  PresentationDeviceRequest(const nsTArray<nsString>& aUrls,
                             const nsAString& aId,
                             const nsAString& aOrigin,
                             uint64_t aWindowId,
                             nsIPresentationServiceCallback* aCallback);
 
 private:
   virtual ~PresentationDeviceRequest() = default;
-  nsresult CreateSessionInfo(nsIPresentationDevice* aDevice);
+  nsresult CreateSessionInfo(nsIPresentationDevice* aDevice,
+                             const nsAString& aSelectedRequestUrl);
 
-  nsString mRequestUrl;
+  nsTArray<nsString> mRequestUrls;
   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 nsTArray<nsString>& aUrls,
                                       const nsAString& aId,
                                       const nsAString& aOrigin,
                                       uint64_t aWindowId,
                                       nsIPresentationServiceCallback* aCallback)
-  : mRequestUrl(aRequestUrl)
+  : mRequestUrls(aUrls)
   , mId(aId)
   , mOrigin(aOrigin)
   , mWindowId(aWindowId)
   , mCallback(aCallback)
 {
-  MOZ_ASSERT(!mRequestUrl.IsEmpty());
+  MOZ_ASSERT(!mRequestUrls.IsEmpty());
   MOZ_ASSERT(!mId.IsEmpty());
   MOZ_ASSERT(!mOrigin.IsEmpty());
   MOZ_ASSERT(mCallback);
 }
 
 NS_IMETHODIMP
 PresentationDeviceRequest::GetOrigin(nsAString& aOrigin)
 {
   aOrigin = mOrigin;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationDeviceRequest::GetRequestURL(nsAString& aRequestUrl)
+PresentationDeviceRequest::GetRequestURLs(nsIArray** aUrls)
 {
-  aRequestUrl = mRequestUrl;
-  return NS_OK;
+  return ConvertURLArrayHelper(mRequestUrls, aUrls);
 }
 
 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;
+  if (NS_WARN_IF(!aDevice)) {
+    MOZ_ASSERT(false, "|aDevice| should noe be null.");
+    mCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
+    return NS_ERROR_INVALID_ARG;
   }
 
-  return mCallback->NotifySuccess();
+  // Select the most suitable URL for starting the presentation.
+  nsAutoString selectedRequestUrl;
+  for (const auto& url : mRequestUrls) {
+    bool isSupported;
+    if (NS_SUCCEEDED(aDevice->IsRequestedUrlSupported(url, &isSupported)) &&
+        isSupported) {
+      selectedRequestUrl.Assign(url);
+      break;
+    }
+  }
+
+  if (selectedRequestUrl.IsEmpty()) {
+    return mCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
+  }
+
+  if (NS_WARN_IF(NS_FAILED(CreateSessionInfo(aDevice, selectedRequestUrl)))) {
+    return mCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
+  }
+
+  return mCallback->NotifySuccess(selectedRequestUrl);
 }
 
 nsresult
-PresentationDeviceRequest::CreateSessionInfo(nsIPresentationDevice* aDevice)
+PresentationDeviceRequest::CreateSessionInfo(
+                                          nsIPresentationDevice* aDevice,
+                                          const nsAString& aSelectedRequestUrl)
 {
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Create the controlling session info
   RefPtr<PresentationSessionInfo> info =
     static_cast<PresentationService*>(service.get())->
-      CreateControllingSessionInfo(mRequestUrl, mId, mWindowId);
+      CreateControllingSessionInfo(aSelectedRequestUrl, 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;
@@ -567,33 +624,32 @@ PresentationService::IsAppInstalled(nsIU
   if (NS_WARN_IF(!app)) {
     return false;
   }
 
   return true;
 }
 
 NS_IMETHODIMP
-PresentationService::StartSession(const nsAString& aUrl,
+PresentationService::StartSession(const nsTArray<nsString>& aUrls,
                                   const nsAString& aSessionId,
                                   const nsAString& aOrigin,
                                   const nsAString& aDeviceId,
                                   uint64_t aWindowId,
                                   nsIPresentationServiceCallback* aCallback)
 {
-  PRES_DEBUG("%s:url[%s], id[%s]\n", __func__,
-             NS_ConvertUTF16toUTF8(aUrl).get(),
-             NS_ConvertUTF16toUTF8(aSessionId).get());
+  PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get());
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(!aSessionId.IsEmpty());
+  MOZ_ASSERT(!aUrls.IsEmpty());
 
   nsCOMPtr<nsIPresentationDeviceRequest> request =
-    new PresentationDeviceRequest(aUrl,
+    new PresentationDeviceRequest(aUrls,
                                   aSessionId,
                                   aOrigin,
                                   aWindowId,
                                   aCallback);
 
   if (aDeviceId.IsVoid()) {
     // Pop up a prompt and ask user to select a device.
     nsCOMPtr<nsIPresentationDevicePrompt> prompt =
@@ -612,25 +668,21 @@ PresentationService::StartSession(const 
 
   // Find the designated device from available device list.
   nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
     do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
   if (NS_WARN_IF(!deviceManager)) {
     return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
-  nsCOMPtr<nsIMutableArray> presentationUrls =
-    do_CreateInstance(NS_ARRAY_CONTRACTID);
-  if (!presentationUrls) {
+  nsCOMPtr<nsIArray> presentationUrls;
+  if (NS_WARN_IF(NS_FAILED(
+    ConvertURLArrayHelper(aUrls, getter_AddRefs(presentationUrls))))) {
     return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
-  nsCOMPtr<nsISupportsString> supportsStr =
-    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-  supportsStr->SetData(aUrl);
-  presentationUrls->AppendElement(supportsStr, false);
 
   nsCOMPtr<nsIArray> devices;
   nsresult rv = deviceManager->GetAvailableDevices(presentationUrls, getter_AddRefs(devices));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   nsCOMPtr<nsISimpleEnumerator> enumerator;
@@ -742,44 +794,43 @@ PresentationService::TerminateSession(co
   if (NS_WARN_IF(!info)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->Close(NS_OK, nsIPresentationSessionListener::STATE_TERMINATED);
 }
 
 NS_IMETHODIMP
-PresentationService::ReconnectSession(const nsAString& aUrl,
+PresentationService::ReconnectSession(const nsTArray<nsString>& aUrls,
                                       const nsAString& aSessionId,
                                       uint8_t aRole,
                                       nsIPresentationServiceCallback* aCallback)
 {
-  PRES_DEBUG("%s:url[%s], id[%s]\n", __func__,
-             NS_ConvertUTF16toUTF8(aUrl).get(),
-             NS_ConvertUTF16toUTF8(aSessionId).get());
+  PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get());
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aSessionId.IsEmpty());
   MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(!aUrls.IsEmpty());
 
   if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
     MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
     return NS_ERROR_INVALID_ARG;
   }
 
   if (NS_WARN_IF(!aCallback)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
   if (NS_WARN_IF(!info)) {
     return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
   }
 
-  if (NS_WARN_IF(!info->GetUrl().Equals(aUrl))) {
+  if (NS_WARN_IF(!aUrls.Contains(info->GetUrl()))) {
     return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
   }
 
   return static_cast<PresentationControllingInfo*>(info.get())->Reconnect(aCallback);
 }
 
 NS_IMETHODIMP
 PresentationService::BuildTransport(const nsAString& aSessionId,
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -801,17 +801,17 @@ PresentationControllingInfo::NotifyRecon
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mReconnectCallback);
 
   if (NS_WARN_IF(mState != nsIPresentationSessionListener::STATE_CONNECTING)) {
     return NS_ERROR_FAILURE;
   }
 
-  return mReconnectCallback->NotifySuccess();
+  return mReconnectCallback->NotifySuccess(GetUrl());
 }
 
 nsresult
 PresentationControllingInfo::BuildTransport()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
--- a/dom/presentation/interfaces/nsIPresentationDevicePrompt.idl
+++ b/dom/presentation/interfaces/nsIPresentationDevicePrompt.idl
@@ -1,31 +1,32 @@
 /* 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 "nsISupports.idl"
 
+interface nsIArray;
 interface nsIPresentationDevice;
 
 %{C++
 #define PRESENTATION_DEVICE_PROMPT_CONTRACTID "@mozilla.org/presentation-device/prompt;1"
 %}
 
 /*
  * The information and callbacks for device selection
  */
 [scriptable, uuid(b2aa7f6a-9448-446a-bba4-9c29638b0ed4)]
 interface nsIPresentationDeviceRequest : nsISupports
 {
   // The origin which initiate the request.
   readonly attribute DOMString origin;
 
-  // The URL to be opened after selection.
-  readonly attribute DOMString requestURL;
+  // The array of candidate URLs.
+  readonly attribute nsIArray requestURLs;
 
   /*
    * Callback after selecting a device
    * @param device The selected device.
    */
   void select(in nsIPresentationDevice device);
 
   /*
--- a/dom/presentation/interfaces/nsIPresentationService.idl
+++ b/dom/presentation/interfaces/nsIPresentationService.idl
@@ -10,25 +10,33 @@ interface nsIPresentationRespondingListe
 interface nsIPresentationSessionListener;
 
 %{C++
 #define PRESENTATION_SERVICE_CID \
   { 0x1d9bb10c, 0xc0ab, 0x4fe8, \
     { 0x9e, 0x4f, 0x40, 0x58, 0xb8, 0x51, 0x98, 0x32 } }
 #define PRESENTATION_SERVICE_CONTRACTID \
   "@mozilla.org/presentation/presentationservice;1"
+
+#include "nsTArray.h"
+
+class nsString;
 %}
 
+[ref] native URLArrayRef(const nsTArray<nsString>);
+
 [scriptable, uuid(12073206-0065-4b10-9488-a6eb9b23e65b)]
 interface nsIPresentationServiceCallback : nsISupports
 {
   /*
    * Called when the operation succeeds.
+   *
+   * @param url: the selected request url used to start or reconnect a session.
    */
-  void notifySuccess();
+  void notifySuccess(in DOMString url);
 
   /*
    * Called when the operation fails.
    *
    * @param error: error message.
    */
   void notifyError(in nsresult error);
 };
@@ -42,36 +50,36 @@ interface nsIPresentationService : nsISu
   const unsigned short CLOSED_REASON_ERROR = 0x1;
   const unsigned short CLOSED_REASON_CLOSED = 0x2;
   const unsigned short CLOSED_REASON_WENTAWAY = 0x3;
 
   /*
    * Start a new presentation session and display a prompt box which asks users
    * to select a device.
    *
-   * @param url: The url of presenting page.
+   * @param urls: The candidate Urls of presenting page. Only one url would be used.
    * @param sessionId: An ID to identify presentation session.
    * @param origin: The url of requesting page.
    * @param deviceId: The specified device of handling this request, null string
                       for prompt device selection dialog.
    * @param windowId: The inner window ID associated with the presentation
    *                  session. (0 implies no window ID since no actual window
    *                  uses 0 as its ID. Generally it's the case the window is
    *                  located in different process from this service)
    * @param callback: Invoke the callback when the operation is completed.
    *                  NotifySuccess() is called with |id| if a session is
    *                  established successfully with the selected device.
    *                  Otherwise, NotifyError() is called with a error message.
    */
-  void startSession(in DOMString url,
-                    in DOMString sessionId,
-                    in DOMString origin,
-                    in DOMString deviceId,
-                    in unsigned long long windowId,
-                    in nsIPresentationServiceCallback callback);
+  [noscript] void startSession(in URLArrayRef urls,
+                               in DOMString sessionId,
+                               in DOMString origin,
+                               in DOMString deviceId,
+                               in unsigned long long windowId,
+                               in nsIPresentationServiceCallback callback);
 
   /*
    * Send the message to the session.
    *
    * @param sessionId: An ID to identify presentation session.
    * @param role: Identify the function called by controller or receiver.
    * @param data: the message being sent out.
    */
@@ -96,27 +104,27 @@ interface nsIPresentationService : nsISu
    * @param role: Identify the function called by controller or receiver.
    */
   void terminateSession(in DOMString sessionId,
                         in uint8_t role);
 
   /*
    * Reconnect the session.
    *
-   * @param url: The url of presenting page.
+   * @param url: The request Urls.
    * @param sessionId: An ID to identify presentation session.
    * @param role: Identify the function called by controller or receiver.
    * @param callback: NotifySuccess() is called when a control channel
    *                  is opened successfully.
    *                  Otherwise, NotifyError() is called with a error message.
    */
-  void reconnectSession(in DOMString url,
-                        in DOMString sessionId,
-                        in uint8_t role,
-                        in nsIPresentationServiceCallback callback);
+  [noscript] void reconnectSession(in URLArrayRef urls,
+                                   in DOMString sessionId,
+                                   in uint8_t role,
+                                   in nsIPresentationServiceCallback callback);
 
   /*
    * Register an availability listener. Must be called from the main thread.
    *
    * @param listener: The listener to register.
    */
   void registerAvailabilityListener(in nsIPresentationAvailabilityListener listener);
 
--- a/dom/presentation/ipc/PPresentation.ipdl
+++ b/dom/presentation/ipc/PPresentation.ipdl
@@ -10,17 +10,17 @@ include protocol PPresentationBuilder;
 
 include InputStreamParams;
 
 namespace mozilla {
 namespace dom {
 
 struct StartSessionRequest
 {
-  nsString url;
+  nsString[] urls;
   nsString sessionId;
   nsString origin;
   nsString deviceId;
   uint64_t windowId;
 };
 
 struct SendSessionMessageRequest
 {
@@ -39,17 +39,17 @@ struct CloseSessionRequest
 struct TerminateSessionRequest
 {
   nsString sessionId;
   uint8_t role;
 };
 
 struct ReconnectSessionRequest
 {
-  nsString url;
+  nsString[] urls;
   nsString sessionId;
   uint8_t role;
 };
 
 struct BuildTransportRequest
 {
   nsString sessionId;
   uint8_t role;
--- a/dom/presentation/ipc/PPresentationRequest.ipdl
+++ b/dom/presentation/ipc/PPresentationRequest.ipdl
@@ -10,12 +10,13 @@ namespace mozilla {
 namespace dom {
 
 sync protocol PPresentationRequest
 {
   manager PPresentation;
 
 child:
   async __delete__(nsresult result);
+  async NotifyRequestUrlSelected(nsString aUrl);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/presentation/ipc/PresentationChild.cpp
+++ b/dom/presentation/ipc/PresentationChild.cpp
@@ -158,17 +158,22 @@ PresentationRequestChild::ActorDestroy(A
 bool
 PresentationRequestChild::Recv__delete__(const nsresult& aResult)
 {
   if (mActorDestroyed) {
     return true;
   }
 
   if (mCallback) {
-    if (NS_SUCCEEDED(aResult)) {
-      NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess()));
-    } else {
+    if (NS_FAILED(aResult)) {
       NS_WARN_IF(NS_FAILED(mCallback->NotifyError(aResult)));
     }
   }
 
   return true;
 }
+
+bool
+PresentationRequestChild::RecvNotifyRequestUrlSelected(const nsString& aUrl)
+{
+  NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess(aUrl)));
+  return true;
+}
--- a/dom/presentation/ipc/PresentationChild.h
+++ b/dom/presentation/ipc/PresentationChild.h
@@ -73,16 +73,19 @@ public:
   explicit PresentationRequestChild(nsIPresentationServiceCallback* aCallback);
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual bool
   Recv__delete__(const nsresult& aResult) override;
 
+  virtual bool
+  RecvNotifyRequestUrlSelected(const nsString& aUrl) override;
+
 private:
   virtual ~PresentationRequestChild();
 
   bool mActorDestroyed = false;
   nsCOMPtr<nsIPresentationServiceCallback> mCallback;
 };
 
 } // namespace dom
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -47,30 +47,30 @@ PresentationIPCService::~PresentationIPC
 
   mAvailabilityListeners.Clear();
   mSessionListeners.Clear();
   mSessionInfos.Clear();
   sPresentationChild = nullptr;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::StartSession(const nsAString& aUrl,
+PresentationIPCService::StartSession(const nsTArray<nsString>& aUrls,
                                      const nsAString& aSessionId,
                                      const nsAString& aOrigin,
                                      const nsAString& aDeviceId,
                                      uint64_t aWindowId,
                                      nsIPresentationServiceCallback* aCallback)
 {
   if (aWindowId != 0) {
     AddRespondingSessionId(aWindowId,
                            aSessionId,
                            nsIPresentationService::ROLE_CONTROLLER);
   }
 
-  return SendRequest(aCallback, StartSessionRequest(nsString(aUrl),
+  return SendRequest(aCallback, StartSessionRequest(aUrls,
                                                     nsString(aSessionId),
                                                     nsString(aOrigin),
                                                     nsString(aDeviceId),
                                                     aWindowId));
 }
 
 NS_IMETHODIMP
 PresentationIPCService::SendSessionMessage(const nsAString& aSessionId,
@@ -130,29 +130,29 @@ PresentationIPCService::TerminateSession
   if (mSessionInfos.Get(aSessionId, getter_AddRefs(info))) {
     return info->Close(NS_OK);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationIPCService::ReconnectSession(const nsAString& aUrl,
+PresentationIPCService::ReconnectSession(const nsTArray<nsString>& aUrls,
                                          const nsAString& aSessionId,
                                          uint8_t aRole,
                                          nsIPresentationServiceCallback* aCallback)
 {
   MOZ_ASSERT(!aSessionId.IsEmpty());
 
   if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
     MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
     return NS_ERROR_INVALID_ARG;
   }
 
-  return SendRequest(aCallback, ReconnectSessionRequest(nsString(aUrl),
+  return SendRequest(aCallback, ReconnectSessionRequest(aUrls,
                                                         nsString(aSessionId),
                                                         aRole));
 }
 
 NS_IMETHODIMP
 PresentationIPCService::BuildTransport(const nsAString& aSessionId,
                                        uint8_t aRole)
 {
--- a/dom/presentation/ipc/PresentationParent.cpp
+++ b/dom/presentation/ipc/PresentationParent.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=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/. */
 
 #include "DCPresentationChannelDescription.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/Unused.h"
 #include "nsIPresentationDeviceManager.h"
 #include "nsServiceManagerUtils.h"
 #include "PresentationBuilderParent.h"
 #include "PresentationParent.h"
 #include "PresentationService.h"
 #include "PresentationSessionInfo.h"
 
 using namespace mozilla::dom;
@@ -328,134 +329,135 @@ PresentationRequestParent::ActorDestroy(
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const StartSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
   mNeedRegisterBuilder = true;
   mSessionId = aRequest.sessionId();
-  return mService->StartSession(aRequest.url(), aRequest.sessionId(),
+  return mService->StartSession(aRequest.urls(), aRequest.sessionId(),
                                 aRequest.origin(), aRequest.deviceId(),
                                 aRequest.windowId(), this);
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const SendSessionMessageRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
                   IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
-    return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
+    return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
   }
 
   nsresult rv = mService->SendSessionMessage(aRequest.sessionId(),
                                              aRequest.role(),
                                              aRequest.data());
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NotifyError(rv);
+    return SendResponse(rv);
   }
-  return NotifySuccess();
+  return SendResponse(NS_OK);
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const CloseSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
                   IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
-    return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
+    return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
   }
 
   nsresult rv = mService->CloseSession(aRequest.sessionId(),
                                        aRequest.role(),
                                        aRequest.closedReason());
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NotifyError(rv);
+    return SendResponse(rv);
   }
-  return NotifySuccess();
+  return SendResponse(NS_OK);
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const TerminateSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
                   IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
-    return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
+    return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
   }
 
   nsresult rv = mService->TerminateSession(aRequest.sessionId(), aRequest.role());
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NotifyError(rv);
+    return SendResponse(rv);
   }
-  return NotifySuccess();
+  return SendResponse(NS_OK);
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const ReconnectSessionRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
     IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
 
     // NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec.
     // https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation
-    return NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
+    return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR);
   }
 
   mNeedRegisterBuilder = true;
   mSessionId = aRequest.sessionId();
-  return mService->ReconnectSession(aRequest.url(),
+  return mService->ReconnectSession(aRequest.urls(),
                                     aRequest.sessionId(),
                                     aRequest.role(),
                                     this);
 }
 
 nsresult
 PresentationRequestParent::DoRequest(const BuildTransportRequest& aRequest)
 {
   MOZ_ASSERT(mService);
 
   // Validate the accessibility (primarily for receiver side) so that a
   // compromised child process can't fake the ID.
   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
                   IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
-    return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
+    return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
   }
 
   nsresult rv = mService->BuildTransport(aRequest.sessionId(), aRequest.role());
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NotifyError(rv);
+    return SendResponse(rv);
   }
-  return NotifySuccess();
+  return SendResponse(NS_OK);
 }
 
 NS_IMETHODIMP
-PresentationRequestParent::NotifySuccess()
+PresentationRequestParent::NotifySuccess(const nsAString& aUrl)
 {
   if (mNeedRegisterBuilder) {
     RefPtr<PresentationParent> parent = static_cast<PresentationParent*>(Manager());
     NS_WARN_IF(!parent->RegisterTransportBuilder(
                                       mSessionId,
                                       nsIPresentationService::ROLE_CONTROLLER));
   }
 
+  Unused << SendNotifyRequestUrlSelected(nsString(aUrl));
   return SendResponse(NS_OK);
 }
 
 NS_IMETHODIMP
 PresentationRequestParent::NotifyError(nsresult aError)
 {
   return SendResponse(aError);
 }
--- a/dom/presentation/tests/mochitest/test_presentation_dc_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_dc_sender.html
@@ -233,26 +233,50 @@ function teardown() {
     gScript.destroy();
     info('teardown-complete');
     SimpleTest.finish();
   });
 
   gScript.sendAsyncMessage('teardown');
 }
 
+function testConstructRequestError() {
+  return Promise.all([
+    new Promise(function(aResolve, aReject) {
+      try {
+        request = new PresentationRequest("\\\\\\");
+      }
+      catch(e) {
+        is(e.name, "SyntaxError", "Expect to get SyntaxError.");
+        aResolve();
+      }
+    }),
+    new Promise(function(aResolve, aReject) {
+      try {
+        request = new PresentationRequest([]);
+      }
+      catch(e) {
+        is(e.name, "NotSupportedError", "Expect to get NotSupportedError.");
+        aResolve();
+      }
+    }),
+  ]);
+}
+
 function runTests() {
   ok(window.PresentationRequest, "PresentationRequest should be available.");
 
   testSetup().
   then(testStartConnection).
   then(testSend).
   then(testIncomingMessage).
   then(testCloseConnection).
   then(testReconnect).
   then(testCloseConnection).
+  then(testConstructRequestError).
   then(teardown);
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
--- 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
@@ -36,27 +36,16 @@ function setup() {
         ok(false, "Error occurred when getting availability: " + aError);
         teardown();
         aReject();
       }
     );
   });
 }
 
-function testCreateRequestWithEmptyURL() {
-  return new Promise(function(aResolve, aReject) {
-    try {
-      request = new PresentationRequest("");
-    } catch (aError) {
-      is(aError.name, "SyntaxError", "SyntaxError is expected when using an empty URL.");
-      aResolve();
-    }
-  });
-}
-
 function testStartConnectionCancelPrompt() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
       gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR);
     });
 
@@ -375,18 +364,17 @@ function teardown() {
   });
 
   gScript.sendAsyncMessage('teardown');
 }
 
 function runTests() {
   ok(window.PresentationRequest, "PresentationRequest should be available.");
 
-  testCreateRequestWithEmptyURL().
-  then(setup).
+  setup().
   then(testStartConnectionCancelPrompt).
   then(testStartConnectionNoDevice).
   then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit).
   then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit).
   then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady).
   then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady).
   then(testStartConnectionUnexpectedDataTransportClose).
   then(teardown);
--- a/dom/webidl/PresentationRequest.webidl
+++ b/dom/webidl/PresentationRequest.webidl
@@ -1,15 +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/.
  */
 
 [Constructor(DOMString url),
+ Constructor(sequence<DOMString> urls),
  Pref="dom.presentation.controller.enabled"]
 interface PresentationRequest : EventTarget {
   /*
    * A requesting page use start() to start a new connection, and it will be
    * returned with the promise. UA may show a prompt box with a list of
    * available devices and ask the user to grant permission, choose a device, or
    * cancel the operation.
    *
--- a/mobile/android/components/PresentationDevicePrompt.js
+++ b/mobile/android/components/PresentationDevicePrompt.js
@@ -41,27 +41,22 @@ PresentationDevicePrompt.prototype = {
     debug("_getString");
 
     if (!this.bundle) {
       this.bundle = Services.strings.createBundle("chrome://browser/locale/devicePrompt.properties");
     }
     return this.bundle.GetStringFromName(aName);
   },
 
-  _loadDevices: function(requestURL) {
+  _loadDevices: function(requestURLs) {
     debug("_loadDevices");
 
-    let presentationUrls = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
-    let url = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
-    url.data = requestURL;
-    presentationUrls.appendElement(url, false);
-
     let deviceManager = Cc["@mozilla.org/presentation-device/manager;1"]
                           .getService(Ci.nsIPresentationDeviceManager);
-    let devices = deviceManager.getAvailableDevices(presentationUrls).QueryInterface(Ci.nsIArray);
+    let devices = deviceManager.getAvailableDevices(requestURLs).QueryInterface(Ci.nsIArray);
 
     // Re-load the available devices
     this._devices = [];
     for (let i = 0; i < devices.length; i++) {
       let device = devices.queryElementAt(i, Ci.nsIPresentationDevice);
       this._devices.push(device);
     }
   },
@@ -113,17 +108,17 @@ PresentationDevicePrompt.prototype = {
     this._request.select(this._devices[aIndex]);
   },
 
   // This will be fired when window.PresentationRequest(URL).start() is called
   promptDeviceSelection: function(aRequest) {
     debug("promptDeviceSelection");
 
     // Load available presentation devices into this._devices
-    this._loadDevices(aRequest.requestURL);
+    this._loadDevices(aRequest.requestURLs);
 
     if (!this._devices.length) { // Cancel request if no available device
       aRequest.cancel(Cr.NS_ERROR_DOM_NOT_FOUND_ERR);
       return;
     }
 
     this._request = aRequest;