1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2012 Apple Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "GeolocationClientMock.h"
34
35#if ENABLE(GEOLOCATION)
36
37#include "GeolocationController.h"
38#include "GeolocationError.h"
39#include "GeolocationPosition.h"
40
41namespace WebCore {
42
43GeolocationClientMock::GeolocationClientMock()
44    : m_controller(0)
45    , m_hasError(false)
46    , m_controllerTimer(this, &GeolocationClientMock::controllerTimerFired)
47    , m_permissionTimer(this, &GeolocationClientMock::permissionTimerFired)
48    , m_isActive(false)
49    , m_permissionState(PermissionStateUnset)
50{
51}
52
53GeolocationClientMock::~GeolocationClientMock()
54{
55    ASSERT(!m_isActive);
56}
57
58void GeolocationClientMock::setController(GeolocationController *controller)
59{
60    ASSERT(controller && !m_controller);
61    m_controller = controller;
62}
63
64void GeolocationClientMock::setPosition(PassRefPtr<GeolocationPosition> position)
65{
66    m_lastPosition = position;
67    clearError();
68    asyncUpdateController();
69}
70
71void GeolocationClientMock::setPositionUnavailableError(const String& errorMessage)
72{
73    m_hasError = true;
74    m_errorMessage = errorMessage;
75    m_lastPosition = nullptr;
76    asyncUpdateController();
77}
78
79void GeolocationClientMock::setPermission(bool allowed)
80{
81    m_permissionState = allowed ? PermissionStateAllowed : PermissionStateDenied;
82    asyncUpdatePermission();
83}
84
85int GeolocationClientMock::numberOfPendingPermissionRequests() const
86{
87    return m_pendingPermission.size();
88}
89
90void GeolocationClientMock::requestPermission(Geolocation* geolocation)
91{
92    m_pendingPermission.add(geolocation);
93    if (m_permissionState != PermissionStateUnset)
94        asyncUpdatePermission();
95}
96
97void GeolocationClientMock::cancelPermissionRequest(Geolocation* geolocation)
98{
99    // Called from Geolocation::disconnectFrame() in response to Frame destruction.
100    m_pendingPermission.remove(geolocation);
101    if (m_pendingPermission.isEmpty() && m_permissionTimer.isActive())
102        m_permissionTimer.stop();
103}
104
105void GeolocationClientMock::asyncUpdatePermission()
106{
107    ASSERT(m_permissionState != PermissionStateUnset);
108    if (!m_permissionTimer.isActive())
109        m_permissionTimer.startOneShot(0);
110}
111
112void GeolocationClientMock::permissionTimerFired(WebCore::Timer<GeolocationClientMock>* timer)
113{
114    ASSERT_UNUSED(timer, timer == &m_permissionTimer);
115    ASSERT(m_permissionState != PermissionStateUnset);
116    bool allowed = m_permissionState == PermissionStateAllowed;
117    GeolocationSet::iterator end = m_pendingPermission.end();
118
119    // Once permission has been set (or denied) on a Geolocation object, there can be
120    // no further requests for permission to the mock. Consequently the callbacks
121    // which fire synchronously from Geolocation::setIsAllowed() cannot reentrantly modify
122    // m_pendingPermission.
123    for (GeolocationSet::iterator it = m_pendingPermission.begin(); it != end; ++it)
124        (*it)->setIsAllowed(allowed);
125    m_pendingPermission.clear();
126}
127
128void GeolocationClientMock::reset()
129{
130    m_lastPosition = 0;
131    clearError();
132    m_permissionState = PermissionStateUnset;
133}
134
135void GeolocationClientMock::geolocationDestroyed()
136{
137    ASSERT(!m_isActive);
138}
139
140void GeolocationClientMock::startUpdating()
141{
142    ASSERT(!m_isActive);
143    m_isActive = true;
144    asyncUpdateController();
145}
146
147void GeolocationClientMock::stopUpdating()
148{
149    ASSERT(m_isActive);
150    m_isActive = false;
151    m_controllerTimer.stop();
152}
153
154void GeolocationClientMock::setEnableHighAccuracy(bool)
155{
156    // FIXME: We need to add some tests regarding "high accuracy" mode.
157    // See https://bugs.webkit.org/show_bug.cgi?id=49438
158}
159
160GeolocationPosition* GeolocationClientMock::lastPosition()
161{
162    return m_lastPosition.get();
163}
164
165void GeolocationClientMock::asyncUpdateController()
166{
167    ASSERT(m_controller);
168    if (m_isActive && !m_controllerTimer.isActive())
169        m_controllerTimer.startOneShot(0);
170}
171
172void GeolocationClientMock::controllerTimerFired(Timer<GeolocationClientMock>* timer)
173{
174    ASSERT_UNUSED(timer, timer == &m_controllerTimer);
175    ASSERT(m_controller);
176
177    if (m_lastPosition.get()) {
178        ASSERT(!m_hasError);
179        m_controller->positionChanged(m_lastPosition.get());
180    } else if (m_hasError) {
181        RefPtr<GeolocationError> geolocatioError = GeolocationError::create(GeolocationError::PositionUnavailable, m_errorMessage);
182        m_controller->errorOccurred(geolocatioError.get());
183    }
184}
185
186void GeolocationClientMock::clearError()
187{
188    m_hasError = false;
189    m_errorMessage = String();
190}
191
192} // WebCore
193
194#endif // ENABLE(GEOLOCATION)
195