Bug 1314229 - return undefined/null for defaultRequest/receiver if not in corresponding UA or context. r=smaug. draft
authorShih-Chiang Chien <schien@mozilla.com>
Wed, 02 Nov 2016 17:53:31 +0800
changeset 433051 8720563524bfd58f8be65d84cda48984f134f074
parent 433010 ac55a6776435142feebf3c20bbabfee100686416
child 535793 0879e100985bae0830eb7547baea4699d54a00c1
push id34467
push userschien@mozilla.com
push dateThu, 03 Nov 2016 02:45:45 +0000
reviewerssmaug
bugs1314229
milestone52.0a1
Bug 1314229 - return undefined/null for defaultRequest/receiver if not in corresponding UA or context. r=smaug. MozReview-Commit-ID: KUWdfz1u55O
dom/presentation/Presentation.cpp
dom/presentation/Presentation.h
dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html
dom/presentation/tests/mochitest/file_presentation_non_receiver.html
dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html
dom/presentation/tests/mochitest/file_presentation_receiver.html
dom/presentation/tests/mochitest/test_presentation_dc_receiver.html
dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html
dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html
dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html
dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
dom/webidl/Presentation.webidl
--- a/dom/presentation/Presentation.cpp
+++ b/dom/presentation/Presentation.cpp
@@ -39,64 +39,16 @@ NS_INTERFACE_MAP_END
 
 /* static */ already_AddRefed<Presentation>
 Presentation::Create(nsPIDOMWindowInner* aWindow)
 {
   RefPtr<Presentation> presentation = new Presentation(aWindow);
   return presentation.forget();
 }
 
-/* static */ bool
-Presentation::HasReceiverSupport(JSContext* aCx, JSObject* aGlobal)
-{
-  JS::Rooted<JSObject*> global(aCx, aGlobal);
-
-  nsCOMPtr<nsPIDOMWindowInner> inner =
-    do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
-  if (NS_WARN_IF(!inner)) {
-    return false;
-  }
-
-  // Grant access to browser receiving pages and their same-origin iframes. (App
-  // pages should be controlled by "presentation" permission in app manifests.)
-  nsCOMPtr<nsIDocShell> docshell = inner->GetDocShell();
-  if (!docshell) {
-    return false;
-  }
-
-  if (!Preferences::GetBool("dom.presentation.testing.simulate-receiver") &&
-      !docshell->GetIsInMozBrowserOrApp()) {
-    return false;
-  }
-
-  nsAutoString presentationURL;
-  nsContentUtils::GetPresentationURL(docshell, presentationURL);
-
-  if (presentationURL.IsEmpty()) {
-    return false;
-  }
-
-  nsCOMPtr<nsIScriptSecurityManager> securityManager =
-    nsContentUtils::GetSecurityManager();
-  if (!securityManager) {
-    return false;
-  }
-
-  nsCOMPtr<nsIURI> presentationURI;
-  nsresult rv = NS_NewURI(getter_AddRefs(presentationURI), presentationURL);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-
-  nsCOMPtr<nsIURI> docURI = inner->GetDocumentURI();
-  return NS_SUCCEEDED(securityManager->CheckSameOriginURI(presentationURI,
-                                                          docURI,
-                                                          false));
-}
-
 Presentation::Presentation(nsPIDOMWindowInner* aWindow)
   : mWindow(aWindow)
 {
 }
 
 Presentation::~Presentation()
 {
 }
@@ -106,53 +58,45 @@ Presentation::WrapObject(JSContext* aCx,
                          JS::Handle<JSObject*> aGivenProto)
 {
   return PresentationBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 Presentation::SetDefaultRequest(PresentationRequest* aRequest)
 {
-  if (IsInPresentedContent()) {
-    return;
-  }
-
   nsCOMPtr<nsIDocument> doc = mWindow ? mWindow->GetExtantDoc() : nullptr;
   if (NS_WARN_IF(!doc)) {
     return;
   }
 
   if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
     return;
   }
 
   mDefaultRequest = aRequest;
 }
 
 already_AddRefed<PresentationRequest>
 Presentation::GetDefaultRequest() const
 {
-  if (IsInPresentedContent()) {
-    return nullptr;
-  }
-
   RefPtr<PresentationRequest> request = mDefaultRequest;
   return request.forget();
 }
 
 already_AddRefed<PresentationReceiver>
 Presentation::GetReceiver()
 {
   // return the same receiver if already created
   if (mReceiver) {
     RefPtr<PresentationReceiver> receiver = mReceiver;
     return receiver.forget();
   }
 
-  if (!IsInPresentedContent()) {
+  if (!HasReceiverSupport() || !IsInPresentedContent()) {
     return nullptr;
   }
 
   mReceiver = PresentationReceiver::Create(mWindow);
   if (NS_WARN_IF(!mReceiver)) {
     MOZ_ASSERT(mReceiver);
     return nullptr;
   }
@@ -169,16 +113,60 @@ Presentation::SetStartSessionUnsettled(b
 
 bool
 Presentation::IsStartSessionUnsettled() const
 {
   return mStartSessionUnsettled;
 }
 
 bool
+Presentation::HasReceiverSupport() const
+{
+  if (!mWindow) {
+    return false;
+  }
+
+  // Grant access to browser receiving pages and their same-origin iframes. (App
+  // pages should be controlled by "presentation" permission in app manifests.)
+  nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell();
+  if (!docShell) {
+    return false;
+  }
+
+  if (!Preferences::GetBool("dom.presentation.testing.simulate-receiver") &&
+      !docShell->GetIsInMozBrowserOrApp()) {
+    return false;
+  }
+
+  nsAutoString presentationURL;
+  nsContentUtils::GetPresentationURL(docShell, presentationURL);
+
+  if (presentationURL.IsEmpty()) {
+    return false;
+  }
+
+  nsCOMPtr<nsIScriptSecurityManager> securityManager =
+    nsContentUtils::GetSecurityManager();
+  if (!securityManager) {
+    return false;
+  }
+
+  nsCOMPtr<nsIURI> presentationURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(presentationURI), presentationURL);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  nsCOMPtr<nsIURI> docURI = mWindow->GetDocumentURI();
+  return NS_SUCCEEDED(securityManager->CheckSameOriginURI(presentationURI,
+                                                          docURI,
+                                                          false));
+}
+
+bool
 Presentation::IsInPresentedContent() const
 {
   if (!mWindow) {
     return false;
   }
 
   nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell();
   MOZ_ASSERT(docShell);
--- a/dom/presentation/Presentation.h
+++ b/dom/presentation/Presentation.h
@@ -25,18 +25,16 @@ class Presentation final : public nsISup
                          , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Presentation)
 
   static already_AddRefed<Presentation> Create(nsPIDOMWindowInner* aWindow);
 
-  static bool HasReceiverSupport(JSContext* aCx, JSObject* aGlobal);
-
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
   }
 
@@ -51,16 +49,18 @@ public:
   void SetStartSessionUnsettled(bool aIsUnsettled);
   bool IsStartSessionUnsettled() const;
 
 private:
   explicit Presentation(nsPIDOMWindowInner* aWindow);
 
   virtual ~Presentation();
 
+  bool HasReceiverSupport() const;
+
   bool IsInPresentedContent() const;
 
   RefPtr<PresentationRequest> mDefaultRequest;
   RefPtr<PresentationReceiver> mReceiver;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   bool mStartSessionUnsettled = false;
 };
 
--- a/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html
+++ b/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html
@@ -58,16 +58,17 @@ function is_same_buffer(recv_data, expec
   return true;
 }
 
 function testConnectionAvailable() {
   return new Promise(function(aResolve, aReject) {
     info('Receiver: --- testConnectionAvailable ---');
     ok(navigator.presentation, "Receiver: navigator.presentation should be available.");
     ok(navigator.presentation.receiver, "Receiver: navigator.presentation.receiver should be available.");
+    is(navigator.presentation.defaultRequest, null, "Receiver: navigator.presentation.defaultRequest should be null.");
 
     navigator.presentation.receiver.connectionList
     .then((aList) => {
       is(aList.connections.length, 1, "Should get one conncetion.");
       connection = aList.connections[0];
       ok(connection.id, "Connection ID should be set: " + connection.id);
       is(connection.state, "connected", "Connection state at receiver side should be connected.");
       aResolve();
--- a/dom/presentation/tests/mochitest/file_presentation_non_receiver.html
+++ b/dom/presentation/tests/mochitest/file_presentation_non_receiver.html
@@ -23,17 +23,17 @@ function info(msg) {
 }
 
 function finish() {
   alert('DONE');
 }
 
 function testConnectionAvailable() {
   return new Promise(function(aResolve, aReject) {
-    ok(!navigator.presentation.receiver, "navigator.presentation.receiver shouldn't be available in non-receiving pages.");
+    is(navigator.presentation.receiver, null, "navigator.presentation.receiver shouldn't be available in non-receiving pages.");
     aResolve();
   });
 }
 
 testConnectionAvailable().
 then(finish);
 
 </script>
--- a/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html
+++ b/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html
@@ -11,16 +11,16 @@
 "use strict";
 
 function ok(a, msg) {
   alert((a ? 'OK ' : 'KO ') + msg);
 }
 
 function testConnectionAvailable() {
   return new Promise(function(aResolve, aReject) {
-    ok(!navigator.presentation.receiver, "navigator.presentation.receiver shouldn't be available in inner iframes with different origins from receiving pages.");
+    is(navigator.presentation.receiver, null, "navigator.presentation.receiver shouldn't be available in inner iframes with different origins from receiving pages.");
     aResolve();
   });
 }
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/file_presentation_receiver.html
+++ b/dom/presentation/tests/mochitest/file_presentation_receiver.html
@@ -50,27 +50,20 @@ function testConnectionAvailable() {
         finish();
         aReject();
       }
     );
     command({ name: 'trigger-incoming-offer' });
   });
 }
 
-function testDefauleRequestIsNull() {
+function testDefaultRequestIsUndefined() {
   return new Promise(function(aResolve, aReject) {
-    navigator.presentation.defaultRequest = new PresentationRequest("http://example.com");
-    if (navigator.presentation.defaultRequest === null) {
-      ok(true, "defaultRequest should be null.");
-      aResolve();
-    }
-    else {
-      ok(false, "defaultRequest should be null.");
-      aReject();
-    }
+    is(navigator.presentation.defaultRequest, undefined, "navigator.presentation.defaultRequest should not be available in receiving UA");
+    aResolve();
   });
 }
 
 function testConnectionAvailableSameOriginInnerIframe() {
   return new Promise(function(aResolve, aReject) {
     var iframe = document.createElement('iframe');
     iframe.setAttribute('src', './file_presentation_receiver_inner_iframe.html');
     document.body.appendChild(iframe);
@@ -129,17 +122,17 @@ function testCloseConnection() {
       aResolve();
     };
 
     connection.close();
   });
 }
 
 testConnectionAvailable().
-then(testDefauleRequestIsNull).
+then(testDefaultRequestIsUndefined).
 then(testConnectionAvailableSameOriginInnerIframe).
 then(testConnectionUnavailableDiffOriginInnerIframe).
 then(testConnectionListSameObject).
 then(testIncomingMessage).
 then(testCloseConnection).
 then(finish);
 
 </script>
--- a/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html
@@ -94,17 +94,17 @@ function setup() {
 
 function testIncomingSessionRequest() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) {
       gScript.removeMessageListener('receiver-launching', launchReceiverHandler);
       info("Trying to launch receiver page.");
 
       ok(navigator.presentation, "navigator.presentation should be available in in-process pages.");
-      ok(!navigator.presentation.receiver, "Non-receiving in-process pages shouldn't get a presentation receiver instance.");
+      is(navigator.presentation.receiver, null, "Non-receiving in-process pages shouldn't get a presentation receiver instance.");
       aResolve();
     });
 
     gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl);
   });
 }
 
 function teardown() {
@@ -123,17 +123,17 @@ function runTests() {
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: "browser", allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.controller.enabled", true],
+                                      ["dom.presentation.controller.enabled", false],
                                       ["dom.presentation.receiver.enabled", true],
                                       ["dom.presentation.session_transport.data_channel.enable", true],
                                       ["dom.mozBrowserFramesEnabled", true]]},
                             runTests);
 });
 
 </script>
 </body>
--- a/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html
@@ -193,17 +193,17 @@ function runTests() {
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'browser', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.controller.enabled", true],
+                                      ["dom.presentation.controller.enabled", false],
                                       ["dom.presentation.receiver.enabled", true],
                                       ["dom.presentation.session_transport.data_channel.enable", true],
                                       ["dom.mozBrowserFramesEnabled", true],
                                       ["dom.ipc.browser_frames.oop_by_default", true],
                                       ["presentation.receiver.loading.timeout", 5000000]]},
                             runTests);
 });
 
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html
@@ -90,17 +90,17 @@ function setup() {
 
 function testIncomingSessionRequest() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) {
       gScript.removeMessageListener('receiver-launching', launchReceiverHandler);
       info("Trying to launch receiver page.");
 
       ok(navigator.presentation, "navigator.presentation should be available in in-process pages.");
-      ok(!navigator.presentation.receiver, "Non-receiving in-process pages shouldn't get a presentation receiver instance.");
+      is(navigator.presentation.receiver, null, "Non-receiving in-process pages shouldn't get a presentation receiver instance.");
       aResolve();
     });
 
     gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl);
   });
 }
 
 function teardown() {
@@ -119,17 +119,17 @@ function runTests() {
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'browser', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.controller.enabled", true],
+                                      ["dom.presentation.controller.enabled", false],
                                       ["dom.presentation.receiver.enabled", true],
                                       ["dom.mozBrowserFramesEnabled", true],
                                       ["dom.presentation.session_transport.data_channel.enable", false]]},
                             runTests);
 });
 
 </script>
 </body>
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html
@@ -159,17 +159,17 @@ function runTests() {
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'browser', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.controller.enabled", true],
+                                      ["dom.presentation.controller.enabled", false],
                                       ["dom.presentation.receiver.enabled", true],
                                       ["dom.presentation.session_transport.data_channel.enable", false],
                                       ["dom.mozBrowserFramesEnabled", true],
                                       ["dom.ipc.browser_frames.oop_by_default", true]]},
                             runTests);
 });
 
 </script>
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
@@ -77,17 +77,17 @@ function testStartConnection() {
     });
 
     gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() {
       gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler);
       info("Data transport channel is initialized.");
       gScript.sendAsyncMessage('trigger-incoming-answer');
     });
 
-    ok(!navigator.presentation.receiver, "Sender shouldn't get a presentation receiver instance.");
+    is(navigator.presentation.receiver, undefined, "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, "connecting", "The initial state should be connecting.");
       connection.onconnect = function() {
@@ -137,14 +137,15 @@ function runTests() {
   then(testStartConnection).
   then(testCloseConnection).
   then(teardown);
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
                                     ["dom.presentation.controller.enabled", true],
+                                    ["dom.presentation.receiver.enabled", false],
                                     ["dom.presentation.session_transport.data_channel.enable", false]]},
                           runTests);
 
 </script>
 </body>
 </html>
--- a/dom/webidl/Presentation.webidl
+++ b/dom/webidl/Presentation.webidl
@@ -1,31 +1,30 @@
 /* -*- 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/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/presentation-api/#interface-presentation
  */
 
 [Pref="dom.presentation.enabled"]
 interface Presentation {
  /*
   * 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).
-  *
-  * The origin of this IDL file is
-  * https://w3c.github.io/presentation-api/#interface-presentation
   */
   [Pref="dom.presentation.controller.enabled"]
   attribute PresentationRequest? defaultRequest;
 
   /*
    * This should be available on the receiving browsing context in order to
    * access the controlling browsing context and communicate with them.
    */
   [SameObject,
-   Pref="dom.presentation.receiver.enabled",
-   Func="Presentation::HasReceiverSupport"]
+   Pref="dom.presentation.receiver.enabled"]
   readonly attribute PresentationReceiver? receiver;
 };