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