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

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

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

Powered by Google App Engine
This is Rietveld 408576698