BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / sessions / tab_loader.cc
blob05ec0bf63917a9eefdf20cc9b7dd24eb76246ff9
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 #if defined(OS_WIN)
26 #include "base/memory/memory_pressure_monitor_win.h"
27 #elif defined(OS_MACOSX) && !defined(OS_IOS)
28 #include "base/memory/memory_pressure_monitor_mac.h"
29 #endif
31 using content::NavigationController;
32 using content::RenderWidgetHost;
33 using content::WebContents;
35 void TabLoader::Observe(int type,
36 const content::NotificationSource& source,
37 const content::NotificationDetails& details) {
38 switch (type) {
39 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
40 WebContents* web_contents = content::Source<WebContents>(source).ptr();
41 HandleTabClosedOrLoaded(&web_contents->GetController());
42 break;
44 case content::NOTIFICATION_LOAD_STOP: {
45 NavigationController* controller =
46 content::Source<NavigationController>(source).ptr();
47 HandleTabClosedOrLoaded(controller);
48 break;
50 default:
51 NOTREACHED() << "Unknown notification received:" << type;
53 // Delete ourselves when we are done.
54 if (tabs_loading_.empty() && tabs_to_load_.empty())
55 this_retainer_ = nullptr;
58 void TabLoader::SetTabLoadingEnabled(bool enable_tab_loading) {
59 // TODO(chrisha): Make the SessionRestoreStatsCollector aware that tab loading
60 // was explicitly stopped or restarted. This can make be used to invalidate
61 // various metrics.
62 if (enable_tab_loading == loading_enabled_)
63 return;
64 loading_enabled_ = enable_tab_loading;
65 if (loading_enabled_) {
66 LoadNextTab();
67 } else {
68 force_load_timer_.Stop();
72 // static
73 void TabLoader::RestoreTabs(const std::vector<RestoredTab>& tabs,
74 const base::TimeTicks& restore_started) {
75 if (!shared_tab_loader_)
76 shared_tab_loader_ = new TabLoader(restore_started);
78 shared_tab_loader_->stats_collector_->TrackTabs(tabs);
79 shared_tab_loader_->StartLoading(tabs);
82 TabLoader::TabLoader(base::TimeTicks restore_started)
83 : memory_pressure_listener_(
84 base::Bind(&TabLoader::OnMemoryPressure, base::Unretained(this))),
85 force_load_delay_multiplier_(1),
86 loading_enabled_(true),
87 restore_started_(restore_started) {
88 stats_collector_ = new SessionRestoreStatsCollector(
89 restore_started,
90 make_scoped_ptr(
91 new SessionRestoreStatsCollector::UmaStatsReportingDelegate()));
92 shared_tab_loader_ = this;
93 this_retainer_ = this;
96 TabLoader::~TabLoader() {
97 DCHECK(tabs_loading_.empty() && tabs_to_load_.empty());
98 DCHECK(shared_tab_loader_ == this);
99 shared_tab_loader_ = nullptr;
102 void TabLoader::StartLoading(const std::vector<RestoredTab>& tabs) {
103 // Add the tabs to the list of tabs loading/to load and register them for
104 // notifications. Also, restore the favicons of the background tabs (the title
105 // has already been set by now).This avoids having blank icons in case the
106 // restore is halted due to memory pressure. Also, when multiple tabs are
107 // restored to a single window, the title may not appear, and the user will
108 // have no way of finding out which tabs corresponds to which page if the icon
109 // is a generic grey one.
110 for (auto& restored_tab : tabs) {
111 if (!restored_tab.is_active()) {
112 tabs_to_load_.push_back(&restored_tab.contents()->GetController());
113 favicon::ContentFaviconDriver* favicon_driver =
114 favicon::ContentFaviconDriver::FromWebContents(
115 restored_tab.contents());
116 favicon_driver->FetchFavicon(favicon_driver->GetActiveURL());
117 } else {
118 tabs_loading_.insert(&restored_tab.contents()->GetController());
120 RegisterForNotifications(&restored_tab.contents()->GetController());
123 // When multiple profiles are using the same TabLoader, another profile might
124 // already have started loading. In that case, the tabs scheduled for loading
125 // by this profile are already in the loading queue, and they will get loaded
126 // eventually.
127 if (delegate_)
128 return;
130 // Create a TabLoaderDelegate which will allow OS specific behavior for tab
131 // loading.
132 if (!delegate_) {
133 delegate_ = TabLoaderDelegate::Create(this);
134 // There is already at least one tab loading (the active tab). As such we
135 // only have to start the timeout timer here. But, don't restore background
136 // tabs if the system is under memory pressure.
137 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level =
138 CurrentMemoryPressureLevel();
140 if (memory_pressure_level !=
141 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
142 OnMemoryPressure(memory_pressure_level);
143 return;
146 StartFirstTimer();
150 void TabLoader::LoadNextTab() {
151 // LoadNextTab should only get called after we have started the tab
152 // loading.
153 CHECK(delegate_);
154 if (!tabs_to_load_.empty()) {
155 NavigationController* controller = tabs_to_load_.front();
156 DCHECK(controller);
157 tabs_loading_.insert(controller);
158 tabs_to_load_.pop_front();
159 controller->LoadIfNecessary();
160 content::WebContents* contents = controller->GetWebContents();
161 if (contents) {
162 Browser* browser = chrome::FindBrowserWithWebContents(contents);
163 if (browser &&
164 browser->tab_strip_model()->GetActiveWebContents() != contents) {
165 // By default tabs are marked as visible. As only the active tab is
166 // visible we need to explicitly tell non-active tabs they are hidden.
167 // Without this call non-active tabs are not marked as backgrounded.
169 // NOTE: We need to do this here rather than when the tab is added to
170 // the Browser as at that time not everything has been created, so that
171 // the call would do nothing.
172 contents->WasHidden();
177 if (!tabs_to_load_.empty())
178 StartTimer();
181 void TabLoader::StartFirstTimer() {
182 force_load_timer_.Stop();
183 force_load_timer_.Start(FROM_HERE,
184 delegate_->GetFirstTabLoadingTimeout(),
185 this, &TabLoader::ForceLoadTimerFired);
188 void TabLoader::StartTimer() {
189 force_load_timer_.Stop();
190 force_load_timer_.Start(FROM_HERE,
191 delegate_->GetTimeoutBeforeLoadingNextTab() *
192 force_load_delay_multiplier_,
193 this, &TabLoader::ForceLoadTimerFired);
196 void TabLoader::RemoveTab(NavigationController* controller) {
197 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
198 content::Source<WebContents>(controller->GetWebContents()));
199 registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP,
200 content::Source<NavigationController>(controller));
202 TabsLoading::iterator i = tabs_loading_.find(controller);
203 if (i != tabs_loading_.end())
204 tabs_loading_.erase(i);
206 TabsToLoad::iterator j =
207 find(tabs_to_load_.begin(), tabs_to_load_.end(), controller);
208 if (j != tabs_to_load_.end())
209 tabs_to_load_.erase(j);
212 void TabLoader::ForceLoadTimerFired() {
213 force_load_delay_multiplier_ *= 2;
214 LoadNextTab();
217 void TabLoader::RegisterForNotifications(NavigationController* controller) {
218 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
219 content::Source<WebContents>(controller->GetWebContents()));
220 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
221 content::Source<NavigationController>(controller));
224 void TabLoader::HandleTabClosedOrLoaded(NavigationController* controller) {
225 RemoveTab(controller);
226 if (delegate_ && loading_enabled_)
227 LoadNextTab();
230 base::MemoryPressureListener::MemoryPressureLevel
231 TabLoader::CurrentMemoryPressureLevel() {
232 #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
233 // Check for explicit memory pressure integration.
234 std::string react_to_memory_pressure = variations::GetVariationParamValue(
235 "IntelligentSessionRestore", "ReactToMemoryPressure");
236 if (react_to_memory_pressure == "true") {
237 #if defined(OS_WIN)
238 return base::win::MemoryPressureMonitor::Get()->GetCurrentPressureLevel();
239 #else
240 return base::mac::MemoryPressureMonitor::Get()->GetCurrentPressureLevel();
241 #endif
243 #endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
245 return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
248 void TabLoader::OnMemoryPressure(
249 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
250 // On Windows and Mac this mechanism is only experimentally enabled.
251 #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
252 // If memory pressure integration isn't explicitly enabled then ignore these
253 // calls.
254 std::string react_to_memory_pressure = variations::GetVariationParamValue(
255 "IntelligentSessionRestore", "ReactToMemoryPressure");
256 if (react_to_memory_pressure != "true")
257 return;
258 #endif
260 // When receiving a resource pressure level warning, we stop pre-loading more
261 // tabs since we are running in danger of loading more tabs by throwing out
262 // old ones.
263 if (tabs_to_load_.empty())
264 return;
265 // Stop the timer and suppress any tab loads while we clean the list.
266 SetTabLoadingEnabled(false);
267 while (!tabs_to_load_.empty()) {
268 NavigationController* tab = tabs_to_load_.front();
269 tabs_to_load_.pop_front();
270 RemoveTab(tab);
272 // Notify the stats collector that a tab's loading has been deferred due to
273 // memory pressure.
274 stats_collector_->DeferTab(tab);
276 // By calling |LoadNextTab| explicitly, we make sure that the
277 // |NOTIFICATION_SESSION_RESTORE_DONE| event gets sent.
278 LoadNextTab();
281 // static
282 TabLoader* TabLoader::shared_tab_loader_ = nullptr;