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