1// Copyright 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 "chrome/browser/geolocation/geolocation_permission_context.h" 6 7#include <functional> 8#include <string> 9#include <vector> 10 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/tab_specific_content_settings.h" 16#include "chrome/browser/profiles/profile.h" 17#include "chrome/browser/tab_contents/tab_util.h" 18#include "chrome/browser/ui/website_settings/permission_bubble_manager.h" 19#include "chrome/browser/ui/website_settings/permission_bubble_request.h" 20#include "chrome/common/pref_names.h" 21#include "chrome/grit/generated_resources.h" 22#include "components/content_settings/core/common/permission_request_id.h" 23#include "content/public/browser/browser_thread.h" 24#include "content/public/browser/render_process_host.h" 25#include "content/public/browser/render_view_host.h" 26#include "content/public/browser/web_contents.h" 27#include "grit/theme_resources.h" 28#include "net/base/net_util.h" 29#include "ui/base/l10n/l10n_util.h" 30 31class GeolocationPermissionRequest : public PermissionBubbleRequest { 32 public: 33 GeolocationPermissionRequest(GeolocationPermissionContext* context, 34 const PermissionRequestID& id, 35 const GURL& requesting_frame, 36 const GURL& embedder, 37 bool user_gesture, 38 base::Callback<void(bool)> callback, 39 const std::string& display_languages); 40 virtual ~GeolocationPermissionRequest(); 41 42 // PermissionBubbleDelegate: 43 virtual int GetIconID() const OVERRIDE; 44 virtual base::string16 GetMessageText() const OVERRIDE; 45 virtual base::string16 GetMessageTextFragment() const OVERRIDE; 46 virtual bool HasUserGesture() const OVERRIDE; 47 virtual GURL GetRequestingHostname() const OVERRIDE; 48 virtual void PermissionGranted() OVERRIDE; 49 virtual void PermissionDenied() OVERRIDE; 50 virtual void Cancelled() OVERRIDE; 51 virtual void RequestFinished() OVERRIDE; 52 53 private: 54 GeolocationPermissionContext* context_; 55 PermissionRequestID id_; 56 GURL requesting_frame_; 57 GURL embedder_; 58 bool user_gesture_; 59 base::Callback<void(bool)> callback_; 60 std::string display_languages_; 61}; 62 63GeolocationPermissionRequest::GeolocationPermissionRequest( 64 GeolocationPermissionContext* context, 65 const PermissionRequestID& id, 66 const GURL& requesting_frame, 67 const GURL& embedder, 68 bool user_gesture, 69 base::Callback<void(bool)> callback, 70 const std::string& display_languages) 71 : context_(context), 72 id_(id), 73 requesting_frame_(requesting_frame), 74 embedder_(embedder), 75 user_gesture_(user_gesture), 76 callback_(callback), 77 display_languages_(display_languages) {} 78 79GeolocationPermissionRequest::~GeolocationPermissionRequest() {} 80 81int GeolocationPermissionRequest::GetIconID() const { 82 return IDR_INFOBAR_GEOLOCATION; 83} 84 85base::string16 GeolocationPermissionRequest::GetMessageText() const { 86 return l10n_util::GetStringFUTF16( 87 IDS_GEOLOCATION_INFOBAR_QUESTION, 88 net::FormatUrl(requesting_frame_.GetOrigin(), display_languages_, 89 net::kFormatUrlOmitUsernamePassword | 90 net::kFormatUrlOmitTrailingSlashOnBareHostname, 91 net::UnescapeRule::SPACES, NULL, NULL, NULL)); 92} 93 94base::string16 GeolocationPermissionRequest::GetMessageTextFragment() const { 95 return l10n_util::GetStringUTF16(IDS_GEOLOCATION_INFOBAR_PERMISSION_FRAGMENT); 96} 97 98bool GeolocationPermissionRequest::HasUserGesture() const { 99 return user_gesture_; 100} 101 102GURL GeolocationPermissionRequest::GetRequestingHostname() const { 103 return requesting_frame_; 104} 105 106void GeolocationPermissionRequest::PermissionGranted() { 107 context_->QueueController()->UpdateContentSetting( 108 requesting_frame_, embedder_, true); 109 context_->NotifyPermissionSet(id_, requesting_frame_, callback_, true); 110} 111 112void GeolocationPermissionRequest::PermissionDenied() { 113 context_->QueueController()->UpdateContentSetting( 114 requesting_frame_, embedder_, false); 115 context_->NotifyPermissionSet(id_, requesting_frame_, callback_, false); 116} 117 118void GeolocationPermissionRequest::Cancelled() { 119} 120 121void GeolocationPermissionRequest::RequestFinished() { 122 // Deletes 'this'. 123 context_->RequestFinished(this); 124} 125 126 127GeolocationPermissionContext::GeolocationPermissionContext( 128 Profile* profile) 129 : profile_(profile), 130 shutting_down_(false), 131 extensions_context_(profile) { 132} 133 134GeolocationPermissionContext::~GeolocationPermissionContext() { 135 // GeolocationPermissionContext may be destroyed on either the UI thread 136 // or the IO thread, but the PermissionQueueController must have been 137 // destroyed on the UI thread. 138 DCHECK(!permission_queue_controller_.get()); 139} 140 141void GeolocationPermissionContext::RequestGeolocationPermission( 142 content::WebContents* web_contents, 143 int bridge_id, 144 const GURL& requesting_frame, 145 bool user_gesture, 146 base::Callback<void(bool)> result_callback, 147 base::Closure* cancel_callback) { 148 GURL requesting_frame_origin = requesting_frame.GetOrigin(); 149 if (shutting_down_) 150 return; 151 152 int render_process_id = web_contents->GetRenderProcessHost()->GetID(); 153 int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID(); 154 if (cancel_callback) { 155 *cancel_callback = base::Bind( 156 &GeolocationPermissionContext::CancelGeolocationPermissionRequest, 157 this, render_process_id, render_view_id, bridge_id); 158 } 159 160 const PermissionRequestID id( 161 render_process_id, render_view_id, bridge_id, GURL()); 162 163 bool permission_set; 164 bool new_permission; 165 if (extensions_context_.RequestPermission( 166 web_contents, id, bridge_id, requesting_frame, user_gesture, 167 result_callback, &permission_set, &new_permission)) { 168 if (permission_set) { 169 NotifyPermissionSet(id, requesting_frame_origin, result_callback, 170 new_permission); 171 } 172 return; 173 } 174 175 GURL embedder = web_contents->GetLastCommittedURL().GetOrigin(); 176 if (!requesting_frame_origin.is_valid() || !embedder.is_valid()) { 177 LOG(WARNING) << "Attempt to use geolocation from an invalid URL: " 178 << requesting_frame_origin << "," << embedder 179 << " (geolocation is not supported in popups)"; 180 NotifyPermissionSet(id, requesting_frame_origin, result_callback, false); 181 return; 182 } 183 184 DecidePermission(web_contents, id, requesting_frame_origin, user_gesture, 185 embedder, result_callback); 186} 187 188void GeolocationPermissionContext::CancelGeolocationPermissionRequest( 189 int render_process_id, 190 int render_view_id, 191 int bridge_id) { 192 content::WebContents* web_contents = tab_util::GetWebContentsByID( 193 render_process_id, render_view_id); 194 if (extensions_context_.CancelPermissionRequest(web_contents, bridge_id)) 195 return; 196 197 CancelPendingInfobarRequest(PermissionRequestID( 198 render_process_id, render_view_id, bridge_id, GURL())); 199} 200 201void GeolocationPermissionContext::DecidePermission( 202 content::WebContents* web_contents, 203 const PermissionRequestID& id, 204 const GURL& requesting_frame, 205 bool user_gesture, 206 const GURL& embedder, 207 base::Callback<void(bool)> callback) { 208 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 209 210 ContentSetting content_setting = 211 profile_->GetHostContentSettingsMap() 212 ->GetContentSettingAndMaybeUpdateLastUsage( 213 requesting_frame, 214 embedder, 215 CONTENT_SETTINGS_TYPE_GEOLOCATION, 216 std::string()); 217 switch (content_setting) { 218 case CONTENT_SETTING_BLOCK: 219 PermissionDecided(id, requesting_frame, embedder, callback, false); 220 break; 221 case CONTENT_SETTING_ALLOW: 222 PermissionDecided(id, requesting_frame, embedder, callback, true); 223 break; 224 default: 225 if (PermissionBubbleManager::Enabled()) { 226 PermissionBubbleManager* mgr = 227 PermissionBubbleManager::FromWebContents(web_contents); 228 if (mgr) { 229 scoped_ptr<GeolocationPermissionRequest> request_ptr( 230 new GeolocationPermissionRequest( 231 this, id, requesting_frame, embedder, user_gesture, callback, 232 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages))); 233 GeolocationPermissionRequest* request = request_ptr.get(); 234 pending_requests_.add(id.ToString(), request_ptr.Pass()); 235 mgr->AddRequest(request); 236 } 237 } else { 238 // setting == ask. Prompt the user. 239 QueueController()->CreateInfoBarRequest( 240 id, requesting_frame, embedder, 241 base::Bind( 242 &GeolocationPermissionContext::NotifyPermissionSet, 243 base::Unretained(this), id, requesting_frame, callback)); 244 } 245 } 246} 247 248void GeolocationPermissionContext::CreateInfoBarRequest( 249 const PermissionRequestID& id, 250 const GURL& requesting_frame, 251 const GURL& embedder, 252 base::Callback<void(bool)> callback) { 253 QueueController()->CreateInfoBarRequest( 254 id, requesting_frame, embedder, base::Bind( 255 &GeolocationPermissionContext::NotifyPermissionSet, 256 base::Unretained(this), id, requesting_frame, callback)); 257} 258 259void GeolocationPermissionContext::RequestFinished( 260 GeolocationPermissionRequest* request) { 261 base::ScopedPtrHashMap<std::string, 262 GeolocationPermissionRequest>::iterator it; 263 for (it = pending_requests_.begin(); it != pending_requests_.end(); ++it) { 264 if (it->second == request) { 265 pending_requests_.take_and_erase(it); 266 return; 267 } 268 } 269} 270 271 272void GeolocationPermissionContext::ShutdownOnUIThread() { 273 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 274 permission_queue_controller_.reset(); 275 shutting_down_ = true; 276} 277 278void GeolocationPermissionContext::PermissionDecided( 279 const PermissionRequestID& id, 280 const GURL& requesting_frame, 281 const GURL& embedder, 282 base::Callback<void(bool)> callback, 283 bool allowed) { 284 NotifyPermissionSet(id, requesting_frame, callback, allowed); 285} 286 287void GeolocationPermissionContext::NotifyPermissionSet( 288 const PermissionRequestID& id, 289 const GURL& requesting_frame, 290 base::Callback<void(bool)> callback, 291 bool allowed) { 292 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 293 294 // WebContents may have gone away (or not exists for extension). 295 TabSpecificContentSettings* content_settings = 296 TabSpecificContentSettings::Get(id.render_process_id(), 297 id.render_view_id()); 298 if (content_settings) { 299 content_settings->OnGeolocationPermissionSet(requesting_frame.GetOrigin(), 300 allowed); 301 } 302 303 callback.Run(allowed); 304} 305 306PermissionQueueController* 307 GeolocationPermissionContext::QueueController() { 308 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 309 DCHECK(!shutting_down_); 310 if (!permission_queue_controller_) 311 permission_queue_controller_.reset(CreateQueueController()); 312 return permission_queue_controller_.get(); 313} 314 315PermissionQueueController* 316 GeolocationPermissionContext::CreateQueueController() { 317 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 318 return new PermissionQueueController(profile(), 319 CONTENT_SETTINGS_TYPE_GEOLOCATION); 320} 321 322void GeolocationPermissionContext::CancelPendingInfobarRequest( 323 const PermissionRequestID& id) { 324 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 325 if (shutting_down_) 326 return; 327 328 if (PermissionBubbleManager::Enabled()) { 329 GeolocationPermissionRequest* cancelling = 330 pending_requests_.get(id.ToString()); 331 content::WebContents* web_contents = tab_util::GetWebContentsByID( 332 id.render_process_id(), id.render_view_id()); 333 if (cancelling != NULL && web_contents != NULL && 334 PermissionBubbleManager::FromWebContents(web_contents) != NULL) { 335 PermissionBubbleManager::FromWebContents(web_contents)-> 336 CancelRequest(cancelling); 337 } 338 return; 339 } 340 341 QueueController()->CancelInfoBarRequest(id); 342} 343