1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
// 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);
}
|