Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/geolocation/nsGeolocation.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsGeolocation.h"
8
9
#include "mozilla/ClearOnShutdown.h"
10
#include "mozilla/CycleCollectedJSContext.h" // for nsAutoMicroTask
11
#include "mozilla/dom/ContentChild.h"
12
#include "mozilla/dom/PermissionMessageUtils.h"
13
#include "mozilla/dom/PositionError.h"
14
#include "mozilla/dom/PositionErrorBinding.h"
15
#include "mozilla/Preferences.h"
16
#include "mozilla/Services.h"
17
#include "mozilla/Telemetry.h"
18
#include "mozilla/UniquePtr.h"
19
#include "mozilla/Unused.h"
20
#include "mozilla/WeakPtr.h"
21
#include "mozilla/EventStateManager.h"
22
#include "nsComponentManagerUtils.h"
23
#include "nsContentPermissionHelper.h"
24
#include "nsContentUtils.h"
25
#include "nsGlobalWindow.h"
26
#include "nsIDocument.h"
27
#include "nsINamed.h"
28
#include "nsIObserverService.h"
29
#include "nsIScriptError.h"
30
#include "nsPIDOMWindow.h"
31
#include "nsServiceManagerUtils.h"
32
#include "nsThreadUtils.h"
33
#include "nsXULAppAPI.h"
34
35
class nsIPrincipal;
36
37
#ifdef MOZ_WIDGET_ANDROID
38
#include "AndroidLocationProvider.h"
39
#endif
40
41
#ifdef MOZ_GPSD
42
#include "GpsdLocationProvider.h"
43
#endif
44
45
#ifdef MOZ_WIDGET_COCOA
46
#include "CoreLocationLocationProvider.h"
47
#endif
48
49
#ifdef XP_WIN
50
#include "WindowsLocationProvider.h"
51
#include "mozilla/WindowsVersion.h"
52
#endif
53
54
// Some limit to the number of get or watch geolocation requests
55
// that a window can make.
56
0
#define MAX_GEO_REQUESTS_PER_WINDOW  1500
57
58
// This preference allows to override the "secure context" by
59
// default policy.
60
0
#define PREF_GEO_SECURITY_ALLOWINSECURE "geo.security.allowinsecure"
61
62
using mozilla::Unused;          // <snicker>
63
using namespace mozilla;
64
using namespace mozilla::dom;
65
66
class nsGeolocationRequest final
67
 : public nsIContentPermissionRequest
68
 , public nsIGeolocationUpdate
69
 , public SupportsWeakPtr<nsGeolocationRequest>
70
{
71
 public:
72
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
73
  NS_DECL_NSICONTENTPERMISSIONREQUEST
74
  NS_DECL_NSIGEOLOCATIONUPDATE
75
76
  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
77
78
  nsGeolocationRequest(Geolocation* aLocator,
79
                       GeoPositionCallback aCallback,
80
                       GeoPositionErrorCallback aErrorCallback,
81
                       UniquePtr<PositionOptions>&& aOptions,
82
                       uint8_t aProtocolType,
83
                       nsIEventTarget* aMainThreadTarget,
84
                       bool aWatchPositionRequest = false,
85
                       bool aIsHandlingUserInput = false,
86
                       int32_t aWatchId = 0);
87
88
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest)
89
90
  void Shutdown();
91
92
  void SendLocation(nsIDOMGeoPosition* aLocation);
93
0
  bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
94
  void SetTimeoutTimer();
95
  void StopTimeoutTimer();
96
  void NotifyErrorAndShutdown(uint16_t);
97
  nsIPrincipal* GetPrincipal();
98
99
0
  bool IsWatch() { return mIsWatchPositionRequest; }
100
0
  int32_t WatchId() { return mWatchId; }
101
 private:
102
  virtual ~nsGeolocationRequest();
103
104
  class TimerCallbackHolder final : public nsITimerCallback
105
                                  , public nsINamed
106
  {
107
  public:
108
    NS_DECL_ISUPPORTS
109
    NS_DECL_NSITIMERCALLBACK
110
111
    explicit TimerCallbackHolder(nsGeolocationRequest* aRequest)
112
      : mRequest(aRequest)
113
0
    {}
114
115
    NS_IMETHOD GetName(nsACString& aName) override
116
0
    {
117
0
      aName.AssignLiteral("nsGeolocationRequest::TimerCallbackHolder");
118
0
      return NS_OK;
119
0
    }
120
121
  private:
122
0
    ~TimerCallbackHolder() = default;
123
    WeakPtr<nsGeolocationRequest> mRequest;
124
  };
125
126
  void Notify();
127
128
  bool mIsWatchPositionRequest;
129
130
  nsCOMPtr<nsITimer> mTimeoutTimer;
131
  GeoPositionCallback mCallback;
132
  GeoPositionErrorCallback mErrorCallback;
133
  UniquePtr<PositionOptions> mOptions;
134
  bool mIsHandlingUserInput;
135
136
  RefPtr<Geolocation> mLocator;
137
138
  int32_t mWatchId;
139
  bool mShutdown;
140
  nsCOMPtr<nsIContentPermissionRequester> mRequester;
141
  uint8_t mProtocolType;
142
  nsCOMPtr<nsIEventTarget> mMainThreadTarget;
143
};
144
145
static UniquePtr<PositionOptions>
146
CreatePositionOptionsCopy(const PositionOptions& aOptions)
147
0
{
148
0
  UniquePtr<PositionOptions> geoOptions = MakeUnique<PositionOptions>();
149
0
150
0
  geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
151
0
  geoOptions->mMaximumAge = aOptions.mMaximumAge;
152
0
  geoOptions->mTimeout = aOptions.mTimeout;
153
0
154
0
  return geoOptions;
155
0
}
156
157
class RequestPromptEvent : public Runnable
158
{
159
public:
160
  RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
161
    : mozilla::Runnable("RequestPromptEvent")
162
    , mRequest(aRequest)
163
    , mWindow(aWindow)
164
0
  {
165
0
  }
166
167
  NS_IMETHOD Run() override
168
0
  {
169
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
170
0
    nsContentPermissionUtils::AskPermission(mRequest, window);
171
0
    return NS_OK;
172
0
  }
173
174
private:
175
  RefPtr<nsGeolocationRequest> mRequest;
176
  nsWeakPtr mWindow;
177
};
178
179
class RequestAllowEvent : public Runnable
180
{
181
public:
182
  RequestAllowEvent(int allow, nsGeolocationRequest* request)
183
    : mozilla::Runnable("RequestAllowEvent")
184
    , mAllow(allow)
185
    , mRequest(request)
186
0
  {
187
0
  }
188
189
0
  NS_IMETHOD Run() override {
190
0
    if (mAllow) {
191
0
      mRequest->Allow(JS::UndefinedHandleValue);
192
0
    } else {
193
0
      mRequest->Cancel();
194
0
    }
195
0
    return NS_OK;
196
0
  }
197
198
private:
199
  bool mAllow;
200
  RefPtr<nsGeolocationRequest> mRequest;
201
};
202
203
class RequestSendLocationEvent : public Runnable
204
{
205
public:
206
  RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
207
                           nsGeolocationRequest* aRequest)
208
    : mozilla::Runnable("RequestSendLocationEvent")
209
    , mPosition(aPosition)
210
    , mRequest(aRequest)
211
0
  {
212
0
  }
213
214
0
  NS_IMETHOD Run() override {
215
0
    mRequest->SendLocation(mPosition);
216
0
    return NS_OK;
217
0
  }
218
219
private:
220
  nsCOMPtr<nsIDOMGeoPosition> mPosition;
221
  RefPtr<nsGeolocationRequest> mRequest;
222
  RefPtr<Geolocation> mLocator;
223
};
224
225
////////////////////////////////////////////////////
226
// nsGeolocationRequest
227
////////////////////////////////////////////////////
228
229
nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
230
                                           GeoPositionCallback aCallback,
231
                                           GeoPositionErrorCallback aErrorCallback,
232
                                           UniquePtr<PositionOptions>&& aOptions,
233
                                           uint8_t aProtocolType,
234
                                           nsIEventTarget* aMainThreadTarget,
235
                                           bool aWatchPositionRequest,
236
                                           bool aIsHandlingUserInput,
237
                                           int32_t aWatchId)
238
  : mIsWatchPositionRequest(aWatchPositionRequest),
239
    mCallback(std::move(aCallback)),
240
    mErrorCallback(std::move(aErrorCallback)),
241
    mOptions(std::move(aOptions)),
242
    mIsHandlingUserInput(aIsHandlingUserInput),
243
    mLocator(aLocator),
244
    mWatchId(aWatchId),
245
    mShutdown(false),
246
    mProtocolType(aProtocolType),
247
    mMainThreadTarget(aMainThreadTarget)
248
0
{
249
0
  if (nsCOMPtr<nsPIDOMWindowInner> win =
250
0
      do_QueryReferent(mLocator->GetOwner())) {
251
0
    mRequester = new nsContentPermissionRequester(win);
252
0
  }
253
0
}
254
255
nsGeolocationRequest::~nsGeolocationRequest()
256
0
{
257
0
  StopTimeoutTimer();
258
0
}
259
260
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
261
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
262
0
  NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
263
0
  NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
264
0
NS_INTERFACE_MAP_END
265
266
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
267
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
268
NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
269
270
void
271
nsGeolocationRequest::Notify()
272
0
{
273
0
  SetTimeoutTimer();
274
0
  NotifyErrorAndShutdown(PositionError_Binding::TIMEOUT);
275
0
}
276
277
void
278
nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
279
0
{
280
0
  MOZ_ASSERT(!mShutdown, "timeout after shutdown");
281
0
  if (!mIsWatchPositionRequest) {
282
0
    Shutdown();
283
0
    mLocator->RemoveRequest(this);
284
0
  }
285
0
286
0
  NotifyError(aErrorCode);
287
0
}
288
289
NS_IMETHODIMP
290
nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
291
0
{
292
0
  NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
293
0
294
0
  nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
295
0
  principal.forget(aRequestingPrincipal);
296
0
297
0
  return NS_OK;
298
0
}
299
300
NS_IMETHODIMP
301
nsGeolocationRequest::GetTypes(nsIArray** aTypes)
302
0
{
303
0
  nsTArray<nsString> emptyOptions;
304
0
  return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
305
0
                                                         NS_LITERAL_CSTRING("unused"),
306
0
                                                         emptyOptions,
307
0
                                                         aTypes);
308
0
}
309
310
NS_IMETHODIMP
311
nsGeolocationRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
312
0
{
313
0
  NS_ENSURE_ARG_POINTER(aRequestingWindow);
314
0
315
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mLocator->GetOwner());
316
0
  window.forget(aRequestingWindow);
317
0
318
0
  return NS_OK;
319
0
}
320
321
NS_IMETHODIMP
322
nsGeolocationRequest::GetElement(Element** aRequestingElement)
323
0
{
324
0
  NS_ENSURE_ARG_POINTER(aRequestingElement);
325
0
  *aRequestingElement = nullptr;
326
0
  return NS_OK;
327
0
}
328
329
NS_IMETHODIMP
330
nsGeolocationRequest::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
331
0
{
332
0
  *aIsHandlingUserInput = mIsHandlingUserInput;
333
0
  return NS_OK;
334
0
}
335
336
NS_IMETHODIMP
337
nsGeolocationRequest::Cancel()
338
0
{
339
0
  if (mRequester) {
340
0
    // Record the number of denied requests for regular web content.
341
0
    // This method is only called when the user explicitly denies the request,
342
0
    // and is not called when the page is simply unloaded, or similar.
343
0
    Telemetry::Accumulate(Telemetry::GEOLOCATION_REQUEST_GRANTED, mProtocolType);
344
0
  }
345
0
346
0
  if (mLocator->ClearPendingRequest(this)) {
347
0
    return NS_OK;
348
0
  }
349
0
350
0
  NotifyError(PositionError_Binding::PERMISSION_DENIED);
351
0
  return NS_OK;
352
0
}
353
354
NS_IMETHODIMP
355
nsGeolocationRequest::Allow(JS::HandleValue aChoices)
356
0
{
357
0
  MOZ_ASSERT(aChoices.isUndefined());
358
0
359
0
  if (mRequester) {
360
0
    // Record the number of granted requests for regular web content.
361
0
    Telemetry::Accumulate(Telemetry::GEOLOCATION_REQUEST_GRANTED, mProtocolType + 10);
362
0
363
0
    // Record whether a location callback is fulfilled while the owner window
364
0
    // is not visible.
365
0
    bool isVisible = false;
366
0
    nsCOMPtr<nsPIDOMWindowInner> window = mLocator->GetParentObject();
367
0
368
0
    if (window) {
369
0
      nsCOMPtr<nsIDocument> doc = window->GetDoc();
370
0
      isVisible = doc && !doc->Hidden();
371
0
    }
372
0
373
0
    if (IsWatch()) {
374
0
      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_WATCHPOSITION_VISIBLE, isVisible);
375
0
    } else {
376
0
      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_GETCURRENTPOSITION_VISIBLE, isVisible);
377
0
    }
378
0
  }
379
0
380
0
  if (mLocator->ClearPendingRequest(this)) {
381
0
    return NS_OK;
382
0
  }
383
0
384
0
  RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
385
0
386
0
  bool canUseCache = false;
387
0
  CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
388
0
  if (lastPosition.position) {
389
0
    DOMTimeStamp cachedPositionTime_ms;
390
0
    lastPosition.position->GetTimestamp(&cachedPositionTime_ms);
391
0
    // check to see if we can use a cached value
392
0
    // if the user has specified a maximumAge, return a cached value.
393
0
    if (mOptions && mOptions->mMaximumAge > 0) {
394
0
      uint32_t maximumAge_ms = mOptions->mMaximumAge;
395
0
      bool isCachedWithinRequestedAccuracy = WantsHighAccuracy() <= lastPosition.isHighAccuracy;
396
0
      bool isCachedWithinRequestedTime =
397
0
        DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) <= cachedPositionTime_ms;
398
0
      canUseCache = isCachedWithinRequestedAccuracy && isCachedWithinRequestedTime;
399
0
    }
400
0
  }
401
0
402
0
  gs->UpdateAccuracy(WantsHighAccuracy());
403
0
  if (canUseCache) {
404
0
    // okay, we can return a cached position
405
0
    // getCurrentPosition requests serviced by the cache
406
0
    // will now be owned by the RequestSendLocationEvent
407
0
    Update(lastPosition.position);
408
0
409
0
    // After Update is called, getCurrentPosition finishes it's job.
410
0
    if (!mIsWatchPositionRequest) {
411
0
      return NS_OK;
412
0
    }
413
0
414
0
  } else {
415
0
    // if it is not a watch request and timeout is 0,
416
0
    // invoke the errorCallback (if present) with TIMEOUT code
417
0
    if (mOptions && mOptions->mTimeout == 0 && !mIsWatchPositionRequest) {
418
0
      NotifyError(PositionError_Binding::TIMEOUT);
419
0
      return NS_OK;
420
0
    }
421
0
422
0
  }
423
0
424
0
  // Kick off the geo device, if it isn't already running
425
0
  nsresult rv = gs->StartDevice(GetPrincipal());
426
0
427
0
  if (NS_FAILED(rv)) {
428
0
    // Location provider error
429
0
    NotifyError(PositionError_Binding::POSITION_UNAVAILABLE);
430
0
    return NS_OK;
431
0
  }
432
0
433
0
  if (mIsWatchPositionRequest || !canUseCache) {
434
0
    // let the locator know we're pending
435
0
    // we will now be owned by the locator
436
0
    mLocator->NotifyAllowedRequest(this);
437
0
  }
438
0
439
0
  SetTimeoutTimer();
440
0
441
0
  return NS_OK;
442
0
}
443
444
NS_IMETHODIMP
445
nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
446
0
{
447
0
  NS_ENSURE_ARG_POINTER(aRequester);
448
0
449
0
  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
450
0
  requester.forget(aRequester);
451
0
452
0
  return NS_OK;
453
0
}
454
455
void
456
nsGeolocationRequest::SetTimeoutTimer()
457
0
{
458
0
  MOZ_ASSERT(!mShutdown, "set timeout after shutdown");
459
0
460
0
  StopTimeoutTimer();
461
0
462
0
  if (mOptions && mOptions->mTimeout != 0 && mOptions->mTimeout != 0x7fffffff) {
463
0
    RefPtr<TimerCallbackHolder> holder = new TimerCallbackHolder(this);
464
0
    NS_NewTimerWithCallback(getter_AddRefs(mTimeoutTimer),
465
0
                            holder, mOptions->mTimeout, nsITimer::TYPE_ONE_SHOT);
466
0
  }
467
0
}
468
469
void
470
nsGeolocationRequest::StopTimeoutTimer()
471
0
{
472
0
  if (mTimeoutTimer) {
473
0
    mTimeoutTimer->Cancel();
474
0
    mTimeoutTimer = nullptr;
475
0
  }
476
0
}
477
478
void
479
nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
480
0
{
481
0
  if (mShutdown) {
482
0
    // Ignore SendLocationEvents issued before we were cleared.
483
0
    return;
484
0
  }
485
0
486
0
  if (mOptions && mOptions->mMaximumAge > 0) {
487
0
    DOMTimeStamp positionTime_ms;
488
0
    aPosition->GetTimestamp(&positionTime_ms);
489
0
    const uint32_t maximumAge_ms = mOptions->mMaximumAge;
490
0
    const bool isTooOld =
491
0
        DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) > positionTime_ms;
492
0
    if (isTooOld) {
493
0
      return;
494
0
    }
495
0
  }
496
0
497
0
  RefPtr<mozilla::dom::Position> wrapped;
498
0
499
0
  if (aPosition) {
500
0
    nsCOMPtr<nsIDOMGeoPositionCoords> coords;
501
0
    aPosition->GetCoords(getter_AddRefs(coords));
502
0
    if (coords) {
503
0
      wrapped = new mozilla::dom::Position(ToSupports(mLocator), aPosition);
504
0
    }
505
0
  }
506
0
507
0
  if (!wrapped) {
508
0
    NotifyError(PositionError_Binding::POSITION_UNAVAILABLE);
509
0
    return;
510
0
  }
511
0
512
0
  if (!mIsWatchPositionRequest) {
513
0
    // Cancel timer and position updates in case the position
514
0
    // callback spins the event loop
515
0
    Shutdown();
516
0
  }
517
0
518
0
  nsAutoMicroTask mt;
519
0
  if (mCallback.HasWebIDLCallback()) {
520
0
    PositionCallback* callback = mCallback.GetWebIDLCallback();
521
0
522
0
    MOZ_ASSERT(callback);
523
0
    callback->Call(*wrapped);
524
0
  } else {
525
0
    nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
526
0
    MOZ_ASSERT(callback);
527
0
    callback->HandleEvent(aPosition);
528
0
  }
529
0
530
0
  if (mIsWatchPositionRequest && !mShutdown) {
531
0
    SetTimeoutTimer();
532
0
  }
533
0
  MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
534
0
             "non-shutdown getCurrentPosition request after callback!");
535
0
}
536
537
nsIPrincipal*
538
nsGeolocationRequest::GetPrincipal()
539
0
{
540
0
  if (!mLocator) {
541
0
    return nullptr;
542
0
  }
543
0
  return mLocator->GetPrincipal();
544
0
}
545
546
NS_IMETHODIMP
547
nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
548
0
{
549
0
  nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
550
0
  mMainThreadTarget->Dispatch(ev.forget());
551
0
  return NS_OK;
552
0
}
553
554
NS_IMETHODIMP
555
nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
556
0
{
557
0
  MOZ_ASSERT(NS_IsMainThread());
558
0
  RefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
559
0
  positionError->NotifyCallback(mErrorCallback);
560
0
  return NS_OK;
561
0
}
562
563
void
564
nsGeolocationRequest::Shutdown()
565
0
{
566
0
  MOZ_ASSERT(!mShutdown, "request shutdown twice");
567
0
  mShutdown = true;
568
0
569
0
  StopTimeoutTimer();
570
0
571
0
  // If there are no other high accuracy requests, the geolocation service will
572
0
  // notify the provider to switch to the default accuracy.
573
0
  if (mOptions && mOptions->mEnableHighAccuracy) {
574
0
    RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
575
0
    if (gs) {
576
0
      gs->UpdateAccuracy();
577
0
    }
578
0
  }
579
0
}
580
581
582
////////////////////////////////////////////////////
583
// nsGeolocationRequest::TimerCallbackHolder
584
////////////////////////////////////////////////////
585
586
NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder,
587
                  nsITimerCallback,
588
                  nsINamed)
589
590
NS_IMETHODIMP
591
nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
592
0
{
593
0
  if (mRequest && mRequest->mLocator) {
594
0
    RefPtr<nsGeolocationRequest> request(mRequest);
595
0
    request->Notify();
596
0
  }
597
0
598
0
  return NS_OK;
599
0
}
600
601
602
////////////////////////////////////////////////////
603
// nsGeolocationService
604
////////////////////////////////////////////////////
605
0
NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
606
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
607
0
  NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
608
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
609
0
NS_INTERFACE_MAP_END
610
611
NS_IMPL_ADDREF(nsGeolocationService)
612
NS_IMPL_RELEASE(nsGeolocationService)
613
614
615
static bool sGeoEnabled = true;
616
static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
617
618
nsresult nsGeolocationService::Init()
619
0
{
620
0
  Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
621
0
  Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
622
0
623
0
  if (!sGeoEnabled) {
624
0
    return NS_ERROR_FAILURE;
625
0
  }
626
0
627
0
  if (XRE_IsContentProcess()) {
628
0
    return NS_OK;
629
0
  }
630
0
631
0
  // geolocation service can be enabled -> now register observer
632
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
633
0
  if (!obs) {
634
0
    return NS_ERROR_FAILURE;
635
0
  }
636
0
637
0
  obs->AddObserver(this, "xpcom-shutdown", false);
638
0
639
#ifdef MOZ_WIDGET_ANDROID
640
  mProvider = new AndroidLocationProvider();
641
#endif
642
643
0
#ifdef MOZ_WIDGET_GTK
644
#ifdef MOZ_GPSD
645
  if (Preferences::GetBool("geo.provider.use_gpsd", false)) {
646
    mProvider = new GpsdLocationProvider();
647
  }
648
#endif
649
#endif
650
0
651
#ifdef MOZ_WIDGET_COCOA
652
  if (Preferences::GetBool("geo.provider.use_corelocation", true)) {
653
    mProvider = new CoreLocationLocationProvider();
654
  }
655
#endif
656
657
#ifdef XP_WIN
658
  if (Preferences::GetBool("geo.provider.ms-windows-location", false) &&
659
      IsWin8OrLater()) {
660
    mProvider = new WindowsLocationProvider();
661
  }
662
#endif
663
664
0
  if (Preferences::GetBool("geo.provider.use_mls", false)) {
665
0
    mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
666
0
  }
667
0
668
0
  // Override platform-specific providers with the default (network)
669
0
  // provider while testing. Our tests are currently not meant to exercise
670
0
  // the provider, and some tests rely on the network provider being used.
671
0
  // "geo.provider.testing" is always set for all plain and browser chrome
672
0
  // mochitests, and also for xpcshell tests.
673
0
  if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) {
674
0
    nsCOMPtr<nsIGeolocationProvider> geoTestProvider =
675
0
      do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
676
0
677
0
    if (geoTestProvider) {
678
0
      mProvider = geoTestProvider;
679
0
    }
680
0
  }
681
0
682
0
  return NS_OK;
683
0
}
684
685
0
nsGeolocationService::~nsGeolocationService() = default;
686
687
NS_IMETHODIMP
688
nsGeolocationService::Observe(nsISupports* aSubject,
689
                              const char* aTopic,
690
                              const char16_t* aData)
691
0
{
692
0
  if (!strcmp("xpcom-shutdown", aTopic)) {
693
0
    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
694
0
    if (obs) {
695
0
      obs->RemoveObserver(this, "xpcom-shutdown");
696
0
    }
697
0
698
0
    for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
699
0
      mGeolocators[i]->Shutdown();
700
0
    }
701
0
    StopDevice();
702
0
703
0
    return NS_OK;
704
0
  }
705
0
706
0
  if (!strcmp("timer-callback", aTopic)) {
707
0
    // decide if we can close down the service.
708
0
    for (uint32_t i = 0; i< mGeolocators.Length(); i++)
709
0
      if (mGeolocators[i]->HasActiveCallbacks()) {
710
0
        SetDisconnectTimer();
711
0
        return NS_OK;
712
0
      }
713
0
714
0
    // okay to close up.
715
0
    StopDevice();
716
0
    Update(nullptr);
717
0
    return NS_OK;
718
0
  }
719
0
720
0
  return NS_ERROR_FAILURE;
721
0
}
722
723
NS_IMETHODIMP
724
nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
725
0
{
726
0
  if (aSomewhere) {
727
0
    SetCachedPosition(aSomewhere);
728
0
  }
729
0
730
0
  for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
731
0
    mGeolocators[i]->Update(aSomewhere);
732
0
  }
733
0
734
0
  return NS_OK;
735
0
}
736
737
NS_IMETHODIMP
738
nsGeolocationService::NotifyError(uint16_t aErrorCode)
739
0
{
740
0
  for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
741
0
    mGeolocators[i]->NotifyError(aErrorCode);
742
0
  }
743
0
  return NS_OK;
744
0
}
745
746
void
747
nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
748
0
{
749
0
  mLastPosition.position = aPosition;
750
0
  mLastPosition.isHighAccuracy = mHigherAccuracy;
751
0
}
752
753
CachedPositionAndAccuracy
754
nsGeolocationService::GetCachedPosition()
755
0
{
756
0
  return mLastPosition;
757
0
}
758
759
nsresult
760
nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
761
0
{
762
0
  if (!sGeoEnabled) {
763
0
    return NS_ERROR_NOT_AVAILABLE;
764
0
  }
765
0
766
0
  // We do not want to keep the geolocation devices online
767
0
  // indefinitely.
768
0
  // Close them down after a reasonable period of inactivivity.
769
0
  SetDisconnectTimer();
770
0
771
0
  if (XRE_IsContentProcess()) {
772
0
    ContentChild* cpc = ContentChild::GetSingleton();
773
0
    cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
774
0
                                    HighAccuracyRequested());
775
0
    return NS_OK;
776
0
  }
777
0
778
0
  // Start them up!
779
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
780
0
  if (!obs) {
781
0
    return NS_ERROR_FAILURE;
782
0
  }
783
0
784
0
  if (!mProvider) {
785
0
    return NS_ERROR_FAILURE;
786
0
  }
787
0
788
0
  nsresult rv;
789
0
790
0
  if (NS_FAILED(rv = mProvider->Startup()) ||
791
0
      NS_FAILED(rv = mProvider->Watch(this))) {
792
0
793
0
    NotifyError(PositionError_Binding::POSITION_UNAVAILABLE);
794
0
    return rv;
795
0
  }
796
0
797
0
  obs->NotifyObservers(mProvider,
798
0
                       "geolocation-device-events",
799
0
                       u"starting");
800
0
801
0
  return NS_OK;
802
0
}
803
804
void
805
nsGeolocationService::SetDisconnectTimer()
806
0
{
807
0
  if (!mDisconnectTimer) {
808
0
    mDisconnectTimer = NS_NewTimer();
809
0
  } else {
810
0
    mDisconnectTimer->Cancel();
811
0
  }
812
0
813
0
  mDisconnectTimer->Init(this,
814
0
                         sProviderTimeout,
815
0
                         nsITimer::TYPE_ONE_SHOT);
816
0
}
817
818
bool
819
nsGeolocationService::HighAccuracyRequested()
820
0
{
821
0
  for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
822
0
    if (mGeolocators[i]->HighAccuracyRequested()) {
823
0
      return true;
824
0
    }
825
0
  }
826
0
827
0
  return false;
828
0
}
829
830
void
831
nsGeolocationService::UpdateAccuracy(bool aForceHigh)
832
0
{
833
0
  bool highRequired = aForceHigh || HighAccuracyRequested();
834
0
835
0
  if (XRE_IsContentProcess()) {
836
0
    ContentChild* cpc = ContentChild::GetSingleton();
837
0
    if (cpc->IsAlive()) {
838
0
      cpc->SendSetGeolocationHigherAccuracy(highRequired);
839
0
    }
840
0
841
0
    return;
842
0
  }
843
0
844
0
  mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
845
0
  mHigherAccuracy = highRequired;
846
0
}
847
848
void
849
nsGeolocationService::StopDevice()
850
0
{
851
0
  if (mDisconnectTimer) {
852
0
    mDisconnectTimer->Cancel();
853
0
    mDisconnectTimer = nullptr;
854
0
  }
855
0
856
0
  if (XRE_IsContentProcess()) {
857
0
    ContentChild* cpc = ContentChild::GetSingleton();
858
0
    cpc->SendRemoveGeolocationListener();
859
0
860
0
    return; // bail early
861
0
  }
862
0
863
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
864
0
  if (!obs) {
865
0
    return;
866
0
  }
867
0
868
0
  if (!mProvider) {
869
0
    return;
870
0
  }
871
0
872
0
  mHigherAccuracy = false;
873
0
874
0
  mProvider->Shutdown();
875
0
  obs->NotifyObservers(mProvider,
876
0
                       "geolocation-device-events",
877
0
                       u"shutdown");
878
0
}
879
880
StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
881
882
already_AddRefed<nsGeolocationService>
883
nsGeolocationService::GetGeolocationService()
884
0
{
885
0
  RefPtr<nsGeolocationService> result;
886
0
  if (nsGeolocationService::sService) {
887
0
    result = nsGeolocationService::sService;
888
0
889
0
    return result.forget();
890
0
  }
891
0
892
0
  result = new nsGeolocationService();
893
0
  if (NS_FAILED(result->Init())) {
894
0
    return nullptr;
895
0
  }
896
0
897
0
  ClearOnShutdown(&nsGeolocationService::sService);
898
0
  nsGeolocationService::sService = result;
899
0
  return result.forget();
900
0
}
901
902
void
903
nsGeolocationService::AddLocator(Geolocation* aLocator)
904
0
{
905
0
  mGeolocators.AppendElement(aLocator);
906
0
}
907
908
void
909
nsGeolocationService::RemoveLocator(Geolocation* aLocator)
910
0
{
911
0
  mGeolocators.RemoveElement(aLocator);
912
0
}
913
914
////////////////////////////////////////////////////
915
// Geolocation
916
////////////////////////////////////////////////////
917
918
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)
919
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
920
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
921
0
  NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
922
0
NS_INTERFACE_MAP_END
923
924
NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
925
NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
926
927
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Geolocation,
928
                                      mPendingCallbacks,
929
                                      mWatchingCallbacks,
930
                                      mPendingRequests)
931
932
Geolocation::Geolocation()
933
: mProtocolType(ProtocolType::OTHER)
934
, mLastWatchId(0)
935
0
{
936
0
}
937
938
Geolocation::~Geolocation()
939
0
{
940
0
  if (mService) {
941
0
    Shutdown();
942
0
  }
943
0
}
944
945
StaticRefPtr<Geolocation> Geolocation::sNonWindowSingleton;
946
947
already_AddRefed<Geolocation>
948
Geolocation::NonWindowSingleton()
949
0
{
950
0
  if (sNonWindowSingleton) {
951
0
    return do_AddRef(sNonWindowSingleton);
952
0
  }
953
0
954
0
  RefPtr<Geolocation> result = new Geolocation();
955
0
  DebugOnly<nsresult> rv = result->Init();
956
0
  MOZ_ASSERT(NS_SUCCEEDED(rv), "How can this fail?");
957
0
958
0
  ClearOnShutdown(&sNonWindowSingleton);
959
0
  sNonWindowSingleton = result;
960
0
  return result.forget();
961
0
}
962
963
nsresult
964
Geolocation::Init(nsPIDOMWindowInner* aContentDom)
965
0
{
966
0
  // Remember the window
967
0
  if (aContentDom) {
968
0
    mOwner = do_GetWeakReference(aContentDom);
969
0
    if (!mOwner) {
970
0
      return NS_ERROR_FAILURE;
971
0
    }
972
0
973
0
    // Grab the principal of the document
974
0
    nsCOMPtr<nsIDocument> doc = aContentDom->GetDoc();
975
0
    if (!doc) {
976
0
      return NS_ERROR_FAILURE;
977
0
    }
978
0
979
0
    mPrincipal = doc->NodePrincipal();
980
0
981
0
    nsCOMPtr<nsIURI> uri;
982
0
    nsresult rv = mPrincipal->GetURI(getter_AddRefs(uri));
983
0
    NS_ENSURE_SUCCESS(rv, rv);
984
0
985
0
    if (uri) {
986
0
      bool isHttp;
987
0
      rv = uri->SchemeIs("http", &isHttp);
988
0
      NS_ENSURE_SUCCESS(rv, rv);
989
0
990
0
      bool isHttps;
991
0
      rv = uri->SchemeIs("https", &isHttps);
992
0
      NS_ENSURE_SUCCESS(rv, rv);
993
0
994
0
      // Store the protocol to send via telemetry later.
995
0
      if (isHttp) {
996
0
        mProtocolType = ProtocolType::HTTP;
997
0
      } else if (isHttps) {
998
0
        mProtocolType = ProtocolType::HTTPS;
999
0
      }
1000
0
    }
1001
0
  }
1002
0
1003
0
  // If no aContentDom was passed into us, we are being used
1004
0
  // by chrome/c++ and have no mOwner, no mPrincipal, and no need
1005
0
  // to prompt.
1006
0
  mService = nsGeolocationService::GetGeolocationService();
1007
0
  if (mService) {
1008
0
    mService->AddLocator(this);
1009
0
  }
1010
0
1011
0
  return NS_OK;
1012
0
}
1013
1014
void
1015
Geolocation::Shutdown()
1016
0
{
1017
0
  // Release all callbacks
1018
0
  mPendingCallbacks.Clear();
1019
0
  mWatchingCallbacks.Clear();
1020
0
1021
0
  if (mService) {
1022
0
    mService->RemoveLocator(this);
1023
0
    mService->UpdateAccuracy();
1024
0
  }
1025
0
1026
0
  mService = nullptr;
1027
0
  mPrincipal = nullptr;
1028
0
}
1029
1030
nsPIDOMWindowInner*
1031
0
Geolocation::GetParentObject() const {
1032
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
1033
0
  return window.get();
1034
0
}
1035
1036
bool
1037
Geolocation::HasActiveCallbacks()
1038
0
{
1039
0
  return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
1040
0
}
1041
1042
bool
1043
Geolocation::HighAccuracyRequested()
1044
0
{
1045
0
  for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
1046
0
    if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
1047
0
      return true;
1048
0
    }
1049
0
  }
1050
0
1051
0
  for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
1052
0
    if (mPendingCallbacks[i]->WantsHighAccuracy()) {
1053
0
      return true;
1054
0
    }
1055
0
  }
1056
0
1057
0
  return false;
1058
0
}
1059
1060
void
1061
Geolocation::RemoveRequest(nsGeolocationRequest* aRequest)
1062
0
{
1063
0
  bool requestWasKnown =
1064
0
    (mPendingCallbacks.RemoveElement(aRequest) !=
1065
0
     mWatchingCallbacks.RemoveElement(aRequest));
1066
0
1067
0
  Unused << requestWasKnown;
1068
0
}
1069
1070
NS_IMETHODIMP
1071
Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
1072
0
{
1073
0
  if (!WindowOwnerStillExists()) {
1074
0
    Shutdown();
1075
0
    return NS_OK;
1076
0
  }
1077
0
1078
0
  if (aSomewhere) {
1079
0
    nsCOMPtr<nsIDOMGeoPositionCoords> coords;
1080
0
    aSomewhere->GetCoords(getter_AddRefs(coords));
1081
0
    if (coords) {
1082
0
      double accuracy = -1;
1083
0
      coords->GetAccuracy(&accuracy);
1084
0
      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY_EXPONENTIAL, accuracy);
1085
0
    }
1086
0
  }
1087
0
1088
0
  for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
1089
0
    mPendingCallbacks[i-1]->Update(aSomewhere);
1090
0
    RemoveRequest(mPendingCallbacks[i-1]);
1091
0
  }
1092
0
1093
0
  // notify everyone that is watching
1094
0
  for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
1095
0
    mWatchingCallbacks[i]->Update(aSomewhere);
1096
0
  }
1097
0
1098
0
  return NS_OK;
1099
0
}
1100
1101
NS_IMETHODIMP
1102
Geolocation::NotifyError(uint16_t aErrorCode)
1103
0
{
1104
0
  if (!WindowOwnerStillExists()) {
1105
0
    Shutdown();
1106
0
    return NS_OK;
1107
0
  }
1108
0
1109
0
  mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
1110
0
1111
0
  for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
1112
0
    mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
1113
0
    //NotifyErrorAndShutdown() removes the request from the array
1114
0
  }
1115
0
1116
0
  // notify everyone that is watching
1117
0
  for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
1118
0
    mWatchingCallbacks[i]->NotifyErrorAndShutdown(aErrorCode);
1119
0
  }
1120
0
1121
0
  return NS_OK;
1122
0
}
1123
1124
bool
1125
Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest)
1126
0
{
1127
0
  for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
1128
0
    if (mClearedWatchIDs[i] == aRequest->WatchId()) {
1129
0
      return true;
1130
0
    }
1131
0
  }
1132
0
1133
0
  return false;
1134
0
}
1135
1136
bool
1137
Geolocation::ShouldBlockInsecureRequests() const
1138
0
{
1139
0
  if (Preferences::GetBool(PREF_GEO_SECURITY_ALLOWINSECURE, false)) {
1140
0
    return false;
1141
0
  }
1142
0
1143
0
  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mOwner);
1144
0
  if (!win) {
1145
0
    return false;
1146
0
  }
1147
0
1148
0
  nsCOMPtr<nsIDocument> doc = win->GetDoc();
1149
0
  if (!doc) {
1150
0
    return false;
1151
0
  }
1152
0
1153
0
  if (!nsGlobalWindowInner::Cast(win)->IsSecureContext()) {
1154
0
    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
1155
0
                                    NS_LITERAL_CSTRING("DOM"), doc,
1156
0
                                    nsContentUtils::eDOM_PROPERTIES,
1157
0
                                    "GeolocationInsecureRequestIsForbidden");
1158
0
    return true;
1159
0
  }
1160
0
1161
0
  return false;
1162
0
}
1163
1164
bool
1165
Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest)
1166
0
{
1167
0
  if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
1168
0
    this->NotifyAllowedRequest(aRequest);
1169
0
    this->ClearWatch(aRequest->WatchId());
1170
0
    return true;
1171
0
  }
1172
0
1173
0
  return false;
1174
0
}
1175
1176
void
1177
Geolocation::GetCurrentPosition(PositionCallback& aCallback,
1178
                                PositionErrorCallback* aErrorCallback,
1179
                                const PositionOptions& aOptions,
1180
                                CallerType aCallerType,
1181
                                ErrorResult& aRv)
1182
0
{
1183
0
  nsresult rv = GetCurrentPosition(GeoPositionCallback(&aCallback),
1184
0
                                   GeoPositionErrorCallback(aErrorCallback),
1185
0
                                   CreatePositionOptionsCopy(aOptions),
1186
0
                                   aCallerType);
1187
0
1188
0
  if (NS_FAILED(rv)) {
1189
0
    aRv.Throw(rv);
1190
0
  }
1191
0
}
1192
1193
static nsIEventTarget* MainThreadTarget(Geolocation* geo)
1194
0
{
1195
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(geo->GetOwner());
1196
0
  if (!window) {
1197
0
    return GetMainThreadEventTarget();
1198
0
  }
1199
0
  return nsGlobalWindowInner::Cast(window)->EventTargetFor(mozilla::TaskCategory::Other);
1200
0
}
1201
1202
nsresult
1203
Geolocation::GetCurrentPosition(GeoPositionCallback callback,
1204
                                GeoPositionErrorCallback errorCallback,
1205
                                UniquePtr<PositionOptions>&& options,
1206
                                CallerType aCallerType)
1207
0
{
1208
0
  if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
1209
0
    return NS_ERROR_NOT_AVAILABLE;
1210
0
  }
1211
0
1212
0
  // After this we hand over ownership of options to our nsGeolocationRequest.
1213
0
1214
0
  // Count the number of requests per protocol/scheme.
1215
0
  Telemetry::Accumulate(Telemetry::GEOLOCATION_GETCURRENTPOSITION_SECURE_ORIGIN,
1216
0
                        static_cast<uint8_t>(mProtocolType));
1217
0
1218
0
  nsIEventTarget* target = MainThreadTarget(this);
1219
0
  RefPtr<nsGeolocationRequest> request =
1220
0
    new nsGeolocationRequest(this, std::move(callback), std::move(errorCallback),
1221
0
                             std::move(options), static_cast<uint8_t>(mProtocolType), target,
1222
0
                             false, EventStateManager::IsHandlingUserInput());
1223
0
1224
0
  if (!sGeoEnabled || ShouldBlockInsecureRequests()) {
1225
0
    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
1226
0
    target->Dispatch(ev.forget());
1227
0
    return NS_OK;
1228
0
  }
1229
0
1230
0
  if (!mOwner && aCallerType != CallerType::System) {
1231
0
    return NS_ERROR_FAILURE;
1232
0
  }
1233
0
1234
0
  if (mOwner) {
1235
0
    if (!RegisterRequestWithPrompt(request)) {
1236
0
      return NS_ERROR_NOT_AVAILABLE;
1237
0
    }
1238
0
1239
0
    return NS_OK;
1240
0
  }
1241
0
1242
0
  if (aCallerType != CallerType::System) {
1243
0
    return NS_ERROR_FAILURE;
1244
0
  }
1245
0
1246
0
  nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, request);
1247
0
  target->Dispatch(ev.forget());
1248
0
1249
0
  return NS_OK;
1250
0
}
1251
1252
int32_t
1253
Geolocation::WatchPosition(PositionCallback& aCallback,
1254
                           PositionErrorCallback* aErrorCallback,
1255
                           const PositionOptions& aOptions,
1256
                           CallerType aCallerType,
1257
                           ErrorResult& aRv)
1258
0
{
1259
0
  return WatchPosition(GeoPositionCallback(&aCallback),
1260
0
                       GeoPositionErrorCallback(aErrorCallback),
1261
0
                       CreatePositionOptionsCopy(aOptions),
1262
0
                       aCallerType,
1263
0
                       aRv);
1264
0
}
1265
1266
int32_t
1267
Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
1268
                           nsIDOMGeoPositionErrorCallback *aErrorCallback,
1269
                           UniquePtr<PositionOptions>&& aOptions)
1270
0
{
1271
0
  MOZ_ASSERT(aCallback);
1272
0
1273
0
  return WatchPosition(GeoPositionCallback(aCallback),
1274
0
                       GeoPositionErrorCallback(aErrorCallback),
1275
0
                       std::move(aOptions), CallerType::System,
1276
0
                       IgnoreErrors());
1277
0
}
1278
1279
// On errors we return -1 because that's not a valid watch id and will
1280
// get ignored in clearWatch.
1281
int32_t
1282
Geolocation::WatchPosition(GeoPositionCallback aCallback,
1283
                           GeoPositionErrorCallback aErrorCallback,
1284
                           UniquePtr<PositionOptions>&& aOptions,
1285
                           CallerType aCallerType,
1286
                           ErrorResult& aRv)
1287
0
{
1288
0
  if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
1289
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1290
0
    return -1;
1291
0
  }
1292
0
1293
0
  // Count the number of requests per protocol/scheme.
1294
0
  Telemetry::Accumulate(Telemetry::GEOLOCATION_WATCHPOSITION_SECURE_ORIGIN,
1295
0
                        static_cast<uint8_t>(mProtocolType));
1296
0
1297
0
  // The watch ID:
1298
0
  int32_t watchId = mLastWatchId++;
1299
0
1300
0
  nsIEventTarget* target = MainThreadTarget(this);
1301
0
  RefPtr<nsGeolocationRequest> request =
1302
0
    new nsGeolocationRequest(this, std::move(aCallback), std::move(aErrorCallback),
1303
0
                             std::move(aOptions),
1304
0
                             static_cast<uint8_t>(mProtocolType), target, true,
1305
0
                             EventStateManager::IsHandlingUserInput(), watchId);
1306
0
1307
0
  if (!sGeoEnabled || ShouldBlockInsecureRequests()) {
1308
0
    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
1309
0
    target->Dispatch(ev.forget());
1310
0
    return watchId;
1311
0
  }
1312
0
1313
0
  if (!mOwner && aCallerType != CallerType::System) {
1314
0
    aRv.Throw(NS_ERROR_FAILURE);
1315
0
    return -1;
1316
0
  }
1317
0
1318
0
  if (mOwner) {
1319
0
    if (!RegisterRequestWithPrompt(request)) {
1320
0
      aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1321
0
      return -1;
1322
0
    }
1323
0
1324
0
    return watchId;
1325
0
  }
1326
0
1327
0
  if (aCallerType != CallerType::System) {
1328
0
    aRv.Throw(NS_ERROR_FAILURE);
1329
0
    return -1;
1330
0
  }
1331
0
1332
0
  request->Allow(JS::UndefinedHandleValue);
1333
0
  return watchId;
1334
0
}
1335
1336
void
1337
Geolocation::ClearWatch(int32_t aWatchId)
1338
0
{
1339
0
  if (aWatchId < 0) {
1340
0
    return;
1341
0
  }
1342
0
1343
0
  if (!mClearedWatchIDs.Contains(aWatchId)) {
1344
0
    mClearedWatchIDs.AppendElement(aWatchId);
1345
0
  }
1346
0
1347
0
  for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
1348
0
    if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
1349
0
      mWatchingCallbacks[i]->Shutdown();
1350
0
      RemoveRequest(mWatchingCallbacks[i]);
1351
0
      mClearedWatchIDs.RemoveElement(aWatchId);
1352
0
      break;
1353
0
    }
1354
0
  }
1355
0
1356
0
  // make sure we also search through the pending requests lists for
1357
0
  // watches to clear...
1358
0
  for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
1359
0
    if (mPendingRequests[i]->IsWatch() &&
1360
0
        (mPendingRequests[i]->WatchId() == aWatchId)) {
1361
0
      mPendingRequests[i]->Shutdown();
1362
0
      mPendingRequests.RemoveElementAt(i);
1363
0
      break;
1364
0
    }
1365
0
  }
1366
0
}
1367
1368
bool
1369
Geolocation::WindowOwnerStillExists()
1370
0
{
1371
0
  // an owner was never set when Geolocation
1372
0
  // was created, which means that this object
1373
0
  // is being used without a window.
1374
0
  if (mOwner == nullptr) {
1375
0
    return true;
1376
0
  }
1377
0
1378
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
1379
0
1380
0
  if (window) {
1381
0
    nsPIDOMWindowOuter* outer = window->GetOuterWindow();
1382
0
    if (!outer || outer->GetCurrentInnerWindow() != window ||
1383
0
        outer->Closed()) {
1384
0
      return false;
1385
0
    }
1386
0
  }
1387
0
1388
0
  return true;
1389
0
}
1390
1391
void
1392
Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest)
1393
0
{
1394
0
  if (aRequest->IsWatch()) {
1395
0
    mWatchingCallbacks.AppendElement(aRequest);
1396
0
  } else {
1397
0
    mPendingCallbacks.AppendElement(aRequest);
1398
0
  }
1399
0
}
1400
1401
bool
1402
Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
1403
0
{
1404
0
  nsIEventTarget* target = MainThreadTarget(this);
1405
0
  if (Preferences::GetBool("geo.prompt.testing", false)) {
1406
0
    bool allow = Preferences::GetBool("geo.prompt.testing.allow", false);
1407
0
    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(allow, request);
1408
0
    target->Dispatch(ev.forget());
1409
0
    return true;
1410
0
  }
1411
0
1412
0
  nsCOMPtr<nsIRunnable> ev  = new RequestPromptEvent(request, mOwner);
1413
0
  target->Dispatch(ev.forget());
1414
0
  return true;
1415
0
}
1416
1417
JSObject*
1418
Geolocation::WrapObject(JSContext *aCtx, JS::Handle<JSObject*> aGivenProto)
1419
0
{
1420
0
  return Geolocation_Binding::Wrap(aCtx, this, aGivenProto);
1421
0
}