blob: f5c0b9b8b22f7f154b3563fc341bdfff5952aeca [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/global_confirm_info_bar.h"
#include <utility>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/infobars/confirm_infobar_creator.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/infobars/core/infobar.h"
#include "ui/gfx/image/image.h"
class GlobalConfirmInfoBar::DelegateProxy : public ConfirmInfoBarDelegate {
public:
explicit DelegateProxy(base::WeakPtr<GlobalConfirmInfoBar> global_info_bar);
DelegateProxy(const DelegateProxy&) = delete;
DelegateProxy& operator=(const DelegateProxy&) = delete;
~DelegateProxy() override;
void Detach();
private:
friend class GlobalConfirmInfoBar;
// ConfirmInfoBarDelegate:
infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
std::u16string GetLinkText() const override;
GURL GetLinkURL() const override;
void InfoBarDismissed() override;
std::u16string GetMessageText() const override;
gfx::ElideBehavior GetMessageElideBehavior() const override;
int GetButtons() const override;
std::u16string GetButtonLabel(InfoBarButton button) const override;
bool Accept() override;
bool Cancel() override;
bool IsCloseable() const override;
bool ShouldAnimate() const override;
base::WeakPtr<GlobalConfirmInfoBar> global_info_bar_;
};
GlobalConfirmInfoBar::DelegateProxy::DelegateProxy(
base::WeakPtr<GlobalConfirmInfoBar> global_info_bar)
: global_info_bar_(global_info_bar) {}
GlobalConfirmInfoBar::DelegateProxy::~DelegateProxy() = default;
infobars::InfoBarDelegate::InfoBarIdentifier
GlobalConfirmInfoBar::DelegateProxy::GetIdentifier() const {
return global_info_bar_ ? global_info_bar_->delegate_->GetIdentifier()
: INVALID;
}
std::u16string GlobalConfirmInfoBar::DelegateProxy::GetLinkText() const {
return global_info_bar_ ? global_info_bar_->delegate_->GetLinkText()
: ConfirmInfoBarDelegate::GetLinkText();
}
GURL GlobalConfirmInfoBar::DelegateProxy::GetLinkURL() const {
return global_info_bar_ ? global_info_bar_->delegate_->GetLinkURL()
: ConfirmInfoBarDelegate::GetLinkURL();
}
bool GlobalConfirmInfoBar::DelegateProxy::IsCloseable() const {
return global_info_bar_ ? global_info_bar_->delegate_->IsCloseable()
: ConfirmInfoBarDelegate::IsCloseable();
}
bool GlobalConfirmInfoBar::DelegateProxy::ShouldAnimate() const {
return global_info_bar_ ? global_info_bar_->delegate_->ShouldAnimate()
: ConfirmInfoBarDelegate::ShouldAnimate();
}
void GlobalConfirmInfoBar::DelegateProxy::InfoBarDismissed() {
base::WeakPtr<GlobalConfirmInfoBar> info_bar = global_info_bar_;
// Remove the current InfoBar (the one whose close button is being clicked)
// from the control of GlobalConfirmInfoBar. This InfoBar will be closed by
// caller of this method, and we don't need GlobalConfirmInfoBar to close it.
// Furthermore, letting GlobalConfirmInfoBar close the current InfoBar can
// cause memory corruption when InfoBar animation is disabled.
if (info_bar) {
info_bar->OnInfoBarRemoved(infobar(), false);
info_bar->delegate_->InfoBarDismissed();
// Check the pointer again in case it's now destroyed.
// TODO(pkasting): We should audit callees for these sorts of methods
// (InfoBarDismissed(), Accept(), Cancel()) to determine if they can close
// the global infobar, then establish better contracts/APIs around the
// lifetimes here, ideally removing WeakPtrs entirely.
if (info_bar)
info_bar->Close();
} else {
ConfirmInfoBarDelegate::InfoBarDismissed();
}
}
std::u16string GlobalConfirmInfoBar::DelegateProxy::GetMessageText() const {
return global_info_bar_ ? global_info_bar_->delegate_->GetMessageText()
: std::u16string();
}
gfx::ElideBehavior
GlobalConfirmInfoBar::DelegateProxy::GetMessageElideBehavior() const {
return global_info_bar_
? global_info_bar_->delegate_->GetMessageElideBehavior()
: ConfirmInfoBarDelegate::GetMessageElideBehavior();
}
int GlobalConfirmInfoBar::DelegateProxy::GetButtons() const {
// ConfirmInfoBarDelegate default behavior here is not very good for a no-op
// case, so return BUTTON_NONE when there is no underlying delegate.
return global_info_bar_ ? global_info_bar_->delegate_->GetButtons()
: BUTTON_NONE;
}
std::u16string GlobalConfirmInfoBar::DelegateProxy::GetButtonLabel(
InfoBarButton button) const {
return global_info_bar_ ? global_info_bar_->delegate_->GetButtonLabel(button)
: ConfirmInfoBarDelegate::GetButtonLabel(button);
}
bool GlobalConfirmInfoBar::DelegateProxy::Accept() {
base::WeakPtr<GlobalConfirmInfoBar> info_bar = global_info_bar_;
// See comments in InfoBarDismissed().
if (info_bar) {
// TODO(pkasting): This implementation assumes the global delegate's
// Accept() always returns true. Ideally, we'd check the return value and
// handle it appropriately. We also need to worry about side effects like
// navigating the current tab and whether that can corrupt state or result
// in double-frees.
info_bar->OnInfoBarRemoved(infobar(), false);
info_bar->delegate_->Accept();
if (info_bar)
info_bar->Close();
return true;
}
return ConfirmInfoBarDelegate::Accept();
}
bool GlobalConfirmInfoBar::DelegateProxy::Cancel() {
base::WeakPtr<GlobalConfirmInfoBar> info_bar = global_info_bar_;
// See comments in InfoBarDismissed().
if (info_bar) {
// See comments in Accept().
info_bar->OnInfoBarRemoved(infobar(), false);
info_bar->delegate_->Cancel();
if (info_bar)
info_bar->Close();
return true;
}
return ConfirmInfoBarDelegate::Cancel();
}
void GlobalConfirmInfoBar::DelegateProxy::Detach() {
global_info_bar_.reset();
}
// static
GlobalConfirmInfoBar* GlobalConfirmInfoBar::Show(
std::unique_ptr<ConfirmInfoBarDelegate> delegate) {
// Owns itself, deleted by Close().
return new GlobalConfirmInfoBar(std::move(delegate));
}
GlobalConfirmInfoBar::GlobalConfirmInfoBar(
std::unique_ptr<ConfirmInfoBarDelegate> delegate)
: delegate_(std::move(delegate)) {
browser_tab_strip_tracker_.Init();
}
GlobalConfirmInfoBar::~GlobalConfirmInfoBar() {
while (!proxies_.empty()) {
auto it = proxies_.begin();
it->second->Detach();
it->first->RemoveObserver(this);
it->first->RemoveInfoBar(it->second->infobar());
proxies_.erase(it);
}
}
void GlobalConfirmInfoBar::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
if (change.type() != TabStripModelChange::kInserted)
return;
for (const auto& contents : change.GetInsert()->contents)
MaybeAddInfoBar(contents.contents);
}
void GlobalConfirmInfoBar::TabChangedAt(content::WebContents* web_contents,
int index,
TabChangeType change_type) {
MaybeAddInfoBar(web_contents);
}
void GlobalConfirmInfoBar::OnInfoBarRemoved(infobars::InfoBar* info_bar,
bool animate) {
// Do not process alien infobars.
for (const auto& it : proxies_) {
if (it.second->infobar() == info_bar) {
OnManagerShuttingDown(info_bar->owner());
break;
}
}
}
void GlobalConfirmInfoBar::OnManagerShuttingDown(
infobars::InfoBarManager* manager) {
manager->RemoveObserver(this);
proxies_.erase(manager);
}
void GlobalConfirmInfoBar::Close() {
delete this;
}
void GlobalConfirmInfoBar::MaybeAddInfoBar(content::WebContents* web_contents) {
if (is_closing_)
return;
infobars::ContentInfoBarManager* infobar_manager =
infobars::ContentInfoBarManager::FromWebContents(web_contents);
// WebContents from the tab strip must have the infobar manager.
DCHECK(infobar_manager);
if (base::Contains(proxies_, infobar_manager))
return;
auto proxy = std::make_unique<GlobalConfirmInfoBar::DelegateProxy>(
weak_factory_.GetWeakPtr());
GlobalConfirmInfoBar::DelegateProxy* proxy_ptr = proxy.get();
infobars::InfoBar* added_bar =
infobar_manager->AddInfoBar(CreateConfirmInfoBar(std::move(proxy)));
// If AddInfoBar() fails, either infobars are globally disabled, or something
// strange has gone wrong and we can't show the infobar on every tab. In
// either case, it doesn't make sense to keep the global object open,
// especially since some callers expect it to delete itself when a user acts
// on the underlying infobars.
//
// Asynchronously delete the global object because the BrowserTabStripTracker
// doesn't support being deleted while iterating over the existing tabs.
if (!added_bar) {
is_closing_ = true;
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&GlobalConfirmInfoBar::Close,
weak_factory_.GetWeakPtr()));
return;
}
proxies_[infobar_manager] = proxy_ptr;
infobar_manager->AddObserver(this);
}