Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / system / nsDeviceSensors.cpp
blob5d4a1be92b40914435a2d7adfd6204a3b879d90e
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "mozilla/Hal.h"
6 #include "mozilla/HalSensor.h"
8 #include "nsDeviceSensors.h"
10 #include "nsAutoPtr.h"
11 #include "nsIDOMEvent.h"
12 #include "nsIDOMWindow.h"
13 #include "nsPIDOMWindow.h"
14 #include "nsIDOMDocument.h"
15 #include "nsIServiceManager.h"
16 #include "nsIServiceManager.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/Attributes.h"
19 #include "mozilla/Services.h"
20 #include "nsIPermissionManager.h"
21 #include "mozilla/dom/DeviceLightEvent.h"
22 #include "mozilla/dom/DeviceOrientationEvent.h"
23 #include "mozilla/dom/DeviceProximityEvent.h"
24 #include "mozilla/dom/UserProximityEvent.h"
26 using namespace mozilla;
27 using namespace mozilla::dom;
28 using namespace hal;
30 #undef near
32 // also see sDefaultSensorHint in mobile/android/base/GeckoAppShell.java
33 #define DEFAULT_SENSOR_POLL 100
35 static const nsTArray<nsIDOMWindow*>::index_type NoIndex =
36 nsTArray<nsIDOMWindow*>::NoIndex;
38 class nsDeviceSensorData MOZ_FINAL : public nsIDeviceSensorData
40 public:
41 NS_DECL_ISUPPORTS
42 NS_DECL_NSIDEVICESENSORDATA
44 nsDeviceSensorData(unsigned long type, double x, double y, double z);
46 private:
47 ~nsDeviceSensorData();
49 protected:
50 unsigned long mType;
51 double mX, mY, mZ;
54 nsDeviceSensorData::nsDeviceSensorData(unsigned long type, double x, double y, double z)
55 : mType(type), mX(x), mY(y), mZ(z)
59 NS_INTERFACE_MAP_BEGIN(nsDeviceSensorData)
60 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDeviceSensorData)
61 NS_INTERFACE_MAP_END
63 NS_IMPL_ADDREF(nsDeviceSensorData)
64 NS_IMPL_RELEASE(nsDeviceSensorData)
66 nsDeviceSensorData::~nsDeviceSensorData()
70 NS_IMETHODIMP nsDeviceSensorData::GetType(uint32_t *aType)
72 NS_ENSURE_ARG_POINTER(aType);
73 *aType = mType;
74 return NS_OK;
77 NS_IMETHODIMP nsDeviceSensorData::GetX(double *aX)
79 NS_ENSURE_ARG_POINTER(aX);
80 *aX = mX;
81 return NS_OK;
84 NS_IMETHODIMP nsDeviceSensorData::GetY(double *aY)
86 NS_ENSURE_ARG_POINTER(aY);
87 *aY = mY;
88 return NS_OK;
91 NS_IMETHODIMP nsDeviceSensorData::GetZ(double *aZ)
93 NS_ENSURE_ARG_POINTER(aZ);
94 *aZ = mZ;
95 return NS_OK;
98 NS_IMPL_ISUPPORTS(nsDeviceSensors, nsIDeviceSensors)
100 nsDeviceSensors::nsDeviceSensors()
102 mIsUserProximityNear = false;
103 mLastDOMMotionEventTime = TimeStamp::Now();
104 mEnabled = Preferences::GetBool("device.sensors.enabled", true);
106 for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
107 nsTArray<nsIDOMWindow*> *windows = new nsTArray<nsIDOMWindow*>();
108 mWindowListeners.AppendElement(windows);
111 mLastDOMMotionEventTime = TimeStamp::Now();
114 nsDeviceSensors::~nsDeviceSensors()
116 for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
117 if (IsSensorEnabled(i))
118 UnregisterSensorObserver((SensorType)i, this);
121 for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
122 delete mWindowListeners[i];
126 NS_IMETHODIMP nsDeviceSensors::HasWindowListener(uint32_t aType, nsIDOMWindow *aWindow, bool *aRetVal)
128 if (!mEnabled)
129 *aRetVal = false;
130 else
131 *aRetVal = mWindowListeners[aType]->IndexOf(aWindow) != NoIndex;
133 return NS_OK;
136 NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
138 if (!mEnabled)
139 return NS_OK;
141 if (mWindowListeners[aType]->IndexOf(aWindow) != NoIndex)
142 return NS_OK;
144 if (!IsSensorEnabled(aType)) {
145 RegisterSensorObserver((SensorType)aType, this);
148 mWindowListeners[aType]->AppendElement(aWindow);
149 return NS_OK;
152 NS_IMETHODIMP nsDeviceSensors::RemoveWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
154 if (mWindowListeners[aType]->IndexOf(aWindow) == NoIndex)
155 return NS_OK;
157 mWindowListeners[aType]->RemoveElement(aWindow);
159 if (mWindowListeners[aType]->Length() == 0)
160 UnregisterSensorObserver((SensorType)aType, this);
162 return NS_OK;
165 NS_IMETHODIMP nsDeviceSensors::RemoveWindowAsListener(nsIDOMWindow *aWindow)
167 for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
168 RemoveWindowListener((SensorType)i, aWindow);
170 return NS_OK;
173 static bool
174 WindowCannotReceiveSensorEvent (nsPIDOMWindow* aWindow)
176 // Check to see if this window is in the background. If
177 // it is and it does not have the "background-sensors" permission,
178 // don't send any device motion events to it.
179 if (!aWindow || !aWindow->IsCurrentInnerWindow()) {
180 return true;
183 if (aWindow->GetOuterWindow()->IsBackground()) {
184 nsCOMPtr<nsIPermissionManager> permMgr =
185 services::GetPermissionManager();
186 NS_ENSURE_TRUE(permMgr, false);
187 uint32_t permission = nsIPermissionManager::DENY_ACTION;
188 permMgr->TestPermissionFromWindow(aWindow, "background-sensors", &permission);
189 return permission != nsIPermissionManager::ALLOW_ACTION;
192 return false;
195 void
196 nsDeviceSensors::Notify(const mozilla::hal::SensorData& aSensorData)
198 uint32_t type = aSensorData.sensor();
200 const InfallibleTArray<float>& values = aSensorData.values();
201 size_t len = values.Length();
202 double x = len > 0 ? values[0] : 0.0;
203 double y = len > 1 ? values[1] : 0.0;
204 double z = len > 2 ? values[2] : 0.0;
206 nsCOMArray<nsIDOMWindow> windowListeners;
207 for (uint32_t i = 0; i < mWindowListeners[type]->Length(); i++) {
208 windowListeners.AppendObject(mWindowListeners[type]->SafeElementAt(i));
211 for (uint32_t i = windowListeners.Count(); i > 0 ; ) {
212 --i;
214 nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(windowListeners[i]);
215 if (WindowCannotReceiveSensorEvent(pwindow)) {
216 continue;
219 nsCOMPtr<nsIDOMDocument> domdoc;
220 windowListeners[i]->GetDocument(getter_AddRefs(domdoc));
222 if (domdoc) {
223 nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(windowListeners[i]);
224 if (type == nsIDeviceSensorData::TYPE_ACCELERATION ||
225 type == nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION ||
226 type == nsIDeviceSensorData::TYPE_GYROSCOPE)
227 FireDOMMotionEvent(domdoc, target, type, x, y, z);
228 else if (type == nsIDeviceSensorData::TYPE_ORIENTATION)
229 FireDOMOrientationEvent(target, x, y, z);
230 else if (type == nsIDeviceSensorData::TYPE_PROXIMITY)
231 FireDOMProximityEvent(target, x, y, z);
232 else if (type == nsIDeviceSensorData::TYPE_LIGHT)
233 FireDOMLightEvent(target, x);
239 void
240 nsDeviceSensors::FireDOMLightEvent(mozilla::dom::EventTarget* aTarget,
241 double aValue)
243 DeviceLightEventInit init;
244 init.mBubbles = true;
245 init.mCancelable = false;
246 init.mValue = aValue;
247 nsRefPtr<DeviceLightEvent> event =
248 DeviceLightEvent::Constructor(aTarget, NS_LITERAL_STRING("devicelight"), init);
250 event->SetTrusted(true);
252 bool defaultActionEnabled;
253 aTarget->DispatchEvent(event, &defaultActionEnabled);
256 void
257 nsDeviceSensors::FireDOMProximityEvent(mozilla::dom::EventTarget* aTarget,
258 double aValue,
259 double aMin,
260 double aMax)
262 DeviceProximityEventInit init;
263 init.mBubbles = true;
264 init.mCancelable = false;
265 init.mValue = aValue;
266 init.mMin = aMin;
267 init.mMax = aMax;
268 nsRefPtr<DeviceProximityEvent> event =
269 DeviceProximityEvent::Constructor(aTarget,
270 NS_LITERAL_STRING("deviceproximity"),
271 init);
272 event->SetTrusted(true);
274 bool defaultActionEnabled;
275 aTarget->DispatchEvent(event, &defaultActionEnabled);
277 // Some proximity sensors only support a binary near or
278 // far measurement. In this case, the sensor should report
279 // its maximum range value in the far state and a lesser
280 // value in the near state.
282 bool near = (aValue < aMax);
283 if (mIsUserProximityNear != near) {
284 mIsUserProximityNear = near;
285 FireDOMUserProximityEvent(aTarget, mIsUserProximityNear);
289 void
290 nsDeviceSensors::FireDOMUserProximityEvent(mozilla::dom::EventTarget* aTarget,
291 bool aNear)
293 UserProximityEventInit init;
294 init.mBubbles = true;
295 init.mCancelable = false;
296 init.mNear = aNear;
297 nsRefPtr<UserProximityEvent> event =
298 UserProximityEvent::Constructor(aTarget,
299 NS_LITERAL_STRING("userproximity"),
300 init);
302 event->SetTrusted(true);
304 bool defaultActionEnabled;
305 aTarget->DispatchEvent(event, &defaultActionEnabled);
308 void
309 nsDeviceSensors::FireDOMOrientationEvent(EventTarget* aTarget,
310 double aAlpha,
311 double aBeta,
312 double aGamma)
314 DeviceOrientationEventInit init;
315 init.mBubbles = true;
316 init.mCancelable = false;
317 init.mAlpha.SetValue(aAlpha);
318 init.mBeta.SetValue(aBeta);
319 init.mGamma.SetValue(aGamma);
320 init.mAbsolute = true;
322 nsRefPtr<DeviceOrientationEvent> event =
323 DeviceOrientationEvent::Constructor(aTarget,
324 NS_LITERAL_STRING("deviceorientation"),
325 init);
326 event->SetTrusted(true);
328 bool dummy;
329 aTarget->DispatchEvent(event, &dummy);
333 void
334 nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument *domdoc,
335 EventTarget* target,
336 uint32_t type,
337 double x,
338 double y,
339 double z)
341 // Attempt to coalesce events
342 bool fireEvent = TimeStamp::Now() > mLastDOMMotionEventTime + TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL);
344 switch (type) {
345 case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION:
346 if (!mLastAcceleration) {
347 mLastAcceleration.emplace();
349 mLastAcceleration->mX.SetValue(x);
350 mLastAcceleration->mY.SetValue(y);
351 mLastAcceleration->mZ.SetValue(z);
352 break;
353 case nsIDeviceSensorData::TYPE_ACCELERATION:
354 if (!mLastAccelerationIncluduingGravity) {
355 mLastAccelerationIncluduingGravity.emplace();
357 mLastAccelerationIncluduingGravity->mX.SetValue(x);
358 mLastAccelerationIncluduingGravity->mY.SetValue(y);
359 mLastAccelerationIncluduingGravity->mZ.SetValue(z);
360 break;
361 case nsIDeviceSensorData::TYPE_GYROSCOPE:
362 if (!mLastRotationRate) {
363 mLastRotationRate.emplace();
365 mLastRotationRate->mAlpha.SetValue(x);
366 mLastRotationRate->mBeta.SetValue(y);
367 mLastRotationRate->mGamma.SetValue(z);
368 break;
371 if (fireEvent) {
372 if (!mLastAcceleration) {
373 mLastAcceleration.emplace();
375 if (!mLastAccelerationIncluduingGravity) {
376 mLastAccelerationIncluduingGravity.emplace();
378 if (!mLastRotationRate) {
379 mLastRotationRate.emplace();
381 } else if (!mLastAcceleration ||
382 !mLastAccelerationIncluduingGravity ||
383 !mLastRotationRate) {
384 return;
387 nsCOMPtr<nsIDOMEvent> event;
388 domdoc->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event));
390 DeviceMotionEvent* me = static_cast<DeviceMotionEvent*>(event.get());
392 ErrorResult rv;
393 me->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"),
394 true,
395 false,
396 *mLastAcceleration,
397 *mLastAccelerationIncluduingGravity,
398 *mLastRotationRate,
399 Nullable<double>(DEFAULT_SENSOR_POLL),
400 rv);
402 event->SetTrusted(true);
404 bool defaultActionEnabled = true;
405 target->DispatchEvent(event, &defaultActionEnabled);
407 mLastRotationRate.reset();
408 mLastAccelerationIncluduingGravity.reset();
409 mLastAcceleration.reset();
410 mLastDOMMotionEventTime = TimeStamp::Now();