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