Bug 1215426 - [Presentation WebAPI] Grant access to browser receiving pages. r=smaug
authorSean Lin <selin@mozilla.com>
Wed, 21 Oct 2015 14:01:08 +0800
changeset 304208 baebc49c4e482a93048c86dbcbe6f1fcd1cdb4ef
parent 304207 ef257382b972c533c0fd08e090e3fe91ae3e62f1
child 304209 cf284898fb2558db29e8d9f039a81eae11189825
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1215426
milestone44.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 1215426 - [Presentation WebAPI] Grant access to browser receiving pages. r=smaug
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/presentation/tests/mochitest/file_presentation_non_receiver_oop.html
dom/presentation/tests/mochitest/file_presentation_receiver_oop.html
dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
dom/webidl/Navigator.webidl
dom/webidl/Presentation.webidl
dom/webidl/PresentationAvailability.webidl
dom/webidl/PresentationConnection.webidl
dom/webidl/PresentationConnectionAvailableEvent.webidl
dom/webidl/PresentationReceiver.webidl
dom/webidl/PresentationRequest.webidl
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -72,16 +72,17 @@
 #include "nsIHttpChannelInternal.h"
 #include "TimeManager.h"
 #include "DeviceStorage.h"
 #include "nsIDOMNavigatorSystemMessages.h"
 #include "nsStreamUtils.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "WidgetUtils.h"
+#include "nsIPresentationService.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 #include "mozilla/dom/MediaDevices.h"
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #endif
@@ -2617,16 +2618,74 @@ Navigator::HasTVSupport(JSContext* aCx, 
   }
 
   // Only support TV Manager API for certified apps for now.
   return status == nsIPrincipal::APP_STATUS_CERTIFIED;
 }
 
 /* static */
 bool
+Navigator::HasPresentationSupport(JSContext* aCx, JSObject* aGlobal)
+{
+  JS::Rooted<JSObject*> global(aCx, aGlobal);
+
+  nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
+  if (NS_WARN_IF(!win)) {
+    return false;
+  }
+
+  // Grant access if it has the permission.
+  if (CheckPermission(win, "presentation")) {
+    return true;
+  }
+
+  // Grant access to browser receiving pages and their same-origin iframes. (App
+  // pages should be controlled by "presentation" permission in app manifests.)
+  mozilla::dom::ContentChild* cc =
+    mozilla::dom::ContentChild::GetSingleton();
+  if (!cc || !cc->IsForBrowser()) {
+    return false;
+  }
+
+  nsCOMPtr<nsIDOMWindow> top;
+  nsresult rv = win->GetTop(getter_AddRefs(top));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(win);
+  nsCOMPtr<nsIScriptObjectPrincipal> topSop = do_QueryInterface(top);
+  if (!sop || !topSop) {
+    return false;
+  }
+
+  nsIPrincipal* principal = sop->GetPrincipal();
+  nsIPrincipal* topPrincipal = topSop->GetPrincipal();
+  if (!principal || !topPrincipal || !principal->Subsumes(topPrincipal)) {
+    return false;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top);
+  if (!piTop || !(piTop = piTop->GetCurrentInnerWindow())) {
+    return false;
+  }
+
+  nsCOMPtr<nsIPresentationService> presentationService =
+    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!presentationService)) {
+    return false;
+  }
+
+  nsAutoString sessionId;
+  presentationService->GetExistentSessionIdAtLaunch(piTop->WindowID(), sessionId);
+  return !sessionId.IsEmpty();
+}
+
+/* static */
+bool
 Navigator::IsE10sEnabled(JSContext* aCx, JSObject* aGlobal)
 {
   return XRE_IsContentProcess();
 }
 
 bool
 Navigator::MozE10sEnabled()
 {
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -330,16 +330,18 @@ public:
   static bool HasDataStoreSupport(JSContext* cx, JSObject* aGlobal);
 
 #ifdef MOZ_B2G
   static bool HasMobileIdSupport(JSContext* aCx, JSObject* aGlobal);
 #endif
 
   static bool HasTVSupport(JSContext* aCx, JSObject* aGlobal);
 
+  static bool HasPresentationSupport(JSContext* aCx, JSObject* aGlobal);
+
   static bool IsE10sEnabled(JSContext* aCx, JSObject* aGlobal);
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetWindow();
   }
 
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/presentation/tests/mochitest/file_presentation_non_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/file_presentation_non_receiver_oop.html
@@ -23,18 +23,17 @@ function info(msg) {
 }
 
 function finish() {
   alert('DONE');
 }
 
 function testConnectionAvailable() {
   return new Promise(function(aResolve, aReject) {
-    ok(navigator.presentation, "navigator.presentation should be available in OOP pages.");
-    ok(!navigator.presentation.receiver, "Non-receiving OOP pages shouldn't get a presentation receiver instance.");
+    ok(!navigator.presentation, "navigator.presentation shouldn't be available in non-receiving OOP pages.");
     aResolve();
   });
 }
 
 testConnectionAvailable().
 then(finish);
 
 </script>
--- a/dom/presentation/tests/mochitest/file_presentation_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/file_presentation_receiver_oop.html
@@ -29,18 +29,18 @@ function command(msg) {
 function finish() {
   alert('DONE');
 }
 
 var connection;
 
 function testConnectionAvailable() {
   return new Promise(function(aResolve, aReject) {
-    ok(navigator.presentation, "navigator.presentation should be available.");
-    ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available.");
+    ok(navigator.presentation, "navigator.presentation should be available in OOP receiving pages.");
+    ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available in OOP receiving pages.");
 
     navigator.presentation.receiver.getConnection().then(
       function(aConnection) {
         connection = aConnection;
 
         ok(connection.id, "Connection ID should be set: " + connection.id);
         is(connection.state, "closed", "Connection state at receiver side should be closed by default.");
         aResolve();
@@ -49,16 +49,26 @@ function testConnectionAvailable() {
         ok(false, "Error occurred when getting the connection: " + aError);
         finish();
         aReject();
       }
     );
   });
 }
 
+function testConnectionAvailableInnerIframe() {
+  return new Promise(function(aResolve, aReject) {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('src', './file_presentation_receiver_inner_iframe_oop.html');
+    document.body.appendChild(iframe);
+
+    aResolve();
+  });
+}
+
 function testConnectionReady() {
   return new Promise(function(aResolve, aReject) {
     connection.onstatechange = function() {
       connection.onstatechange = null;
       is(connection.state, "connected", "Connection state should become connected.");
       aResolve();
     };
 
@@ -89,16 +99,17 @@ function testTerminateConnection() {
       aResolve();
     };
 
     connection.terminate();
   });
 }
 
 testConnectionAvailable().
+then(testConnectionAvailableInnerIframe).
 then(testConnectionReady).
 then(testIncomingMessage).
 then(testTerminateConnection).
 then(finish);
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
@@ -27,19 +27,16 @@ var isNonReceiverFinished = false;
 var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
           .getService(SpecialPowers.Ci.nsIObserverService);
 
 function setup() {
   return new Promise(function(aResolve, aReject) {
     gScript.sendAsyncMessage('trigger-device-add');
 
     // Create a receiver OOP iframe.
-    SpecialPowers.addPermission('presentation', true, { url: receiverUrl,
-                                                        appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
-                                                        isInBrowserElement: true });
     var receiverIframe = document.createElement('iframe');
     receiverIframe.setAttribute('remote', 'true');
     receiverIframe.setAttribute('mozbrowser', 'true');
     receiverIframe.setAttribute('src', receiverUrl);
 
     // This event is triggered when the iframe calls "alert".
     receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(aEvent) {
       var message = aEvent.detail.message;
@@ -67,19 +64,16 @@ function setup() {
     var promise = new Promise(function(aResolve, aReject) {
       document.body.appendChild(receiverIframe);
 
       aResolve(receiverIframe);
     });
     obs.notifyObservers(promise, 'setup-request-promise', null);
 
     // Create a non-receiver OOP iframe.
-    SpecialPowers.addPermission('presentation', true, { url: nonReceiverUrl,
-                                                        appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
-                                                        isInBrowserElement: true });
     var nonReceiverIframe = document.createElement('iframe');
     nonReceiverIframe.setAttribute('remote', 'true');
     nonReceiverIframe.setAttribute('mozbrowser', 'true');
     nonReceiverIframe.setAttribute('src', nonReceiverUrl);
 
     // This event is triggered when the iframe calls "alert".
     nonReceiverIframe.addEventListener('mozbrowsershowmodalprompt', function nonReceiverListener(aEvent) {
       var message = aEvent.detail.message;
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -428,17 +428,17 @@ partial interface Navigator {
 };
 
 partial interface Navigator {
   [Throws, Pref="dom.inputport.enabled", CheckAnyPermissions="inputport", AvailableIn=CertifiedApps]
   readonly attribute InputPortManager inputPortManager;
 };
 
 partial interface Navigator {
-  [Throws, Pref="dom.presentation.enabled", CheckAnyPermissions="presentation", SameObject]
+  [Throws, Pref="dom.presentation.enabled", Func="Navigator::HasPresentationSupport", SameObject]
   readonly attribute Presentation? presentation;
 };
 
 partial interface Navigator {
   [NewObject, Pref="dom.mozTCPSocket.enabled", CheckAnyPermissions="tcp-socket"]
   readonly attribute LegacyMozTCPSocket mozTCPSocket;
 };
 
--- a/dom/webidl/Presentation.webidl
+++ b/dom/webidl/Presentation.webidl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation"]
+ Func="Navigator::HasPresentationSupport"]
 interface Presentation : EventTarget {
  /*
   * This should be used by the UA as the default presentation request for the
   * controller. When the UA wishes to initiate a PresentationConnection on the
   * controller's behalf, it MUST start a presentation connection using the default
   * presentation request (as if the controller had called |defaultRequest.start()|).
   *
   * Only used by controlling browsing context (senders).
--- a/dom/webidl/PresentationAvailability.webidl
+++ b/dom/webidl/PresentationAvailability.webidl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation"]
+ Func="Navigator::HasPresentationSupport"]
 interface PresentationAvailability : EventTarget {
   /*
    * If there is at least one device discovered by UA, the value is |true|.
    * Otherwise, its value should be |false|.
    */
   readonly attribute boolean value;
 
   /*
--- a/dom/webidl/PresentationConnection.webidl
+++ b/dom/webidl/PresentationConnection.webidl
@@ -13,17 +13,17 @@ enum PresentationConnectionState
   "closed",
 
   // The presentation is nonexistent anymore. It could be terminated manually,
   // or either controlling or receiving browsing context is no longer available.
   "terminated"
 };
 
 [Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation"]
+ Func="Navigator::HasPresentationSupport"]
 interface PresentationConnection : EventTarget {
   /*
    * Unique id for all existing connections.
    */
   [Constant]
   readonly attribute DOMString id;
 
   /*
--- a/dom/webidl/PresentationConnectionAvailableEvent.webidl
+++ b/dom/webidl/PresentationConnectionAvailableEvent.webidl
@@ -2,17 +2,17 @@
 /* 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 type,
              optional PresentationConnectionAvailableEventInit eventInitDict),
  Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation"]
+ Func="Navigator::HasPresentationSupport"]
 interface PresentationConnectionAvailableEvent : Event
 {
   [SameObject]
   readonly attribute PresentationConnection connection;
 };
 
 dictionary PresentationConnectionAvailableEventInit : EventInit
 {
--- a/dom/webidl/PresentationReceiver.webidl
+++ b/dom/webidl/PresentationReceiver.webidl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation"]
+ Func="Navigator::HasPresentationSupport"]
 interface PresentationReceiver : EventTarget {
   /*
    * Get the first connected presentation connection in a receiving browsing
    * context.
    */
   [Throws]
   Promise<PresentationConnection> getConnection();
 
--- a/dom/webidl/PresentationRequest.webidl
+++ b/dom/webidl/PresentationRequest.webidl
@@ -1,17 +1,17 @@
 /* -*- 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),
  Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation"]
+ Func="Navigator::HasPresentationSupport"]
 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.
    *
    * The promise is resolved when the presenting page is successfully loaded and