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 Geolocation.
15 : *
16 : * The Initial Developer of the Original Code is Mozilla Foundation
17 : * Portions created by the Initial Developer are Copyright (C) 2008
18 : * the Initial Developer. All Rights Reserved.
19 : *
20 : * Contributor(s):
21 : * Doug Turner <dougt@meer.net> (Original Author)
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 "nsContentPermissionHelper.h"
38 : #include "nsXULAppAPI.h"
39 :
40 : #include "mozilla/dom/PBrowserChild.h"
41 : #include "mozilla/dom/PBrowserParent.h"
42 : #include "mozilla/dom/ContentChild.h"
43 : #include "nsNetUtil.h"
44 :
45 : #include "nsFrameManager.h"
46 : #include "nsFrameLoader.h"
47 : #include "nsIFrameLoader.h"
48 :
49 : #include "nsIDocShellTreeOwner.h"
50 : #include "nsIDocShellTreeItem.h"
51 : #include "nsIWebProgressListener2.h"
52 :
53 : #include "nsDOMEventTargetHelper.h"
54 : #include "TabChild.h"
55 :
56 : #include "nsGeolocation.h"
57 : #include "nsAutoPtr.h"
58 : #include "nsCOMPtr.h"
59 : #include "nsIDOMWindow.h"
60 : #include "nsDOMClassInfoID.h"
61 : #include "nsComponentManagerUtils.h"
62 : #include "nsICategoryManager.h"
63 : #include "nsISupportsPrimitives.h"
64 : #include "nsServiceManagerUtils.h"
65 : #include "nsContentUtils.h"
66 : #include "nsIURI.h"
67 : #include "nsIPermissionManager.h"
68 : #include "nsIObserverService.h"
69 : #include "nsIPrefService.h"
70 : #include "nsIPrefBranch.h"
71 : #include "nsIJSContextStack.h"
72 : #include "nsThreadUtils.h"
73 : #include "mozilla/Services.h"
74 : #include "mozilla/unused.h"
75 : #include "mozilla/Preferences.h"
76 :
77 : #include <math.h>
78 :
79 : #ifdef MOZ_MAEMO_LIBLOCATION
80 : #include "MaemoLocationProvider.h"
81 : #endif
82 :
83 : #ifdef MOZ_ENABLE_QTMOBILITY
84 : #include "QTMLocationProvider.h"
85 : #endif
86 :
87 : #ifdef MOZ_WIDGET_ANDROID
88 : #include "AndroidLocationProvider.h"
89 : #endif
90 :
91 : #ifdef MOZ_WIDGET_GONK
92 : #include "GonkGPSGeolocationProvider.h"
93 : #endif
94 :
95 : #include "nsIDOMDocument.h"
96 : #include "nsIDocument.h"
97 :
98 : // Some limit to the number of get or watch geolocation requests
99 : // that a window can make.
100 : #define MAX_GEO_REQUESTS_PER_WINDOW 1500
101 :
102 : using mozilla::unused; // <snicker>
103 : using namespace mozilla;
104 : using namespace mozilla::dom;
105 :
106 : class RequestPromptEvent : public nsRunnable
107 0 : {
108 : public:
109 0 : RequestPromptEvent(nsGeolocationRequest* request)
110 0 : : mRequest(request)
111 : {
112 0 : }
113 :
114 0 : NS_IMETHOD Run() {
115 0 : nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
116 0 : if (prompt) {
117 0 : prompt->Prompt(mRequest);
118 : }
119 0 : return NS_OK;
120 : }
121 :
122 : private:
123 : nsRefPtr<nsGeolocationRequest> mRequest;
124 : };
125 :
126 : class RequestAllowEvent : public nsRunnable
127 0 : {
128 : public:
129 0 : RequestAllowEvent(int allow, nsGeolocationRequest* request)
130 : : mAllow(allow),
131 0 : mRequest(request)
132 : {
133 0 : }
134 :
135 0 : NS_IMETHOD Run() {
136 0 : if (mAllow)
137 0 : mRequest->Allow();
138 : else
139 0 : mRequest->Cancel();
140 0 : return NS_OK;
141 : }
142 :
143 : private:
144 : bool mAllow;
145 : nsRefPtr<nsGeolocationRequest> mRequest;
146 : };
147 :
148 : class RequestSendLocationEvent : public nsRunnable
149 8 : {
150 : public:
151 : // a bit funky. if locator is passed, that means this
152 : // event should remove the request from it. If we ever
153 : // have to do more, then we can change this around.
154 2 : RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
155 : nsGeolocationRequest* aRequest,
156 : nsGeolocation* aLocator)
157 : : mPosition(aPosition),
158 : mRequest(aRequest),
159 2 : mLocator(aLocator)
160 : {
161 2 : }
162 :
163 2 : NS_IMETHOD Run() {
164 2 : mRequest->SendLocation(mPosition);
165 2 : if (mLocator)
166 0 : mLocator->RemoveRequest(mRequest);
167 2 : return NS_OK;
168 : }
169 :
170 : private:
171 : nsCOMPtr<nsIDOMGeoPosition> mPosition;
172 : nsRefPtr<nsGeolocationRequest> mRequest;
173 :
174 : nsRefPtr<nsGeolocation> mLocator;
175 : };
176 :
177 : ////////////////////////////////////////////////////
178 : // nsDOMGeoPositionError
179 : ////////////////////////////////////////////////////
180 :
181 : class nsDOMGeoPositionError : public nsIDOMGeoPositionError
182 : {
183 : public:
184 : NS_DECL_ISUPPORTS
185 : NS_DECL_NSIDOMGEOPOSITIONERROR
186 :
187 : nsDOMGeoPositionError(PRInt16 aCode);
188 : void NotifyCallback(nsIDOMGeoPositionErrorCallback* callback);
189 :
190 : private:
191 : ~nsDOMGeoPositionError();
192 : PRInt16 mCode;
193 : };
194 :
195 : DOMCI_DATA(GeoPositionError, nsDOMGeoPositionError)
196 :
197 0 : NS_INTERFACE_MAP_BEGIN(nsDOMGeoPositionError)
198 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
199 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
200 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoPositionError)
201 0 : NS_INTERFACE_MAP_END
202 :
203 0 : NS_IMPL_THREADSAFE_ADDREF(nsDOMGeoPositionError)
204 0 : NS_IMPL_THREADSAFE_RELEASE(nsDOMGeoPositionError)
205 :
206 0 : nsDOMGeoPositionError::nsDOMGeoPositionError(PRInt16 aCode)
207 0 : : mCode(aCode)
208 : {
209 0 : }
210 :
211 0 : nsDOMGeoPositionError::~nsDOMGeoPositionError(){}
212 :
213 :
214 : NS_IMETHODIMP
215 0 : nsDOMGeoPositionError::GetCode(PRInt16 *aCode)
216 : {
217 0 : NS_ENSURE_ARG_POINTER(aCode);
218 0 : *aCode = mCode;
219 0 : return NS_OK;
220 : }
221 :
222 : void
223 0 : nsDOMGeoPositionError::NotifyCallback(nsIDOMGeoPositionErrorCallback* aCallback)
224 : {
225 0 : if (!aCallback)
226 0 : return;
227 :
228 : // Ensure that the proper context is on the stack (bug 452762)
229 0 : nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
230 0 : if (!stack || NS_FAILED(stack->Push(nsnull)))
231 : return;
232 :
233 0 : aCallback->HandleEvent(this);
234 :
235 : // remove the stack
236 : JSContext* cx;
237 0 : stack->Pop(&cx);
238 : }
239 : ////////////////////////////////////////////////////
240 : // nsGeolocationRequest
241 : ////////////////////////////////////////////////////
242 :
243 1 : nsGeolocationRequest::nsGeolocationRequest(nsGeolocation* aLocator,
244 : nsIDOMGeoPositionCallback* aCallback,
245 : nsIDOMGeoPositionErrorCallback* aErrorCallback,
246 : nsIDOMGeoPositionOptions* aOptions,
247 : bool aWatchPositionRequest)
248 : : mAllowed(false),
249 : mCleared(false),
250 : mIsWatchPositionRequest(aWatchPositionRequest),
251 : mCallback(aCallback),
252 : mErrorCallback(aErrorCallback),
253 : mOptions(aOptions),
254 1 : mLocator(aLocator)
255 : {
256 1 : }
257 :
258 2 : nsGeolocationRequest::~nsGeolocationRequest()
259 : {
260 4 : }
261 :
262 : nsresult
263 1 : nsGeolocationRequest::Init()
264 : {
265 : // This method is called before the user has given permission for this request.
266 1 : return NS_OK;
267 : }
268 :
269 16 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
270 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
271 0 : NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
272 0 : NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
273 0 : NS_INTERFACE_MAP_END
274 :
275 5 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
276 5 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
277 :
278 1468 : NS_IMPL_CYCLE_COLLECTION_4(nsGeolocationRequest, mCallback, mErrorCallback, mOptions, mLocator)
279 :
280 :
281 : void
282 0 : nsGeolocationRequest::NotifyError(PRInt16 errorCode)
283 : {
284 0 : nsRefPtr<nsDOMGeoPositionError> positionError = new nsDOMGeoPositionError(errorCode);
285 0 : if (!positionError)
286 : return;
287 :
288 0 : positionError->NotifyCallback(mErrorCallback);
289 : }
290 :
291 :
292 : NS_IMETHODIMP
293 0 : nsGeolocationRequest::Notify(nsITimer* aTimer)
294 : {
295 : // If we haven't gotten an answer from the geolocation
296 : // provider yet, cancel the request. Same logic as
297 : // ::Cancel, just a different error
298 :
299 : // remove ourselves from the locator's callback lists.
300 0 : mLocator->RemoveRequest(this);
301 0 : NotifyError(nsIDOMGeoPositionError::TIMEOUT);
302 :
303 0 : mTimeoutTimer = nsnull;
304 0 : return NS_OK;
305 : }
306 :
307 : NS_IMETHODIMP
308 0 : nsGeolocationRequest::GetUri(nsIURI * *aRequestingURI)
309 : {
310 0 : NS_ENSURE_ARG_POINTER(aRequestingURI);
311 :
312 0 : nsCOMPtr<nsIURI> uri = mLocator->GetURI();
313 0 : uri.forget(aRequestingURI);
314 :
315 0 : return NS_OK;
316 : }
317 :
318 : NS_IMETHODIMP
319 0 : nsGeolocationRequest::GetType(nsACString & aType)
320 : {
321 0 : aType = "geolocation";
322 0 : return NS_OK;
323 : }
324 :
325 : NS_IMETHODIMP
326 0 : nsGeolocationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow)
327 : {
328 0 : NS_ENSURE_ARG_POINTER(aRequestingWindow);
329 :
330 0 : nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mLocator->GetOwner());
331 0 : window.forget(aRequestingWindow);
332 :
333 0 : return NS_OK;
334 : }
335 :
336 : NS_IMETHODIMP
337 0 : nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
338 : {
339 0 : NS_ENSURE_ARG_POINTER(aRequestingElement);
340 0 : *aRequestingElement = nsnull;
341 0 : return NS_OK;
342 : }
343 :
344 : NS_IMETHODIMP
345 0 : nsGeolocationRequest::Cancel()
346 : {
347 : // remove ourselves from the locators callback lists.
348 0 : mLocator->RemoveRequest(this);
349 :
350 0 : NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
351 0 : return NS_OK;
352 : }
353 :
354 : NS_IMETHODIMP
355 1 : nsGeolocationRequest::Allow()
356 : {
357 2 : nsRefPtr<nsGeolocationService> geoService = nsGeolocationService::GetInstance();
358 :
359 : // Kick off the geo device, if it isn't already running
360 1 : nsresult rv = geoService->StartDevice();
361 :
362 1 : if (NS_FAILED(rv)) {
363 : // Location provider error
364 0 : NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
365 0 : return NS_OK;
366 : }
367 :
368 2 : nsCOMPtr<nsIDOMGeoPosition> lastPosition = geoService->GetCachedPosition();
369 : DOMTimeStamp cachedPositionTime;
370 1 : if (lastPosition)
371 0 : lastPosition->GetTimestamp(&cachedPositionTime);
372 :
373 : // check to see if we can use a cached value
374 : //
375 : // either:
376 : // a) the user has specified a maximumAge which allows us to return a cached value,
377 : // -or-
378 : // b) the cached position time is some reasonable value to return to the user (<30s)
379 :
380 1 : PRUint32 maximumAge = 30 * PR_MSEC_PER_SEC;
381 1 : if (mOptions) {
382 : PRInt32 tempAge;
383 0 : nsresult rv = mOptions->GetMaximumAge(&tempAge);
384 0 : if (NS_SUCCEEDED(rv)) {
385 0 : if (tempAge >= 0)
386 0 : maximumAge = tempAge;
387 : }
388 : }
389 :
390 1 : if (lastPosition && maximumAge > 0 &&
391 0 : ( PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
392 : PRTime(cachedPositionTime) )) {
393 : // okay, we can return a cached position
394 0 : mAllowed = true;
395 :
396 : nsCOMPtr<nsIRunnable> ev =
397 : new RequestSendLocationEvent(lastPosition, this,
398 0 : mIsWatchPositionRequest ? nsnull : mLocator);
399 0 : NS_DispatchToMainThread(ev);
400 : }
401 :
402 1 : SetTimeoutTimer();
403 :
404 1 : mAllowed = true;
405 1 : return NS_OK;
406 : }
407 :
408 : void
409 2 : nsGeolocationRequest::SetTimeoutTimer()
410 : {
411 2 : if (mTimeoutTimer) {
412 0 : mTimeoutTimer->Cancel();
413 0 : mTimeoutTimer = nsnull;
414 : }
415 : PRInt32 timeout;
416 2 : if (mOptions && NS_SUCCEEDED(mOptions->GetTimeout(&timeout)) && timeout > 0) {
417 :
418 0 : if (timeout < 10)
419 0 : timeout = 10;
420 :
421 0 : mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
422 0 : mTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
423 : }
424 2 : }
425 :
426 : void
427 1 : nsGeolocationRequest::MarkCleared()
428 : {
429 1 : if (mTimeoutTimer) {
430 0 : mTimeoutTimer->Cancel();
431 0 : mTimeoutTimer = nsnull;
432 : }
433 1 : mCleared = true;
434 1 : }
435 :
436 : void
437 2 : nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
438 : {
439 2 : if (mCleared || !mAllowed)
440 1 : return;
441 :
442 1 : if (mTimeoutTimer) {
443 0 : mTimeoutTimer->Cancel();
444 0 : mTimeoutTimer = nsnull;
445 : }
446 :
447 : // we should not pass null back to the DOM.
448 1 : if (!aPosition) {
449 0 : NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
450 0 : return;
451 : }
452 :
453 : // Ensure that the proper context is on the stack (bug 452762)
454 2 : nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
455 1 : if (!stack || NS_FAILED(stack->Push(nsnull)))
456 : return; // silently fail
457 :
458 1 : mCallback->HandleEvent(aPosition);
459 :
460 : // remove the stack
461 : JSContext* cx;
462 1 : stack->Pop(&cx);
463 :
464 1 : if (mIsWatchPositionRequest)
465 1 : SetTimeoutTimer();
466 : }
467 :
468 : bool
469 2 : nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
470 : {
471 2 : if (!mAllowed)
472 0 : return false;
473 : nsCOMPtr<nsIRunnable> ev =
474 : new RequestSendLocationEvent(aPosition, this,
475 6 : mIsWatchPositionRequest ? nsnull : mLocator);
476 2 : NS_DispatchToMainThread(ev);
477 2 : return true;
478 : }
479 :
480 : void
481 0 : nsGeolocationRequest::Shutdown()
482 : {
483 0 : if (mTimeoutTimer) {
484 0 : mTimeoutTimer->Cancel();
485 0 : mTimeoutTimer = nsnull;
486 : }
487 0 : mCleared = true;
488 0 : mCallback = nsnull;
489 0 : mErrorCallback = nsnull;
490 0 : }
491 :
492 0 : bool nsGeolocationRequest::Recv__delete__(const bool& allow)
493 : {
494 0 : if (allow)
495 0 : (void) Allow();
496 : else
497 0 : (void) Cancel();
498 0 : return true;
499 : }
500 : ////////////////////////////////////////////////////
501 : // nsGeolocationService
502 : ////////////////////////////////////////////////////
503 19 : NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
504 19 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
505 16 : NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
506 13 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
507 13 : NS_INTERFACE_MAP_END
508 :
509 14 : NS_IMPL_THREADSAFE_ADDREF(nsGeolocationService)
510 15 : NS_IMPL_THREADSAFE_RELEASE(nsGeolocationService)
511 :
512 :
513 : static bool sGeoEnabled = true;
514 : static bool sGeoIgnoreLocationFilter = false;
515 : static PRInt32 sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
516 :
517 1 : nsresult nsGeolocationService::Init()
518 : {
519 1 : Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
520 1 : Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
521 1 : Preferences::AddBoolVarCache(&sGeoIgnoreLocationFilter, "geo.ignore.location_filter", sGeoIgnoreLocationFilter);
522 :
523 1 : if (!sGeoEnabled)
524 0 : return NS_ERROR_FAILURE;
525 :
526 2 : nsCOMPtr<nsIGeolocationProvider> provider = do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
527 1 : if (provider)
528 1 : mProviders.AppendObject(provider);
529 :
530 : // look up any providers that were registered via the category manager
531 2 : nsCOMPtr<nsICategoryManager> catMan(do_GetService("@mozilla.org/categorymanager;1"));
532 1 : if (!catMan)
533 0 : return NS_ERROR_FAILURE;
534 :
535 : // geolocation service can be enabled -> now register observer
536 2 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
537 1 : if (!obs)
538 0 : return NS_ERROR_FAILURE;
539 :
540 1 : obs->AddObserver(this, "quit-application", false);
541 :
542 2 : nsCOMPtr<nsISimpleEnumerator> geoproviders;
543 1 : catMan->EnumerateCategory("geolocation-provider", getter_AddRefs(geoproviders));
544 1 : if (geoproviders) {
545 :
546 : bool hasMore;
547 3 : while (NS_SUCCEEDED(geoproviders->HasMoreElements(&hasMore)) && hasMore) {
548 2 : nsCOMPtr<nsISupports> elem;
549 1 : geoproviders->GetNext(getter_AddRefs(elem));
550 :
551 2 : nsCOMPtr<nsISupportsCString> elemString = do_QueryInterface(elem);
552 :
553 2 : nsCAutoString name;
554 1 : elemString->GetData(name);
555 :
556 2 : nsXPIDLCString spec;
557 1 : catMan->GetCategoryEntry("geolocation-provider", name.get(), getter_Copies(spec));
558 :
559 1 : provider = do_GetService(spec);
560 1 : if (provider)
561 1 : mProviders.AppendObject(provider);
562 : }
563 : }
564 :
565 : // we should move these providers outside of this file! dft
566 :
567 : #ifdef MOZ_MAEMO_LIBLOCATION
568 : provider = new MaemoLocationProvider();
569 : if (provider)
570 : mProviders.AppendObject(provider);
571 : #endif
572 :
573 : #ifdef MOZ_ENABLE_QTMOBILITY
574 : provider = new QTMLocationProvider();
575 : if (provider)
576 : mProviders.AppendObject(provider);
577 : #endif
578 :
579 : #ifdef MOZ_WIDGET_ANDROID
580 : provider = new AndroidLocationProvider();
581 : if (provider)
582 : mProviders.AppendObject(provider);
583 : #endif
584 :
585 : #ifdef MOZ_WIDGET_GONK
586 : provider = GonkGPSGeolocationProvider::GetSingleton();
587 : if (provider)
588 : mProviders.AppendObject(provider);
589 : #endif
590 :
591 1 : return NS_OK;
592 : }
593 :
594 1 : nsGeolocationService::~nsGeolocationService()
595 : {
596 1 : }
597 :
598 : NS_IMETHODIMP
599 1 : nsGeolocationService::Observe(nsISupports* aSubject,
600 : const char* aTopic,
601 : const PRUnichar* aData)
602 : {
603 1 : if (!strcmp("quit-application", aTopic))
604 : {
605 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
606 0 : if (obs) {
607 0 : obs->RemoveObserver(this, "quit-application");
608 : }
609 :
610 0 : for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
611 0 : mGeolocators[i]->Shutdown();
612 :
613 0 : StopDevice();
614 :
615 0 : return NS_OK;
616 : }
617 :
618 1 : if (!strcmp("timer-callback", aTopic))
619 : {
620 : // decide if we can close down the service.
621 2 : for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
622 1 : if (mGeolocators[i]->HasActiveCallbacks())
623 : {
624 0 : SetDisconnectTimer();
625 0 : return NS_OK;
626 : }
627 :
628 : // okay to close up.
629 1 : StopDevice();
630 1 : Update(nsnull);
631 1 : return NS_OK;
632 : }
633 :
634 0 : return NS_ERROR_FAILURE;
635 : }
636 :
637 : NS_IMETHODIMP
638 2 : nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
639 : {
640 2 : SetCachedPosition(aSomewhere);
641 :
642 4 : for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
643 2 : mGeolocators[i]->Update(aSomewhere);
644 2 : return NS_OK;
645 : }
646 :
647 : void
648 2 : nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
649 : {
650 2 : mLastPosition = aPosition;
651 2 : }
652 :
653 : nsIDOMGeoPosition*
654 1 : nsGeolocationService::GetCachedPosition()
655 : {
656 1 : return mLastPosition;
657 : }
658 :
659 : nsresult
660 1 : nsGeolocationService::StartDevice()
661 : {
662 1 : if (!sGeoEnabled)
663 0 : return NS_ERROR_NOT_AVAILABLE;
664 :
665 : // we do not want to keep the geolocation devices online
666 : // indefinitely. Close them down after a reasonable period of
667 : // inactivivity
668 1 : SetDisconnectTimer();
669 :
670 1 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
671 0 : ContentChild* cpc = ContentChild::GetSingleton();
672 0 : cpc->SendAddGeolocationListener();
673 0 : return NS_OK;
674 : }
675 :
676 : // Start them up!
677 2 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
678 1 : if (!obs)
679 0 : return NS_ERROR_FAILURE;
680 :
681 3 : for (PRInt32 i = 0; i < mProviders.Count(); i++) {
682 2 : mProviders[i]->Startup();
683 2 : mProviders[i]->Watch(this);
684 4 : obs->NotifyObservers(mProviders[i],
685 : "geolocation-device-events",
686 4 : NS_LITERAL_STRING("starting").get());
687 : }
688 :
689 1 : return NS_OK;
690 : }
691 :
692 : void
693 1 : nsGeolocationService::SetDisconnectTimer()
694 : {
695 1 : if (!mDisconnectTimer)
696 1 : mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
697 : else
698 0 : mDisconnectTimer->Cancel();
699 :
700 1 : mDisconnectTimer->Init(this,
701 : sProviderTimeout,
702 1 : nsITimer::TYPE_ONE_SHOT);
703 1 : }
704 :
705 : void
706 1 : nsGeolocationService::StopDevice()
707 : {
708 1 : if(mDisconnectTimer) {
709 1 : mDisconnectTimer->Cancel();
710 1 : mDisconnectTimer = nsnull;
711 : }
712 :
713 1 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
714 0 : ContentChild* cpc = ContentChild::GetSingleton();
715 0 : cpc->SendRemoveGeolocationListener();
716 0 : return; // bail early
717 : }
718 :
719 2 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
720 1 : if (!obs)
721 : return;
722 :
723 3 : for (PRInt32 i = 0; i < mProviders.Count(); i++) {
724 2 : mProviders[i]->Shutdown();
725 4 : obs->NotifyObservers(mProviders[i],
726 : "geolocation-device-events",
727 4 : NS_LITERAL_STRING("shutdown").get());
728 : }
729 : }
730 :
731 : nsGeolocationService* nsGeolocationService::gService = nsnull;
732 :
733 : nsGeolocationService*
734 3 : nsGeolocationService::GetInstance()
735 : {
736 3 : if (!nsGeolocationService::gService) {
737 1 : nsGeolocationService::gService = new nsGeolocationService();
738 1 : NS_ASSERTION(nsGeolocationService::gService, "null nsGeolocationService.");
739 :
740 1 : if (nsGeolocationService::gService) {
741 1 : if (NS_FAILED(nsGeolocationService::gService->Init())) {
742 0 : delete nsGeolocationService::gService;
743 0 : nsGeolocationService::gService = nsnull;
744 : }
745 : }
746 : }
747 3 : return nsGeolocationService::gService;
748 : }
749 :
750 : nsGeolocationService*
751 1 : nsGeolocationService::GetGeolocationService()
752 : {
753 1 : nsGeolocationService* inst = nsGeolocationService::GetInstance();
754 1 : NS_IF_ADDREF(inst);
755 1 : return inst;
756 : }
757 :
758 : void
759 1 : nsGeolocationService::AddLocator(nsGeolocation* aLocator)
760 : {
761 1 : mGeolocators.AppendElement(aLocator);
762 1 : }
763 :
764 : void
765 1 : nsGeolocationService::RemoveLocator(nsGeolocation* aLocator)
766 : {
767 1 : mGeolocators.RemoveElement(aLocator);
768 1 : }
769 :
770 : ////////////////////////////////////////////////////
771 : // nsGeolocation
772 : ////////////////////////////////////////////////////
773 :
774 : DOMCI_DATA(GeoGeolocation, nsGeolocation)
775 :
776 29 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocation)
777 9 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
778 8 : NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
779 5 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoGeolocation)
780 4 : NS_INTERFACE_MAP_END
781 :
782 9 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocation)
783 10 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocation)
784 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsGeolocation)
785 :
786 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGeolocation)
787 1 : tmp->mPendingCallbacks.Clear();
788 1 : tmp->mWatchingCallbacks.Clear();
789 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
790 :
791 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGeolocation)
792 : PRUint32 i;
793 2 : for (i = 0; i < tmp->mPendingCallbacks.Length(); ++i)
794 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPendingCallbacks[i], nsIContentPermissionRequest)
795 :
796 4 : for (i = 0; i < tmp->mWatchingCallbacks.Length(); ++i)
797 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mWatchingCallbacks[i], nsIContentPermissionRequest)
798 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
799 :
800 1 : nsGeolocation::nsGeolocation()
801 : {
802 1 : }
803 :
804 2 : nsGeolocation::~nsGeolocation()
805 : {
806 1 : if (mService)
807 1 : Shutdown();
808 1 : }
809 :
810 : nsresult
811 1 : nsGeolocation::Init(nsIDOMWindow* aContentDom)
812 : {
813 : // Remember the window
814 1 : if (aContentDom) {
815 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContentDom);
816 0 : if (!window)
817 0 : return NS_ERROR_FAILURE;
818 :
819 0 : mOwner = do_GetWeakReference(window->GetCurrentInnerWindow());
820 0 : if (!mOwner)
821 0 : return NS_ERROR_FAILURE;
822 :
823 : // Grab the uri of the document
824 0 : nsCOMPtr<nsIDOMDocument> domdoc;
825 0 : aContentDom->GetDocument(getter_AddRefs(domdoc));
826 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
827 0 : if (!doc)
828 0 : return NS_ERROR_FAILURE;
829 :
830 0 : doc->NodePrincipal()->GetURI(getter_AddRefs(mURI));
831 :
832 0 : if (!mURI)
833 0 : return NS_ERROR_FAILURE;
834 : }
835 :
836 : // If no aContentDom was passed into us, we are being used
837 : // by chrome/c++ and have no mOwner, no mURI, and no need
838 : // to prompt.
839 1 : mService = nsGeolocationService::GetInstance();
840 1 : if (mService)
841 1 : mService->AddLocator(this);
842 :
843 1 : return NS_OK;
844 : }
845 :
846 : void
847 1 : nsGeolocation::Shutdown()
848 : {
849 : // Shutdown and release all callbacks
850 1 : for (PRUint32 i = 0; i< mPendingCallbacks.Length(); i++)
851 0 : mPendingCallbacks[i]->Shutdown();
852 1 : mPendingCallbacks.Clear();
853 :
854 1 : for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++)
855 0 : mWatchingCallbacks[i]->Shutdown();
856 1 : mWatchingCallbacks.Clear();
857 :
858 1 : if (mService)
859 1 : mService->RemoveLocator(this);
860 :
861 1 : mService = nsnull;
862 1 : mURI = nsnull;
863 1 : }
864 :
865 : bool
866 1 : nsGeolocation::HasActiveCallbacks()
867 : {
868 2 : for (PRUint32 i = 0; i < mWatchingCallbacks.Length(); i++)
869 1 : if (mWatchingCallbacks[i]->IsActive())
870 0 : return true;
871 :
872 1 : return mPendingCallbacks.Length() != 0;
873 : }
874 :
875 : void
876 0 : nsGeolocation::RemoveRequest(nsGeolocationRequest* aRequest)
877 : {
878 0 : mPendingCallbacks.RemoveElement(aRequest);
879 :
880 : // if it is in the mWatchingCallbacks, we can't do much
881 : // since we passed back the position in the array to who
882 : // ever called WatchPosition() and we do not want to mess
883 : // around with the ordering of the array. Instead, just
884 : // mark the request as "cleared".
885 :
886 0 : aRequest->MarkCleared();
887 0 : }
888 :
889 : void
890 2 : nsGeolocation::Update(nsIDOMGeoPosition *aSomewhere)
891 : {
892 2 : if (!WindowOwnerStillExists())
893 0 : return Shutdown();
894 :
895 2 : for (PRUint32 i = mPendingCallbacks.Length(); i> 0; i--) {
896 0 : if (mPendingCallbacks[i-1]->Update(aSomewhere))
897 0 : mPendingCallbacks.RemoveElementAt(i-1);
898 : }
899 :
900 : // notify everyone that is watching
901 4 : for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++) {
902 2 : mWatchingCallbacks[i]->Update(aSomewhere);
903 : }
904 : }
905 :
906 : NS_IMETHODIMP
907 0 : nsGeolocation::GetCurrentPosition(nsIDOMGeoPositionCallback *callback,
908 : nsIDOMGeoPositionErrorCallback *errorCallback,
909 : nsIDOMGeoPositionOptions *options)
910 : {
911 0 : NS_ENSURE_ARG_POINTER(callback);
912 :
913 0 : if (!sGeoEnabled)
914 0 : return NS_ERROR_NOT_AVAILABLE;
915 :
916 0 : if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW)
917 0 : return NS_ERROR_NOT_AVAILABLE;
918 :
919 : nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
920 : callback,
921 : errorCallback,
922 : options,
923 0 : false);
924 0 : if (!request)
925 0 : return NS_ERROR_OUT_OF_MEMORY;
926 :
927 0 : if (NS_FAILED(request->Init()))
928 0 : return NS_ERROR_FAILURE; // this as OKAY. not sure why we wouldn't throw. xxx dft
929 :
930 0 : if (mOwner) {
931 0 : if (!RegisterRequestWithPrompt(request))
932 0 : return NS_ERROR_NOT_AVAILABLE;
933 :
934 0 : mPendingCallbacks.AppendElement(request);
935 0 : return NS_OK;
936 : }
937 :
938 0 : if (!nsContentUtils::IsCallerChrome())
939 0 : return NS_ERROR_FAILURE;
940 :
941 0 : mPendingCallbacks.AppendElement(request);
942 :
943 0 : nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, request);
944 0 : NS_DispatchToMainThread(ev);
945 :
946 0 : return NS_OK;
947 : }
948 :
949 : NS_IMETHODIMP
950 1 : nsGeolocation::WatchPosition(nsIDOMGeoPositionCallback *callback,
951 : nsIDOMGeoPositionErrorCallback *errorCallback,
952 : nsIDOMGeoPositionOptions *options,
953 : PRInt32 *_retval NS_OUTPARAM)
954 : {
955 :
956 1 : NS_ENSURE_ARG_POINTER(callback);
957 :
958 1 : if (!sGeoEnabled)
959 0 : return NS_ERROR_NOT_AVAILABLE;
960 :
961 1 : if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW)
962 0 : return NS_ERROR_NOT_AVAILABLE;
963 :
964 : nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
965 : callback,
966 : errorCallback,
967 : options,
968 2 : true);
969 1 : if (!request)
970 0 : return NS_ERROR_OUT_OF_MEMORY;
971 :
972 1 : if (NS_FAILED(request->Init()))
973 0 : return NS_ERROR_FAILURE; // this as OKAY. not sure why we wouldn't throw. xxx dft
974 :
975 1 : if (mOwner) {
976 0 : if (!RegisterRequestWithPrompt(request))
977 0 : return NS_ERROR_NOT_AVAILABLE;
978 :
979 : // need to hand back an index/reference.
980 0 : mWatchingCallbacks.AppendElement(request);
981 0 : *_retval = mWatchingCallbacks.Length() - 1;
982 :
983 0 : return NS_OK;
984 : }
985 :
986 1 : if (!nsContentUtils::IsCallerChrome())
987 0 : return NS_ERROR_FAILURE;
988 :
989 1 : request->Allow();
990 :
991 : // need to hand back an index/reference.
992 1 : mWatchingCallbacks.AppendElement(request);
993 1 : *_retval = mWatchingCallbacks.Length() - 1;
994 :
995 1 : return NS_OK;
996 : }
997 :
998 : NS_IMETHODIMP
999 1 : nsGeolocation::ClearWatch(PRInt32 aWatchId)
1000 : {
1001 1 : PRUint32 count = mWatchingCallbacks.Length();
1002 1 : if (aWatchId < 0 || count == 0 || PRUint32(aWatchId) >= count)
1003 0 : return NS_OK;
1004 :
1005 1 : mWatchingCallbacks[aWatchId]->MarkCleared();
1006 1 : return NS_OK;
1007 : }
1008 :
1009 : bool
1010 2 : nsGeolocation::WindowOwnerStillExists()
1011 : {
1012 : // an owner was never set when nsGeolocation
1013 : // was created, which means that this object
1014 : // is being used without a window.
1015 2 : if (mOwner == nsnull)
1016 2 : return true;
1017 :
1018 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
1019 :
1020 0 : if (window)
1021 : {
1022 0 : bool closed = false;
1023 0 : window->GetClosed(&closed);
1024 0 : if (closed)
1025 0 : return false;
1026 :
1027 0 : nsPIDOMWindow* outer = window->GetOuterWindow();
1028 0 : if (!outer || outer->GetCurrentInnerWindow() != window)
1029 0 : return false;
1030 : }
1031 :
1032 0 : return true;
1033 : }
1034 :
1035 : bool
1036 0 : nsGeolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
1037 : {
1038 0 : if (Preferences::GetBool("geo.prompt.testing", false)) {
1039 : nsCOMPtr<nsIRunnable> ev =
1040 : new RequestAllowEvent(Preferences::GetBool("geo.prompt.testing.allow",
1041 0 : false), request);
1042 0 : NS_DispatchToMainThread(ev);
1043 0 : return true;
1044 : }
1045 :
1046 0 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
1047 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
1048 0 : if (!window)
1049 0 : return true;
1050 :
1051 : // because owner implements nsITabChild, we can assume that it is
1052 : // the one and only TabChild.
1053 0 : TabChild* child = GetTabChildFrom(window->GetDocShell());
1054 0 : if (!child)
1055 0 : return false;
1056 :
1057 : // Retain a reference so the object isn't deleted without IPDL's knowledge.
1058 : // Corresponding release occurs in DeallocPContentPermissionRequest.
1059 0 : request->AddRef();
1060 :
1061 0 : nsCString type = NS_LITERAL_CSTRING("geolocation");
1062 0 : child->SendPContentPermissionRequestConstructor(request, type, IPC::URI(mURI));
1063 :
1064 0 : request->Sendprompt();
1065 0 : return true;
1066 : }
1067 :
1068 0 : nsCOMPtr<nsIRunnable> ev = new RequestPromptEvent(request);
1069 0 : NS_DispatchToMainThread(ev);
1070 0 : return true;
1071 4392 : }
1072 :
|