blob: e3e6e4de08f2b359ec8b0385bf0c5154b46eed82 [file] [log] [blame]
// Copyright 2018 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/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.h"
#include "base/bind.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/views/relaunch_notification/relaunch_notification_metrics.h"
#include "chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.h"
#include "chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h"
#include "chrome/common/buildflags.h"
#include "ui/views/widget/widget.h"
#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
#include "chrome/browser/background/background_mode_manager.h"
#endif // BUILDFLAG(ENABLE_BACKGROUND_MODE)
namespace {
// Returns the reason why a dialog was not shown when the conditions were ripe
// for such.
relaunch_notification::ShowResult GetNotShownReason() {
#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
BackgroundModeManager* background_mode_manager =
g_browser_process->background_mode_manager();
if (background_mode_manager &&
background_mode_manager->IsBackgroundWithoutWindows()) {
return relaunch_notification::ShowResult::kBackgroundModeNoWindows;
}
#endif // BUILDFLAG(ENABLE_BACKGROUND_MODE)
return relaunch_notification::ShowResult::kUnknownNotShownReason;
}
// Returns the last active tabbed browser.
Browser* FindLastActiveTabbedBrowser() {
BrowserList* browser_list = BrowserList::GetInstance();
const auto end = browser_list->end_last_active();
for (auto scan = browser_list->begin_last_active(); scan != end; ++scan) {
if ((*scan)->is_type_normal())
return *scan;
}
return nullptr;
}
} // namespace
RelaunchNotificationControllerPlatformImpl::
RelaunchNotificationControllerPlatformImpl() = default;
RelaunchNotificationControllerPlatformImpl::
~RelaunchNotificationControllerPlatformImpl() {
DCHECK(!widget_);
if (on_visible_)
BrowserList::RemoveObserver(this);
}
void RelaunchNotificationControllerPlatformImpl::NotifyRelaunchRecommended(
base::Time detection_time,
bool /*past_deadline*/) {
// Nothing to do if the bubble is visible.
if (widget_)
return;
// Show the bubble in the most recently active browser.
Browser* browser = FindLastActiveTabbedBrowser();
relaunch_notification::RecordRecommendedShowResult(
browser ? relaunch_notification::ShowResult::kShown
: GetNotShownReason());
if (!browser)
return;
widget_ = RelaunchRecommendedBubbleView::ShowBubble(
browser, detection_time, base::BindRepeating(&chrome::AttemptRelaunch));
// Monitor the widget so that |widget_| can be cleared on close.
widget_->AddObserver(this);
}
void RelaunchNotificationControllerPlatformImpl::NotifyRelaunchRequired(
base::Time deadline,
base::OnceCallback<base::Time()> on_visible) {
// Nothing to do if the dialog is visible.
if (widget_)
return;
// Show the dialog in the active tabbed browser window.
Browser* browser = chrome::FindBrowserWithActiveWindow();
if (browser && browser->is_type_normal()) {
DCHECK(!on_visible_);
ShowRequiredNotification(browser, deadline);
relaunch_notification::RecordRequiredShowResult(
relaunch_notification::ShowResult::kShown);
return;
}
relaunch_notification::RecordRequiredShowResult(GetNotShownReason());
// If the instance is not already waiting for one to become active from a
// previous call, start observing now.
if (!on_visible_)
BrowserList::AddObserver(this);
// Hold on to the callback until an active tabbed browser is found.
on_visible_ = std::move(on_visible);
last_relaunch_deadline_ = deadline;
}
void RelaunchNotificationControllerPlatformImpl::CloseRelaunchNotification() {
if (widget_)
widget_->Close();
if (on_visible_) {
BrowserList::RemoveObserver(this);
on_visible_.Reset();
last_relaunch_deadline_ = base::Time();
}
has_shown_ = false;
}
void RelaunchNotificationControllerPlatformImpl::SetDeadline(
base::Time deadline) {
// Nothing to do if the dialog hasn't been shown yet (because no tabbed
// browser has become active) or if the user has seen and dismissed the
// dialog.
if (widget_)
RelaunchRequiredDialogView::FromWidget(widget_)->SetDeadline(deadline);
// Hold on to the new deadline if the instance is waiting for a Browser to
// become active.
if (on_visible_)
last_relaunch_deadline_ = deadline;
}
bool RelaunchNotificationControllerPlatformImpl::IsRequiredNotificationShown()
const {
return widget_ != nullptr;
}
void RelaunchNotificationControllerPlatformImpl::OnWidgetClosing(
views::Widget* widget) {
DCHECK_EQ(widget, widget_);
widget->RemoveObserver(this);
widget_ = nullptr;
}
void RelaunchNotificationControllerPlatformImpl::OnWidgetDestroying(
views::Widget* widget) {
DCHECK_EQ(widget, widget_);
widget->RemoveObserver(this);
widget_ = nullptr;
}
void RelaunchNotificationControllerPlatformImpl::OnBrowserSetLastActive(
Browser* browser) {
// Ignore non-tabbed browsers.
if (!browser->is_type_normal())
return;
BrowserList::RemoveObserver(this);
base::Time new_deadline =
has_shown_ ? last_relaunch_deadline_ : std::move(on_visible_).Run();
DCHECK(!new_deadline.is_null());
on_visible_.Reset();
last_relaunch_deadline_ = base::Time();
ShowRequiredNotification(browser, new_deadline);
}
void RelaunchNotificationControllerPlatformImpl::ShowRequiredNotification(
Browser* browser,
base::Time deadline) {
widget_ = RelaunchRequiredDialogView::Show(
browser, deadline, base::BindRepeating(&chrome::AttemptRelaunch));
has_shown_ = true;
// Monitor the widget so that |widget_| can be cleared on close/destruction.
widget_->AddObserver(this);
}