1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/geolocation_dispatcher.h"
6
7#include "content/common/geolocation_messages.h"
8#include "content/renderer/render_view_impl.h"
9#include "third_party/WebKit/public/platform/WebString.h"
10#include "third_party/WebKit/public/web/WebGeolocationPermissionRequest.h"
11#include "third_party/WebKit/public/web/WebGeolocationPermissionRequestManager.h"
12#include "third_party/WebKit/public/web/WebGeolocationClient.h"
13#include "third_party/WebKit/public/web/WebGeolocationPosition.h"
14#include "third_party/WebKit/public/web/WebGeolocationError.h"
15
16using WebKit::WebGeolocationController;
17using WebKit::WebGeolocationError;
18using WebKit::WebGeolocationPermissionRequest;
19using WebKit::WebGeolocationPermissionRequestManager;
20using WebKit::WebGeolocationPosition;
21
22namespace content {
23
24GeolocationDispatcher::GeolocationDispatcher(RenderViewImpl* render_view)
25    : RenderViewObserver(render_view),
26      pending_permissions_(new WebGeolocationPermissionRequestManager()),
27      enable_high_accuracy_(false),
28      updating_(false) {
29}
30
31GeolocationDispatcher::~GeolocationDispatcher() {}
32
33bool GeolocationDispatcher::OnMessageReceived(const IPC::Message& message) {
34  bool handled = true;
35  IPC_BEGIN_MESSAGE_MAP(GeolocationDispatcher, message)
36    IPC_MESSAGE_HANDLER(GeolocationMsg_PermissionSet, OnPermissionSet)
37    IPC_MESSAGE_HANDLER(GeolocationMsg_PositionUpdated, OnPositionUpdated)
38    IPC_MESSAGE_UNHANDLED(handled = false)
39  IPC_END_MESSAGE_MAP()
40  return handled;
41}
42
43void GeolocationDispatcher::geolocationDestroyed() {
44  controller_.reset();
45  DCHECK(!updating_);
46}
47
48void GeolocationDispatcher::startUpdating() {
49  GURL url;
50  Send(new GeolocationHostMsg_StartUpdating(
51      routing_id(), url, enable_high_accuracy_));
52  updating_ = true;
53}
54
55void GeolocationDispatcher::stopUpdating() {
56  Send(new GeolocationHostMsg_StopUpdating(routing_id()));
57  updating_ = false;
58}
59
60void GeolocationDispatcher::setEnableHighAccuracy(bool enable_high_accuracy) {
61  // GeolocationController calls setEnableHighAccuracy(true) before
62  // startUpdating in response to the first high-accuracy Geolocation
63  // subscription. When the last high-accuracy Geolocation unsubscribes
64  // it calls setEnableHighAccuracy(false) after stopUpdating.
65  bool has_changed = enable_high_accuracy_ != enable_high_accuracy;
66  enable_high_accuracy_ = enable_high_accuracy;
67  // We have a different accuracy requirement. Request browser to update.
68  if (updating_ && has_changed)
69    startUpdating();
70}
71
72void GeolocationDispatcher::setController(
73    WebGeolocationController* controller) {
74  controller_.reset(controller);
75}
76
77bool GeolocationDispatcher::lastPosition(WebGeolocationPosition&) {
78  // The latest position is stored in the browser, not the renderer, so we
79  // would have to fetch it synchronously to give a good value here.  The
80  // WebCore::GeolocationController already caches the last position it
81  // receives, so there is not much benefit to more position caching here.
82  return false;
83}
84
85// TODO(jknotten): Change the messages to use a security origin, so no
86// conversion is necessary.
87void GeolocationDispatcher::requestPermission(
88    const WebGeolocationPermissionRequest& permissionRequest) {
89  int bridge_id = pending_permissions_->add(permissionRequest);
90  string16 origin = permissionRequest.securityOrigin().toString();
91  Send(new GeolocationHostMsg_RequestPermission(
92      routing_id(), bridge_id, GURL(origin)));
93}
94
95// TODO(jknotten): Change the messages to use a security origin, so no
96// conversion is necessary.
97void GeolocationDispatcher::cancelPermissionRequest(
98    const WebGeolocationPermissionRequest& permissionRequest) {
99  int bridge_id;
100  if (!pending_permissions_->remove(permissionRequest, bridge_id))
101    return;
102  string16 origin = permissionRequest.securityOrigin().toString();
103  Send(new GeolocationHostMsg_CancelPermissionRequest(
104      routing_id(), bridge_id, GURL(origin)));
105}
106
107// Permission for using geolocation has been set.
108void GeolocationDispatcher::OnPermissionSet(int bridge_id, bool is_allowed) {
109  WebGeolocationPermissionRequest permissionRequest;
110  if (!pending_permissions_->remove(bridge_id, permissionRequest))
111    return;
112  permissionRequest.setIsAllowed(is_allowed);
113}
114
115// We have an updated geolocation position or error code.
116void GeolocationDispatcher::OnPositionUpdated(
117    const Geoposition& geoposition) {
118  // It is possible for the browser process to have queued an update message
119  // before receiving the stop updating message.
120  if (!updating_)
121    return;
122
123  if (geoposition.Validate()) {
124    controller_->positionChanged(
125        WebGeolocationPosition(
126            geoposition.timestamp.ToDoubleT(),
127            geoposition.latitude, geoposition.longitude,
128            geoposition.accuracy,
129            // Lowest point on land is at approximately -400 meters.
130            geoposition.altitude > -10000.,
131            geoposition.altitude,
132            geoposition.altitude_accuracy >= 0.,
133            geoposition.altitude_accuracy,
134            geoposition.heading >= 0. && geoposition.heading <= 360.,
135            geoposition.heading,
136            geoposition.speed >= 0.,
137            geoposition.speed));
138  } else {
139    WebGeolocationError::Error code;
140    switch (geoposition.error_code) {
141      case Geoposition::ERROR_CODE_PERMISSION_DENIED:
142        code = WebGeolocationError::ErrorPermissionDenied;
143        break;
144      case Geoposition::ERROR_CODE_POSITION_UNAVAILABLE:
145        code = WebGeolocationError::ErrorPositionUnavailable;
146        break;
147      default:
148        NOTREACHED() << geoposition.error_code;
149        return;
150    }
151    controller_->errorOccurred(
152        WebGeolocationError(
153            code, WebKit::WebString::fromUTF8(geoposition.error_message)));
154  }
155}
156
157}  // namespace content
158