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 "chrome/browser/geolocation/chrome_geolocation_permission_context.h" 6 7#include <functional> 8#include <string> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/strings/utf_string_conversions.h" 13#include "chrome/browser/content_settings/host_content_settings_map.h" 14#include "chrome/browser/content_settings/permission_request_id.h" 15#include "chrome/browser/content_settings/tab_specific_content_settings.h" 16#include "chrome/browser/extensions/extension_service.h" 17#include "chrome/browser/extensions/extension_system.h" 18#include "chrome/browser/extensions/suggest_permission_util.h" 19#include "chrome/browser/profiles/profile.h" 20#include "chrome/browser/tab_contents/tab_util.h" 21#include "content/public/browser/browser_thread.h" 22#include "content/public/browser/render_view_host.h" 23#include "content/public/browser/web_contents.h" 24#include "extensions/browser/view_type_utils.h" 25#include "extensions/common/extension.h" 26 27using extensions::APIPermission; 28 29ChromeGeolocationPermissionContext::ChromeGeolocationPermissionContext( 30 Profile* profile) 31 : profile_(profile), 32 shutting_down_(false) { 33} 34 35ChromeGeolocationPermissionContext::~ChromeGeolocationPermissionContext() { 36 // ChromeGeolocationPermissionContext may be destroyed on either the UI thread 37 // or the IO thread, but the PermissionQueueController must have been 38 // destroyed on the UI thread. 39 DCHECK(!permission_queue_controller_.get()); 40} 41 42void ChromeGeolocationPermissionContext::RequestGeolocationPermission( 43 int render_process_id, 44 int render_view_id, 45 int bridge_id, 46 const GURL& requesting_frame, 47 base::Callback<void(bool)> callback) { 48 GURL requesting_frame_origin = requesting_frame.GetOrigin(); 49 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { 50 content::BrowserThread::PostTask( 51 content::BrowserThread::UI, FROM_HERE, 52 base::Bind( 53 &ChromeGeolocationPermissionContext::RequestGeolocationPermission, 54 this, render_process_id, render_view_id, bridge_id, 55 requesting_frame_origin, callback)); 56 return; 57 } 58 59 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 60 if (shutting_down_) 61 return; 62 63 content::WebContents* web_contents = 64 tab_util::GetWebContentsByID(render_process_id, render_view_id); 65 const PermissionRequestID id(render_process_id, render_view_id, 66 bridge_id); 67 ExtensionService* extension_service = 68 extensions::ExtensionSystem::Get(profile_)->extension_service(); 69 if (extension_service) { 70 const extensions::Extension* extension = 71 extension_service->extensions()->GetExtensionOrAppByURL( 72 requesting_frame_origin); 73 if (IsExtensionWithPermissionOrSuggestInConsole(APIPermission::kGeolocation, 74 extension, 75 profile_)) { 76 // Make sure the extension is in the calling process. 77 if (extension_service->process_map()->Contains(extension->id(), 78 id.render_process_id())) { 79 NotifyPermissionSet(id, requesting_frame_origin, callback, true); 80 return; 81 } 82 } 83 } 84 85 if (extensions::GetViewType(web_contents) != 86 extensions::VIEW_TYPE_TAB_CONTENTS) { 87 // The tab may have gone away, or the request may not be from a tab at all. 88 // TODO(mpcomplete): the request could be from a background page or 89 // extension popup (web_contents will have a different ViewType). But why do 90 // we care? Shouldn't we still put an infobar up in the current tab? 91 LOG(WARNING) << "Attempt to use geolocation tabless renderer: " 92 << id.ToString() 93 << " (can't prompt user without a visible tab)"; 94 NotifyPermissionSet(id, requesting_frame_origin, callback, false); 95 return; 96 } 97 98 GURL embedder = web_contents->GetLastCommittedURL().GetOrigin(); 99 if (!requesting_frame_origin.is_valid() || !embedder.is_valid()) { 100 LOG(WARNING) << "Attempt to use geolocation from an invalid URL: " 101 << requesting_frame_origin << "," << embedder 102 << " (geolocation is not supported in popups)"; 103 NotifyPermissionSet(id, requesting_frame_origin, callback, false); 104 return; 105 } 106 107 DecidePermission(id, requesting_frame_origin, embedder, callback); 108} 109 110void ChromeGeolocationPermissionContext::CancelGeolocationPermissionRequest( 111 int render_process_id, 112 int render_view_id, 113 int bridge_id, 114 const GURL& requesting_frame) { 115 CancelPendingInfoBarRequest(PermissionRequestID( 116 render_process_id, render_view_id, bridge_id)); 117} 118 119void ChromeGeolocationPermissionContext::DecidePermission( 120 const PermissionRequestID& id, 121 const GURL& requesting_frame, 122 const GURL& embedder, 123 base::Callback<void(bool)> callback) { 124 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 125 126 ContentSetting content_setting = 127 profile_->GetHostContentSettingsMap()->GetContentSetting( 128 requesting_frame, embedder, CONTENT_SETTINGS_TYPE_GEOLOCATION, 129 std::string()); 130 switch (content_setting) { 131 case CONTENT_SETTING_BLOCK: 132 PermissionDecided(id, requesting_frame, embedder, callback, false); 133 break; 134 case CONTENT_SETTING_ALLOW: 135 PermissionDecided(id, requesting_frame, embedder, callback, true); 136 break; 137 default: 138 // setting == ask. Prompt the user. 139 QueueController()->CreateInfoBarRequest( 140 id, requesting_frame, embedder, base::Bind( 141 &ChromeGeolocationPermissionContext::NotifyPermissionSet, 142 base::Unretained(this), id, requesting_frame, callback)); 143 } 144} 145 146void ChromeGeolocationPermissionContext::ShutdownOnUIThread() { 147 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 148 permission_queue_controller_.reset(); 149 shutting_down_ = true; 150} 151 152void ChromeGeolocationPermissionContext::PermissionDecided( 153 const PermissionRequestID& id, 154 const GURL& requesting_frame, 155 const GURL& embedder, 156 base::Callback<void(bool)> callback, 157 bool allowed) { 158 NotifyPermissionSet(id, requesting_frame, callback, allowed); 159} 160 161void ChromeGeolocationPermissionContext::NotifyPermissionSet( 162 const PermissionRequestID& id, 163 const GURL& requesting_frame, 164 base::Callback<void(bool)> callback, 165 bool allowed) { 166 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 167 168 // WebContents may have gone away (or not exists for extension). 169 TabSpecificContentSettings* content_settings = 170 TabSpecificContentSettings::Get(id.render_process_id(), 171 id.render_view_id()); 172 if (content_settings) { 173 content_settings->OnGeolocationPermissionSet(requesting_frame.GetOrigin(), 174 allowed); 175 } 176 177 callback.Run(allowed); 178} 179 180PermissionQueueController* 181 ChromeGeolocationPermissionContext::QueueController() { 182 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 183 DCHECK(!shutting_down_); 184 if (!permission_queue_controller_) 185 permission_queue_controller_.reset(CreateQueueController()); 186 return permission_queue_controller_.get(); 187} 188 189PermissionQueueController* 190 ChromeGeolocationPermissionContext::CreateQueueController() { 191 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 192 return new PermissionQueueController(profile(), 193 CONTENT_SETTINGS_TYPE_GEOLOCATION); 194} 195 196void ChromeGeolocationPermissionContext::CancelPendingInfoBarRequest( 197 const PermissionRequestID& id) { 198 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { 199 content::BrowserThread::PostTask( 200 content::BrowserThread::UI, FROM_HERE, 201 base::Bind( 202 &ChromeGeolocationPermissionContext::CancelPendingInfoBarRequest, 203 this, id)); 204 return; 205 } 206 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 207 if (shutting_down_) 208 return; 209 QueueController()->CancelInfoBarRequest(id); 210} 211