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"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_finder.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "components/favicon/content/content_favicon_driver.h"
16 #include "components/variations/variations_associated_data.h"
17 #include "content/public/browser/navigation_controller.h"
18 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/notification_types.h"
20 #include "content/public/browser/render_widget_host.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/web_contents.h"
24 using content::NavigationController
;
25 using content::RenderWidgetHost
;
26 using content::WebContents
;
28 void TabLoader::Observe(int type
,
29 const content::NotificationSource
& source
,
30 const content::NotificationDetails
& details
) {
32 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED
: {
33 WebContents
* web_contents
= content::Source
<WebContents
>(source
).ptr();
34 HandleTabClosedOrLoaded(&web_contents
->GetController());
37 case content::NOTIFICATION_LOAD_STOP
: {
38 NavigationController
* controller
=
39 content::Source
<NavigationController
>(source
).ptr();
40 HandleTabClosedOrLoaded(controller
);
44 NOTREACHED() << "Unknown notification received:" << type
;
46 // Delete ourselves when we are done.
47 if (tabs_loading_
.empty() && tabs_to_load_
.empty())
48 this_retainer_
= nullptr;
51 void TabLoader::SetTabLoadingEnabled(bool enable_tab_loading
) {
52 if (enable_tab_loading
== loading_enabled_
)
54 loading_enabled_
= enable_tab_loading
;
58 force_load_timer_
.Stop();
62 void TabLoader::RestoreTabs(const std::vector
<RestoredTab
>& tabs
,
63 const base::TimeTicks
& restore_started
) {
64 if (!shared_tab_loader_
)
65 shared_tab_loader_
= new TabLoader(restore_started
);
67 shared_tab_loader_
->StartLoading(tabs
);
70 TabLoader::TabLoader(base::TimeTicks restore_started
)
71 : memory_pressure_listener_(
72 base::Bind(&TabLoader::OnMemoryPressure
, base::Unretained(this))),
73 force_load_delay_multiplier_(1),
74 loading_enabled_(true),
75 restore_started_(restore_started
) {
76 shared_tab_loader_
= this;
77 this_retainer_
= this;
80 TabLoader::~TabLoader() {
81 DCHECK(tabs_loading_
.empty() && tabs_to_load_
.empty());
82 DCHECK(shared_tab_loader_
== this);
83 shared_tab_loader_
= nullptr;
86 void TabLoader::StartLoading(const std::vector
<RestoredTab
>& tabs
) {
87 // Add the tabs to the list of tabs loading/to load and register them for
88 // notifications. Also, restore the favicons of the background tabs (the title
89 // has already been set by now).This avoids having blank icons in case the
90 // restore is halted due to memory pressure. Also, when multiple tabs are
91 // restored to a single window, the title may not appear, and the user will
92 // have no way of finding out which tabs corresponds to which page if the icon
93 // is a generic grey one.
94 for (auto& restored_tab
: tabs
) {
95 if (!restored_tab
.is_active()) {
96 tabs_to_load_
.push_back(&restored_tab
.contents()->GetController());
97 favicon::ContentFaviconDriver
* favicon_driver
=
98 favicon::ContentFaviconDriver::FromWebContents(
99 restored_tab
.contents());
100 favicon_driver
->FetchFavicon(favicon_driver
->GetActiveURL());
102 tabs_loading_
.insert(&restored_tab
.contents()->GetController());
104 RegisterForNotifications(&restored_tab
.contents()->GetController());
107 // When multiple profiles are using the same TabLoader, another profile might
108 // already have started loading. In that case, the tabs scheduled for loading
109 // by this profile are already in the loading queue, and they will get loaded
114 // Create a TabLoaderDelegate which will allow OS specific behavior for tab
117 delegate_
= TabLoaderDelegate::Create(this);
118 // There is already at least one tab loading (the active tab). As such we
119 // only have to start the timeout timer here.
124 void TabLoader::LoadNextTab() {
125 // LoadNextTab should only get called after we have started the tab
128 if (!tabs_to_load_
.empty()) {
129 NavigationController
* controller
= tabs_to_load_
.front();
131 tabs_loading_
.insert(controller
);
132 tabs_to_load_
.pop_front();
133 controller
->LoadIfNecessary();
134 content::WebContents
* contents
= controller
->GetWebContents();
136 Browser
* browser
= chrome::FindBrowserWithWebContents(contents
);
138 browser
->tab_strip_model()->GetActiveWebContents() != contents
) {
139 // By default tabs are marked as visible. As only the active tab is
140 // visible we need to explicitly tell non-active tabs they are hidden.
141 // Without this call non-active tabs are not marked as backgrounded.
143 // NOTE: We need to do this here rather than when the tab is added to
144 // the Browser as at that time not everything has been created, so that
145 // the call would do nothing.
146 contents
->WasHidden();
151 if (!tabs_to_load_
.empty())
155 void TabLoader::StartFirstTimer() {
156 force_load_timer_
.Stop();
157 force_load_timer_
.Start(FROM_HERE
,
158 delegate_
->GetFirstTabLoadingTimeout(),
159 this, &TabLoader::ForceLoadTimerFired
);
162 void TabLoader::StartTimer() {
163 force_load_timer_
.Stop();
164 force_load_timer_
.Start(FROM_HERE
,
165 delegate_
->GetTimeoutBeforeLoadingNextTab() *
166 force_load_delay_multiplier_
,
167 this, &TabLoader::ForceLoadTimerFired
);
170 void TabLoader::RemoveTab(NavigationController
* controller
) {
171 registrar_
.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
172 content::Source
<WebContents
>(controller
->GetWebContents()));
173 registrar_
.Remove(this, content::NOTIFICATION_LOAD_STOP
,
174 content::Source
<NavigationController
>(controller
));
176 TabsLoading::iterator i
= tabs_loading_
.find(controller
);
177 if (i
!= tabs_loading_
.end())
178 tabs_loading_
.erase(i
);
180 TabsToLoad::iterator j
=
181 find(tabs_to_load_
.begin(), tabs_to_load_
.end(), controller
);
182 if (j
!= tabs_to_load_
.end())
183 tabs_to_load_
.erase(j
);
186 void TabLoader::ForceLoadTimerFired() {
187 force_load_delay_multiplier_
*= 2;
191 void TabLoader::RegisterForNotifications(NavigationController
* controller
) {
192 registrar_
.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
193 content::Source
<WebContents
>(controller
->GetWebContents()));
194 registrar_
.Add(this, content::NOTIFICATION_LOAD_STOP
,
195 content::Source
<NavigationController
>(controller
));
198 void TabLoader::HandleTabClosedOrLoaded(NavigationController
* controller
) {
199 RemoveTab(controller
);
200 if (delegate_
&& loading_enabled_
)
204 void TabLoader::OnMemoryPressure(
205 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level
) {
206 // On Windows and Mac this mechanism is only experimentally enabled.
207 #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
208 // If memory pressure integration isn't explicitly enabled then ignore these
210 std::string react_to_memory_pressure
= variations::GetVariationParamValue(
211 "IntelligentSessionRestore", "ReactToMemoryPressure");
212 if (react_to_memory_pressure
!= "true")
216 // When receiving a resource pressure level warning, we stop pre-loading more
217 // tabs since we are running in danger of loading more tabs by throwing out
219 if (tabs_to_load_
.empty())
221 // Stop the timer and suppress any tab loads while we clean the list.
222 SetTabLoadingEnabled(false);
223 while (!tabs_to_load_
.empty()) {
224 NavigationController
* controller
= tabs_to_load_
.front();
225 tabs_to_load_
.pop_front();
226 RemoveTab(controller
);
228 // By calling |LoadNextTab| explicitly, we make sure that the
229 // |NOTIFICATION_SESSION_RESTORE_DONE| event gets sent.
234 TabLoader
* TabLoader::shared_tab_loader_
= nullptr;