Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: chrome/browser/media/permission_bubble_media_access_handler.cc

Issue 1095393004: Refactor: Make MediaCaptureDevicesDispatcher have pluggable handlers. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase and address review comments of ps#2 Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698