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