1/*
2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009 Torch Mobile, Inc.
4 * Copyright 2010, The Android Open Source Project
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "Geolocation.h"
30
31#if ENABLE(GEOLOCATION)
32
33#include "Coordinates.h"
34#include "Document.h"
35#include "Frame.h"
36#include "GeoNotifier.h"
37#include "GeolocationController.h"
38#include "GeolocationError.h"
39#include "GeolocationPosition.h"
40#include "Geoposition.h"
41#include "Page.h"
42#include "PositionError.h"
43#include "RuntimeApplicationChecks.h"
44#include "SecurityOrigin.h"
45#include <wtf/IsoMallocInlines.h>
46#include <wtf/Ref.h>
47#include <wtf/text/StringBuilder.h>
48
49namespace WebCore {
50
51static const ASCIILiteral permissionDeniedErrorMessage { "User denied Geolocation"_s };
52static const ASCIILiteral failedToStartServiceErrorMessage { "Failed to start Geolocation service"_s };
53static const ASCIILiteral framelessDocumentErrorMessage { "Geolocation cannot be used in frameless documents"_s };
54static const ASCIILiteral originCannotRequestGeolocationErrorMessage { "Origin does not have permission to use Geolocation service"_s };
55
56WTF_MAKE_ISO_ALLOCATED_IMPL(Geolocation);
57
58static RefPtr<Geoposition> createGeoposition(Optional<GeolocationPosition>&& position)
59{
60 if (!position)
61 return nullptr;
62
63 DOMTimeStamp timestamp = convertSecondsToDOMTimeStamp(position->timestamp);
64 return Geoposition::create(Coordinates::create(WTFMove(position.value())), timestamp);
65}
66
67static Ref<PositionError> createPositionError(GeolocationError& error)
68{
69 PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE;
70 switch (error.code()) {
71 case GeolocationError::PermissionDenied:
72 code = PositionError::PERMISSION_DENIED;
73 break;
74 case GeolocationError::PositionUnavailable:
75 code = PositionError::POSITION_UNAVAILABLE;
76 break;
77 }
78
79 return PositionError::create(code, error.message());
80}
81
82bool Geolocation::Watchers::add(int id, RefPtr<GeoNotifier>&& notifier)
83{
84 ASSERT(id > 0);
85
86 if (!m_idToNotifierMap.add(id, notifier.get()).isNewEntry)
87 return false;
88 m_notifierToIdMap.set(WTFMove(notifier), id);
89 return true;
90}
91
92GeoNotifier* Geolocation::Watchers::find(int id)
93{
94 ASSERT(id > 0);
95 return m_idToNotifierMap.get(id);
96}
97
98void Geolocation::Watchers::remove(int id)
99{
100 ASSERT(id > 0);
101 if (auto notifier = m_idToNotifierMap.take(id))
102 m_notifierToIdMap.remove(notifier);
103}
104
105void Geolocation::Watchers::remove(GeoNotifier* notifier)
106{
107 if (auto identifier = m_notifierToIdMap.take(notifier))
108 m_idToNotifierMap.remove(identifier);
109}
110
111bool Geolocation::Watchers::contains(GeoNotifier* notifier) const
112{
113 return m_notifierToIdMap.contains(notifier);
114}
115
116void Geolocation::Watchers::clear()
117{
118 m_idToNotifierMap.clear();
119 m_notifierToIdMap.clear();
120}
121
122bool Geolocation::Watchers::isEmpty() const
123{
124 return m_idToNotifierMap.isEmpty();
125}
126
127void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const
128{
129 copy = copyToVector(m_idToNotifierMap.values());
130}
131
132Ref<Geolocation> Geolocation::create(ScriptExecutionContext* context)
133{
134 auto geolocation = adoptRef(*new Geolocation(context));
135 geolocation.get().suspendIfNeeded();
136 return geolocation;
137}
138
139Geolocation::Geolocation(ScriptExecutionContext* context)
140 : ActiveDOMObject(context)
141 , m_allowGeolocation(Unknown)
142 , m_isSuspended(false)
143 , m_hasChangedPosition(false)
144 , m_resumeTimer(*this, &Geolocation::resumeTimerFired)
145{
146}
147
148Geolocation::~Geolocation()
149{
150 ASSERT(m_allowGeolocation != InProgress);
151}
152
153SecurityOrigin* Geolocation::securityOrigin() const
154{
155 return scriptExecutionContext()->securityOrigin();
156}
157
158Page* Geolocation::page() const
159{
160 return document() ? document()->page() : nullptr;
161}
162
163bool Geolocation::canSuspendForDocumentSuspension() const
164{
165 return true;
166}
167
168void Geolocation::suspend(ReasonForSuspension reason)
169{
170 if (reason == ReasonForSuspension::PageCache) {
171 stop();
172 m_resetOnResume = true;
173 }
174
175 // Suspend GeoNotifier timeout timers.
176 if (hasListeners())
177 stopTimers();
178
179 m_isSuspended = true;
180 m_resumeTimer.stop();
181 ActiveDOMObject::suspend(reason);
182}
183
184void Geolocation::resume()
185{
186#if USE(WEB_THREAD)
187 ASSERT(WebThreadIsLockedOrDisabled());
188#endif
189 ActiveDOMObject::resume();
190
191 if (!m_resumeTimer.isActive())
192 m_resumeTimer.startOneShot(0_s);
193}
194
195void Geolocation::resumeTimerFired()
196{
197 m_isSuspended = false;
198
199 if (m_resetOnResume) {
200 resetAllGeolocationPermission();
201 m_resetOnResume = false;
202 }
203
204 // Resume GeoNotifier timeout timers.
205 if (hasListeners()) {
206 for (auto& notifier : m_oneShots)
207 notifier->startTimerIfNeeded();
208 GeoNotifierVector watcherCopy;
209 m_watchers.getNotifiersVector(watcherCopy);
210 for (auto& watcher : watcherCopy)
211 watcher->startTimerIfNeeded();
212 }
213
214 if ((isAllowed() || isDenied()) && !m_pendingForPermissionNotifiers.isEmpty()) {
215 // The pending permission was granted while the object was suspended.
216 setIsAllowed(isAllowed());
217 ASSERT(!m_hasChangedPosition);
218 ASSERT(!m_errorWaitingForResume);
219 return;
220 }
221
222 if (isDenied() && hasListeners()) {
223 // The permission was revoked while the object was suspended.
224 setIsAllowed(false);
225 return;
226 }
227
228 if (m_hasChangedPosition) {
229 positionChanged();
230 m_hasChangedPosition = false;
231 }
232
233 if (m_errorWaitingForResume) {
234 handleError(*m_errorWaitingForResume);
235 m_errorWaitingForResume = nullptr;
236 }
237}
238
239void Geolocation::resetAllGeolocationPermission()
240{
241 if (m_isSuspended) {
242 m_resetOnResume = true;
243 return;
244 }
245
246 if (m_allowGeolocation == InProgress) {
247 Page* page = this->page();
248 if (page)
249 GeolocationController::from(page)->cancelPermissionRequest(*this);
250
251 // This return is not technically correct as GeolocationController::cancelPermissionRequest() should have cleared the active request.
252 // Neither iOS nor OS X supports cancelPermissionRequest() (https://bugs.webkit.org/show_bug.cgi?id=89524), so we workaround that and let ongoing requests complete. :(
253 return;
254 }
255
256 // 1) Reset our own state.
257 stopUpdating();
258 m_allowGeolocation = Unknown;
259 m_hasChangedPosition = false;
260 m_errorWaitingForResume = nullptr;
261
262 // 2) Request new permission for the active notifiers.
263 stopTimers();
264
265 // Go over the one shot and re-request permission.
266 for (auto& notifier : m_oneShots)
267 startRequest(notifier.get());
268 // Go over the watchers and re-request permission.
269 GeoNotifierVector watcherCopy;
270 m_watchers.getNotifiersVector(watcherCopy);
271 for (auto& watcher : watcherCopy)
272 startRequest(watcher.get());
273}
274
275void Geolocation::stop()
276{
277 Page* page = this->page();
278 if (page && m_allowGeolocation == InProgress)
279 GeolocationController::from(page)->cancelPermissionRequest(*this);
280 // The frame may be moving to a new page and we want to get the permissions from the new page's client.
281 m_allowGeolocation = Unknown;
282 cancelAllRequests();
283 stopUpdating();
284 m_hasChangedPosition = false;
285 m_errorWaitingForResume = nullptr;
286 m_pendingForPermissionNotifiers.clear();
287}
288
289const char* Geolocation::activeDOMObjectName() const
290{
291 return "Geolocation";
292}
293
294Geoposition* Geolocation::lastPosition()
295{
296 Page* page = this->page();
297 if (!page)
298 return nullptr;
299
300 m_lastPosition = createGeoposition(GeolocationController::from(page)->lastPosition());
301
302 return m_lastPosition.get();
303}
304
305void Geolocation::getCurrentPosition(Ref<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, PositionOptions&& options)
306{
307 if (!frame())
308 return;
309
310 auto notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options));
311 startRequest(notifier.ptr());
312
313 m_oneShots.add(WTFMove(notifier));
314}
315
316int Geolocation::watchPosition(Ref<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, PositionOptions&& options)
317{
318 if (!frame())
319 return 0;
320
321 auto notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options));
322 startRequest(notifier.ptr());
323
324 int watchID;
325 // Keep asking for the next id until we're given one that we don't already have.
326 do {
327 watchID = m_scriptExecutionContext->circularSequentialID();
328 } while (!m_watchers.add(watchID, notifier.copyRef()));
329 return watchID;
330}
331
332static void logError(const String& target, const bool isSecure, const bool isMixedContent, Document* document)
333{
334 StringBuilder message;
335 message.append("[blocked] Access to geolocation was blocked over");
336
337 if (!isSecure)
338 message.append(" insecure connection to ");
339 else if (isMixedContent)
340 message.append(" secure connection with mixed content to ");
341 else
342 return;
343
344 message.append(target);
345 message.append(".\n");
346 document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message.toString());
347}
348
349// FIXME: remove this function when rdar://problem/32137821 is fixed.
350static bool isRequestFromIBooks()
351{
352#if PLATFORM(MAC)
353 return MacApplication::isIBooks();
354#elif PLATFORM(IOS_FAMILY)
355 return IOSApplication::isIBooks();
356#endif
357 return false;
358}
359
360bool Geolocation::shouldBlockGeolocationRequests()
361{
362 bool isSecure = SecurityOrigin::isSecure(document()->url());
363 bool hasMixedContent = !document()->foundMixedContent().isEmpty();
364 bool isLocalOrigin = securityOrigin()->isLocal();
365 if (securityOrigin()->canRequestGeolocation()) {
366 if (isLocalOrigin || (isSecure && !hasMixedContent) || isRequestFromIBooks())
367 return false;
368 }
369
370 logError(securityOrigin()->toString(), isSecure, hasMixedContent, document());
371 return true;
372}
373
374void Geolocation::startRequest(GeoNotifier* notifier)
375{
376 if (shouldBlockGeolocationRequests()) {
377 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, originCannotRequestGeolocationErrorMessage));
378 return;
379 }
380 document()->setGeolocationAccessed();
381
382 // Check whether permissions have already been denied. Note that if this is the case,
383 // the permission state can not change again in the lifetime of this page.
384 if (isDenied())
385 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
386 else if (haveSuitableCachedPosition(notifier->options()))
387 notifier->setUseCachedPosition();
388 else if (notifier->hasZeroTimeout())
389 notifier->startTimerIfNeeded();
390 else if (!isAllowed()) {
391 // if we don't yet have permission, request for permission before calling startUpdating()
392 m_pendingForPermissionNotifiers.add(notifier);
393 requestPermission();
394 } else if (startUpdating(notifier))
395 notifier->startTimerIfNeeded();
396 else
397 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
398}
399
400void Geolocation::fatalErrorOccurred(GeoNotifier* notifier)
401{
402 // This request has failed fatally. Remove it from our lists.
403 m_oneShots.remove(notifier);
404 m_watchers.remove(notifier);
405
406 if (!hasListeners())
407 stopUpdating();
408}
409
410void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier)
411{
412 // This is called asynchronously, so the permissions could have been denied
413 // since we last checked in startRequest.
414 if (isDenied()) {
415 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
416 return;
417 }
418
419 m_requestsAwaitingCachedPosition.add(notifier);
420
421 // If permissions are allowed, make the callback
422 if (isAllowed()) {
423 makeCachedPositionCallbacks();
424 return;
425 }
426
427 // Request permissions, which may be synchronous or asynchronous.
428 requestPermission();
429}
430
431void Geolocation::makeCachedPositionCallbacks()
432{
433 // All modifications to m_requestsAwaitingCachedPosition are done
434 // asynchronously, so we don't need to worry about it being modified from
435 // the callbacks.
436 for (auto& notifier : m_requestsAwaitingCachedPosition) {
437 // FIXME: This seems wrong, since makeCachedPositionCallbacks() is called in a branch where
438 // lastPosition() is known to be null in Geolocation::setIsAllowed().
439 notifier->runSuccessCallback(lastPosition());
440
441 // If this is a one-shot request, stop it. Otherwise, if the watch still
442 // exists, start the service to get updates.
443 if (!m_oneShots.remove(notifier.get()) && m_watchers.contains(notifier.get())) {
444 if (notifier->hasZeroTimeout() || startUpdating(notifier.get()))
445 notifier->startTimerIfNeeded();
446 else
447 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
448 }
449 }
450
451 m_requestsAwaitingCachedPosition.clear();
452
453 if (!hasListeners())
454 stopUpdating();
455}
456
457void Geolocation::requestTimedOut(GeoNotifier* notifier)
458{
459 // If this is a one-shot request, stop it.
460 m_oneShots.remove(notifier);
461
462 if (!hasListeners())
463 stopUpdating();
464}
465
466bool Geolocation::haveSuitableCachedPosition(const PositionOptions& options)
467{
468 Geoposition* cachedPosition = lastPosition();
469 if (!cachedPosition)
470 return false;
471 if (!options.maximumAge)
472 return false;
473 DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(WallTime::now().secondsSinceEpoch());
474 return cachedPosition->timestamp() > currentTimeMillis - options.maximumAge;
475}
476
477void Geolocation::clearWatch(int watchID)
478{
479 if (watchID <= 0)
480 return;
481
482 if (GeoNotifier* notifier = m_watchers.find(watchID))
483 m_pendingForPermissionNotifiers.remove(notifier);
484 m_watchers.remove(watchID);
485
486 if (!hasListeners())
487 stopUpdating();
488}
489
490void Geolocation::setIsAllowed(bool allowed)
491{
492 // Protect the Geolocation object from garbage collection during a callback.
493 Ref<Geolocation> protectedThis(*this);
494
495 // This may be due to either a new position from the service, or a cached
496 // position.
497 m_allowGeolocation = allowed ? Yes : No;
498
499 if (m_isSuspended)
500 return;
501
502 // Permission request was made during the startRequest process
503 if (!m_pendingForPermissionNotifiers.isEmpty()) {
504 handlePendingPermissionNotifiers();
505 m_pendingForPermissionNotifiers.clear();
506 return;
507 }
508
509 if (!isAllowed()) {
510 auto error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
511 error->setIsFatal(true);
512 handleError(error);
513 m_requestsAwaitingCachedPosition.clear();
514 m_hasChangedPosition = false;
515 m_errorWaitingForResume = nullptr;
516 return;
517 }
518
519 // If the service has a last position, use it to call back for all requests.
520 // If any of the requests are waiting for permission for a cached position,
521 // the position from the service will be at least as fresh.
522 if (RefPtr<Geoposition> position = lastPosition())
523 makeSuccessCallbacks(*position);
524 else
525 makeCachedPositionCallbacks();
526}
527
528void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError& error)
529{
530 for (auto& notifier : notifiers)
531 notifier->runErrorCallback(error);
532}
533
534void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition& position)
535{
536 for (auto& notifier : notifiers)
537 notifier->runSuccessCallback(&position);
538}
539
540void Geolocation::stopTimer(GeoNotifierVector& notifiers)
541{
542 for (auto& notifier : notifiers)
543 notifier->stopTimer();
544}
545
546void Geolocation::stopTimersForOneShots()
547{
548 auto copy = copyToVector(m_oneShots);
549 stopTimer(copy);
550}
551
552void Geolocation::stopTimersForWatchers()
553{
554 GeoNotifierVector copy;
555 m_watchers.getNotifiersVector(copy);
556
557 stopTimer(copy);
558}
559
560void Geolocation::stopTimers()
561{
562 stopTimersForOneShots();
563 stopTimersForWatchers();
564}
565
566void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
567{
568 for (auto& notifier : notifiers)
569 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, framelessDocumentErrorMessage));
570}
571
572void Geolocation::cancelAllRequests()
573{
574 auto copy = copyToVector(m_oneShots);
575 cancelRequests(copy);
576 m_watchers.getNotifiersVector(copy);
577 cancelRequests(copy);
578}
579
580void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
581{
582 GeoNotifierVector nonCached;
583 for (auto& notifier : notifiers) {
584 if (notifier->useCachedPosition()) {
585 if (cached)
586 cached->append(notifier.get());
587 } else
588 nonCached.append(notifier.get());
589 }
590 notifiers.swap(nonCached);
591}
592
593void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
594{
595 for (auto& notifier : src)
596 dest.add(notifier.get());
597}
598
599void Geolocation::handleError(PositionError& error)
600{
601 auto oneShotsCopy = copyToVector(m_oneShots);
602
603 GeoNotifierVector watchersCopy;
604 m_watchers.getNotifiersVector(watchersCopy);
605
606 // Clear the lists before we make the callbacks, to avoid clearing notifiers
607 // added by calls to Geolocation methods from the callbacks, and to prevent
608 // further callbacks to these notifiers.
609 GeoNotifierVector oneShotsWithCachedPosition;
610 m_oneShots.clear();
611 if (error.isFatal())
612 m_watchers.clear();
613 else {
614 // Don't send non-fatal errors to notifiers due to receive a cached position.
615 extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition);
616 extractNotifiersWithCachedPosition(watchersCopy, 0);
617 }
618
619 sendError(oneShotsCopy, error);
620 sendError(watchersCopy, error);
621
622 // hasListeners() doesn't distinguish between notifiers due to receive a
623 // cached position and those requiring a fresh position. Perform the check
624 // before restoring the notifiers below.
625 if (!hasListeners())
626 stopUpdating();
627
628 // Maintain a reference to the cached notifiers until their timer fires.
629 copyToSet(oneShotsWithCachedPosition, m_oneShots);
630}
631
632void Geolocation::requestPermission()
633{
634 if (m_allowGeolocation > Unknown)
635 return;
636
637 Page* page = this->page();
638 if (!page)
639 return;
640
641 m_allowGeolocation = InProgress;
642
643 // Ask the embedder: it maintains the geolocation challenge policy itself.
644 GeolocationController::from(page)->requestPermission(*this);
645}
646
647void Geolocation::makeSuccessCallbacks(Geoposition& position)
648{
649 ASSERT(lastPosition());
650 ASSERT(isAllowed());
651
652 auto oneShotsCopy = copyToVector(m_oneShots);
653
654 GeoNotifierVector watchersCopy;
655 m_watchers.getNotifiersVector(watchersCopy);
656
657 // Clear the lists before we make the callbacks, to avoid clearing notifiers
658 // added by calls to Geolocation methods from the callbacks, and to prevent
659 // further callbacks to these notifiers.
660 m_oneShots.clear();
661
662 sendPosition(oneShotsCopy, position);
663 sendPosition(watchersCopy, position);
664
665 if (!hasListeners())
666 stopUpdating();
667}
668
669void Geolocation::positionChanged()
670{
671 ASSERT(isAllowed());
672
673 // Stop all currently running timers.
674 stopTimers();
675
676 if (m_isSuspended) {
677 m_hasChangedPosition = true;
678 return;
679 }
680
681 RefPtr<Geoposition> position = lastPosition();
682 ASSERT(position);
683
684 makeSuccessCallbacks(*position);
685}
686
687void Geolocation::setError(GeolocationError& error)
688{
689 if (m_isSuspended) {
690 m_errorWaitingForResume = createPositionError(error);
691 return;
692 }
693
694 auto positionError = createPositionError(error);
695 handleError(positionError);
696}
697
698bool Geolocation::startUpdating(GeoNotifier* notifier)
699{
700 Page* page = this->page();
701 if (!page)
702 return false;
703
704 GeolocationController::from(page)->addObserver(*this, notifier->options().enableHighAccuracy);
705 return true;
706}
707
708void Geolocation::stopUpdating()
709{
710 Page* page = this->page();
711 if (!page)
712 return;
713
714 GeolocationController::from(page)->removeObserver(*this);
715}
716
717void Geolocation::handlePendingPermissionNotifiers()
718{
719 // While we iterate through the list, we need not worry about list being modified as the permission
720 // is already set to Yes/No and no new listeners will be added to the pending list
721 for (auto& notifier : m_pendingForPermissionNotifiers) {
722 if (isAllowed()) {
723 // start all pending notification requests as permission granted.
724 // The notifier is always ref'ed by m_oneShots or m_watchers.
725 if (startUpdating(notifier.get()))
726 notifier->startTimerIfNeeded();
727 else
728 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
729 } else
730 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
731 }
732}
733
734} // namespace WebCore
735
736#endif // ENABLE(GEOLOCATION)
737