1/*
2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#include "modules/geolocation/GeolocationController.h"
29
30#include "core/inspector/InspectorController.h"
31#include "core/page/Page.h"
32#include "modules/geolocation/GeolocationClient.h"
33#include "modules/geolocation/GeolocationError.h"
34#include "modules/geolocation/GeolocationInspectorAgent.h"
35#include "modules/geolocation/GeolocationPosition.h"
36
37namespace WebCore {
38
39GeolocationController::GeolocationController(LocalFrame& frame, GeolocationClient* client)
40    : PageLifecycleObserver(frame.page())
41    , m_client(client)
42    , m_hasClientForTest(false)
43    , m_isClientUpdating(false)
44    , m_inspectorAgent()
45{
46    // FIXME: Once GeolocationInspectorAgent is per frame, there will be a 1:1 relationship between
47    // it and this class. Until then, there's one GeolocationInspectorAgent per page that the main
48    // frame is responsible for creating.
49    if (frame.isMainFrame()) {
50        OwnPtr<GeolocationInspectorAgent> geolocationAgent(GeolocationInspectorAgent::create());
51        m_inspectorAgent = geolocationAgent.get();
52        frame.page()->inspectorController().registerModuleAgent(geolocationAgent.release());
53    } else {
54        m_inspectorAgent = GeolocationController::from(frame.page()->deprecatedLocalMainFrame())->m_inspectorAgent;
55    }
56
57    m_inspectorAgent->AddController(this);
58
59    if (!frame.isMainFrame()) {
60        // internals.setGeolocationClientMock is per page.
61        GeolocationController* mainController = GeolocationController::from(frame.page()->deprecatedLocalMainFrame());
62        if (mainController->hasClientForTest())
63            setClientForTest(mainController->client());
64    }
65}
66
67void GeolocationController::startUpdatingIfNeeded()
68{
69    if (m_isClientUpdating)
70        return;
71    m_isClientUpdating = true;
72    m_client->startUpdating();
73}
74
75void GeolocationController::stopUpdatingIfNeeded()
76{
77    if (!m_isClientUpdating)
78        return;
79    m_isClientUpdating = false;
80    m_client->stopUpdating();
81}
82
83GeolocationController::~GeolocationController()
84{
85    ASSERT(m_observers.isEmpty());
86    if (page())
87        m_inspectorAgent->RemoveController(this);
88
89    if (m_hasClientForTest)
90        m_client->controllerForTestRemoved(this);
91}
92
93// FIXME: Oilpan: Once GeolocationClient is on-heap m_client should be a strong
94// pointer and |willBeDestroyed| can potentially be removed from Supplement.
95void GeolocationController::willBeDestroyed()
96{
97    if (m_client)
98        m_client->geolocationDestroyed();
99}
100
101void GeolocationController::persistentHostHasBeenDestroyed()
102{
103    observeContext(0);
104}
105
106PassOwnPtrWillBeRawPtr<GeolocationController> GeolocationController::create(LocalFrame& frame, GeolocationClient* client)
107{
108    return adoptPtrWillBeNoop(new GeolocationController(frame, client));
109}
110
111void GeolocationController::addObserver(Geolocation* observer, bool enableHighAccuracy)
112{
113    // This may be called multiple times with the same observer, though removeObserver()
114    // is called only once with each.
115    bool wasEmpty = m_observers.isEmpty();
116    m_observers.add(observer);
117    if (enableHighAccuracy)
118        m_highAccuracyObservers.add(observer);
119
120    if (m_client) {
121        if (enableHighAccuracy)
122            m_client->setEnableHighAccuracy(true);
123        if (wasEmpty && page() && page()->visibilityState() == PageVisibilityStateVisible)
124            startUpdatingIfNeeded();
125    }
126}
127
128void GeolocationController::removeObserver(Geolocation* observer)
129{
130    if (!m_observers.contains(observer))
131        return;
132
133    m_observers.remove(observer);
134    m_highAccuracyObservers.remove(observer);
135
136    if (m_client) {
137        if (m_observers.isEmpty())
138            stopUpdatingIfNeeded();
139        else if (m_highAccuracyObservers.isEmpty())
140            m_client->setEnableHighAccuracy(false);
141    }
142}
143
144void GeolocationController::requestPermission(Geolocation* geolocation)
145{
146    if (m_client)
147        m_client->requestPermission(geolocation);
148}
149
150void GeolocationController::cancelPermissionRequest(Geolocation* geolocation)
151{
152    if (m_client)
153        m_client->cancelPermissionRequest(geolocation);
154}
155
156void GeolocationController::positionChanged(GeolocationPosition* position)
157{
158    position = m_inspectorAgent->overrideGeolocationPosition(position);
159    if (!position) {
160        errorOccurred(GeolocationError::create(GeolocationError::PositionUnavailable, "PositionUnavailable"));
161        return;
162    }
163    m_lastPosition = position;
164    HeapVector<Member<Geolocation> > observersVector;
165    copyToVector(m_observers, observersVector);
166    for (size_t i = 0; i < observersVector.size(); ++i)
167        observersVector[i]->positionChanged();
168}
169
170void GeolocationController::errorOccurred(GeolocationError* error)
171{
172    HeapVector<Member<Geolocation> > observersVector;
173    copyToVector(m_observers, observersVector);
174    for (size_t i = 0; i < observersVector.size(); ++i)
175        observersVector[i]->setError(error);
176}
177
178GeolocationPosition* GeolocationController::lastPosition()
179{
180    if (m_lastPosition.get())
181        return m_lastPosition.get();
182
183    if (!m_client)
184        return 0;
185
186    return m_client->lastPosition();
187}
188
189void GeolocationController::setClientForTest(GeolocationClient* client)
190{
191    if (m_hasClientForTest)
192        m_client->controllerForTestRemoved(this);
193    m_client = client;
194    m_hasClientForTest = true;
195
196    client->controllerForTestAdded(this);
197}
198
199void GeolocationController::pageVisibilityChanged()
200{
201    if (m_observers.isEmpty() || !m_client)
202        return;
203
204    if (page() && page()->visibilityState() == PageVisibilityStateVisible)
205        startUpdatingIfNeeded();
206    else
207        stopUpdatingIfNeeded();
208}
209
210const char* GeolocationController::supplementName()
211{
212    return "GeolocationController";
213}
214
215void GeolocationController::trace(Visitor* visitor)
216{
217    visitor->trace(m_lastPosition);
218    visitor->trace(m_observers);
219    visitor->trace(m_highAccuracyObservers);
220    WillBeHeapSupplement<LocalFrame>::trace(visitor);
221}
222
223void provideGeolocationTo(LocalFrame& frame, GeolocationClient* client)
224{
225    WillBeHeapSupplement<LocalFrame>::provideTo(frame, GeolocationController::supplementName(), GeolocationController::create(frame, client));
226}
227
228} // namespace WebCore
229