Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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/media/permission_bubble_media_access_handler.h" | |
| 6 | |
| 7 #include "base/metrics/field_trial.h" | |
| 8 #include "chrome/browser/media/media_stream_device_permissions.h" | |
| 9 #include "chrome/browser/media/media_stream_infobar_delegate.h" | |
| 10 #include "chrome/browser/profiles/profile.h" | |
| 11 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h" | |
| 12 #include "chrome/common/pref_names.h" | |
| 13 #include "components/content_settings/core/browser/host_content_settings_map.h" | |
| 14 #include "content/public/browser/browser_thread.h" | |
| 15 #include "content/public/browser/notification_service.h" | |
| 16 #include "content/public/browser/notification_types.h" | |
| 17 #include "content/public/browser/web_contents.h" | |
| 18 | |
| 19 #if defined(ENABLE_EXTENSIONS) | |
| 20 #include "extensions/common/extension.h" | |
|
Sergey Ulanov
2015/06/01 23:39:18
I don't think you need this include
| |
| 21 #endif | |
| 22 | |
| 23 using content::BrowserThread; | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 // A finch experiment to enable the permission bubble for media requests only. | |
| 28 bool MediaStreamPermissionBubbleExperimentEnabled() { | |
| 29 const std::string group = | |
| 30 base::FieldTrialList::FindFullName("MediaStreamPermissionBubble"); | |
| 31 if (group == "enabled") | |
| 32 return true; | |
| 33 | |
| 34 return false; | |
| 35 } | |
| 36 | |
| 37 } // namespace | |
| 38 | |
| 39 PendingAccessRequest::PendingAccessRequest( | |
| 40 const content::MediaStreamRequest& request, | |
| 41 const content::MediaResponseCallback& callback) | |
| 42 : request(request), callback(callback) { | |
| 43 } | |
| 44 | |
| 45 PendingAccessRequest::~PendingAccessRequest() { | |
| 46 } | |
| 47 | |
| 48 PermissionBubbleMediaAccessHandler::PermissionBubbleMediaAccessHandler() { | |
| 49 // PermissionBubbleMediaAccessHandler should be created on UI thread. | |
| 50 // Otherwise, it will not receive | |
| 51 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in | |
| 52 // possible use after free. | |
| 53 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 54 notifications_registrar_.Add(this, | |
| 55 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
| 56 content::NotificationService::AllSources()); | |
| 57 } | |
| 58 | |
| 59 PermissionBubbleMediaAccessHandler::~PermissionBubbleMediaAccessHandler() { | |
| 60 } | |
| 61 | |
| 62 bool PermissionBubbleMediaAccessHandler::SupportsStreamType( | |
| 63 const content::MediaStreamType type, | |
| 64 const extensions::Extension* extension) { | |
| 65 return type == content::MEDIA_DEVICE_VIDEO_CAPTURE || | |
| 66 type == content::MEDIA_DEVICE_AUDIO_CAPTURE; | |
| 67 } | |
| 68 | |
| 69 bool PermissionBubbleMediaAccessHandler::CheckMediaAccessPermission( | |
| 70 content::WebContents* web_contents, | |
| 71 const GURL& security_origin, | |
| 72 content::MediaStreamType type, | |
| 73 const extensions::Extension* extension) { | |
| 74 Profile* profile = | |
| 75 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
| 76 | |
| 77 ContentSettingsType contentSettingsType = | |
|
Sergey Ulanov
2015/06/01 23:39:18
content_settings_type
| |
| 78 type == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
| 79 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC | |
| 80 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA; | |
| 81 | |
| 82 if (CheckAllowAllMediaStreamContentForOrigin(profile, security_origin, | |
| 83 contentSettingsType)) { | |
| 84 return true; | |
| 85 } | |
| 86 | |
| 87 const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
| 88 ? prefs::kAudioCaptureAllowed | |
| 89 : prefs::kVideoCaptureAllowed; | |
| 90 const char* list_policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
| 91 ? prefs::kAudioCaptureAllowedUrls | |
| 92 : prefs::kVideoCaptureAllowedUrls; | |
| 93 if (GetDevicePolicy(profile, security_origin, policy_name, | |
| 94 list_policy_name) == ALWAYS_ALLOW) { | |
| 95 return true; | |
| 96 } | |
| 97 | |
| 98 // There's no secondary URL for these content types, hence duplicating | |
| 99 // |security_origin|. | |
| 100 if (profile->GetHostContentSettingsMap()->GetContentSetting( | |
| 101 security_origin, security_origin, contentSettingsType, | |
| 102 content_settings::ResourceIdentifier()) == CONTENT_SETTING_ALLOW) { | |
| 103 return true; | |
| 104 } | |
| 105 | |
| 106 return false; | |
| 107 } | |
| 108 | |
| 109 void PermissionBubbleMediaAccessHandler::HandleRequest( | |
| 110 content::WebContents* web_contents, | |
| 111 const content::MediaStreamRequest& request, | |
| 112 const content::MediaResponseCallback& callback, | |
| 113 const extensions::Extension* extension) { | |
| 114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 115 | |
| 116 RequestsQueue& queue = pending_requests_[web_contents]; | |
| 117 queue.push_back(PendingAccessRequest(request, callback)); | |
| 118 | |
| 119 // If this is the only request then show the infobar. | |
| 120 if (queue.size() == 1) | |
| 121 ProcessQueuedAccessRequest(web_contents); | |
| 122 } | |
| 123 | |
| 124 void PermissionBubbleMediaAccessHandler::ProcessQueuedAccessRequest( | |
| 125 content::WebContents* web_contents) { | |
| 126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 127 | |
| 128 std::map<content::WebContents*, RequestsQueue>::iterator it = | |
| 129 pending_requests_.find(web_contents); | |
| 130 | |
| 131 if (it == pending_requests_.end() || it->second.empty()) { | |
| 132 // Don't do anything if the tab was closed. | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 DCHECK(!it->second.empty()); | |
| 137 | |
| 138 if (PermissionBubbleManager::Enabled() || | |
| 139 MediaStreamPermissionBubbleExperimentEnabled()) { | |
| 140 scoped_ptr<MediaStreamDevicesController> controller( | |
| 141 new MediaStreamDevicesController( | |
| 142 web_contents, it->second.front().request, | |
| 143 base::Bind( | |
| 144 &PermissionBubbleMediaAccessHandler::OnAccessRequestResponse, | |
| 145 base::Unretained(this), web_contents))); | |
| 146 if (controller->DismissInfoBarAndTakeActionOnSettings()) | |
| 147 return; | |
| 148 PermissionBubbleManager* bubble_manager = | |
| 149 PermissionBubbleManager::FromWebContents(web_contents); | |
| 150 if (bubble_manager) | |
| 151 bubble_manager->AddRequest(controller.release()); | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate | |
| 156 // when we've transitioned to bubbles. (crbug/337458) | |
| 157 MediaStreamInfoBarDelegate::Create( | |
| 158 web_contents, it->second.front().request, | |
| 159 base::Bind(&PermissionBubbleMediaAccessHandler::OnAccessRequestResponse, | |
| 160 base::Unretained(this), web_contents)); | |
| 161 } | |
| 162 | |
| 163 void PermissionBubbleMediaAccessHandler::UpdateMediaRequest( | |
| 164 int render_process_id, | |
| 165 int render_frame_id, | |
| 166 int page_request_id, | |
| 167 content::MediaStreamType stream_type, | |
| 168 content::MediaRequestState state) { | |
| 169 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 170 if (state != content::MEDIA_REQUEST_STATE_CLOSING) | |
| 171 return; | |
| 172 | |
| 173 bool found = false; | |
| 174 for (RequestsQueues::iterator rqs_it = pending_requests_.begin(); | |
| 175 rqs_it != pending_requests_.end(); ++rqs_it) { | |
| 176 RequestsQueue& queue = rqs_it->second; | |
| 177 for (RequestsQueue::iterator it = queue.begin(); it != queue.end(); ++it) { | |
| 178 if (it->request.render_process_id == render_process_id && | |
| 179 it->request.render_frame_id == render_frame_id && | |
| 180 it->request.page_request_id == page_request_id) { | |
| 181 queue.erase(it); | |
| 182 found = true; | |
| 183 break; | |
| 184 } | |
| 185 } | |
| 186 if (found) | |
| 187 break; | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 void PermissionBubbleMediaAccessHandler::OnAccessRequestResponse( | |
| 192 content::WebContents* web_contents, | |
| 193 const content::MediaStreamDevices& devices, | |
| 194 content::MediaStreamRequestResult result, | |
| 195 scoped_ptr<content::MediaStreamUI> ui) { | |
| 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 197 | |
| 198 std::map<content::WebContents*, RequestsQueue>::iterator it = | |
| 199 pending_requests_.find(web_contents); | |
| 200 if (it == pending_requests_.end()) { | |
| 201 // WebContents has been destroyed. Don't need to do anything. | |
| 202 return; | |
| 203 } | |
| 204 | |
| 205 RequestsQueue& queue(it->second); | |
| 206 if (queue.empty()) | |
| 207 return; | |
| 208 | |
| 209 content::MediaResponseCallback callback = queue.front().callback; | |
| 210 queue.pop_front(); | |
| 211 | |
| 212 if (!queue.empty()) { | |
| 213 // Post a task to process next queued request. It has to be done | |
| 214 // asynchronously to make sure that calling infobar is not destroyed until | |
| 215 // after this function returns. | |
| 216 BrowserThread::PostTask( | |
| 217 BrowserThread::UI, FROM_HERE, | |
| 218 base::Bind( | |
| 219 &PermissionBubbleMediaAccessHandler::ProcessQueuedAccessRequest, | |
| 220 base::Unretained(this), web_contents)); | |
| 221 } | |
| 222 | |
| 223 callback.Run(devices, result, ui.Pass()); | |
| 224 } | |
| 225 | |
| 226 void PermissionBubbleMediaAccessHandler::Observe( | |
| 227 int type, | |
| 228 const content::NotificationSource& source, | |
| 229 const content::NotificationDetails& details) { | |
| 230 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 231 if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { | |
| 232 content::WebContents* web_contents = | |
| 233 content::Source<content::WebContents>(source).ptr(); | |
| 234 pending_requests_.erase(web_contents); | |
| 235 } | |
| 236 } | |
| OLD | NEW |