1aa1d4fc255d5b42a0100056165fa55209e8a4d5
[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 "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 blink {
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
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
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
79 Geolocation* Geolocation::create(ExecutionContext* context)
80 {
81     Geolocation* geolocation = new Geolocation(context);
82     geolocation->suspendIfNeeded();
83     return geolocation;
84 }
85
86 Geolocation::Geolocation(ExecutionContext* context)
87     : ActiveDOMObject(context)
88     , m_geolocationPermission(PermissionUnknown)
89 {
90 }
91
92 Geolocation::~Geolocation()
93 {
94     ASSERT(m_geolocationPermission != PermissionRequested);
95 }
96
97 void Geolocation::trace(Visitor* visitor)
98 {
99     visitor->trace(m_oneShots);
100     visitor->trace(m_watchers);
101     visitor->trace(m_pendingForPermissionNotifiers);
102     visitor->trace(m_lastPosition);
103     visitor->trace(m_requestsAwaitingCachedPosition);
104 }
105
106 Document* Geolocation::document() const
107 {
108     return toDocument(executionContext());
109 }
110
111 LocalFrame* Geolocation::frame() const
112 {
113     return document() ? document()->frame() : 0;
114 }
115
116 void Geolocation::stop()
117 {
118     LocalFrame* frame = this->frame();
119     if (frame && m_geolocationPermission == PermissionRequested)
120         GeolocationController::from(frame)->cancelPermissionRequest(this);
121
122     // The frame may be moving to a new page and we want to get the permissions from the new page's client.
123     m_geolocationPermission = PermissionUnknown;
124     cancelAllRequests();
125     stopUpdating();
126     m_pendingForPermissionNotifiers.clear();
127 }
128
129 Geoposition* Geolocation::lastPosition()
130 {
131     LocalFrame* frame = this->frame();
132     if (!frame)
133         return 0;
134
135     m_lastPosition = createGeoposition(GeolocationController::from(frame)->lastPosition());
136
137     return m_lastPosition.get();
138 }
139
140 void Geolocation::getCurrentPosition(PositionCallback* successCallback, PositionErrorCallback* errorCallback, const Dictionary& options)
141 {
142     if (!frame())
143         return;
144
145     GeoNotifier* notifier = GeoNotifier::create(this, successCallback, errorCallback, PositionOptions::create(options));
146     startRequest(notifier);
147
148     m_oneShots.add(notifier);
149 }
150
151 int Geolocation::watchPosition(PositionCallback* successCallback, PositionErrorCallback* errorCallback, const Dictionary& options)
152 {
153     if (!frame())
154         return 0;
155
156     GeoNotifier* notifier = GeoNotifier::create(this, successCallback, errorCallback, PositionOptions::create(options));
157     startRequest(notifier);
158
159     int watchID;
160     // Keep asking for the next id until we're given one that we don't already have.
161     do {
162         watchID = executionContext()->circularSequentialID();
163     } while (!m_watchers.add(watchID, notifier));
164     return watchID;
165 }
166
167 void Geolocation::startRequest(GeoNotifier *notifier)
168 {
169     // Check whether permissions have already been denied. Note that if this is the case,
170     // the permission state can not change again in the lifetime of this page.
171     if (isDenied())
172         notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
173     else if (haveSuitableCachedPosition(notifier->options()))
174         notifier->setUseCachedPosition();
175     else if (!notifier->options()->timeout())
176         notifier->startTimer();
177     else if (!isAllowed()) {
178         // if we don't yet have permission, request for permission before calling startUpdating()
179         m_pendingForPermissionNotifiers.add(notifier);
180         requestPermission();
181     } else if (startUpdating(notifier))
182         notifier->startTimer();
183     else
184         notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
185 }
186
187 void Geolocation::fatalErrorOccurred(GeoNotifier* notifier)
188 {
189     // This request has failed fatally. Remove it from our lists.
190     m_oneShots.remove(notifier);
191     m_watchers.remove(notifier);
192
193     if (!hasListeners())
194         stopUpdating();
195 }
196
197 void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier)
198 {
199     // This is called asynchronously, so the permissions could have been denied
200     // since we last checked in startRequest.
201     if (isDenied()) {
202         notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
203         return;
204     }
205
206     m_requestsAwaitingCachedPosition.add(notifier);
207
208     // If permissions are allowed, make the callback
209     if (isAllowed()) {
210         makeCachedPositionCallbacks();
211         return;
212     }
213
214     // Request permissions, which may be synchronous or asynchronous.
215     requestPermission();
216 }
217
218 void Geolocation::makeCachedPositionCallbacks()
219 {
220     // All modifications to m_requestsAwaitingCachedPosition are done
221     // asynchronously, so we don't need to worry about it being modified from
222     // the callbacks.
223     GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end();
224     for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) {
225         GeoNotifier* notifier = iter->get();
226         notifier->runSuccessCallback(lastPosition());
227
228         // If this is a one-shot request, stop it. Otherwise, if the watch still
229         // exists, start the service to get updates.
230         if (m_oneShots.contains(notifier))
231             m_oneShots.remove(notifier);
232         else if (m_watchers.contains(notifier)) {
233             if (!notifier->options()->timeout() || startUpdating(notifier))
234                 notifier->startTimer();
235             else
236                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
237         }
238     }
239
240     m_requestsAwaitingCachedPosition.clear();
241
242     if (!hasListeners())
243         stopUpdating();
244 }
245
246 void Geolocation::requestTimedOut(GeoNotifier* notifier)
247 {
248     // If this is a one-shot request, stop it.
249     m_oneShots.remove(notifier);
250
251     if (!hasListeners())
252         stopUpdating();
253 }
254
255 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
256 {
257     Geoposition* cachedPosition = lastPosition();
258     if (!cachedPosition)
259         return false;
260     if (!options->maximumAge())
261         return false;
262     DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
263     return cachedPosition->timestamp() > currentTimeMillis - options->maximumAge();
264 }
265
266 void Geolocation::clearWatch(int watchID)
267 {
268     if (watchID <= 0)
269         return;
270
271     if (GeoNotifier* notifier = m_watchers.find(watchID))
272         m_pendingForPermissionNotifiers.remove(notifier);
273     m_watchers.remove(watchID);
274
275     if (!hasListeners())
276         stopUpdating();
277 }
278
279 void Geolocation::setIsAllowed(bool allowed)
280 {
281     // This may be due to either a new position from the service, or a cached position.
282     m_geolocationPermission = allowed ? PermissionAllowed : PermissionDenied;
283
284     // Permission request was made during the startRequest process
285     if (!m_pendingForPermissionNotifiers.isEmpty()) {
286         handlePendingPermissionNotifiers();
287         m_pendingForPermissionNotifiers.clear();
288         return;
289     }
290
291     if (!isAllowed()) {
292         PositionError* error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
293         error->setIsFatal(true);
294         handleError(error);
295         m_requestsAwaitingCachedPosition.clear();
296         return;
297     }
298
299     // If the service has a last position, use it to call back for all requests.
300     // If any of the requests are waiting for permission for a cached position,
301     // the position from the service will be at least as fresh.
302     if (lastPosition())
303         makeSuccessCallbacks();
304     else
305         makeCachedPositionCallbacks();
306 }
307
308 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
309 {
310     GeoNotifierVector::const_iterator end = notifiers.end();
311     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
312         (*it)->runErrorCallback(error);
313 }
314
315 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
316 {
317     GeoNotifierVector::const_iterator end = notifiers.end();
318     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
319         (*it)->runSuccessCallback(position);
320 }
321
322 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
323 {
324     GeoNotifierVector::const_iterator end = notifiers.end();
325     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
326         (*it)->stopTimer();
327 }
328
329 void Geolocation::stopTimersForOneShots()
330 {
331     GeoNotifierVector copy;
332     copyToVector(m_oneShots, copy);
333
334     stopTimer(copy);
335 }
336
337 void Geolocation::stopTimersForWatchers()
338 {
339     GeoNotifierVector copy;
340     m_watchers.getNotifiersVector(copy);
341
342     stopTimer(copy);
343 }
344
345 void Geolocation::stopTimers()
346 {
347     stopTimersForOneShots();
348     stopTimersForWatchers();
349 }
350
351 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
352 {
353     GeoNotifierVector::const_iterator end = notifiers.end();
354     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
355         (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, framelessDocumentErrorMessage));
356 }
357
358 void Geolocation::cancelAllRequests()
359 {
360     GeoNotifierVector copy;
361     copyToVector(m_oneShots, copy);
362     cancelRequests(copy);
363     m_watchers.getNotifiersVector(copy);
364     cancelRequests(copy);
365 }
366
367 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
368 {
369     GeoNotifierVector nonCached;
370     GeoNotifierVector::iterator end = notifiers.end();
371     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
372         GeoNotifier* notifier = it->get();
373         if (notifier->useCachedPosition()) {
374             if (cached)
375                 cached->append(notifier);
376         } else
377             nonCached.append(notifier);
378     }
379     notifiers.swap(nonCached);
380 }
381
382 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
383 {
384      GeoNotifierVector::const_iterator end = src.end();
385      for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) {
386          GeoNotifier* notifier = it->get();
387          dest.add(notifier);
388      }
389 }
390
391 void Geolocation::handleError(PositionError* error)
392 {
393     ASSERT(error);
394
395     GeoNotifierVector oneShotsCopy;
396     copyToVector(m_oneShots, oneShotsCopy);
397
398     GeoNotifierVector watchersCopy;
399     m_watchers.getNotifiersVector(watchersCopy);
400
401     // Clear the lists before we make the callbacks, to avoid clearing notifiers
402     // added by calls to Geolocation methods from the callbacks, and to prevent
403     // further callbacks to these notifiers.
404     GeoNotifierVector oneShotsWithCachedPosition;
405     m_oneShots.clear();
406     if (error->isFatal())
407         m_watchers.clear();
408     else {
409         // Don't send non-fatal errors to notifiers due to receive a cached position.
410         extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition);
411         extractNotifiersWithCachedPosition(watchersCopy, 0);
412     }
413
414     sendError(oneShotsCopy, error);
415     sendError(watchersCopy, error);
416
417     // hasListeners() doesn't distinguish between notifiers due to receive a
418     // cached position and those requiring a fresh position. Perform the check
419     // before restoring the notifiers below.
420     if (!hasListeners())
421         stopUpdating();
422
423     // Maintain a reference to the cached notifiers until their timer fires.
424     copyToSet(oneShotsWithCachedPosition, m_oneShots);
425 }
426
427 void Geolocation::requestPermission()
428 {
429     if (m_geolocationPermission != PermissionUnknown)
430         return;
431
432     LocalFrame* frame = this->frame();
433     if (!frame)
434         return;
435
436     m_geolocationPermission = PermissionRequested;
437
438     // Ask the embedder: it maintains the geolocation challenge policy itself.
439     GeolocationController::from(frame)->requestPermission(this);
440 }
441
442 void Geolocation::makeSuccessCallbacks()
443 {
444     ASSERT(lastPosition());
445     ASSERT(isAllowed());
446
447     GeoNotifierVector oneShotsCopy;
448     copyToVector(m_oneShots, oneShotsCopy);
449
450     GeoNotifierVector watchersCopy;
451     m_watchers.getNotifiersVector(watchersCopy);
452
453     // Clear the lists before we make the callbacks, to avoid clearing notifiers
454     // added by calls to Geolocation methods from the callbacks, and to prevent
455     // further callbacks to these notifiers.
456     m_oneShots.clear();
457
458     // Also clear the set of notifiers waiting for a cached position. All the
459     // oneshots and watchers will receive a position now, and if they happen to
460     // be lingering in that set, avoid this bug: http://crbug.com/311876 .
461     m_requestsAwaitingCachedPosition.clear();
462
463     sendPosition(oneShotsCopy, lastPosition());
464     sendPosition(watchersCopy, lastPosition());
465
466     if (!hasListeners())
467         stopUpdating();
468 }
469
470 void Geolocation::positionChanged()
471 {
472     ASSERT(isAllowed());
473
474     // Stop all currently running timers.
475     stopTimers();
476
477     makeSuccessCallbacks();
478 }
479
480 void Geolocation::setError(GeolocationError* error)
481 {
482     handleError(createPositionError(error));
483 }
484
485 bool Geolocation::startUpdating(GeoNotifier* notifier)
486 {
487     LocalFrame* frame = this->frame();
488     if (!frame)
489         return false;
490
491     GeolocationController::from(frame)->addObserver(this, notifier->options()->enableHighAccuracy());
492     return true;
493 }
494
495 void Geolocation::stopUpdating()
496 {
497     LocalFrame* frame = this->frame();
498     if (!frame)
499         return;
500
501     GeolocationController::from(frame)->removeObserver(this);
502 }
503
504 void Geolocation::handlePendingPermissionNotifiers()
505 {
506     // While we iterate through the list, we need not worry about list being modified as the permission
507     // is already set to Yes/No and no new listeners will be added to the pending list
508     GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end();
509     for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) {
510         GeoNotifier* notifier = iter->get();
511
512         if (isAllowed()) {
513             // start all pending notification requests as permission granted.
514             // The notifier is always ref'ed by m_oneShots or m_watchers.
515             if (startUpdating(notifier))
516                 notifier->startTimer();
517             else
518                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
519         } else {
520             notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
521         }
522     }
523 }
524
525 } // namespace blink