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