blob: 5a519841b7cbb34c5a37ccd3571455e26bbfbe9e [file] [log] [blame]
// Copyright (c) 2013 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/active_tab_tracker.h"
#include "chrome/browser/history/history_service.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents.h"
namespace {
// Amount of time a page has to be active before we commit it.
const int kTimeBeforeCommitMS = 1000;
// Number of seconds input should be received before considered idle.
const int kIdleTimeSeconds = 30;
} // namespace
#if !defined(OS_WIN) && !defined(USE_AURA)
// static
NativeFocusTracker* NativeFocusTracker::Create(NativeFocusTrackerHost* host) {
return NULL;
}
#endif
ActiveTabTracker::ActiveTabTracker()
: browser_(NULL),
web_contents_(NULL),
idle_state_(IDLE_STATE_UNKNOWN),
timer_(false, false),
weak_ptr_factory_(this) {
native_focus_tracker_.reset(NativeFocusTracker::Create(this));
Browser* browser =
BrowserList::GetInstance(chrome::GetActiveDesktop())->GetLastActive();
SetBrowser(browser);
BrowserList::AddObserver(this);
}
ActiveTabTracker::~ActiveTabTracker() {
native_focus_tracker_.reset();
SetBrowser(NULL);
BrowserList::RemoveObserver(this);
}
void ActiveTabTracker::ActiveTabChanged(content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) {
SetWebContents(new_contents);
}
void ActiveTabTracker::TabReplacedAt(TabStripModel* tab_strip_model,
content::WebContents* old_contents,
content::WebContents* new_contents,
int index) {
if (index == tab_strip_model->selection_model().active())
SetWebContents(new_contents);
}
void ActiveTabTracker::TabStripEmpty() {
SetBrowser(NULL);
}
void ActiveTabTracker::OnBrowserRemoved(Browser* browser) {
if (browser == browser_)
SetBrowser(NULL);
}
void ActiveTabTracker::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
const GURL url = GetURLFromWebContents();
if (url == url_)
return;
CommitActiveTime();
url_ = url;
}
void ActiveTabTracker::SetBrowser(Browser* browser) {
if (browser_ == browser)
return;
CommitActiveTime();
if (browser_)
browser_->tab_strip_model()->RemoveObserver(this);
// Don't track anything for otr profiles.
if (browser && browser->profile()->IsOffTheRecord())
browser = NULL;
browser_ = browser;
content::WebContents* web_contents = NULL;
if (browser_) {
TabStripModel* tab_strip = browser_->tab_strip_model();
tab_strip->AddObserver(this);
web_contents = tab_strip->GetActiveWebContents();
} else {
idle_state_ = IDLE_STATE_UNKNOWN;
timer_.Stop();
weak_ptr_factory_.InvalidateWeakPtrs();
}
SetWebContents(web_contents);
}
void ActiveTabTracker::SetWebContents(content::WebContents* web_contents) {
if (web_contents_ == web_contents)
return;
CommitActiveTime();
active_time_ = base::TimeTicks::Now();
web_contents_ = web_contents;
url_ = GetURLFromWebContents();
registrar_.RemoveAll();
// TODO(sky): this isn't quite right. We should really not include transient
// entries here. For that we need to make Browser::NavigationStateChanged()
// call through to this class.
if (web_contents_) {
registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::Source<content::NavigationController>(
&web_contents_->GetController()));
QueryIdleState();
}
}
void ActiveTabTracker::SetIdleState(IdleState idle_state) {
if (idle_state_ != idle_state) {
if (idle_state_ == IDLE_STATE_ACTIVE)
CommitActiveTime();
else if (idle_state == IDLE_STATE_ACTIVE)
active_time_ = base::TimeTicks::Now();
idle_state_ = idle_state;
}
if (browser_) {
timer_.Start(FROM_HERE,
base::TimeDelta::FromSeconds(kIdleTimeSeconds),
base::Bind(&ActiveTabTracker::QueryIdleState,
base::Unretained(this)));
}
}
void ActiveTabTracker::QueryIdleState() {
if (weak_ptr_factory_.HasWeakPtrs())
return;
CalculateIdleState(kIdleTimeSeconds,
base::Bind(&ActiveTabTracker::SetIdleState,
weak_ptr_factory_.GetWeakPtr()));
}
GURL ActiveTabTracker::GetURLFromWebContents() const {
if (!web_contents_)
return GURL();
// TODO: handle subframe transitions better. Maybe go back to first entry
// that isn't a main frame?
content::NavigationEntry* entry =
web_contents_->GetController().GetLastCommittedEntry();
if (!entry || !PageTransitionIsMainFrame(entry->GetTransitionType()))
return GURL();
return !entry->GetUserTypedURL().is_empty() ?
entry->GetUserTypedURL() : entry->GetURL();
}
void ActiveTabTracker::CommitActiveTime() {
const base::TimeDelta active_delta = base::TimeTicks::Now() - active_time_;
active_time_ = base::TimeTicks::Now();
if (!web_contents_ || url_.is_empty() || idle_state_ != IDLE_STATE_ACTIVE ||
active_delta.InMilliseconds() < kTimeBeforeCommitMS)
return;
Profile* profile = Profile::FromBrowserContext(
web_contents_->GetBrowserContext());
HistoryService* history = HistoryServiceFactory::GetForProfile(
profile, Profile::EXPLICIT_ACCESS);
if (history)
history->IncreaseSegmentDuration(url_, base::Time::Now(), active_delta);
}