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