1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/nsDeviceSensors.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,416 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "mozilla/Hal.h" 1.9 +#include "mozilla/HalSensor.h" 1.10 + 1.11 +#include "nsDeviceSensors.h" 1.12 + 1.13 +#include "nsAutoPtr.h" 1.14 +#include "nsIDOMEvent.h" 1.15 +#include "nsIDOMWindow.h" 1.16 +#include "nsPIDOMWindow.h" 1.17 +#include "nsIDOMDocument.h" 1.18 +#include "nsIServiceManager.h" 1.19 +#include "nsIServiceManager.h" 1.20 +#include "GeneratedEvents.h" 1.21 +#include "mozilla/Preferences.h" 1.22 +#include "mozilla/Attributes.h" 1.23 +#include "nsIPermissionManager.h" 1.24 +#include "mozilla/dom/DeviceLightEvent.h" 1.25 +#include "mozilla/dom/DeviceProximityEvent.h" 1.26 +#include "mozilla/dom/UserProximityEvent.h" 1.27 + 1.28 +using namespace mozilla; 1.29 +using namespace mozilla::dom; 1.30 +using namespace hal; 1.31 + 1.32 +#undef near 1.33 + 1.34 +// also see sDefaultSensorHint in mobile/android/base/GeckoAppShell.java 1.35 +#define DEFAULT_SENSOR_POLL 100 1.36 + 1.37 +static const nsTArray<nsIDOMWindow*>::index_type NoIndex = 1.38 + nsTArray<nsIDOMWindow*>::NoIndex; 1.39 + 1.40 +class nsDeviceSensorData MOZ_FINAL : public nsIDeviceSensorData 1.41 +{ 1.42 +public: 1.43 + NS_DECL_ISUPPORTS 1.44 + NS_DECL_NSIDEVICESENSORDATA 1.45 + 1.46 + nsDeviceSensorData(unsigned long type, double x, double y, double z); 1.47 + 1.48 +private: 1.49 + ~nsDeviceSensorData(); 1.50 + 1.51 +protected: 1.52 + unsigned long mType; 1.53 + double mX, mY, mZ; 1.54 +}; 1.55 + 1.56 +nsDeviceSensorData::nsDeviceSensorData(unsigned long type, double x, double y, double z) 1.57 + : mType(type), mX(x), mY(y), mZ(z) 1.58 +{ 1.59 +} 1.60 + 1.61 +NS_INTERFACE_MAP_BEGIN(nsDeviceSensorData) 1.62 +NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDeviceSensorData) 1.63 +NS_INTERFACE_MAP_END 1.64 + 1.65 +NS_IMPL_ADDREF(nsDeviceSensorData) 1.66 +NS_IMPL_RELEASE(nsDeviceSensorData) 1.67 + 1.68 +nsDeviceSensorData::~nsDeviceSensorData() 1.69 +{ 1.70 +} 1.71 + 1.72 +NS_IMETHODIMP nsDeviceSensorData::GetType(uint32_t *aType) 1.73 +{ 1.74 + NS_ENSURE_ARG_POINTER(aType); 1.75 + *aType = mType; 1.76 + return NS_OK; 1.77 +} 1.78 + 1.79 +NS_IMETHODIMP nsDeviceSensorData::GetX(double *aX) 1.80 +{ 1.81 + NS_ENSURE_ARG_POINTER(aX); 1.82 + *aX = mX; 1.83 + return NS_OK; 1.84 +} 1.85 + 1.86 +NS_IMETHODIMP nsDeviceSensorData::GetY(double *aY) 1.87 +{ 1.88 + NS_ENSURE_ARG_POINTER(aY); 1.89 + *aY = mY; 1.90 + return NS_OK; 1.91 +} 1.92 + 1.93 +NS_IMETHODIMP nsDeviceSensorData::GetZ(double *aZ) 1.94 +{ 1.95 + NS_ENSURE_ARG_POINTER(aZ); 1.96 + *aZ = mZ; 1.97 + return NS_OK; 1.98 +} 1.99 + 1.100 +NS_IMPL_ISUPPORTS(nsDeviceSensors, nsIDeviceSensors) 1.101 + 1.102 +nsDeviceSensors::nsDeviceSensors() 1.103 +{ 1.104 + mIsUserProximityNear = false; 1.105 + mLastDOMMotionEventTime = TimeStamp::Now(); 1.106 + mEnabled = Preferences::GetBool("device.sensors.enabled", true); 1.107 + 1.108 + for (int i = 0; i < NUM_SENSOR_TYPE; i++) { 1.109 + nsTArray<nsIDOMWindow*> *windows = new nsTArray<nsIDOMWindow*>(); 1.110 + mWindowListeners.AppendElement(windows); 1.111 + } 1.112 + 1.113 + mLastDOMMotionEventTime = TimeStamp::Now(); 1.114 +} 1.115 + 1.116 +nsDeviceSensors::~nsDeviceSensors() 1.117 +{ 1.118 + for (int i = 0; i < NUM_SENSOR_TYPE; i++) { 1.119 + if (IsSensorEnabled(i)) 1.120 + UnregisterSensorObserver((SensorType)i, this); 1.121 + } 1.122 + 1.123 + for (int i = 0; i < NUM_SENSOR_TYPE; i++) { 1.124 + delete mWindowListeners[i]; 1.125 + } 1.126 +} 1.127 + 1.128 +NS_IMETHODIMP nsDeviceSensors::HasWindowListener(uint32_t aType, nsIDOMWindow *aWindow, bool *aRetVal) 1.129 +{ 1.130 + if (!mEnabled) 1.131 + *aRetVal = false; 1.132 + else 1.133 + *aRetVal = mWindowListeners[aType]->IndexOf(aWindow) != NoIndex; 1.134 + 1.135 + return NS_OK; 1.136 +} 1.137 + 1.138 +NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *aWindow) 1.139 +{ 1.140 + if (!mEnabled) 1.141 + return NS_OK; 1.142 + 1.143 + if (mWindowListeners[aType]->IndexOf(aWindow) != NoIndex) 1.144 + return NS_OK; 1.145 + 1.146 + if (!IsSensorEnabled(aType)) { 1.147 + RegisterSensorObserver((SensorType)aType, this); 1.148 + } 1.149 + 1.150 + mWindowListeners[aType]->AppendElement(aWindow); 1.151 + return NS_OK; 1.152 +} 1.153 + 1.154 +NS_IMETHODIMP nsDeviceSensors::RemoveWindowListener(uint32_t aType, nsIDOMWindow *aWindow) 1.155 +{ 1.156 + if (mWindowListeners[aType]->IndexOf(aWindow) == NoIndex) 1.157 + return NS_OK; 1.158 + 1.159 + mWindowListeners[aType]->RemoveElement(aWindow); 1.160 + 1.161 + if (mWindowListeners[aType]->Length() == 0) 1.162 + UnregisterSensorObserver((SensorType)aType, this); 1.163 + 1.164 + return NS_OK; 1.165 +} 1.166 + 1.167 +NS_IMETHODIMP nsDeviceSensors::RemoveWindowAsListener(nsIDOMWindow *aWindow) 1.168 +{ 1.169 + for (int i = 0; i < NUM_SENSOR_TYPE; i++) { 1.170 + RemoveWindowListener((SensorType)i, aWindow); 1.171 + } 1.172 + return NS_OK; 1.173 +} 1.174 + 1.175 +static bool 1.176 +WindowCannotReceiveSensorEvent (nsPIDOMWindow* aWindow) 1.177 +{ 1.178 + // Check to see if this window is in the background. If 1.179 + // it is and it does not have the "background-sensors" permission, 1.180 + // don't send any device motion events to it. 1.181 + if (!aWindow || !aWindow->IsCurrentInnerWindow()) { 1.182 + return true; 1.183 + } 1.184 + 1.185 + if (aWindow->GetOuterWindow()->IsBackground()) { 1.186 + nsCOMPtr<nsIPermissionManager> permMgr = 1.187 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); 1.188 + NS_ENSURE_TRUE(permMgr, false); 1.189 + uint32_t permission = nsIPermissionManager::DENY_ACTION; 1.190 + permMgr->TestPermissionFromWindow(aWindow, "background-sensors", &permission); 1.191 + return permission != nsIPermissionManager::ALLOW_ACTION; 1.192 + } 1.193 + 1.194 + return false; 1.195 +} 1.196 + 1.197 +void 1.198 +nsDeviceSensors::Notify(const mozilla::hal::SensorData& aSensorData) 1.199 +{ 1.200 + uint32_t type = aSensorData.sensor(); 1.201 + 1.202 + const InfallibleTArray<float>& values = aSensorData.values(); 1.203 + size_t len = values.Length(); 1.204 + double x = len > 0 ? values[0] : 0.0; 1.205 + double y = len > 1 ? values[1] : 0.0; 1.206 + double z = len > 2 ? values[2] : 0.0; 1.207 + 1.208 + nsCOMArray<nsIDOMWindow> windowListeners; 1.209 + for (uint32_t i = 0; i < mWindowListeners[type]->Length(); i++) { 1.210 + windowListeners.AppendObject(mWindowListeners[type]->SafeElementAt(i)); 1.211 + } 1.212 + 1.213 + for (uint32_t i = windowListeners.Count(); i > 0 ; ) { 1.214 + --i; 1.215 + 1.216 + nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(windowListeners[i]); 1.217 + if (WindowCannotReceiveSensorEvent(pwindow)) { 1.218 + continue; 1.219 + } 1.220 + 1.221 + nsCOMPtr<nsIDOMDocument> domdoc; 1.222 + windowListeners[i]->GetDocument(getter_AddRefs(domdoc)); 1.223 + 1.224 + if (domdoc) { 1.225 + nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(windowListeners[i]); 1.226 + if (type == nsIDeviceSensorData::TYPE_ACCELERATION || 1.227 + type == nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION || 1.228 + type == nsIDeviceSensorData::TYPE_GYROSCOPE) 1.229 + FireDOMMotionEvent(domdoc, target, type, x, y, z); 1.230 + else if (type == nsIDeviceSensorData::TYPE_ORIENTATION) 1.231 + FireDOMOrientationEvent(domdoc, target, x, y, z); 1.232 + else if (type == nsIDeviceSensorData::TYPE_PROXIMITY) 1.233 + FireDOMProximityEvent(target, x, y, z); 1.234 + else if (type == nsIDeviceSensorData::TYPE_LIGHT) 1.235 + FireDOMLightEvent(target, x); 1.236 + 1.237 + } 1.238 + } 1.239 +} 1.240 + 1.241 +void 1.242 +nsDeviceSensors::FireDOMLightEvent(mozilla::dom::EventTarget* aTarget, 1.243 + double aValue) 1.244 +{ 1.245 + DeviceLightEventInit init; 1.246 + init.mBubbles = true; 1.247 + init.mCancelable = false; 1.248 + init.mValue = aValue; 1.249 + nsRefPtr<DeviceLightEvent> event = 1.250 + DeviceLightEvent::Constructor(aTarget, NS_LITERAL_STRING("devicelight"), init); 1.251 + 1.252 + event->SetTrusted(true); 1.253 + 1.254 + bool defaultActionEnabled; 1.255 + aTarget->DispatchEvent(event, &defaultActionEnabled); 1.256 +} 1.257 + 1.258 +void 1.259 +nsDeviceSensors::FireDOMProximityEvent(mozilla::dom::EventTarget* aTarget, 1.260 + double aValue, 1.261 + double aMin, 1.262 + double aMax) 1.263 +{ 1.264 + DeviceProximityEventInit init; 1.265 + init.mBubbles = true; 1.266 + init.mCancelable = false; 1.267 + init.mValue = aValue; 1.268 + init.mMin = aMin; 1.269 + init.mMax = aMax; 1.270 + nsRefPtr<DeviceProximityEvent> event = 1.271 + DeviceProximityEvent::Constructor(aTarget, 1.272 + NS_LITERAL_STRING("deviceproximity"), 1.273 + init); 1.274 + event->SetTrusted(true); 1.275 + 1.276 + bool defaultActionEnabled; 1.277 + aTarget->DispatchEvent(event, &defaultActionEnabled); 1.278 + 1.279 + // Some proximity sensors only support a binary near or 1.280 + // far measurement. In this case, the sensor should report 1.281 + // its maximum range value in the far state and a lesser 1.282 + // value in the near state. 1.283 + 1.284 + bool near = (aValue < aMax); 1.285 + if (mIsUserProximityNear != near) { 1.286 + mIsUserProximityNear = near; 1.287 + FireDOMUserProximityEvent(aTarget, mIsUserProximityNear); 1.288 + } 1.289 +} 1.290 + 1.291 +void 1.292 +nsDeviceSensors::FireDOMUserProximityEvent(mozilla::dom::EventTarget* aTarget, 1.293 + bool aNear) 1.294 +{ 1.295 + UserProximityEventInit init; 1.296 + init.mBubbles = true; 1.297 + init.mCancelable = false; 1.298 + init.mNear = aNear; 1.299 + nsRefPtr<UserProximityEvent> event = 1.300 + UserProximityEvent::Constructor(aTarget, 1.301 + NS_LITERAL_STRING("userproximity"), 1.302 + init); 1.303 + 1.304 + event->SetTrusted(true); 1.305 + 1.306 + bool defaultActionEnabled; 1.307 + aTarget->DispatchEvent(event, &defaultActionEnabled); 1.308 +} 1.309 + 1.310 +void 1.311 +nsDeviceSensors::FireDOMOrientationEvent(nsIDOMDocument* domdoc, 1.312 + EventTarget* target, 1.313 + double alpha, 1.314 + double beta, 1.315 + double gamma) 1.316 +{ 1.317 + nsCOMPtr<nsIDOMEvent> event; 1.318 + bool defaultActionEnabled = true; 1.319 + domdoc->CreateEvent(NS_LITERAL_STRING("DeviceOrientationEvent"), getter_AddRefs(event)); 1.320 + 1.321 + nsCOMPtr<nsIDOMDeviceOrientationEvent> oe = do_QueryInterface(event); 1.322 + 1.323 + if (!oe) { 1.324 + return; 1.325 + } 1.326 + 1.327 + oe->InitDeviceOrientationEvent(NS_LITERAL_STRING("deviceorientation"), 1.328 + true, 1.329 + false, 1.330 + alpha, 1.331 + beta, 1.332 + gamma, 1.333 + true); 1.334 + 1.335 + event->SetTrusted(true); 1.336 + 1.337 + target->DispatchEvent(event, &defaultActionEnabled); 1.338 +} 1.339 + 1.340 + 1.341 +void 1.342 +nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument *domdoc, 1.343 + EventTarget* target, 1.344 + uint32_t type, 1.345 + double x, 1.346 + double y, 1.347 + double z) 1.348 +{ 1.349 + // Attempt to coalesce events 1.350 + bool fireEvent = TimeStamp::Now() > mLastDOMMotionEventTime + TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL); 1.351 + 1.352 + switch (type) { 1.353 + case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION: 1.354 + if (mLastAcceleration.empty()) { 1.355 + mLastAcceleration.construct(); 1.356 + } 1.357 + mLastAcceleration.ref().mX.SetValue(x); 1.358 + mLastAcceleration.ref().mY.SetValue(y); 1.359 + mLastAcceleration.ref().mZ.SetValue(z); 1.360 + break; 1.361 + case nsIDeviceSensorData::TYPE_ACCELERATION: 1.362 + if (mLastAccelerationIncluduingGravity.empty()) { 1.363 + mLastAccelerationIncluduingGravity.construct(); 1.364 + } 1.365 + mLastAccelerationIncluduingGravity.ref().mX.SetValue(x); 1.366 + mLastAccelerationIncluduingGravity.ref().mY.SetValue(y); 1.367 + mLastAccelerationIncluduingGravity.ref().mZ.SetValue(z); 1.368 + break; 1.369 + case nsIDeviceSensorData::TYPE_GYROSCOPE: 1.370 + if (mLastRotationRate.empty()) { 1.371 + mLastRotationRate.construct(); 1.372 + } 1.373 + mLastRotationRate.ref().mAlpha.SetValue(x); 1.374 + mLastRotationRate.ref().mBeta.SetValue(y); 1.375 + mLastRotationRate.ref().mGamma.SetValue(z); 1.376 + break; 1.377 + } 1.378 + 1.379 + if (fireEvent) { 1.380 + if (mLastAcceleration.empty()) { 1.381 + mLastAcceleration.construct(); 1.382 + } 1.383 + if (mLastAccelerationIncluduingGravity.empty()) { 1.384 + mLastAccelerationIncluduingGravity.construct(); 1.385 + } 1.386 + if (mLastRotationRate.empty()) { 1.387 + mLastRotationRate.construct(); 1.388 + } 1.389 + } else if (mLastAcceleration.empty() || 1.390 + mLastAccelerationIncluduingGravity.empty() || 1.391 + mLastRotationRate.empty()) { 1.392 + return; 1.393 + } 1.394 + 1.395 + nsCOMPtr<nsIDOMEvent> event; 1.396 + domdoc->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event)); 1.397 + 1.398 + DeviceMotionEvent* me = static_cast<DeviceMotionEvent*>(event.get()); 1.399 + 1.400 + ErrorResult rv; 1.401 + me->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"), 1.402 + true, 1.403 + false, 1.404 + mLastAcceleration.ref(), 1.405 + mLastAccelerationIncluduingGravity.ref(), 1.406 + mLastRotationRate.ref(), 1.407 + Nullable<double>(DEFAULT_SENSOR_POLL), 1.408 + rv); 1.409 + 1.410 + event->SetTrusted(true); 1.411 + 1.412 + bool defaultActionEnabled = true; 1.413 + target->DispatchEvent(event, &defaultActionEnabled); 1.414 + 1.415 + mLastRotationRate.destroy(); 1.416 + mLastAccelerationIncluduingGravity.destroy(); 1.417 + mLastAcceleration.destroy(); 1.418 + mLastDOMMotionEventTime = TimeStamp::Now(); 1.419 +}