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
42using content::UserMetricsAction;
43using content::WebContents;
44using content_settings::SettingInfo;
45using content_settings::SettingSource;
46using content_settings::SETTING_SOURCE_USER;
47using content_settings::SETTING_SOURCE_NONE;
48
49namespace {
50
51const int kAllowButtonIndex = 0;
52
53struct ContentSettingsTypeIdEntry {
54  ContentSettingsType type;
55  int id;
56};
57
58int 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
68const 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
86ContentSettingTitleAndLinkModel::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
99void 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
134void 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
153void ContentSettingTitleAndLinkModel::OnManageLinkClicked() {
154  if (delegate_)
155    delegate_->ShowContentSettingsPage(content_type());
156}
157
158class 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
172ContentSettingTitleLinkAndCustomModel::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
182void 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
194class 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
216ContentSettingSingleRadioGroup::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
228ContentSettingSingleRadioGroup::~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
238bool 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.
244void 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 = 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<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  if (setting_source != SETTING_SOURCE_USER) {
365    set_radio_group_enabled(false);
366  } else {
367    set_radio_group_enabled(true);
368  }
369  selected_item_ = radio_group.default_item;
370  set_radio_group(radio_group);
371}
372
373void ContentSettingSingleRadioGroup::AddException(ContentSetting setting) {
374  if (profile()) {
375    profile()->GetHostContentSettingsMap()->AddExceptionForURL(
376        bubble_content().radio_group.url,
377        bubble_content().radio_group.url,
378        content_type(),
379        setting);
380  }
381}
382
383void ContentSettingSingleRadioGroup::OnRadioClicked(int radio_index) {
384  selected_item_ = radio_index;
385}
386
387class ContentSettingCookiesBubbleModel : public ContentSettingSingleRadioGroup {
388 public:
389  ContentSettingCookiesBubbleModel(Delegate* delegate,
390                                   WebContents* web_contents,
391                                   Profile* profile,
392                                   ContentSettingsType content_type);
393
394  virtual ~ContentSettingCookiesBubbleModel();
395
396 private:
397  virtual void OnCustomLinkClicked() OVERRIDE;
398};
399
400ContentSettingCookiesBubbleModel::ContentSettingCookiesBubbleModel(
401    Delegate* delegate,
402    WebContents* web_contents,
403    Profile* profile,
404    ContentSettingsType content_type)
405    : ContentSettingSingleRadioGroup(
406          delegate, web_contents, profile, content_type) {
407  DCHECK_EQ(CONTENT_SETTINGS_TYPE_COOKIES, content_type);
408  set_custom_link_enabled(true);
409}
410
411ContentSettingCookiesBubbleModel::~ContentSettingCookiesBubbleModel() {
412  // On some plattforms e.g. MacOS X it is possible to close a tab while the
413  // cookies settings bubble is open. This resets the web contents to NULL.
414  if (settings_changed() && web_contents()) {
415    CollectedCookiesInfoBarDelegate::Create(
416        InfoBarService::FromWebContents(web_contents()));
417  }
418}
419
420void ContentSettingCookiesBubbleModel::OnCustomLinkClicked() {
421  if (!web_contents())
422    return;
423  content::NotificationService::current()->Notify(
424      chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
425      content::Source<TabSpecificContentSettings>(
426          TabSpecificContentSettings::FromWebContents(web_contents())),
427      content::NotificationService::NoDetails());
428  delegate()->ShowCollectedCookiesDialog(web_contents());
429}
430
431class ContentSettingPluginBubbleModel : public ContentSettingSingleRadioGroup {
432 public:
433  ContentSettingPluginBubbleModel(Delegate* delegate,
434                                  WebContents* web_contents,
435                                  Profile* profile,
436                                  ContentSettingsType content_type);
437
438  virtual ~ContentSettingPluginBubbleModel();
439
440 private:
441  virtual void OnCustomLinkClicked() OVERRIDE;
442};
443
444ContentSettingPluginBubbleModel::ContentSettingPluginBubbleModel(
445    Delegate* delegate,
446    WebContents* web_contents,
447    Profile* profile,
448    ContentSettingsType content_type)
449    : ContentSettingSingleRadioGroup(
450          delegate, web_contents, profile, content_type) {
451  DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_PLUGINS);
452  set_custom_link_enabled(web_contents &&
453                          TabSpecificContentSettings::FromWebContents(
454                              web_contents)->load_plugins_link_enabled());
455}
456
457ContentSettingPluginBubbleModel::~ContentSettingPluginBubbleModel() {
458  if (settings_changed()) {
459    // If the user elected to allow all plugins then run plugins at this time.
460    if (selected_item() == kAllowButtonIndex)
461      OnCustomLinkClicked();
462  }
463}
464
465void ContentSettingPluginBubbleModel::OnCustomLinkClicked() {
466  content::RecordAction(UserMetricsAction("ClickToPlay_LoadAll_Bubble"));
467  DCHECK(web_contents());
468  content::RenderViewHost* host = web_contents()->GetRenderViewHost();
469#if defined(ENABLE_PLUGINS)
470  ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
471      host->GetProcess()->GetID());
472#endif
473  // TODO(bauerb): We should send the identifiers of blocked plug-ins here.
474  host->Send(new ChromeViewMsg_LoadBlockedPlugins(host->GetRoutingID(),
475                                                  std::string()));
476  set_custom_link_enabled(false);
477  TabSpecificContentSettings::FromWebContents(web_contents())->
478      set_load_plugins_link_enabled(false);
479}
480
481class ContentSettingPopupBubbleModel : public ContentSettingSingleRadioGroup {
482 public:
483  ContentSettingPopupBubbleModel(Delegate* delegate,
484                                 WebContents* web_contents,
485                                 Profile* profile,
486                                 ContentSettingsType content_type);
487  virtual ~ContentSettingPopupBubbleModel() {}
488
489 private:
490  void SetPopups();
491  virtual void OnPopupClicked(int index) OVERRIDE;
492};
493
494ContentSettingPopupBubbleModel::ContentSettingPopupBubbleModel(
495    Delegate* delegate,
496    WebContents* web_contents,
497    Profile* profile,
498    ContentSettingsType content_type)
499    : ContentSettingSingleRadioGroup(
500        delegate, web_contents, profile, content_type) {
501  SetPopups();
502}
503
504
505void ContentSettingPopupBubbleModel::SetPopups() {
506  std::map<int32, GURL> blocked_popups =
507      PopupBlockerTabHelper::FromWebContents(web_contents())
508          ->GetBlockedPopupRequests();
509  for (std::map<int32, GURL>::const_iterator iter = blocked_popups.begin();
510       iter != blocked_popups.end();
511       ++iter) {
512    std::string title(iter->second.spec());
513    // The popup may not have a valid URL.
514    if (title.empty())
515      title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
516    PopupItem popup_item(
517        ui::ResourceBundle::GetSharedInstance().GetImageNamed(
518            IDR_DEFAULT_FAVICON),
519        title,
520        iter->first);
521    add_popup(popup_item);
522  }
523}
524
525void ContentSettingPopupBubbleModel::OnPopupClicked(int index) {
526  if (web_contents()) {
527    PopupBlockerTabHelper::FromWebContents(web_contents())->
528        ShowBlockedPopup(bubble_content().popup_items[index].popup_id);
529  }
530}
531
532// The model of the content settings bubble for media settings.
533class ContentSettingMediaStreamBubbleModel
534    : public ContentSettingTitleAndLinkModel {
535 public:
536  ContentSettingMediaStreamBubbleModel(Delegate* delegate,
537                                       WebContents* web_contents,
538                                       Profile* profile);
539
540  virtual ~ContentSettingMediaStreamBubbleModel();
541
542 private:
543  void SetTitle();
544  // Sets the data for the radio buttons of the bubble.
545  void SetRadioGroup();
546  // Sets the data for the media menus of the bubble.
547  void SetMediaMenus();
548  // Updates the camera and microphone setting with the passed |setting|.
549  void UpdateSettings(ContentSetting setting);
550  // Updates the camera and microphone default device with the passed |type|
551  // and device.
552  void UpdateDefaultDeviceForType(content::MediaStreamType type,
553                                  const std::string& device);
554
555  // ContentSettingBubbleModel implementation.
556  virtual void OnRadioClicked(int radio_index) OVERRIDE;
557  virtual void OnMediaMenuClicked(content::MediaStreamType type,
558                                  const std::string& selected_device) OVERRIDE;
559
560  // The index of the selected radio item.
561  int selected_item_;
562  // The content settings that are associated with the individual radio
563  // buttons.
564  ContentSetting radio_item_setting_[2];
565  // The state of the microphone and camera access.
566  TabSpecificContentSettings::MicrophoneCameraState state_;
567};
568
569ContentSettingMediaStreamBubbleModel::ContentSettingMediaStreamBubbleModel(
570    Delegate* delegate,
571    WebContents* web_contents,
572    Profile* profile)
573    : ContentSettingTitleAndLinkModel(
574          delegate, web_contents, profile, CONTENT_SETTINGS_TYPE_MEDIASTREAM),
575      selected_item_(0),
576      state_(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED) {
577  DCHECK(profile);
578  // Initialize the content settings associated with the individual radio
579  // buttons.
580  radio_item_setting_[0] = CONTENT_SETTING_ASK;
581  radio_item_setting_[1] = CONTENT_SETTING_BLOCK;
582
583  TabSpecificContentSettings* content_settings =
584      TabSpecificContentSettings::FromWebContents(web_contents);
585  state_ = content_settings->GetMicrophoneCameraState();
586
587  SetTitle();
588  SetRadioGroup();
589  SetMediaMenus();
590}
591
592ContentSettingMediaStreamBubbleModel::~ContentSettingMediaStreamBubbleModel() {
593  // On some platforms (e.g. MacOS X) it is possible to close a tab while the
594  // media stream bubble is open. This resets the web contents to NULL.
595  if (!web_contents())
596    return;
597
598  bool media_setting_changed = false;
599  for (MediaMenuMap::const_iterator it = bubble_content().media_menus.begin();
600      it != bubble_content().media_menus.end(); ++it) {
601    if (it->second.selected_device.id != it->second.default_device.id) {
602      UpdateDefaultDeviceForType(it->first, it->second.selected_device.id);
603      media_setting_changed = true;
604    }
605  }
606
607  // Update the media settings if the radio button selection was changed.
608  if (selected_item_ != bubble_content().radio_group.default_item) {
609    UpdateSettings(radio_item_setting_[selected_item_]);
610    media_setting_changed = true;
611  }
612
613  // Trigger the reload infobar if the media setting has been changed.
614  if (media_setting_changed) {
615    MediaSettingChangedInfoBarDelegate::Create(
616        InfoBarService::FromWebContents(web_contents()));
617  }
618}
619
620void ContentSettingMediaStreamBubbleModel::SetTitle() {
621  int title_id = 0;
622  switch (state_) {
623    case TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED:
624      // If neither microphone nor camera stream was accessed, then there is no
625      // icon didplayed in the omnibox and no settings bubble availbale. Hence
626      // there is no title.
627      NOTREACHED();
628      return;
629    case TabSpecificContentSettings::MICROPHONE_ACCESSED:
630      title_id = IDS_MICROPHONE_ACCESSED;
631      break;
632    case TabSpecificContentSettings::CAMERA_ACCESSED:
633      title_id = IDS_CAMERA_ACCESSED;
634      break;
635    case TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED:
636      title_id = IDS_MICROPHONE_CAMERA_ALLOWED;
637      break;
638    case TabSpecificContentSettings::MICROPHONE_BLOCKED:
639      title_id = IDS_MICROPHONE_BLOCKED;
640      break;
641    case TabSpecificContentSettings::CAMERA_BLOCKED:
642      title_id = IDS_CAMERA_BLOCKED;
643      break;
644    case TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED:
645      title_id = IDS_MICROPHONE_CAMERA_BLOCKED;
646      break;
647  }
648  set_title(l10n_util::GetStringUTF8(title_id));
649}
650
651void ContentSettingMediaStreamBubbleModel::SetRadioGroup() {
652  TabSpecificContentSettings* content_settings =
653      TabSpecificContentSettings::FromWebContents(web_contents());
654  GURL url = content_settings->media_stream_access_origin();
655  RadioGroup radio_group;
656  radio_group.url = url;
657
658  base::string16 display_host_utf16;
659  net::AppendFormattedHost(
660      url,
661      profile()->GetPrefs()->GetString(prefs::kAcceptLanguages),
662      &display_host_utf16);
663  std::string display_host(UTF16ToUTF8(display_host_utf16));
664  if (display_host.empty())
665    display_host = url.spec();
666
667  int radio_allow_label_id = 0;
668  int radio_block_label_id = 0;
669  switch (state_) {
670    case TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED:
671      NOTREACHED();
672      return;
673    case TabSpecificContentSettings::MICROPHONE_ACCESSED:
674      radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_NO_ACTION;
675      radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_BLOCK;
676      selected_item_ = 0;
677      break;
678    case TabSpecificContentSettings::CAMERA_ACCESSED:
679      radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION;
680      radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_BLOCK;
681      selected_item_ = 0;
682      break;
683    case TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED:
684      radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
685      radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_BLOCK;
686      selected_item_ = 0;
687      break;
688    case TabSpecificContentSettings::MICROPHONE_BLOCKED:
689      if (url.SchemeIsSecure()) {
690        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_ALLOW;
691        radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
692      } else {
693        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_ASK;
694      }
695
696      radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_NO_ACTION;
697      selected_item_ = 1;
698      break;
699    case TabSpecificContentSettings::CAMERA_BLOCKED:
700      if (url.SchemeIsSecure()) {
701        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ALLOW;
702        radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
703      } else {
704        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ASK;
705      }
706
707      radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_NO_ACTION;
708      selected_item_ = 1;
709      break;
710    case TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED:
711      if (url.SchemeIsSecure()) {
712        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ALLOW;
713        radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
714      } else {
715        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ASK;
716      }
717
718      radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
719      selected_item_ = 1;
720      break;
721  }
722
723  std::string radio_allow_label = l10n_util::GetStringFUTF8(
724      radio_allow_label_id, UTF8ToUTF16(display_host));
725  std::string radio_block_label =
726      l10n_util::GetStringUTF8(radio_block_label_id);
727
728  radio_group.default_item = selected_item_;
729  radio_group.radio_items.push_back(radio_allow_label);
730  radio_group.radio_items.push_back(radio_block_label);
731
732  set_radio_group(radio_group);
733  set_radio_group_enabled(true);
734}
735
736void ContentSettingMediaStreamBubbleModel::UpdateSettings(
737    ContentSetting setting) {
738  if (profile()) {
739    HostContentSettingsMap* content_settings =
740        profile()->GetHostContentSettingsMap();
741    TabSpecificContentSettings* tab_content_settings =
742        TabSpecificContentSettings::FromWebContents(web_contents());
743    // The same patterns must be used as in other places (e.g. the infobar) in
744    // order to override the existing rule. Otherwise a new rule is created.
745    // TODO(markusheintz): Extract to a helper so that there is only a single
746    // place to touch.
747    ContentSettingsPattern primary_pattern =
748        ContentSettingsPattern::FromURLNoWildcard(
749            tab_content_settings->media_stream_access_origin());
750    ContentSettingsPattern secondary_pattern =
751        ContentSettingsPattern::Wildcard();
752    if (state_ == TabSpecificContentSettings::MICROPHONE_ACCESSED ||
753        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
754        state_ == TabSpecificContentSettings::MICROPHONE_BLOCKED ||
755        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED) {
756      content_settings->SetContentSetting(
757          primary_pattern, secondary_pattern,
758          CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(), setting);
759    }
760    if (state_ == TabSpecificContentSettings::CAMERA_ACCESSED ||
761        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
762        state_ == TabSpecificContentSettings::CAMERA_BLOCKED ||
763        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED) {
764      content_settings->SetContentSetting(
765          primary_pattern, secondary_pattern,
766          CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(), setting);
767    }
768  }
769}
770
771void ContentSettingMediaStreamBubbleModel::UpdateDefaultDeviceForType(
772    content::MediaStreamType type,
773    const std::string& device) {
774  PrefService* prefs = profile()->GetPrefs();
775  if (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
776    prefs->SetString(prefs::kDefaultAudioCaptureDevice, device);
777  } else {
778    DCHECK_EQ(content::MEDIA_DEVICE_VIDEO_CAPTURE, type);
779    prefs->SetString(prefs::kDefaultVideoCaptureDevice, device);
780  }
781}
782
783void ContentSettingMediaStreamBubbleModel::SetMediaMenus() {
784  TabSpecificContentSettings* content_settings =
785      TabSpecificContentSettings::FromWebContents(web_contents());
786  const std::string& requested_microphone =
787       content_settings->media_stream_requested_audio_device();
788   const std::string& requested_camera =
789       content_settings->media_stream_requested_video_device();
790
791  // Add microphone menu.
792  PrefService* prefs = profile()->GetPrefs();
793  MediaCaptureDevicesDispatcher* dispatcher =
794      MediaCaptureDevicesDispatcher::GetInstance();
795  const content::MediaStreamDevices& microphones =
796      dispatcher->GetAudioCaptureDevices();
797
798  bool show_mic_menu =
799      (state_ == TabSpecificContentSettings::MICROPHONE_ACCESSED ||
800       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
801       state_ == TabSpecificContentSettings::MICROPHONE_BLOCKED ||
802       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED);
803  bool show_camera_menu =
804      (state_ == TabSpecificContentSettings::CAMERA_ACCESSED ||
805       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
806       state_ == TabSpecificContentSettings::CAMERA_BLOCKED ||
807       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED);
808  DCHECK(show_mic_menu || show_camera_menu);
809
810  if (show_mic_menu) {
811    MediaMenu mic_menu;
812    mic_menu.label = l10n_util::GetStringUTF8(IDS_MEDIA_SELECTED_MIC_LABEL);
813    if (!microphones.empty()) {
814      std::string preferred_mic;
815      if (requested_microphone.empty()) {
816        preferred_mic = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
817        mic_menu.disabled = false;
818      } else {
819        // Set the |disabled| to true in order to disable the device selection
820        // menu on the media settings bubble. This must be done if the website
821        // manages the microphone devices itself.
822        preferred_mic = requested_microphone;
823        mic_menu.disabled = true;
824      }
825
826      mic_menu.default_device = GetMediaDeviceById(preferred_mic, microphones);
827      mic_menu.selected_device = mic_menu.default_device;
828    }
829    add_media_menu(content::MEDIA_DEVICE_AUDIO_CAPTURE, mic_menu);
830  }
831
832  if (show_camera_menu) {
833    const content::MediaStreamDevices& cameras =
834        dispatcher->GetVideoCaptureDevices();
835    MediaMenu camera_menu;
836    camera_menu.label =
837        l10n_util::GetStringUTF8(IDS_MEDIA_SELECTED_CAMERA_LABEL);
838    if (!cameras.empty()) {
839      std::string preferred_camera;
840      if (requested_camera.empty()) {
841        preferred_camera = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
842        camera_menu.disabled = false;
843      } else {
844        // Disable the menu since the website is managing the camera devices
845        // itself.
846        preferred_camera = requested_camera;
847        camera_menu.disabled = true;
848      }
849
850      camera_menu.default_device =
851          GetMediaDeviceById(preferred_camera, cameras);
852      camera_menu.selected_device = camera_menu.default_device;
853    }
854    add_media_menu(content::MEDIA_DEVICE_VIDEO_CAPTURE, camera_menu);
855  }
856}
857
858void ContentSettingMediaStreamBubbleModel::OnRadioClicked(int radio_index) {
859  selected_item_ = radio_index;
860}
861
862void ContentSettingMediaStreamBubbleModel::OnMediaMenuClicked(
863    content::MediaStreamType type,
864    const std::string& selected_device_id) {
865  DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
866         type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
867  DCHECK_EQ(1U, bubble_content().media_menus.count(type));
868  MediaCaptureDevicesDispatcher* dispatcher =
869      MediaCaptureDevicesDispatcher::GetInstance();
870  const content::MediaStreamDevices& devices =
871      (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) ?
872          dispatcher->GetAudioCaptureDevices() :
873          dispatcher->GetVideoCaptureDevices();
874  set_selected_device(GetMediaDeviceById(selected_device_id, devices));
875}
876
877class ContentSettingDomainListBubbleModel
878    : public ContentSettingTitleAndLinkModel {
879 public:
880  ContentSettingDomainListBubbleModel(Delegate* delegate,
881                                      WebContents* web_contents,
882                                      Profile* profile,
883                                      ContentSettingsType content_type);
884  virtual ~ContentSettingDomainListBubbleModel() {}
885
886 private:
887  void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id);
888  void SetDomainsAndCustomLink();
889  virtual void OnCustomLinkClicked() OVERRIDE;
890};
891
892ContentSettingDomainListBubbleModel::ContentSettingDomainListBubbleModel(
893    Delegate* delegate,
894    WebContents* web_contents,
895    Profile* profile,
896    ContentSettingsType content_type)
897    : ContentSettingTitleAndLinkModel(
898        delegate, web_contents, profile, content_type) {
899  DCHECK_EQ(CONTENT_SETTINGS_TYPE_GEOLOCATION, content_type) <<
900      "SetDomains currently only supports geolocation content type";
901  SetDomainsAndCustomLink();
902}
903
904void ContentSettingDomainListBubbleModel::MaybeAddDomainList(
905    const std::set<std::string>& hosts, int title_id) {
906  if (!hosts.empty()) {
907    DomainList domain_list;
908    domain_list.title = l10n_util::GetStringUTF8(title_id);
909    domain_list.hosts = hosts;
910    add_domain_list(domain_list);
911  }
912}
913
914void ContentSettingDomainListBubbleModel::SetDomainsAndCustomLink() {
915  TabSpecificContentSettings* content_settings =
916      TabSpecificContentSettings::FromWebContents(web_contents());
917  const ContentSettingsUsagesState& usages =
918      content_settings->geolocation_usages_state();
919  ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
920  unsigned int tab_state_flags = 0;
921  usages.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags);
922  // Divide the tab's current geolocation users into sets according to their
923  // permission state.
924  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW],
925                     IDS_GEOLOCATION_BUBBLE_SECTION_ALLOWED);
926
927  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK],
928                     IDS_GEOLOCATION_BUBBLE_SECTION_DENIED);
929
930  if (tab_state_flags & ContentSettingsUsagesState::TABSTATE_HAS_EXCEPTION) {
931    set_custom_link(l10n_util::GetStringUTF8(
932        IDS_GEOLOCATION_BUBBLE_CLEAR_LINK));
933    set_custom_link_enabled(true);
934  } else if (tab_state_flags &
935             ContentSettingsUsagesState::TABSTATE_HAS_CHANGED) {
936    set_custom_link(l10n_util::GetStringUTF8(
937        IDS_GEOLOCATION_BUBBLE_REQUIRE_RELOAD_TO_CLEAR));
938  }
939}
940
941void ContentSettingDomainListBubbleModel::OnCustomLinkClicked() {
942  if (!web_contents())
943    return;
944  // Reset this embedder's entry to default for each of the requesting
945  // origins currently on the page.
946  const GURL& embedder_url = web_contents()->GetURL();
947  TabSpecificContentSettings* content_settings =
948      TabSpecificContentSettings::FromWebContents(web_contents());
949  const ContentSettingsUsagesState::StateMap& state_map =
950      content_settings->geolocation_usages_state().state_map();
951  HostContentSettingsMap* settings_map =
952      profile()->GetHostContentSettingsMap();
953
954  for (ContentSettingsUsagesState::StateMap::const_iterator it =
955       state_map.begin(); it != state_map.end(); ++it) {
956    settings_map->SetContentSetting(
957        ContentSettingsPattern::FromURLNoWildcard(it->first),
958        ContentSettingsPattern::FromURLNoWildcard(embedder_url),
959        CONTENT_SETTINGS_TYPE_GEOLOCATION,
960        std::string(),
961        CONTENT_SETTING_DEFAULT);
962  }
963}
964
965class ContentSettingMixedScriptBubbleModel
966    : public ContentSettingTitleLinkAndCustomModel {
967 public:
968  ContentSettingMixedScriptBubbleModel(Delegate* delegate,
969                                       WebContents* web_contents,
970                                       Profile* profile,
971                                       ContentSettingsType content_type);
972
973  virtual ~ContentSettingMixedScriptBubbleModel() {}
974
975 private:
976  virtual void OnCustomLinkClicked() OVERRIDE;
977};
978
979ContentSettingMixedScriptBubbleModel::ContentSettingMixedScriptBubbleModel(
980    Delegate* delegate,
981    WebContents* web_contents,
982    Profile* profile,
983    ContentSettingsType content_type)
984    : ContentSettingTitleLinkAndCustomModel(
985        delegate, web_contents, profile, content_type) {
986  DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_MIXEDSCRIPT);
987  set_custom_link_enabled(true);
988}
989
990void ContentSettingMixedScriptBubbleModel::OnCustomLinkClicked() {
991  content::RecordAction(UserMetricsAction("MixedScript_LoadAnyway_Bubble"));
992  DCHECK(web_contents());
993  content::RenderViewHost* host = web_contents()->GetRenderViewHost();
994  host->Send(new ChromeViewMsg_SetAllowRunningInsecureContent(
995      host->GetRoutingID(), true));
996}
997
998ContentSettingRPHBubbleModel::ContentSettingRPHBubbleModel(
999    Delegate* delegate,
1000    WebContents* web_contents,
1001    Profile* profile,
1002    ProtocolHandlerRegistry* registry,
1003    ContentSettingsType content_type)
1004    : ContentSettingTitleAndLinkModel(
1005          delegate, web_contents, profile, content_type),
1006      selected_item_(0),
1007      registry_(registry),
1008      pending_handler_(ProtocolHandler::EmptyProtocolHandler()),
1009      previous_handler_(ProtocolHandler::EmptyProtocolHandler()) {
1010
1011  DCHECK_EQ(CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, content_type);
1012
1013  TabSpecificContentSettings* content_settings =
1014      TabSpecificContentSettings::FromWebContents(web_contents);
1015  pending_handler_ = content_settings->pending_protocol_handler();
1016  previous_handler_ = content_settings->previous_protocol_handler();
1017
1018  base::string16 protocol;
1019  if (pending_handler_.protocol() == "mailto") {
1020    protocol = l10n_util::GetStringUTF16(
1021        IDS_REGISTER_PROTOCOL_HANDLER_MAILTO_NAME);
1022  } else if (pending_handler_.protocol() == "webcal") {
1023    protocol = l10n_util::GetStringUTF16(
1024        IDS_REGISTER_PROTOCOL_HANDLER_WEBCAL_NAME);
1025  } else {
1026    protocol = UTF8ToUTF16(pending_handler_.protocol());
1027  }
1028
1029  if (previous_handler_.IsEmpty()) {
1030    set_title(l10n_util::GetStringFUTF8(
1031        IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM,
1032        pending_handler_.title(), UTF8ToUTF16(pending_handler_.url().host()),
1033        protocol));
1034  } else {
1035    set_title(l10n_util::GetStringFUTF8(
1036        IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE,
1037        pending_handler_.title(), UTF8ToUTF16(pending_handler_.url().host()),
1038        protocol, previous_handler_.title()));
1039  }
1040
1041  std::string radio_allow_label =
1042      l10n_util::GetStringFUTF8(IDS_REGISTER_PROTOCOL_HANDLER_ACCEPT,
1043                                pending_handler_.title());
1044  std::string radio_deny_label =
1045      l10n_util::GetStringUTF8(IDS_REGISTER_PROTOCOL_HANDLER_DENY);
1046  std::string radio_ignore_label =
1047      l10n_util::GetStringUTF8(IDS_REGISTER_PROTOCOL_HANDLER_IGNORE);
1048
1049  GURL url = web_contents->GetURL();
1050  RadioGroup radio_group;
1051  radio_group.url = url;
1052
1053  radio_group.radio_items.push_back(radio_allow_label);
1054  radio_group.radio_items.push_back(radio_deny_label);
1055  radio_group.radio_items.push_back(radio_ignore_label);
1056  ContentSetting setting =
1057      content_settings->pending_protocol_handler_setting();
1058  if (setting == CONTENT_SETTING_ALLOW)
1059    radio_group.default_item = RPH_ALLOW;
1060  else if (setting == CONTENT_SETTING_BLOCK)
1061    radio_group.default_item = RPH_BLOCK;
1062  else
1063    radio_group.default_item = RPH_IGNORE;
1064
1065  selected_item_ = radio_group.default_item;
1066  set_radio_group_enabled(true);
1067  set_radio_group(radio_group);
1068}
1069
1070void ContentSettingRPHBubbleModel::OnRadioClicked(int radio_index) {
1071  if (selected_item_ == radio_index)
1072    return;
1073
1074  selected_item_ = radio_index;
1075
1076  if (radio_index == RPH_ALLOW)
1077    RegisterProtocolHandler();
1078  else if (radio_index == RPH_BLOCK)
1079    UnregisterProtocolHandler();
1080  else if (radio_index == RPH_IGNORE)
1081    IgnoreProtocolHandler();
1082  else
1083    NOTREACHED();
1084}
1085
1086void ContentSettingRPHBubbleModel::OnDoneClicked() {
1087  // The user has one chance to deal with the RPH content setting UI,
1088  // then we remove it.
1089  TabSpecificContentSettings::FromWebContents(web_contents())->
1090      ClearPendingProtocolHandler();
1091  content::NotificationService::current()->Notify(
1092      chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
1093      content::Source<WebContents>(web_contents()),
1094      content::NotificationService::NoDetails());
1095}
1096
1097void ContentSettingRPHBubbleModel::RegisterProtocolHandler() {
1098  // A no-op if the handler hasn't been ignored, but needed in case the user
1099  // selects sequences like register/ignore/register.
1100  registry_->RemoveIgnoredHandler(pending_handler_);
1101
1102  registry_->OnAcceptRegisterProtocolHandler(pending_handler_);
1103  TabSpecificContentSettings::FromWebContents(web_contents())->
1104      set_pending_protocol_handler_setting(CONTENT_SETTING_ALLOW);
1105}
1106
1107void ContentSettingRPHBubbleModel::UnregisterProtocolHandler() {
1108  registry_->OnDenyRegisterProtocolHandler(pending_handler_);
1109  TabSpecificContentSettings::FromWebContents(web_contents())->
1110      set_pending_protocol_handler_setting(CONTENT_SETTING_BLOCK);
1111  ClearOrSetPreviousHandler();
1112}
1113
1114void ContentSettingRPHBubbleModel::IgnoreProtocolHandler() {
1115  registry_->OnIgnoreRegisterProtocolHandler(pending_handler_);
1116  TabSpecificContentSettings::FromWebContents(web_contents())->
1117      set_pending_protocol_handler_setting(CONTENT_SETTING_DEFAULT);
1118  ClearOrSetPreviousHandler();
1119}
1120
1121void ContentSettingRPHBubbleModel::ClearOrSetPreviousHandler() {
1122  if (previous_handler_.IsEmpty()) {
1123    registry_->ClearDefault(pending_handler_.protocol());
1124  } else {
1125    registry_->OnAcceptRegisterProtocolHandler(previous_handler_);
1126  }
1127}
1128
1129// TODO(toyoshim): Should share as many code with geolocation as possible.
1130class ContentSettingMIDISysExBubbleModel
1131    : public ContentSettingTitleAndLinkModel {
1132 public:
1133  ContentSettingMIDISysExBubbleModel(Delegate* delegate,
1134                                     WebContents* web_contents,
1135                                     Profile* profile,
1136                                     ContentSettingsType content_type);
1137  virtual ~ContentSettingMIDISysExBubbleModel() {}
1138
1139 private:
1140  void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id);
1141  void SetDomainsAndCustomLink();
1142  virtual void OnCustomLinkClicked() OVERRIDE;
1143};
1144
1145ContentSettingMIDISysExBubbleModel::ContentSettingMIDISysExBubbleModel(
1146    Delegate* delegate,
1147    WebContents* web_contents,
1148    Profile* profile,
1149    ContentSettingsType content_type)
1150    : ContentSettingTitleAndLinkModel(
1151        delegate, web_contents, profile, content_type) {
1152  DCHECK_EQ(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, content_type);
1153  SetDomainsAndCustomLink();
1154}
1155
1156void ContentSettingMIDISysExBubbleModel::MaybeAddDomainList(
1157    const std::set<std::string>& hosts, int title_id) {
1158  if (!hosts.empty()) {
1159    DomainList domain_list;
1160    domain_list.title = l10n_util::GetStringUTF8(title_id);
1161    domain_list.hosts = hosts;
1162    add_domain_list(domain_list);
1163  }
1164}
1165
1166void ContentSettingMIDISysExBubbleModel::SetDomainsAndCustomLink() {
1167  TabSpecificContentSettings* content_settings =
1168      TabSpecificContentSettings::FromWebContents(web_contents());
1169  const ContentSettingsUsagesState& usages_state =
1170      content_settings->midi_usages_state();
1171  ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
1172  unsigned int tab_state_flags = 0;
1173  usages_state.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags);
1174  // Divide the tab's current MIDI sysex users into sets according to their
1175  // permission state.
1176  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW],
1177                     IDS_MIDI_SYSEX_BUBBLE_ALLOWED);
1178
1179  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK],
1180                     IDS_MIDI_SYSEX_BUBBLE_DENIED);
1181
1182  if (tab_state_flags & ContentSettingsUsagesState::TABSTATE_HAS_EXCEPTION) {
1183    set_custom_link(l10n_util::GetStringUTF8(
1184        IDS_MIDI_SYSEX_BUBBLE_CLEAR_LINK));
1185    set_custom_link_enabled(true);
1186  } else if (tab_state_flags &
1187             ContentSettingsUsagesState::TABSTATE_HAS_CHANGED) {
1188    set_custom_link(l10n_util::GetStringUTF8(
1189        IDS_MIDI_SYSEX_BUBBLE_REQUIRE_RELOAD_TO_CLEAR));
1190  }
1191}
1192
1193void ContentSettingMIDISysExBubbleModel::OnCustomLinkClicked() {
1194  if (!web_contents())
1195    return;
1196  // Reset this embedder's entry to default for each of the requesting
1197  // origins currently on the page.
1198  TabSpecificContentSettings* content_settings =
1199      TabSpecificContentSettings::FromWebContents(web_contents());
1200  const ContentSettingsUsagesState::StateMap& state_map =
1201      content_settings->midi_usages_state().state_map();
1202  HostContentSettingsMap* settings_map =
1203      profile()->GetHostContentSettingsMap();
1204
1205  for (ContentSettingsUsagesState::StateMap::const_iterator it =
1206       state_map.begin(); it != state_map.end(); ++it) {
1207    settings_map->SetContentSetting(
1208        ContentSettingsPattern::FromURLNoWildcard(it->first),
1209        ContentSettingsPattern::Wildcard(),
1210        CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
1211        std::string(),
1212        CONTENT_SETTING_DEFAULT);
1213  }
1214}
1215
1216// static
1217ContentSettingBubbleModel*
1218    ContentSettingBubbleModel::CreateContentSettingBubbleModel(
1219        Delegate* delegate,
1220        WebContents* web_contents,
1221        Profile* profile,
1222        ContentSettingsType content_type) {
1223  if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
1224    return new ContentSettingCookiesBubbleModel(delegate, web_contents, profile,
1225                                                content_type);
1226  }
1227  if (content_type == CONTENT_SETTINGS_TYPE_POPUPS) {
1228    return new ContentSettingPopupBubbleModel(delegate, web_contents, profile,
1229                                              content_type);
1230  }
1231  if (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) {
1232    return new ContentSettingDomainListBubbleModel(delegate, web_contents,
1233                                                   profile, content_type);
1234  }
1235  if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
1236    return new ContentSettingMediaStreamBubbleModel(delegate, web_contents,
1237                                                    profile);
1238  }
1239  if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
1240    return new ContentSettingPluginBubbleModel(delegate, web_contents, profile,
1241                                               content_type);
1242  }
1243  if (content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT) {
1244    return new ContentSettingMixedScriptBubbleModel(delegate, web_contents,
1245                                                    profile, content_type);
1246  }
1247  if (content_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) {
1248    ProtocolHandlerRegistry* registry =
1249        ProtocolHandlerRegistryFactory::GetForProfile(profile);
1250    return new ContentSettingRPHBubbleModel(delegate, web_contents, profile,
1251                                            registry, content_type);
1252  }
1253  if (content_type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
1254    return new ContentSettingMIDISysExBubbleModel(delegate, web_contents,
1255                                                  profile, content_type);
1256  }
1257  return new ContentSettingSingleRadioGroup(delegate, web_contents, profile,
1258                                            content_type);
1259}
1260
1261ContentSettingBubbleModel::ContentSettingBubbleModel(
1262    WebContents* web_contents,
1263    Profile* profile,
1264    ContentSettingsType content_type)
1265    : web_contents_(web_contents),
1266      profile_(profile),
1267      content_type_(content_type) {
1268  registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
1269                 content::Source<WebContents>(web_contents));
1270  registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
1271                 content::Source<Profile>(profile_));
1272}
1273
1274ContentSettingBubbleModel::~ContentSettingBubbleModel() {
1275}
1276
1277ContentSettingBubbleModel::RadioGroup::RadioGroup() : default_item(0) {}
1278
1279ContentSettingBubbleModel::RadioGroup::~RadioGroup() {}
1280
1281ContentSettingBubbleModel::DomainList::DomainList() {}
1282
1283ContentSettingBubbleModel::DomainList::~DomainList() {}
1284
1285ContentSettingBubbleModel::MediaMenu::MediaMenu() : disabled(false) {}
1286
1287ContentSettingBubbleModel::MediaMenu::~MediaMenu() {}
1288
1289ContentSettingBubbleModel::BubbleContent::BubbleContent()
1290    : radio_group_enabled(false),
1291      custom_link_enabled(false) {
1292}
1293
1294ContentSettingBubbleModel::BubbleContent::~BubbleContent() {}
1295
1296void ContentSettingBubbleModel::Observe(
1297    int type,
1298    const content::NotificationSource& source,
1299    const content::NotificationDetails& details) {
1300  if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
1301    DCHECK_EQ(web_contents_,
1302              content::Source<WebContents>(source).ptr());
1303    web_contents_ = NULL;
1304  } else {
1305    DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
1306    DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
1307    profile_ = NULL;
1308  }
1309}
1310