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