nsDeviceSensors.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/Hal.h"
  6. #include "mozilla/HalSensor.h"
  7. #include "nsDeviceSensors.h"
  8. #include "nsIDOMEvent.h"
  9. #include "nsIDOMWindow.h"
  10. #include "nsPIDOMWindow.h"
  11. #include "nsIDOMDocument.h"
  12. #include "nsIScriptObjectPrincipal.h"
  13. #include "nsIServiceManager.h"
  14. #include "nsIServiceManager.h"
  15. #include "mozilla/Preferences.h"
  16. #include "mozilla/Attributes.h"
  17. #include "mozilla/Services.h"
  18. #include "nsIPermissionManager.h"
  19. #include "mozilla/dom/DeviceLightEvent.h"
  20. #include "mozilla/dom/DeviceOrientationEvent.h"
  21. #include "mozilla/dom/DeviceProximityEvent.h"
  22. #include "mozilla/dom/UserProximityEvent.h"
  23. #include <cmath>
  24. using namespace mozilla;
  25. using namespace mozilla::dom;
  26. using namespace hal;
  27. #undef near
  28. #define DEFAULT_SENSOR_POLL 100
  29. static const nsTArray<nsIDOMWindow*>::index_type NoIndex =
  30. nsTArray<nsIDOMWindow*>::NoIndex;
  31. class nsDeviceSensorData final : public nsIDeviceSensorData
  32. {
  33. public:
  34. NS_DECL_ISUPPORTS
  35. NS_DECL_NSIDEVICESENSORDATA
  36. nsDeviceSensorData(unsigned long type, double x, double y, double z);
  37. private:
  38. ~nsDeviceSensorData();
  39. protected:
  40. unsigned long mType;
  41. double mX, mY, mZ;
  42. };
  43. nsDeviceSensorData::nsDeviceSensorData(unsigned long type, double x, double y, double z)
  44. : mType(type), mX(x), mY(y), mZ(z)
  45. {
  46. }
  47. NS_INTERFACE_MAP_BEGIN(nsDeviceSensorData)
  48. NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDeviceSensorData)
  49. NS_INTERFACE_MAP_END
  50. NS_IMPL_ADDREF(nsDeviceSensorData)
  51. NS_IMPL_RELEASE(nsDeviceSensorData)
  52. nsDeviceSensorData::~nsDeviceSensorData()
  53. {
  54. }
  55. NS_IMETHODIMP nsDeviceSensorData::GetType(uint32_t *aType)
  56. {
  57. NS_ENSURE_ARG_POINTER(aType);
  58. *aType = mType;
  59. return NS_OK;
  60. }
  61. NS_IMETHODIMP nsDeviceSensorData::GetX(double *aX)
  62. {
  63. NS_ENSURE_ARG_POINTER(aX);
  64. *aX = mX;
  65. return NS_OK;
  66. }
  67. NS_IMETHODIMP nsDeviceSensorData::GetY(double *aY)
  68. {
  69. NS_ENSURE_ARG_POINTER(aY);
  70. *aY = mY;
  71. return NS_OK;
  72. }
  73. NS_IMETHODIMP nsDeviceSensorData::GetZ(double *aZ)
  74. {
  75. NS_ENSURE_ARG_POINTER(aZ);
  76. *aZ = mZ;
  77. return NS_OK;
  78. }
  79. NS_IMPL_ISUPPORTS(nsDeviceSensors, nsIDeviceSensors)
  80. nsDeviceSensors::nsDeviceSensors()
  81. {
  82. mIsUserProximityNear = false;
  83. mLastDOMMotionEventTime = TimeStamp::Now();
  84. mEnabled = Preferences::GetBool("device.sensors.enabled", true);
  85. for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
  86. nsTArray<nsIDOMWindow*> *windows = new nsTArray<nsIDOMWindow*>();
  87. mWindowListeners.AppendElement(windows);
  88. }
  89. mLastDOMMotionEventTime = TimeStamp::Now();
  90. }
  91. nsDeviceSensors::~nsDeviceSensors()
  92. {
  93. for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
  94. if (IsSensorEnabled(i))
  95. UnregisterSensorObserver((SensorType)i, this);
  96. }
  97. for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
  98. delete mWindowListeners[i];
  99. }
  100. }
  101. NS_IMETHODIMP nsDeviceSensors::HasWindowListener(uint32_t aType, nsIDOMWindow *aWindow, bool *aRetVal)
  102. {
  103. if (!mEnabled)
  104. *aRetVal = false;
  105. else
  106. *aRetVal = mWindowListeners[aType]->IndexOf(aWindow) != NoIndex;
  107. return NS_OK;
  108. }
  109. class DeviceSensorTestEvent : public Runnable
  110. {
  111. public:
  112. DeviceSensorTestEvent(nsDeviceSensors* aTarget,
  113. uint32_t aType)
  114. : mTarget(aTarget)
  115. , mType(aType)
  116. {
  117. }
  118. NS_IMETHOD Run() override
  119. {
  120. SensorData sensorData;
  121. sensorData.sensor() = static_cast<SensorType>(mType);
  122. sensorData.timestamp() = PR_Now();
  123. sensorData.values().AppendElement(0.5f);
  124. sensorData.values().AppendElement(0.5f);
  125. sensorData.values().AppendElement(0.5f);
  126. sensorData.values().AppendElement(0.5f);
  127. sensorData.accuracy() = SENSOR_ACCURACY_UNRELIABLE;
  128. mTarget->Notify(sensorData);
  129. return NS_OK;
  130. }
  131. private:
  132. RefPtr<nsDeviceSensors> mTarget;
  133. uint32_t mType;
  134. };
  135. static bool sTestSensorEvents = false;
  136. NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
  137. {
  138. if (!mEnabled)
  139. return NS_OK;
  140. if (mWindowListeners[aType]->IndexOf(aWindow) != NoIndex)
  141. return NS_OK;
  142. if (!IsSensorEnabled(aType)) {
  143. RegisterSensorObserver((SensorType)aType, this);
  144. }
  145. mWindowListeners[aType]->AppendElement(aWindow);
  146. static bool sPrefCacheInitialized = false;
  147. if (!sPrefCacheInitialized) {
  148. sPrefCacheInitialized = true;
  149. Preferences::AddBoolVarCache(&sTestSensorEvents,
  150. "device.sensors.test.events",
  151. false);
  152. }
  153. if (sTestSensorEvents) {
  154. nsCOMPtr<nsIRunnable> event = new DeviceSensorTestEvent(this, aType);
  155. NS_DispatchToCurrentThread(event);
  156. }
  157. return NS_OK;
  158. }
  159. NS_IMETHODIMP nsDeviceSensors::RemoveWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
  160. {
  161. if (mWindowListeners[aType]->IndexOf(aWindow) == NoIndex)
  162. return NS_OK;
  163. mWindowListeners[aType]->RemoveElement(aWindow);
  164. if (mWindowListeners[aType]->Length() == 0)
  165. UnregisterSensorObserver((SensorType)aType, this);
  166. return NS_OK;
  167. }
  168. NS_IMETHODIMP nsDeviceSensors::RemoveWindowAsListener(nsIDOMWindow *aWindow)
  169. {
  170. for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
  171. RemoveWindowListener((SensorType)i, aWindow);
  172. }
  173. return NS_OK;
  174. }
  175. static bool
  176. WindowCannotReceiveSensorEvent (nsPIDOMWindowInner* aWindow)
  177. {
  178. // Check to see if this window is in the background. If
  179. // it is and it does not have the "background-sensors" permission,
  180. // don't send any device motion events to it.
  181. if (!aWindow || !aWindow->IsCurrentInnerWindow()) {
  182. return true;
  183. }
  184. bool disabled = aWindow->GetOuterWindow()->IsBackground() ||
  185. !aWindow->IsTopLevelWindowActive();
  186. if (!disabled) {
  187. nsCOMPtr<nsPIDOMWindowOuter> top = aWindow->GetScriptableTop();
  188. nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
  189. nsCOMPtr<nsIScriptObjectPrincipal> topSop = do_QueryInterface(top);
  190. if (!sop || !topSop) {
  191. return true;
  192. }
  193. nsIPrincipal* principal = sop->GetPrincipal();
  194. nsIPrincipal* topPrincipal = topSop->GetPrincipal();
  195. if (!principal || !topPrincipal) {
  196. return true;
  197. }
  198. disabled = !principal->Subsumes(topPrincipal);
  199. }
  200. if (disabled) {
  201. nsCOMPtr<nsIPermissionManager> permMgr =
  202. services::GetPermissionManager();
  203. NS_ENSURE_TRUE(permMgr, true);
  204. uint32_t permission = nsIPermissionManager::DENY_ACTION;
  205. permMgr->TestPermissionFromWindow(aWindow, "background-sensors", &permission);
  206. return permission != nsIPermissionManager::ALLOW_ACTION;
  207. }
  208. return false;
  209. }
  210. // Holds the device orientation in Euler angle degrees (azimuth, pitch, roll).
  211. struct Orientation
  212. {
  213. enum OrientationReference
  214. {
  215. kRelative = 0,
  216. kAbsolute
  217. };
  218. static Orientation RadToDeg(const Orientation& aOrient)
  219. {
  220. const static double kRadToDeg = 180.0 / M_PI;
  221. return { aOrient.alpha * kRadToDeg,
  222. aOrient.beta * kRadToDeg,
  223. aOrient.gamma * kRadToDeg };
  224. }
  225. double alpha;
  226. double beta;
  227. double gamma;
  228. };
  229. static Orientation
  230. RotationVectorToOrientation(double aX, double aY, double aZ, double aW)
  231. {
  232. static const double kFuzzyOne = 1.0 - 1e-6;
  233. static const double kCircleRad = 2.0 * M_PI;
  234. Orientation orient = { 2.0 * std::atan2(aY, aW),
  235. M_PI_2,
  236. 0.0 };
  237. const double sqX = aX * aX;
  238. const double sqY = aY * aY;
  239. const double sqZ = aZ * aZ;
  240. const double sqW = aW * aW;
  241. const double unitLength = sqX + sqY + sqZ + sqW;
  242. const double xwyz = 2.0 * (aX * aW + aY * aZ) / unitLength;
  243. if (xwyz < -kFuzzyOne) {
  244. orient.alpha *= -1.0;
  245. orient.beta *= -1.0;
  246. } else if (xwyz <= kFuzzyOne) {
  247. const double gammaX = -sqX - sqY + sqZ + sqW;
  248. const double gammaY = 2.0 * (aY * aW - aX * aZ);
  249. const double alphaX = -sqX + sqY - sqZ + sqW;
  250. const double alphaY = 2.0 * (aZ * aW - aX * aY);
  251. const double fac = gammaX > 0 ? 1.0 : -1.0;
  252. orient.alpha = std::fmod(kCircleRad + std::atan2(fac * alphaY, fac * alphaX),
  253. kCircleRad);
  254. orient.beta = fac * std::asin(xwyz);
  255. orient.gamma = std::atan2(fac * gammaY, fac * gammaX);
  256. if (fac < 0.0) {
  257. orient.beta = fmod(M_PI + orient.beta, M_PI);
  258. }
  259. }
  260. return Orientation::RadToDeg(orient);
  261. }
  262. void
  263. nsDeviceSensors::Notify(const mozilla::hal::SensorData& aSensorData)
  264. {
  265. uint32_t type = aSensorData.sensor();
  266. const InfallibleTArray<float>& values = aSensorData.values();
  267. size_t len = values.Length();
  268. double x = len > 0 ? values[0] : 0.0;
  269. double y = len > 1 ? values[1] : 0.0;
  270. double z = len > 2 ? values[2] : 0.0;
  271. double w = len > 3 ? values[3] : 0.0;
  272. PRTime timestamp = aSensorData.timestamp();
  273. nsCOMArray<nsIDOMWindow> windowListeners;
  274. for (uint32_t i = 0; i < mWindowListeners[type]->Length(); i++) {
  275. windowListeners.AppendObject(mWindowListeners[type]->SafeElementAt(i));
  276. }
  277. for (uint32_t i = windowListeners.Count(); i > 0 ; ) {
  278. --i;
  279. nsCOMPtr<nsPIDOMWindowInner> pwindow = do_QueryInterface(windowListeners[i]);
  280. if (WindowCannotReceiveSensorEvent(pwindow)) {
  281. continue;
  282. }
  283. if (nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(pwindow->GetDoc())) {
  284. nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(windowListeners[i]);
  285. if (type == nsIDeviceSensorData::TYPE_ACCELERATION ||
  286. type == nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION ||
  287. type == nsIDeviceSensorData::TYPE_GYROSCOPE) {
  288. FireDOMMotionEvent(domDoc, target, type, timestamp, x, y, z);
  289. } else if (type == nsIDeviceSensorData::TYPE_ORIENTATION) {
  290. FireDOMOrientationEvent(target, x, y, z, Orientation::kAbsolute);
  291. } else if (type == nsIDeviceSensorData::TYPE_ROTATION_VECTOR) {
  292. const Orientation orient = RotationVectorToOrientation(x, y, z, w);
  293. FireDOMOrientationEvent(target, orient.alpha, orient.beta, orient.gamma,
  294. Orientation::kAbsolute);
  295. } else if (type == nsIDeviceSensorData::TYPE_GAME_ROTATION_VECTOR) {
  296. const Orientation orient = RotationVectorToOrientation(x, y, z, w);
  297. FireDOMOrientationEvent(target, orient.alpha, orient.beta, orient.gamma,
  298. Orientation::kRelative);
  299. } else if (type == nsIDeviceSensorData::TYPE_PROXIMITY) {
  300. FireDOMProximityEvent(target, x, y, z);
  301. } else if (type == nsIDeviceSensorData::TYPE_LIGHT) {
  302. FireDOMLightEvent(target, x);
  303. }
  304. }
  305. }
  306. }
  307. void
  308. nsDeviceSensors::FireDOMLightEvent(mozilla::dom::EventTarget* aTarget,
  309. double aValue)
  310. {
  311. DeviceLightEventInit init;
  312. init.mBubbles = true;
  313. init.mCancelable = false;
  314. init.mValue = round(aValue);
  315. RefPtr<DeviceLightEvent> event =
  316. DeviceLightEvent::Constructor(aTarget, NS_LITERAL_STRING("devicelight"), init);
  317. event->SetTrusted(true);
  318. bool defaultActionEnabled;
  319. aTarget->DispatchEvent(event, &defaultActionEnabled);
  320. }
  321. void
  322. nsDeviceSensors::FireDOMProximityEvent(mozilla::dom::EventTarget* aTarget,
  323. double aValue,
  324. double aMin,
  325. double aMax)
  326. {
  327. DeviceProximityEventInit init;
  328. init.mBubbles = true;
  329. init.mCancelable = false;
  330. init.mValue = aValue;
  331. init.mMin = aMin;
  332. init.mMax = aMax;
  333. RefPtr<DeviceProximityEvent> event =
  334. DeviceProximityEvent::Constructor(aTarget,
  335. NS_LITERAL_STRING("deviceproximity"),
  336. init);
  337. event->SetTrusted(true);
  338. bool defaultActionEnabled;
  339. aTarget->DispatchEvent(event, &defaultActionEnabled);
  340. // Some proximity sensors only support a binary near or
  341. // far measurement. In this case, the sensor should report
  342. // its maximum range value in the far state and a lesser
  343. // value in the near state.
  344. bool near = (aValue < aMax);
  345. if (mIsUserProximityNear != near) {
  346. mIsUserProximityNear = near;
  347. FireDOMUserProximityEvent(aTarget, mIsUserProximityNear);
  348. }
  349. }
  350. void
  351. nsDeviceSensors::FireDOMUserProximityEvent(mozilla::dom::EventTarget* aTarget,
  352. bool aNear)
  353. {
  354. UserProximityEventInit init;
  355. init.mBubbles = true;
  356. init.mCancelable = false;
  357. init.mNear = aNear;
  358. RefPtr<UserProximityEvent> event =
  359. UserProximityEvent::Constructor(aTarget,
  360. NS_LITERAL_STRING("userproximity"),
  361. init);
  362. event->SetTrusted(true);
  363. bool defaultActionEnabled;
  364. aTarget->DispatchEvent(event, &defaultActionEnabled);
  365. }
  366. void
  367. nsDeviceSensors::FireDOMOrientationEvent(EventTarget* aTarget,
  368. double aAlpha,
  369. double aBeta,
  370. double aGamma,
  371. bool aIsAbsolute)
  372. {
  373. DeviceOrientationEventInit init;
  374. init.mBubbles = true;
  375. init.mCancelable = false;
  376. init.mAlpha.SetValue(aAlpha);
  377. init.mBeta.SetValue(aBeta);
  378. init.mGamma.SetValue(aGamma);
  379. init.mAbsolute = aIsAbsolute;
  380. auto Dispatch = [&](EventTarget* aEventTarget, const nsAString& aType)
  381. {
  382. RefPtr<DeviceOrientationEvent> event =
  383. DeviceOrientationEvent::Constructor(aEventTarget, aType, init);
  384. event->SetTrusted(true);
  385. bool dummy;
  386. aEventTarget->DispatchEvent(event, &dummy);
  387. };
  388. Dispatch(aTarget, aIsAbsolute ? NS_LITERAL_STRING("absolutedeviceorientation") :
  389. NS_LITERAL_STRING("deviceorientation"));
  390. // This is used to determine whether relative events have been dispatched
  391. // during the current session, in which case we don't dispatch the additional
  392. // compatibility events.
  393. static bool sIsDispatchingRelativeEvents = false;
  394. sIsDispatchingRelativeEvents = sIsDispatchingRelativeEvents || !aIsAbsolute;
  395. // Android devices with SENSOR_GAME_ROTATION_VECTOR support dispatch
  396. // relative events for "deviceorientation" by default, while other platforms
  397. // and devices without such support dispatch absolute events by default.
  398. if (aIsAbsolute && !sIsDispatchingRelativeEvents) {
  399. // For absolute events on devices without support for relative events,
  400. // we need to additionally dispatch type "deviceorientation" to keep
  401. // backwards-compatibility.
  402. Dispatch(aTarget, NS_LITERAL_STRING("deviceorientation"));
  403. }
  404. }
  405. void
  406. nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument *domdoc,
  407. EventTarget* target,
  408. uint32_t type,
  409. PRTime timestamp,
  410. double x,
  411. double y,
  412. double z)
  413. {
  414. // Attempt to coalesce events
  415. TimeDuration sensorPollDuration =
  416. TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL);
  417. bool fireEvent =
  418. (TimeStamp::Now() > mLastDOMMotionEventTime + sensorPollDuration) ||
  419. sTestSensorEvents;
  420. switch (type) {
  421. case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION:
  422. if (!mLastAcceleration) {
  423. mLastAcceleration.emplace();
  424. }
  425. mLastAcceleration->mX.SetValue(x);
  426. mLastAcceleration->mY.SetValue(y);
  427. mLastAcceleration->mZ.SetValue(z);
  428. break;
  429. case nsIDeviceSensorData::TYPE_ACCELERATION:
  430. if (!mLastAccelerationIncludingGravity) {
  431. mLastAccelerationIncludingGravity.emplace();
  432. }
  433. mLastAccelerationIncludingGravity->mX.SetValue(x);
  434. mLastAccelerationIncludingGravity->mY.SetValue(y);
  435. mLastAccelerationIncludingGravity->mZ.SetValue(z);
  436. break;
  437. case nsIDeviceSensorData::TYPE_GYROSCOPE:
  438. if (!mLastRotationRate) {
  439. mLastRotationRate.emplace();
  440. }
  441. mLastRotationRate->mAlpha.SetValue(x);
  442. mLastRotationRate->mBeta.SetValue(y);
  443. mLastRotationRate->mGamma.SetValue(z);
  444. break;
  445. }
  446. if (fireEvent) {
  447. if (!mLastAcceleration) {
  448. mLastAcceleration.emplace();
  449. }
  450. if (!mLastAccelerationIncludingGravity) {
  451. mLastAccelerationIncludingGravity.emplace();
  452. }
  453. if (!mLastRotationRate) {
  454. mLastRotationRate.emplace();
  455. }
  456. } else if (!mLastAcceleration ||
  457. !mLastAccelerationIncludingGravity ||
  458. !mLastRotationRate) {
  459. return;
  460. }
  461. nsCOMPtr<nsIDOMEvent> event;
  462. domdoc->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event));
  463. DeviceMotionEvent* me = static_cast<DeviceMotionEvent*>(event.get());
  464. me->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"),
  465. true,
  466. false,
  467. *mLastAcceleration,
  468. *mLastAccelerationIncludingGravity,
  469. *mLastRotationRate,
  470. Nullable<double>(DEFAULT_SENSOR_POLL),
  471. Nullable<uint64_t>(timestamp));
  472. event->SetTrusted(true);
  473. bool defaultActionEnabled = true;
  474. target->DispatchEvent(event, &defaultActionEnabled);
  475. mLastRotationRate.reset();
  476. mLastAccelerationIncludingGravity.reset();
  477. mLastAcceleration.reset();
  478. mLastDOMMotionEventTime = TimeStamp::Now();
  479. }