Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / content_settings / permission_queue_controller.cc
blob2a7e075fa98dafd1ac3f459a234265f68621d20f
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.
5 #include "chrome/browser/content_settings/permission_queue_controller.h"
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.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/profiles/profile.h"
14 #include "chrome/browser/tab_contents/tab_util.h"
15 #include "chrome/common/content_settings.h"
16 #include "chrome/common/pref_names.h"
17 #include "components/infobars/core/infobar.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/notification_details.h"
20 #include "content/public/browser/notification_source.h"
21 #include "content/public/browser/notification_types.h"
22 #include "content/public/browser/web_contents.h"
24 #if defined(OS_ANDROID)
25 #include "chrome/browser/media/protected_media_identifier_infobar_delegate.h"
26 #endif
28 namespace {
30 InfoBarService* GetInfoBarService(const PermissionRequestID& id) {
31 content::WebContents* web_contents =
32 tab_util::GetWebContentsByID(id.render_process_id(), id.render_view_id());
33 return web_contents ? InfoBarService::FromWebContents(web_contents) : NULL;
39 class PermissionQueueController::PendingInfobarRequest {
40 public:
41 PendingInfobarRequest(ContentSettingsType type,
42 const PermissionRequestID& id,
43 const GURL& requesting_frame,
44 const GURL& embedder,
45 const std::string& accept_button_label,
46 PermissionDecidedCallback callback);
47 ~PendingInfobarRequest();
49 bool IsForPair(const GURL& requesting_frame,
50 const GURL& embedder) const;
52 const PermissionRequestID& id() const { return id_; }
53 const GURL& requesting_frame() const { return requesting_frame_; }
54 bool has_infobar() const { return !!infobar_; }
55 infobars::InfoBar* infobar() { return infobar_; }
57 void RunCallback(bool allowed);
58 void CreateInfoBar(PermissionQueueController* controller,
59 const std::string& display_languages);
61 private:
62 ContentSettingsType type_;
63 PermissionRequestID id_;
64 GURL requesting_frame_;
65 GURL embedder_;
66 std::string accept_button_label_;
67 PermissionDecidedCallback callback_;
68 infobars::InfoBar* infobar_;
70 // Purposefully do not disable copying, as this is stored in STL containers.
73 PermissionQueueController::PendingInfobarRequest::PendingInfobarRequest(
74 ContentSettingsType type,
75 const PermissionRequestID& id,
76 const GURL& requesting_frame,
77 const GURL& embedder,
78 const std::string& accept_button_label,
79 PermissionDecidedCallback callback)
80 : type_(type),
81 id_(id),
82 requesting_frame_(requesting_frame),
83 embedder_(embedder),
84 accept_button_label_(accept_button_label),
85 callback_(callback),
86 infobar_(NULL) {
89 PermissionQueueController::PendingInfobarRequest::~PendingInfobarRequest() {
92 bool PermissionQueueController::PendingInfobarRequest::IsForPair(
93 const GURL& requesting_frame,
94 const GURL& embedder) const {
95 return (requesting_frame_ == requesting_frame) && (embedder_ == embedder);
98 void PermissionQueueController::PendingInfobarRequest::RunCallback(
99 bool allowed) {
100 callback_.Run(allowed);
103 void PermissionQueueController::PendingInfobarRequest::CreateInfoBar(
104 PermissionQueueController* controller,
105 const std::string& display_languages) {
106 // TODO(toyoshim): Remove following ContentType dependent code.
107 // Also these InfoBarDelegate can share much more code each other.
108 // http://crbug.com/266743
109 switch (type_) {
110 case CONTENT_SETTINGS_TYPE_GEOLOCATION:
111 infobar_ = GeolocationInfoBarDelegate::Create(
112 GetInfoBarService(id_), controller, id_, requesting_frame_,
113 display_languages, accept_button_label_);
114 break;
115 case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
116 infobar_ = MidiPermissionInfoBarDelegate::Create(
117 GetInfoBarService(id_), controller, id_, requesting_frame_,
118 display_languages);
119 break;
120 #if defined(OS_ANDROID)
121 case CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER:
122 infobar_ = ProtectedMediaIdentifierInfoBarDelegate::Create(
123 GetInfoBarService(id_), controller, id_, requesting_frame_,
124 display_languages);
125 break;
126 #endif
127 default:
128 NOTREACHED();
129 break;
134 PermissionQueueController::PermissionQueueController(Profile* profile,
135 ContentSettingsType type)
136 : profile_(profile),
137 type_(type),
138 in_shutdown_(false) {
141 PermissionQueueController::~PermissionQueueController() {
142 // Cancel all outstanding requests.
143 in_shutdown_ = true;
144 while (!pending_infobar_requests_.empty())
145 CancelInfoBarRequest(pending_infobar_requests_.front().id());
148 void PermissionQueueController::CreateInfoBarRequest(
149 const PermissionRequestID& id,
150 const GURL& requesting_frame,
151 const GURL& embedder,
152 const std::string& accept_button_label,
153 PermissionDecidedCallback callback) {
154 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
156 // We shouldn't get duplicate requests.
157 for (PendingInfobarRequests::const_iterator i(
158 pending_infobar_requests_.begin());
159 i != pending_infobar_requests_.end(); ++i)
160 DCHECK(!i->id().Equals(id));
162 pending_infobar_requests_.push_back(PendingInfobarRequest(
163 type_, id, requesting_frame, embedder,
164 accept_button_label, callback));
165 if (!AlreadyShowingInfoBarForTab(id))
166 ShowQueuedInfoBarForTab(id);
169 void PermissionQueueController::CancelInfoBarRequest(
170 const PermissionRequestID& id) {
171 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
173 for (PendingInfobarRequests::iterator i(pending_infobar_requests_.begin());
174 i != pending_infobar_requests_.end(); ++i) {
175 if (i->id().Equals(id)) {
176 if (i->has_infobar())
177 GetInfoBarService(id)->RemoveInfoBar(i->infobar());
178 else
179 pending_infobar_requests_.erase(i);
180 return;
185 void PermissionQueueController::CancelInfoBarRequests(int group_id) {
186 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
188 // If we remove an infobar in the following loop, the next pending infobar
189 // will be shown. Therefore, we erase all the pending infobars first and
190 // remove an infobar later.
191 PendingInfobarRequests infobar_requests_to_cancel;
192 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
193 i != pending_infobar_requests_.end();) {
194 if (i->id().group_id() == group_id) {
195 if (i->has_infobar()) {
196 // |i| will be erased from |pending_infobar_requests_|
197 // in |PermissionQueueController::Observe| when the infobar is removed.
198 infobar_requests_to_cancel.push_back(*i);
199 ++i;
200 } else {
201 i = pending_infobar_requests_.erase(i);
203 } else {
204 ++i;
208 for (PendingInfobarRequests::iterator i = infobar_requests_to_cancel.begin();
209 i != infobar_requests_to_cancel.end();
210 ++i) {
211 GetInfoBarService(i->id())->RemoveInfoBar(i->infobar());
215 void PermissionQueueController::OnPermissionSet(
216 const PermissionRequestID& id,
217 const GURL& requesting_frame,
218 const GURL& embedder,
219 bool update_content_setting,
220 bool allowed) {
221 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
223 if (update_content_setting)
224 UpdateContentSetting(requesting_frame, embedder, allowed);
226 // Cancel this request first, then notify listeners. TODO(pkasting): Why
227 // is this order important?
228 PendingInfobarRequests requests_to_notify;
229 PendingInfobarRequests infobars_to_remove;
230 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
231 i != pending_infobar_requests_.end(); ) {
232 if (i->IsForPair(requesting_frame, embedder)) {
233 requests_to_notify.push_back(*i);
234 if (i->id().Equals(id)) {
235 // The infobar that called us is i->infobar(), and its delegate is
236 // currently in either Accept() or Cancel(). This means that
237 // RemoveInfoBar() will be called later on, and that will trigger a
238 // notification we're observing.
239 ++i;
240 } else if (i->has_infobar()) {
241 // This infobar is for the same frame/embedder pair, but in a different
242 // tab. We should remove it now that we've got an answer for it.
243 infobars_to_remove.push_back(*i);
244 ++i;
245 } else {
246 // We haven't created an infobar yet, just remove the pending request.
247 i = pending_infobar_requests_.erase(i);
249 } else {
250 ++i;
254 // Remove all infobars for the same |requesting_frame| and |embedder|.
255 for (PendingInfobarRequests::iterator i = infobars_to_remove.begin();
256 i != infobars_to_remove.end(); ++i)
257 GetInfoBarService(i->id())->RemoveInfoBar(i->infobar());
259 // Send out the permission notifications.
260 for (PendingInfobarRequests::iterator i = requests_to_notify.begin();
261 i != requests_to_notify.end(); ++i)
262 i->RunCallback(allowed);
265 void PermissionQueueController::Observe(
266 int type,
267 const content::NotificationSource& source,
268 const content::NotificationDetails& details) {
269 DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type);
270 // We will receive this notification for all infobar closures, so we need to
271 // check whether this is the geolocation infobar we're tracking. Note that the
272 // InfoBarContainer (if any) may have received this notification before us and
273 // caused the infobar to be deleted, so it's not safe to dereference the
274 // contents of the infobar. The address of the infobar, however, is OK to
275 // use to find the PendingInfobarRequest to remove because
276 // pending_infobar_requests_ will not have received any new entries between
277 // the NotificationService's call to InfoBarContainer::Observe and this
278 // method.
279 infobars::InfoBar* infobar =
280 content::Details<infobars::InfoBar::RemovedDetails>(details)->first;
281 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
282 i != pending_infobar_requests_.end(); ++i) {
283 if (i->infobar() == infobar) {
284 PermissionRequestID id(i->id());
285 pending_infobar_requests_.erase(i);
286 ShowQueuedInfoBarForTab(id);
287 return;
292 bool PermissionQueueController::AlreadyShowingInfoBarForTab(
293 const PermissionRequestID& id) const {
294 for (PendingInfobarRequests::const_iterator i(
295 pending_infobar_requests_.begin());
296 i != pending_infobar_requests_.end(); ++i) {
297 if (i->id().IsForSameTabAs(id) && i->has_infobar())
298 return true;
300 return false;
303 void PermissionQueueController::ShowQueuedInfoBarForTab(
304 const PermissionRequestID& id) {
305 DCHECK(!AlreadyShowingInfoBarForTab(id));
307 // We can get here for example during tab shutdown, when the InfoBarService is
308 // removing all existing infobars, thus calling back to Observe(). In this
309 // case the service still exists, and is supplied as the source of the
310 // notification we observed, but is no longer accessible from its WebContents.
311 // In this case we should just go ahead and cancel further infobars for this
312 // tab instead of trying to access the service.
314 // Similarly, if we're being destroyed, we should also avoid showing further
315 // infobars.
316 InfoBarService* infobar_service = GetInfoBarService(id);
317 if (!infobar_service || in_shutdown_) {
318 ClearPendingInfobarRequestsForTab(id);
319 return;
322 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
323 i != pending_infobar_requests_.end(); ++i) {
324 if (i->id().IsForSameTabAs(id) && !i->has_infobar()) {
325 RegisterForInfoBarNotifications(infobar_service);
326 i->CreateInfoBar(
327 this, profile_->GetPrefs()->GetString(prefs::kAcceptLanguages));
328 return;
332 UnregisterForInfoBarNotifications(infobar_service);
335 void PermissionQueueController::ClearPendingInfobarRequestsForTab(
336 const PermissionRequestID& id) {
337 for (PendingInfobarRequests::iterator i = pending_infobar_requests_.begin();
338 i != pending_infobar_requests_.end(); ) {
339 if (i->id().IsForSameTabAs(id)) {
340 DCHECK(!i->has_infobar());
341 i = pending_infobar_requests_.erase(i);
342 } else {
343 ++i;
348 void PermissionQueueController::RegisterForInfoBarNotifications(
349 InfoBarService* infobar_service) {
350 if (!registrar_.IsRegistered(
351 this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
352 content::Source<InfoBarService>(infobar_service))) {
353 registrar_.Add(this,
354 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
355 content::Source<InfoBarService>(infobar_service));
359 void PermissionQueueController::UnregisterForInfoBarNotifications(
360 InfoBarService* infobar_service) {
361 if (registrar_.IsRegistered(
362 this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
363 content::Source<InfoBarService>(infobar_service))) {
364 registrar_.Remove(this,
365 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
366 content::Source<InfoBarService>(infobar_service));
370 void PermissionQueueController::UpdateContentSetting(
371 const GURL& requesting_frame,
372 const GURL& embedder,
373 bool allowed) {
374 if (requesting_frame.GetOrigin().SchemeIsFile()) {
375 // Chrome can be launched with --disable-web-security which allows
376 // geolocation requests from file:// URLs. We don't want to store these
377 // in the host content settings map.
378 return;
381 ContentSetting content_setting =
382 allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
383 profile_->GetHostContentSettingsMap()->SetContentSetting(
384 ContentSettingsPattern::FromURLNoWildcard(requesting_frame.GetOrigin()),
385 ContentSettingsPattern::FromURLNoWildcard(embedder.GetOrigin()),
386 type_,
387 std::string(),
388 content_setting);