Bumping manifests a=b2g-bump
[gecko.git] / dom / system / nsDeviceSensors.cpp
blob1fee4062f13943e1fbba4875bbb6c883b47ffa8d
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 #define DEFAULT_SENSOR_POLL 100
34 static const nsTArray<nsIDOMWindow*>::index_type NoIndex =
35 nsTArray<nsIDOMWindow*>::NoIndex;
37 class nsDeviceSensorData MOZ_FINAL : public nsIDeviceSensorData
39 public:
40 NS_DECL_ISUPPORTS
41 NS_DECL_NSIDEVICESENSORDATA
43 nsDeviceSensorData(unsigned long type, double x, double y, double z);
45 private:
46 ~nsDeviceSensorData();
48 protected:
49 unsigned long mType;
50 double mX, mY, mZ;
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)
60 NS_INTERFACE_MAP_END
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);
72 *aType = mType;
73 return NS_OK;
76 NS_IMETHODIMP nsDeviceSensorData::GetX(double *aX)
78 NS_ENSURE_ARG_POINTER(aX);
79 *aX = mX;
80 return NS_OK;
83 NS_IMETHODIMP nsDeviceSensorData::GetY(double *aY)
85 NS_ENSURE_ARG_POINTER(aY);
86 *aY = mY;
87 return NS_OK;
90 NS_IMETHODIMP nsDeviceSensorData::GetZ(double *aZ)
92 NS_ENSURE_ARG_POINTER(aZ);
93 *aZ = mZ;
94 return NS_OK;
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)
127 if (!mEnabled)
128 *aRetVal = false;
129 else
130 *aRetVal = mWindowListeners[aType]->IndexOf(aWindow) != NoIndex;
132 return NS_OK;
135 NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
137 if (!mEnabled)
138 return NS_OK;
140 if (mWindowListeners[aType]->IndexOf(aWindow) != NoIndex)
141 return NS_OK;
143 if (!IsSensorEnabled(aType)) {
144 RegisterSensorObserver((SensorType)aType, this);
147 mWindowListeners[aType]->AppendElement(aWindow);
148 return NS_OK;
151 NS_IMETHODIMP nsDeviceSensors::RemoveWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
153 if (mWindowListeners[aType]->IndexOf(aWindow) == NoIndex)
154 return NS_OK;
156 mWindowListeners[aType]->RemoveElement(aWindow);
158 if (mWindowListeners[aType]->Length() == 0)
159 UnregisterSensorObserver((SensorType)aType, this);
161 return NS_OK;
164 NS_IMETHODIMP nsDeviceSensors::RemoveWindowAsListener(nsIDOMWindow *aWindow)
166 for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
167 RemoveWindowListener((SensorType)i, aWindow);
169 return NS_OK;
172 static bool
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()) {
179 return true;
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;
191 return false;
194 void
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 ; ) {
211 --i;
213 nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(windowListeners[i]);
214 if (WindowCannotReceiveSensorEvent(pwindow)) {
215 continue;
218 nsCOMPtr<nsIDOMDocument> domdoc;
219 windowListeners[i]->GetDocument(getter_AddRefs(domdoc));
221 if (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);
238 void
239 nsDeviceSensors::FireDOMLightEvent(mozilla::dom::EventTarget* aTarget,
240 double aValue)
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);
255 void
256 nsDeviceSensors::FireDOMProximityEvent(mozilla::dom::EventTarget* aTarget,
257 double aValue,
258 double aMin,
259 double aMax)
261 DeviceProximityEventInit init;
262 init.mBubbles = true;
263 init.mCancelable = false;
264 init.mValue = aValue;
265 init.mMin = aMin;
266 init.mMax = aMax;
267 nsRefPtr<DeviceProximityEvent> event =
268 DeviceProximityEvent::Constructor(aTarget,
269 NS_LITERAL_STRING("deviceproximity"),
270 init);
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);
288 void
289 nsDeviceSensors::FireDOMUserProximityEvent(mozilla::dom::EventTarget* aTarget,
290 bool aNear)
292 UserProximityEventInit init;
293 init.mBubbles = true;
294 init.mCancelable = false;
295 init.mNear = aNear;
296 nsRefPtr<UserProximityEvent> event =
297 UserProximityEvent::Constructor(aTarget,
298 NS_LITERAL_STRING("userproximity"),
299 init);
301 event->SetTrusted(true);
303 bool defaultActionEnabled;
304 aTarget->DispatchEvent(event, &defaultActionEnabled);
307 void
308 nsDeviceSensors::FireDOMOrientationEvent(EventTarget* aTarget,
309 double aAlpha,
310 double aBeta,
311 double aGamma)
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"),
324 init);
325 event->SetTrusted(true);
327 bool dummy;
328 aTarget->DispatchEvent(event, &dummy);
332 void
333 nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument *domdoc,
334 EventTarget* target,
335 uint32_t type,
336 double x,
337 double y,
338 double z)
340 // Attempt to coalesce events
341 bool fireEvent = TimeStamp::Now() > mLastDOMMotionEventTime + TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL);
343 switch (type) {
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);
351 break;
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);
359 break;
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);
367 break;
370 if (fireEvent) {
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) {
383 return;
386 nsCOMPtr<nsIDOMEvent> event;
387 domdoc->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event));
389 DeviceMotionEvent* me = static_cast<DeviceMotionEvent*>(event.get());
391 ErrorResult rv;
392 me->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"),
393 true,
394 false,
395 *mLastAcceleration,
396 *mLastAccelerationIncludingGravity,
397 *mLastRotationRate,
398 Nullable<double>(DEFAULT_SENSOR_POLL),
399 rv);
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();