blob: d5aecc9b98b957d5f815be0c9bf4a54a60a963ed [file] [log] [blame]
// Copyright 2017 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/resource_coordinator/tab_lifecycle_unit_source.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit.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/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
namespace resource_coordinator {
namespace {
TabLifecycleUnitSource* instance_ = nullptr;
} // namespace
TabLifecycleUnitSource::TabLifecycleUnitSource()
: browser_tab_strip_tracker_(this, nullptr, this) {
DCHECK(!instance_);
DCHECK(BrowserList::GetInstance()->empty());
browser_tab_strip_tracker_.Init();
instance_ = this;
}
TabLifecycleUnitSource::~TabLifecycleUnitSource() {
DCHECK_EQ(instance_, this);
instance_ = nullptr;
}
// static
TabLifecycleUnitSource* TabLifecycleUnitSource::GetInstance() {
return instance_;
}
TabLifecycleUnitExternal* TabLifecycleUnitSource::GetTabLifecycleUnitExternal(
content::WebContents* web_contents) const {
auto it = tabs_.find(web_contents);
if (it == tabs_.end())
return nullptr;
return it->second.get();
}
void TabLifecycleUnitSource::AddTabLifecycleObserver(
TabLifecycleObserver* observer) {
tab_lifecycle_observers_.AddObserver(observer);
}
void TabLifecycleUnitSource::RemoveTabLifecycleObserver(
TabLifecycleObserver* observer) {
tab_lifecycle_observers_.RemoveObserver(observer);
}
void TabLifecycleUnitSource::SetFocusedTabStripModelForTesting(
TabStripModel* tab_strip) {
focused_tab_strip_model_for_testing_ = tab_strip;
UpdateFocusedTab();
}
TabStripModel* TabLifecycleUnitSource::GetFocusedTabStripModel() const {
if (focused_tab_strip_model_for_testing_)
return focused_tab_strip_model_for_testing_;
Browser* const focused_browser = chrome::FindBrowserWithActiveWindow();
if (!focused_browser)
return nullptr;
return focused_browser->tab_strip_model();
}
void TabLifecycleUnitSource::UpdateFocusedTab() {
TabStripModel* const focused_tab_strip_model = GetFocusedTabStripModel();
content::WebContents* const focused_web_contents =
focused_tab_strip_model ? focused_tab_strip_model->GetActiveWebContents()
: nullptr;
DCHECK(!focused_web_contents ||
base::ContainsKey(tabs_, focused_web_contents));
UpdateFocusedTabTo(focused_web_contents ? tabs_[focused_web_contents].get()
: nullptr);
}
void TabLifecycleUnitSource::UpdateFocusedTabTo(
TabLifecycleUnit* new_focused_tab_lifecycle_unit) {
if (new_focused_tab_lifecycle_unit == focused_tab_lifecycle_unit_)
return;
if (focused_tab_lifecycle_unit_)
focused_tab_lifecycle_unit_->SetFocused(false);
if (new_focused_tab_lifecycle_unit)
new_focused_tab_lifecycle_unit->SetFocused(true);
focused_tab_lifecycle_unit_ = new_focused_tab_lifecycle_unit;
}
void TabLifecycleUnitSource::TabInsertedAt(TabStripModel* tab_strip_model,
content::WebContents* contents,
int index,
bool foreground) {
auto it = tabs_.find(contents);
if (it == tabs_.end()) {
// A tab was created.
auto res = tabs_.insert(std::make_pair(
contents, std::make_unique<TabLifecycleUnit>(
&tab_lifecycle_observers_, contents, tab_strip_model)));
TabLifecycleUnit* lifecycle_unit = res.first->second.get();
if (GetFocusedTabStripModel() == tab_strip_model && foreground)
UpdateFocusedTabTo(lifecycle_unit);
// Add a self-owned observer to the LifecycleUnit to record metrics.
lifecycle_unit->AddObserver(new DiscardMetricsLifecycleUnitObserver());
NotifyLifecycleUnitCreated(lifecycle_unit);
} else {
// A tab was moved to another window.
it->second->SetTabStripModel(tab_strip_model);
if (foreground)
UpdateFocusedTab();
}
}
void TabLifecycleUnitSource::TabClosingAt(TabStripModel* tab_strip_model,
content::WebContents* contents,
int index) {
auto it = tabs_.find(contents);
DCHECK(it != tabs_.end());
TabLifecycleUnit* lifecycle_unit = it->second.get();
if (focused_tab_lifecycle_unit_ == lifecycle_unit)
focused_tab_lifecycle_unit_ = nullptr;
NotifyLifecycleUnitDestroyed(lifecycle_unit);
tabs_.erase(contents);
}
void TabLifecycleUnitSource::ActiveTabChanged(
content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) {
UpdateFocusedTab();
}
void TabLifecycleUnitSource::TabReplacedAt(TabStripModel* tab_strip_model,
content::WebContents* old_contents,
content::WebContents* new_contents,
int index) {
DCHECK(!base::ContainsKey(tabs_, new_contents));
auto it = tabs_.find(old_contents);
DCHECK(it != tabs_.end());
std::unique_ptr<TabLifecycleUnit> lifecycle_unit = std::move(it->second);
lifecycle_unit->SetWebContents(new_contents);
tabs_.erase(it);
tabs_[new_contents] = std::move(lifecycle_unit);
}
void TabLifecycleUnitSource::TabChangedAt(content::WebContents* contents,
int index,
TabChangeType change_type) {
if (change_type != TabChangeType::kAll)
return;
auto it = tabs_.find(contents);
DCHECK(it != tabs_.end());
TabLifecycleUnit* lifecycle_unit = it->second.get();
lifecycle_unit->SetRecentlyAudible(contents->WasRecentlyAudible());
}
void TabLifecycleUnitSource::OnBrowserSetLastActive(Browser* browser) {
UpdateFocusedTab();
}
void TabLifecycleUnitSource::OnBrowserNoLongerActive(Browser* browser) {
UpdateFocusedTab();
}
} // namespace resource_coordinator