Fix build for non-iOS platforms. :|
[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 #if PLATFORM(IOS)
291     ASSERT(WebThreadIsLockedOrDisabled());
292 #endif
293     ActiveDOMObject::resume();
294
295     if (!m_resumeTimer.isActive())
296         m_resumeTimer.startOneShot(0);
297 }
298
299 void Geolocation::resumeTimerFired()
300 {
301     m_isSuspended = false;
302
303     if (m_resetOnResume) {
304         resetAllGeolocationPermission();
305         m_resetOnResume = false;
306     }
307
308     // Resume GeoNotifier timeout timers.
309     if (hasListeners()) {
310         GeoNotifierSet::const_iterator end = m_oneShots.end();
311         for (GeoNotifierSet::const_iterator it = m_oneShots.begin(); it != end; ++it)
312             (*it)->startTimerIfNeeded();
313         GeoNotifierVector watcherCopy;
314         m_watchers.getNotifiersVector(watcherCopy);
315         for (size_t i = 0; i < watcherCopy.size(); ++i)
316             watcherCopy[i]->startTimerIfNeeded();
317     }
318
319     if ((isAllowed() || isDenied()) && !m_pendingForPermissionNotifiers.isEmpty()) {
320         // The pending permission was granted while the object was suspended.
321         setIsAllowed(isAllowed());
322         ASSERT(!m_hasChangedPosition);
323         ASSERT(!m_errorWaitingForResume);
324         return;
325     }
326
327     if (isDenied() && hasListeners()) {
328         // The permission was revoked while the object was suspended.
329         setIsAllowed(false);
330         return;
331     }
332
333     if (m_hasChangedPosition) {
334         positionChanged();
335         m_hasChangedPosition = false;
336     }
337
338     if (m_errorWaitingForResume) {
339         handleError(m_errorWaitingForResume.get());
340         m_errorWaitingForResume = nullptr;
341     }
342 }
343
344 void Geolocation::resetAllGeolocationPermission()
345 {
346     if (m_isSuspended) {
347         m_resetOnResume = true;
348         return;
349     }
350
351     if (m_allowGeolocation == InProgress) {
352         Page* page = this->page();
353         if (page)
354             GeolocationController::from(page)->cancelPermissionRequest(this);
355
356         // This return is not technically correct as GeolocationController::cancelPermissionRequest() should have cleared the active request.
357         // 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. :(
358         return;
359     }
360
361     // 1) Reset our own state.
362     stopUpdating();
363     m_allowGeolocation = Unknown;
364     m_hasChangedPosition = false;
365     m_errorWaitingForResume = nullptr;
366
367     // 2) Request new permission for the active notifiers.
368     stopTimers();
369
370     // Go over the one shot and re-request permission.
371     GeoNotifierSet::iterator end = m_oneShots.end();
372     for (GeoNotifierSet::iterator it = m_oneShots.begin(); it != end; ++it)
373         startRequest((*it).get());
374     // Go over the watchers and re-request permission.
375     GeoNotifierVector watcherCopy;
376     m_watchers.getNotifiersVector(watcherCopy);
377     for (size_t i = 0; i < watcherCopy.size(); ++i)
378         startRequest(watcherCopy[i].get());
379 }
380
381 void Geolocation::stop()
382 {
383     Page* page = this->page();
384     if (page && m_allowGeolocation == InProgress)
385         GeolocationController::from(page)->cancelPermissionRequest(this);
386     // The frame may be moving to a new page and we want to get the permissions from the new page's client.
387     m_allowGeolocation = Unknown;
388     cancelAllRequests();
389     stopUpdating();
390     m_hasChangedPosition = false;
391     m_errorWaitingForResume = nullptr;
392     m_pendingForPermissionNotifiers.clear();
393 }
394
395 Geoposition* Geolocation::lastPosition()
396 {
397     Page* page = this->page();
398     if (!page)
399         return 0;
400
401     m_lastPosition = createGeoposition(GeolocationController::from(page)->lastPosition());
402
403     return m_lastPosition.get();
404 }
405
406 void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
407 {
408     if (!frame())
409         return;
410
411     RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
412     startRequest(notifier.get());
413
414     m_oneShots.add(notifier);
415 }
416
417 int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
418 {
419     if (!frame())
420         return 0;
421
422     RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
423     startRequest(notifier.get());
424
425     int watchID;
426     // Keep asking for the next id until we're given one that we don't already have.
427     do {
428         watchID = m_scriptExecutionContext->circularSequentialID();
429     } while (!m_watchers.add(watchID, notifier));
430     return watchID;
431 }
432
433 void Geolocation::startRequest(GeoNotifier *notifier)
434 {
435     // Check whether permissions have already been denied. Note that if this is the case,
436     // the permission state can not change again in the lifetime of this page.
437     if (isDenied())
438         notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, ASCIILiteral(permissionDeniedErrorMessage)));
439     else if (haveSuitableCachedPosition(notifier->options()))
440         notifier->setUseCachedPosition();
441     else if (notifier->hasZeroTimeout())
442         notifier->startTimerIfNeeded();
443     else if (!isAllowed()) {
444         // if we don't yet have permission, request for permission before calling startUpdating()
445         m_pendingForPermissionNotifiers.add(notifier);
446         requestPermission();
447     } else if (startUpdating(notifier))
448         notifier->startTimerIfNeeded();
449     else
450         notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));
451 }
452
453 void Geolocation::fatalErrorOccurred(Geolocation::GeoNotifier* notifier)
454 {
455     // This request has failed fatally. Remove it from our lists.
456     m_oneShots.remove(notifier);
457     m_watchers.remove(notifier);
458
459     if (!hasListeners())
460         stopUpdating();
461 }
462
463 void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier)
464 {
465     // This is called asynchronously, so the permissions could have been denied
466     // since we last checked in startRequest.
467     if (isDenied()) {
468         notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, ASCIILiteral(permissionDeniedErrorMessage)));
469         return;
470     }
471
472     m_requestsAwaitingCachedPosition.add(notifier);
473
474     // If permissions are allowed, make the callback
475     if (isAllowed()) {
476         makeCachedPositionCallbacks();
477         return;
478     }
479
480     // Request permissions, which may be synchronous or asynchronous.
481     requestPermission();
482 }
483
484 void Geolocation::makeCachedPositionCallbacks()
485 {
486     // All modifications to m_requestsAwaitingCachedPosition are done
487     // asynchronously, so we don't need to worry about it being modified from
488     // the callbacks.
489     GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end();
490     for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) {
491         GeoNotifier* notifier = iter->get();
492         notifier->runSuccessCallback(lastPosition());
493
494         // If this is a one-shot request, stop it. Otherwise, if the watch still
495         // exists, start the service to get updates.
496         if (!m_oneShots.remove(notifier) && m_watchers.contains(notifier)) {
497             if (notifier->hasZeroTimeout() || startUpdating(notifier))
498                 notifier->startTimerIfNeeded();
499             else
500                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));
501         }
502     }
503
504     m_requestsAwaitingCachedPosition.clear();
505
506     if (!hasListeners())
507         stopUpdating();
508 }
509
510 void Geolocation::requestTimedOut(GeoNotifier* notifier)
511 {
512     // If this is a one-shot request, stop it.
513     m_oneShots.remove(notifier);
514
515     if (!hasListeners())
516         stopUpdating();
517 }
518
519 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
520 {
521     Geoposition* cachedPosition = lastPosition();
522     if (!cachedPosition)
523         return false;
524     if (!options->hasMaximumAge())
525         return true;
526     if (!options->maximumAge())
527         return false;
528     DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
529     return cachedPosition->timestamp() > currentTimeMillis - options->maximumAge();
530 }
531
532 void Geolocation::clearWatch(int watchID)
533 {
534     if (watchID <= 0)
535         return;
536
537     if (GeoNotifier* notifier = m_watchers.find(watchID))
538         m_pendingForPermissionNotifiers.remove(notifier);
539     m_watchers.remove(watchID);
540     
541     if (!hasListeners())
542         stopUpdating();
543 }
544
545 void Geolocation::setIsAllowed(bool allowed)
546 {
547     // Protect the Geolocation object from garbage collection during a callback.
548     Ref<Geolocation> protect(*this);
549
550     // This may be due to either a new position from the service, or a cached
551     // position.
552     m_allowGeolocation = allowed ? Yes : No;
553     
554     if (m_isSuspended)
555         return;
556
557     // Permission request was made during the startRequest process
558     if (!m_pendingForPermissionNotifiers.isEmpty()) {
559         handlePendingPermissionNotifiers();
560         m_pendingForPermissionNotifiers.clear();
561         return;
562     }
563
564     if (!isAllowed()) {
565         RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED,  ASCIILiteral(permissionDeniedErrorMessage));
566         error->setIsFatal(true);
567         handleError(error.get());
568         m_requestsAwaitingCachedPosition.clear();
569         m_hasChangedPosition = false;
570         m_errorWaitingForResume = nullptr;
571
572         return;
573     }
574
575     // If the service has a last position, use it to call back for all requests.
576     // If any of the requests are waiting for permission for a cached position,
577     // the position from the service will be at least as fresh.
578     if (lastPosition())
579         makeSuccessCallbacks();
580     else
581         makeCachedPositionCallbacks();
582 }
583
584 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
585 {
586      GeoNotifierVector::const_iterator end = notifiers.end();
587      for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
588          RefPtr<GeoNotifier> notifier = *it;
589          
590          notifier->runErrorCallback(error);
591      }
592 }
593
594 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
595 {
596     GeoNotifierVector::const_iterator end = notifiers.end();
597     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
598         (*it)->runSuccessCallback(position);
599 }
600
601 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
602 {
603     GeoNotifierVector::const_iterator end = notifiers.end();
604     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
605         (*it)->stopTimer();
606 }
607
608 void Geolocation::stopTimersForOneShots()
609 {
610     GeoNotifierVector copy;
611     copyToVector(m_oneShots, copy);
612     
613     stopTimer(copy);
614 }
615
616 void Geolocation::stopTimersForWatchers()
617 {
618     GeoNotifierVector copy;
619     m_watchers.getNotifiersVector(copy);
620     
621     stopTimer(copy);
622 }
623
624 void Geolocation::stopTimers()
625 {
626     stopTimersForOneShots();
627     stopTimersForWatchers();
628 }
629
630 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
631 {
632     GeoNotifierVector::const_iterator end = notifiers.end();
633     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
634         (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(framelessDocumentErrorMessage)));
635 }
636
637 void Geolocation::cancelAllRequests()
638 {
639     GeoNotifierVector copy;
640     copyToVector(m_oneShots, copy);
641     cancelRequests(copy);
642     m_watchers.getNotifiersVector(copy);
643     cancelRequests(copy);
644 }
645
646 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
647 {
648     GeoNotifierVector nonCached;
649     GeoNotifierVector::iterator end = notifiers.end();
650     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
651         GeoNotifier* notifier = it->get();
652         if (notifier->useCachedPosition()) {
653             if (cached)
654                 cached->append(notifier);
655         } else
656             nonCached.append(notifier);
657     }
658     notifiers.swap(nonCached);
659 }
660
661 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
662 {
663      GeoNotifierVector::const_iterator end = src.end();
664      for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) {
665          GeoNotifier* notifier = it->get();
666          dest.add(notifier);
667      }
668 }
669
670 void Geolocation::handleError(PositionError* error)
671 {
672     ASSERT(error);
673     
674     GeoNotifierVector oneShotsCopy;
675     copyToVector(m_oneShots, oneShotsCopy);
676
677     GeoNotifierVector watchersCopy;
678     m_watchers.getNotifiersVector(watchersCopy);
679
680     // Clear the lists before we make the callbacks, to avoid clearing notifiers
681     // added by calls to Geolocation methods from the callbacks, and to prevent
682     // further callbacks to these notifiers.
683     GeoNotifierVector oneShotsWithCachedPosition;
684     m_oneShots.clear();
685     if (error->isFatal())
686         m_watchers.clear();
687     else {
688         // Don't send non-fatal errors to notifiers due to receive a cached position.
689         extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition);
690         extractNotifiersWithCachedPosition(watchersCopy, 0);
691     }
692
693     sendError(oneShotsCopy, error);
694     sendError(watchersCopy, error);
695
696     // hasListeners() doesn't distinguish between notifiers due to receive a
697     // cached position and those requiring a fresh position. Perform the check
698     // before restoring the notifiers below.
699     if (!hasListeners())
700         stopUpdating();
701
702     // Maintain a reference to the cached notifiers until their timer fires.
703     copyToSet(oneShotsWithCachedPosition, m_oneShots);
704 }
705
706 void Geolocation::requestPermission()
707 {
708     if (m_allowGeolocation > Unknown)
709         return;
710
711     Page* page = this->page();
712     if (!page)
713         return;
714
715     m_allowGeolocation = InProgress;
716
717     // Ask the embedder: it maintains the geolocation challenge policy itself.
718     GeolocationController::from(page)->requestPermission(this);
719 }
720
721 void Geolocation::makeSuccessCallbacks()
722 {
723     ASSERT(lastPosition());
724     ASSERT(isAllowed());
725     
726     GeoNotifierVector oneShotsCopy;
727     copyToVector(m_oneShots, oneShotsCopy);
728     
729     GeoNotifierVector watchersCopy;
730     m_watchers.getNotifiersVector(watchersCopy);
731     
732     // Clear the lists before we make the callbacks, to avoid clearing notifiers
733     // added by calls to Geolocation methods from the callbacks, and to prevent
734     // further callbacks to these notifiers.
735     m_oneShots.clear();
736
737     sendPosition(oneShotsCopy, lastPosition());
738     sendPosition(watchersCopy, lastPosition());
739
740     if (!hasListeners())
741         stopUpdating();
742 }
743
744 void Geolocation::positionChanged()
745 {
746     ASSERT(isAllowed());
747
748     // Stop all currently running timers.
749     stopTimers();
750
751     if (m_isSuspended) {
752         m_hasChangedPosition = true;
753         return;
754     }
755
756     makeSuccessCallbacks();
757 }
758
759 void Geolocation::setError(GeolocationError* error)
760 {
761     if (m_isSuspended) {
762         m_errorWaitingForResume = createPositionError(error);
763         return;
764     }
765     RefPtr<PositionError> positionError = createPositionError(error);
766     handleError(positionError.get());
767 }
768
769 bool Geolocation::startUpdating(GeoNotifier* notifier)
770 {
771     Page* page = this->page();
772     if (!page)
773         return false;
774
775     GeolocationController::from(page)->addObserver(this, notifier->options()->enableHighAccuracy());
776     return true;
777 }
778
779 void Geolocation::stopUpdating()
780 {
781     Page* page = this->page();
782     if (!page)
783         return;
784
785     GeolocationController::from(page)->removeObserver(this);
786 }
787
788 void Geolocation::handlePendingPermissionNotifiers()
789 {
790     // While we iterate through the list, we need not worry about list being modified as the permission 
791     // is already set to Yes/No and no new listeners will be added to the pending list
792     GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end();
793     for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) {
794         GeoNotifier* notifier = iter->get();
795
796         if (isAllowed()) {
797             // start all pending notification requests as permission granted.
798             // The notifier is always ref'ed by m_oneShots or m_watchers.
799             if (startUpdating(notifier))
800                 notifier->startTimerIfNeeded();
801             else
802                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));
803         } else
804             notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, ASCIILiteral(permissionDeniedErrorMessage)));
805     }
806 }
807
808 } // namespace WebCore
809                                                         
810 #endif // ENABLE(GEOLOCATION)