blob: 1cf47e77801bf679db12cb32828875b029b34f0e [file] [log] [blame]
Scott Violet87450ce2020-01-23 01:56:251// Copyright 2020 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.
4
5#include "weblayer/browser/session_service.h"
6
7#include "base/files/file_path.h"
8#include "base/path_service.h"
9#include "base/run_loop.h"
Colin Blundellbe1dc132020-01-29 09:54:0610#include "build/build_config.h"
Scott Violet87450ce2020-01-23 01:56:2511#include "components/sessions/core/command_storage_manager_test_helper.h"
12#include "content/public/test/browser_test_utils.h"
13#include "content/public/test/url_loader_interceptor.h"
14#include "net/base/filename_util.h"
15#include "net/test/embedded_test_server/embedded_test_server.h"
16#include "weblayer/browser/browser_impl.h"
17#include "weblayer/browser/profile_impl.h"
18#include "weblayer/browser/tab_impl.h"
19#include "weblayer/common/weblayer_paths.h"
20#include "weblayer/public/navigation.h"
21#include "weblayer/public/navigation_controller.h"
22#include "weblayer/public/navigation_observer.h"
23#include "weblayer/public/tab.h"
24#include "weblayer/shell/browser/shell.h"
25#include "weblayer/test/interstitial_utils.h"
26#include "weblayer/test/test_navigation_observer.h"
27#include "weblayer/test/weblayer_browser_test.h"
28#include "weblayer/test/weblayer_browser_test_utils.h"
29
30namespace weblayer {
31
32class SessionServiceTestHelper {
33 public:
34 static sessions::CommandStorageManager* GetCommandStorageManager(
35 SessionService* service) {
36 return service->command_storage_manager_.get();
37 }
38};
39
40namespace {
41
42class OneShotNavigationObserver : public NavigationObserver {
43 public:
44 explicit OneShotNavigationObserver(Shell* shell) : tab_(shell->tab()) {
45 tab_->GetNavigationController()->AddObserver(this);
46 }
47
48 ~OneShotNavigationObserver() override {
49 tab_->GetNavigationController()->RemoveObserver(this);
50 }
51
52 void WaitForNavigation() { run_loop_.Run(); }
53
54 bool completed() { return completed_; }
55 bool is_error_page() { return is_error_page_; }
56 Navigation::LoadError load_error() { return load_error_; }
57 int http_status_code() { return http_status_code_; }
58 NavigationState navigation_state() { return navigation_state_; }
59
60 private:
61 // NavigationObserver implementation:
62 void NavigationCompleted(Navigation* navigation) override {
63 completed_ = true;
64 Finish(navigation);
65 }
66
67 void NavigationFailed(Navigation* navigation) override { Finish(navigation); }
68
69 void Finish(Navigation* navigation) {
70 is_error_page_ = navigation->IsErrorPage();
71 load_error_ = navigation->GetLoadError();
72 http_status_code_ = navigation->GetHttpStatusCode();
73 navigation_state_ = navigation->GetState();
74 run_loop_.Quit();
75 }
76
77 base::RunLoop run_loop_;
78 Tab* tab_;
79 bool completed_ = false;
80 bool is_error_page_ = false;
81 Navigation::LoadError load_error_ = Navigation::kNoError;
82 int http_status_code_ = 0;
83 NavigationState navigation_state_ = NavigationState::kWaitingResponse;
84};
85
86class BrowserObserverImpl : public BrowserObserver {
87 public:
88 static void WaitForNewTab(Browser* browser) {
89 BrowserObserverImpl observer(browser);
90 observer.Wait();
91 }
92
93 private:
94 explicit BrowserObserverImpl(Browser* browser) : browser_(browser) {
95 browser_->AddObserver(this);
96 }
97 ~BrowserObserverImpl() override { browser_->RemoveObserver(this); }
98
99 void Wait() { run_loop_.Run(); }
100
101 // BrowserObserver:
102 void OnTabAdded(Tab* tab) override { run_loop_.Quit(); }
103
104 Browser* browser_;
105 base::RunLoop run_loop_;
106};
107
108class BrowserNavigationObserverImpl : public BrowserObserver,
109 public NavigationObserver {
110 public:
111 static void WaitForNewTabToCompleteNavigation(Browser* browser,
112 const GURL& url,
113 int tab_to_wait_for = 1) {
114 BrowserNavigationObserverImpl observer(browser, url, tab_to_wait_for);
115 observer.Wait();
116 }
117
118 private:
119 BrowserNavigationObserverImpl(Browser* browser,
120 const GURL& url,
121 int tab_to_wait_for)
122 : browser_(browser), url_(url), tab_to_wait_for_(tab_to_wait_for) {
123 browser_->AddObserver(this);
124 }
125 ~BrowserNavigationObserverImpl() override {
126 tab_->GetNavigationController()->RemoveObserver(this);
127 }
128
129 void Wait() { run_loop_.Run(); }
130
131 // NavigationObserver;
132 void NavigationCompleted(Navigation* navigation) override {
133 if (navigation->GetURL() == url_)
134 run_loop_.Quit();
135 }
136
137 // BrowserObserver:
138 void OnTabAdded(Tab* tab) override {
139 if (--tab_to_wait_for_ != 0)
140 return;
141
142 browser_->RemoveObserver(this);
143 tab_ = tab;
144 tab_->GetNavigationController()->AddObserver(this);
145 }
146
147 Browser* browser_;
148 const GURL& url_;
149 Tab* tab_ = nullptr;
150 int tab_to_wait_for_;
151 std::unique_ptr<TestNavigationObserver> navigation_observer_;
152 base::RunLoop run_loop_;
153};
154
155void ShutdownSessionServiceAndWait(BrowserImpl* browser) {
156 auto task_runner = sessions::CommandStorageManagerTestHelper(
157 SessionServiceTestHelper::GetCommandStorageManager(
158 browser->session_service()))
159 .GetBackendTaskRunner();
160 browser->PrepareForShutdown();
161 base::RunLoop run_loop;
162 task_runner->PostTaskAndReply(FROM_HERE, base::DoNothing(),
163 run_loop.QuitClosure());
164 run_loop.Run();
165}
166
Scott Violetbf8b8aa72020-01-28 19:37:32167std::unique_ptr<BrowserImpl> CreateBrowser(ProfileImpl* profile,
168 const std::string& persistence_id) {
169 Browser::PersistenceInfo info;
170 info.id = persistence_id;
171 return std::make_unique<BrowserImpl>(profile, &info);
172}
173
Scott Violet87450ce2020-01-23 01:56:25174} // namespace
175
176using SessionServiceTest = WebLayerBrowserTest;
177
178IN_PROC_BROWSER_TEST_F(SessionServiceTest, SingleTab) {
179 ASSERT_TRUE(embedded_test_server()->Start());
180
Scott Violetbf8b8aa72020-01-28 19:37:32181 std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
Scott Violet87450ce2020-01-23 01:56:25182 std::unique_ptr<Tab> tab = Tab::Create(GetProfile());
183 browser->AddTab(tab.get());
184 const GURL url = embedded_test_server()->GetURL("/simple_page.html");
185 NavigateAndWaitForCompletion(url, tab.get());
186 ShutdownSessionServiceAndWait(browser.get());
187 tab.reset();
188 browser.reset();
189
Scott Violetbf8b8aa72020-01-28 19:37:32190 browser = CreateBrowser(GetProfile(), "x");
Scott Violet87450ce2020-01-23 01:56:25191 // Should be no tabs while waiting for restore.
192 EXPECT_TRUE(browser->GetTabs().empty());
193 // Wait for the restore and navigation to complete.
194 BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
195 browser.get(), url);
196
197 ASSERT_EQ(1u, browser->GetTabs().size());
198 EXPECT_EQ(browser->GetTabs()[0], browser->GetActiveTab());
199 EXPECT_EQ(1, browser->GetTabs()[0]
200 ->GetNavigationController()
201 ->GetNavigationListSize());
Colin Blundellbe1dc132020-01-29 09:54:06202
203 // A DCHECK goes off in //content if the Tab created via session restore
204 // leaks at shutdown.
205 // TODO(crbug.com/1046406): Rationalize the ownership model here.
206 Tab* restored_tab = browser->GetTabs()[0];
207 delete restored_tab;
Scott Violet87450ce2020-01-23 01:56:25208}
209
210IN_PROC_BROWSER_TEST_F(SessionServiceTest, TwoTabs) {
211 ASSERT_TRUE(embedded_test_server()->Start());
212
Scott Violetbf8b8aa72020-01-28 19:37:32213 std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
Scott Violet87450ce2020-01-23 01:56:25214 std::unique_ptr<Tab> tab1 = Tab::Create(GetProfile());
215 browser->AddTab(tab1.get());
216 const GURL url1 = embedded_test_server()->GetURL("/simple_page.html");
217 NavigateAndWaitForCompletion(url1, tab1.get());
218
219 std::unique_ptr<Tab> tab2 = Tab::Create(GetProfile());
220 browser->AddTab(tab2.get());
221 const GURL url2 = embedded_test_server()->GetURL("/simple_page2.html");
222 NavigateAndWaitForCompletion(url2, tab2.get());
223 browser->SetActiveTab(tab2.get());
224
Colin Blundellbe1dc132020-01-29 09:54:06225 // Shut down the service.
226 ShutdownSessionServiceAndWait(browser.get());
227 tab1.reset();
228 tab2.reset();
229 browser.reset();
230
231 // Recreate the browser and run the assertions twice to ensure we handle
Scott Violet87450ce2020-01-23 01:56:25232 // correctly storing state of tabs that need to be reloaded.
233 for (int i = 0; i < 2; ++i) {
Scott Violetbf8b8aa72020-01-28 19:37:32234 browser = CreateBrowser(GetProfile(), "x");
Scott Violet87450ce2020-01-23 01:56:25235 // Should be no tabs while waiting for restore.
236 EXPECT_TRUE(browser->GetTabs().empty()) << "iteration " << i;
237 // Wait for the restore and navigation to complete. This waits for the
238 // second tab as that was the active one.
239 BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
240 browser.get(), url2, 2);
241
242 ASSERT_EQ(2u, browser->GetTabs().size()) << "iteration " << i;
243 // The first tab shouldn't have loaded yet, as it's not active.
244 EXPECT_TRUE(static_cast<TabImpl*>(browser->GetTabs()[0])
245 ->web_contents()
246 ->GetController()
247 .NeedsReload())
248 << "iteration " << i;
249 EXPECT_EQ(browser->GetTabs()[1], browser->GetActiveTab())
250 << "iteration " << i;
251 EXPECT_EQ(1, browser->GetTabs()[1]
252 ->GetNavigationController()
253 ->GetNavigationListSize())
254 << "iteration " << i;
Colin Blundellbe1dc132020-01-29 09:54:06255
256 ShutdownSessionServiceAndWait(browser.get());
257
258 // A DCHECK goes off in //content if the Tabs created via session restore
259 // leak at shutdown.
260 // TODO(crbug.com/1046406): Rationalize the ownership model here.
261 Tab* restored_tab_1 = browser->GetTabs()[0];
262 Tab* restored_tab_2 = browser->GetTabs()[1];
263 delete restored_tab_1;
264 delete restored_tab_2;
265
266 browser.reset();
Scott Violet87450ce2020-01-23 01:56:25267 }
268}
269
270IN_PROC_BROWSER_TEST_F(SessionServiceTest, MoveBetweenBrowsers) {
271 ASSERT_TRUE(embedded_test_server()->Start());
272
273 // Create a browser with two tabs.
Scott Violetbf8b8aa72020-01-28 19:37:32274 std::unique_ptr<BrowserImpl> browser1 = CreateBrowser(GetProfile(), "x");
Scott Violet87450ce2020-01-23 01:56:25275 std::unique_ptr<Tab> tab1 = Tab::Create(GetProfile());
276 browser1->AddTab(tab1.get());
277 const GURL url1 = embedded_test_server()->GetURL("/simple_page.html");
278 NavigateAndWaitForCompletion(url1, tab1.get());
279
280 std::unique_ptr<Tab> tab2 = Tab::Create(GetProfile());
281 browser1->AddTab(tab2.get());
282 const GURL url2 = embedded_test_server()->GetURL("/simple_page2.html");
283 NavigateAndWaitForCompletion(url2, tab2.get());
284 browser1->SetActiveTab(tab2.get());
285
286 // Create another browser with a single tab.
Scott Violetbf8b8aa72020-01-28 19:37:32287 std::unique_ptr<BrowserImpl> browser2 = CreateBrowser(GetProfile(), "y");
Scott Violet87450ce2020-01-23 01:56:25288 std::unique_ptr<Tab> tab3 = Tab::Create(GetProfile());
289 browser2->AddTab(tab3.get());
290 const GURL url3 = embedded_test_server()->GetURL("/simple_page3.html");
291 NavigateAndWaitForCompletion(url3, tab3.get());
292
293 // Move |tab2| to |browser2|.
294 browser2->AddTab(tab2.get());
295 browser2->SetActiveTab(tab2.get());
296
297 ShutdownSessionServiceAndWait(browser1.get());
298 ShutdownSessionServiceAndWait(browser2.get());
299 tab1.reset();
300 browser1.reset();
301
302 tab2.reset();
303 tab3.reset();
304 browser2.reset();
305
306 // Restore the browsers.
Scott Violetbf8b8aa72020-01-28 19:37:32307 browser1 = CreateBrowser(GetProfile(), "x");
Scott Violet87450ce2020-01-23 01:56:25308 BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
309 browser1.get(), url1, 1);
310 ASSERT_EQ(1u, browser1->GetTabs().size());
311 EXPECT_EQ(1, browser1->GetTabs()[0]
312 ->GetNavigationController()
313 ->GetNavigationListSize());
314
Scott Violetbf8b8aa72020-01-28 19:37:32315 browser2 = CreateBrowser(GetProfile(), "y");
Scott Violet87450ce2020-01-23 01:56:25316 BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
317 browser2.get(), url2, 2);
318 ASSERT_EQ(2u, browser2->GetTabs().size());
319 EXPECT_EQ(1, browser2->GetTabs()[1]
320 ->GetNavigationController()
321 ->GetNavigationListSize());
322
323 // As |tab3| isn't active it needs to be loaded. Force that now.
324 TabImpl* restored_tab_3 = static_cast<TabImpl*>(browser2->GetTabs()[0]);
325 EXPECT_TRUE(restored_tab_3->web_contents()->GetController().NeedsReload());
326 restored_tab_3->web_contents()->GetController().LoadIfNecessary();
327 content::WaitForLoadStop(restored_tab_3->web_contents());
Colin Blundellbe1dc132020-01-29 09:54:06328
329 // A DCHECK goes off in //content if the Tabs created via session restore
330 // leak at shutdown.
331 // TODO(crbug.com/1046406): Rationalize the ownership model here.
332 Tab* restored_tab_1 = browser1->GetTabs()[0];
333 Tab* restored_tab_2 = browser2->GetTabs()[1];
334 delete restored_tab_1;
335 delete restored_tab_2;
336 delete restored_tab_3;
Scott Violet87450ce2020-01-23 01:56:25337}
338
339} // namespace weblayer