85ae40f6341753271672f3a377fe993df4a318c0
[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 "SecurityOrigin.h"
44 #include <wtf/CurrentTime.h>
45 #include <wtf/Ref.h>
46 #include <wtf/text/StringBuilder.h>
47
48 namespace WebCore {
49
50 static const char permissionDeniedErrorMessage[] = "User denied Geolocation";
51 static const char failedToStartServiceErrorMessage[] = "Failed to start Geolocation service";
52 static const char framelessDocumentErrorMessage[] = "Geolocation cannot be used in frameless documents";
53 static const char originCannotRequestGeolocationErrorMessage[] = "Origin does not have permission to use Geolocation service";
54
55 static RefPtr<Geoposition> createGeoposition(GeolocationPosition* position)
56 {
57     if (!position)
58         return nullptr;
59     
60     RefPtr<Coordinates> coordinates = Coordinates::create(position->latitude(), position->longitude(), position->canProvideAltitude(), position->altitude(), 
61                                                           position->accuracy(), position->canProvideAltitudeAccuracy(), position->altitudeAccuracy(),
62                                                           position->canProvideHeading(), position->heading(), position->canProvideSpeed(), position->speed());
63     return Geoposition::create(WTFMove(coordinates), convertSecondsToDOMTimeStamp(position->timestamp()));
64 }
65
66 static Ref<PositionError> createPositionError(GeolocationError* error)
67 {
68     PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE;
69     switch (error->code()) {
70     case GeolocationError::PermissionDenied:
71         code = PositionError::PERMISSION_DENIED;
72         break;
73     case GeolocationError::PositionUnavailable:
74         code = PositionError::POSITION_UNAVAILABLE;
75         break;
76     }
77
78     return PositionError::create(code, error->message());
79 }
80
81 bool Geolocation::Watchers::add(int id, RefPtr<GeoNotifier>&& notifier)
82 {
83     ASSERT(id > 0);
84
85     if (!m_idToNotifierMap.add(id, notifier.get()).isNewEntry)
86         return false;
87     m_notifierToIdMap.set(WTFMove(notifier), id);
88     return true;
89 }
90
91 GeoNotifier* Geolocation::Watchers::find(int id)
92 {
93     ASSERT(id > 0);
94     return m_idToNotifierMap.get(id);
95 }
96
97 void Geolocation::Watchers::remove(int id)
98 {
99     ASSERT(id > 0);
100     if (auto notifier = m_idToNotifierMap.take(id))
101         m_notifierToIdMap.remove(notifier);
102 }
103
104 void Geolocation::Watchers::remove(GeoNotifier* notifier)
105 {
106     if (auto identifier = m_notifierToIdMap.take(notifier))
107         m_idToNotifierMap.remove(identifier);
108 }
109
110 bool Geolocation::Watchers::contains(GeoNotifier* notifier) const
111 {
112     return m_notifierToIdMap.contains(notifier);
113 }
114
115 void Geolocation::Watchers::clear()
116 {
117     m_idToNotifierMap.clear();
118     m_notifierToIdMap.clear();
119 }
120
121 bool Geolocation::Watchers::isEmpty() const
122 {
123     return m_idToNotifierMap.isEmpty();
124 }
125
126 void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const
127 {
128     copyValuesToVector(m_idToNotifierMap, copy);
129 }
130
131 Ref<Geolocation> Geolocation::create(ScriptExecutionContext* context)
132 {
133     auto geolocation = adoptRef(*new Geolocation(context));
134     geolocation.get().suspendIfNeeded();
135     return geolocation;
136 }
137
138 Geolocation::Geolocation(ScriptExecutionContext* context)
139     : ActiveDOMObject(context)
140     , m_allowGeolocation(Unknown)
141     , m_isSuspended(false)
142     , m_hasChangedPosition(false)
143     , m_resumeTimer(*this, &Geolocation::resumeTimerFired)
144 {
145 }
146
147 Geolocation::~Geolocation()
148 {
149     ASSERT(m_allowGeolocation != InProgress);
150 }
151
152 Document* Geolocation::document() const
153 {
154     return downcast<Document>(scriptExecutionContext());
155 }
156
157 SecurityOrigin* Geolocation::securityOrigin() const
158 {
159     return scriptExecutionContext()->securityOrigin();
160 }
161
162 Frame* Geolocation::frame() const
163 {
164     return document() ? document()->frame() : nullptr;
165 }
166
167 Page* Geolocation::page() const
168 {
169     return document() ? document()->page() : nullptr;
170 }
171
172 bool Geolocation::canSuspendForDocumentSuspension() const
173 {
174     return true;
175 }
176     
177 void Geolocation::suspend(ReasonForSuspension reason)
178 {
179     if (reason == ActiveDOMObject::PageCache) {
180         stop();
181         m_resetOnResume = true;
182     }
183
184     // Suspend GeoNotifier timeout timers.
185     if (hasListeners())
186         stopTimers();
187
188     m_isSuspended = true;
189     m_resumeTimer.stop();
190     ActiveDOMObject::suspend(reason);
191 }
192
193 void Geolocation::resume()
194 {
195 #if USE(WEB_THREAD)
196     ASSERT(WebThreadIsLockedOrDisabled());
197 #endif
198     ActiveDOMObject::resume();
199
200     if (!m_resumeTimer.isActive())
201         m_resumeTimer.startOneShot(0);
202 }
203
204 void Geolocation::resumeTimerFired()
205 {
206     m_isSuspended = false;
207
208     if (m_resetOnResume) {
209         resetAllGeolocationPermission();
210         m_resetOnResume = false;
211     }
212
213     // Resume GeoNotifier timeout timers.
214     if (hasListeners()) {
215         for (auto& notifier : m_oneShots)
216             notifier->startTimerIfNeeded();
217         GeoNotifierVector watcherCopy;
218         m_watchers.getNotifiersVector(watcherCopy);
219         for (auto& watcher : watcherCopy)
220             watcher->startTimerIfNeeded();
221     }
222
223     if ((isAllowed() || isDenied()) && !m_pendingForPermissionNotifiers.isEmpty()) {
224         // The pending permission was granted while the object was suspended.
225         setIsAllowed(isAllowed());
226         ASSERT(!m_hasChangedPosition);
227         ASSERT(!m_errorWaitingForResume);
228         return;
229     }
230
231     if (isDenied() && hasListeners()) {
232         // The permission was revoked while the object was suspended.
233         setIsAllowed(false);
234         return;
235     }
236
237     if (m_hasChangedPosition) {
238         positionChanged();
239         m_hasChangedPosition = false;
240     }
241
242     if (m_errorWaitingForResume) {
243         handleError(m_errorWaitingForResume.get());
244         m_errorWaitingForResume = nullptr;
245     }
246 }
247
248 void Geolocation::resetAllGeolocationPermission()
249 {
250     if (m_isSuspended) {
251         m_resetOnResume = true;
252         return;
253     }
254
255     if (m_allowGeolocation == InProgress) {
256         Page* page = this->page();
257         if (page)
258             GeolocationController::from(page)->cancelPermissionRequest(this);
259
260         // This return is not technically correct as GeolocationController::cancelPermissionRequest() should have cleared the active request.
261         // 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. :(
262         return;
263     }
264
265     // 1) Reset our own state.
266     stopUpdating();
267     m_allowGeolocation = Unknown;
268     m_hasChangedPosition = false;
269     m_errorWaitingForResume = nullptr;
270
271     // 2) Request new permission for the active notifiers.
272     stopTimers();
273
274     // Go over the one shot and re-request permission.
275     for (auto& notifier : m_oneShots)
276         startRequest(notifier.get());
277     // Go over the watchers and re-request permission.
278     GeoNotifierVector watcherCopy;
279     m_watchers.getNotifiersVector(watcherCopy);
280     for (auto& watcher : watcherCopy)
281         startRequest(watcher.get());
282 }
283
284 void Geolocation::stop()
285 {
286     Page* page = this->page();
287     if (page && m_allowGeolocation == InProgress)
288         GeolocationController::from(page)->cancelPermissionRequest(this);
289     // The frame may be moving to a new page and we want to get the permissions from the new page's client.
290     m_allowGeolocation = Unknown;
291     cancelAllRequests();
292     stopUpdating();
293     m_hasChangedPosition = false;
294     m_errorWaitingForResume = nullptr;
295     m_pendingForPermissionNotifiers.clear();
296 }
297
298 const char* Geolocation::activeDOMObjectName() const
299 {
300     return "Geolocation";
301 }
302
303 Geoposition* Geolocation::lastPosition()
304 {
305     Page* page = this->page();
306     if (!page)
307         return 0;
308
309     m_lastPosition = createGeoposition(GeolocationController::from(page)->lastPosition());
310
311     return m_lastPosition.get();
312 }
313
314 void Geolocation::getCurrentPosition(RefPtr<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, RefPtr<PositionOptions>&& options)
315 {
316     if (!frame())
317         return;
318
319     RefPtr<GeoNotifier> notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options));
320     startRequest(notifier.get());
321
322     m_oneShots.add(notifier);
323 }
324
325 int Geolocation::watchPosition(RefPtr<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, RefPtr<PositionOptions>&& options)
326 {
327     if (!frame())
328         return 0;
329
330     RefPtr<GeoNotifier> notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options));
331     startRequest(notifier.get());
332
333     int watchID;
334     // Keep asking for the next id until we're given one that we don't already have.
335     do {
336         watchID = m_scriptExecutionContext->circularSequentialID();
337     } while (!m_watchers.add(watchID, WTFMove(notifier)));
338     return watchID;
339 }
340
341 static void logError(const String& target, const bool isSecure, const bool isMixedContent, Document* document)
342 {
343     StringBuilder message;
344     message.append("[blocked] Access to geolocation was blocked over");
345     
346     if (!isSecure)
347         message.append(" insecure connection to ");
348     else if (isMixedContent)
349         message.append(" secure connection with mixed content to ");
350     else
351         return;
352     
353     message.append(target);
354     message.append(".\n");
355     document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message.toString());
356 }
357
358 bool Geolocation::shouldBlockGeolocationRequests()
359 {
360     bool isSecure = SecurityOrigin::isSecure(document()->url());
361     bool hasMixedContent = document()->foundMixedContent();
362     bool isLocalFile = document()->url().isLocalFile();
363     if (securityOrigin()->canRequestGeolocation()) {
364         if (isLocalFile || (isSecure && !hasMixedContent))
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         notifier->runSuccessCallback(lastPosition());
436
437         // If this is a one-shot request, stop it. Otherwise, if the watch still
438         // exists, start the service to get updates.
439         if (!m_oneShots.remove(notifier.get()) && m_watchers.contains(notifier.get())) {
440             if (notifier->hasZeroTimeout() || startUpdating(notifier.get()))
441                 notifier->startTimerIfNeeded();
442             else
443                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));
444         }
445     }
446
447     m_requestsAwaitingCachedPosition.clear();
448
449     if (!hasListeners())
450         stopUpdating();
451 }
452
453 void Geolocation::requestTimedOut(GeoNotifier* notifier)
454 {
455     // If this is a one-shot request, stop it.
456     m_oneShots.remove(notifier);
457
458     if (!hasListeners())
459         stopUpdating();
460 }
461
462 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
463 {
464     Geoposition* cachedPosition = lastPosition();
465     if (!cachedPosition)
466         return false;
467     if (!options->hasMaximumAge())
468         return true;
469     if (!options->maximumAge())
470         return false;
471     DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
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         RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED,  ASCIILiteral(permissionDeniedErrorMessage));
509         error->setIsFatal(true);
510         handleError(error.get());
511         m_requestsAwaitingCachedPosition.clear();
512         m_hasChangedPosition = false;
513         m_errorWaitingForResume = nullptr;
514
515         return;
516     }
517
518     // If the service has a last position, use it to call back for all requests.
519     // If any of the requests are waiting for permission for a cached position,
520     // the position from the service will be at least as fresh.
521     if (lastPosition())
522         makeSuccessCallbacks();
523     else
524         makeCachedPositionCallbacks();
525 }
526
527 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
528 {
529     for (auto& notifier : notifiers)
530         notifier->runErrorCallback(error);
531 }
532
533 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
534 {
535     for (auto& notifier : notifiers)
536         notifier->runSuccessCallback(position);
537 }
538
539 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
540 {
541     for (auto& notifier : notifiers)
542         notifier->stopTimer();
543 }
544
545 void Geolocation::stopTimersForOneShots()
546 {
547     GeoNotifierVector copy;
548     copyToVector(m_oneShots, copy);
549     
550     stopTimer(copy);
551 }
552
553 void Geolocation::stopTimersForWatchers()
554 {
555     GeoNotifierVector copy;
556     m_watchers.getNotifiersVector(copy);
557     
558     stopTimer(copy);
559 }
560
561 void Geolocation::stopTimers()
562 {
563     stopTimersForOneShots();
564     stopTimersForWatchers();
565 }
566
567 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
568 {
569     for (auto& notifier : notifiers)
570         notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(framelessDocumentErrorMessage)));
571 }
572
573 void Geolocation::cancelAllRequests()
574 {
575     GeoNotifierVector copy;
576     copyToVector(m_oneShots, copy);
577     cancelRequests(copy);
578     m_watchers.getNotifiersVector(copy);
579     cancelRequests(copy);
580 }
581
582 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
583 {
584     GeoNotifierVector nonCached;
585     for (auto& notifier : notifiers) {
586         if (notifier->useCachedPosition()) {
587             if (cached)
588                 cached->append(notifier.get());
589         } else
590             nonCached.append(notifier.get());
591     }
592     notifiers.swap(nonCached);
593 }
594
595 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
596 {
597     for (auto& notifier : src)
598         dest.add(notifier.get());
599 }
600
601 void Geolocation::handleError(PositionError* error)
602 {
603     ASSERT(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()
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, lastPosition());
669     sendPosition(watchersCopy, lastPosition());
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     makeSuccessCallbacks();
688 }
689
690 void Geolocation::setError(GeolocationError* error)
691 {
692     if (m_isSuspended) {
693         m_errorWaitingForResume = createPositionError(error);
694         return;
695     }
696     RefPtr<PositionError> positionError = createPositionError(error);
697     handleError(positionError.get());
698 }
699
700 bool Geolocation::startUpdating(GeoNotifier* notifier)
701 {
702     Page* page = this->page();
703     if (!page)
704         return false;
705
706     GeolocationController::from(page)->addObserver(this, notifier->options()->enableHighAccuracy());
707     return true;
708 }
709
710 void Geolocation::stopUpdating()
711 {
712     Page* page = this->page();
713     if (!page)
714         return;
715
716     GeolocationController::from(page)->removeObserver(this);
717 }
718
719 void Geolocation::handlePendingPermissionNotifiers()
720 {
721     // While we iterate through the list, we need not worry about list being modified as the permission 
722     // is already set to Yes/No and no new listeners will be added to the pending list
723     for (auto& notifier : m_pendingForPermissionNotifiers) {
724         if (isAllowed()) {
725             // start all pending notification requests as permission granted.
726             // The notifier is always ref'ed by m_oneShots or m_watchers.
727             if (startUpdating(notifier.get()))
728                 notifier->startTimerIfNeeded();
729             else
730                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));
731         } else
732             notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, ASCIILiteral(permissionDeniedErrorMessage)));
733     }
734 }
735
736 } // namespace WebCore
737                                                         
738 #endif // ENABLE(GEOLOCATION)