// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/infobars/infobar_tab_helper.h" #include "chrome/browser/infobars/infobar.h" #include "chrome/browser/infobars/infobar_delegate.h" #include "chrome/browser/tab_contents/insecure_content_infobar_delegate.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/render_messages.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/web_contents.h" using content::NavigationController; using content::WebContents; InfoBarTabHelper::InfoBarTabHelper(WebContents* web_contents) : content::WebContentsObserver(web_contents), infobars_enabled_(true) { DCHECK(web_contents); } InfoBarTabHelper::~InfoBarTabHelper() { // Destroy all remaining InfoBars. It's important to not animate here so that // we guarantee that we'll delete all delegates before we do anything else. // // TODO(pkasting): If there is no InfoBarContainer, this leaks all the // InfoBarDelegates. This will be fixed once we call CloseSoon() directly on // Infobars. RemoveAllInfoBars(false); } void InfoBarTabHelper::AddInfoBar(InfoBarDelegate* delegate) { if (!infobars_enabled_) { delegate->InfoBarClosed(); return; } for (size_t i = 0; i < infobars_.size(); ++i) { if (GetInfoBarDelegateAt(i)->EqualsDelegate(delegate)) { delegate->InfoBarClosed(); return; } } // TODO(pkasting): Consider removing InfoBarTabHelper arg from delegate // constructors and instead using a setter from here. infobars_.push_back(delegate); content::NotificationService::current()->Notify( chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, content::Source(this), content::Details(delegate)); // Add ourselves as an observer for navigations the first time a delegate is // added. We use this notification to expire InfoBars that need to expire on // page transitions. if (infobars_.size() == 1) { registrar_.Add( this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, content::Source( &web_contents()->GetController())); } } void InfoBarTabHelper::RemoveInfoBar(InfoBarDelegate* delegate) { RemoveInfoBarInternal(delegate, true); } void InfoBarTabHelper::ReplaceInfoBar(InfoBarDelegate* old_delegate, InfoBarDelegate* new_delegate) { if (!infobars_enabled_) { AddInfoBar(new_delegate); // Deletes the delegate. return; } size_t i; for (i = 0; i < infobars_.size(); ++i) { if (GetInfoBarDelegateAt(i) == old_delegate) break; } DCHECK_LT(i, infobars_.size()); infobars_.insert(infobars_.begin() + i, new_delegate); old_delegate->clear_owner(); InfoBarReplacedDetails replaced_details(old_delegate, new_delegate); content::NotificationService::current()->Notify( chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED, content::Source(this), content::Details(&replaced_details)); infobars_.erase(infobars_.begin() + i + 1); } InfoBarDelegate* InfoBarTabHelper::GetInfoBarDelegateAt(size_t index) { return infobars_[index]; } void InfoBarTabHelper::RemoveInfoBarInternal(InfoBarDelegate* delegate, bool animate) { if (!infobars_enabled_) { DCHECK(infobars_.empty()); return; } size_t i; for (i = 0; i < infobars_.size(); ++i) { if (GetInfoBarDelegateAt(i) == delegate) break; } DCHECK_LT(i, infobars_.size()); InfoBarDelegate* infobar = infobars_[i]; infobar->clear_owner(); InfoBarRemovedDetails removed_details(infobar, animate); content::NotificationService::current()->Notify( chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, content::Source(this), content::Details(&removed_details)); infobars_.erase(infobars_.begin() + i); // Remove ourselves as an observer if we are tracking no more InfoBars. if (infobars_.empty()) { registrar_.Remove( this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, content::Source( &web_contents()->GetController())); } } void InfoBarTabHelper::RemoveAllInfoBars(bool animate) { while (!infobars_.empty()) RemoveInfoBarInternal(GetInfoBarDelegateAt(infobar_count() - 1), animate); } void InfoBarTabHelper::OnDidBlockDisplayingInsecureContent() { // At most one infobar and do not supersede the stronger running content bar. for (size_t i = 0; i < infobars_.size(); ++i) { if (GetInfoBarDelegateAt(i)->AsInsecureContentInfoBarDelegate()) return; } AddInfoBar(new InsecureContentInfoBarDelegate(this, InsecureContentInfoBarDelegate::DISPLAY)); } void InfoBarTabHelper::OnDidBlockRunningInsecureContent() { // At most one infobar superseding any weaker displaying content bar. for (size_t i = 0; i < infobars_.size(); ++i) { InsecureContentInfoBarDelegate* delegate = GetInfoBarDelegateAt(i)->AsInsecureContentInfoBarDelegate(); if (delegate) { if (delegate->type() != InsecureContentInfoBarDelegate::RUN) { ReplaceInfoBar(delegate, new InsecureContentInfoBarDelegate( this, InsecureContentInfoBarDelegate::RUN)); } return; } } AddInfoBar(new InsecureContentInfoBarDelegate(this, InsecureContentInfoBarDelegate::RUN)); } void InfoBarTabHelper::RenderViewGone(base::TerminationStatus status) { RemoveAllInfoBars(true); } bool InfoBarTabHelper::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(InfoBarTabHelper, message) IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidBlockDisplayingInsecureContent, OnDidBlockDisplayingInsecureContent) IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidBlockRunningInsecureContent, OnDidBlockRunningInsecureContent) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void InfoBarTabHelper::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { case content::NOTIFICATION_NAV_ENTRY_COMMITTED: { DCHECK(&web_contents()->GetController() == content::Source(source).ptr()); content::LoadCommittedDetails& committed_details = *(content::Details(details).ptr()); // NOTE: It is not safe to change the following code to count upwards or // use iterators, as the RemoveInfoBar() call synchronously modifies our // delegate list. for (size_t i = infobars_.size(); i > 0; --i) { InfoBarDelegate* delegate = GetInfoBarDelegateAt(i - 1); if (delegate->ShouldExpire(committed_details)) RemoveInfoBar(delegate); } break; } default: NOTREACHED(); } }