Bug 1314229 - return undefined/null for defaultRequest/receiver if not in corresponding UA or context. r=smaug.
authorShih-Chiang Chien <schien@mozilla.com>
Wed, 02 Nov 2016 17:53:31 +0800
changeset 320776 74f6bc91f74707dd075c3ce78d4839efe9709622
parent 320775 8891610c7c7bebe5fd0237c8df619a699cb12b6c
child 320777 ae743ad1f3260fc23be090535ff86f7097e144bb
push id30904
push userphilringnalda@gmail.com
push dateFri, 04 Nov 2016 02:24:17 +0000
treeherdermozilla-central@38fcc30d818f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1314229
milestone52.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 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;
 };