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 #define DEFAULT_SENSOR_POLL 100
34 static const nsTArray
<nsIDOMWindow
*>::index_type NoIndex
=
35 nsTArray
<nsIDOMWindow
*>::NoIndex
;
37 class nsDeviceSensorData MOZ_FINAL
: public nsIDeviceSensorData
41 NS_DECL_NSIDEVICESENSORDATA
43 nsDeviceSensorData(unsigned long type
, double x
, double y
, double z
);
46 ~nsDeviceSensorData();
53 nsDeviceSensorData::nsDeviceSensorData(unsigned long type
, double x
, double y
, double z
)
54 : mType(type
), mX(x
), mY(y
), mZ(z
)
58 NS_INTERFACE_MAP_BEGIN(nsDeviceSensorData
)
59 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDeviceSensorData
)
62 NS_IMPL_ADDREF(nsDeviceSensorData
)
63 NS_IMPL_RELEASE(nsDeviceSensorData
)
65 nsDeviceSensorData::~nsDeviceSensorData()
69 NS_IMETHODIMP
nsDeviceSensorData::GetType(uint32_t *aType
)
71 NS_ENSURE_ARG_POINTER(aType
);
76 NS_IMETHODIMP
nsDeviceSensorData::GetX(double *aX
)
78 NS_ENSURE_ARG_POINTER(aX
);
83 NS_IMETHODIMP
nsDeviceSensorData::GetY(double *aY
)
85 NS_ENSURE_ARG_POINTER(aY
);
90 NS_IMETHODIMP
nsDeviceSensorData::GetZ(double *aZ
)
92 NS_ENSURE_ARG_POINTER(aZ
);
97 NS_IMPL_ISUPPORTS(nsDeviceSensors
, nsIDeviceSensors
)
99 nsDeviceSensors::nsDeviceSensors()
101 mIsUserProximityNear
= false;
102 mLastDOMMotionEventTime
= TimeStamp::Now();
103 mEnabled
= Preferences::GetBool("device.sensors.enabled", true);
105 for (int i
= 0; i
< NUM_SENSOR_TYPE
; i
++) {
106 nsTArray
<nsIDOMWindow
*> *windows
= new nsTArray
<nsIDOMWindow
*>();
107 mWindowListeners
.AppendElement(windows
);
110 mLastDOMMotionEventTime
= TimeStamp::Now();
113 nsDeviceSensors::~nsDeviceSensors()
115 for (int i
= 0; i
< NUM_SENSOR_TYPE
; i
++) {
116 if (IsSensorEnabled(i
))
117 UnregisterSensorObserver((SensorType
)i
, this);
120 for (int i
= 0; i
< NUM_SENSOR_TYPE
; i
++) {
121 delete mWindowListeners
[i
];
125 NS_IMETHODIMP
nsDeviceSensors::HasWindowListener(uint32_t aType
, nsIDOMWindow
*aWindow
, bool *aRetVal
)
130 *aRetVal
= mWindowListeners
[aType
]->IndexOf(aWindow
) != NoIndex
;
135 NS_IMETHODIMP
nsDeviceSensors::AddWindowListener(uint32_t aType
, nsIDOMWindow
*aWindow
)
140 if (mWindowListeners
[aType
]->IndexOf(aWindow
) != NoIndex
)
143 if (!IsSensorEnabled(aType
)) {
144 RegisterSensorObserver((SensorType
)aType
, this);
147 mWindowListeners
[aType
]->AppendElement(aWindow
);
151 NS_IMETHODIMP
nsDeviceSensors::RemoveWindowListener(uint32_t aType
, nsIDOMWindow
*aWindow
)
153 if (mWindowListeners
[aType
]->IndexOf(aWindow
) == NoIndex
)
156 mWindowListeners
[aType
]->RemoveElement(aWindow
);
158 if (mWindowListeners
[aType
]->Length() == 0)
159 UnregisterSensorObserver((SensorType
)aType
, this);
164 NS_IMETHODIMP
nsDeviceSensors::RemoveWindowAsListener(nsIDOMWindow
*aWindow
)
166 for (int i
= 0; i
< NUM_SENSOR_TYPE
; i
++) {
167 RemoveWindowListener((SensorType
)i
, aWindow
);
173 WindowCannotReceiveSensorEvent (nsPIDOMWindow
* aWindow
)
175 // Check to see if this window is in the background. If
176 // it is and it does not have the "background-sensors" permission,
177 // don't send any device motion events to it.
178 if (!aWindow
|| !aWindow
->IsCurrentInnerWindow()) {
182 if (aWindow
->GetOuterWindow()->IsBackground()) {
183 nsCOMPtr
<nsIPermissionManager
> permMgr
=
184 services::GetPermissionManager();
185 NS_ENSURE_TRUE(permMgr
, false);
186 uint32_t permission
= nsIPermissionManager::DENY_ACTION
;
187 permMgr
->TestPermissionFromWindow(aWindow
, "background-sensors", &permission
);
188 return permission
!= nsIPermissionManager::ALLOW_ACTION
;
195 nsDeviceSensors::Notify(const mozilla::hal::SensorData
& aSensorData
)
197 uint32_t type
= aSensorData
.sensor();
199 const InfallibleTArray
<float>& values
= aSensorData
.values();
200 size_t len
= values
.Length();
201 double x
= len
> 0 ? values
[0] : 0.0;
202 double y
= len
> 1 ? values
[1] : 0.0;
203 double z
= len
> 2 ? values
[2] : 0.0;
205 nsCOMArray
<nsIDOMWindow
> windowListeners
;
206 for (uint32_t i
= 0; i
< mWindowListeners
[type
]->Length(); i
++) {
207 windowListeners
.AppendObject(mWindowListeners
[type
]->SafeElementAt(i
));
210 for (uint32_t i
= windowListeners
.Count(); i
> 0 ; ) {
213 nsCOMPtr
<nsPIDOMWindow
> pwindow
= do_QueryInterface(windowListeners
[i
]);
214 if (WindowCannotReceiveSensorEvent(pwindow
)) {
218 nsCOMPtr
<nsIDOMDocument
> domdoc
;
219 windowListeners
[i
]->GetDocument(getter_AddRefs(domdoc
));
222 nsCOMPtr
<mozilla::dom::EventTarget
> target
= do_QueryInterface(windowListeners
[i
]);
223 if (type
== nsIDeviceSensorData::TYPE_ACCELERATION
||
224 type
== nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION
||
225 type
== nsIDeviceSensorData::TYPE_GYROSCOPE
)
226 FireDOMMotionEvent(domdoc
, target
, type
, x
, y
, z
);
227 else if (type
== nsIDeviceSensorData::TYPE_ORIENTATION
)
228 FireDOMOrientationEvent(target
, x
, y
, z
);
229 else if (type
== nsIDeviceSensorData::TYPE_PROXIMITY
)
230 FireDOMProximityEvent(target
, x
, y
, z
);
231 else if (type
== nsIDeviceSensorData::TYPE_LIGHT
)
232 FireDOMLightEvent(target
, x
);
239 nsDeviceSensors::FireDOMLightEvent(mozilla::dom::EventTarget
* aTarget
,
242 DeviceLightEventInit init
;
243 init
.mBubbles
= true;
244 init
.mCancelable
= false;
245 init
.mValue
= aValue
;
246 nsRefPtr
<DeviceLightEvent
> event
=
247 DeviceLightEvent::Constructor(aTarget
, NS_LITERAL_STRING("devicelight"), init
);
249 event
->SetTrusted(true);
251 bool defaultActionEnabled
;
252 aTarget
->DispatchEvent(event
, &defaultActionEnabled
);
256 nsDeviceSensors::FireDOMProximityEvent(mozilla::dom::EventTarget
* aTarget
,
261 DeviceProximityEventInit init
;
262 init
.mBubbles
= true;
263 init
.mCancelable
= false;
264 init
.mValue
= aValue
;
267 nsRefPtr
<DeviceProximityEvent
> event
=
268 DeviceProximityEvent::Constructor(aTarget
,
269 NS_LITERAL_STRING("deviceproximity"),
271 event
->SetTrusted(true);
273 bool defaultActionEnabled
;
274 aTarget
->DispatchEvent(event
, &defaultActionEnabled
);
276 // Some proximity sensors only support a binary near or
277 // far measurement. In this case, the sensor should report
278 // its maximum range value in the far state and a lesser
279 // value in the near state.
281 bool near
= (aValue
< aMax
);
282 if (mIsUserProximityNear
!= near
) {
283 mIsUserProximityNear
= near
;
284 FireDOMUserProximityEvent(aTarget
, mIsUserProximityNear
);
289 nsDeviceSensors::FireDOMUserProximityEvent(mozilla::dom::EventTarget
* aTarget
,
292 UserProximityEventInit init
;
293 init
.mBubbles
= true;
294 init
.mCancelable
= false;
296 nsRefPtr
<UserProximityEvent
> event
=
297 UserProximityEvent::Constructor(aTarget
,
298 NS_LITERAL_STRING("userproximity"),
301 event
->SetTrusted(true);
303 bool defaultActionEnabled
;
304 aTarget
->DispatchEvent(event
, &defaultActionEnabled
);
308 nsDeviceSensors::FireDOMOrientationEvent(EventTarget
* aTarget
,
313 DeviceOrientationEventInit init
;
314 init
.mBubbles
= true;
315 init
.mCancelable
= false;
316 init
.mAlpha
.SetValue(aAlpha
);
317 init
.mBeta
.SetValue(aBeta
);
318 init
.mGamma
.SetValue(aGamma
);
319 init
.mAbsolute
= true;
321 nsRefPtr
<DeviceOrientationEvent
> event
=
322 DeviceOrientationEvent::Constructor(aTarget
,
323 NS_LITERAL_STRING("deviceorientation"),
325 event
->SetTrusted(true);
328 aTarget
->DispatchEvent(event
, &dummy
);
333 nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument
*domdoc
,
340 // Attempt to coalesce events
341 bool fireEvent
= TimeStamp::Now() > mLastDOMMotionEventTime
+ TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL
);
344 case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION
:
345 if (!mLastAcceleration
) {
346 mLastAcceleration
.emplace();
348 mLastAcceleration
->mX
.SetValue(x
);
349 mLastAcceleration
->mY
.SetValue(y
);
350 mLastAcceleration
->mZ
.SetValue(z
);
352 case nsIDeviceSensorData::TYPE_ACCELERATION
:
353 if (!mLastAccelerationIncludingGravity
) {
354 mLastAccelerationIncludingGravity
.emplace();
356 mLastAccelerationIncludingGravity
->mX
.SetValue(x
);
357 mLastAccelerationIncludingGravity
->mY
.SetValue(y
);
358 mLastAccelerationIncludingGravity
->mZ
.SetValue(z
);
360 case nsIDeviceSensorData::TYPE_GYROSCOPE
:
361 if (!mLastRotationRate
) {
362 mLastRotationRate
.emplace();
364 mLastRotationRate
->mAlpha
.SetValue(x
);
365 mLastRotationRate
->mBeta
.SetValue(y
);
366 mLastRotationRate
->mGamma
.SetValue(z
);
371 if (!mLastAcceleration
) {
372 mLastAcceleration
.emplace();
374 if (!mLastAccelerationIncludingGravity
) {
375 mLastAccelerationIncludingGravity
.emplace();
377 if (!mLastRotationRate
) {
378 mLastRotationRate
.emplace();
380 } else if (!mLastAcceleration
||
381 !mLastAccelerationIncludingGravity
||
382 !mLastRotationRate
) {
386 nsCOMPtr
<nsIDOMEvent
> event
;
387 domdoc
->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event
));
389 DeviceMotionEvent
* me
= static_cast<DeviceMotionEvent
*>(event
.get());
392 me
->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"),
396 *mLastAccelerationIncludingGravity
,
398 Nullable
<double>(DEFAULT_SENSOR_POLL
),
401 event
->SetTrusted(true);
403 bool defaultActionEnabled
= true;
404 target
->DispatchEvent(event
, &defaultActionEnabled
);
406 mLastRotationRate
.reset();
407 mLastAccelerationIncludingGravity
.reset();
408 mLastAcceleration
.reset();
409 mLastDOMMotionEventTime
= TimeStamp::Now();