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.
5 #include "chrome/browser/infobars/infobar_tab_helper.h"
7 #include "chrome/browser/infobars/infobar.h"
8 #include "chrome/browser/infobars/infobar_delegate.h"
9 #include "chrome/browser/infobars/insecure_content_infobar_delegate.h"
10 #include "chrome/common/chrome_notification_types.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"
16 using content::NavigationController
;
17 using content::WebContents
;
19 DEFINE_WEB_CONTENTS_USER_DATA_KEY(InfoBarTabHelper
);
21 void InfoBarService::CreateForWebContents(content::WebContents
* web_contents
) {
22 return content::WebContentsUserData
<InfoBarTabHelper
>::CreateForWebContents(
26 InfoBarService
* InfoBarService::FromWebContents(WebContents
* web_contents
) {
27 return content::WebContentsUserData
<InfoBarTabHelper
>::FromWebContents(
31 const InfoBarService
* InfoBarService::FromWebContents(
32 const WebContents
* web_contents
) {
33 return content::WebContentsUserData
<InfoBarTabHelper
>::FromWebContents(
37 InfoBarService::~InfoBarService() {
40 InfoBarTabHelper::InfoBarTabHelper(WebContents
* web_contents
)
41 : content::WebContentsObserver(web_contents
),
42 infobars_enabled_(true) {
45 content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
46 content::Source
<WebContents
>(web_contents
));
49 InfoBarTabHelper::~InfoBarTabHelper() {
50 // Destroy all remaining InfoBars. It's important to not animate here so that
51 // we guarantee that we'll delete all delegates before we do anything else.
53 // TODO(pkasting): If there is no InfoBarContainer, this leaks all the
54 // InfoBarDelegates. This will be fixed once we call CloseSoon() directly on
56 RemoveAllInfoBars(false);
59 void InfoBarTabHelper::SetInfoBarsEnabled(bool enabled
) {
60 infobars_enabled_
= enabled
;
63 InfoBarDelegate
* InfoBarTabHelper::AddInfoBar(
64 scoped_ptr
<InfoBarDelegate
> delegate
) {
65 if (!infobars_enabled_
)
68 for (InfoBars::const_iterator
i(infobars_
.begin()); i
!= infobars_
.end();
70 if ((*i
)->EqualsDelegate(delegate
.get())) {
71 DCHECK_NE(*i
, delegate
.get());
76 // TODO(pkasting): Consider removing InfoBarTabHelper arg from delegate
77 // constructors and instead using a setter from here.
78 InfoBarDelegate
* delegate_ptr
= delegate
.release();
79 infobars_
.push_back(delegate_ptr
);
80 // Add ourselves as an observer for navigations the first time a delegate is
81 // added. We use this notification to expire InfoBars that need to expire on
83 if (infobars_
.size() == 1) {
85 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
86 content::Source
<NavigationController
>(
87 &web_contents()->GetController()));
90 content::NotificationService::current()->Notify(
91 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED
,
92 content::Source
<InfoBarService
>(this),
93 content::Details
<InfoBarAddedDetails
>(delegate_ptr
));
97 void InfoBarTabHelper::RemoveInfoBar(InfoBarDelegate
* delegate
) {
98 RemoveInfoBarInternal(delegate
, true);
101 InfoBarDelegate
* InfoBarTabHelper::ReplaceInfoBar(
102 InfoBarDelegate
* old_delegate
,
103 scoped_ptr
<InfoBarDelegate
> new_delegate
) {
104 if (!infobars_enabled_
)
105 return AddInfoBar(new_delegate
.Pass()); // Deletes the delegate.
107 InfoBars::iterator
i(std::find(infobars_
.begin(), infobars_
.end(),
109 DCHECK(i
!= infobars_
.end());
111 InfoBarDelegate
* new_delegate_ptr
= new_delegate
.release();
112 i
= infobars_
.insert(i
, new_delegate_ptr
);
113 InfoBarReplacedDetails
replaced_details(old_delegate
, new_delegate_ptr
);
114 // Remove the old delegate before notifying, so that if any observers call
115 // back to AddInfoBar() or similar, we don't dupe-check against this delegate.
116 infobars_
.erase(++i
);
118 old_delegate
->clear_owner();
119 content::NotificationService::current()->Notify(
120 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED
,
121 content::Source
<InfoBarService
>(this),
122 content::Details
<InfoBarReplacedDetails
>(&replaced_details
));
123 return new_delegate_ptr
;
126 size_t InfoBarTabHelper::GetInfoBarCount() const {
127 return infobars_
.size();
130 InfoBarDelegate
* InfoBarTabHelper::GetInfoBarDelegateAt(size_t index
) {
131 return infobars_
[index
];
134 content::WebContents
* InfoBarTabHelper::GetWebContents() {
135 return content::WebContentsObserver::web_contents();
138 void InfoBarTabHelper::RemoveInfoBarInternal(InfoBarDelegate
* delegate
,
140 if (!infobars_enabled_
) {
141 DCHECK(infobars_
.empty());
145 InfoBars::iterator
i(std::find(infobars_
.begin(), infobars_
.end(), delegate
));
146 DCHECK(i
!= infobars_
.end());
148 delegate
->clear_owner();
149 // Remove the delegate before notifying, so that if any observers call back to
150 // AddInfoBar() or similar, we don't dupe-check against this delegate.
152 // Remove ourselves as an observer if we are tracking no more InfoBars.
153 if (infobars_
.empty()) {
155 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
156 content::Source
<NavigationController
>(
157 &web_contents()->GetController()));
160 InfoBarRemovedDetails
removed_details(delegate
, animate
);
161 content::NotificationService::current()->Notify(
162 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED
,
163 content::Source
<InfoBarService
>(this),
164 content::Details
<InfoBarRemovedDetails
>(&removed_details
));
167 void InfoBarTabHelper::RemoveAllInfoBars(bool animate
) {
168 while (!infobars_
.empty())
169 RemoveInfoBarInternal(GetInfoBarDelegateAt(GetInfoBarCount() - 1), animate
);
172 void InfoBarTabHelper::OnDidBlockDisplayingInsecureContent() {
173 InsecureContentInfoBarDelegate::Create(
174 this, InsecureContentInfoBarDelegate::DISPLAY
);
177 void InfoBarTabHelper::OnDidBlockRunningInsecureContent() {
178 InsecureContentInfoBarDelegate::Create(this,
179 InsecureContentInfoBarDelegate::RUN
);
182 void InfoBarTabHelper::RenderViewGone(base::TerminationStatus status
) {
183 RemoveAllInfoBars(true);
186 bool InfoBarTabHelper::OnMessageReceived(const IPC::Message
& message
) {
188 IPC_BEGIN_MESSAGE_MAP(InfoBarTabHelper
, message
)
189 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidBlockDisplayingInsecureContent
,
190 OnDidBlockDisplayingInsecureContent
)
191 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidBlockRunningInsecureContent
,
192 OnDidBlockRunningInsecureContent
)
193 IPC_MESSAGE_UNHANDLED(handled
= false)
194 IPC_END_MESSAGE_MAP()
198 void InfoBarTabHelper::Observe(int type
,
199 const content::NotificationSource
& source
,
200 const content::NotificationDetails
& details
) {
201 if (type
== content::NOTIFICATION_NAV_ENTRY_COMMITTED
) {
202 DCHECK(&web_contents()->GetController() ==
203 content::Source
<NavigationController
>(source
).ptr());
205 content::LoadCommittedDetails
& committed_details
=
206 *(content::Details
<content::LoadCommittedDetails
>(details
).ptr());
208 // NOTE: It is not safe to change the following code to count upwards or
209 // use iterators, as the RemoveInfoBar() call synchronously modifies our
211 for (size_t i
= infobars_
.size(); i
> 0; --i
) {
212 InfoBarDelegate
* delegate
= GetInfoBarDelegateAt(i
- 1);
213 if (delegate
->ShouldExpire(committed_details
))
214 RemoveInfoBar(delegate
);
220 DCHECK_EQ(type
, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
);
221 // The WebContents is going away; be aggressively paranoid and delete
222 // ourselves lest other parts of the system attempt to add infobars or use
223 // us otherwise during the destruction.
224 DCHECK_EQ(web_contents(), content::Source
<WebContents
>(source
).ptr());
225 web_contents()->RemoveUserData(UserDataKey());
226 // That was the equivalent of "delete this". This object is now destroyed;
227 // returning from this function is the only safe thing to do.