ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / sessions / tab_loader.cc
blob0bff726d5ce68e67a3b9e632882622e1daa0fc0e
1 // Copyright 2015 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/sessions/tab_loader.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/metrics/histogram.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/browser/sessions/session_restore_stats_collector.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_finder.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "components/favicon/content/content_favicon_driver.h"
17 #include "components/variations/variations_associated_data.h"
18 #include "content/public/browser/navigation_controller.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_types.h"
21 #include "content/public/browser/render_widget_host.h"
22 #include "content/public/browser/render_widget_host_view.h"
23 #include "content/public/browser/web_contents.h"
25 using content::NavigationController;
26 using content::RenderWidgetHost;
27 using content::WebContents;
29 void TabLoader::Observe(int type,
30 const content::NotificationSource& source,
31 const content::NotificationDetails& details) {
32 switch (type) {
33 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
34 WebContents* web_contents = content::Source<WebContents>(source).ptr();
35 HandleTabClosedOrLoaded(&web_contents->GetController());
36 break;
38 case content::NOTIFICATION_LOAD_STOP: {
39 NavigationController* controller =
40 content::Source<NavigationController>(source).ptr();
41 HandleTabClosedOrLoaded(controller);
42 break;
44 default:
45 NOTREACHED() << "Unknown notification received:" << type;
47 // Delete ourselves when we are done.
48 if (tabs_loading_.empty() && tabs_to_load_.empty())
49 this_retainer_ = nullptr;
52 void TabLoader::SetTabLoadingEnabled(bool enable_tab_loading) {
53 // TODO(chrisha): Make the SessionRestoreStatsCollector aware that tab loading
54 // was explicitly stopped or restarted. This can make be used to invalidate
55 // various metrics.
56 if (enable_tab_loading == loading_enabled_)
57 return;
58 loading_enabled_ = enable_tab_loading;
59 if (loading_enabled_) {
60 LoadNextTab();
61 } else {
62 force_load_timer_.Stop();
66 // static
67 void TabLoader::RestoreTabs(const std::vector<RestoredTab>& tabs,
68 const base::TimeTicks& restore_started) {
69 if (!shared_tab_loader_)
70 shared_tab_loader_ = new TabLoader(restore_started);
72 shared_tab_loader_->stats_collector_->TrackTabs(tabs);
73 shared_tab_loader_->StartLoading(tabs);
76 TabLoader::TabLoader(base::TimeTicks restore_started)
77 : memory_pressure_listener_(
78 base::Bind(&TabLoader::OnMemoryPressure, base::Unretained(this))),
79 force_load_delay_multiplier_(1),
80 loading_enabled_(true),
81 restore_started_(restore_started) {
82 stats_collector_ = new SessionRestoreStatsCollector(
83 restore_started,
84 make_scoped_ptr(
85 new SessionRestoreStatsCollector::UmaStatsReportingDelegate()));
86 shared_tab_loader_ = this;
87 this_retainer_ = this;
90 TabLoader::~TabLoader() {
91 DCHECK(tabs_loading_.empty() && tabs_to_load_.empty());
92 DCHECK(shared_tab_loader_ == this);
93 shared_tab_loader_ = nullptr;
96 void TabLoader::StartLoading(const std::vector<RestoredTab>& tabs) {
97 // Add the tabs to the list of tabs loading/to load and register them for
98 // notifications. Also, restore the favicons of the background tabs (the title
99 // has already been set by now).This avoids having blank icons in case the
100 // restore is halted due to memory pressure. Also, when multiple tabs are
101 // restored to a single window, the title may not appear, and the user will
102 // have no way of finding out which tabs corresponds to which page if the icon
103 // is a generic grey one.
104 for (auto& restored_tab : tabs) {
105 if (!restored_tab.is_active()) {
106 tabs_to_load_.push_back(&restored_tab.contents()->GetController());
107 favicon::ContentFaviconDriver* favicon_driver =
108 favicon::ContentFaviconDriver::FromWebContents(
109 restored_tab.contents());
110 favicon_driver->FetchFavicon(favicon_driver->GetActiveURL());
111 } else {
112 tabs_loading_.insert(&restored_tab.contents()->GetController());
114 RegisterForNotifications(&restored_tab.contents()->GetController());
117 // When multiple profiles are using the same TabLoader, another profile might
118 // already have started loading. In that case, the tabs scheduled for loading
119 // by this profile are already in the loading queue, and they will get loaded
120 // eventually.
121 if (delegate_)
122 return;
124 // Create a TabLoaderDelegate which will allow OS specific behavior for tab
125 // loading.
126 if (!delegate_) {
127 delegate_ = TabLoaderDelegate::Create(this);
128 // There is already at least one tab loading (the active tab). As such we
129 // only have to start the timeout timer here.
130 StartFirstTimer();
134 void TabLoader::LoadNextTab() {
135 // LoadNextTab should only get called after we have started the tab
136 // loading.
137 CHECK(delegate_);
138 if (!tabs_to_load_.empty()) {
139 NavigationController* controller = tabs_to_load_.front();
140 DCHECK(controller);
141 tabs_loading_.insert(controller);
142 tabs_to_load_.pop_front();
143 controller->LoadIfNecessary();
144 content::WebContents* contents = controller->GetWebContents();
145 if (contents) {
146 Browser* browser = chrome::FindBrowserWithWebContents(contents);
147 if (browser &&
148 browser->tab_strip_model()->GetActiveWebContents() != contents) {
149 // By default tabs are marked as visible. As only the active tab is
150 // visible we need to explicitly tell non-active tabs they are hidden.
151 // Without this call non-active tabs are not marked as backgrounded.
153 // NOTE: We need to do this here rather than when the tab is added to
154 // the Browser as at that time not everything has been created, so that
155 // the call would do nothing.
156 contents->WasHidden();
161 if (!tabs_to_load_.empty())
162 StartTimer();
165 void TabLoader::StartFirstTimer() {
166 force_load_timer_.Stop();
167 force_load_timer_.Start(FROM_HERE,
168 delegate_->GetFirstTabLoadingTimeout(),
169 this, &TabLoader::ForceLoadTimerFired);
172 void TabLoader::StartTimer() {
173 force_load_timer_.Stop();
174 force_load_timer_.Start(FROM_HERE,
175 delegate_->GetTimeoutBeforeLoadingNextTab() *
176 force_load_delay_multiplier_,
177 this, &TabLoader::ForceLoadTimerFired);
180 void TabLoader::RemoveTab(NavigationController* controller) {
181 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
182 content::Source<WebContents>(controller->GetWebContents()));
183 registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP,
184 content::Source<NavigationController>(controller));
186 TabsLoading::iterator i = tabs_loading_.find(controller);
187 if (i != tabs_loading_.end())
188 tabs_loading_.erase(i);
190 TabsToLoad::iterator j =
191 find(tabs_to_load_.begin(), tabs_to_load_.end(), controller);
192 if (j != tabs_to_load_.end())
193 tabs_to_load_.erase(j);
196 void TabLoader::ForceLoadTimerFired() {
197 force_load_delay_multiplier_ *= 2;
198 LoadNextTab();
201 void TabLoader::RegisterForNotifications(NavigationController* controller) {
202 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
203 content::Source<WebContents>(controller->GetWebContents()));
204 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
205 content::Source<NavigationController>(controller));
208 void TabLoader::HandleTabClosedOrLoaded(NavigationController* controller) {
209 RemoveTab(controller);
210 if (delegate_ && loading_enabled_)
211 LoadNextTab();
214 void TabLoader::OnMemoryPressure(
215 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
216 // On Windows and Mac this mechanism is only experimentally enabled.
217 #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
218 // If memory pressure integration isn't explicitly enabled then ignore these
219 // calls.
220 std::string react_to_memory_pressure = variations::GetVariationParamValue(
221 "IntelligentSessionRestore", "ReactToMemoryPressure");
222 if (react_to_memory_pressure != "true")
223 return;
224 #endif
226 // When receiving a resource pressure level warning, we stop pre-loading more
227 // tabs since we are running in danger of loading more tabs by throwing out
228 // old ones.
229 if (tabs_to_load_.empty())
230 return;
231 // Stop the timer and suppress any tab loads while we clean the list.
232 SetTabLoadingEnabled(false);
233 while (!tabs_to_load_.empty()) {
234 NavigationController* tab = tabs_to_load_.front();
235 tabs_to_load_.pop_front();
236 RemoveTab(tab);
238 // Notify the stats collector that a tab's loading has been deferred due to
239 // memory pressure.
240 stats_collector_->DeferTab(tab);
242 // By calling |LoadNextTab| explicitly, we make sure that the
243 // |NOTIFICATION_SESSION_RESTORE_DONE| event gets sent.
244 LoadNextTab();
247 // static
248 TabLoader* TabLoader::shared_tab_loader_ = nullptr;