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.
5 #include "chrome/browser/geolocation/chrome_geolocation_permission_context.h"
11 #include "base/bind.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/content_settings/host_content_settings_map.h"
15 #include "chrome/browser/content_settings/permission_request_id.h"
16 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
17 #include "chrome/browser/extensions/suggest_permission_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/tab_contents/tab_util.h"
20 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
21 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
22 #include "chrome/common/pref_names.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/process_map.h"
28 #include "extensions/browser/view_type_utils.h"
29 #include "extensions/common/extension.h"
30 #include "grit/generated_resources.h"
31 #include "net/base/net_util.h"
32 #include "ui/base/l10n/l10n_util.h"
34 using extensions::APIPermission
;
35 using extensions::ExtensionRegistry
;
37 class GeolocationPermissionRequest
: public PermissionBubbleRequest
{
39 GeolocationPermissionRequest(
40 ChromeGeolocationPermissionContext
* context
,
41 const PermissionRequestID
& id
,
42 const GURL
& requesting_frame
,
43 base::Callback
<void(bool)> callback
,
44 const std::string
& display_languages
);
45 virtual ~GeolocationPermissionRequest();
47 // PermissionBubbleDelegate:
48 virtual base::string16
GetMessageText() const OVERRIDE
;
49 virtual base::string16
GetMessageTextFragment() const OVERRIDE
;
50 virtual base::string16
GetAlternateAcceptButtonText() const OVERRIDE
;
51 virtual base::string16
GetAlternateDenyButtonText() const OVERRIDE
;
52 virtual void PermissionGranted() OVERRIDE
;
53 virtual void PermissionDenied() OVERRIDE
;
54 virtual void Cancelled() OVERRIDE
;
55 virtual void RequestFinished() OVERRIDE
;
58 ChromeGeolocationPermissionContext
* context_
;
59 PermissionRequestID id_
;
60 GURL requesting_frame_
;
61 base::Callback
<void(bool)> callback_
;
62 std::string display_languages_
;
65 GeolocationPermissionRequest::GeolocationPermissionRequest(
66 ChromeGeolocationPermissionContext
* context
,
67 const PermissionRequestID
& id
,
68 const GURL
& requesting_frame
,
69 base::Callback
<void(bool)> callback
,
70 const std::string
& display_languages
)
73 requesting_frame_(requesting_frame
),
75 display_languages_(display_languages
) {}
77 GeolocationPermissionRequest::~GeolocationPermissionRequest() {}
79 base::string16
GeolocationPermissionRequest::GetMessageText() const {
80 return l10n_util::GetStringFUTF16(IDS_GEOLOCATION_INFOBAR_QUESTION
,
81 net::FormatUrl(requesting_frame_
, display_languages_
));
84 base::string16
GeolocationPermissionRequest::GetMessageTextFragment() const {
85 return l10n_util::GetStringUTF16(IDS_GEOLOCATION_INFOBAR_PERMISSION_FRAGMENT
);
89 GeolocationPermissionRequest::GetAlternateAcceptButtonText() const {
90 return l10n_util::GetStringUTF16(IDS_GEOLOCATION_ALLOW_BUTTON
);
94 GeolocationPermissionRequest::GetAlternateDenyButtonText() const {
95 return l10n_util::GetStringUTF16(IDS_GEOLOCATION_DENY_BUTTON
);
98 void GeolocationPermissionRequest::PermissionGranted() {
99 context_
->NotifyPermissionSet(id_
, requesting_frame_
, callback_
, true);
102 void GeolocationPermissionRequest::PermissionDenied() {
103 context_
->NotifyPermissionSet(id_
, requesting_frame_
, callback_
, false);
106 void GeolocationPermissionRequest::Cancelled() {
107 context_
->NotifyPermissionSet(id_
, requesting_frame_
, callback_
, false);
110 void GeolocationPermissionRequest::RequestFinished() {
115 ChromeGeolocationPermissionContext::ChromeGeolocationPermissionContext(
118 shutting_down_(false) {
121 ChromeGeolocationPermissionContext::~ChromeGeolocationPermissionContext() {
122 // ChromeGeolocationPermissionContext may be destroyed on either the UI thread
123 // or the IO thread, but the PermissionQueueController must have been
124 // destroyed on the UI thread.
125 DCHECK(!permission_queue_controller_
.get());
128 void ChromeGeolocationPermissionContext::RequestGeolocationPermission(
129 int render_process_id
,
132 const GURL
& requesting_frame
,
133 base::Callback
<void(bool)> callback
) {
134 GURL requesting_frame_origin
= requesting_frame
.GetOrigin();
135 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
136 content::BrowserThread::PostTask(
137 content::BrowserThread::UI
, FROM_HERE
,
139 &ChromeGeolocationPermissionContext::RequestGeolocationPermission
,
140 this, render_process_id
, render_view_id
, bridge_id
,
141 requesting_frame_origin
, callback
));
145 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
149 content::WebContents
* web_contents
=
150 tab_util::GetWebContentsByID(render_process_id
, render_view_id
);
151 const PermissionRequestID
id(render_process_id
, render_view_id
, bridge_id
, 0);
152 ExtensionRegistry
* extension_registry
= ExtensionRegistry::Get(profile_
);
153 if (extension_registry
) {
154 const extensions::Extension
* extension
=
155 extension_registry
->enabled_extensions().GetExtensionOrAppByURL(
156 requesting_frame_origin
);
157 if (IsExtensionWithPermissionOrSuggestInConsole(APIPermission::kGeolocation
,
160 // Make sure the extension is in the calling process.
161 if (extensions::ProcessMap::Get(profile_
)
162 ->Contains(extension
->id(), id
.render_process_id())) {
163 NotifyPermissionSet(id
, requesting_frame_origin
, callback
, true);
169 if (extensions::GetViewType(web_contents
) !=
170 extensions::VIEW_TYPE_TAB_CONTENTS
) {
171 // The tab may have gone away, or the request may not be from a tab at all.
172 // TODO(mpcomplete): the request could be from a background page or
173 // extension popup (web_contents will have a different ViewType). But why do
174 // we care? Shouldn't we still put an infobar up in the current tab?
175 LOG(WARNING
) << "Attempt to use geolocation tabless renderer: "
177 << " (can't prompt user without a visible tab)";
178 NotifyPermissionSet(id
, requesting_frame_origin
, callback
, false);
182 GURL embedder
= web_contents
->GetLastCommittedURL().GetOrigin();
183 if (!requesting_frame_origin
.is_valid() || !embedder
.is_valid()) {
184 LOG(WARNING
) << "Attempt to use geolocation from an invalid URL: "
185 << requesting_frame_origin
<< "," << embedder
186 << " (geolocation is not supported in popups)";
187 NotifyPermissionSet(id
, requesting_frame_origin
, callback
, false);
191 DecidePermission(web_contents
, id
, requesting_frame_origin
,
195 void ChromeGeolocationPermissionContext::CancelGeolocationPermissionRequest(
196 int render_process_id
,
199 const GURL
& requesting_frame
) {
200 CancelPendingInfobarRequest(PermissionRequestID(
201 render_process_id
, render_view_id
, bridge_id
, 0));
204 void ChromeGeolocationPermissionContext::DecidePermission(
205 content::WebContents
* web_contents
,
206 const PermissionRequestID
& id
,
207 const GURL
& requesting_frame
,
208 const GURL
& embedder
,
209 base::Callback
<void(bool)> callback
) {
210 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
212 ContentSetting content_setting
=
213 profile_
->GetHostContentSettingsMap()->GetContentSetting(
214 requesting_frame
, embedder
, CONTENT_SETTINGS_TYPE_GEOLOCATION
,
216 switch (content_setting
) {
217 case CONTENT_SETTING_BLOCK
:
218 PermissionDecided(id
, requesting_frame
, embedder
, callback
, false);
220 case CONTENT_SETTING_ALLOW
:
221 PermissionDecided(id
, requesting_frame
, embedder
, callback
, true);
224 if (PermissionBubbleManager::Enabled()) {
225 PermissionBubbleManager
* mgr
=
226 PermissionBubbleManager::FromWebContents(web_contents
);
227 mgr
->AddRequest(new GeolocationPermissionRequest(
228 this, id
, requesting_frame
, callback
,
229 profile_
->GetPrefs()->GetString(prefs::kAcceptLanguages
)));
231 // setting == ask. Prompt the user.
232 QueueController()->CreateInfoBarRequest(
233 id
, requesting_frame
, embedder
, base::Bind(
234 &ChromeGeolocationPermissionContext::NotifyPermissionSet
,
235 base::Unretained(this), id
, requesting_frame
, callback
));
240 void ChromeGeolocationPermissionContext::ShutdownOnUIThread() {
241 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
242 permission_queue_controller_
.reset();
243 shutting_down_
= true;
246 void ChromeGeolocationPermissionContext::PermissionDecided(
247 const PermissionRequestID
& id
,
248 const GURL
& requesting_frame
,
249 const GURL
& embedder
,
250 base::Callback
<void(bool)> callback
,
252 NotifyPermissionSet(id
, requesting_frame
, callback
, allowed
);
255 void ChromeGeolocationPermissionContext::NotifyPermissionSet(
256 const PermissionRequestID
& id
,
257 const GURL
& requesting_frame
,
258 base::Callback
<void(bool)> callback
,
260 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
262 // WebContents may have gone away (or not exists for extension).
263 TabSpecificContentSettings
* content_settings
=
264 TabSpecificContentSettings::Get(id
.render_process_id(),
265 id
.render_view_id());
266 if (content_settings
) {
267 content_settings
->OnGeolocationPermissionSet(requesting_frame
.GetOrigin(),
271 callback
.Run(allowed
);
274 PermissionQueueController
*
275 ChromeGeolocationPermissionContext::QueueController() {
276 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
277 DCHECK(!shutting_down_
);
278 if (!permission_queue_controller_
)
279 permission_queue_controller_
.reset(CreateQueueController());
280 return permission_queue_controller_
.get();
283 PermissionQueueController
*
284 ChromeGeolocationPermissionContext::CreateQueueController() {
285 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
286 return new PermissionQueueController(profile(),
287 CONTENT_SETTINGS_TYPE_GEOLOCATION
);
290 void ChromeGeolocationPermissionContext::CancelPendingInfobarRequest(
291 const PermissionRequestID
& id
) {
292 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
293 content::BrowserThread::PostTask(
294 content::BrowserThread::UI
, FROM_HERE
,
296 &ChromeGeolocationPermissionContext::CancelPendingInfobarRequest
,
300 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
304 // TODO(gbillock): handle permission bubble cancellation.
305 QueueController()->CancelInfoBarRequest(id
);