/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Doug Turner * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "mozilla/Hal.h" #include "mozilla/HalSensor.h" #include "nsDeviceSensors.h" #include "nsAutoPtr.h" #include "nsIDOMEvent.h" #include "nsIDOMWindow.h" #include "nsPIDOMWindow.h" #include "nsIDOMDocument.h" #include "nsIDOMEventTarget.h" #include "nsIServiceManager.h" #include "nsIPrivateDOMEvent.h" #include "nsIServiceManager.h" #include "mozilla/Preferences.h" using namespace mozilla; using namespace hal; // also see sDefaultSensorHint in mobile/android/base/GeckoAppShell.java #define DEFAULT_SENSOR_POLL 100 static const nsTArray::index_type NoIndex = nsTArray::NoIndex; class nsDeviceSensorData : public nsIDeviceSensorData { public: NS_DECL_ISUPPORTS NS_DECL_NSIDEVICESENSORDATA nsDeviceSensorData(unsigned long type, double x, double y, double z); private: ~nsDeviceSensorData(); protected: unsigned long mType; double mX, mY, mZ; }; nsDeviceSensorData::nsDeviceSensorData(unsigned long type, double x, double y, double z) : mType(type), mX(x), mY(y), mZ(z) { } NS_INTERFACE_MAP_BEGIN(nsDeviceSensorData) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDeviceSensorData) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsDeviceSensorData) NS_IMPL_RELEASE(nsDeviceSensorData) nsDeviceSensorData::~nsDeviceSensorData() { } NS_IMETHODIMP nsDeviceSensorData::GetType(PRUint32 *aType) { NS_ENSURE_ARG_POINTER(aType); *aType = mType; return NS_OK; } NS_IMETHODIMP nsDeviceSensorData::GetX(double *aX) { NS_ENSURE_ARG_POINTER(aX); *aX = mX; return NS_OK; } NS_IMETHODIMP nsDeviceSensorData::GetY(double *aY) { NS_ENSURE_ARG_POINTER(aY); *aY = mY; return NS_OK; } NS_IMETHODIMP nsDeviceSensorData::GetZ(double *aZ) { NS_ENSURE_ARG_POINTER(aZ); *aZ = mZ; return NS_OK; } NS_IMPL_ISUPPORTS1(nsDeviceSensors, nsIDeviceSensors) nsDeviceSensors::nsDeviceSensors() { mLastDOMMotionEventTime = TimeStamp::Now(); mEnabled = Preferences::GetBool("device.motion.enabled", true); for (int i = 0; i < NUM_SENSOR_TYPE; i++) { nsTArray *windows = new nsTArray(); mWindowListeners.AppendElement(windows); } mLastDOMMotionEventTime = TimeStamp::Now(); } nsDeviceSensors::~nsDeviceSensors() { for (int i = 0; i < NUM_SENSOR_TYPE; i++) { if (IsSensorEnabled(i)) UnregisterSensorObserver((SensorType)i, this); } for (int i = 0; i < NUM_SENSOR_TYPE; i++) { delete mWindowListeners[i]; } } NS_IMETHODIMP nsDeviceSensors::AddWindowListener(PRUint32 aType, nsIDOMWindow *aWindow) { if (mWindowListeners[aType]->IndexOf(aWindow) != NoIndex) return NS_OK; if (!IsSensorEnabled(aType)) { RegisterSensorObserver((SensorType)aType, this); } mWindowListeners[aType]->AppendElement(aWindow); return NS_OK; } NS_IMETHODIMP nsDeviceSensors::RemoveWindowListener(PRUint32 aType, nsIDOMWindow *aWindow) { if (mWindowListeners[aType]->IndexOf(aWindow) == NoIndex) return NS_OK; mWindowListeners[aType]->RemoveElement(aWindow); if (mWindowListeners[aType]->Length() == 0) UnregisterSensorObserver((SensorType)aType, this); return NS_OK; } NS_IMETHODIMP nsDeviceSensors::RemoveWindowAsListener(nsIDOMWindow *aWindow) { for (int i = 0; i < NUM_SENSOR_TYPE; i++) { RemoveWindowListener((SensorType)i, aWindow); } return NS_OK; } void nsDeviceSensors::Notify(const mozilla::hal::SensorData& aSensorData) { if (!mEnabled) return; PRUint32 type = aSensorData.sensor(); double x = aSensorData.values()[0]; double y = aSensorData.values()[1]; double z = aSensorData.values()[2]; nsCOMArray windowListeners; for (PRUint32 i = 0; i < mWindowListeners[type]->Length(); i++) { windowListeners.AppendObject(mWindowListeners[type]->SafeElementAt(i)); } for (PRUint32 i = windowListeners.Count(); i > 0 ; ) { --i; // check to see if this window is in the background. if // it is, don't send any device motion to it. nsCOMPtr pwindow = do_QueryInterface(windowListeners[i]); if (!pwindow || !pwindow->GetOuterWindow() || pwindow->GetOuterWindow()->IsBackground()) continue; nsCOMPtr domdoc; windowListeners[i]->GetDocument(getter_AddRefs(domdoc)); if (domdoc) { nsCOMPtr target = do_QueryInterface(windowListeners[i]); if (type == nsIDeviceSensorData::TYPE_ACCELERATION || type == nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION || type == nsIDeviceSensorData::TYPE_GYROSCOPE) FireDOMMotionEvent(domdoc, target, type, x, y, z); else if (type == nsIDeviceSensorData::TYPE_ORIENTATION) FireDOMOrientationEvent(domdoc, target, x, y, z); } } } void nsDeviceSensors::FireDOMOrientationEvent(nsIDOMDocument *domdoc, nsIDOMEventTarget *target, double alpha, double beta, double gamma) { nsCOMPtr event; bool defaultActionEnabled = true; domdoc->CreateEvent(NS_LITERAL_STRING("DeviceOrientationEvent"), getter_AddRefs(event)); nsCOMPtr oe = do_QueryInterface(event); if (!oe) { return; } oe->InitDeviceOrientationEvent(NS_LITERAL_STRING("deviceorientation"), true, false, alpha, beta, gamma, true); nsCOMPtr privateEvent = do_QueryInterface(event); if (privateEvent) privateEvent->SetTrusted(true); target->DispatchEvent(event, &defaultActionEnabled); } void nsDeviceSensors::FireDOMMotionEvent(nsIDOMDocument *domdoc, nsIDOMEventTarget *target, PRUint32 type, double x, double y, double z) { // Attempt to coalesce events bool fireEvent = TimeStamp::Now() > mLastDOMMotionEventTime + TimeDuration::FromMilliseconds(DEFAULT_SENSOR_POLL); switch (type) { case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION: mLastAcceleration = new nsDOMDeviceAcceleration(x, y, z); break; case nsIDeviceSensorData::TYPE_ACCELERATION: mLastAccelerationIncluduingGravity = new nsDOMDeviceAcceleration(x, y, z); break; case nsIDeviceSensorData::TYPE_GYROSCOPE: mLastRotationRate = new nsDOMDeviceRotationRate(x, y, z); break; } if (!fireEvent && (!mLastAcceleration || !mLastAccelerationIncluduingGravity || !mLastRotationRate)) { return; } nsCOMPtr event; domdoc->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event)); nsCOMPtr me = do_QueryInterface(event); if (!me) return; me->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"), true, false, mLastAcceleration, mLastAccelerationIncluduingGravity, mLastRotationRate, DEFAULT_SENSOR_POLL); nsCOMPtr privateEvent = do_QueryInterface(event); if (privateEvent) privateEvent->SetTrusted(true); bool defaultActionEnabled = true; target->DispatchEvent(event, &defaultActionEnabled); mLastRotationRate = nsnull; mLastAccelerationIncluduingGravity = nsnull; mLastAcceleration = nsnull; mLastDOMMotionEventTime = TimeStamp::Now(); }