• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/content_setting_bubble_model.h"
6 
7 #include "base/command_line.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/blocked_content_container.h"
10 #include "chrome/browser/content_settings/host_content_settings_map.h"
11 #include "chrome/browser/geolocation/geolocation_content_settings_map.h"
12 #include "chrome/browser/metrics/user_metrics.h"
13 #include "chrome/browser/prefs/pref_service.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/tab_contents/tab_specific_content_settings.h"
16 #include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
17 #include "chrome/common/pref_names.h"
18 #include "content/browser/renderer_host/render_view_host.h"
19 #include "content/browser/tab_contents/tab_contents.h"
20 #include "content/browser/tab_contents/tab_contents_delegate.h"
21 #include "content/common/notification_service.h"
22 #include "grit/generated_resources.h"
23 #include "net/base/net_util.h"
24 #include "ui/base/l10n/l10n_util.h"
25 
26 class ContentSettingTitleAndLinkModel : public ContentSettingBubbleModel {
27  public:
ContentSettingTitleAndLinkModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)28   ContentSettingTitleAndLinkModel(TabContents* tab_contents,
29                                   Profile* profile,
30                                   ContentSettingsType content_type)
31       : ContentSettingBubbleModel(tab_contents, profile, content_type) {
32      // Notifications do not have a bubble.
33      DCHECK_NE(content_type, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
34      SetBlockedResources();
35      SetTitle();
36      SetManageLink();
37   }
38 
~ContentSettingTitleAndLinkModel()39   virtual ~ContentSettingTitleAndLinkModel() {}
40 
41  private:
SetBlockedResources()42   void SetBlockedResources() {
43     TabSpecificContentSettings* settings =
44         tab_contents()->GetTabSpecificContentSettings();
45     const std::set<std::string>& resources = settings->BlockedResourcesForType(
46         content_type());
47     for (std::set<std::string>::const_iterator it = resources.begin();
48         it != resources.end(); ++it) {
49       AddBlockedResource(*it);
50     }
51   }
52 
SetTitle()53   void SetTitle() {
54     static const int kBlockedTitleIDs[] = {
55       IDS_BLOCKED_COOKIES_TITLE,
56       IDS_BLOCKED_IMAGES_TITLE,
57       IDS_BLOCKED_JAVASCRIPT_TITLE,
58       IDS_BLOCKED_PLUGINS_MESSAGE,
59       IDS_BLOCKED_POPUPS_TITLE,
60       0,  // Geolocation does not have an overall title.
61       0,  // Notifications do not have a bubble.
62       0,  // Prerender does not have a bubble.
63     };
64     // Fields as for kBlockedTitleIDs, above.
65     static const int kResourceSpecificBlockedTitleIDs[] = {
66       0,
67       0,
68       0,
69       IDS_BLOCKED_PLUGINS_TITLE,
70       0,
71       0,
72       0,
73       0,
74     };
75     static const int kAccessedTitleIDs[] = {
76       IDS_ACCESSED_COOKIES_TITLE,
77       0,
78       0,
79       0,
80       0,
81       0,
82       0,
83       0,
84     };
85     COMPILE_ASSERT(arraysize(kAccessedTitleIDs) == CONTENT_SETTINGS_NUM_TYPES,
86                    Need_a_setting_for_every_content_settings_type);
87     COMPILE_ASSERT(arraysize(kBlockedTitleIDs) == CONTENT_SETTINGS_NUM_TYPES,
88                    Need_a_setting_for_every_content_settings_type);
89     COMPILE_ASSERT(arraysize(kResourceSpecificBlockedTitleIDs) ==
90         CONTENT_SETTINGS_NUM_TYPES,
91         Need_a_setting_for_every_content_settings_type);
92     const int *title_ids = kBlockedTitleIDs;
93     if (tab_contents() &&
94         tab_contents()->GetTabSpecificContentSettings()->IsContentAccessed(
95             content_type()) &&
96         !tab_contents()->GetTabSpecificContentSettings()->IsContentBlocked(
97             content_type())) {
98       title_ids = kAccessedTitleIDs;
99     } else if (!bubble_content().resource_identifiers.empty()) {
100       title_ids = kResourceSpecificBlockedTitleIDs;
101     }
102     if (title_ids[content_type()])
103       set_title(l10n_util::GetStringUTF8(title_ids[content_type()]));
104   }
105 
SetManageLink()106   void SetManageLink() {
107     static const int kLinkIDs[] = {
108       IDS_BLOCKED_COOKIES_LINK,
109       IDS_BLOCKED_IMAGES_LINK,
110       IDS_BLOCKED_JAVASCRIPT_LINK,
111       IDS_BLOCKED_PLUGINS_LINK,
112       IDS_BLOCKED_POPUPS_LINK,
113       IDS_GEOLOCATION_BUBBLE_MANAGE_LINK,
114       0,  // Notifications do not have a bubble.
115       0,  // Prerender does not have a bubble.
116     };
117     COMPILE_ASSERT(arraysize(kLinkIDs) == CONTENT_SETTINGS_NUM_TYPES,
118                    Need_a_setting_for_every_content_settings_type);
119     set_manage_link(l10n_util::GetStringUTF8(kLinkIDs[content_type()]));
120   }
121 
OnManageLinkClicked()122   virtual void OnManageLinkClicked() {
123     if (tab_contents())
124       tab_contents()->delegate()->ShowContentSettingsPage(content_type());
125   }
126 };
127 
128 class ContentSettingTitleLinkAndCustomModel
129     : public ContentSettingTitleAndLinkModel {
130  public:
ContentSettingTitleLinkAndCustomModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)131   ContentSettingTitleLinkAndCustomModel(TabContents* tab_contents,
132                                         Profile* profile,
133                                         ContentSettingsType content_type)
134       : ContentSettingTitleAndLinkModel(tab_contents, profile, content_type) {
135     SetCustomLink();
136   }
137 
~ContentSettingTitleLinkAndCustomModel()138   virtual ~ContentSettingTitleLinkAndCustomModel() {}
139 
140  private:
SetCustomLink()141   void SetCustomLink() {
142     static const int kCustomIDs[] = {
143       IDS_BLOCKED_COOKIES_INFO,
144       0,  // Images do not have a custom link.
145       0,  // Javascript doesn't have a custom link.
146       IDS_BLOCKED_PLUGINS_LOAD_ALL,
147       0,  // Popups do not have a custom link.
148       0,  // Geolocation custom links are set within that class.
149       0,  // Notifications do not have a bubble.
150       0,  // Prerender does not have a bubble.
151     };
152     COMPILE_ASSERT(arraysize(kCustomIDs) == CONTENT_SETTINGS_NUM_TYPES,
153                    Need_a_setting_for_every_content_settings_type);
154     if (kCustomIDs[content_type()])
155       set_custom_link(l10n_util::GetStringUTF8(kCustomIDs[content_type()]));
156   }
157 
OnCustomLinkClicked()158   virtual void OnCustomLinkClicked() {}
159 };
160 
161 
162 class ContentSettingSingleRadioGroup
163     : public ContentSettingTitleLinkAndCustomModel {
164  public:
ContentSettingSingleRadioGroup(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)165   ContentSettingSingleRadioGroup(TabContents* tab_contents,
166                                  Profile* profile,
167                                  ContentSettingsType content_type)
168       : ContentSettingTitleLinkAndCustomModel(tab_contents, profile,
169                                               content_type),
170         block_setting_(CONTENT_SETTING_BLOCK),
171         selected_item_(0) {
172     SetRadioGroup();
173   }
174 
~ContentSettingSingleRadioGroup()175   virtual ~ContentSettingSingleRadioGroup() {
176     if (settings_changed()) {
177       ContentSetting setting =
178           selected_item_ == 0 ? CONTENT_SETTING_ALLOW : block_setting_;
179       const std::set<std::string>& resources =
180           bubble_content().resource_identifiers;
181       if (resources.empty()) {
182         AddException(setting, std::string());
183       } else {
184         for (std::set<std::string>::const_iterator it = resources.begin();
185              it != resources.end(); ++it) {
186           AddException(setting, *it);
187         }
188       }
189     }
190   }
191 
192  protected:
settings_changed() const193   bool settings_changed() const {
194     return selected_item_ != bubble_content().radio_group.default_item;
195   }
196 
197  private:
198   ContentSetting block_setting_;
199   int selected_item_;
200 
201   // Initialize the radio group by setting the appropriate labels for the
202   // content type and setting the default value based on the content setting.
SetRadioGroup()203   void SetRadioGroup() {
204     GURL url = tab_contents()->GetURL();
205     std::wstring display_host_wide;
206     net::AppendFormattedHost(url,
207         UTF8ToWide(profile()->GetPrefs()->GetString(prefs::kAcceptLanguages)),
208         &display_host_wide, NULL, NULL);
209     std::string display_host(WideToUTF8(display_host_wide));
210 
211     if (display_host.empty())
212       display_host = url.spec();
213 
214     const std::set<std::string>& resources =
215         bubble_content().resource_identifiers;
216 
217     RadioGroup radio_group;
218     radio_group.url = url;
219 
220     static const int kAllowIDs[] = {
221       IDS_BLOCKED_COOKIES_UNBLOCK,
222       IDS_BLOCKED_IMAGES_UNBLOCK,
223       IDS_BLOCKED_JAVASCRIPT_UNBLOCK,
224       IDS_BLOCKED_PLUGINS_UNBLOCK_ALL,
225       IDS_BLOCKED_POPUPS_UNBLOCK,
226       0,  // We don't manage geolocation here.
227       0,  // Notifications do not have a bubble.
228       0,  // Prerender does not have a bubble.
229     };
230     COMPILE_ASSERT(arraysize(kAllowIDs) == CONTENT_SETTINGS_NUM_TYPES,
231                    Need_a_setting_for_every_content_settings_type);
232      // Fields as for kAllowIDs, above.
233     static const int kResourceSpecificAllowIDs[] = {
234       0,
235       0,
236       0,
237       IDS_BLOCKED_PLUGINS_UNBLOCK,
238       0,
239       0,
240       0,
241       0,  // Prerender does not have a bubble.
242     };
243     COMPILE_ASSERT(
244         arraysize(kResourceSpecificAllowIDs) == CONTENT_SETTINGS_NUM_TYPES,
245         Need_a_setting_for_every_content_settings_type);
246     std::string radio_allow_label;
247     const int* allowIDs = resources.empty() ?
248         kAllowIDs : kResourceSpecificAllowIDs;
249     radio_allow_label = l10n_util::GetStringFUTF8(
250         allowIDs[content_type()], UTF8ToUTF16(display_host));
251 
252     static const int kBlockIDs[] = {
253       IDS_BLOCKED_COOKIES_NO_ACTION,
254       IDS_BLOCKED_IMAGES_NO_ACTION,
255       IDS_BLOCKED_JAVASCRIPT_NO_ACTION,
256       IDS_BLOCKED_PLUGINS_NO_ACTION,
257       IDS_BLOCKED_POPUPS_NO_ACTION,
258       0,  // We don't manage geolocation here.
259       0,  // Notifications do not have a bubble.
260       0,  // Prerender does not have a bubble.
261     };
262     COMPILE_ASSERT(arraysize(kBlockIDs) == CONTENT_SETTINGS_NUM_TYPES,
263                    Need_a_setting_for_every_content_settings_type);
264     std::string radio_block_label;
265     radio_block_label = l10n_util::GetStringUTF8(kBlockIDs[content_type()]);
266 
267     radio_group.radio_items.push_back(radio_allow_label);
268     radio_group.radio_items.push_back(radio_block_label);
269     HostContentSettingsMap* map = profile()->GetHostContentSettingsMap();
270     ContentSetting mostRestrictiveSetting;
271     if (resources.empty()) {
272       mostRestrictiveSetting =
273           map->GetContentSetting(url, content_type(), std::string());
274     } else {
275       mostRestrictiveSetting = CONTENT_SETTING_ALLOW;
276       for (std::set<std::string>::const_iterator it = resources.begin();
277            it != resources.end(); ++it) {
278         ContentSetting setting = map->GetContentSetting(url,
279                                                         content_type(),
280                                                         *it);
281         if (setting == CONTENT_SETTING_BLOCK) {
282           mostRestrictiveSetting = CONTENT_SETTING_BLOCK;
283           break;
284         }
285         if (setting == CONTENT_SETTING_ASK)
286           mostRestrictiveSetting = CONTENT_SETTING_ASK;
287       }
288     }
289     if (mostRestrictiveSetting == CONTENT_SETTING_ALLOW) {
290       radio_group.default_item = 0;
291       // |block_setting_| is already set to |CONTENT_SETTING_BLOCK|.
292     } else {
293       radio_group.default_item = 1;
294       block_setting_ = mostRestrictiveSetting;
295     }
296     selected_item_ = radio_group.default_item;
297     set_radio_group(radio_group);
298   }
299 
AddException(ContentSetting setting,const std::string & resource_identifier)300   void AddException(ContentSetting setting,
301                     const std::string& resource_identifier) {
302     profile()->GetHostContentSettingsMap()->AddExceptionForURL(
303         bubble_content().radio_group.url, content_type(), resource_identifier,
304         setting);
305   }
306 
OnRadioClicked(int radio_index)307   virtual void OnRadioClicked(int radio_index) {
308     selected_item_ = radio_index;
309   }
310 };
311 
312 class ContentSettingCookiesBubbleModel : public ContentSettingSingleRadioGroup {
313  public:
ContentSettingCookiesBubbleModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)314   ContentSettingCookiesBubbleModel(TabContents* tab_contents,
315                                    Profile* profile,
316                                    ContentSettingsType content_type)
317       : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) {
318     DCHECK_EQ(CONTENT_SETTINGS_TYPE_COOKIES, content_type);
319     set_custom_link_enabled(true);
320   }
321 
~ContentSettingCookiesBubbleModel()322   virtual ~ContentSettingCookiesBubbleModel() {
323     if (settings_changed()) {
324       tab_contents()->AddInfoBar(
325           new CollectedCookiesInfoBarDelegate(tab_contents()));
326     }
327   }
328 
329  private:
OnCustomLinkClicked()330   virtual void OnCustomLinkClicked() OVERRIDE {
331     if (tab_contents()) {
332       NotificationService::current()->Notify(
333           NotificationType::COLLECTED_COOKIES_SHOWN,
334           Source<TabSpecificContentSettings>(
335               tab_contents()->GetTabSpecificContentSettings()),
336           NotificationService::NoDetails());
337       tab_contents()->delegate()->ShowCollectedCookiesDialog(tab_contents());
338     }
339   }
340 };
341 
342 class ContentSettingPluginBubbleModel : public ContentSettingSingleRadioGroup {
343  public:
ContentSettingPluginBubbleModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)344   ContentSettingPluginBubbleModel(TabContents* tab_contents,
345                                   Profile* profile,
346                                   ContentSettingsType content_type)
347       : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) {
348     DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_PLUGINS);
349     set_custom_link_enabled(tab_contents && tab_contents->
350         GetTabSpecificContentSettings()->load_plugins_link_enabled());
351   }
352 
~ContentSettingPluginBubbleModel()353   virtual ~ContentSettingPluginBubbleModel() {}
354 
355  private:
OnCustomLinkClicked()356   virtual void OnCustomLinkClicked() OVERRIDE {
357     UserMetrics::RecordAction(UserMetricsAction("ClickToPlay_LoadAll_Bubble"));
358     DCHECK(tab_contents());
359     tab_contents()->render_view_host()->LoadBlockedPlugins();
360     set_custom_link_enabled(false);
361     tab_contents()->GetTabSpecificContentSettings()->
362         set_load_plugins_link_enabled(false);
363   }
364 };
365 
366 class ContentSettingPopupBubbleModel : public ContentSettingSingleRadioGroup {
367  public:
ContentSettingPopupBubbleModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)368   ContentSettingPopupBubbleModel(TabContents* tab_contents,
369                                  Profile* profile,
370                                  ContentSettingsType content_type)
371       : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) {
372     SetPopups();
373   }
374 
~ContentSettingPopupBubbleModel()375   virtual ~ContentSettingPopupBubbleModel() {}
376 
377  private:
SetPopups()378   void SetPopups() {
379     // check for crbug.com/53176
380     if (!tab_contents()->blocked_content_container())
381       return;
382     std::vector<TabContents*> blocked_contents;
383     tab_contents()->blocked_content_container()->GetBlockedContents(
384         &blocked_contents);
385     for (std::vector<TabContents*>::const_iterator
386          i(blocked_contents.begin()); i != blocked_contents.end(); ++i) {
387       std::string title(UTF16ToUTF8((*i)->GetTitle()));
388       // The popup may not have committed a load yet, in which case it won't
389       // have a URL or title.
390       if (title.empty())
391         title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
392       PopupItem popup_item;
393       popup_item.title = title;
394       popup_item.bitmap = (*i)->GetFavicon();
395       popup_item.tab_contents = (*i);
396       add_popup(popup_item);
397     }
398   }
399 
OnPopupClicked(int index)400   virtual void OnPopupClicked(int index) {
401     if (tab_contents() && tab_contents()->blocked_content_container()) {
402       tab_contents()->blocked_content_container()->LaunchForContents(
403           bubble_content().popup_items[index].tab_contents);
404     }
405   }
406 };
407 
408 class ContentSettingDomainListBubbleModel
409     : public ContentSettingTitleAndLinkModel {
410  public:
ContentSettingDomainListBubbleModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)411   ContentSettingDomainListBubbleModel(TabContents* tab_contents,
412                                       Profile* profile,
413                                       ContentSettingsType content_type)
414       : ContentSettingTitleAndLinkModel(tab_contents, profile, content_type) {
415     DCHECK_EQ(CONTENT_SETTINGS_TYPE_GEOLOCATION, content_type) <<
416         "SetDomains currently only supports geolocation content type";
417     SetDomainsAndCustomLink();
418   }
419 
~ContentSettingDomainListBubbleModel()420   virtual ~ContentSettingDomainListBubbleModel() {}
421 
422  private:
MaybeAddDomainList(const std::set<std::string> & hosts,int title_id)423   void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id) {
424     if (!hosts.empty()) {
425       DomainList domain_list;
426       domain_list.title = l10n_util::GetStringUTF8(title_id);
427       domain_list.hosts = hosts;
428       add_domain_list(domain_list);
429     }
430   }
SetDomainsAndCustomLink()431   void SetDomainsAndCustomLink() {
432     TabSpecificContentSettings* content_settings =
433         tab_contents()->GetTabSpecificContentSettings();
434     const GeolocationSettingsState& settings =
435         content_settings->geolocation_settings_state();
436     GeolocationSettingsState::FormattedHostsPerState formatted_hosts_per_state;
437     unsigned int tab_state_flags = 0;
438     settings.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags);
439     // Divide the tab's current geolocation users into sets according to their
440     // permission state.
441     MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW],
442                        IDS_GEOLOCATION_BUBBLE_SECTION_ALLOWED);
443 
444     MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK],
445                        IDS_GEOLOCATION_BUBBLE_SECTION_DENIED);
446 
447     if (tab_state_flags & GeolocationSettingsState::TABSTATE_HAS_EXCEPTION) {
448       set_custom_link(l10n_util::GetStringUTF8(
449                       IDS_GEOLOCATION_BUBBLE_CLEAR_LINK));
450       set_custom_link_enabled(true);
451     } else if (tab_state_flags &
452                GeolocationSettingsState::TABSTATE_HAS_CHANGED) {
453       set_custom_link(l10n_util::GetStringUTF8(
454                       IDS_GEOLOCATION_BUBBLE_REQUIRE_RELOAD_TO_CLEAR));
455     }
456   }
OnCustomLinkClicked()457   virtual void OnCustomLinkClicked() OVERRIDE {
458     if (!tab_contents())
459       return;
460     // Reset this embedder's entry to default for each of the requesting
461     // origins currently on the page.
462     const GURL& embedder_url = tab_contents()->GetURL();
463     TabSpecificContentSettings* content_settings =
464         tab_contents()->GetTabSpecificContentSettings();
465     const GeolocationSettingsState::StateMap& state_map =
466         content_settings->geolocation_settings_state().state_map();
467     GeolocationContentSettingsMap* settings_map =
468         profile()->GetGeolocationContentSettingsMap();
469     for (GeolocationSettingsState::StateMap::const_iterator it =
470          state_map.begin(); it != state_map.end(); ++it) {
471       settings_map->SetContentSetting(it->first, embedder_url,
472                                       CONTENT_SETTING_DEFAULT);
473     }
474   }
475 };
476 
477 // static
478 ContentSettingBubbleModel*
CreateContentSettingBubbleModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)479     ContentSettingBubbleModel::CreateContentSettingBubbleModel(
480         TabContents* tab_contents,
481         Profile* profile,
482         ContentSettingsType content_type) {
483   if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
484     return new ContentSettingCookiesBubbleModel(tab_contents, profile,
485                                                 content_type);
486   }
487   if (content_type == CONTENT_SETTINGS_TYPE_POPUPS) {
488     return new ContentSettingPopupBubbleModel(tab_contents, profile,
489                                               content_type);
490   }
491   if (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) {
492     return new ContentSettingDomainListBubbleModel(tab_contents, profile,
493                                                    content_type);
494   }
495   if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
496     return new ContentSettingPluginBubbleModel(tab_contents, profile,
497                                                content_type);
498   }
499   return new ContentSettingSingleRadioGroup(tab_contents, profile,
500                                             content_type);
501 }
502 
ContentSettingBubbleModel(TabContents * tab_contents,Profile * profile,ContentSettingsType content_type)503 ContentSettingBubbleModel::ContentSettingBubbleModel(
504     TabContents* tab_contents,
505     Profile* profile,
506     ContentSettingsType content_type)
507     : tab_contents_(tab_contents),
508       profile_(profile),
509       content_type_(content_type) {
510   registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
511                  Source<TabContents>(tab_contents));
512 }
513 
~ContentSettingBubbleModel()514 ContentSettingBubbleModel::~ContentSettingBubbleModel() {
515 }
516 
RadioGroup()517 ContentSettingBubbleModel::RadioGroup::RadioGroup() : default_item(0) {}
518 
~RadioGroup()519 ContentSettingBubbleModel::RadioGroup::~RadioGroup() {}
520 
DomainList()521 ContentSettingBubbleModel::DomainList::DomainList() {}
522 
~DomainList()523 ContentSettingBubbleModel::DomainList::~DomainList() {}
524 
BubbleContent()525 ContentSettingBubbleModel::BubbleContent::BubbleContent()
526     : custom_link_enabled(false) {
527 }
528 
~BubbleContent()529 ContentSettingBubbleModel::BubbleContent::~BubbleContent() {}
530 
531 
AddBlockedResource(const std::string & resource_identifier)532 void ContentSettingBubbleModel::AddBlockedResource(
533     const std::string& resource_identifier) {
534   bubble_content_.resource_identifiers.insert(resource_identifier);
535 }
536 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)537 void ContentSettingBubbleModel::Observe(NotificationType type,
538                                         const NotificationSource& source,
539                                         const NotificationDetails& details) {
540   DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
541   DCHECK(source == Source<TabContents>(tab_contents_));
542   tab_contents_ = NULL;
543 }
544