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