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