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
;
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
42 NS_DECL_NSIDEVICESENSORDATA
44 nsDeviceSensorData(unsigned long type
, double x
, double y
, double z
);
47 ~nsDeviceSensorData();
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
)
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
);
77 NS_IMETHODIMP
nsDeviceSensorData::GetX(double *aX
)
79 NS_ENSURE_ARG_POINTER(aX
);
84 NS_IMETHODIMP
nsDeviceSensorData::GetY(double *aY
)
86 NS_ENSURE_ARG_POINTER(aY
);
91 NS_IMETHODIMP
nsDeviceSensorData::GetZ(double *aZ
)
93 NS_ENSURE_ARG_POINTER(aZ
);
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
)
131 *aRetVal
= mWindowListeners
[aType
]->IndexOf(aWindow
) != NoIndex
;
136 NS_IMETHODIMP
nsDeviceSensors::AddWindowListener(uint32_t aType
, nsIDOMWindow
*aWindow
)
141 if (mWindowListeners
[aType
]->IndexOf(aWindow
) != NoIndex
)
144 if (!IsSensorEnabled(aType
)) {
145 RegisterSensorObserver((SensorType
)aType
, this);
148 mWindowListeners
[aType
]->AppendElement(aWindow
);
152 NS_IMETHODIMP
nsDeviceSensors::RemoveWindowListener(uint32_t aType
, nsIDOMWindow
*aWindow
)
154 if (mWindowListeners
[aType
]->IndexOf(aWindow
) == NoIndex
)
157 mWindowListeners
[aType
]->RemoveElement(aWindow
);
159 if (mWindowListeners
[aType
]->Length() == 0)
160 UnregisterSensorObserver((SensorType
)aType
, this);
165 NS_IMETHODIMP
nsDeviceSensors::RemoveWindowAsListener(nsIDOMWindow
*aWindow
)
167 for (int i
= 0; i
< NUM_SENSOR_TYPE
; i
++) {
168 RemoveWindowListener((SensorType
)i
, aWindow
);
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()) {
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
;
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 ; ) {
214 nsCOMPtr
<nsPIDOMWindow
> pwindow
= do_QueryInterface(windowListeners
[i
]);
215 if (WindowCannotReceiveSensorEvent(pwindow
)) {
219 nsCOMPtr
<nsIDOMDocument
> domdoc
;
220 windowListeners
[i
]->GetDocument(getter_AddRefs(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
);
240 nsDeviceSensors::FireDOMLightEvent(mozilla::dom::EventTarget
* aTarget
,
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
);
257 nsDeviceSensors::FireDOMProximityEvent(mozilla::dom::EventTarget
* aTarget
,
262 DeviceProximityEventInit init
;
263 init
.mBubbles
= true;
264 init
.mCancelable
= false;
265 init
.mValue
= aValue
;
268 nsRefPtr
<DeviceProximityEvent
> event
=
269 DeviceProximityEvent::Constructor(aTarget
,
270 NS_LITERAL_STRING("deviceproximity"),
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
);
290 nsDeviceSensors::FireDOMUserProximityEvent(mozilla::dom::EventTarget
* aTarget
,
293 UserProximityEventInit init
;
294 init
.mBubbles
= true;
295 init
.mCancelable
= false;
297 nsRefPtr
<UserProximityEvent
> event
=
298 UserProximityEvent::Constructor(aTarget
,
299 NS_LITERAL_STRING("userproximity"),
302 event
->SetTrusted(true);
304 bool defaultActionEnabled
;
305 aTarget
->DispatchEvent(event
, &defaultActionEnabled
);
309 nsDeviceSensors::FireDOMOrientationEvent(EventTarget
* aTarget
,
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"),
326 event
->SetTrusted(true);
329 aTarget
->DispatchEvent(event
, &dummy
);
334 nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument
*domdoc
,
341 // Attempt to coalesce events
342 bool fireEvent
= TimeStamp::Now() > mLastDOMMotionEventTime
+ TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL
);
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
);
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
);
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
);
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
) {
387 nsCOMPtr
<nsIDOMEvent
> event
;
388 domdoc
->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event
));
390 DeviceMotionEvent
* me
= static_cast<DeviceMotionEvent
*>(event
.get());
393 me
->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"),
397 *mLastAccelerationIncluduingGravity
,
399 Nullable
<double>(DEFAULT_SENSOR_POLL
),
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();