| Index: chrome/browser/ui/tabs/tab_strip_model_stats_recorder.cc
|
| diff --git a/chrome/browser/ui/tabs/tab_strip_model_stats_recorder.cc b/chrome/browser/ui/tabs/tab_strip_model_stats_recorder.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9dad9864109575bcb73eb5fc603eb902519e73c3
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/tabs/tab_strip_model_stats_recorder.cc
|
| @@ -0,0 +1,271 @@
|
| +// 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/ui/tabs/tab_strip_model_stats_recorder.h"
|
| +
|
| +#include <utility>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/metrics/histogram_macros.h"
|
| +#include "chrome/browser/ui/browser.h"
|
| +#include "chrome/browser/ui/browser_list.h"
|
| +#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
| +
|
| +namespace chrome {
|
| +
|
| +TabStripModelStatsRecorder::TabStripModelStatsRecorder() {
|
| + BrowserList::AddObserver(this);
|
| +}
|
| +
|
| +TabStripModelStatsRecorder::~TabStripModelStatsRecorder() {
|
| + BrowserList::RemoveObserver(this);
|
| +}
|
| +
|
| +void TabStripModelStatsRecorder::OnBrowserAdded(Browser* browser) {
|
| + browser->tab_strip_model()->AddObserver(this);
|
| +}
|
| +
|
| +void TabStripModelStatsRecorder::OnBrowserRemoved(Browser* browser) {
|
| + browser->tab_strip_model()->RemoveObserver(this);
|
| +}
|
| +
|
| +using TabState = TabStripModelStatsRecorder::TabInfo::State;
|
| +
|
| +void TabStripModelStatsRecorder::TabInfo::UpdateState(TabState new_state) {
|
| + if (new_state == current_state_) {
|
| + return;
|
| + }
|
| +
|
| + base::Time now = base::Time::Now();
|
| + base::TimeDelta delta = now - last_state_modified_;
|
| +
|
| + switch (current_state_) {
|
| + case TabState::INITIAL:
|
| + UMA_HISTOGRAM_ENUMERATION("TabStateTransferTarget.Initial",
|
| + static_cast<int>(new_state),
|
| + static_cast<int>(TabState::MAX));
|
| + break;
|
| + case TabState::ACTIVE:
|
| + switch (new_state) {
|
| + case TabState::INITIAL:
|
| + case TabState::ACTIVE:
|
| + case TabState::MAX:
|
| + NOTREACHED();
|
| + break;
|
| + case TabState::INACTIVE:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Active.Inactive",
|
| + delta);
|
| + break;
|
| + case TabState::DETACHED:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Active.Detached",
|
| + delta);
|
| + break;
|
| + case TabState::CLOSED:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Active.Closed",
|
| + delta);
|
| + break;
|
| + }
|
| +
|
| + UMA_HISTOGRAM_ENUMERATION("TabStateTransferTarget.Active",
|
| + static_cast<int>(new_state),
|
| + static_cast<int>(TabState::MAX));
|
| + break;
|
| + case TabState::INACTIVE:
|
| + switch (new_state) {
|
| + case TabState::INITIAL:
|
| + case TabState::INACTIVE:
|
| + case TabState::MAX:
|
| + NOTREACHED();
|
| + break;
|
| + case TabState::ACTIVE:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Inactive.Active",
|
| + delta);
|
| + break;
|
| + case TabState::DETACHED:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Inactive.Detached",
|
| + delta);
|
| + break;
|
| + case TabState::CLOSED:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Inactive.Closed",
|
| + delta);
|
| + break;
|
| + }
|
| +
|
| + UMA_HISTOGRAM_ENUMERATION("TabStateTransferTarget.Inactive",
|
| + static_cast<int>(new_state),
|
| + static_cast<int>(TabState::MAX));
|
| + break;
|
| + case TabState::DETACHED:
|
| + switch (new_state) {
|
| + case TabState::INITIAL:
|
| + case TabState::DETACHED:
|
| + case TabState::MAX:
|
| + NOTREACHED();
|
| + break;
|
| + case TabState::ACTIVE:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Detached.Active",
|
| + delta);
|
| + break;
|
| + case TabState::INACTIVE:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Detached.Inactive",
|
| + delta);
|
| + break;
|
| + case TabState::CLOSED:
|
| + UMA_HISTOGRAM_LONG_TIMES_100("TabStateTransferTime.Detached.Closed",
|
| + delta);
|
| + break;
|
| + }
|
| +
|
| + UMA_HISTOGRAM_ENUMERATION("TabStateTransferTarget.Detached",
|
| + static_cast<int>(new_state),
|
| + static_cast<int>(TabState::MAX));
|
| + break;
|
| + case TabState::CLOSED:
|
| + case TabState::MAX:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| + last_state_modified_ = now;
|
| + current_state_ = new_state;
|
| +}
|
| +
|
| +void TabStripModelStatsRecorder::TabInsertedAt(content::WebContents* contents,
|
| + int index,
|
| + bool foreground) {
|
| + tab_info_map_[contents].UpdateState(foreground ? TabState::ACTIVE
|
| + : TabState::INACTIVE);
|
| +}
|
| +
|
| +void TabStripModelStatsRecorder::TabClosingAt(TabStripModel*,
|
| + content::WebContents* contents,
|
| + int index) {
|
| + auto it = tab_info_map_.find(contents);
|
| + if (it == tab_info_map_.end()) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + auto& info = it->second;
|
| + info.UpdateState(TabState::CLOSED);
|
| + tab_info_map_.erase(it);
|
| +
|
| + for (auto it = active_tab_history_.begin(); it != active_tab_history_.end();
|
| + ++it) {
|
| + if (*it == contents) {
|
| + active_tab_history_.erase(it);
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TabStripModelStatsRecorder::TabDetachedAt(content::WebContents* contents,
|
| + int index) {
|
| + auto it = tab_info_map_.find(contents);
|
| + if (it == tab_info_map_.end()) {
|
| + return;
|
| + }
|
| +
|
| + auto& info = it->second;
|
| + info.UpdateState(TabState::DETACHED);
|
| +}
|
| +
|
| +void TabStripModelStatsRecorder::ActiveTabChanged(
|
| + content::WebContents* old_contents,
|
| + content::WebContents* new_contents,
|
| + int index,
|
| + int reason) {
|
| + if (reason & TabStripModelObserver::CHANGE_REASON_REPLACED) {
|
| + // We already handled tab clobber at TabReplacedAt notification.
|
| + return;
|
| + }
|
| +
|
| + if (old_contents) {
|
| + auto it = tab_info_map_.find(old_contents);
|
| + if (it == tab_info_map_.end()) {
|
| + // The old tab already closed.
|
| + return;
|
| + }
|
| + auto& old_tab_info = it->second;
|
| + old_tab_info.UpdateState(TabState::INACTIVE);
|
| + }
|
| +
|
| + DCHECK(new_contents);
|
| + auto it = tab_info_map_.find(new_contents);
|
| + if (it == tab_info_map_.end()) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + auto& tab_info = it->second;
|
| +
|
| + bool was_inactive = tab_info.state() == TabState::INACTIVE;
|
| + tab_info.UpdateState(TabState::ACTIVE);
|
| +
|
| + int distance = last_active_index_ - index;
|
| + last_active_index_ = index;
|
| +
|
| + if (was_inactive) {
|
| + // We offset and clamp the |distance| by MAX_DIST, as UMA enum only supports
|
| + // positive bounded int.
|
| + const int MAX_DIST = 50;
|
| + int encoded_dist = distance + MAX_DIST;
|
| + encoded_dist = std::max(std::min(encoded_dist, MAX_DIST * 2), 0);
|
| + UMA_HISTOGRAM_ENUMERATION("TabStateTransfer.TabDistanceInactiveToActive",
|
| + encoded_dist, MAX_DIST * 2);
|
| + }
|
| +
|
| + const int MAX_AGE = 64;
|
| + int age = 1;
|
| + for (auto it = active_tab_history_.begin();;) {
|
| + if (it == active_tab_history_.end()) {
|
| + // If the active tab was never active before, record it as 0.
|
| + // This can happen if the tab was created in the background.
|
| + if (was_inactive) {
|
| + UMA_HISTOGRAM_ENUMERATION("TabStateTransfer.TabAgeInactiveToActive", 0,
|
| + MAX_AGE);
|
| + }
|
| + break;
|
| + }
|
| +
|
| + if (*it == new_contents) {
|
| + active_tab_history_.erase(it);
|
| +
|
| + if (was_inactive) {
|
| + UMA_HISTOGRAM_ENUMERATION("TabStateTransfer.TabAgeInactiveToActive",
|
| + std::min(age, MAX_AGE), MAX_AGE);
|
| + }
|
| + break;
|
| + }
|
| +
|
| + ++age;
|
| + ++it;
|
| + }
|
| + active_tab_history_.push_front(new_contents);
|
| +}
|
| +
|
| +void TabStripModelStatsRecorder::TabReplacedAt(
|
| + TabStripModel* tab_strip_model,
|
| + content::WebContents* old_contents,
|
| + content::WebContents* new_contents,
|
| + int index) {
|
| + DCHECK(old_contents != new_contents);
|
| +
|
| + auto it = tab_info_map_.find(old_contents);
|
| + if (it == tab_info_map_.end()) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + auto& tab_info = it->second;
|
| + tab_info_map_.insert(std::make_pair(new_contents, tab_info));
|
| + tab_info_map_.erase(it);
|
| +
|
| + for (auto it = active_tab_history_.begin(); it != active_tab_history_.end();
|
| + ++it) {
|
| + if (*it == old_contents) {
|
| + *it = new_contents;
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace chrome
|
|
|