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