Chromium Code Reviews| OLD | NEW | 
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "chrome/browser/permissions/permission_context_base.h" | 5 #include "chrome/browser/permissions/permission_context_base.h" | 
| 6 | 6 | 
| 7 #include <stddef.h> | 7 #include <stddef.h> | 
| 8 | 8 | 
| 9 #include <string> | 9 #include <string> | 
| 10 #include <utility> | 10 #include <utility> | 
| 11 | 11 | 
| 12 #include "base/callback.h" | 12 #include "base/callback.h" | 
| 13 #include "base/logging.h" | 13 #include "base/logging.h" | 
| 14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" | 
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" | 
| 16 #include "base/time/time.h" | 16 #include "base/time/time.h" | 
| 17 #include "build/build_config.h" | 17 #include "build/build_config.h" | 
| 18 #include "chrome/browser/browser_process.h" | 18 #include "chrome/browser/browser_process.h" | 
| 19 #include "chrome/browser/content_settings/host_content_settings_map_factory.h" | 19 #include "chrome/browser/content_settings/host_content_settings_map_factory.h" | 
| 20 #include "chrome/browser/permissions/permission_decision_auto_blocker.h" | 20 #include "chrome/browser/permissions/permission_decision_auto_blocker.h" | 
| 21 #include "chrome/browser/permissions/permission_request.h" | 21 #include "chrome/browser/permissions/permission_request.h" | 
| 22 #include "chrome/browser/permissions/permission_request_id.h" | 22 #include "chrome/browser/permissions/permission_request_id.h" | 
| 23 #include "chrome/browser/permissions/permission_request_impl.h" | 23 #include "chrome/browser/permissions/permission_request_impl.h" | 
| 24 #include "chrome/browser/permissions/permission_request_manager.h" | 24 #include "chrome/browser/permissions/permission_request_manager.h" | 
| 25 #include "chrome/browser/permissions/permission_uma_util.h" | 25 #include "chrome/browser/permissions/permission_uma_util.h" | 
| 26 #include "chrome/browser/permissions/permission_util.h" | |
| 27 #include "chrome/browser/profiles/profile.h" | 26 #include "chrome/browser/profiles/profile.h" | 
| 28 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | 27 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | 
| 29 #include "chrome/common/chrome_features.h" | 28 #include "chrome/common/chrome_features.h" | 
| 30 #include "chrome/common/pref_names.h" | 29 #include "chrome/common/pref_names.h" | 
| 31 #include "components/content_settings/core/browser/host_content_settings_map.h" | 30 #include "components/content_settings/core/browser/host_content_settings_map.h" | 
| 32 #include "components/prefs/pref_service.h" | 31 #include "components/prefs/pref_service.h" | 
| 33 #include "components/safe_browsing_db/database_manager.h" | 32 #include "components/safe_browsing_db/database_manager.h" | 
| 34 #include "components/variations/variations_associated_data.h" | 33 #include "components/variations/variations_associated_data.h" | 
| 35 #include "content/public/browser/browser_thread.h" | 34 #include "content/public/browser/browser_thread.h" | 
| 36 #include "content/public/browser/render_frame_host.h" | 35 #include "content/public/browser/render_frame_host.h" | 
| 37 #include "content/public/browser/web_contents.h" | 36 #include "content/public/browser/web_contents.h" | 
| 38 #include "content/public/common/origin_util.h" | 37 #include "content/public/common/origin_util.h" | 
| 39 #include "url/gurl.h" | 38 #include "url/gurl.h" | 
| 40 | 39 | 
| 41 #if defined(OS_ANDROID) | 40 #if defined(OS_ANDROID) | 
| 42 #include "chrome/browser/permissions/permission_queue_controller.h" | 41 #include "chrome/browser/permissions/permission_queue_controller.h" | 
| 43 #endif | 42 #endif | 
| 44 | 43 | 
| 45 // static | 44 // static | 
| 46 const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] = | 45 const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] = | 
| 47 "PermissionsKillSwitch"; | 46 "PermissionsKillSwitch"; | 
| 48 // static | 47 // static | 
| 49 const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] = | 48 const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] = | 
| 50 "blocked"; | 49 "blocked"; | 
| 51 | 50 | 
| 52 PermissionResult::PermissionResult(ContentSetting cs, | |
| 53 PermissionStatusSource pss) | |
| 54 : content_setting(cs), source(pss) {} | |
| 55 | |
| 56 PermissionResult::~PermissionResult() {} | |
| 57 | |
| 58 PermissionContextBase::PermissionContextBase( | 51 PermissionContextBase::PermissionContextBase( | 
| 59 Profile* profile, | 52 Profile* profile, | 
| 60 const ContentSettingsType content_settings_type) | 53 const ContentSettingsType content_settings_type) | 
| 61 : profile_(profile), | 54 : profile_(profile), | 
| 62 content_settings_type_(content_settings_type), | 55 content_settings_type_(content_settings_type), | 
| 63 weak_factory_(this) { | 56 weak_factory_(this) { | 
| 64 #if defined(OS_ANDROID) | 57 #if defined(OS_ANDROID) | 
| 65 permission_queue_controller_.reset( | 58 permission_queue_controller_.reset( | 
| 66 new PermissionQueueController(profile_, content_settings_type_)); | 59 new PermissionQueueController(profile_, content_settings_type_)); | 
| 67 #endif | 60 #endif | 
| 68 PermissionDecisionAutoBlocker::UpdateFromVariations(); | 61 PermissionDecisionAutoBlocker::UpdateFromVariations(); | 
| 69 } | 62 } | 
| 70 | 63 | 
| 71 PermissionContextBase::~PermissionContextBase() { | 64 PermissionContextBase::~PermissionContextBase() { | 
| 72 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 65 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| 73 } | 66 } | 
| 74 | 67 | 
| 75 void PermissionContextBase::RequestPermission( | 68 void PermissionContextBase::RequestPermission( | 
| 76 content::WebContents* web_contents, | 69 content::WebContents* web_contents, | 
| 77 const PermissionRequestID& id, | 70 const PermissionRequestID& id, | 
| 78 const GURL& requesting_frame, | 71 const GURL& requesting_frame, | 
| 79 bool user_gesture, | 72 bool user_gesture, | 
| 80 const BrowserPermissionCallback& callback) { | 73 const BrowserPermissionCallback& callback) { | 
| 81 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 74 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| 82 | 75 | 
| 83 // First check if this permission has been disabled. | |
| 84 if (IsPermissionKillSwitchOn()) { | |
| 85 // Log to the developer console. | |
| 86 web_contents->GetMainFrame()->AddMessageToConsole( | |
| 87 content::CONSOLE_MESSAGE_LEVEL_INFO, | |
| 88 base::StringPrintf( | |
| 89 "%s permission has been blocked.", | |
| 90 PermissionUtil::GetPermissionString(content_settings_type_) | |
| 91 .c_str())); | |
| 92 // The kill switch is enabled for this permission; Block all requests. | |
| 93 callback.Run(CONTENT_SETTING_BLOCK); | |
| 94 return; | |
| 95 } | |
| 96 | |
| 97 GURL requesting_origin = requesting_frame.GetOrigin(); | 76 GURL requesting_origin = requesting_frame.GetOrigin(); | 
| 98 GURL embedding_origin = web_contents->GetLastCommittedURL().GetOrigin(); | 77 GURL embedding_origin = web_contents->GetLastCommittedURL().GetOrigin(); | 
| 99 | 78 | 
| 100 if (!requesting_origin.is_valid() || !embedding_origin.is_valid()) { | 79 if (!requesting_origin.is_valid() || !embedding_origin.is_valid()) { | 
| 101 std::string type_name = | 80 std::string type_name = | 
| 102 PermissionUtil::GetPermissionString(content_settings_type_); | 81 PermissionUtil::GetPermissionString(content_settings_type_); | 
| 103 | 82 | 
| 104 DVLOG(1) << "Attempt to use " << type_name | 83 DVLOG(1) << "Attempt to use " << type_name | 
| 105 << " from an invalid URL: " << requesting_origin << "," | 84 << " from an invalid URL: " << requesting_origin << "," | 
| 106 << embedding_origin << " (" << type_name | 85 << embedding_origin << " (" << type_name | 
| 107 << " is not supported in popups)"; | 86 << " is not supported in popups)"; | 
| 108 NotifyPermissionSet(id, requesting_origin, embedding_origin, callback, | 87 NotifyPermissionSet(id, requesting_origin, embedding_origin, callback, | 
| 109 false /* persist */, CONTENT_SETTING_BLOCK); | 88 false /* persist */, CONTENT_SETTING_BLOCK); | 
| 110 return; | 89 return; | 
| 111 } | 90 } | 
| 112 | 91 | 
| 113 // Synchronously check the content setting to see if the user has already made | 92 // Synchronously check the content setting to see if the user has already made | 
| 114 // a decision, or if the origin is under embargo. If so, respect that | 93 // a decision, or if the origin is under embargo. If so, respect that | 
| 115 // decision. | 94 // decision. | 
| 116 PermissionResult result = | 95 PermissionResult result = | 
| 117 GetPermissionStatus(requesting_origin, embedding_origin); | 96 GetPermissionStatus(requesting_origin, embedding_origin); | 
| 118 | 97 | 
| 119 if (result.content_setting == CONTENT_SETTING_ALLOW || | 98 if (result.content_setting == CONTENT_SETTING_ALLOW || | 
| 120 result.content_setting == CONTENT_SETTING_BLOCK) { | 99 result.content_setting == CONTENT_SETTING_BLOCK) { | 
| 100 if (result.source == PermissionStatusSource::KILL_SWITCH) { | |
| 101 // Block the request and log to the developer console. | |
| 102 web_contents->GetMainFrame()->AddMessageToConsole( | |
| 103 content::CONSOLE_MESSAGE_LEVEL_INFO, | |
| 104 base::StringPrintf( | |
| 105 "%s permission has been blocked.", | |
| 106 PermissionUtil::GetPermissionString(content_settings_type_) | |
| 107 .c_str())); | |
| 108 callback.Run(CONTENT_SETTING_BLOCK); | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 // If we are under embargo, record the embargo reason for which we have | |
| 113 // suppressed the prompt. Explicitly switch to ensure that any new | |
| 114 // PermissionStatusSource values are dealt with appropriately. | |
| 115 switch (result.source) { | |
| 116 case PermissionStatusSource::MULTIPLE_DISMISSALS: | |
| 117 PermissionUmaUtil::RecordEmbargoPromptSuppression( | |
| 118 PermissionEmbargoStatus::REPEATED_DISMISSALS); | |
| 119 break; | |
| 120 case PermissionStatusSource::SAFE_BROWSING_BLACKLIST: | |
| 121 PermissionUmaUtil::RecordEmbargoPromptSuppression( | |
| 122 PermissionEmbargoStatus::PERMISSIONS_BLACKLISTING); | |
| 123 break; | |
| 124 case PermissionStatusSource::UNSPECIFIED: | |
| 125 case PermissionStatusSource::KILL_SWITCH: | |
| 126 case PermissionStatusSource::NUM: | |
| 127 break; | |
| 128 } | |
| 
 
raymes
2017/02/22 23:13:50
nit: Could we make the conversion a helper functio
 
dominickn
2017/02/22 23:38:54
This can only be done by proxying the call to Reco
 
raymes
2017/02/23 01:28:58
I agree - it's not ideal. We could alternatively h
 
 | |
| 121 NotifyPermissionSet(id, requesting_origin, embedding_origin, callback, | 129 NotifyPermissionSet(id, requesting_origin, embedding_origin, callback, | 
| 122 false /* persist */, result.content_setting); | 130 false /* persist */, result.content_setting); | 
| 123 return; | 131 return; | 
| 124 } | 132 } | 
| 125 | 133 | 
| 126 // Asynchronously check whether the origin should be blocked from making this | 134 // Asynchronously check whether the origin should be blocked from making this | 
| 127 // permission request. It may be on the Safe Browsing API blacklist, or it may | 135 // permission request, e.g. it may be on the Safe Browsing API blacklist. | 
| 128 // have been dismissed too many times in a row. If the origin is allowed to | |
| 129 // request, that request will be made to ContinueRequestPermission(). | |
| 130 PermissionDecisionAutoBlocker::GetForProfile(profile_)->UpdateEmbargoedStatus( | 136 PermissionDecisionAutoBlocker::GetForProfile(profile_)->UpdateEmbargoedStatus( | 
| 131 content_settings_type_, requesting_origin, web_contents, | 137 content_settings_type_, requesting_origin, web_contents, | 
| 132 base::Bind(&PermissionContextBase::ContinueRequestPermission, | 138 base::Bind(&PermissionContextBase::ContinueRequestPermission, | 
| 133 weak_factory_.GetWeakPtr(), web_contents, id, | 139 weak_factory_.GetWeakPtr(), web_contents, id, | 
| 134 requesting_origin, embedding_origin, user_gesture, callback)); | 140 requesting_origin, embedding_origin, user_gesture, callback)); | 
| 135 } | 141 } | 
| 136 | 142 | 
| 137 void PermissionContextBase::ContinueRequestPermission( | 143 void PermissionContextBase::ContinueRequestPermission( | 
| 138 content::WebContents* web_contents, | 144 content::WebContents* web_contents, | 
| 139 const PermissionRequestID& id, | 145 const PermissionRequestID& id, | 
| 140 const GURL& requesting_origin, | 146 const GURL& requesting_origin, | 
| 141 const GURL& embedding_origin, | 147 const GURL& embedding_origin, | 
| 142 bool user_gesture, | 148 bool user_gesture, | 
| 143 const BrowserPermissionCallback& callback, | 149 const BrowserPermissionCallback& callback, | 
| 144 bool permission_blocked) { | 150 bool permission_blocked) { | 
| 145 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 151 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| 146 if (permission_blocked) { | 152 if (permission_blocked) { | 
| 147 web_contents->GetMainFrame()->AddMessageToConsole( | 153 web_contents->GetMainFrame()->AddMessageToConsole( | 
| 148 content::CONSOLE_MESSAGE_LEVEL_INFO, | 154 content::CONSOLE_MESSAGE_LEVEL_INFO, | 
| 149 base::StringPrintf( | 155 base::StringPrintf( | 
| 150 "%s permission has been auto-blocked.", | 156 "%s permission has been auto-blocked.", | 
| 151 PermissionUtil::GetPermissionString(content_settings_type_) | 157 PermissionUtil::GetPermissionString(content_settings_type_) | 
| 152 .c_str())); | 158 .c_str())); | 
| 153 // Permission has been automatically blocked. | 159 // Permission has been automatically blocked. Record that the prompt was | 
| 154 PermissionUmaUtil::RecordPermissionEmbargoStatus( | 160 // suppressed and that we hit the blacklist. | 
| 161 PermissionUmaUtil::RecordEmbargoPromptSuppression( | |
| 162 PermissionEmbargoStatus::PERMISSIONS_BLACKLISTING); | |
| 163 PermissionUmaUtil::RecordEmbargoStatus( | |
| 155 PermissionEmbargoStatus::PERMISSIONS_BLACKLISTING); | 164 PermissionEmbargoStatus::PERMISSIONS_BLACKLISTING); | 
| 156 callback.Run(CONTENT_SETTING_BLOCK); | 165 callback.Run(CONTENT_SETTING_BLOCK); | 
| 157 return; | 166 return; | 
| 158 } | 167 } | 
| 159 | 168 | 
| 169 // We are going to show a prompt now. | |
| 160 PermissionUmaUtil::PermissionRequested( | 170 PermissionUmaUtil::PermissionRequested( | 
| 161 content_settings_type_, requesting_origin, embedding_origin, profile_); | 171 content_settings_type_, requesting_origin, embedding_origin, profile_); | 
| 172 PermissionUmaUtil::RecordEmbargoPromptSuppression( | |
| 173 PermissionEmbargoStatus::NOT_EMBARGOED); | |
| 162 | 174 | 
| 163 DecidePermission(web_contents, id, requesting_origin, embedding_origin, | 175 DecidePermission(web_contents, id, requesting_origin, embedding_origin, | 
| 164 user_gesture, callback); | 176 user_gesture, callback); | 
| 165 } | 177 } | 
| 166 | 178 | 
| 167 PermissionResult PermissionContextBase::GetPermissionStatus( | 179 PermissionResult PermissionContextBase::GetPermissionStatus( | 
| 168 const GURL& requesting_origin, | 180 const GURL& requesting_origin, | 
| 169 const GURL& embedding_origin) const { | 181 const GURL& embedding_origin) const { | 
| 170 // TODO(raymes): Ensure we return appropriate decision reasons in the | |
| 171 // PermissionResult. We should add these as each is needed. | |
| 172 | |
| 173 // If the permission has been disabled through Finch, block all requests. | 182 // If the permission has been disabled through Finch, block all requests. | 
| 174 if (IsPermissionKillSwitchOn()) { | 183 if (IsPermissionKillSwitchOn()) { | 
| 175 return PermissionResult(CONTENT_SETTING_BLOCK, | 184 return PermissionResult(CONTENT_SETTING_BLOCK, | 
| 176 PermissionStatusSource::UNSPECIFIED); | 185 PermissionStatusSource::KILL_SWITCH); | 
| 177 } | 186 } | 
| 178 | 187 | 
| 179 if (IsRestrictedToSecureOrigins() && | 188 if (IsRestrictedToSecureOrigins() && | 
| 180 !content::IsOriginSecure(requesting_origin)) { | 189 !content::IsOriginSecure(requesting_origin)) { | 
| 181 return PermissionResult(CONTENT_SETTING_BLOCK, | 190 return PermissionResult(CONTENT_SETTING_BLOCK, | 
| 182 PermissionStatusSource::UNSPECIFIED); | 191 PermissionStatusSource::UNSPECIFIED); | 
| 183 } | 192 } | 
| 184 | 193 | 
| 185 ContentSetting content_setting = | 194 ContentSetting content_setting = | 
| 186 GetPermissionStatusInternal(requesting_origin, embedding_origin); | 195 GetPermissionStatusInternal(requesting_origin, embedding_origin); | 
| 187 if (content_setting == CONTENT_SETTING_ASK && | 196 if (content_setting == CONTENT_SETTING_ASK) { | 
| 188 PermissionDecisionAutoBlocker::GetForProfile(profile_)->IsUnderEmbargo( | 197 return PermissionDecisionAutoBlocker::GetForProfile(profile_) | 
| 189 content_settings_type_, requesting_origin)) { | 198 ->GetEmbargoResult(content_settings_type_, requesting_origin); | 
| 
 
raymes
2017/02/22 23:13:50
Could we DCHECK that the content setting of the re
 
dominickn
2017/02/22 23:38:54
Done.
 
 | |
| 190 return PermissionResult(CONTENT_SETTING_BLOCK, | |
| 191 PermissionStatusSource::UNSPECIFIED); | |
| 192 } | 199 } | 
| 193 | 200 | 
| 194 return PermissionResult(content_setting, PermissionStatusSource::UNSPECIFIED); | 201 return PermissionResult(content_setting, PermissionStatusSource::UNSPECIFIED); | 
| 195 } | 202 } | 
| 196 | 203 | 
| 197 void PermissionContextBase::ResetPermission( | 204 void PermissionContextBase::ResetPermission(const GURL& requesting_origin, | 
| 198 const GURL& requesting_origin, | 205 const GURL& embedding_origin) { | 
| 199 const GURL& embedding_origin) { | |
| 200 HostContentSettingsMapFactory::GetForProfile(profile_) | 206 HostContentSettingsMapFactory::GetForProfile(profile_) | 
| 201 ->SetContentSettingDefaultScope(requesting_origin, embedding_origin, | 207 ->SetContentSettingDefaultScope(requesting_origin, embedding_origin, | 
| 202 content_settings_storage_type(), | 208 content_settings_storage_type(), | 
| 203 std::string(), CONTENT_SETTING_DEFAULT); | 209 std::string(), CONTENT_SETTING_DEFAULT); | 
| 204 } | 210 } | 
| 205 | 211 | 
| 206 void PermissionContextBase::CancelPermissionRequest( | 212 void PermissionContextBase::CancelPermissionRequest( | 
| 207 content::WebContents* web_contents, | 213 content::WebContents* web_contents, | 
| 208 const PermissionRequestID& id) { | 214 const PermissionRequestID& id) { | 
| 209 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 215 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 317 } else { | 323 } else { | 
| 318 PermissionUmaUtil::PermissionDismissed( | 324 PermissionUmaUtil::PermissionDismissed( | 
| 319 content_settings_type_, gesture_type, requesting_origin, profile_); | 325 content_settings_type_, gesture_type, requesting_origin, profile_); | 
| 320 | 326 | 
| 321 if (PermissionDecisionAutoBlocker::GetForProfile(profile_) | 327 if (PermissionDecisionAutoBlocker::GetForProfile(profile_) | 
| 322 ->RecordDismissAndEmbargo(requesting_origin, | 328 ->RecordDismissAndEmbargo(requesting_origin, | 
| 323 content_settings_type_)) { | 329 content_settings_type_)) { | 
| 324 embargo_status = PermissionEmbargoStatus::REPEATED_DISMISSALS; | 330 embargo_status = PermissionEmbargoStatus::REPEATED_DISMISSALS; | 
| 325 } | 331 } | 
| 326 } | 332 } | 
| 327 PermissionUmaUtil::RecordPermissionEmbargoStatus(embargo_status); | 333 PermissionUmaUtil::RecordEmbargoStatus(embargo_status); | 
| 328 } | 334 } | 
| 329 | 335 | 
| 330 NotifyPermissionSet(id, requesting_origin, embedding_origin, callback, | 336 NotifyPermissionSet(id, requesting_origin, embedding_origin, callback, | 
| 331 persist, content_setting); | 337 persist, content_setting); | 
| 332 } | 338 } | 
| 333 | 339 | 
| 334 #if defined(OS_ANDROID) | 340 #if defined(OS_ANDROID) | 
| 335 PermissionQueueController* PermissionContextBase::GetQueueController() { | 341 PermissionQueueController* PermissionContextBase::GetQueueController() { | 
| 336 return permission_queue_controller_.get(); | 342 return permission_queue_controller_.get(); | 
| 337 } | 343 } | 
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 383 content_settings_storage_type(), | 389 content_settings_storage_type(), | 
| 384 std::string(), content_setting); | 390 std::string(), content_setting); | 
| 385 } | 391 } | 
| 386 | 392 | 
| 387 ContentSettingsType PermissionContextBase::content_settings_storage_type() | 393 ContentSettingsType PermissionContextBase::content_settings_storage_type() | 
| 388 const { | 394 const { | 
| 389 if (content_settings_type_ == CONTENT_SETTINGS_TYPE_PUSH_MESSAGING) | 395 if (content_settings_type_ == CONTENT_SETTINGS_TYPE_PUSH_MESSAGING) | 
| 390 return CONTENT_SETTINGS_TYPE_NOTIFICATIONS; | 396 return CONTENT_SETTINGS_TYPE_NOTIFICATIONS; | 
| 391 return content_settings_type_; | 397 return content_settings_type_; | 
| 392 } | 398 } | 
| OLD | NEW |