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

Side by Side Diff: chrome/browser/permissions/permission_queue_controller.cc

Issue 1422053004: permissions: remove PermissionQueueController and introduce PermissionInfoBarManager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address small issues Created 5 years, 1 month 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 2013 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/permissions/permission_queue_controller.h"
6
7 #include "base/prefs/pref_service.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
10 #include "chrome/browser/geolocation/geolocation_infobar_delegate_android.h"
11 #include "chrome/browser/infobars/infobar_service.h"
12 #include "chrome/browser/media/midi_permission_infobar_delegate_android.h"
13 #include "chrome/browser/media/protected_media_identifier_infobar_delegate_andro id.h"
14 #include "chrome/browser/notifications/notification_permission_infobar_delegate. h"
15 #include "chrome/browser/permissions/permission_context_uma_util.h"
16 #include "chrome/browser/permissions/permission_request_id.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/storage/durable_storage_permission_infobar_delegate_and roid.h"
19 #include "chrome/browser/tab_contents/tab_util.h"
20 #include "chrome/common/pref_names.h"
21 #include "components/content_settings/core/browser/host_content_settings_map.h"
22 #include "components/content_settings/core/common/content_settings.h"
23 #include "components/infobars/core/infobar.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/common/url_constants.h"
30
31 namespace {
32
33 InfoBarService* GetInfoBarService(const PermissionRequestID& id) {
34 content::WebContents* web_contents = tab_util::GetWebContentsByFrameID(
35 id.render_process_id(), id.render_frame_id());
36 return web_contents ? InfoBarService::FromWebContents(web_contents) : NULL;
37 }
38
39 bool ArePermissionRequestsForSameTab(
40 const PermissionRequestID& request,
41 const PermissionRequestID& other_request) {
42 content::WebContents* web_contents = tab_util::GetWebContentsByFrameID(
43 request.render_process_id(), request.render_frame_id());
44 content::WebContents* other_web_contents = tab_util::GetWebContentsByFrameID(
45 other_request.render_process_id(), other_request.render_frame_id());
46
47 return web_contents == other_web_contents;
48 }
49
50 } // anonymous namespace
51
52 class PermissionQueueController::PendingInfobarRequest {
53 public:
54 PendingInfobarRequest(ContentSettingsType type,
55 const PermissionRequestID& id,
56 const GURL& requesting_frame,
57 const GURL& embedder,
58 const PermissionDecidedCallback& callback);
59 ~PendingInfobarRequest();
60
61 bool IsForPair(const GURL& requesting_frame,
62 const GURL& embedder) const;
63
64 const PermissionRequestID& id() const { return id_; }
65 const GURL& requesting_frame() const { return requesting_frame_; }
66 bool has_infobar() const { return !!infobar_; }
67 infobars::InfoBar* infobar() { return infobar_; }
68
69 void RunCallback(ContentSetting content_setting);
70 void CreateInfoBar(PermissionQueueController* controller,
71 const std::string& display_languages);
72
73 private:
74 ContentSettingsType type_;
75 PermissionRequestID id_;
76 GURL requesting_frame_;
77 GURL embedder_;
78 PermissionDecidedCallback callback_;
79 infobars::InfoBar* infobar_;
80
81 // Purposefully do not disable copying, as this is stored in STL containers.
82 };
83
84 PermissionQueueController::PendingInfobarRequest::PendingInfobarRequest(
85 ContentSettingsType type,
86 const PermissionRequestID& id,
87 const GURL& requesting_frame,
88 const GURL& embedder,
89 const PermissionDecidedCallback& callback)
90 : type_(type),
91 id_(id),
92 requesting_frame_(requesting_frame),
93 embedder_(embedder),
94 callback_(callback),
95 infobar_(NULL) {
96 }
97
98 PermissionQueueController::PendingInfobarRequest::~PendingInfobarRequest() {
99 }
100
101 bool PermissionQueueController::PendingInfobarRequest::IsForPair(
102 const GURL& requesting_frame,
103 const GURL& embedder) const {
104 return (requesting_frame_ == requesting_frame) && (embedder_ == embedder);
105 }
106
107 void PermissionQueueController::PendingInfobarRequest::RunCallback(
108 ContentSetting content_setting) {
109 callback_.Run(content_setting);
110 }
111
112 void PermissionQueueController::PendingInfobarRequest::CreateInfoBar(
113 PermissionQueueController* controller,
114 const std::string& display_languages) {
115 // Controller can be Unretained because the lifetime of the infobar
116 // is tied to that of the queue controller. Before QueueController
117 // is destroyed, all requests will be cancelled and so all delegates
118 // will be destroyed.
119 PermissionInfobarDelegate::PermissionSetCallback callback =
120 base::Bind(&PermissionQueueController::OnPermissionSet,
121 base::Unretained(controller),
122 id_,
123 requesting_frame_,
124 embedder_);
125 switch (type_) {
126 case CONTENT_SETTINGS_TYPE_GEOLOCATION:
127 infobar_ = GeolocationInfoBarDelegateAndroid::Create(
128 GetInfoBarService(id_), requesting_frame_, display_languages,
129 callback);
130 break;
131 #if defined(ENABLE_NOTIFICATIONS)
132 case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
133 infobar_ = NotificationPermissionInfobarDelegate::Create(
134 GetInfoBarService(id_), requesting_frame_,
135 display_languages, callback);
136 break;
137 #endif // ENABLE_NOTIFICATIONS
138 case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
139 infobar_ = MidiPermissionInfoBarDelegateAndroid::Create(
140 GetInfoBarService(id_), requesting_frame_, display_languages, type_,
141 callback);
142 break;
143 case CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER:
144 infobar_ = ProtectedMediaIdentifierInfoBarDelegateAndroid::Create(
145 GetInfoBarService(id_), requesting_frame_, display_languages,
146 callback);
147 break;
148 case CONTENT_SETTINGS_TYPE_DURABLE_STORAGE:
149 infobar_ = DurableStoragePermissionInfoBarDelegateAndroid::Create(
150 GetInfoBarService(id_), requesting_frame_, display_languages, type_,
151 callback);
152 break;
153 default:
154 NOTREACHED();
155 break;
156 }
157 }
158
159
160 PermissionQueueController::PermissionQueueController(Profile* profile,
161 ContentSettingsType type)
162 : profile_(profile),
163 type_(type),
164 in_shutdown_(false) {
165 }
166
167 PermissionQueueController::~PermissionQueueController() {
168 // Cancel all outstanding requests.
169 in_shutdown_ = true;
170 while (!pending_infobar_requests_.empty())
171 CancelInfoBarRequest(pending_infobar_requests_.front().id());
172 }
173
174 void PermissionQueueController::CreateInfoBarRequest(
175 const PermissionRequestID& id,
176 const GURL& requesting_frame,
177 const GURL& embedder,
178 const PermissionDecidedCallback& callback) {
179 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
180
181 if (requesting_frame.SchemeIs(content::kChromeUIScheme) ||
182 embedder.SchemeIs(content::kChromeUIScheme))
183 return;
184
185 pending_infobar_requests_.push_back(PendingInfobarRequest(
186 type_, id, requesting_frame, embedder, callback));
187 if (!AlreadyShowingInfoBarForTab(id))
188 ShowQueuedInfoBarForTab(id);
189 }
190
191 void PermissionQueueController::CancelInfoBarRequest(
192 const PermissionRequestID& id) {
193 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
194
195 for (PendingInfobarRequests::iterator i(pending_infobar_requests_.begin());
196 i != pending_infobar_requests_.end(); ++i) {
197 if (id != i->id())
198 continue;
199
200 InfoBarService* infobar_service = GetInfoBarService(id);
201 if (infobar_service && i->has_infobar())
202 infobar_service->RemoveInfoBar(i->infobar());
203 else
204 pending_infobar_requests_.erase(i);
205 return;
206 }
207 }
208
209 void PermissionQueueController::OnPermissionSet(
210 const PermissionRequestID& id,
211 const GURL& requesting_frame,
212 const GURL& embedder,
213 bool update_content_setting,
214 bool allowed) {
215 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
216
217 // TODO(miguelg): move the permission persistence to
218 // PermissionContextBase once all the types are moved there.
219 if (update_content_setting) {
220 UpdateContentSetting(requesting_frame, embedder, allowed);
221 if (allowed)
222 PermissionContextUmaUtil::PermissionGranted(type_, requesting_frame);
223 else
224 PermissionContextUmaUtil::PermissionDenied(type_, requesting_frame);
225 } else {
226 PermissionContextUmaUtil::PermissionDismissed(type_, requesting_frame);
227 }
228
229 // Cancel this request first, then notify listeners. TODO(pkasting): Why
230 // is this order important?
231 PendingInfobarRequests requests_to_notify;
232 PendingInfobarRequests infobars_to_remove;
233 std::vector<PendingInfobarRequests::iterator> pending_requests_to_remove;
234 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
235 i != pending_infobar_requests_.end(); ++i) {
236 if (!i->IsForPair(requesting_frame, embedder))
237 continue;
238 requests_to_notify.push_back(*i);
239 if (!i->has_infobar()) {
240 // We haven't created an infobar yet, just record the pending request
241 // index and remove it later.
242 pending_requests_to_remove.push_back(i);
243 continue;
244 }
245 if (id == i->id()) {
246 // The infobar that called us is i->infobar(), and its delegate is
247 // currently in either Accept() or Cancel(). This means that
248 // RemoveInfoBar() will be called later on, and that will trigger a
249 // notification we're observing.
250 continue;
251 }
252
253 // This infobar is for the same frame/embedder pair, but in a different
254 // tab. We should remove it now that we've got an answer for it.
255 infobars_to_remove.push_back(*i);
256 }
257
258 // Remove all infobars for the same |requesting_frame| and |embedder|.
259 for (PendingInfobarRequests::iterator i = infobars_to_remove.begin();
260 i != infobars_to_remove.end(); ++i)
261 GetInfoBarService(i->id())->RemoveInfoBar(i->infobar());
262
263 // PermissionContextBase needs to know about the new ContentSetting value,
264 // CONTENT_SETTING_DEFAULT being the value for nothing happened. The callers
265 // of ::OnPermissionSet passes { true, true } for allow, { true, false } for
266 // block and { false, * } for dismissed. The tuple being
267 // { update_content_setting, allowed }.
268 ContentSetting content_setting = CONTENT_SETTING_DEFAULT;
269 if (update_content_setting) {
270 content_setting = allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
271 }
272
273 // Send out the permission notifications.
274 for (PendingInfobarRequests::iterator i = requests_to_notify.begin();
275 i != requests_to_notify.end(); ++i)
276 i->RunCallback(content_setting);
277
278 // Remove the pending requests in reverse order.
279 for (int i = pending_requests_to_remove.size() - 1; i >= 0; --i)
280 pending_infobar_requests_.erase(pending_requests_to_remove[i]);
281 }
282
283 void PermissionQueueController::Observe(
284 int type,
285 const content::NotificationSource& source,
286 const content::NotificationDetails& details) {
287 DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type);
288 // We will receive this notification for all infobar closures, so we need to
289 // check whether this is the geolocation infobar we're tracking. Note that the
290 // InfoBarContainer (if any) may have received this notification before us and
291 // caused the infobar to be deleted, so it's not safe to dereference the
292 // contents of the infobar. The address of the infobar, however, is OK to
293 // use to find the PendingInfobarRequest to remove because
294 // pending_infobar_requests_ will not have received any new entries between
295 // the NotificationService's call to InfoBarContainer::Observe and this
296 // method.
297 infobars::InfoBar* infobar =
298 content::Details<infobars::InfoBar::RemovedDetails>(details)->first;
299 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
300 i != pending_infobar_requests_.end(); ++i) {
301 if (i->infobar() == infobar) {
302 PermissionRequestID id(i->id());
303 pending_infobar_requests_.erase(i);
304 ShowQueuedInfoBarForTab(id);
305 return;
306 }
307 }
308 }
309
310 bool PermissionQueueController::AlreadyShowingInfoBarForTab(
311 const PermissionRequestID& id) const {
312 for (PendingInfobarRequests::const_iterator i(
313 pending_infobar_requests_.begin());
314 i != pending_infobar_requests_.end(); ++i) {
315 if (ArePermissionRequestsForSameTab(i->id(), id) && i->has_infobar())
316 return true;
317 }
318 return false;
319 }
320
321 void PermissionQueueController::ShowQueuedInfoBarForTab(
322 const PermissionRequestID& id) {
323 DCHECK(!AlreadyShowingInfoBarForTab(id));
324
325 // We can get here for example during tab shutdown, when the InfoBarService is
326 // removing all existing infobars, thus calling back to Observe(). In this
327 // case the service still exists, and is supplied as the source of the
328 // notification we observed, but is no longer accessible from its WebContents.
329 // In this case we should just go ahead and cancel further infobars for this
330 // tab instead of trying to access the service.
331 //
332 // Similarly, if we're being destroyed, we should also avoid showing further
333 // infobars.
334 InfoBarService* infobar_service = GetInfoBarService(id);
335 if (!infobar_service || in_shutdown_) {
336 ClearPendingInfobarRequestsForTab(id);
337 return;
338 }
339
340 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
341 i != pending_infobar_requests_.end(); ++i) {
342 if (ArePermissionRequestsForSameTab(i->id(), id) && !i->has_infobar()) {
343 RegisterForInfoBarNotifications(infobar_service);
344 i->CreateInfoBar(
345 this, profile_->GetPrefs()->GetString(prefs::kAcceptLanguages));
346 return;
347 }
348 }
349
350 UnregisterForInfoBarNotifications(infobar_service);
351 }
352
353 void PermissionQueueController::ClearPendingInfobarRequestsForTab(
354 const PermissionRequestID& id) {
355 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
356 i != pending_infobar_requests_.end(); ) {
357 if (ArePermissionRequestsForSameTab(i->id(), id)) {
358 DCHECK(!i->has_infobar());
359 i = pending_infobar_requests_.erase(i);
360 } else {
361 ++i;
362 }
363 }
364 }
365
366 void PermissionQueueController::RegisterForInfoBarNotifications(
367 InfoBarService* infobar_service) {
368 if (!registrar_.IsRegistered(
369 this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
370 content::Source<InfoBarService>(infobar_service))) {
371 registrar_.Add(this,
372 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
373 content::Source<InfoBarService>(infobar_service));
374 }
375 }
376
377 void PermissionQueueController::UnregisterForInfoBarNotifications(
378 InfoBarService* infobar_service) {
379 if (registrar_.IsRegistered(
380 this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
381 content::Source<InfoBarService>(infobar_service))) {
382 registrar_.Remove(this,
383 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
384 content::Source<InfoBarService>(infobar_service));
385 }
386 }
387
388 void PermissionQueueController::UpdateContentSetting(
389 const GURL& requesting_frame,
390 const GURL& embedder,
391 bool allowed) {
392 if (requesting_frame.GetOrigin().SchemeIsFile()) {
393 // Chrome can be launched with --disable-web-security which allows
394 // geolocation requests from file:// URLs. We don't want to store these
395 // in the host content settings map.
396 return;
397 }
398
399 ContentSetting content_setting =
400 allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
401
402 ContentSettingsPattern embedder_pattern =
403 (type_ == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) ?
404 ContentSettingsPattern::Wildcard() :
405 ContentSettingsPattern::FromURLNoWildcard(embedder.GetOrigin());
406
407 HostContentSettingsMapFactory::GetForProfile(profile_)->SetContentSetting(
408 ContentSettingsPattern::FromURLNoWildcard(requesting_frame.GetOrigin()),
409 embedder_pattern,
410 type_,
411 std::string(),
412 content_setting);
413 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698