1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/active_tab_tracker.h"
7 #include "chrome/browser/history/history_service.h"
8 #include "chrome/browser/history/history_service_factory.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_list.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "content/public/browser/navigation_entry.h"
14 #include "content/public/browser/notification_source.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/web_contents.h"
20 // Amount of time a page has to be active before we commit it.
21 const int kTimeBeforeCommitMS
= 1000;
23 // Number of seconds input should be received before considered idle.
24 const int kIdleTimeSeconds
= 30;
28 #if !defined(OS_WIN) && !defined(USE_AURA)
30 NativeFocusTracker
* NativeFocusTracker::Create(NativeFocusTrackerHost
* host
) {
35 ActiveTabTracker::ActiveTabTracker()
38 idle_state_(IDLE_STATE_UNKNOWN
),
40 weak_ptr_factory_(this) {
41 native_focus_tracker_
.reset(NativeFocusTracker::Create(this));
43 BrowserList::GetInstance(chrome::GetActiveDesktop())->GetLastActive();
45 BrowserList::AddObserver(this);
48 ActiveTabTracker::~ActiveTabTracker() {
49 native_focus_tracker_
.reset();
51 BrowserList::RemoveObserver(this);
54 void ActiveTabTracker::ActiveTabChanged(content::WebContents
* old_contents
,
55 content::WebContents
* new_contents
,
58 SetWebContents(new_contents
);
61 void ActiveTabTracker::TabReplacedAt(TabStripModel
* tab_strip_model
,
62 content::WebContents
* old_contents
,
63 content::WebContents
* new_contents
,
65 if (index
== tab_strip_model
->selection_model().active())
66 SetWebContents(new_contents
);
69 void ActiveTabTracker::TabStripEmpty() {
73 void ActiveTabTracker::OnBrowserRemoved(Browser
* browser
) {
74 if (browser
== browser_
)
78 void ActiveTabTracker::Observe(int type
,
79 const content::NotificationSource
& source
,
80 const content::NotificationDetails
& details
) {
81 const GURL url
= GetURLFromWebContents();
89 void ActiveTabTracker::SetBrowser(Browser
* browser
) {
90 if (browser_
== browser
)
95 browser_
->tab_strip_model()->RemoveObserver(this);
96 // Don't track anything for otr profiles.
97 if (browser
&& browser
->profile()->IsOffTheRecord())
100 content::WebContents
* web_contents
= NULL
;
102 TabStripModel
* tab_strip
= browser_
->tab_strip_model();
103 tab_strip
->AddObserver(this);
104 web_contents
= tab_strip
->GetActiveWebContents();
106 idle_state_
= IDLE_STATE_UNKNOWN
;
108 weak_ptr_factory_
.InvalidateWeakPtrs();
110 SetWebContents(web_contents
);
113 void ActiveTabTracker::SetWebContents(content::WebContents
* web_contents
) {
114 if (web_contents_
== web_contents
)
119 active_time_
= base::TimeTicks::Now();
120 web_contents_
= web_contents
;
121 url_
= GetURLFromWebContents();
122 registrar_
.RemoveAll();
123 // TODO(sky): this isn't quite right. We should really not include transient
124 // entries here. For that we need to make Browser::NavigationStateChanged()
125 // call through to this class.
127 registrar_
.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
128 content::Source
<content::NavigationController
>(
129 &web_contents_
->GetController()));
134 void ActiveTabTracker::SetIdleState(IdleState idle_state
) {
135 if (idle_state_
!= idle_state
) {
136 if (idle_state_
== IDLE_STATE_ACTIVE
)
138 else if (idle_state
== IDLE_STATE_ACTIVE
)
139 active_time_
= base::TimeTicks::Now();
140 idle_state_
= idle_state
;
143 timer_
.Start(FROM_HERE
,
144 base::TimeDelta::FromSeconds(kIdleTimeSeconds
),
145 base::Bind(&ActiveTabTracker::QueryIdleState
,
146 base::Unretained(this)));
150 void ActiveTabTracker::QueryIdleState() {
151 if (weak_ptr_factory_
.HasWeakPtrs())
154 CalculateIdleState(kIdleTimeSeconds
,
155 base::Bind(&ActiveTabTracker::SetIdleState
,
156 weak_ptr_factory_
.GetWeakPtr()));
159 GURL
ActiveTabTracker::GetURLFromWebContents() const {
163 // TODO: handle subframe transitions better. Maybe go back to first entry
164 // that isn't a main frame?
165 content::NavigationEntry
* entry
=
166 web_contents_
->GetController().GetLastCommittedEntry();
167 if (!entry
|| !PageTransitionIsMainFrame(entry
->GetTransitionType()))
169 return !entry
->GetUserTypedURL().is_empty() ?
170 entry
->GetUserTypedURL() : entry
->GetURL();
173 void ActiveTabTracker::CommitActiveTime() {
174 const base::TimeDelta active_delta
= base::TimeTicks::Now() - active_time_
;
175 active_time_
= base::TimeTicks::Now();
177 if (!web_contents_
|| url_
.is_empty() || idle_state_
!= IDLE_STATE_ACTIVE
||
178 active_delta
.InMilliseconds() < kTimeBeforeCommitMS
)
181 Profile
* profile
= Profile::FromBrowserContext(
182 web_contents_
->GetBrowserContext());
183 HistoryService
* history
= HistoryServiceFactory::GetForProfile(
184 profile
, Profile::EXPLICIT_ACCESS
);
186 history
->IncreaseSegmentDuration(url_
, base::Time::Now(), active_delta
);