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