1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is mozilla.org code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Doug Turner <dougt@dougt.org>
18 : * Portions created by the Initial Developer are Copyright (C) 2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsDeviceMotion.h"
38 :
39 : #include "nsAutoPtr.h"
40 : #include "nsIDOMEvent.h"
41 : #include "nsIDOMWindow.h"
42 : #include "nsPIDOMWindow.h"
43 : #include "nsIDOMDocument.h"
44 : #include "nsIDOMEventTarget.h"
45 : #include "nsIServiceManager.h"
46 : #include "nsIPrivateDOMEvent.h"
47 : #include "nsIDOMDeviceOrientationEvent.h"
48 : #include "nsIDOMDeviceMotionEvent.h"
49 : #include "nsIServiceManager.h"
50 : #include "nsIPrefService.h"
51 : #include "nsDOMDeviceMotionEvent.h"
52 :
53 : static const nsTArray<nsIDOMWindow*>::index_type NoIndex =
54 : nsTArray<nsIDOMWindow*>::NoIndex;
55 :
56 : class nsDeviceMotionData : public nsIDeviceMotionData
57 : {
58 : public:
59 : NS_DECL_ISUPPORTS
60 : NS_DECL_NSIDEVICEMOTIONDATA
61 :
62 : nsDeviceMotionData(unsigned long type, double x, double y, double z);
63 :
64 : private:
65 : ~nsDeviceMotionData();
66 :
67 : protected:
68 : unsigned long mType;
69 : double mX, mY, mZ;
70 : };
71 :
72 0 : nsDeviceMotionData::nsDeviceMotionData(unsigned long type, double x, double y, double z)
73 0 : : mType(type), mX(x), mY(y), mZ(z)
74 : {
75 0 : }
76 :
77 0 : NS_INTERFACE_MAP_BEGIN(nsDeviceMotionData)
78 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDeviceMotionData)
79 0 : NS_INTERFACE_MAP_END
80 :
81 0 : NS_IMPL_ADDREF(nsDeviceMotionData)
82 0 : NS_IMPL_RELEASE(nsDeviceMotionData)
83 :
84 0 : nsDeviceMotionData::~nsDeviceMotionData()
85 : {
86 0 : }
87 :
88 0 : NS_IMETHODIMP nsDeviceMotionData::GetType(PRUint32 *aType)
89 : {
90 0 : NS_ENSURE_ARG_POINTER(aType);
91 0 : *aType = mType;
92 0 : return NS_OK;
93 : }
94 :
95 0 : NS_IMETHODIMP nsDeviceMotionData::GetX(double *aX)
96 : {
97 0 : NS_ENSURE_ARG_POINTER(aX);
98 0 : *aX = mX;
99 0 : return NS_OK;
100 : }
101 :
102 0 : NS_IMETHODIMP nsDeviceMotionData::GetY(double *aY)
103 : {
104 0 : NS_ENSURE_ARG_POINTER(aY);
105 0 : *aY = mY;
106 0 : return NS_OK;
107 : }
108 :
109 0 : NS_IMETHODIMP nsDeviceMotionData::GetZ(double *aZ)
110 : {
111 0 : NS_ENSURE_ARG_POINTER(aZ);
112 0 : *aZ = mZ;
113 0 : return NS_OK;
114 : }
115 :
116 0 : NS_IMPL_ISUPPORTS2(nsDeviceMotion, nsIDeviceMotion, nsIDeviceMotionUpdate)
117 :
118 0 : nsDeviceMotion::nsDeviceMotion()
119 : : mStarted(false),
120 : mUpdateInterval(50), /* default to 50 ms */
121 0 : mEnabled(true)
122 : {
123 0 : nsCOMPtr<nsIPrefBranch> prefSrv = do_GetService(NS_PREFSERVICE_CONTRACTID);
124 0 : if (prefSrv) {
125 : PRInt32 value;
126 0 : nsresult rv = prefSrv->GetIntPref("device.motion.update.interval", &value);
127 0 : if (NS_SUCCEEDED(rv))
128 0 : mUpdateInterval = value;
129 :
130 : bool bvalue;
131 0 : rv = prefSrv->GetBoolPref("device.motion.enabled", &bvalue);
132 0 : if (NS_SUCCEEDED(rv) && bvalue == false)
133 0 : mEnabled = false;
134 : }
135 0 : }
136 :
137 0 : nsDeviceMotion::~nsDeviceMotion()
138 : {
139 0 : if (mTimeoutTimer)
140 0 : mTimeoutTimer->Cancel();
141 0 : }
142 :
143 : void
144 0 : nsDeviceMotion::StartDisconnectTimer()
145 : {
146 0 : if (mTimeoutTimer)
147 0 : mTimeoutTimer->Cancel();
148 :
149 0 : mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
150 0 : if (mTimeoutTimer)
151 0 : mTimeoutTimer->InitWithFuncCallback(TimeoutHandler,
152 : this,
153 : 2000,
154 0 : nsITimer::TYPE_ONE_SHOT);
155 0 : }
156 :
157 : void
158 0 : nsDeviceMotion::TimeoutHandler(nsITimer *aTimer, void *aClosure)
159 : {
160 : // the reason that we use self, instead of just using nsITimerCallback or nsIObserver
161 : // is so that subclasses are free to use timers without worry about the base classes's
162 : // usage.
163 0 : nsDeviceMotion *self = reinterpret_cast<nsDeviceMotion *>(aClosure);
164 0 : if (!self) {
165 0 : NS_ERROR("no self");
166 0 : return;
167 : }
168 :
169 : // what about listeners that don't clean up properly? they will leak
170 0 : if (self->mListeners.Count() == 0 && self->mWindowListeners.Length() == 0) {
171 0 : self->Shutdown();
172 0 : self->mStarted = false;
173 : }
174 : }
175 :
176 0 : NS_IMETHODIMP nsDeviceMotion::AddListener(nsIDeviceMotionListener *aListener)
177 : {
178 0 : if (mListeners.IndexOf(aListener) != -1)
179 0 : return NS_OK; // already exists
180 :
181 0 : if (mStarted == false) {
182 0 : mStarted = true;
183 0 : Startup();
184 : }
185 :
186 0 : mListeners.AppendObject(aListener);
187 0 : return NS_OK;
188 : }
189 :
190 0 : NS_IMETHODIMP nsDeviceMotion::RemoveListener(nsIDeviceMotionListener *aListener)
191 : {
192 0 : if (mListeners.IndexOf(aListener) == -1)
193 0 : return NS_OK; // doesn't exist
194 :
195 0 : mListeners.RemoveObject(aListener);
196 0 : StartDisconnectTimer();
197 0 : return NS_OK;
198 : }
199 :
200 0 : NS_IMETHODIMP nsDeviceMotion::AddWindowListener(nsIDOMWindow *aWindow)
201 : {
202 0 : if (mWindowListeners.IndexOf(aWindow) != NoIndex)
203 0 : return NS_OK;
204 :
205 0 : if (mStarted == false) {
206 0 : mStarted = true;
207 0 : Startup();
208 : }
209 :
210 0 : mWindowListeners.AppendElement(aWindow);
211 0 : return NS_OK;
212 : }
213 :
214 0 : NS_IMETHODIMP nsDeviceMotion::RemoveWindowListener(nsIDOMWindow *aWindow)
215 : {
216 0 : if (mWindowListeners.IndexOf(aWindow) == NoIndex)
217 0 : return NS_OK;
218 :
219 0 : mWindowListeners.RemoveElement(aWindow);
220 0 : StartDisconnectTimer();
221 0 : return NS_OK;
222 : }
223 :
224 : NS_IMETHODIMP
225 0 : nsDeviceMotion::DeviceMotionChanged(PRUint32 type, double x, double y, double z)
226 : {
227 0 : if (!mEnabled)
228 0 : return NS_ERROR_NOT_INITIALIZED;
229 :
230 0 : nsCOMArray<nsIDeviceMotionListener> listeners = mListeners;
231 0 : for (PRUint32 i = listeners.Count(); i > 0 ; ) {
232 0 : --i;
233 0 : nsRefPtr<nsDeviceMotionData> a = new nsDeviceMotionData(type, x, y, z);
234 0 : listeners[i]->OnMotionChange(a);
235 : }
236 :
237 0 : nsCOMArray<nsIDOMWindow> windowListeners;
238 0 : for (PRUint32 i = 0; i < mWindowListeners.Length(); i++) {
239 0 : windowListeners.AppendObject(mWindowListeners[i]);
240 : }
241 :
242 0 : for (PRUint32 i = windowListeners.Count(); i > 0 ; ) {
243 0 : --i;
244 :
245 : // check to see if this window is in the background. if
246 : // it is, don't send any device motion to it.
247 0 : nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(windowListeners[i]);
248 0 : if (!pwindow ||
249 0 : !pwindow->GetOuterWindow() ||
250 0 : pwindow->GetOuterWindow()->IsBackground())
251 0 : continue;
252 :
253 0 : nsCOMPtr<nsIDOMDocument> domdoc;
254 0 : windowListeners[i]->GetDocument(getter_AddRefs(domdoc));
255 :
256 0 : if (domdoc) {
257 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(windowListeners[i]);
258 0 : if (type == nsIDeviceMotionData::TYPE_ACCELERATION)
259 0 : FireDOMMotionEvent(domdoc, target, x, y, z);
260 0 : else if (type == nsIDeviceMotionData::TYPE_ORIENTATION)
261 0 : FireDOMOrientationEvent(domdoc, target, x, y, z);
262 : }
263 : }
264 0 : return NS_OK;
265 : }
266 :
267 : void
268 0 : nsDeviceMotion::FireDOMOrientationEvent(nsIDOMDocument *domdoc,
269 : nsIDOMEventTarget *target,
270 : double alpha,
271 : double beta,
272 : double gamma)
273 : {
274 0 : nsCOMPtr<nsIDOMEvent> event;
275 0 : bool defaultActionEnabled = true;
276 0 : domdoc->CreateEvent(NS_LITERAL_STRING("DeviceOrientationEvent"), getter_AddRefs(event));
277 :
278 0 : nsCOMPtr<nsIDOMDeviceOrientationEvent> oe = do_QueryInterface(event);
279 :
280 0 : if (!oe) {
281 : return;
282 : }
283 :
284 0 : oe->InitDeviceOrientationEvent(NS_LITERAL_STRING("deviceorientation"),
285 : true,
286 : false,
287 : alpha,
288 : beta,
289 : gamma,
290 0 : true);
291 :
292 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
293 0 : if (privateEvent)
294 0 : privateEvent->SetTrusted(true);
295 :
296 0 : target->DispatchEvent(event, &defaultActionEnabled);
297 : }
298 :
299 :
300 : void
301 0 : nsDeviceMotion::FireDOMMotionEvent(nsIDOMDocument *domdoc,
302 : nsIDOMEventTarget *target,
303 : double x,
304 : double y,
305 : double z) {
306 0 : nsCOMPtr<nsIDOMEvent> event;
307 0 : bool defaultActionEnabled = true;
308 0 : domdoc->CreateEvent(NS_LITERAL_STRING("DeviceMotionEvent"), getter_AddRefs(event));
309 :
310 0 : nsCOMPtr<nsIDOMDeviceMotionEvent> me = do_QueryInterface(event);
311 :
312 0 : if (!me) {
313 : return;
314 : }
315 :
316 : // Currently acceleration as determined includes gravity.
317 0 : nsRefPtr<nsDOMDeviceAcceleration> acceleration = new nsDOMDeviceAcceleration(x, y, z);
318 :
319 0 : me->InitDeviceMotionEvent(NS_LITERAL_STRING("devicemotion"),
320 : true,
321 : false,
322 : nsnull,
323 : acceleration,
324 : nsnull,
325 0 : 0);
326 :
327 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
328 0 : if (privateEvent)
329 0 : privateEvent->SetTrusted(true);
330 :
331 0 : target->DispatchEvent(event, &defaultActionEnabled);
332 : }
333 :
|