1 // Copyright (c) 2012 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.
6 #include "base/bind_helpers.h"
7 #include "base/files/file_util.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/run_loop.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/defaults.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/sessions/session_backend.h"
22 #include "chrome/browser/sessions/session_service.h"
23 #include "chrome/browser/sessions/session_service_test_helper.h"
24 #include "chrome/browser/sessions/session_types.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "chrome/common/url_constants.h"
27 #include "chrome/test/base/browser_with_test_window_test.h"
28 #include "chrome/test/base/testing_browser_process.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "chrome/test/base/testing_profile_manager.h"
31 #include "components/sessions/serialized_navigation_entry_test_helper.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/notification_observer.h"
34 #include "content/public/browser/notification_registrar.h"
35 #include "content/public/browser/notification_service.h"
36 #include "content/public/common/page_state.h"
37 #include "testing/gtest/include/gtest/gtest.h"
39 using content::NavigationEntry;
40 using sessions::SerializedNavigationEntry;
41 using sessions::SerializedNavigationEntryTestHelper;
43 class SessionServiceTest : public BrowserWithTestWindowTest,
44 public content::NotificationObserver {
46 SessionServiceTest() : window_bounds(0, 1, 2, 3), sync_save_count_(0) {}
49 void SetUp() override {
50 BrowserWithTestWindowTest::SetUp();
52 profile_manager_.reset(
53 new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
54 ASSERT_TRUE(profile_manager_->SetUp());
56 std::string b = base::Int64ToString(base::Time::Now().ToInternalValue());
57 TestingProfile* profile = profile_manager_->CreateTestingProfile(b);
58 SessionService* session_service = new SessionService(profile);
59 path_ = profile->GetPath();
61 helper_.SetService(session_service);
63 service()->SetWindowType(window_id,
65 SessionService::TYPE_NORMAL);
66 service()->SetWindowBounds(window_id,
68 ui::SHOW_STATE_NORMAL);
71 // Upon notification, increment the sync_save_count variable
72 void Observe(int type,
73 const content::NotificationSource& source,
74 const content::NotificationDetails& details) override {
75 ASSERT_EQ(type, chrome::NOTIFICATION_SESSION_SERVICE_SAVED);
79 void TearDown() override {
80 helper_.SetService(NULL);
81 BrowserWithTestWindowTest::TearDown();
84 void UpdateNavigation(
85 const SessionID& window_id,
86 const SessionID& tab_id,
87 const SerializedNavigationEntry& navigation,
89 service()->UpdateTabNavigation(window_id, tab_id, navigation);
91 service()->SetSelectedNavigationIndex(
92 window_id, tab_id, navigation.index());
96 void ReadWindows(std::vector<SessionWindow*>* windows,
97 SessionID::id_type* active_window_id) {
98 // Forces closing the file.
99 helper_.SetService(NULL);
101 SessionService* session_service = new SessionService(path_);
102 helper_.SetService(session_service);
104 SessionID::id_type* non_null_active_window_id = active_window_id;
105 SessionID::id_type dummy_active_window_id = 0;
106 if (!non_null_active_window_id)
107 non_null_active_window_id = &dummy_active_window_id;
108 helper_.ReadWindows(windows, non_null_active_window_id);
111 // Configures the session service with one window with one tab and a single
112 // navigation. If |pinned_state| is true or |write_always| is true, the
113 // pinned state of the tab is updated. The session service is then recreated
114 // and the pinned state of the read back tab is returned.
115 bool CreateAndWriteSessionWithOneTab(bool pinned_state, bool write_always) {
117 SerializedNavigationEntry nav1 =
118 SerializedNavigationEntryTestHelper::CreateNavigation(
119 "http://google.com", "abc");
121 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
122 UpdateNavigation(window_id, tab_id, nav1, true);
124 if (pinned_state || write_always)
125 helper_.service()->SetPinnedState(window_id, tab_id, pinned_state);
127 ScopedVector<SessionWindow> windows;
128 ReadWindows(&(windows.get()), NULL);
130 EXPECT_EQ(1U, windows.size());
131 if (HasFatalFailure())
133 EXPECT_EQ(1U, windows[0]->tabs.size());
134 if (HasFatalFailure())
137 SessionTab* tab = windows[0]->tabs[0];
138 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
143 void CreateAndWriteSessionWithTwoWindows(
144 const SessionID& window2_id,
145 const SessionID& tab1_id,
146 const SessionID& tab2_id,
147 SerializedNavigationEntry* nav1,
148 SerializedNavigationEntry* nav2) {
149 *nav1 = SerializedNavigationEntryTestHelper::CreateNavigation(
150 "http://google.com", "abc");
151 *nav2 = SerializedNavigationEntryTestHelper::CreateNavigation(
152 "http://google2.com", "abcd");
154 helper_.PrepareTabInWindow(window_id, tab1_id, 0, true);
155 UpdateNavigation(window_id, tab1_id, *nav1, true);
157 const gfx::Rect window2_bounds(3, 4, 5, 6);
158 service()->SetWindowType(window2_id,
159 Browser::TYPE_TABBED,
160 SessionService::TYPE_NORMAL);
161 service()->SetWindowBounds(window2_id,
163 ui::SHOW_STATE_MAXIMIZED);
164 helper_.PrepareTabInWindow(window2_id, tab2_id, 0, true);
165 UpdateNavigation(window2_id, tab2_id, *nav2, true);
168 SessionService* service() { return helper_.service(); }
170 const gfx::Rect window_bounds;
174 int sync_save_count_;
176 // Path used in testing.
177 base::ScopedTempDir temp_dir_;
178 base::FilePath path_;
180 SessionServiceTestHelper helper_;
181 scoped_ptr<TestingProfileManager> profile_manager_;
184 TEST_F(SessionServiceTest, Basic) {
186 ASSERT_NE(window_id.id(), tab_id.id());
188 SerializedNavigationEntry nav1 =
189 SerializedNavigationEntryTestHelper::CreateNavigation(
190 "http://google.com", "abc");
191 SerializedNavigationEntryTestHelper::SetOriginalRequestURL(
192 GURL("http://original.request.com"), &nav1);
194 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
195 UpdateNavigation(window_id, tab_id, nav1, true);
197 ScopedVector<SessionWindow> windows;
198 ReadWindows(&(windows.get()), NULL);
200 ASSERT_EQ(1U, windows.size());
201 ASSERT_TRUE(window_bounds == windows[0]->bounds);
202 ASSERT_EQ(0, windows[0]->selected_tab_index);
203 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
204 ASSERT_EQ(1U, windows[0]->tabs.size());
205 ASSERT_EQ(SessionWindow::TYPE_TABBED, windows[0]->type);
207 SessionTab* tab = windows[0]->tabs[0];
208 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
210 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
213 // Make sure we persist post entries.
214 TEST_F(SessionServiceTest, PersistPostData) {
216 ASSERT_NE(window_id.id(), tab_id.id());
218 SerializedNavigationEntry nav1 =
219 SerializedNavigationEntryTestHelper::CreateNavigation(
220 "http://google.com", "abc");
221 SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1);
223 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
224 UpdateNavigation(window_id, tab_id, nav1, true);
226 ScopedVector<SessionWindow> windows;
227 ReadWindows(&(windows.get()), NULL);
229 helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
232 TEST_F(SessionServiceTest, ClosingTabStaysClosed) {
235 ASSERT_NE(tab_id.id(), tab2_id.id());
237 SerializedNavigationEntry nav1 =
238 SerializedNavigationEntryTestHelper::CreateNavigation(
239 "http://google.com", "abc");
240 SerializedNavigationEntry nav2 =
241 SerializedNavigationEntryTestHelper::CreateNavigation(
242 "http://google2.com", "abcd");
244 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
245 UpdateNavigation(window_id, tab_id, nav1, true);
247 helper_.PrepareTabInWindow(window_id, tab2_id, 1, false);
248 UpdateNavigation(window_id, tab2_id, nav2, true);
249 service()->TabClosed(window_id, tab2_id, false);
251 ScopedVector<SessionWindow> windows;
252 ReadWindows(&(windows.get()), NULL);
254 ASSERT_EQ(1U, windows.size());
255 ASSERT_EQ(0, windows[0]->selected_tab_index);
256 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
257 ASSERT_EQ(1U, windows[0]->tabs.size());
259 SessionTab* tab = windows[0]->tabs[0];
260 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
262 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
265 TEST_F(SessionServiceTest, Pruning) {
268 SerializedNavigationEntry nav1 =
269 SerializedNavigationEntryTestHelper::CreateNavigation(
270 "http://google.com", "abc");
271 SerializedNavigationEntry nav2 =
272 SerializedNavigationEntryTestHelper::CreateNavigation(
273 "http://google2.com", "abcd");
275 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
276 for (int i = 0; i < 6; ++i) {
277 SerializedNavigationEntry* nav = (i % 2) == 0 ? &nav1 : &nav2;
279 UpdateNavigation(window_id, tab_id, *nav, true);
281 service()->TabNavigationPathPrunedFromBack(window_id, tab_id, 3);
283 ScopedVector<SessionWindow> windows;
284 ReadWindows(&(windows.get()), NULL);
286 ASSERT_EQ(1U, windows.size());
287 ASSERT_EQ(0, windows[0]->selected_tab_index);
288 ASSERT_EQ(1U, windows[0]->tabs.size());
290 SessionTab* tab = windows[0]->tabs[0];
291 // We left the selected index at 5, then pruned. When rereading the
292 // index should get reset to last valid navigation, which is 2.
293 helper_.AssertTabEquals(window_id, tab_id, 0, 2, 3, *tab);
295 ASSERT_EQ(3u, tab->navigations.size());
296 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
297 helper_.AssertNavigationEquals(nav2, tab->navigations[1]);
298 helper_.AssertNavigationEquals(nav1, tab->navigations[2]);
301 TEST_F(SessionServiceTest, TwoWindows) {
302 SessionID window2_id;
305 SerializedNavigationEntry nav1;
306 SerializedNavigationEntry nav2;
308 CreateAndWriteSessionWithTwoWindows(
309 window2_id, tab1_id, tab2_id, &nav1, &nav2);
311 ScopedVector<SessionWindow> windows;
312 ReadWindows(&(windows.get()), NULL);
314 ASSERT_EQ(2U, windows.size());
315 ASSERT_EQ(0, windows[0]->selected_tab_index);
316 ASSERT_EQ(0, windows[1]->selected_tab_index);
317 ASSERT_EQ(1U, windows[0]->tabs.size());
318 ASSERT_EQ(1U, windows[1]->tabs.size());
322 if (windows[0]->window_id.id() == window_id.id()) {
323 ASSERT_EQ(window2_id.id(), windows[1]->window_id.id());
324 ASSERT_EQ(ui::SHOW_STATE_NORMAL, windows[0]->show_state);
325 ASSERT_EQ(ui::SHOW_STATE_MAXIMIZED, windows[1]->show_state);
326 rt1 = windows[0]->tabs[0];
327 rt2 = windows[1]->tabs[0];
329 ASSERT_EQ(window2_id.id(), windows[0]->window_id.id());
330 ASSERT_EQ(window_id.id(), windows[1]->window_id.id());
331 ASSERT_EQ(ui::SHOW_STATE_MAXIMIZED, windows[0]->show_state);
332 ASSERT_EQ(ui::SHOW_STATE_NORMAL, windows[1]->show_state);
333 rt1 = windows[1]->tabs[0];
334 rt2 = windows[0]->tabs[0];
336 SessionTab* tab = rt1;
337 helper_.AssertTabEquals(window_id, tab1_id, 0, 0, 1, *tab);
338 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
341 helper_.AssertTabEquals(window2_id, tab2_id, 0, 0, 1, *tab);
342 helper_.AssertNavigationEquals(nav2, tab->navigations[0]);
345 TEST_F(SessionServiceTest, WindowWithNoTabsGetsPruned) {
346 SessionID window2_id;
350 SerializedNavigationEntry nav1 =
351 SerializedNavigationEntryTestHelper::CreateNavigation(
352 "http://google.com", "abc");
354 helper_.PrepareTabInWindow(window_id, tab1_id, 0, true);
355 UpdateNavigation(window_id, tab1_id, nav1, true);
357 const gfx::Rect window2_bounds(3, 4, 5, 6);
358 service()->SetWindowType(window2_id,
359 Browser::TYPE_TABBED,
360 SessionService::TYPE_NORMAL);
361 service()->SetWindowBounds(window2_id,
363 ui::SHOW_STATE_NORMAL);
364 helper_.PrepareTabInWindow(window2_id, tab2_id, 0, true);
366 ScopedVector<SessionWindow> windows;
367 ReadWindows(&(windows.get()), NULL);
369 ASSERT_EQ(1U, windows.size());
370 ASSERT_EQ(0, windows[0]->selected_tab_index);
371 ASSERT_EQ(1U, windows[0]->tabs.size());
372 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
374 SessionTab* tab = windows[0]->tabs[0];
375 helper_.AssertTabEquals(window_id, tab1_id, 0, 0, 1, *tab);
376 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
379 TEST_F(SessionServiceTest, ClosingWindowDoesntCloseTabs) {
382 ASSERT_NE(tab_id.id(), tab2_id.id());
384 SerializedNavigationEntry nav1 =
385 SerializedNavigationEntryTestHelper::CreateNavigation(
386 "http://google.com", "abc");
387 SerializedNavigationEntry nav2 =
388 SerializedNavigationEntryTestHelper::CreateNavigation(
389 "http://google2.com", "abcd");
391 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
392 UpdateNavigation(window_id, tab_id, nav1, true);
394 helper_.PrepareTabInWindow(window_id, tab2_id, 1, false);
395 UpdateNavigation(window_id, tab2_id, nav2, true);
397 service()->WindowClosing(window_id);
399 ScopedVector<SessionWindow> windows;
400 ReadWindows(&(windows.get()), NULL);
402 ASSERT_EQ(1U, windows.size());
403 ASSERT_EQ(0, windows[0]->selected_tab_index);
404 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
405 ASSERT_EQ(2U, windows[0]->tabs.size());
407 SessionTab* tab = windows[0]->tabs[0];
408 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
409 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
411 tab = windows[0]->tabs[1];
412 helper_.AssertTabEquals(window_id, tab2_id, 1, 0, 1, *tab);
413 helper_.AssertNavigationEquals(nav2, tab->navigations[0]);
416 TEST_F(SessionServiceTest, LockingWindowRemembersAll) {
417 SessionID window2_id;
420 SerializedNavigationEntry nav1;
421 SerializedNavigationEntry nav2;
423 CreateAndWriteSessionWithTwoWindows(
424 window2_id, tab1_id, tab2_id, &nav1, &nav2);
426 ASSERT_TRUE(service()->profile() != NULL);
427 ASSERT_TRUE(g_browser_process->profile_manager() != NULL);
428 ProfileInfoCache& profile_info =
429 g_browser_process->profile_manager()->GetProfileInfoCache();
430 size_t profile_index = profile_info.GetIndexOfProfileWithPath(
431 service()->profile()->GetPath());
432 ASSERT_NE(std::string::npos, profile_index);
433 profile_info.SetProfileSigninRequiredAtIndex(profile_index, true);
435 service()->WindowClosing(window_id);
436 service()->WindowClosed(window_id);
437 service()->WindowClosing(window2_id);
438 service()->WindowClosed(window2_id);
440 ScopedVector<SessionWindow> windows;
441 ReadWindows(&(windows.get()), NULL);
443 ASSERT_EQ(2U, windows.size());
444 ASSERT_EQ(1U, windows[0]->tabs.size());
445 ASSERT_EQ(1U, windows[1]->tabs.size());
448 TEST_F(SessionServiceTest, WindowCloseCommittedAfterNavigate) {
449 SessionID window2_id;
452 ASSERT_NE(window2_id.id(), window_id.id());
454 service()->SetWindowType(window2_id,
455 Browser::TYPE_TABBED,
456 SessionService::TYPE_NORMAL);
457 service()->SetWindowBounds(window2_id,
459 ui::SHOW_STATE_NORMAL);
461 SerializedNavigationEntry nav1 =
462 SerializedNavigationEntryTestHelper::CreateNavigation(
463 "http://google.com", "abc");
464 SerializedNavigationEntry nav2 =
465 SerializedNavigationEntryTestHelper::CreateNavigation(
466 "http://google2.com", "abcd");
468 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
469 UpdateNavigation(window_id, tab_id, nav1, true);
471 helper_.PrepareTabInWindow(window2_id, tab2_id, 0, false);
472 UpdateNavigation(window2_id, tab2_id, nav2, true);
474 service()->WindowClosing(window2_id);
475 service()->TabClosed(window2_id, tab2_id, false);
476 service()->WindowClosed(window2_id);
478 ScopedVector<SessionWindow> windows;
479 ReadWindows(&(windows.get()), NULL);
481 ASSERT_EQ(1U, windows.size());
482 ASSERT_EQ(0, windows[0]->selected_tab_index);
483 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
484 ASSERT_EQ(1U, windows[0]->tabs.size());
486 SessionTab* tab = windows[0]->tabs[0];
487 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
488 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
491 // Makes sure we don't track popups.
492 TEST_F(SessionServiceTest, IgnorePopups) {
493 SessionID window2_id;
496 ASSERT_NE(window2_id.id(), window_id.id());
498 service()->SetWindowType(window2_id,
500 SessionService::TYPE_NORMAL);
501 service()->SetWindowBounds(window2_id,
503 ui::SHOW_STATE_NORMAL);
505 SerializedNavigationEntry nav1 =
506 SerializedNavigationEntryTestHelper::CreateNavigation(
507 "http://google.com", "abc");
508 SerializedNavigationEntry nav2 =
509 SerializedNavigationEntryTestHelper::CreateNavigation(
510 "http://google2.com", "abcd");
512 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
513 UpdateNavigation(window_id, tab_id, nav1, true);
515 helper_.PrepareTabInWindow(window2_id, tab2_id, 0, false);
516 UpdateNavigation(window2_id, tab2_id, nav2, true);
518 ScopedVector<SessionWindow> windows;
519 ReadWindows(&(windows.get()), NULL);
521 ASSERT_EQ(1U, windows.size());
522 ASSERT_EQ(0, windows[0]->selected_tab_index);
523 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
524 ASSERT_EQ(1U, windows[0]->tabs.size());
526 SessionTab* tab = windows[0]->tabs[0];
527 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
528 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
531 #if defined (OS_CHROMEOS)
532 // Makes sure we track apps. Only applicable on chromeos.
533 TEST_F(SessionServiceTest, RestoreApp) {
534 SessionID window2_id;
537 ASSERT_NE(window2_id.id(), window_id.id());
539 service()->SetWindowType(window2_id,
541 SessionService::TYPE_APP);
542 service()->SetWindowBounds(window2_id,
544 ui::SHOW_STATE_NORMAL);
545 service()->SetWindowAppName(window2_id, "TestApp");
547 SerializedNavigationEntry nav1 =
548 SerializedNavigationEntryTestHelper::CreateNavigation(
549 "http://google.com", "abc");
550 SerializedNavigationEntry nav2 =
551 SerializedNavigationEntryTestHelper::CreateNavigation(
552 "http://google2.com", "abcd");
554 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
555 UpdateNavigation(window_id, tab_id, nav1, true);
557 helper_.PrepareTabInWindow(window2_id, tab2_id, 0, false);
558 UpdateNavigation(window2_id, tab2_id, nav2, true);
560 ScopedVector<SessionWindow> windows;
561 ReadWindows(&(windows.get()), NULL);
563 ASSERT_EQ(2U, windows.size());
564 int tabbed_index = windows[0]->type == SessionWindow::TYPE_TABBED ?
566 int app_index = tabbed_index == 0 ? 1 : 0;
567 ASSERT_EQ(0, windows[tabbed_index]->selected_tab_index);
568 ASSERT_EQ(window_id.id(), windows[tabbed_index]->window_id.id());
569 ASSERT_EQ(1U, windows[tabbed_index]->tabs.size());
571 SessionTab* tab = windows[tabbed_index]->tabs[0];
572 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
573 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
575 ASSERT_EQ(0, windows[app_index]->selected_tab_index);
576 ASSERT_EQ(window2_id.id(), windows[app_index]->window_id.id());
577 ASSERT_EQ(1U, windows[app_index]->tabs.size());
578 ASSERT_TRUE(windows[app_index]->type == SessionWindow::TYPE_POPUP);
579 ASSERT_EQ("TestApp", windows[app_index]->app_name);
581 tab = windows[app_index]->tabs[0];
582 helper_.AssertTabEquals(window2_id, tab2_id, 0, 0, 1, *tab);
583 helper_.AssertNavigationEquals(nav2, tab->navigations[0]);
585 #endif // defined (OS_CHROMEOS)
587 // Tests pruning from the front.
588 TEST_F(SessionServiceTest, PruneFromFront) {
589 const std::string base_url("http://google.com/");
592 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
594 // Add 5 navigations, with the 4th selected.
595 for (int i = 0; i < 5; ++i) {
596 SerializedNavigationEntry nav =
597 SerializedNavigationEntryTestHelper::CreateNavigation(
598 base_url + base::IntToString(i), "a");
600 UpdateNavigation(window_id, tab_id, nav, (i == 3));
603 // Prune the first two navigations from the front.
604 helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 2);
607 ScopedVector<SessionWindow> windows;
608 ReadWindows(&(windows.get()), NULL);
610 ASSERT_EQ(1U, windows.size());
611 ASSERT_EQ(0, windows[0]->selected_tab_index);
612 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
613 ASSERT_EQ(1U, windows[0]->tabs.size());
615 // There shouldn't be an app id.
616 EXPECT_TRUE(windows[0]->tabs[0]->extension_app_id.empty());
618 // We should be left with three navigations, the 2nd selected.
619 SessionTab* tab = windows[0]->tabs[0];
620 ASSERT_EQ(1, tab->current_navigation_index);
621 EXPECT_EQ(3U, tab->navigations.size());
622 EXPECT_TRUE(GURL(base_url + base::IntToString(2)) ==
623 tab->navigations[0].virtual_url());
624 EXPECT_TRUE(GURL(base_url + base::IntToString(3)) ==
625 tab->navigations[1].virtual_url());
626 EXPECT_TRUE(GURL(base_url + base::IntToString(4)) ==
627 tab->navigations[2].virtual_url());
630 // Prunes from front so that we have no entries.
631 TEST_F(SessionServiceTest, PruneToEmpty) {
632 const std::string base_url("http://google.com/");
635 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
637 // Add 5 navigations, with the 4th selected.
638 for (int i = 0; i < 5; ++i) {
639 SerializedNavigationEntry nav =
640 SerializedNavigationEntryTestHelper::CreateNavigation(
641 base_url + base::IntToString(i), "a");
643 UpdateNavigation(window_id, tab_id, nav, (i == 3));
646 // Prune the first two navigations from the front.
647 helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 5);
650 ScopedVector<SessionWindow> windows;
651 ReadWindows(&(windows.get()), NULL);
653 ASSERT_EQ(0U, windows.size());
656 // Don't set the pinned state and make sure the pinned value is false.
657 TEST_F(SessionServiceTest, PinnedDefaultsToFalse) {
658 EXPECT_FALSE(CreateAndWriteSessionWithOneTab(false, false));
661 // Explicitly set the pinned state to false and make sure we get back false.
662 TEST_F(SessionServiceTest, PinnedFalseWhenSetToFalse) {
663 EXPECT_FALSE(CreateAndWriteSessionWithOneTab(false, true));
666 // Explicitly set the pinned state to true and make sure we get back true.
667 TEST_F(SessionServiceTest, PinnedTrue) {
668 EXPECT_TRUE(CreateAndWriteSessionWithOneTab(true, true));
671 // Make sure application extension ids are persisted.
672 TEST_F(SessionServiceTest, PersistApplicationExtensionID) {
674 ASSERT_NE(window_id.id(), tab_id.id());
675 std::string app_id("foo");
677 SerializedNavigationEntry nav1 =
678 SerializedNavigationEntryTestHelper::CreateNavigation(
679 "http://google.com", "abc");
681 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
682 UpdateNavigation(window_id, tab_id, nav1, true);
683 helper_.SetTabExtensionAppID(window_id, tab_id, app_id);
685 ScopedVector<SessionWindow> windows;
686 ReadWindows(&(windows.get()), NULL);
688 helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
689 EXPECT_TRUE(app_id == windows[0]->tabs[0]->extension_app_id);
692 // Check that user agent overrides are persisted.
693 TEST_F(SessionServiceTest, PersistUserAgentOverrides) {
695 ASSERT_NE(window_id.id(), tab_id.id());
696 std::string user_agent_override = "Mozilla/5.0 (X11; Linux x86_64) "
697 "AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.45 "
700 SerializedNavigationEntry nav1 =
701 SerializedNavigationEntryTestHelper::CreateNavigation(
702 "http://google.com", "abc");
703 SerializedNavigationEntryTestHelper::SetIsOverridingUserAgent(true, &nav1);
705 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
706 UpdateNavigation(window_id, tab_id, nav1, true);
707 helper_.SetTabUserAgentOverride(window_id, tab_id, user_agent_override);
709 ScopedVector<SessionWindow> windows;
710 ReadWindows(&(windows.get()), NULL);
711 helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
713 SessionTab* tab = windows[0]->tabs[0];
714 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
715 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
716 EXPECT_TRUE(user_agent_override == tab->user_agent_override);
719 // Test that the notification for SESSION_SERVICE_SAVED is working properly.
720 TEST_F(SessionServiceTest, SavedSessionNotification) {
721 content::NotificationRegistrar registrar_;
722 registrar_.Add(this, chrome::NOTIFICATION_SESSION_SERVICE_SAVED,
723 content::NotificationService::AllSources());
724 service()->GetBaseSessionServiceForTest()->Save();
725 EXPECT_EQ(sync_save_count_, 1);
728 // Makes sure a tab closed by a user gesture is not restored.
729 TEST_F(SessionServiceTest, CloseTabUserGesture) {
731 ASSERT_NE(window_id.id(), tab_id.id());
733 SerializedNavigationEntry nav1 =
734 SerializedNavigationEntryTestHelper::CreateNavigation(
735 "http://google.com", "abc");
737 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
738 UpdateNavigation(window_id, tab_id, nav1, true);
739 service()->TabClosed(window_id, tab_id, true);
741 ScopedVector<SessionWindow> windows;
742 ReadWindows(&(windows.get()), NULL);
744 ASSERT_TRUE(windows.empty());
747 // Verifies SetWindowBounds maps SHOW_STATE_DEFAULT to SHOW_STATE_NORMAL.
748 TEST_F(SessionServiceTest, DontPersistDefault) {
750 ASSERT_NE(window_id.id(), tab_id.id());
751 SerializedNavigationEntry nav1 =
752 SerializedNavigationEntryTestHelper::CreateNavigation(
753 "http://google.com", "abc");
754 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
755 UpdateNavigation(window_id, tab_id, nav1, true);
756 service()->SetWindowBounds(window_id,
758 ui::SHOW_STATE_DEFAULT);
760 ScopedVector<SessionWindow> windows;
761 ReadWindows(&(windows.get()), NULL);
762 ASSERT_EQ(1U, windows.size());
763 EXPECT_EQ(ui::SHOW_STATE_NORMAL, windows[0]->show_state);
766 TEST_F(SessionServiceTest, KeepPostDataWithoutPasswords) {
768 ASSERT_NE(window_id.id(), tab_id.id());
770 // Create a page state representing a HTTP body without posted passwords.
771 content::PageState page_state =
772 content::PageState::CreateForTesting(GURL(), false, "data", NULL);
774 // Create a TabNavigation containing page_state and representing a POST
776 SerializedNavigationEntry nav1 =
777 SerializedNavigationEntryTestHelper::CreateNavigation(
778 "http://google.com", "title");
779 SerializedNavigationEntryTestHelper::SetEncodedPageState(
780 page_state.ToEncodedData(), &nav1);
781 SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1);
783 // Create a TabNavigation containing page_state and representing a normal
785 SerializedNavigationEntry nav2 =
786 SerializedNavigationEntryTestHelper::CreateNavigation(
787 "http://google.com/nopost", "title");
788 SerializedNavigationEntryTestHelper::SetEncodedPageState(
789 page_state.ToEncodedData(), &nav2);
792 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
793 UpdateNavigation(window_id, tab_id, nav1, true);
794 UpdateNavigation(window_id, tab_id, nav2, true);
796 ScopedVector<SessionWindow> windows;
797 ReadWindows(&(windows.get()), NULL);
799 helper_.AssertSingleWindowWithSingleTab(windows.get(), 2);
801 // Expected: the page state of both navigations was saved and restored.
802 ASSERT_EQ(2u, windows[0]->tabs[0]->navigations.size());
803 helper_.AssertNavigationEquals(nav1, windows[0]->tabs[0]->navigations[0]);
804 helper_.AssertNavigationEquals(nav2, windows[0]->tabs[0]->navigations[1]);
807 TEST_F(SessionServiceTest, RemovePostDataWithPasswords) {
809 ASSERT_NE(window_id.id(), tab_id.id());
811 // Create a page state representing a HTTP body with posted passwords.
812 content::PageState page_state =
813 content::PageState::CreateForTesting(GURL(), true, "data", NULL);
815 // Create a TabNavigation containing page_state and representing a POST
816 // request with passwords.
817 SerializedNavigationEntry nav1 =
818 SerializedNavigationEntryTestHelper::CreateNavigation(
819 "http://google.com", "title");
820 SerializedNavigationEntryTestHelper::SetEncodedPageState(
821 page_state.ToEncodedData(), &nav1);
822 SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1);
823 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
824 UpdateNavigation(window_id, tab_id, nav1, true);
826 ScopedVector<SessionWindow> windows;
827 ReadWindows(&(windows.get()), NULL);
829 helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
831 // Expected: the HTTP body was removed from the page state of the POST
832 // navigation with passwords.
833 EXPECT_NE(page_state.ToEncodedData(),
834 windows[0]->tabs[0]->navigations[0].encoded_page_state());
837 TEST_F(SessionServiceTest, ReplacePendingNavigation) {
838 const std::string base_url("http://google.com/");
841 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
843 // Add 5 navigations, some with the same index
844 for (int i = 0; i < 5; ++i) {
845 SerializedNavigationEntry nav =
846 SerializedNavigationEntryTestHelper::CreateNavigation(
847 base_url + base::IntToString(i), "a");
848 nav.set_index(i / 2);
849 UpdateNavigation(window_id, tab_id, nav, true);
853 ScopedVector<SessionWindow> windows;
854 ReadWindows(&(windows.get()), NULL);
856 // The ones with index 0, and 2 should have been replaced by 1 and 3.
857 ASSERT_EQ(1U, windows.size());
858 ASSERT_EQ(1U, windows[0]->tabs.size());
859 EXPECT_EQ(3U, windows[0]->tabs[0]->navigations.size());
860 EXPECT_EQ(GURL(base_url + base::IntToString(1)),
861 windows[0]->tabs[0]->navigations[0].virtual_url());
862 EXPECT_EQ(GURL(base_url + base::IntToString(3)),
863 windows[0]->tabs[0]->navigations[1].virtual_url());
864 EXPECT_EQ(GURL(base_url + base::IntToString(4)),
865 windows[0]->tabs[0]->navigations[2].virtual_url());
868 TEST_F(SessionServiceTest, ReplacePendingNavigationAndPrune) {
869 const std::string base_url("http://google.com/");
872 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
874 for (int i = 0; i < 5; ++i) {
875 SerializedNavigationEntry nav =
876 SerializedNavigationEntryTestHelper::CreateNavigation(
877 base_url + base::IntToString(i), "a");
879 UpdateNavigation(window_id, tab_id, nav, true);
882 // Prune all those navigations.
883 helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 5);
885 // Add another navigation to replace the last one.
886 SerializedNavigationEntry nav =
887 SerializedNavigationEntryTestHelper::CreateNavigation(
888 base_url + base::IntToString(5), "a");
890 UpdateNavigation(window_id, tab_id, nav, true);
893 ScopedVector<SessionWindow> windows;
894 ReadWindows(&(windows.get()), NULL);
896 // We should still have that last navigation at the end,
897 // even though it replaced one that was set before the prune.
898 ASSERT_EQ(1U, windows.size());
899 ASSERT_EQ(1U, windows[0]->tabs.size());
900 ASSERT_EQ(1U, windows[0]->tabs[0]->navigations.size());
901 EXPECT_EQ(GURL(base_url + base::IntToString(5)),
902 windows[0]->tabs[0]->navigations[0].virtual_url());
905 TEST_F(SessionServiceTest, RestoreActivation1) {
906 SessionID window2_id;
909 SerializedNavigationEntry nav1;
910 SerializedNavigationEntry nav2;
912 CreateAndWriteSessionWithTwoWindows(
913 window2_id, tab1_id, tab2_id, &nav1, &nav2);
915 service()->ScheduleCommand(CreateSetActiveWindowCommand(window2_id).Pass());
916 service()->ScheduleCommand(CreateSetActiveWindowCommand(window_id).Pass());
918 ScopedVector<SessionWindow> windows;
919 SessionID::id_type active_window_id = 0;
920 ReadWindows(&(windows.get()), &active_window_id);
921 EXPECT_EQ(window_id.id(), active_window_id);
924 // It's easier to have two separate tests with setup/teardown than to manualy
925 // reset the state for the different flavors of the test.
926 TEST_F(SessionServiceTest, RestoreActivation2) {
927 SessionID window2_id;
930 SerializedNavigationEntry nav1;
931 SerializedNavigationEntry nav2;
933 CreateAndWriteSessionWithTwoWindows(
934 window2_id, tab1_id, tab2_id, &nav1, &nav2);
936 service()->ScheduleCommand(CreateSetActiveWindowCommand(window2_id).Pass());
937 service()->ScheduleCommand(CreateSetActiveWindowCommand(window_id).Pass());
938 service()->ScheduleCommand(CreateSetActiveWindowCommand(window2_id).Pass());
940 ScopedVector<SessionWindow> windows;
941 SessionID::id_type active_window_id = 0;
942 ReadWindows(&(windows.get()), &active_window_id);
943 EXPECT_EQ(window2_id.id(), active_window_id);
946 // Makes sure we don't track blacklisted URLs.
947 TEST_F(SessionServiceTest, IgnoreBlacklistedUrls) {
950 SerializedNavigationEntry nav1 =
951 SerializedNavigationEntryTestHelper::CreateNavigation(
952 "http://google.com", "abc");
953 SerializedNavigationEntry nav2 =
954 SerializedNavigationEntryTestHelper::CreateNavigation(
955 chrome::kChromeUIQuitURL, "quit");
956 SerializedNavigationEntry nav3 =
957 SerializedNavigationEntryTestHelper::CreateNavigation(
958 chrome::kChromeUIRestartURL, "restart");
960 helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
961 UpdateNavigation(window_id, tab_id, nav1, true);
962 UpdateNavigation(window_id, tab_id, nav2, true);
963 UpdateNavigation(window_id, tab_id, nav3, true);
965 ScopedVector<SessionWindow> windows;
966 ReadWindows(&(windows.get()), NULL);
968 ASSERT_EQ(1U, windows.size());
969 ASSERT_EQ(0, windows[0]->selected_tab_index);
970 ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
971 ASSERT_EQ(1U, windows[0]->tabs.size());
973 SessionTab* tab = windows[0]->tabs[0];
974 helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
975 helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
978 // Functions used by GetSessionsAndDestroy.
981 void OnGotPreviousSession(ScopedVector<SessionWindow> windows,
982 SessionID::id_type ignored_active_window) {
983 FAIL() << "SessionService was destroyed, this shouldn't be reached.";
986 void PostBackToThread(base::MessageLoop* message_loop,
987 base::RunLoop* run_loop) {
988 message_loop->PostTask(FROM_HERE,
989 base::Bind(&base::RunLoop::Quit,
990 base::Unretained(run_loop)));
995 // Verifies that SessionService::GetLastSession() works correctly if the
996 // SessionService is deleted during processing. To verify the problematic case
997 // does the following:
998 // 1. Sends a task to the background thread that blocks.
999 // 2. Asks SessionService for the last session commands. This is blocked by 1.
1000 // 3. Posts another task to the background thread, this too is blocked by 1.
1001 // 4. Deletes SessionService.
1002 // 5. Signals the semaphore that 2 and 3 are waiting on, allowing
1003 // GetLastSession() to continue.
1004 // 6. runs the message loop, this is quit when the task scheduled in 3 posts
1005 // back to the ui thread to quit the run loop.
1006 // The call to get the previous session should never be invoked because the
1007 // SessionService was destroyed before SessionService could process the results.
1008 TEST_F(SessionServiceTest, GetSessionsAndDestroy) {
1009 base::CancelableTaskTracker cancelable_task_tracker;
1010 base::RunLoop run_loop;
1011 base::WaitableEvent event(true, false);
1012 helper_.RunTaskOnBackendThread(FROM_HERE,
1013 base::Bind(&base::WaitableEvent::Wait,
1014 base::Unretained(&event)));
1015 service()->GetLastSession(base::Bind(&OnGotPreviousSession),
1016 &cancelable_task_tracker);
1017 helper_.RunTaskOnBackendThread(
1019 base::Bind(&PostBackToThread,
1020 base::Unretained(base::MessageLoop::current()),
1021 base::Unretained(&run_loop)));
1022 delete helper_.ReleaseService();