| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/media/webrtc/media_stream_devices_controller.h" | 5 #include "chrome/browser/media/webrtc/media_stream_devices_controller.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 #include "content/public/browser/render_widget_host_view.h" | 37 #include "content/public/browser/render_widget_host_view.h" |
| 38 #include "content/public/common/media_stream_request.h" | 38 #include "content/public/common/media_stream_request.h" |
| 39 #include "content/public/common/origin_util.h" | 39 #include "content/public/common/origin_util.h" |
| 40 #include "extensions/common/constants.h" | 40 #include "extensions/common/constants.h" |
| 41 #include "ui/base/l10n/l10n_util.h" | 41 #include "ui/base/l10n/l10n_util.h" |
| 42 | 42 |
| 43 #if defined(OS_ANDROID) | 43 #if defined(OS_ANDROID) |
| 44 #include <vector> | 44 #include <vector> |
| 45 | 45 |
| 46 #include "chrome/browser/android/preferences/pref_service_bridge.h" | 46 #include "chrome/browser/android/preferences/pref_service_bridge.h" |
| 47 #include "chrome/browser/media/webrtc/media_stream_infobar_delegate_android.h" |
| 47 #include "chrome/browser/permissions/permission_update_infobar_delegate_android.
h" | 48 #include "chrome/browser/permissions/permission_update_infobar_delegate_android.
h" |
| 48 #include "chrome/grit/theme_resources.h" | 49 #include "chrome/grit/theme_resources.h" |
| 49 #include "content/public/browser/android/content_view_core.h" | 50 #include "content/public/browser/android/content_view_core.h" |
| 50 #include "ui/android/window_android.h" | 51 #include "ui/android/window_android.h" |
| 51 #else // !defined(OS_ANDROID) | 52 #else // !defined(OS_ANDROID) |
| 53 #include "chrome/browser/permissions/permission_request_manager.h" |
| 52 #include "ui/vector_icons/vector_icons.h" | 54 #include "ui/vector_icons/vector_icons.h" |
| 53 #endif | 55 #endif |
| 54 | 56 |
| 55 using content::BrowserThread; | 57 using content::BrowserThread; |
| 56 | 58 |
| 57 namespace { | 59 namespace { |
| 58 | 60 |
| 59 // Returns true if the given ContentSettingsType is being requested in | 61 // Returns true if the given ContentSettingsType is being requested in |
| 60 // |request|. | 62 // |request|. |
| 61 bool ContentTypeIsRequested(ContentSettingsType type, | 63 bool ContentTypeIsRequested(ContentSettingsType type, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 // Calls |action_function| for each permission requested by |request|. | 95 // Calls |action_function| for each permission requested by |request|. |
| 94 void RecordPermissionAction(const content::MediaStreamRequest& request, | 96 void RecordPermissionAction(const content::MediaStreamRequest& request, |
| 95 Profile* profile, | 97 Profile* profile, |
| 96 PermissionActionCallback callback) { | 98 PermissionActionCallback callback) { |
| 97 RecordSinglePermissionAction(request, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, | 99 RecordSinglePermissionAction(request, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, |
| 98 profile, callback); | 100 profile, callback); |
| 99 RecordSinglePermissionAction( | 101 RecordSinglePermissionAction( |
| 100 request, CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, profile, callback); | 102 request, CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, profile, callback); |
| 101 } | 103 } |
| 102 | 104 |
| 105 #if defined(OS_ANDROID) |
| 106 // Callback for the permission update infobar when the site and Chrome |
| 107 // permissions are mismatched on Android. |
| 108 void OnPermissionConflictResolved( |
| 109 std::unique_ptr<MediaStreamDevicesController> controller, |
| 110 bool allowed) { |
| 111 if (allowed) |
| 112 controller->PermissionGranted(); |
| 113 else |
| 114 controller->ForcePermissionDeniedTemporarily(); |
| 115 } |
| 116 |
| 117 #endif // defined(OS_ANDROID) |
| 118 |
| 103 // This helper class helps to measure the number of media stream requests that | 119 // This helper class helps to measure the number of media stream requests that |
| 104 // occur. It ensures that only one request will be recorded per navigation, per | 120 // occur. It ensures that only one request will be recorded per navigation, per |
| 105 // frame. TODO(raymes): Remove this when https://crbug.com/526324 is fixed. | 121 // frame. TODO(raymes): Remove this when https://crbug.com/526324 is fixed. |
| 106 class MediaPermissionRequestLogger : content::WebContentsObserver { | 122 class MediaPermissionRequestLogger : content::WebContentsObserver { |
| 107 // Map of <render process id, render frame id> -> | 123 // Map of <render process id, render frame id> -> |
| 108 // MediaPermissionRequestLogger. | 124 // MediaPermissionRequestLogger. |
| 109 using RequestMap = std::map<std::pair<int, int>, | 125 using RequestMap = std::map<std::pair<int, int>, |
| 110 std::unique_ptr<MediaPermissionRequestLogger>>; | 126 std::unique_ptr<MediaPermissionRequestLogger>>; |
| 111 | 127 |
| 112 public: | 128 public: |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 void RenderFrameDeleted( | 167 void RenderFrameDeleted( |
| 152 content::RenderFrameHost* render_frame_host) override { | 168 content::RenderFrameHost* render_frame_host) override { |
| 153 PageChanged(render_frame_host); | 169 PageChanged(render_frame_host); |
| 154 } | 170 } |
| 155 | 171 |
| 156 RequestMap::key_type key_; | 172 RequestMap::key_type key_; |
| 157 }; | 173 }; |
| 158 | 174 |
| 159 } // namespace | 175 } // namespace |
| 160 | 176 |
| 161 MediaStreamDevicesController::MediaStreamDevicesController( | 177 // static |
| 178 void MediaStreamDevicesController::RequestPermissions( |
| 162 content::WebContents* web_contents, | 179 content::WebContents* web_contents, |
| 163 const content::MediaStreamRequest& request, | 180 const content::MediaStreamRequest& request, |
| 164 const content::MediaResponseCallback& callback) | 181 const content::MediaResponseCallback& callback) { |
| 165 : web_contents_(web_contents), | 182 std::unique_ptr<MediaStreamDevicesController> controller( |
| 166 request_(request), | 183 new MediaStreamDevicesController(web_contents, request, callback)); |
| 167 callback_(callback) { | 184 if (!controller->IsAskingForAudio() && !controller->IsAskingForVideo()) { |
| 168 if (request_.request_type == content::MEDIA_OPEN_DEVICE_PEPPER_ONLY) { | 185 #if defined(OS_ANDROID) |
| 169 MediaPermissionRequestLogger::LogRequest( | 186 // If either audio or video was previously allowed and Chrome no longer has |
| 170 web_contents, request.render_process_id, request.render_frame_id, | 187 // the necessary permissions, show a infobar to attempt to address this |
| 171 content::IsOriginSecure(request_.security_origin)); | 188 // mismatch. |
| 172 } | 189 std::vector<ContentSettingsType> content_settings_types; |
| 173 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 190 if (controller->IsAllowedForAudio()) |
| 174 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents); | 191 content_settings_types.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC); |
| 175 | 192 |
| 176 content::MediaStreamRequestResult denial_reason = content::MEDIA_DEVICE_OK; | 193 if (controller->IsAllowedForVideo()) { |
| 177 old_audio_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, | 194 content_settings_types.push_back( |
| 178 request_, &denial_reason); | 195 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA); |
| 179 old_video_setting_ = GetContentSetting( | 196 } |
| 180 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, request_, &denial_reason); | 197 if (!content_settings_types.empty() && |
| 181 | 198 PermissionUpdateInfoBarDelegate::ShouldShowPermissionInfobar( |
| 182 // If either setting is ask, we show the infobar. | 199 web_contents, content_settings_types)) { |
| 183 if (old_audio_setting_ == CONTENT_SETTING_ASK || | 200 PermissionUpdateInfoBarDelegate::Create( |
| 184 old_video_setting_ == CONTENT_SETTING_ASK) { | 201 web_contents, content_settings_types, |
| 202 base::Bind(&OnPermissionConflictResolved, base::Passed(&controller))); |
| 203 } |
| 204 #endif |
| 185 return; | 205 return; |
| 186 } | 206 } |
| 187 | 207 |
| 188 #if defined(OS_ANDROID) | 208 #if defined(OS_ANDROID) |
| 189 std::vector<ContentSettingsType> content_settings_types; | 209 PermissionUmaUtil::RecordPermissionPromptShown( |
| 190 if (IsAllowedForAudio()) | 210 controller->GetPermissionRequestType(), |
| 191 content_settings_types.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC); | 211 PermissionUtil::GetGestureType(request.user_gesture)); |
| 192 | 212 if (PermissionDialogDelegate::ShouldShowDialog(request.user_gesture)) { |
| 193 if (IsAllowedForVideo()) { | 213 PermissionDialogDelegate::CreateMediaStreamDialog( |
| 194 content_settings_types.push_back( | 214 web_contents, request.user_gesture, std::move(controller)); |
| 195 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA); | 215 } else { |
| 216 MediaStreamInfoBarDelegateAndroid::Create( |
| 217 web_contents, request.user_gesture, std::move(controller)); |
| 196 } | 218 } |
| 197 | 219 #else |
| 198 // If the site had been previously granted the access to audio or video but | 220 PermissionRequestManager* permission_request_manager = |
| 199 // Chrome is now missing the necessary permission, we need to show an infobar | 221 PermissionRequestManager::FromWebContents(web_contents); |
| 200 // to resolve the difference. | 222 if (permission_request_manager) |
| 201 if (!content_settings_types.empty() && | 223 permission_request_manager->AddRequest(controller.release()); |
| 202 PermissionUpdateInfoBarDelegate::ShouldShowPermissionInfobar( | |
| 203 web_contents, content_settings_types)) { | |
| 204 return; | |
| 205 } | |
| 206 #endif | 224 #endif |
| 207 | |
| 208 // Otherwise we can run the callback immediately. | |
| 209 RunCallback(old_audio_setting_, old_video_setting_, denial_reason); | |
| 210 } | |
| 211 | |
| 212 MediaStreamDevicesController::~MediaStreamDevicesController() { | |
| 213 if (!callback_.is_null()) { | |
| 214 RecordPermissionAction(request_, profile_, | |
| 215 base::Bind(PermissionUmaUtil::PermissionIgnored)); | |
| 216 callback_.Run(content::MediaStreamDevices(), | |
| 217 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN, | |
| 218 std::unique_ptr<content::MediaStreamUI>()); | |
| 219 } | |
| 220 } | 225 } |
| 221 | 226 |
| 222 // static | 227 // static |
| 223 void MediaStreamDevicesController::RegisterProfilePrefs( | 228 void MediaStreamDevicesController::RegisterProfilePrefs( |
| 224 user_prefs::PrefRegistrySyncable* prefs) { | 229 user_prefs::PrefRegistrySyncable* prefs) { |
| 225 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, true); | 230 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, true); |
| 226 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, true); | 231 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, true); |
| 227 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls); | 232 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls); |
| 228 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls); | 233 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls); |
| 229 } | 234 } |
| 230 | 235 |
| 231 bool MediaStreamDevicesController::IsAllowedForAudio() const { | 236 MediaStreamDevicesController::~MediaStreamDevicesController() { |
| 232 return old_audio_setting_ == CONTENT_SETTING_ALLOW; | 237 if (!callback_.is_null()) { |
| 233 } | 238 RecordPermissionAction(request_, profile_, |
| 234 | 239 base::Bind(PermissionUmaUtil::PermissionIgnored)); |
| 235 bool MediaStreamDevicesController::IsAllowedForVideo() const { | 240 callback_.Run(content::MediaStreamDevices(), |
| 236 return old_video_setting_ == CONTENT_SETTING_ALLOW; | 241 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN, |
| 242 std::unique_ptr<content::MediaStreamUI>()); |
| 243 } |
| 237 } | 244 } |
| 238 | 245 |
| 239 bool MediaStreamDevicesController::IsAskingForAudio() const { | 246 bool MediaStreamDevicesController::IsAskingForAudio() const { |
| 240 return old_audio_setting_ == CONTENT_SETTING_ASK; | 247 return old_audio_setting_ == CONTENT_SETTING_ASK; |
| 241 } | 248 } |
| 242 | 249 |
| 243 bool MediaStreamDevicesController::IsAskingForVideo() const { | 250 bool MediaStreamDevicesController::IsAskingForVideo() const { |
| 244 return old_video_setting_ == CONTENT_SETTING_ASK; | 251 return old_video_setting_ == CONTENT_SETTING_ASK; |
| 245 } | 252 } |
| 246 | 253 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 | 328 |
| 322 void MediaStreamDevicesController::RequestFinished() { | 329 void MediaStreamDevicesController::RequestFinished() { |
| 323 delete this; | 330 delete this; |
| 324 } | 331 } |
| 325 | 332 |
| 326 PermissionRequestType MediaStreamDevicesController::GetPermissionRequestType() | 333 PermissionRequestType MediaStreamDevicesController::GetPermissionRequestType() |
| 327 const { | 334 const { |
| 328 return PermissionRequestType::MEDIA_STREAM; | 335 return PermissionRequestType::MEDIA_STREAM; |
| 329 } | 336 } |
| 330 | 337 |
| 338 MediaStreamDevicesController::MediaStreamDevicesController( |
| 339 content::WebContents* web_contents, |
| 340 const content::MediaStreamRequest& request, |
| 341 const content::MediaResponseCallback& callback) |
| 342 : web_contents_(web_contents), request_(request), callback_(callback) { |
| 343 if (request_.request_type == content::MEDIA_OPEN_DEVICE_PEPPER_ONLY) { |
| 344 MediaPermissionRequestLogger::LogRequest( |
| 345 web_contents, request.render_process_id, request.render_frame_id, |
| 346 content::IsOriginSecure(request_.security_origin)); |
| 347 } |
| 348 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 349 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents); |
| 350 |
| 351 content::MediaStreamRequestResult denial_reason = content::MEDIA_DEVICE_OK; |
| 352 old_audio_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, |
| 353 request_, &denial_reason); |
| 354 old_video_setting_ = GetContentSetting( |
| 355 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, request_, &denial_reason); |
| 356 |
| 357 // If either setting is ask, we show the infobar. |
| 358 if (old_audio_setting_ == CONTENT_SETTING_ASK || |
| 359 old_video_setting_ == CONTENT_SETTING_ASK) { |
| 360 return; |
| 361 } |
| 362 |
| 363 #if defined(OS_ANDROID) |
| 364 std::vector<ContentSettingsType> content_settings_types; |
| 365 if (IsAllowedForAudio()) |
| 366 content_settings_types.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC); |
| 367 |
| 368 if (IsAllowedForVideo()) { |
| 369 content_settings_types.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA); |
| 370 } |
| 371 |
| 372 // If the site had been previously granted the access to audio or video but |
| 373 // Chrome is now missing the necessary permission, we need to show an infobar |
| 374 // to resolve the difference. |
| 375 if (!content_settings_types.empty() && |
| 376 PermissionUpdateInfoBarDelegate::ShouldShowPermissionInfobar( |
| 377 web_contents, content_settings_types)) { |
| 378 return; |
| 379 } |
| 380 #endif |
| 381 |
| 382 // Otherwise we can run the callback immediately. |
| 383 RunCallback(old_audio_setting_, old_video_setting_, denial_reason); |
| 384 } |
| 385 |
| 386 bool MediaStreamDevicesController::IsAllowedForAudio() const { |
| 387 return old_audio_setting_ == CONTENT_SETTING_ALLOW; |
| 388 } |
| 389 |
| 390 bool MediaStreamDevicesController::IsAllowedForVideo() const { |
| 391 return old_video_setting_ == CONTENT_SETTING_ALLOW; |
| 392 } |
| 393 |
| 331 content::MediaStreamDevices MediaStreamDevicesController::GetDevices( | 394 content::MediaStreamDevices MediaStreamDevicesController::GetDevices( |
| 332 ContentSetting audio_setting, | 395 ContentSetting audio_setting, |
| 333 ContentSetting video_setting) { | 396 ContentSetting video_setting) { |
| 334 bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW; | 397 bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW; |
| 335 bool video_allowed = video_setting == CONTENT_SETTING_ALLOW; | 398 bool video_allowed = video_setting == CONTENT_SETTING_ALLOW; |
| 336 | 399 |
| 337 if (!audio_allowed && !video_allowed) | 400 if (!audio_allowed && !video_allowed) |
| 338 return content::MediaStreamDevices(); | 401 return content::MediaStreamDevices(); |
| 339 | 402 |
| 340 content::MediaStreamDevices devices; | 403 content::MediaStreamDevices devices; |
| (...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 if (android_permission_blocked) | 674 if (android_permission_blocked) |
| 612 return false; | 675 return false; |
| 613 | 676 |
| 614 // Don't approve device requests if the tab was hidden. | 677 // Don't approve device requests if the tab was hidden. |
| 615 // TODO(qinmin): Add a test for this. http://crbug.com/396869. | 678 // TODO(qinmin): Add a test for this. http://crbug.com/396869. |
| 616 // TODO(raymes): Shouldn't this apply to all permissions not just audio/video? | 679 // TODO(raymes): Shouldn't this apply to all permissions not just audio/video? |
| 617 return web_contents_->GetRenderWidgetHostView()->IsShowing(); | 680 return web_contents_->GetRenderWidgetHostView()->IsShowing(); |
| 618 #endif | 681 #endif |
| 619 return true; | 682 return true; |
| 620 } | 683 } |
| OLD | NEW |