Bug 1197901, ensure sensor events dispatching follows the becoming spec change, r=bz
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Wed, 24 Feb 2016 19:43:07 +0200
changeset 285350 78d2fbf52588
parent 285349 80baf2babce0
child 285351 b9e59028b304
push id72334
push useropettay@mozilla.com
push date2016-02-24 18:09 +0000
treeherdermozilla-inbound@78d2fbf52588 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1197901
milestone47.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 1197901, ensure sensor events dispatching follows the becoming spec change, r=bz
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/system/nsDeviceSensors.cpp
dom/system/tests/file_bug1197901.html
dom/system/tests/mochitest.ini
dom/system/tests/test_bug1197901.html
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9522,16 +9522,34 @@ NotifyDocumentTree(nsIDocument* aDocumen
 
 void
 nsGlobalWindow::SetActive(bool aActive)
 {
   nsPIDOMWindow::SetActive(aActive);
   NotifyDocumentTree(mDoc, nullptr);
 }
 
+bool
+nsGlobalWindow::IsTopLevelWindowActive()
+{
+   nsCOMPtr<nsIDocShellTreeItem> treeItem(GetDocShell());
+   if (!treeItem) {
+     return false;
+   }
+
+   nsCOMPtr<nsIDocShellTreeItem> rootItem;
+   treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
+   if (!rootItem) {
+     return false;
+   }
+
+   nsCOMPtr<nsPIDOMWindowOuter> domWindow = rootItem->GetWindow();
+   return domWindow && domWindow->IsActive();
+}
+
 void nsGlobalWindow::SetIsBackground(bool aIsBackground)
 {
   MOZ_ASSERT(IsOuterWindow());
 
   bool resetTimers = (!aIsBackground && AsOuter()->IsBackground());
   nsPIDOMWindow::SetIsBackground(aIsBackground);
   if (resetTimers) {
     ResetTimersForNonBackgroundWindow();
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -417,16 +417,17 @@ public:
   }
 
   // nsPIDOMWindow
   virtual nsPIDOMWindowOuter* GetPrivateRoot() override;
 
   // Outer windows only.
   virtual void ActivateOrDeactivate(bool aActivate) override;
   virtual void SetActive(bool aActive) override;
+  virtual bool IsTopLevelWindowActive() override;
   virtual void SetIsBackground(bool aIsBackground) override;
   virtual void SetChromeEventHandler(mozilla::dom::EventTarget* aChromeEventHandler) override;
 
   // Outer windows only.
   virtual void SetInitialPrincipalToSubject() override;
 
   virtual PopupControlState PushPopupControlState(PopupControlState state, bool aForce) const override;
   virtual void PopPopupControlState(PopupControlState state) const override;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -117,16 +117,18 @@ public:
   virtual nsPIDOMWindowOuter* GetScriptableTop() = 0;
   virtual nsPIDOMWindowOuter* GetScriptableParent() = 0;
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() = 0;
 
   // Inner windows only.
   virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0;
   virtual nsresult UnregisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0;
 
+  virtual bool IsTopLevelWindowActive() = 0;
+
   // Outer windows only.
   virtual void SetActive(bool aActive)
   {
     MOZ_ASSERT(IsOuterWindow());
     mIsActive = aActive;
   }
 
   virtual void SetIsBackground(bool aIsBackground)
--- a/dom/system/nsDeviceSensors.cpp
+++ b/dom/system/nsDeviceSensors.cpp
@@ -9,16 +9,17 @@
 
 #include "nsDeviceSensors.h"
 
 #include "nsAutoPtr.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMDocument.h"
+#include "nsIScriptObjectPrincipal.h"
 #include "nsIServiceManager.h"
 #include "nsIServiceManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Services.h"
 #include "nsIPermissionManager.h"
 #include "mozilla/dom/DeviceLightEvent.h"
 #include "mozilla/dom/DeviceOrientationEvent.h"
@@ -131,29 +132,74 @@ NS_IMETHODIMP nsDeviceSensors::HasWindow
   if (!mEnabled)
     *aRetVal = false;
   else
     *aRetVal = mWindowListeners[aType]->IndexOf(aWindow) != NoIndex;
 
   return NS_OK;
 }
 
+class DeviceSensorTestEvent : public nsRunnable
+{
+public:
+  DeviceSensorTestEvent(nsDeviceSensors* aTarget,
+                        uint32_t aType)
+  : mTarget(aTarget)
+  , mType(aType)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    SensorData sensorData;
+    sensorData.sensor() = static_cast<SensorType>(mType);
+    sensorData.timestamp() = PR_Now();
+    sensorData.values().AppendElement(0.5f);
+    sensorData.values().AppendElement(0.5f);
+    sensorData.values().AppendElement(0.5f);
+    sensorData.values().AppendElement(0.5f);
+    sensorData.accuracy() = SENSOR_ACCURACY_UNRELIABLE;
+    mTarget->Notify(sensorData);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<nsDeviceSensors> mTarget;
+  uint32_t mType;
+};
+
+static bool sTestSensorEvents = false;
+
 NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
 {
   if (!mEnabled)
     return NS_OK;
 
   if (mWindowListeners[aType]->IndexOf(aWindow) != NoIndex)
     return NS_OK;
 
   if (!IsSensorEnabled(aType)) {
     RegisterSensorObserver((SensorType)aType, this);
   }
 
   mWindowListeners[aType]->AppendElement(aWindow);
+
+  static bool sPrefCacheInitialized = false;
+  if (!sPrefCacheInitialized) {
+    sPrefCacheInitialized = true;
+    Preferences::AddBoolVarCache(&sTestSensorEvents,
+                                 "device.sensors.test.events",
+                                 false);
+  }
+
+  if (sTestSensorEvents) {
+    nsCOMPtr<nsIRunnable> event = new DeviceSensorTestEvent(this, aType);
+    NS_DispatchToCurrentThread(event);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP nsDeviceSensors::RemoveWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
 {
   if (mWindowListeners[aType]->IndexOf(aWindow) == NoIndex)
     return NS_OK;
 
@@ -178,20 +224,39 @@ WindowCannotReceiveSensorEvent (nsPIDOMW
 {
   // Check to see if this window is in the background.  If
   // it is and it does not have the "background-sensors" permission,
   // don't send any device motion events to it.
   if (!aWindow || !aWindow->IsCurrentInnerWindow()) {
     return true;
   }
 
-  if (aWindow->GetOuterWindow()->IsBackground()) {
+  bool disabled = aWindow->GetOuterWindow()->IsBackground() ||
+                  !aWindow->IsTopLevelWindowActive();
+  if (!disabled) {
+    nsCOMPtr<nsPIDOMWindowOuter> top = aWindow->GetScriptableTop();
+    nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
+    nsCOMPtr<nsIScriptObjectPrincipal> topSop = do_QueryInterface(top);
+    if (!sop || !topSop) {
+      return true;
+    }
+
+    nsIPrincipal* principal = sop->GetPrincipal();
+    nsIPrincipal* topPrincipal = topSop->GetPrincipal();
+    if (!principal || !topPrincipal) {
+      return true;
+    }
+
+    disabled = !principal->Subsumes(topPrincipal);
+  }
+
+  if (disabled) {
     nsCOMPtr<nsIPermissionManager> permMgr =
       services::GetPermissionManager();
-    NS_ENSURE_TRUE(permMgr, false);
+    NS_ENSURE_TRUE(permMgr, true);
     uint32_t permission = nsIPermissionManager::DENY_ACTION;
     permMgr->TestPermissionFromWindow(aWindow, "background-sensors", &permission);
     return permission != nsIPermissionManager::ALLOW_ACTION;
   }
 
   return false;
 }
 
@@ -423,17 +488,21 @@ void
 nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument *domdoc,
                                     EventTarget* target,
                                     uint32_t type,
                                     double x,
                                     double y,
                                     double z)
 {
   // Attempt to coalesce events
-  bool fireEvent = TimeStamp::Now() > mLastDOMMotionEventTime + TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL);
+  TimeDuration sensorPollDuration =
+    TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL);
+  bool fireEvent =
+    (TimeStamp::Now() > mLastDOMMotionEventTime + sensorPollDuration) ||
+    sTestSensorEvents;
 
   switch (type) {
   case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION:
     if (!mLastAcceleration) {
       mLastAcceleration.emplace();
     }
     mLastAcceleration->mX.SetValue(x);
     mLastAcceleration->mY.SetValue(y);
new file mode 100644
--- /dev/null
+++ b/dom/system/tests/file_bug1197901.html
@@ -0,0 +1,16 @@
+<pre>Sensor events testing</pre>
+<script>
+
+window.onmessage = function (event) {
+  if (event.data.command == "addEventListener") {
+    window.addEventListener(
+      "devicemotion", function() {
+          event.source.postMessage({ result: event.data.expected,
+                                     message: event.data.message },
+                                   "*");
+        }
+      );
+  }
+}
+
+</script>
--- a/dom/system/tests/mochitest.ini
+++ b/dom/system/tests/mochitest.ini
@@ -1,6 +1,9 @@
 [DEFAULT]
-skip-if = buildapp != 'b2g'
 support-files =
   preload-SystemUpdateManager-jsm.js
+  file_bug1197901.html
 
 [test_system_update_enabled.html]
+skip-if = buildapp != 'b2g'
+[test_bug1197901.html]
+skip-if = buildapp == 'mulet'
new file mode 100644
--- /dev/null
+++ b/dom/system/tests/test_bug1197901.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1197901
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1197901</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1197901 **/
+  SimpleTest.requestFlakyTimeout("requestFlakyTimeout is silly");
+  SimpleTest.waitForExplicitFinish();
+  window.onload = function() {
+    SimpleTest.waitForFocus(function() {
+      SpecialPowers.pushPrefEnv({"set": [['device.sensors.test.events', true]]},
+                                doTest); 
+      }, window);
+  }
+
+  function doTest() {
+    window.onmessage = function(event) {
+      ok(event.data.result, event.data.message);
+    }
+
+    // Only same-origin iframe should get the events.
+    var xo = document.getElementById("cross-origin");
+    xo.contentWindow.postMessage(
+      { command: "addEventListener",
+        expected: false,
+        message: "Cross-origin iframe shouldn't get the sensor events."},
+      "*");
+
+    var so = document.getElementById("same-origin");
+    so.contentWindow.postMessage(
+      { command: "addEventListener",
+        expected: true,
+        message: "Same-origin iframe should get the sensor events." },
+      "*");
+
+    // We need a timeout here to check that something does not happen.
+    setTimeout(function() {
+      so.parentNode.removeChild(so);
+      xo.parentNode.removeChild(xo);
+      doWindowTest();
+    }, 500);
+  }
+
+  function doWindowTest() {
+    var win = window.open("file_bug1197901.html", "w1", "height=100,width=100");
+    win.onload = function() {
+      win.focus();
+      SimpleTest.waitForFocus(function() {
+        var win2 = window.open("file_bug1197901.html", "w2", "height=100,width=100,left=100");
+        win2.onload = function() {
+          win2.focus();
+          SimpleTest.waitForFocus(function() {
+            // Only focused window should get the events.
+            win.postMessage(
+              { command: "addEventListener",
+                expected: false,
+                message: "Only focused window should get the sensor events." },
+              "*");
+            win2.postMessage(
+              { command: "addEventListener",
+                expected: true,
+                message: "Focused window should get the sensor events." },
+              "*");
+            setTimeout(function() {
+              window.onmessage = null;
+              win.close();
+              win2.close();
+              SimpleTest.finish();
+            }, 500);
+          }, win2);
+        }
+      }, win);
+    }
+  }
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe src="file_bug1197901.html" id="same-origin"></iframe>
+<iframe src="http://example.com/tests/dom/system/tests/file_bug1197901.html" id="cross-origin"></iframe>
+</body>
+</html>