Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / content_settings / content_setting_bubble_model.cc
1 // Copyright (c) 2012 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/ui/content_settings/content_setting_bubble_model.h"
6
7 #include "base/command_line.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/content_settings/cookie_settings.h"
12 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
13 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
14 #include "chrome/browser/favicon/favicon_tab_helper.h"
15 #include "chrome/browser/infobars/infobar_service.h"
16 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
17 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
20 #include "chrome/browser/ui/browser_navigator.h"
21 #include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
22 #include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/pref_names.h"
25 #include "chrome/common/render_messages.h"
26 #include "chrome/grit/generated_resources.h"
27 #include "components/content_settings/core/browser/content_settings_utils.h"
28 #include "components/content_settings/core/common/content_settings.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/render_view_host.h"
33 #include "content/public/browser/user_metrics.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_contents_delegate.h"
36 #include "grit/components_strings.h"
37 #include "net/base/net_util.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #include "ui/resources/grit/ui_resources.h"
41
42 using base::UserMetricsAction;
43 using content::WebContents;
44 using content_settings::SettingInfo;
45 using content_settings::SettingSource;
46 using content_settings::SETTING_SOURCE_USER;
47 using content_settings::SETTING_SOURCE_NONE;
48
49 namespace {
50
51 const int kAllowButtonIndex = 0;
52
53 struct ContentSettingsTypeIdEntry {
54   ContentSettingsType type;
55   int id;
56 };
57
58 int GetIdForContentType(const ContentSettingsTypeIdEntry* entries,
59                         size_t num_entries,
60                         ContentSettingsType type) {
61   for (size_t i = 0; i < num_entries; ++i) {
62     if (entries[i].type == type)
63       return entries[i].id;
64   }
65   return 0;
66 }
67
68 const content::MediaStreamDevice& GetMediaDeviceById(
69     const std::string& device_id,
70     const content::MediaStreamDevices& devices) {
71   DCHECK(!devices.empty());
72   for (content::MediaStreamDevices::const_iterator it = devices.begin();
73        it != devices.end(); ++it) {
74     if (it->id == device_id)
75       return *(it);
76   }
77
78   // A device with the |device_id| was not found. It is likely that the device
79   // has been unplugged from the OS. Return the first device as the default
80   // device.
81   return *devices.begin();
82 }
83
84 }  // namespace
85
86 ContentSettingTitleAndLinkModel::ContentSettingTitleAndLinkModel(
87     Delegate* delegate,
88     WebContents* web_contents,
89     Profile* profile,
90     ContentSettingsType content_type)
91     : ContentSettingBubbleModel(web_contents, profile, content_type),
92         delegate_(delegate) {
93   // Notifications do not have a bubble.
94   DCHECK_NE(content_type, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
95   SetTitle();
96   SetManageLink();
97   SetLearnMoreLink();
98 }
99
100 void ContentSettingTitleAndLinkModel::SetTitle() {
101   static const ContentSettingsTypeIdEntry kBlockedTitleIDs[] = {
102     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_TITLE},
103     {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_TITLE},
104     {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_TITLE},
105     {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_MESSAGE},
106     {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_TITLE},
107     {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT,
108         IDS_BLOCKED_DISPLAYING_INSECURE_CONTENT},
109     {CONTENT_SETTINGS_TYPE_PPAPI_BROKER,
110         IDS_BLOCKED_PPAPI_BROKER_TITLE},
111     {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOAD_TITLE},
112   };
113   // Fields as for kBlockedTitleIDs, above.
114   static const ContentSettingsTypeIdEntry kAccessedTitleIDs[] = {
115     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_ACCESSED_COOKIES_TITLE},
116     {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_ALLOWED_PPAPI_BROKER_TITLE},
117     {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_ALLOWED_DOWNLOAD_TITLE},
118   };
119   const ContentSettingsTypeIdEntry *title_ids = kBlockedTitleIDs;
120   size_t num_title_ids = arraysize(kBlockedTitleIDs);
121   if (web_contents() &&
122       TabSpecificContentSettings::FromWebContents(
123           web_contents())->IsContentAllowed(content_type()) &&
124       !TabSpecificContentSettings::FromWebContents(
125           web_contents())->IsContentBlocked(content_type())) {
126     title_ids = kAccessedTitleIDs;
127     num_title_ids = arraysize(kAccessedTitleIDs);
128   }
129   int title_id =
130       GetIdForContentType(title_ids, num_title_ids, content_type());
131   if (title_id)
132     set_title(l10n_util::GetStringUTF8(title_id));
133 }
134
135 void ContentSettingTitleAndLinkModel::SetManageLink() {
136   static const ContentSettingsTypeIdEntry kLinkIDs[] = {
137     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_LINK},
138     {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_LINK},
139     {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_LINK},
140     {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_LINK},
141     {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_LINK},
142     {CONTENT_SETTINGS_TYPE_GEOLOCATION, IDS_GEOLOCATION_BUBBLE_MANAGE_LINK},
143     {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, IDS_LEARN_MORE},
144     {CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, IDS_HANDLERS_BUBBLE_MANAGE_LINK},
145     {CONTENT_SETTINGS_TYPE_MEDIASTREAM, IDS_MEDIASTREAM_BUBBLE_MANAGE_LINK},
146     {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_PPAPI_BROKER_BUBBLE_MANAGE_LINK},
147     {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOADS_LINK},
148     {CONTENT_SETTINGS_TYPE_MIDI_SYSEX, IDS_MIDI_SYSEX_BUBBLE_MANAGE_LINK},
149   };
150   set_manage_link(l10n_util::GetStringUTF8(
151       GetIdForContentType(kLinkIDs, arraysize(kLinkIDs), content_type())));
152 }
153
154 void ContentSettingTitleAndLinkModel::OnManageLinkClicked() {
155   if (delegate_)
156     delegate_->ShowContentSettingsPage(content_type());
157 }
158
159 void ContentSettingTitleAndLinkModel::SetLearnMoreLink() {
160   static const ContentSettingsTypeIdEntry kLearnMoreIDs[] = {
161     {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_LEARN_MORE},
162   };
163   int learn_more_id =
164       GetIdForContentType(kLearnMoreIDs, arraysize(kLearnMoreIDs),
165                           content_type());
166   if (learn_more_id)
167     set_learn_more_link(l10n_util::GetStringUTF8(learn_more_id));
168 }
169
170 void ContentSettingTitleAndLinkModel::OnLearnMoreLinkClicked() {
171   if (delegate_)
172     delegate_->ShowLearnMorePage(content_type());
173 }
174
175 class ContentSettingTitleLinkAndCustomModel
176     : public ContentSettingTitleAndLinkModel {
177  public:
178   ContentSettingTitleLinkAndCustomModel(Delegate* delegate,
179                                         WebContents* web_contents,
180                                         Profile* profile,
181                                         ContentSettingsType content_type);
182   ~ContentSettingTitleLinkAndCustomModel() override {}
183
184  private:
185   void SetCustomLink();
186   void OnCustomLinkClicked() override {}
187 };
188
189 ContentSettingTitleLinkAndCustomModel::ContentSettingTitleLinkAndCustomModel(
190     Delegate* delegate,
191     WebContents* web_contents,
192     Profile* profile,
193     ContentSettingsType content_type)
194     : ContentSettingTitleAndLinkModel(
195           delegate, web_contents, profile, content_type) {
196   SetCustomLink();
197 }
198
199 void ContentSettingTitleLinkAndCustomModel::SetCustomLink() {
200   static const ContentSettingsTypeIdEntry kCustomIDs[] = {
201     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_INFO},
202     {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_LOAD_ALL},
203     {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, IDS_ALLOW_INSECURE_CONTENT_BUTTON},
204   };
205   int custom_link_id =
206       GetIdForContentType(kCustomIDs, arraysize(kCustomIDs), content_type());
207   if (custom_link_id)
208     set_custom_link(l10n_util::GetStringUTF8(custom_link_id));
209 }
210
211 class ContentSettingSingleRadioGroup
212     : public ContentSettingTitleLinkAndCustomModel {
213  public:
214   ContentSettingSingleRadioGroup(Delegate* delegate,
215                                  WebContents* web_contents,
216                                  Profile* profile,
217                                  ContentSettingsType content_type);
218   ~ContentSettingSingleRadioGroup() override;
219
220  protected:
221   bool settings_changed() const;
222   int selected_item() const { return selected_item_; }
223
224  private:
225   void SetRadioGroup();
226   void AddException(ContentSetting setting);
227   void OnRadioClicked(int radio_index) override;
228
229   ContentSetting block_setting_;
230   int selected_item_;
231 };
232
233 ContentSettingSingleRadioGroup::ContentSettingSingleRadioGroup(
234     Delegate* delegate,
235     WebContents* web_contents,
236     Profile* profile,
237     ContentSettingsType content_type)
238     : ContentSettingTitleLinkAndCustomModel(delegate, web_contents, profile,
239                                             content_type),
240       block_setting_(CONTENT_SETTING_BLOCK),
241       selected_item_(0) {
242   SetRadioGroup();
243 }
244
245 ContentSettingSingleRadioGroup::~ContentSettingSingleRadioGroup() {
246   if (settings_changed()) {
247     ContentSetting setting =
248         selected_item_ == kAllowButtonIndex ?
249                           CONTENT_SETTING_ALLOW :
250                           block_setting_;
251     AddException(setting);
252   }
253 }
254
255 bool ContentSettingSingleRadioGroup::settings_changed() const {
256   return selected_item_ != bubble_content().radio_group.default_item;
257 }
258
259 // Initialize the radio group by setting the appropriate labels for the
260 // content type and setting the default value based on the content setting.
261 void ContentSettingSingleRadioGroup::SetRadioGroup() {
262   GURL url = web_contents()->GetURL();
263   base::string16 display_host;
264   net::AppendFormattedHost(
265       url,
266       profile()->GetPrefs()->GetString(prefs::kAcceptLanguages),
267       &display_host);
268
269   if (display_host.empty())
270     display_host = base::ASCIIToUTF16(url.spec());
271
272   TabSpecificContentSettings* content_settings =
273       TabSpecificContentSettings::FromWebContents(web_contents());
274   bool allowed =
275       !content_settings->IsContentBlocked(content_type());
276   DCHECK(!allowed ||
277          content_settings->IsContentAllowed(content_type()));
278
279   RadioGroup radio_group;
280   radio_group.url = url;
281
282   static const ContentSettingsTypeIdEntry kBlockedAllowIDs[] = {
283     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_UNBLOCK},
284     {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_UNBLOCK},
285     {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_UNBLOCK},
286     {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_UNBLOCK_ALL},
287     {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_UNBLOCK},
288     {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_BLOCKED_PPAPI_BROKER_UNBLOCK},
289     {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOAD_UNBLOCK},
290   };
291   // Fields as for kBlockedAllowIDs, above.
292   static const ContentSettingsTypeIdEntry kAllowedAllowIDs[] = {
293     // TODO(bauerb): The string shouldn't be "unblock" (they weren't blocked).
294     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_UNBLOCK},
295     {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_ALLOWED_PPAPI_BROKER_NO_ACTION},
296     {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_ALLOWED_DOWNLOAD_NO_ACTION},
297   };
298
299   std::string radio_allow_label;
300   if (allowed) {
301     int resource_id = GetIdForContentType(kAllowedAllowIDs,
302                                           arraysize(kAllowedAllowIDs),
303                                           content_type());
304     radio_allow_label = (content_type() == CONTENT_SETTINGS_TYPE_COOKIES) ?
305         l10n_util::GetStringFUTF8(resource_id, display_host) :
306         l10n_util::GetStringUTF8(resource_id);
307   } else {
308     radio_allow_label = l10n_util::GetStringFUTF8(
309         GetIdForContentType(kBlockedAllowIDs, arraysize(kBlockedAllowIDs),
310                             content_type()),
311         display_host);
312   }
313
314   static const ContentSettingsTypeIdEntry kBlockedBlockIDs[] = {
315     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_NO_ACTION},
316     {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_NO_ACTION},
317     {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_NO_ACTION},
318     {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_NO_ACTION},
319     {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_NO_ACTION},
320     {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_BLOCKED_PPAPI_BROKER_NO_ACTION},
321     {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOAD_NO_ACTION},
322   };
323   static const ContentSettingsTypeIdEntry kAllowedBlockIDs[] = {
324     // TODO(bauerb): The string should say "block".
325     {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_NO_ACTION},
326     {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_ALLOWED_PPAPI_BROKER_BLOCK},
327     {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_ALLOWED_DOWNLOAD_BLOCK},
328   };
329
330   std::string radio_block_label;
331   if (allowed) {
332     int resource_id = GetIdForContentType(kAllowedBlockIDs,
333                                           arraysize(kAllowedBlockIDs),
334                                           content_type());
335     radio_block_label = (content_type() == CONTENT_SETTINGS_TYPE_COOKIES) ?
336         l10n_util::GetStringUTF8(resource_id) :
337         l10n_util::GetStringFUTF8(resource_id, display_host);
338   } else {
339     radio_block_label = l10n_util::GetStringUTF8(
340         GetIdForContentType(kBlockedBlockIDs, arraysize(kBlockedBlockIDs),
341                             content_type()));
342   }
343
344   radio_group.radio_items.push_back(radio_allow_label);
345   radio_group.radio_items.push_back(radio_block_label);
346   ContentSetting setting;
347   SettingSource setting_source = SETTING_SOURCE_NONE;
348   bool setting_is_wildcard = false;
349
350   if (content_type() == CONTENT_SETTINGS_TYPE_COOKIES) {
351     CookieSettings* cookie_settings =
352         CookieSettings::Factory::GetForProfile(profile()).get();
353     setting = cookie_settings->GetCookieSetting(
354         url, url, true, &setting_source);
355   } else {
356     SettingInfo info;
357     HostContentSettingsMap* map = profile()->GetHostContentSettingsMap();
358     scoped_ptr<base::Value> value =
359         map->GetWebsiteSetting(url, url, content_type(), std::string(), &info);
360     setting = content_settings::ValueToContentSetting(value.get());
361     setting_source = info.source;
362     setting_is_wildcard =
363         info.primary_pattern == ContentSettingsPattern::Wildcard() &&
364         info.secondary_pattern == ContentSettingsPattern::Wildcard();
365   }
366
367   if (content_type() == CONTENT_SETTINGS_TYPE_PLUGINS &&
368       setting == CONTENT_SETTING_ALLOW &&
369       setting_is_wildcard) {
370     // In the corner case of unrecognized plugins (which are now blocked by
371     // default) we indicate the blocked state in the UI and allow the user to
372     // whitelist.
373     radio_group.default_item = 1;
374   } else if (setting == CONTENT_SETTING_ALLOW) {
375     radio_group.default_item = kAllowButtonIndex;
376     // |block_setting_| is already set to |CONTENT_SETTING_BLOCK|.
377   } else {
378     radio_group.default_item = 1;
379     block_setting_ = setting;
380   }
381
382   set_setting_is_managed(setting_source != SETTING_SOURCE_USER);
383   if (setting_source != SETTING_SOURCE_USER) {
384     set_radio_group_enabled(false);
385   } else {
386     set_radio_group_enabled(true);
387   }
388   selected_item_ = radio_group.default_item;
389   set_radio_group(radio_group);
390 }
391
392 void ContentSettingSingleRadioGroup::AddException(ContentSetting setting) {
393   if (profile()) {
394     profile()->GetHostContentSettingsMap()->AddExceptionForURL(
395         bubble_content().radio_group.url,
396         bubble_content().radio_group.url,
397         content_type(),
398         setting);
399   }
400 }
401
402 void ContentSettingSingleRadioGroup::OnRadioClicked(int radio_index) {
403   selected_item_ = radio_index;
404 }
405
406 class ContentSettingCookiesBubbleModel : public ContentSettingSingleRadioGroup {
407  public:
408   ContentSettingCookiesBubbleModel(Delegate* delegate,
409                                    WebContents* web_contents,
410                                    Profile* profile,
411                                    ContentSettingsType content_type);
412
413   ~ContentSettingCookiesBubbleModel() override;
414
415  private:
416   void OnCustomLinkClicked() override;
417 };
418
419 ContentSettingCookiesBubbleModel::ContentSettingCookiesBubbleModel(
420     Delegate* delegate,
421     WebContents* web_contents,
422     Profile* profile,
423     ContentSettingsType content_type)
424     : ContentSettingSingleRadioGroup(
425           delegate, web_contents, profile, content_type) {
426   DCHECK_EQ(CONTENT_SETTINGS_TYPE_COOKIES, content_type);
427   set_custom_link_enabled(true);
428 }
429
430 ContentSettingCookiesBubbleModel::~ContentSettingCookiesBubbleModel() {
431   // On some plattforms e.g. MacOS X it is possible to close a tab while the
432   // cookies settings bubble is open. This resets the web contents to NULL.
433   if (settings_changed() && web_contents()) {
434     CollectedCookiesInfoBarDelegate::Create(
435         InfoBarService::FromWebContents(web_contents()));
436   }
437 }
438
439 void ContentSettingCookiesBubbleModel::OnCustomLinkClicked() {
440   if (!web_contents())
441     return;
442   content::NotificationService::current()->Notify(
443       chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
444       content::Source<TabSpecificContentSettings>(
445           TabSpecificContentSettings::FromWebContents(web_contents())),
446       content::NotificationService::NoDetails());
447   delegate()->ShowCollectedCookiesDialog(web_contents());
448 }
449
450 class ContentSettingPluginBubbleModel : public ContentSettingSingleRadioGroup {
451  public:
452   ContentSettingPluginBubbleModel(Delegate* delegate,
453                                   WebContents* web_contents,
454                                   Profile* profile,
455                                   ContentSettingsType content_type);
456
457   ~ContentSettingPluginBubbleModel() override;
458
459  private:
460   void OnCustomLinkClicked() override;
461 };
462
463 ContentSettingPluginBubbleModel::ContentSettingPluginBubbleModel(
464     Delegate* delegate,
465     WebContents* web_contents,
466     Profile* profile,
467     ContentSettingsType content_type)
468     : ContentSettingSingleRadioGroup(
469           delegate, web_contents, profile, content_type) {
470   DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_PLUGINS);
471   // Disable the "Run all plugins this time" link if the setting is managed and
472   // can't be controlled by the user or if the user already clicked on the link
473   // and ran all plugins.
474   set_custom_link_enabled(!setting_is_managed() &&
475                           web_contents &&
476                           TabSpecificContentSettings::FromWebContents(
477                               web_contents)->load_plugins_link_enabled());
478 }
479
480 ContentSettingPluginBubbleModel::~ContentSettingPluginBubbleModel() {
481   if (settings_changed()) {
482     // If the user elected to allow all plugins then run plugins at this time.
483     if (selected_item() == kAllowButtonIndex)
484       OnCustomLinkClicked();
485   }
486 }
487
488 void ContentSettingPluginBubbleModel::OnCustomLinkClicked() {
489   content::RecordAction(UserMetricsAction("ClickToPlay_LoadAll_Bubble"));
490   DCHECK(web_contents());
491 #if defined(ENABLE_PLUGINS)
492   // TODO(bauerb): We should send the identifiers of blocked plug-ins here.
493   ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
494       web_contents(), true, std::string());
495 #endif
496   set_custom_link_enabled(false);
497   TabSpecificContentSettings::FromWebContents(web_contents())->
498       set_load_plugins_link_enabled(false);
499 }
500
501 class ContentSettingPopupBubbleModel : public ContentSettingSingleRadioGroup {
502  public:
503   ContentSettingPopupBubbleModel(Delegate* delegate,
504                                  WebContents* web_contents,
505                                  Profile* profile,
506                                  ContentSettingsType content_type);
507   ~ContentSettingPopupBubbleModel() override {}
508
509  private:
510   void SetPopups();
511   void OnPopupClicked(int index) override;
512 };
513
514 ContentSettingPopupBubbleModel::ContentSettingPopupBubbleModel(
515     Delegate* delegate,
516     WebContents* web_contents,
517     Profile* profile,
518     ContentSettingsType content_type)
519     : ContentSettingSingleRadioGroup(
520         delegate, web_contents, profile, content_type) {
521   SetPopups();
522 }
523
524
525 void ContentSettingPopupBubbleModel::SetPopups() {
526   std::map<int32, GURL> blocked_popups =
527       PopupBlockerTabHelper::FromWebContents(web_contents())
528           ->GetBlockedPopupRequests();
529   for (std::map<int32, GURL>::const_iterator iter = blocked_popups.begin();
530        iter != blocked_popups.end();
531        ++iter) {
532     std::string title(iter->second.spec());
533     // The popup may not have a valid URL.
534     if (title.empty())
535       title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
536     PopupItem popup_item(
537         ui::ResourceBundle::GetSharedInstance().GetImageNamed(
538             IDR_DEFAULT_FAVICON),
539         title,
540         iter->first);
541     add_popup(popup_item);
542   }
543 }
544
545 void ContentSettingPopupBubbleModel::OnPopupClicked(int index) {
546   if (web_contents()) {
547     PopupBlockerTabHelper::FromWebContents(web_contents())->
548         ShowBlockedPopup(bubble_content().popup_items[index].popup_id);
549   }
550 }
551
552 // The model of the content settings bubble for media settings.
553 class ContentSettingMediaStreamBubbleModel
554     : public ContentSettingTitleAndLinkModel {
555  public:
556   ContentSettingMediaStreamBubbleModel(Delegate* delegate,
557                                        WebContents* web_contents,
558                                        Profile* profile);
559
560   ~ContentSettingMediaStreamBubbleModel() override;
561
562  private:
563   void SetTitle();
564   // Sets the data for the radio buttons of the bubble.
565   void SetRadioGroup();
566   // Sets the data for the media menus of the bubble.
567   void SetMediaMenus();
568   void SetCustomLink();
569   // Updates the camera and microphone setting with the passed |setting|.
570   void UpdateSettings(ContentSetting setting);
571   // Updates the camera and microphone default device with the passed |type|
572   // and device.
573   void UpdateDefaultDeviceForType(content::MediaStreamType type,
574                                   const std::string& device);
575
576   // ContentSettingBubbleModel implementation.
577   void OnRadioClicked(int radio_index) override;
578   void OnMediaMenuClicked(content::MediaStreamType type,
579                           const std::string& selected_device) override;
580
581   // The index of the selected radio item.
582   int selected_item_;
583   // The content settings that are associated with the individual radio
584   // buttons.
585   ContentSetting radio_item_setting_[2];
586   // The state of the microphone and camera access.
587   TabSpecificContentSettings::MicrophoneCameraState state_;
588 };
589
590 ContentSettingMediaStreamBubbleModel::ContentSettingMediaStreamBubbleModel(
591     Delegate* delegate,
592     WebContents* web_contents,
593     Profile* profile)
594     : ContentSettingTitleAndLinkModel(
595           delegate, web_contents, profile, CONTENT_SETTINGS_TYPE_MEDIASTREAM),
596       selected_item_(0),
597       state_(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED) {
598   DCHECK(profile);
599   // Initialize the content settings associated with the individual radio
600   // buttons.
601   radio_item_setting_[0] = CONTENT_SETTING_ASK;
602   radio_item_setting_[1] = CONTENT_SETTING_BLOCK;
603
604   TabSpecificContentSettings* content_settings =
605       TabSpecificContentSettings::FromWebContents(web_contents);
606   state_ = content_settings->GetMicrophoneCameraState();
607   DCHECK(state_ & (TabSpecificContentSettings::MICROPHONE_ACCESSED |
608                    TabSpecificContentSettings::CAMERA_ACCESSED));
609
610   SetTitle();
611   SetRadioGroup();
612   SetMediaMenus();
613   SetCustomLink();
614 }
615
616 ContentSettingMediaStreamBubbleModel::~ContentSettingMediaStreamBubbleModel() {
617   // On some platforms (e.g. MacOS X) it is possible to close a tab while the
618   // media stream bubble is open. This resets the web contents to NULL.
619   if (!web_contents())
620     return;
621
622   for (MediaMenuMap::const_iterator it = bubble_content().media_menus.begin();
623       it != bubble_content().media_menus.end(); ++it) {
624     if (it->second.selected_device.id != it->second.default_device.id) {
625       UpdateDefaultDeviceForType(it->first, it->second.selected_device.id);
626     }
627   }
628
629   // Update the media settings if the radio button selection was changed.
630   if (selected_item_ != bubble_content().radio_group.default_item) {
631     UpdateSettings(radio_item_setting_[selected_item_]);
632   }
633 }
634
635 void ContentSettingMediaStreamBubbleModel::SetTitle() {
636   DCHECK_NE(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED, state_);
637   int title_id = 0;
638   if (state_ & TabSpecificContentSettings::MICROPHONE_BLOCKED) {
639     title_id = (state_ & TabSpecificContentSettings::CAMERA_BLOCKED) ?
640         IDS_MICROPHONE_CAMERA_BLOCKED : IDS_MICROPHONE_BLOCKED;
641   } else if (state_ & TabSpecificContentSettings::CAMERA_BLOCKED) {
642     title_id = IDS_CAMERA_BLOCKED;
643   } else if (state_ & TabSpecificContentSettings::MICROPHONE_ACCESSED) {
644     title_id = (state_ & TabSpecificContentSettings::CAMERA_ACCESSED) ?
645         IDS_MICROPHONE_CAMERA_ALLOWED : IDS_MICROPHONE_ACCESSED;
646   } else if (state_ & TabSpecificContentSettings::CAMERA_ACCESSED) {
647     title_id = IDS_CAMERA_ACCESSED;
648   }
649   set_title(l10n_util::GetStringUTF8(title_id));
650 }
651
652 void ContentSettingMediaStreamBubbleModel::SetRadioGroup() {
653   TabSpecificContentSettings* content_settings =
654       TabSpecificContentSettings::FromWebContents(web_contents());
655   GURL url = content_settings->media_stream_access_origin();
656   RadioGroup radio_group;
657   radio_group.url = url;
658
659   base::string16 display_host_utf16;
660   net::AppendFormattedHost(
661       url,
662       profile()->GetPrefs()->GetString(prefs::kAcceptLanguages),
663       &display_host_utf16);
664   std::string display_host(base::UTF16ToUTF8(display_host_utf16));
665   if (display_host.empty())
666     display_host = url.spec();
667
668   bool is_mic = (state_ & TabSpecificContentSettings::MICROPHONE_ACCESSED) != 0;
669   bool is_cam = (state_ & TabSpecificContentSettings::CAMERA_ACCESSED) != 0;
670   DCHECK(is_mic || is_cam);
671   int radio_allow_label_id = 0;
672   int radio_block_label_id = 0;
673   if (state_ & (TabSpecificContentSettings::MICROPHONE_BLOCKED |
674                 TabSpecificContentSettings::CAMERA_BLOCKED)) {
675     if (url.SchemeIsSecure()) {
676       radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
677       radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ALLOW;
678       if (is_mic)
679         radio_allow_label_id = is_cam ?
680             IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ALLOW :
681             IDS_BLOCKED_MEDIASTREAM_MIC_ALLOW;
682     } else {
683       radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ASK;
684       if (is_mic)
685         radio_allow_label_id = is_cam ?
686             IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ASK :
687             IDS_BLOCKED_MEDIASTREAM_MIC_ASK;
688     }
689     radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_NO_ACTION;
690     if (is_mic)
691       radio_block_label_id = is_cam ?
692           IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION :
693           IDS_BLOCKED_MEDIASTREAM_MIC_NO_ACTION;
694   } else {
695     if (is_mic && is_cam) {
696       radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
697       radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_BLOCK;
698     } else if (is_mic) {
699       radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_NO_ACTION;
700       radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_BLOCK;
701     } else {
702       radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION;
703       radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_BLOCK;
704     }
705   }
706   selected_item_ =
707       (is_mic && content_settings->IsContentBlocked(
708           CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)) ||
709       (is_cam && content_settings->IsContentBlocked(
710           CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)) ? 1 : 0;
711
712   std::string radio_allow_label = l10n_util::GetStringFUTF8(
713       radio_allow_label_id, base::UTF8ToUTF16(display_host));
714   std::string radio_block_label =
715       l10n_util::GetStringUTF8(radio_block_label_id);
716
717   radio_group.default_item = selected_item_;
718   radio_group.radio_items.push_back(radio_allow_label);
719   radio_group.radio_items.push_back(radio_block_label);
720
721   set_radio_group(radio_group);
722   set_radio_group_enabled(true);
723 }
724
725 void ContentSettingMediaStreamBubbleModel::UpdateSettings(
726     ContentSetting setting) {
727   if (profile()) {
728     HostContentSettingsMap* content_settings =
729         profile()->GetHostContentSettingsMap();
730     TabSpecificContentSettings* tab_content_settings =
731         TabSpecificContentSettings::FromWebContents(web_contents());
732     // The same patterns must be used as in other places (e.g. the infobar) in
733     // order to override the existing rule. Otherwise a new rule is created.
734     // TODO(markusheintz): Extract to a helper so that there is only a single
735     // place to touch.
736     ContentSettingsPattern primary_pattern =
737         ContentSettingsPattern::FromURLNoWildcard(
738             tab_content_settings->media_stream_access_origin());
739     ContentSettingsPattern secondary_pattern =
740         ContentSettingsPattern::Wildcard();
741     if (state_ & TabSpecificContentSettings::MICROPHONE_ACCESSED) {
742       content_settings->SetContentSetting(
743           primary_pattern, secondary_pattern,
744           CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(), setting);
745     }
746     if (state_ & TabSpecificContentSettings::CAMERA_ACCESSED) {
747       content_settings->SetContentSetting(
748           primary_pattern, secondary_pattern,
749           CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(), setting);
750     }
751   }
752 }
753
754 void ContentSettingMediaStreamBubbleModel::UpdateDefaultDeviceForType(
755     content::MediaStreamType type,
756     const std::string& device) {
757   PrefService* prefs = profile()->GetPrefs();
758   if (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
759     prefs->SetString(prefs::kDefaultAudioCaptureDevice, device);
760   } else {
761     DCHECK_EQ(content::MEDIA_DEVICE_VIDEO_CAPTURE, type);
762     prefs->SetString(prefs::kDefaultVideoCaptureDevice, device);
763   }
764 }
765
766 void ContentSettingMediaStreamBubbleModel::SetMediaMenus() {
767   TabSpecificContentSettings* content_settings =
768       TabSpecificContentSettings::FromWebContents(web_contents());
769   const std::string& requested_microphone =
770        content_settings->media_stream_requested_audio_device();
771   const std::string& requested_camera =
772       content_settings->media_stream_requested_video_device();
773
774   // Add microphone menu.
775   PrefService* prefs = profile()->GetPrefs();
776   MediaCaptureDevicesDispatcher* dispatcher =
777       MediaCaptureDevicesDispatcher::GetInstance();
778   const content::MediaStreamDevices& microphones =
779       dispatcher->GetAudioCaptureDevices();
780
781   if (state_ & TabSpecificContentSettings::MICROPHONE_ACCESSED) {
782     MediaMenu mic_menu;
783     mic_menu.label = l10n_util::GetStringUTF8(IDS_MEDIA_SELECTED_MIC_LABEL);
784     if (!microphones.empty()) {
785       std::string preferred_mic;
786       if (requested_microphone.empty()) {
787         preferred_mic = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
788         mic_menu.disabled = false;
789       } else {
790         // Set the |disabled| to true in order to disable the device selection
791         // menu on the media settings bubble. This must be done if the website
792         // manages the microphone devices itself.
793         preferred_mic = requested_microphone;
794         mic_menu.disabled = true;
795       }
796
797       mic_menu.default_device = GetMediaDeviceById(preferred_mic, microphones);
798       mic_menu.selected_device = mic_menu.default_device;
799     }
800     add_media_menu(content::MEDIA_DEVICE_AUDIO_CAPTURE, mic_menu);
801   }
802
803   if (state_ & TabSpecificContentSettings::CAMERA_ACCESSED) {
804     const content::MediaStreamDevices& cameras =
805         dispatcher->GetVideoCaptureDevices();
806     MediaMenu camera_menu;
807     camera_menu.label =
808         l10n_util::GetStringUTF8(IDS_MEDIA_SELECTED_CAMERA_LABEL);
809     if (!cameras.empty()) {
810       std::string preferred_camera;
811       if (requested_camera.empty()) {
812         preferred_camera = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
813         camera_menu.disabled = false;
814       } else {
815         // Disable the menu since the website is managing the camera devices
816         // itself.
817         preferred_camera = requested_camera;
818         camera_menu.disabled = true;
819       }
820
821       camera_menu.default_device =
822           GetMediaDeviceById(preferred_camera, cameras);
823       camera_menu.selected_device = camera_menu.default_device;
824     }
825     add_media_menu(content::MEDIA_DEVICE_VIDEO_CAPTURE, camera_menu);
826   }
827 }
828
829 void ContentSettingMediaStreamBubbleModel::SetCustomLink() {
830   TabSpecificContentSettings* content_settings =
831       TabSpecificContentSettings::FromWebContents(web_contents());
832   if (content_settings->IsMicrophoneCameraStateChanged()) {
833     set_custom_link(l10n_util::GetStringUTF8(
834         IDS_MEDIASTREAM_SETTING_CHANGED_MESSAGE));
835   }
836 }
837
838 void ContentSettingMediaStreamBubbleModel::OnRadioClicked(int radio_index) {
839   selected_item_ = radio_index;
840 }
841
842 void ContentSettingMediaStreamBubbleModel::OnMediaMenuClicked(
843     content::MediaStreamType type,
844     const std::string& selected_device_id) {
845   DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
846          type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
847   DCHECK_EQ(1U, bubble_content().media_menus.count(type));
848   MediaCaptureDevicesDispatcher* dispatcher =
849       MediaCaptureDevicesDispatcher::GetInstance();
850   const content::MediaStreamDevices& devices =
851       (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) ?
852           dispatcher->GetAudioCaptureDevices() :
853           dispatcher->GetVideoCaptureDevices();
854   set_selected_device(GetMediaDeviceById(selected_device_id, devices));
855 }
856
857 class ContentSettingDomainListBubbleModel
858     : public ContentSettingTitleAndLinkModel {
859  public:
860   ContentSettingDomainListBubbleModel(Delegate* delegate,
861                                       WebContents* web_contents,
862                                       Profile* profile,
863                                       ContentSettingsType content_type);
864   ~ContentSettingDomainListBubbleModel() override {}
865
866  private:
867   void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id);
868   void SetDomainsAndCustomLink();
869   void OnCustomLinkClicked() override;
870 };
871
872 ContentSettingDomainListBubbleModel::ContentSettingDomainListBubbleModel(
873     Delegate* delegate,
874     WebContents* web_contents,
875     Profile* profile,
876     ContentSettingsType content_type)
877     : ContentSettingTitleAndLinkModel(
878         delegate, web_contents, profile, content_type) {
879   DCHECK_EQ(CONTENT_SETTINGS_TYPE_GEOLOCATION, content_type) <<
880       "SetDomains currently only supports geolocation content type";
881   SetDomainsAndCustomLink();
882 }
883
884 void ContentSettingDomainListBubbleModel::MaybeAddDomainList(
885     const std::set<std::string>& hosts, int title_id) {
886   if (!hosts.empty()) {
887     DomainList domain_list;
888     domain_list.title = l10n_util::GetStringUTF8(title_id);
889     domain_list.hosts = hosts;
890     add_domain_list(domain_list);
891   }
892 }
893
894 void ContentSettingDomainListBubbleModel::SetDomainsAndCustomLink() {
895   TabSpecificContentSettings* content_settings =
896       TabSpecificContentSettings::FromWebContents(web_contents());
897   const ContentSettingsUsagesState& usages =
898       content_settings->geolocation_usages_state();
899   ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
900   unsigned int tab_state_flags = 0;
901   usages.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags);
902   // Divide the tab's current geolocation users into sets according to their
903   // permission state.
904   MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW],
905                      IDS_GEOLOCATION_BUBBLE_SECTION_ALLOWED);
906
907   MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK],
908                      IDS_GEOLOCATION_BUBBLE_SECTION_DENIED);
909
910   if (tab_state_flags & ContentSettingsUsagesState::TABSTATE_HAS_EXCEPTION) {
911     set_custom_link(l10n_util::GetStringUTF8(
912         IDS_GEOLOCATION_BUBBLE_CLEAR_LINK));
913     set_custom_link_enabled(true);
914   } else if (tab_state_flags &
915              ContentSettingsUsagesState::TABSTATE_HAS_CHANGED) {
916     set_custom_link(l10n_util::GetStringUTF8(
917         IDS_GEOLOCATION_BUBBLE_REQUIRE_RELOAD_TO_CLEAR));
918   }
919 }
920
921 void ContentSettingDomainListBubbleModel::OnCustomLinkClicked() {
922   if (!web_contents())
923     return;
924   // Reset this embedder's entry to default for each of the requesting
925   // origins currently on the page.
926   const GURL& embedder_url = web_contents()->GetURL();
927   TabSpecificContentSettings* content_settings =
928       TabSpecificContentSettings::FromWebContents(web_contents());
929   const ContentSettingsUsagesState::StateMap& state_map =
930       content_settings->geolocation_usages_state().state_map();
931   HostContentSettingsMap* settings_map =
932       profile()->GetHostContentSettingsMap();
933
934   for (ContentSettingsUsagesState::StateMap::const_iterator it =
935        state_map.begin(); it != state_map.end(); ++it) {
936     settings_map->SetContentSetting(
937         ContentSettingsPattern::FromURLNoWildcard(it->first),
938         ContentSettingsPattern::FromURLNoWildcard(embedder_url),
939         CONTENT_SETTINGS_TYPE_GEOLOCATION,
940         std::string(),
941         CONTENT_SETTING_DEFAULT);
942   }
943 }
944
945 class ContentSettingMixedScriptBubbleModel
946     : public ContentSettingTitleLinkAndCustomModel {
947  public:
948   ContentSettingMixedScriptBubbleModel(Delegate* delegate,
949                                        WebContents* web_contents,
950                                        Profile* profile,
951                                        ContentSettingsType content_type);
952
953   ~ContentSettingMixedScriptBubbleModel() override {}
954
955  private:
956   void OnCustomLinkClicked() override;
957 };
958
959 ContentSettingMixedScriptBubbleModel::ContentSettingMixedScriptBubbleModel(
960     Delegate* delegate,
961     WebContents* web_contents,
962     Profile* profile,
963     ContentSettingsType content_type)
964     : ContentSettingTitleLinkAndCustomModel(
965         delegate, web_contents, profile, content_type) {
966   DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_MIXEDSCRIPT);
967   TabSpecificContentSettings::RecordMixedScriptAction(
968       TabSpecificContentSettings::MIXED_SCRIPT_ACTION_DISPLAYED_BUBBLE);
969   set_custom_link_enabled(true);
970 }
971
972 void ContentSettingMixedScriptBubbleModel::OnCustomLinkClicked() {
973   TabSpecificContentSettings::RecordMixedScriptAction(
974       TabSpecificContentSettings::MIXED_SCRIPT_ACTION_CLICKED_ALLOW);
975   DCHECK(web_contents());
976   web_contents()->SendToAllFrames(
977       new ChromeViewMsg_SetAllowRunningInsecureContent(MSG_ROUTING_NONE, true));
978   web_contents()->GetMainFrame()->Send(new ChromeViewMsg_ReloadFrame(
979       web_contents()->GetMainFrame()->GetRoutingID()));
980 }
981
982 ContentSettingRPHBubbleModel::ContentSettingRPHBubbleModel(
983     Delegate* delegate,
984     WebContents* web_contents,
985     Profile* profile,
986     ProtocolHandlerRegistry* registry,
987     ContentSettingsType content_type)
988     : ContentSettingTitleAndLinkModel(
989           delegate, web_contents, profile, content_type),
990       selected_item_(0),
991       registry_(registry),
992       pending_handler_(ProtocolHandler::EmptyProtocolHandler()),
993       previous_handler_(ProtocolHandler::EmptyProtocolHandler()) {
994   DCHECK_EQ(CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, content_type);
995
996   TabSpecificContentSettings* content_settings =
997       TabSpecificContentSettings::FromWebContents(web_contents);
998   pending_handler_ = content_settings->pending_protocol_handler();
999   previous_handler_ = content_settings->previous_protocol_handler();
1000
1001   base::string16 protocol;
1002   if (pending_handler_.protocol() == "mailto") {
1003     protocol = l10n_util::GetStringUTF16(
1004         IDS_REGISTER_PROTOCOL_HANDLER_MAILTO_NAME);
1005   } else if (pending_handler_.protocol() == "webcal") {
1006     protocol = l10n_util::GetStringUTF16(
1007         IDS_REGISTER_PROTOCOL_HANDLER_WEBCAL_NAME);
1008   } else {
1009     protocol = base::UTF8ToUTF16(pending_handler_.protocol());
1010   }
1011
1012   // Note that we ignore the |title| parameter.
1013   if (previous_handler_.IsEmpty()) {
1014     set_title(l10n_util::GetStringFUTF8(
1015         IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM,
1016         base::UTF8ToUTF16(pending_handler_.url().host()),
1017         protocol));
1018   } else {
1019     set_title(l10n_util::GetStringFUTF8(
1020         IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE,
1021         base::UTF8ToUTF16(pending_handler_.url().host()),
1022         protocol,
1023         base::UTF8ToUTF16(previous_handler_.url().host())));
1024   }
1025
1026   std::string radio_allow_label =
1027       l10n_util::GetStringUTF8(IDS_REGISTER_PROTOCOL_HANDLER_ACCEPT);
1028   std::string radio_deny_label =
1029       l10n_util::GetStringUTF8(IDS_REGISTER_PROTOCOL_HANDLER_DENY);
1030   std::string radio_ignore_label =
1031       l10n_util::GetStringUTF8(IDS_REGISTER_PROTOCOL_HANDLER_IGNORE);
1032
1033   GURL url = web_contents->GetURL();
1034   RadioGroup radio_group;
1035   radio_group.url = url;
1036
1037   radio_group.radio_items.push_back(radio_allow_label);
1038   radio_group.radio_items.push_back(radio_deny_label);
1039   radio_group.radio_items.push_back(radio_ignore_label);
1040   ContentSetting setting =
1041       content_settings->pending_protocol_handler_setting();
1042   if (setting == CONTENT_SETTING_ALLOW)
1043     radio_group.default_item = RPH_ALLOW;
1044   else if (setting == CONTENT_SETTING_BLOCK)
1045     radio_group.default_item = RPH_BLOCK;
1046   else
1047     radio_group.default_item = RPH_IGNORE;
1048
1049   selected_item_ = radio_group.default_item;
1050   set_radio_group_enabled(true);
1051   set_radio_group(radio_group);
1052 }
1053
1054 void ContentSettingRPHBubbleModel::OnRadioClicked(int radio_index) {
1055   if (selected_item_ == radio_index)
1056     return;
1057
1058   selected_item_ = radio_index;
1059
1060   if (radio_index == RPH_ALLOW)
1061     RegisterProtocolHandler();
1062   else if (radio_index == RPH_BLOCK)
1063     UnregisterProtocolHandler();
1064   else if (radio_index == RPH_IGNORE)
1065     IgnoreProtocolHandler();
1066   else
1067     NOTREACHED();
1068 }
1069
1070 void ContentSettingRPHBubbleModel::OnDoneClicked() {
1071   // The user has one chance to deal with the RPH content setting UI,
1072   // then we remove it.
1073   TabSpecificContentSettings::FromWebContents(web_contents())->
1074       ClearPendingProtocolHandler();
1075   content::NotificationService::current()->Notify(
1076       chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
1077       content::Source<WebContents>(web_contents()),
1078       content::NotificationService::NoDetails());
1079 }
1080
1081 void ContentSettingRPHBubbleModel::RegisterProtocolHandler() {
1082   // A no-op if the handler hasn't been ignored, but needed in case the user
1083   // selects sequences like register/ignore/register.
1084   registry_->RemoveIgnoredHandler(pending_handler_);
1085
1086   registry_->OnAcceptRegisterProtocolHandler(pending_handler_);
1087   TabSpecificContentSettings::FromWebContents(web_contents())->
1088       set_pending_protocol_handler_setting(CONTENT_SETTING_ALLOW);
1089 }
1090
1091 void ContentSettingRPHBubbleModel::UnregisterProtocolHandler() {
1092   registry_->OnDenyRegisterProtocolHandler(pending_handler_);
1093   TabSpecificContentSettings::FromWebContents(web_contents())->
1094       set_pending_protocol_handler_setting(CONTENT_SETTING_BLOCK);
1095   ClearOrSetPreviousHandler();
1096 }
1097
1098 void ContentSettingRPHBubbleModel::IgnoreProtocolHandler() {
1099   registry_->OnIgnoreRegisterProtocolHandler(pending_handler_);
1100   TabSpecificContentSettings::FromWebContents(web_contents())->
1101       set_pending_protocol_handler_setting(CONTENT_SETTING_DEFAULT);
1102   ClearOrSetPreviousHandler();
1103 }
1104
1105 void ContentSettingRPHBubbleModel::ClearOrSetPreviousHandler() {
1106   if (previous_handler_.IsEmpty()) {
1107     registry_->ClearDefault(pending_handler_.protocol());
1108   } else {
1109     registry_->OnAcceptRegisterProtocolHandler(previous_handler_);
1110   }
1111 }
1112
1113 class ContentSettingMidiSysExBubbleModel
1114     : public ContentSettingTitleAndLinkModel {
1115  public:
1116   ContentSettingMidiSysExBubbleModel(Delegate* delegate,
1117                                      WebContents* web_contents,
1118                                      Profile* profile,
1119                                      ContentSettingsType content_type);
1120   ~ContentSettingMidiSysExBubbleModel() override {}
1121
1122  private:
1123   void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id);
1124   void SetDomainsAndCustomLink();
1125   void OnCustomLinkClicked() override;
1126 };
1127
1128 ContentSettingMidiSysExBubbleModel::ContentSettingMidiSysExBubbleModel(
1129     Delegate* delegate,
1130     WebContents* web_contents,
1131     Profile* profile,
1132     ContentSettingsType content_type)
1133     : ContentSettingTitleAndLinkModel(
1134         delegate, web_contents, profile, content_type) {
1135   DCHECK_EQ(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, content_type);
1136   SetDomainsAndCustomLink();
1137 }
1138
1139 void ContentSettingMidiSysExBubbleModel::MaybeAddDomainList(
1140     const std::set<std::string>& hosts, int title_id) {
1141   if (!hosts.empty()) {
1142     DomainList domain_list;
1143     domain_list.title = l10n_util::GetStringUTF8(title_id);
1144     domain_list.hosts = hosts;
1145     add_domain_list(domain_list);
1146   }
1147 }
1148
1149 void ContentSettingMidiSysExBubbleModel::SetDomainsAndCustomLink() {
1150   TabSpecificContentSettings* content_settings =
1151       TabSpecificContentSettings::FromWebContents(web_contents());
1152   const ContentSettingsUsagesState& usages_state =
1153       content_settings->midi_usages_state();
1154   ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
1155   unsigned int tab_state_flags = 0;
1156   usages_state.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags);
1157   // Divide the tab's current MIDI sysex users into sets according to their
1158   // permission state.
1159   MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW],
1160                      IDS_MIDI_SYSEX_BUBBLE_ALLOWED);
1161
1162   MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK],
1163                      IDS_MIDI_SYSEX_BUBBLE_DENIED);
1164
1165   if (tab_state_flags & ContentSettingsUsagesState::TABSTATE_HAS_EXCEPTION) {
1166     set_custom_link(l10n_util::GetStringUTF8(
1167         IDS_MIDI_SYSEX_BUBBLE_CLEAR_LINK));
1168     set_custom_link_enabled(true);
1169   } else if (tab_state_flags &
1170              ContentSettingsUsagesState::TABSTATE_HAS_CHANGED) {
1171     set_custom_link(l10n_util::GetStringUTF8(
1172         IDS_MIDI_SYSEX_BUBBLE_REQUIRE_RELOAD_TO_CLEAR));
1173   }
1174 }
1175
1176 void ContentSettingMidiSysExBubbleModel::OnCustomLinkClicked() {
1177   if (!web_contents())
1178     return;
1179   // Reset this embedder's entry to default for each of the requesting
1180   // origins currently on the page.
1181   TabSpecificContentSettings* content_settings =
1182       TabSpecificContentSettings::FromWebContents(web_contents());
1183   const ContentSettingsUsagesState::StateMap& state_map =
1184       content_settings->midi_usages_state().state_map();
1185   HostContentSettingsMap* settings_map =
1186       profile()->GetHostContentSettingsMap();
1187
1188   for (ContentSettingsUsagesState::StateMap::const_iterator it =
1189        state_map.begin(); it != state_map.end(); ++it) {
1190     settings_map->SetContentSetting(
1191         ContentSettingsPattern::FromURLNoWildcard(it->first),
1192         ContentSettingsPattern::Wildcard(),
1193         CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
1194         std::string(),
1195         CONTENT_SETTING_DEFAULT);
1196   }
1197 }
1198
1199 // static
1200 ContentSettingBubbleModel*
1201     ContentSettingBubbleModel::CreateContentSettingBubbleModel(
1202         Delegate* delegate,
1203         WebContents* web_contents,
1204         Profile* profile,
1205         ContentSettingsType content_type) {
1206   if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
1207     return new ContentSettingCookiesBubbleModel(delegate, web_contents, profile,
1208                                                 content_type);
1209   }
1210   if (content_type == CONTENT_SETTINGS_TYPE_POPUPS) {
1211     return new ContentSettingPopupBubbleModel(delegate, web_contents, profile,
1212                                               content_type);
1213   }
1214   if (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) {
1215     return new ContentSettingDomainListBubbleModel(delegate, web_contents,
1216                                                    profile, content_type);
1217   }
1218   if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
1219     return new ContentSettingMediaStreamBubbleModel(delegate, web_contents,
1220                                                     profile);
1221   }
1222   if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
1223     return new ContentSettingPluginBubbleModel(delegate, web_contents, profile,
1224                                                content_type);
1225   }
1226   if (content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT) {
1227     return new ContentSettingMixedScriptBubbleModel(delegate, web_contents,
1228                                                     profile, content_type);
1229   }
1230   if (content_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) {
1231     ProtocolHandlerRegistry* registry =
1232         ProtocolHandlerRegistryFactory::GetForBrowserContext(profile);
1233     return new ContentSettingRPHBubbleModel(delegate, web_contents, profile,
1234                                             registry, content_type);
1235   }
1236   if (content_type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
1237     return new ContentSettingMidiSysExBubbleModel(delegate, web_contents,
1238                                                   profile, content_type);
1239   }
1240   return new ContentSettingSingleRadioGroup(delegate, web_contents, profile,
1241                                             content_type);
1242 }
1243
1244 ContentSettingBubbleModel::ContentSettingBubbleModel(
1245     WebContents* web_contents,
1246     Profile* profile,
1247     ContentSettingsType content_type)
1248     : web_contents_(web_contents),
1249       profile_(profile),
1250       content_type_(content_type),
1251       setting_is_managed_(false) {
1252   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
1253                  content::Source<WebContents>(web_contents));
1254   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
1255                  content::Source<Profile>(profile_));
1256 }
1257
1258 ContentSettingBubbleModel::~ContentSettingBubbleModel() {
1259 }
1260
1261 ContentSettingBubbleModel::RadioGroup::RadioGroup() : default_item(0) {}
1262
1263 ContentSettingBubbleModel::RadioGroup::~RadioGroup() {}
1264
1265 ContentSettingBubbleModel::DomainList::DomainList() {}
1266
1267 ContentSettingBubbleModel::DomainList::~DomainList() {}
1268
1269 ContentSettingBubbleModel::MediaMenu::MediaMenu() : disabled(false) {}
1270
1271 ContentSettingBubbleModel::MediaMenu::~MediaMenu() {}
1272
1273 ContentSettingBubbleModel::BubbleContent::BubbleContent()
1274     : radio_group_enabled(false),
1275       custom_link_enabled(false) {
1276 }
1277
1278 ContentSettingBubbleModel::BubbleContent::~BubbleContent() {}
1279
1280 void ContentSettingBubbleModel::Observe(
1281     int type,
1282     const content::NotificationSource& source,
1283     const content::NotificationDetails& details) {
1284   if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
1285     DCHECK_EQ(web_contents_,
1286               content::Source<WebContents>(source).ptr());
1287     web_contents_ = NULL;
1288   } else {
1289     DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
1290     DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
1291     profile_ = NULL;
1292   }
1293 }