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