blob: c5b4d1639fb5cf11cede1bb614912ff503e37615 [file] [log] [blame]
// Copyright 2015 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/media/router/issue_manager.h"
#include <algorithm>
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
namespace media_router {
namespace {
// The number of minutes a NOTIFICATION Issue stays in the IssueManager
// before it is auto-dismissed.
constexpr int kNotificationAutoDismissMins = 1;
// The number of minutes a WARNING Issue stays in the IssueManager before it
// is auto-dismissed.
constexpr int kWarningAutoDismissMins = 5;
} // namespace
// static
base::TimeDelta IssueManager::GetAutoDismissTimeout(
const IssueInfo& issue_info) {
if (issue_info.is_blocking)
return base::TimeDelta();
switch (issue_info.severity) {
case IssueInfo::Severity::NOTIFICATION:
return base::TimeDelta::FromMinutes(kNotificationAutoDismissMins);
case IssueInfo::Severity::WARNING:
return base::TimeDelta::FromMinutes(kWarningAutoDismissMins);
case IssueInfo::Severity::FATAL:
NOTREACHED() << "FATAL issues should be blocking";
return base::TimeDelta();
}
NOTREACHED();
return base::TimeDelta();
}
IssueManager::IssueManager()
: top_issue_(nullptr),
task_runner_(base::CreateSingleThreadTaskRunnerWithTraits(
{content::BrowserThread::UI})) {}
IssueManager::~IssueManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void IssueManager::AddIssue(const IssueInfo& issue_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto& issues_map =
issue_info.is_blocking ? blocking_issues_ : non_blocking_issues_;
for (const auto& key_value_pair : issues_map) {
const auto& issue = key_value_pair.second->issue;
if (issue.info() == issue_info)
return;
}
Issue issue(issue_info);
std::unique_ptr<base::CancelableClosure> cancelable_dismiss_cb;
base::TimeDelta timeout = GetAutoDismissTimeout(issue_info);
if (!timeout.is_zero()) {
cancelable_dismiss_cb =
std::make_unique<base::CancelableClosure>(base::Bind(
&IssueManager::ClearIssue, base::Unretained(this), issue.id()));
task_runner_->PostDelayedTask(FROM_HERE, cancelable_dismiss_cb->callback(),
timeout);
}
issues_map.emplace(issue.id(), std::make_unique<IssueManager::Entry>(
issue, std::move(cancelable_dismiss_cb)));
MaybeUpdateTopIssue();
}
void IssueManager::ClearIssue(const Issue::Id& issue_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (non_blocking_issues_.erase(issue_id) || blocking_issues_.erase(issue_id))
MaybeUpdateTopIssue();
}
void IssueManager::ClearNonBlockingIssues() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (non_blocking_issues_.empty())
return;
non_blocking_issues_.clear();
MaybeUpdateTopIssue();
}
void IssueManager::RegisterObserver(IssuesObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(observer);
DCHECK(!issues_observers_.HasObserver(observer));
issues_observers_.AddObserver(observer);
if (top_issue_)
observer->OnIssue(*top_issue_);
}
void IssueManager::UnregisterObserver(IssuesObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
issues_observers_.RemoveObserver(observer);
}
IssueManager::Entry::Entry(
const Issue& issue,
std::unique_ptr<base::CancelableClosure> cancelable_dismiss_callback)
: issue(issue),
cancelable_dismiss_callback(std::move(cancelable_dismiss_callback)) {}
IssueManager::Entry::~Entry() = default;
void IssueManager::MaybeUpdateTopIssue() {
const Issue* new_top_issue = nullptr;
// Select the first blocking issue in the list of issues.
// If there are none, simply select the first issue in the list.
if (!blocking_issues_.empty()) {
new_top_issue = &blocking_issues_.begin()->second->issue;
} else if (!non_blocking_issues_.empty()) {
new_top_issue = &non_blocking_issues_.begin()->second->issue;
}
// If we've found a new top issue, then report it via the observer.
if (new_top_issue != top_issue_) {
top_issue_ = new_top_issue;
for (auto& observer : issues_observers_) {
if (top_issue_)
observer.OnIssue(*top_issue_);
else
observer.OnIssuesCleared();
}
}
}
} // namespace media_router