infobar_service.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2013 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/infobars/infobar_service.h"
6
7#include "chrome/browser/chrome_notification_types.h"
8#include "chrome/browser/infobars/infobar.h"
9#include "chrome/browser/infobars/infobar_delegate.h"
10#include "chrome/browser/infobars/insecure_content_infobar_delegate.h"
11#include "chrome/common/render_messages.h"
12#include "content/public/browser/navigation_controller.h"
13#include "content/public/browser/notification_service.h"
14#include "content/public/browser/web_contents.h"
15
16
17DEFINE_WEB_CONTENTS_USER_DATA_KEY(InfoBarService);
18
19InfoBar* InfoBarService::AddInfoBar(scoped_ptr<InfoBar> infobar) {
20  DCHECK(infobar);
21  if (!infobars_enabled_)
22    return NULL;
23
24  for (InfoBars::const_iterator i(infobars_.begin()); i != infobars_.end();
25       ++i) {
26    if ((*i)->delegate()->EqualsDelegate(infobar->delegate())) {
27      DCHECK_NE((*i)->delegate(), infobar->delegate());
28      return NULL;
29    }
30  }
31
32  InfoBar* infobar_ptr = infobar.release();
33  infobars_.push_back(infobar_ptr);
34  infobar_ptr->SetOwner(this);
35
36  content::NotificationService::current()->Notify(
37      chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
38      content::Source<InfoBarService>(this),
39      content::Details<InfoBar::AddedDetails>(infobar_ptr));
40  return infobar_ptr;
41}
42
43void InfoBarService::RemoveInfoBar(InfoBar* infobar) {
44  RemoveInfoBarInternal(infobar, true);
45}
46
47InfoBar* InfoBarService::ReplaceInfoBar(InfoBar* old_infobar,
48                                        scoped_ptr<InfoBar> new_infobar) {
49  DCHECK(old_infobar);
50  if (!infobars_enabled_)
51    return AddInfoBar(new_infobar.Pass());  // Deletes the infobar.
52  DCHECK(new_infobar);
53
54  InfoBars::iterator i(std::find(infobars_.begin(), infobars_.end(),
55                                 old_infobar));
56  DCHECK(i != infobars_.end());
57
58  InfoBar* new_infobar_ptr = new_infobar.release();
59  i = infobars_.insert(i, new_infobar_ptr);
60  new_infobar_ptr->SetOwner(this);
61  InfoBar::ReplacedDetails replaced_details(old_infobar, new_infobar_ptr);
62
63  // Remove the old infobar before notifying, so that if any observers call back
64  // to AddInfoBar() or similar, we don't dupe-check against this infobar.
65  infobars_.erase(++i);
66
67  content::NotificationService::current()->Notify(
68      chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED,
69      content::Source<InfoBarService>(this),
70      content::Details<InfoBar::ReplacedDetails>(&replaced_details));
71
72  old_infobar->CloseSoon();
73  return new_infobar_ptr;
74}
75
76InfoBarService::InfoBarService(content::WebContents* web_contents)
77    : content::WebContentsObserver(web_contents),
78      infobars_enabled_(true) {
79  DCHECK(web_contents);
80}
81
82InfoBarService::~InfoBarService() {
83  // Destroy all remaining InfoBars.  It's important to not animate here so that
84  // we guarantee that we'll delete all delegates before we do anything else.
85  RemoveAllInfoBars(false);
86}
87
88void InfoBarService::RenderProcessGone(base::TerminationStatus status) {
89  RemoveAllInfoBars(true);
90}
91
92void InfoBarService::NavigationEntryCommitted(
93    const content::LoadCommittedDetails& load_details) {
94  // NOTE: It is not safe to change the following code to count upwards or
95  // use iterators, as the RemoveInfoBar() call synchronously modifies our
96  // delegate list.
97  for (size_t i = infobars_.size(); i > 0; --i) {
98    InfoBar* infobar = infobars_[i - 1];
99    if (infobar->delegate()->ShouldExpire(load_details))
100      RemoveInfoBar(infobar);
101  }
102}
103
104void InfoBarService::WebContentsDestroyed(content::WebContents* web_contents) {
105  // The WebContents is going away; be aggressively paranoid and delete
106  // ourselves lest other parts of the system attempt to add infobars or use
107  // us otherwise during the destruction.
108  web_contents->RemoveUserData(UserDataKey());
109  // That was the equivalent of "delete this". This object is now destroyed;
110  // returning from this function is the only safe thing to do.
111}
112
113bool InfoBarService::OnMessageReceived(const IPC::Message& message) {
114  bool handled = true;
115  IPC_BEGIN_MESSAGE_MAP(InfoBarService, message)
116    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidBlockDisplayingInsecureContent,
117                        OnDidBlockDisplayingInsecureContent)
118    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidBlockRunningInsecureContent,
119                        OnDidBlockRunningInsecureContent)
120    IPC_MESSAGE_UNHANDLED(handled = false)
121  IPC_END_MESSAGE_MAP()
122  return handled;
123}
124
125void InfoBarService::RemoveInfoBarInternal(InfoBar* infobar, bool animate) {
126  DCHECK(infobar);
127  if (!infobars_enabled_) {
128    DCHECK(infobars_.empty());
129    return;
130  }
131
132  InfoBars::iterator i(std::find(infobars_.begin(), infobars_.end(), infobar));
133  DCHECK(i != infobars_.end());
134
135  // Remove the infobar before notifying, so that if any observers call back to
136  // AddInfoBar() or similar, we don't dupe-check against this infobar.
137  infobars_.erase(i);
138
139  // This notification must happen before the call to CloseSoon() below, since
140  // observers may want to access |infobar| and that call can delete it.
141  InfoBar::RemovedDetails removed_details(infobar, animate);
142  content::NotificationService::current()->Notify(
143      chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
144      content::Source<InfoBarService>(this),
145      content::Details<InfoBar::RemovedDetails>(&removed_details));
146
147  infobar->CloseSoon();
148}
149
150void InfoBarService::RemoveAllInfoBars(bool animate) {
151  while (!infobars_.empty())
152    RemoveInfoBarInternal(infobars_.back(), animate);
153}
154
155void InfoBarService::OnDidBlockDisplayingInsecureContent() {
156  InsecureContentInfoBarDelegate::Create(
157      this, InsecureContentInfoBarDelegate::DISPLAY);
158}
159
160void InfoBarService::OnDidBlockRunningInsecureContent() {
161  InsecureContentInfoBarDelegate::Create(this,
162                                         InsecureContentInfoBarDelegate::RUN);
163}
164