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.
4
5#include "base/bind.h"
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/path_service.h"
12#include "base/run_loop.h"
13#include "base/stl_util.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/utf_string_conversions.h"
16#include "base/synchronization/waitable_event.h"
17#include "base/time/time.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/chrome_notification_types.h"
20#include "chrome/browser/defaults.h"
21#include "chrome/browser/profiles/profile_manager.h"
22#include "chrome/browser/sessions/session_backend.h"
23#include "chrome/browser/sessions/session_service.h"
24#include "chrome/browser/sessions/session_service_test_helper.h"
25#include "chrome/browser/sessions/session_types.h"
26#include "chrome/common/chrome_paths.h"
27#include "chrome/common/url_constants.h"
28#include "chrome/test/base/browser_with_test_window_test.h"
29#include "chrome/test/base/testing_browser_process.h"
30#include "chrome/test/base/testing_profile.h"
31#include "chrome/test/base/testing_profile_manager.h"
32#include "components/sessions/serialized_navigation_entry_test_helper.h"
33#include "content/public/browser/navigation_entry.h"
34#include "content/public/browser/notification_observer.h"
35#include "content/public/browser/notification_registrar.h"
36#include "content/public/browser/notification_service.h"
37#include "content/public/common/page_state.h"
38#include "testing/gtest/include/gtest/gtest.h"
39
40using content::NavigationEntry;
41using sessions::SerializedNavigationEntry;
42using sessions::SerializedNavigationEntryTestHelper;
43
44class SessionServiceTest : public BrowserWithTestWindowTest,
45                           public content::NotificationObserver {
46 public:
47  SessionServiceTest() : window_bounds(0, 1, 2, 3), sync_save_count_(0) {}
48
49 protected:
50  virtual void SetUp() {
51    BrowserWithTestWindowTest::SetUp();
52
53    profile_manager_.reset(
54        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
55    ASSERT_TRUE(profile_manager_->SetUp());
56
57    std::string b = base::Int64ToString(base::Time::Now().ToInternalValue());
58    TestingProfile* profile = profile_manager_->CreateTestingProfile(b);
59    SessionService* session_service = new SessionService(profile);
60    path_ = profile->GetPath();
61
62    helper_.SetService(session_service);
63
64    service()->SetWindowType(
65        window_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL);
66    service()->SetWindowBounds(window_id,
67                               window_bounds,
68                               ui::SHOW_STATE_NORMAL);
69  }
70
71  // Upon notification, increment the sync_save_count variable
72  virtual void Observe(int type,
73                       const content::NotificationSource& source,
74                       const content::NotificationDetails& details) OVERRIDE {
75    ASSERT_EQ(type, chrome::NOTIFICATION_SESSION_SERVICE_SAVED);
76    sync_save_count_++;
77  }
78
79  virtual void TearDown() {
80    helper_.SetService(NULL);
81    BrowserWithTestWindowTest::TearDown();
82  }
83
84  void UpdateNavigation(
85      const SessionID& window_id,
86      const SessionID& tab_id,
87      const SerializedNavigationEntry& navigation,
88      bool select) {
89    service()->UpdateTabNavigation(window_id, tab_id, navigation);
90    if (select) {
91      service()->SetSelectedNavigationIndex(
92          window_id, tab_id, navigation.index());
93    }
94  }
95
96  void ReadWindows(std::vector<SessionWindow*>* windows,
97                   SessionID::id_type* active_window_id) {
98    // Forces closing the file.
99    helper_.SetService(NULL);
100
101    SessionService* session_service = new SessionService(path_);
102    helper_.SetService(session_service);
103
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);
109  }
110
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) {
116    SessionID tab_id;
117    SerializedNavigationEntry nav1 =
118        SerializedNavigationEntryTestHelper::CreateNavigation(
119            "http://google.com", "abc");
120
121    helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
122    UpdateNavigation(window_id, tab_id, nav1, true);
123
124    if (pinned_state || write_always)
125      helper_.service()->SetPinnedState(window_id, tab_id, pinned_state);
126
127    ScopedVector<SessionWindow> windows;
128    ReadWindows(&(windows.get()), NULL);
129
130    EXPECT_EQ(1U, windows.size());
131    if (HasFatalFailure())
132      return false;
133    EXPECT_EQ(1U, windows[0]->tabs.size());
134    if (HasFatalFailure())
135      return false;
136
137    SessionTab* tab = windows[0]->tabs[0];
138    helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
139
140    return tab->pinned;
141  }
142
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");
153
154    helper_.PrepareTabInWindow(window_id, tab1_id, 0, true);
155    UpdateNavigation(window_id, tab1_id, *nav1, true);
156
157    const gfx::Rect window2_bounds(3, 4, 5, 6);
158    service()->SetWindowType(
159        window2_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL);
160    service()->SetWindowBounds(window2_id,
161                               window2_bounds,
162                               ui::SHOW_STATE_MAXIMIZED);
163    helper_.PrepareTabInWindow(window2_id, tab2_id, 0, true);
164    UpdateNavigation(window2_id, tab2_id, *nav2, true);
165  }
166
167  SessionService* service() { return helper_.service(); }
168
169  const gfx::Rect window_bounds;
170
171  SessionID window_id;
172
173  int sync_save_count_;
174
175  // Path used in testing.
176  base::ScopedTempDir temp_dir_;
177  base::FilePath path_;
178
179  SessionServiceTestHelper helper_;
180  scoped_ptr<TestingProfileManager> profile_manager_;
181};
182
183TEST_F(SessionServiceTest, Basic) {
184  SessionID tab_id;
185  ASSERT_NE(window_id.id(), tab_id.id());
186
187  SerializedNavigationEntry nav1 =
188      SerializedNavigationEntryTestHelper::CreateNavigation(
189          "http://google.com", "abc");
190  SerializedNavigationEntryTestHelper::SetOriginalRequestURL(
191      GURL("http://original.request.com"), &nav1);
192
193  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
194  UpdateNavigation(window_id, tab_id, nav1, true);
195
196  ScopedVector<SessionWindow> windows;
197  ReadWindows(&(windows.get()), NULL);
198
199  ASSERT_EQ(1U, windows.size());
200  ASSERT_TRUE(window_bounds == windows[0]->bounds);
201  ASSERT_EQ(0, windows[0]->selected_tab_index);
202  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
203  ASSERT_EQ(1U, windows[0]->tabs.size());
204  ASSERT_EQ(Browser::TYPE_TABBED, windows[0]->type);
205
206  SessionTab* tab = windows[0]->tabs[0];
207  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
208
209  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
210}
211
212// Make sure we persist post entries.
213TEST_F(SessionServiceTest, PersistPostData) {
214  SessionID tab_id;
215  ASSERT_NE(window_id.id(), tab_id.id());
216
217  SerializedNavigationEntry nav1 =
218      SerializedNavigationEntryTestHelper::CreateNavigation(
219          "http://google.com", "abc");
220  SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1);
221
222  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
223  UpdateNavigation(window_id, tab_id, nav1, true);
224
225  ScopedVector<SessionWindow> windows;
226  ReadWindows(&(windows.get()), NULL);
227
228  helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
229}
230
231TEST_F(SessionServiceTest, ClosingTabStaysClosed) {
232  SessionID tab_id;
233  SessionID tab2_id;
234  ASSERT_NE(tab_id.id(), tab2_id.id());
235
236  SerializedNavigationEntry nav1 =
237      SerializedNavigationEntryTestHelper::CreateNavigation(
238          "http://google.com", "abc");
239  SerializedNavigationEntry nav2 =
240      SerializedNavigationEntryTestHelper::CreateNavigation(
241          "http://google2.com", "abcd");
242
243  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
244  UpdateNavigation(window_id, tab_id, nav1, true);
245
246  helper_.PrepareTabInWindow(window_id, tab2_id, 1, false);
247  UpdateNavigation(window_id, tab2_id, nav2, true);
248  service()->TabClosed(window_id, tab2_id, false);
249
250  ScopedVector<SessionWindow> windows;
251  ReadWindows(&(windows.get()), NULL);
252
253  ASSERT_EQ(1U, windows.size());
254  ASSERT_EQ(0, windows[0]->selected_tab_index);
255  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
256  ASSERT_EQ(1U, windows[0]->tabs.size());
257
258  SessionTab* tab = windows[0]->tabs[0];
259  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
260
261  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
262}
263
264TEST_F(SessionServiceTest, Pruning) {
265  SessionID tab_id;
266
267  SerializedNavigationEntry nav1 =
268      SerializedNavigationEntryTestHelper::CreateNavigation(
269          "http://google.com", "abc");
270  SerializedNavigationEntry nav2 =
271      SerializedNavigationEntryTestHelper::CreateNavigation(
272          "http://google2.com", "abcd");
273
274  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
275  for (int i = 0; i < 6; ++i) {
276    SerializedNavigationEntry* nav = (i % 2) == 0 ? &nav1 : &nav2;
277    nav->set_index(i);
278    UpdateNavigation(window_id, tab_id, *nav, true);
279  }
280  service()->TabNavigationPathPrunedFromBack(window_id, tab_id, 3);
281
282  ScopedVector<SessionWindow> windows;
283  ReadWindows(&(windows.get()), NULL);
284
285  ASSERT_EQ(1U, windows.size());
286  ASSERT_EQ(0, windows[0]->selected_tab_index);
287  ASSERT_EQ(1U, windows[0]->tabs.size());
288
289  SessionTab* tab = windows[0]->tabs[0];
290  // We left the selected index at 5, then pruned. When rereading the
291  // index should get reset to last valid navigation, which is 2.
292  helper_.AssertTabEquals(window_id, tab_id, 0, 2, 3, *tab);
293
294  ASSERT_EQ(3u, tab->navigations.size());
295  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
296  helper_.AssertNavigationEquals(nav2, tab->navigations[1]);
297  helper_.AssertNavigationEquals(nav1, tab->navigations[2]);
298}
299
300TEST_F(SessionServiceTest, TwoWindows) {
301  SessionID window2_id;
302  SessionID tab1_id;
303  SessionID tab2_id;
304  SerializedNavigationEntry nav1;
305  SerializedNavigationEntry nav2;
306
307  CreateAndWriteSessionWithTwoWindows(
308      window2_id, tab1_id, tab2_id, &nav1, &nav2);
309
310  ScopedVector<SessionWindow> windows;
311  ReadWindows(&(windows.get()), NULL);
312
313  ASSERT_EQ(2U, windows.size());
314  ASSERT_EQ(0, windows[0]->selected_tab_index);
315  ASSERT_EQ(0, windows[1]->selected_tab_index);
316  ASSERT_EQ(1U, windows[0]->tabs.size());
317  ASSERT_EQ(1U, windows[1]->tabs.size());
318
319  SessionTab* rt1;
320  SessionTab* rt2;
321  if (windows[0]->window_id.id() == window_id.id()) {
322    ASSERT_EQ(window2_id.id(), windows[1]->window_id.id());
323    ASSERT_EQ(ui::SHOW_STATE_NORMAL, windows[0]->show_state);
324    ASSERT_EQ(ui::SHOW_STATE_MAXIMIZED, windows[1]->show_state);
325    rt1 = windows[0]->tabs[0];
326    rt2 = windows[1]->tabs[0];
327  } else {
328    ASSERT_EQ(window2_id.id(), windows[0]->window_id.id());
329    ASSERT_EQ(window_id.id(), windows[1]->window_id.id());
330    ASSERT_EQ(ui::SHOW_STATE_MAXIMIZED, windows[0]->show_state);
331    ASSERT_EQ(ui::SHOW_STATE_NORMAL, windows[1]->show_state);
332    rt1 = windows[1]->tabs[0];
333    rt2 = windows[0]->tabs[0];
334  }
335  SessionTab* tab = rt1;
336  helper_.AssertTabEquals(window_id, tab1_id, 0, 0, 1, *tab);
337  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
338
339  tab = rt2;
340  helper_.AssertTabEquals(window2_id, tab2_id, 0, 0, 1, *tab);
341  helper_.AssertNavigationEquals(nav2, tab->navigations[0]);
342}
343
344TEST_F(SessionServiceTest, WindowWithNoTabsGetsPruned) {
345  SessionID window2_id;
346  SessionID tab1_id;
347  SessionID tab2_id;
348
349  SerializedNavigationEntry nav1 =
350      SerializedNavigationEntryTestHelper::CreateNavigation(
351          "http://google.com", "abc");
352
353  helper_.PrepareTabInWindow(window_id, tab1_id, 0, true);
354  UpdateNavigation(window_id, tab1_id, nav1, true);
355
356  const gfx::Rect window2_bounds(3, 4, 5, 6);
357  service()->SetWindowType(
358      window2_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL);
359  service()->SetWindowBounds(window2_id,
360                             window2_bounds,
361                             ui::SHOW_STATE_NORMAL);
362  helper_.PrepareTabInWindow(window2_id, tab2_id, 0, true);
363
364  ScopedVector<SessionWindow> windows;
365  ReadWindows(&(windows.get()), NULL);
366
367  ASSERT_EQ(1U, windows.size());
368  ASSERT_EQ(0, windows[0]->selected_tab_index);
369  ASSERT_EQ(1U, windows[0]->tabs.size());
370  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
371
372  SessionTab* tab = windows[0]->tabs[0];
373  helper_.AssertTabEquals(window_id, tab1_id, 0, 0, 1, *tab);
374  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
375}
376
377TEST_F(SessionServiceTest, ClosingWindowDoesntCloseTabs) {
378  SessionID tab_id;
379  SessionID tab2_id;
380  ASSERT_NE(tab_id.id(), tab2_id.id());
381
382  SerializedNavigationEntry nav1 =
383      SerializedNavigationEntryTestHelper::CreateNavigation(
384          "http://google.com", "abc");
385  SerializedNavigationEntry nav2 =
386      SerializedNavigationEntryTestHelper::CreateNavigation(
387          "http://google2.com", "abcd");
388
389  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
390  UpdateNavigation(window_id, tab_id, nav1, true);
391
392  helper_.PrepareTabInWindow(window_id, tab2_id, 1, false);
393  UpdateNavigation(window_id, tab2_id, nav2, true);
394
395  service()->WindowClosing(window_id);
396
397  ScopedVector<SessionWindow> windows;
398  ReadWindows(&(windows.get()), NULL);
399
400  ASSERT_EQ(1U, windows.size());
401  ASSERT_EQ(0, windows[0]->selected_tab_index);
402  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
403  ASSERT_EQ(2U, windows[0]->tabs.size());
404
405  SessionTab* tab = windows[0]->tabs[0];
406  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
407  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
408
409  tab = windows[0]->tabs[1];
410  helper_.AssertTabEquals(window_id, tab2_id, 1, 0, 1, *tab);
411  helper_.AssertNavigationEquals(nav2, tab->navigations[0]);
412}
413
414TEST_F(SessionServiceTest, LockingWindowRemembersAll) {
415  SessionID window2_id;
416  SessionID tab1_id;
417  SessionID tab2_id;
418  SerializedNavigationEntry nav1;
419  SerializedNavigationEntry nav2;
420
421  CreateAndWriteSessionWithTwoWindows(
422      window2_id, tab1_id, tab2_id, &nav1, &nav2);
423
424  ASSERT_TRUE(service()->profile() != NULL);
425  ASSERT_TRUE(g_browser_process->profile_manager() != NULL);
426  ProfileInfoCache& profile_info =
427      g_browser_process->profile_manager()->GetProfileInfoCache();
428  size_t profile_index = profile_info.GetIndexOfProfileWithPath(
429      service()->profile()->GetPath());
430  ASSERT_NE(std::string::npos, profile_index);
431  profile_info.SetProfileSigninRequiredAtIndex(profile_index, true);
432
433  service()->WindowClosing(window_id);
434  service()->WindowClosed(window_id);
435  service()->WindowClosing(window2_id);
436  service()->WindowClosed(window2_id);
437
438  ScopedVector<SessionWindow> windows;
439  ReadWindows(&(windows.get()), NULL);
440
441  ASSERT_EQ(2U, windows.size());
442  ASSERT_EQ(1U, windows[0]->tabs.size());
443  ASSERT_EQ(1U, windows[1]->tabs.size());
444}
445
446TEST_F(SessionServiceTest, WindowCloseCommittedAfterNavigate) {
447  SessionID window2_id;
448  SessionID tab_id;
449  SessionID tab2_id;
450  ASSERT_NE(window2_id.id(), window_id.id());
451
452  service()->SetWindowType(
453      window2_id, Browser::TYPE_TABBED, SessionService::TYPE_NORMAL);
454  service()->SetWindowBounds(window2_id,
455                             window_bounds,
456                             ui::SHOW_STATE_NORMAL);
457
458  SerializedNavigationEntry nav1 =
459      SerializedNavigationEntryTestHelper::CreateNavigation(
460          "http://google.com", "abc");
461  SerializedNavigationEntry nav2 =
462      SerializedNavigationEntryTestHelper::CreateNavigation(
463          "http://google2.com", "abcd");
464
465  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
466  UpdateNavigation(window_id, tab_id, nav1, true);
467
468  helper_.PrepareTabInWindow(window2_id, tab2_id, 0, false);
469  UpdateNavigation(window2_id, tab2_id, nav2, true);
470
471  service()->WindowClosing(window2_id);
472  service()->TabClosed(window2_id, tab2_id, false);
473  service()->WindowClosed(window2_id);
474
475  ScopedVector<SessionWindow> windows;
476  ReadWindows(&(windows.get()), NULL);
477
478  ASSERT_EQ(1U, windows.size());
479  ASSERT_EQ(0, windows[0]->selected_tab_index);
480  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
481  ASSERT_EQ(1U, windows[0]->tabs.size());
482
483  SessionTab* tab = windows[0]->tabs[0];
484  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
485  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
486}
487
488// Makes sure we don't track popups.
489TEST_F(SessionServiceTest, IgnorePopups) {
490  SessionID window2_id;
491  SessionID tab_id;
492  SessionID tab2_id;
493  ASSERT_NE(window2_id.id(), window_id.id());
494
495  service()->SetWindowType(
496      window2_id, Browser::TYPE_POPUP, SessionService::TYPE_NORMAL);
497  service()->SetWindowBounds(window2_id,
498                             window_bounds,
499                             ui::SHOW_STATE_NORMAL);
500
501  SerializedNavigationEntry nav1 =
502      SerializedNavigationEntryTestHelper::CreateNavigation(
503          "http://google.com", "abc");
504  SerializedNavigationEntry nav2 =
505      SerializedNavigationEntryTestHelper::CreateNavigation(
506          "http://google2.com", "abcd");
507
508  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
509  UpdateNavigation(window_id, tab_id, nav1, true);
510
511  helper_.PrepareTabInWindow(window2_id, tab2_id, 0, false);
512  UpdateNavigation(window2_id, tab2_id, nav2, true);
513
514  ScopedVector<SessionWindow> windows;
515  ReadWindows(&(windows.get()), NULL);
516
517  ASSERT_EQ(1U, windows.size());
518  ASSERT_EQ(0, windows[0]->selected_tab_index);
519  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
520  ASSERT_EQ(1U, windows[0]->tabs.size());
521
522  SessionTab* tab = windows[0]->tabs[0];
523  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
524  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
525}
526
527#if defined (OS_CHROMEOS)
528// Makes sure we track apps. Only applicable on chromeos.
529TEST_F(SessionServiceTest, RestoreApp) {
530  SessionID window2_id;
531  SessionID tab_id;
532  SessionID tab2_id;
533  ASSERT_NE(window2_id.id(), window_id.id());
534
535  service()->SetWindowType(
536      window2_id, Browser::TYPE_POPUP, SessionService::TYPE_APP);
537  service()->SetWindowBounds(window2_id,
538                             window_bounds,
539                             ui::SHOW_STATE_NORMAL);
540  service()->SetWindowAppName(window2_id, "TestApp");
541
542  SerializedNavigationEntry nav1 =
543      SerializedNavigationEntryTestHelper::CreateNavigation(
544          "http://google.com", "abc");
545  SerializedNavigationEntry nav2 =
546      SerializedNavigationEntryTestHelper::CreateNavigation(
547          "http://google2.com", "abcd");
548
549  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
550  UpdateNavigation(window_id, tab_id, nav1, true);
551
552  helper_.PrepareTabInWindow(window2_id, tab2_id, 0, false);
553  UpdateNavigation(window2_id, tab2_id, nav2, true);
554
555  ScopedVector<SessionWindow> windows;
556  ReadWindows(&(windows.get()), NULL);
557
558  ASSERT_EQ(2U, windows.size());
559  int tabbed_index = windows[0]->type == Browser::TYPE_TABBED ?
560      0 : 1;
561  int app_index = tabbed_index == 0 ? 1 : 0;
562  ASSERT_EQ(0, windows[tabbed_index]->selected_tab_index);
563  ASSERT_EQ(window_id.id(), windows[tabbed_index]->window_id.id());
564  ASSERT_EQ(1U, windows[tabbed_index]->tabs.size());
565
566  SessionTab* tab = windows[tabbed_index]->tabs[0];
567  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
568  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
569
570  ASSERT_EQ(0, windows[app_index]->selected_tab_index);
571  ASSERT_EQ(window2_id.id(), windows[app_index]->window_id.id());
572  ASSERT_EQ(1U, windows[app_index]->tabs.size());
573  ASSERT_TRUE(windows[app_index]->type == Browser::TYPE_POPUP);
574  ASSERT_EQ("TestApp", windows[app_index]->app_name);
575
576  tab = windows[app_index]->tabs[0];
577  helper_.AssertTabEquals(window2_id, tab2_id, 0, 0, 1, *tab);
578  helper_.AssertNavigationEquals(nav2, tab->navigations[0]);
579}
580#endif  // defined (OS_CHROMEOS)
581
582// Tests pruning from the front.
583TEST_F(SessionServiceTest, PruneFromFront) {
584  const std::string base_url("http://google.com/");
585  SessionID tab_id;
586
587  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
588
589  // Add 5 navigations, with the 4th selected.
590  for (int i = 0; i < 5; ++i) {
591    SerializedNavigationEntry nav =
592        SerializedNavigationEntryTestHelper::CreateNavigation(
593            base_url + base::IntToString(i), "a");
594    nav.set_index(i);
595    UpdateNavigation(window_id, tab_id, nav, (i == 3));
596  }
597
598  // Prune the first two navigations from the front.
599  helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 2);
600
601  // Read back in.
602  ScopedVector<SessionWindow> windows;
603  ReadWindows(&(windows.get()), NULL);
604
605  ASSERT_EQ(1U, windows.size());
606  ASSERT_EQ(0, windows[0]->selected_tab_index);
607  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
608  ASSERT_EQ(1U, windows[0]->tabs.size());
609
610  // There shouldn't be an app id.
611  EXPECT_TRUE(windows[0]->tabs[0]->extension_app_id.empty());
612
613  // We should be left with three navigations, the 2nd selected.
614  SessionTab* tab = windows[0]->tabs[0];
615  ASSERT_EQ(1, tab->current_navigation_index);
616  EXPECT_EQ(3U, tab->navigations.size());
617  EXPECT_TRUE(GURL(base_url + base::IntToString(2)) ==
618              tab->navigations[0].virtual_url());
619  EXPECT_TRUE(GURL(base_url + base::IntToString(3)) ==
620              tab->navigations[1].virtual_url());
621  EXPECT_TRUE(GURL(base_url + base::IntToString(4)) ==
622              tab->navigations[2].virtual_url());
623}
624
625// Prunes from front so that we have no entries.
626TEST_F(SessionServiceTest, PruneToEmpty) {
627  const std::string base_url("http://google.com/");
628  SessionID tab_id;
629
630  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
631
632  // Add 5 navigations, with the 4th selected.
633  for (int i = 0; i < 5; ++i) {
634    SerializedNavigationEntry nav =
635        SerializedNavigationEntryTestHelper::CreateNavigation(
636            base_url + base::IntToString(i), "a");
637    nav.set_index(i);
638    UpdateNavigation(window_id, tab_id, nav, (i == 3));
639  }
640
641  // Prune the first two navigations from the front.
642  helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 5);
643
644  // Read back in.
645  ScopedVector<SessionWindow> windows;
646  ReadWindows(&(windows.get()), NULL);
647
648  ASSERT_EQ(0U, windows.size());
649}
650
651// Don't set the pinned state and make sure the pinned value is false.
652TEST_F(SessionServiceTest, PinnedDefaultsToFalse) {
653  EXPECT_FALSE(CreateAndWriteSessionWithOneTab(false, false));
654}
655
656// Explicitly set the pinned state to false and make sure we get back false.
657TEST_F(SessionServiceTest, PinnedFalseWhenSetToFalse) {
658  EXPECT_FALSE(CreateAndWriteSessionWithOneTab(false, true));
659}
660
661// Explicitly set the pinned state to true and make sure we get back true.
662TEST_F(SessionServiceTest, PinnedTrue) {
663  EXPECT_TRUE(CreateAndWriteSessionWithOneTab(true, true));
664}
665
666// Make sure application extension ids are persisted.
667TEST_F(SessionServiceTest, PersistApplicationExtensionID) {
668  SessionID tab_id;
669  ASSERT_NE(window_id.id(), tab_id.id());
670  std::string app_id("foo");
671
672  SerializedNavigationEntry nav1 =
673      SerializedNavigationEntryTestHelper::CreateNavigation(
674          "http://google.com", "abc");
675
676  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
677  UpdateNavigation(window_id, tab_id, nav1, true);
678  helper_.SetTabExtensionAppID(window_id, tab_id, app_id);
679
680  ScopedVector<SessionWindow> windows;
681  ReadWindows(&(windows.get()), NULL);
682
683  helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
684  EXPECT_TRUE(app_id == windows[0]->tabs[0]->extension_app_id);
685}
686
687// Check that user agent overrides are persisted.
688TEST_F(SessionServiceTest, PersistUserAgentOverrides) {
689  SessionID tab_id;
690  ASSERT_NE(window_id.id(), tab_id.id());
691  std::string user_agent_override = "Mozilla/5.0 (X11; Linux x86_64) "
692      "AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.45 "
693      "Safari/535.19";
694
695  SerializedNavigationEntry nav1 =
696      SerializedNavigationEntryTestHelper::CreateNavigation(
697          "http://google.com", "abc");
698  SerializedNavigationEntryTestHelper::SetIsOverridingUserAgent(true, &nav1);
699
700  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
701  UpdateNavigation(window_id, tab_id, nav1, true);
702  helper_.SetTabUserAgentOverride(window_id, tab_id, user_agent_override);
703
704  ScopedVector<SessionWindow> windows;
705  ReadWindows(&(windows.get()), NULL);
706  helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
707
708  SessionTab* tab = windows[0]->tabs[0];
709  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
710  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
711  EXPECT_TRUE(user_agent_override == tab->user_agent_override);
712}
713
714// Test that the notification for SESSION_SERVICE_SAVED is working properly.
715TEST_F(SessionServiceTest, SavedSessionNotification) {
716  content::NotificationRegistrar registrar_;
717  registrar_.Add(this, chrome::NOTIFICATION_SESSION_SERVICE_SAVED,
718                 content::NotificationService::AllSources());
719  service()->Save();
720  EXPECT_EQ(sync_save_count_, 1);
721}
722
723// Makes sure a tab closed by a user gesture is not restored.
724TEST_F(SessionServiceTest, CloseTabUserGesture) {
725  SessionID tab_id;
726  ASSERT_NE(window_id.id(), tab_id.id());
727
728  SerializedNavigationEntry nav1 =
729      SerializedNavigationEntryTestHelper::CreateNavigation(
730          "http://google.com", "abc");
731
732  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
733  UpdateNavigation(window_id, tab_id, nav1, true);
734  service()->TabClosed(window_id, tab_id, true);
735
736  ScopedVector<SessionWindow> windows;
737  ReadWindows(&(windows.get()), NULL);
738
739  ASSERT_TRUE(windows.empty());
740}
741
742// Verifies SetWindowBounds maps SHOW_STATE_DEFAULT to SHOW_STATE_NORMAL.
743TEST_F(SessionServiceTest, DontPersistDefault) {
744  SessionID tab_id;
745  ASSERT_NE(window_id.id(), tab_id.id());
746  SerializedNavigationEntry nav1 =
747      SerializedNavigationEntryTestHelper::CreateNavigation(
748          "http://google.com", "abc");
749  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
750  UpdateNavigation(window_id, tab_id, nav1, true);
751  service()->SetWindowBounds(window_id,
752                             window_bounds,
753                             ui::SHOW_STATE_DEFAULT);
754
755  ScopedVector<SessionWindow> windows;
756  ReadWindows(&(windows.get()), NULL);
757  ASSERT_EQ(1U, windows.size());
758  EXPECT_EQ(ui::SHOW_STATE_NORMAL, windows[0]->show_state);
759}
760
761TEST_F(SessionServiceTest, KeepPostDataWithoutPasswords) {
762  SessionID tab_id;
763  ASSERT_NE(window_id.id(), tab_id.id());
764
765  // Create a page state representing a HTTP body without posted passwords.
766  content::PageState page_state =
767      content::PageState::CreateForTesting(GURL(), false, "data", NULL);
768
769  // Create a TabNavigation containing page_state and representing a POST
770  // request.
771  SerializedNavigationEntry nav1 =
772      SerializedNavigationEntryTestHelper::CreateNavigation(
773          "http://google.com", "title");
774  SerializedNavigationEntryTestHelper::SetPageState(page_state, &nav1);
775  SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1);
776
777  // Create a TabNavigation containing page_state and representing a normal
778  // request.
779  SerializedNavigationEntry nav2 =
780      SerializedNavigationEntryTestHelper::CreateNavigation(
781          "http://google.com/nopost", "title");
782  SerializedNavigationEntryTestHelper::SetPageState(page_state, &nav2);
783  nav2.set_index(1);
784
785  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
786  UpdateNavigation(window_id, tab_id, nav1, true);
787  UpdateNavigation(window_id, tab_id, nav2, true);
788
789  ScopedVector<SessionWindow> windows;
790  ReadWindows(&(windows.get()), NULL);
791
792  helper_.AssertSingleWindowWithSingleTab(windows.get(), 2);
793
794  // Expected: the page state of both navigations was saved and restored.
795  ASSERT_EQ(2u, windows[0]->tabs[0]->navigations.size());
796  helper_.AssertNavigationEquals(nav1, windows[0]->tabs[0]->navigations[0]);
797  helper_.AssertNavigationEquals(nav2, windows[0]->tabs[0]->navigations[1]);
798}
799
800TEST_F(SessionServiceTest, RemovePostDataWithPasswords) {
801  SessionID tab_id;
802  ASSERT_NE(window_id.id(), tab_id.id());
803
804  // Create a page state representing a HTTP body with posted passwords.
805  content::PageState page_state =
806      content::PageState::CreateForTesting(GURL(), true, "data", NULL);
807
808  // Create a TabNavigation containing page_state and representing a POST
809  // request with passwords.
810  SerializedNavigationEntry nav1 =
811      SerializedNavigationEntryTestHelper::CreateNavigation(
812          "http://google.com", "title");
813  SerializedNavigationEntryTestHelper::SetPageState(page_state, &nav1);
814  SerializedNavigationEntryTestHelper::SetHasPostData(true, &nav1);
815  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
816  UpdateNavigation(window_id, tab_id, nav1, true);
817
818  ScopedVector<SessionWindow> windows;
819  ReadWindows(&(windows.get()), NULL);
820
821  helper_.AssertSingleWindowWithSingleTab(windows.get(), 1);
822
823  // Expected: the HTTP body was removed from the page state of the POST
824  // navigation with passwords.
825  EXPECT_NE(page_state, windows[0]->tabs[0]->navigations[0].page_state());
826}
827
828// This test is only applicable to chromeos.
829#if defined(OS_CHROMEOS)
830// Verifies migration of tab/window closed works.
831TEST_F(SessionServiceTest, CanOpenV1TabClosed) {
832  base::FilePath v1_file_path;
833  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &v1_file_path));
834  // v1_session_file contains a tab closed command with the original id. The
835  // file was generated from ClosingTabStaysClosed. If we successfully processed
836  // the file we'll have one tab.
837  v1_file_path =
838      v1_file_path.AppendASCII("sessions").AppendASCII("v1_session_file");
839  base::FilePath dest_file_path(path_);
840  dest_file_path = dest_file_path.AppendASCII("Current Session");
841
842  // Forces closing the file.
843  helper_.SetService(NULL);
844
845  ASSERT_TRUE(base::CopyFile(v1_file_path, dest_file_path));
846
847  SessionService* session_service = new SessionService(path_);
848  helper_.SetService(session_service);
849  ScopedVector<SessionWindow> windows;
850  SessionID::id_type active_window_id = 0;
851  helper_.ReadWindows(&(windows.get()), &active_window_id);
852  ASSERT_EQ(1u, windows.size());
853  EXPECT_EQ(1u, windows[0]->tabs.size());
854}
855#endif  // defined(OS_CHROMEOS)
856
857TEST_F(SessionServiceTest, ReplacePendingNavigation) {
858  const std::string base_url("http://google.com/");
859  SessionID tab_id;
860
861  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
862
863  // Add 5 navigations, some with the same index
864  for (int i = 0; i < 5; ++i) {
865    SerializedNavigationEntry nav =
866        SerializedNavigationEntryTestHelper::CreateNavigation(
867            base_url + base::IntToString(i), "a");
868    nav.set_index(i / 2);
869    UpdateNavigation(window_id, tab_id, nav, true);
870  }
871
872  // Read back in.
873  ScopedVector<SessionWindow> windows;
874  ReadWindows(&(windows.get()), NULL);
875
876  // The ones with index 0, and 2 should have been replaced by 1 and 3.
877  ASSERT_EQ(1U, windows.size());
878  ASSERT_EQ(1U, windows[0]->tabs.size());
879  EXPECT_EQ(3U, windows[0]->tabs[0]->navigations.size());
880  EXPECT_EQ(GURL(base_url + base::IntToString(1)),
881            windows[0]->tabs[0]->navigations[0].virtual_url());
882  EXPECT_EQ(GURL(base_url + base::IntToString(3)),
883            windows[0]->tabs[0]->navigations[1].virtual_url());
884  EXPECT_EQ(GURL(base_url + base::IntToString(4)),
885            windows[0]->tabs[0]->navigations[2].virtual_url());
886}
887
888TEST_F(SessionServiceTest, ReplacePendingNavigationAndPrune) {
889  const std::string base_url("http://google.com/");
890  SessionID tab_id;
891
892  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
893
894  for (int i = 0; i < 5; ++i) {
895    SerializedNavigationEntry nav =
896        SerializedNavigationEntryTestHelper::CreateNavigation(
897            base_url + base::IntToString(i), "a");
898    nav.set_index(i);
899    UpdateNavigation(window_id, tab_id, nav, true);
900  }
901
902  // Prune all those navigations.
903  helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 5);
904
905  // Add another navigation to replace the last one.
906  SerializedNavigationEntry nav =
907      SerializedNavigationEntryTestHelper::CreateNavigation(
908        base_url + base::IntToString(5), "a");
909  nav.set_index(4);
910  UpdateNavigation(window_id, tab_id, nav, true);
911
912  // Read back in.
913  ScopedVector<SessionWindow> windows;
914  ReadWindows(&(windows.get()), NULL);
915
916  // We should still have that last navigation at the end,
917  // even though it replaced one that was set before the prune.
918  ASSERT_EQ(1U, windows.size());
919  ASSERT_EQ(1U, windows[0]->tabs.size());
920  ASSERT_EQ(1U, windows[0]->tabs[0]->navigations.size());
921  EXPECT_EQ(GURL(base_url + base::IntToString(5)),
922            windows[0]->tabs[0]->navigations[0].virtual_url());
923}
924
925TEST_F(SessionServiceTest, RestoreActivation1) {
926  SessionID window2_id;
927  SessionID tab1_id;
928  SessionID tab2_id;
929  SerializedNavigationEntry nav1;
930  SerializedNavigationEntry nav2;
931
932  CreateAndWriteSessionWithTwoWindows(
933      window2_id, tab1_id, tab2_id, &nav1, &nav2);
934
935  service()->ScheduleCommand(
936      service()->CreateSetActiveWindowCommand(window2_id));
937  service()->ScheduleCommand(
938      service()->CreateSetActiveWindowCommand(window_id));
939
940  ScopedVector<SessionWindow> windows;
941  SessionID::id_type active_window_id = 0;
942  ReadWindows(&(windows.get()), &active_window_id);
943  EXPECT_EQ(window_id.id(), active_window_id);
944}
945
946// It's easier to have two separate tests with setup/teardown than to manualy
947// reset the state for the different flavors of the test.
948TEST_F(SessionServiceTest, RestoreActivation2) {
949  SessionID window2_id;
950  SessionID tab1_id;
951  SessionID tab2_id;
952  SerializedNavigationEntry nav1;
953  SerializedNavigationEntry nav2;
954
955  CreateAndWriteSessionWithTwoWindows(
956      window2_id, tab1_id, tab2_id, &nav1, &nav2);
957
958  service()->ScheduleCommand(
959      service()->CreateSetActiveWindowCommand(window2_id));
960  service()->ScheduleCommand(
961      service()->CreateSetActiveWindowCommand(window_id));
962  service()->ScheduleCommand(
963      service()->CreateSetActiveWindowCommand(window2_id));
964
965  ScopedVector<SessionWindow> windows;
966  SessionID::id_type active_window_id = 0;
967  ReadWindows(&(windows.get()), &active_window_id);
968  EXPECT_EQ(window2_id.id(), active_window_id);
969}
970
971// Makes sure we don't track blacklisted URLs.
972TEST_F(SessionServiceTest, IgnoreBlacklistedUrls) {
973  SessionID tab_id;
974
975  SerializedNavigationEntry nav1 =
976      SerializedNavigationEntryTestHelper::CreateNavigation(
977          "http://google.com", "abc");
978  SerializedNavigationEntry nav2 =
979      SerializedNavigationEntryTestHelper::CreateNavigation(
980          chrome::kChromeUIQuitURL, "quit");
981  SerializedNavigationEntry nav3 =
982      SerializedNavigationEntryTestHelper::CreateNavigation(
983          chrome::kChromeUIRestartURL, "restart");
984
985  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
986  UpdateNavigation(window_id, tab_id, nav1, true);
987  UpdateNavigation(window_id, tab_id, nav2, true);
988  UpdateNavigation(window_id, tab_id, nav3, true);
989
990  ScopedVector<SessionWindow> windows;
991  ReadWindows(&(windows.get()), NULL);
992
993  ASSERT_EQ(1U, windows.size());
994  ASSERT_EQ(0, windows[0]->selected_tab_index);
995  ASSERT_EQ(window_id.id(), windows[0]->window_id.id());
996  ASSERT_EQ(1U, windows[0]->tabs.size());
997
998  SessionTab* tab = windows[0]->tabs[0];
999  helper_.AssertTabEquals(window_id, tab_id, 0, 0, 1, *tab);
1000  helper_.AssertNavigationEquals(nav1, tab->navigations[0]);
1001}
1002
1003// Functions used by GetSessionsAndDestroy.
1004namespace {
1005
1006void OnGotPreviousSession(ScopedVector<SessionWindow> windows,
1007                          SessionID::id_type ignored_active_window) {
1008  FAIL() << "SessionService was destroyed, this shouldn't be reached.";
1009}
1010
1011void PostBackToThread(base::MessageLoop* message_loop,
1012                      base::RunLoop* run_loop) {
1013  message_loop->PostTask(FROM_HERE,
1014                         base::Bind(&base::RunLoop::Quit,
1015                                    base::Unretained(run_loop)));
1016}
1017
1018}  // namespace
1019
1020// Verifies that SessionService::GetLastSession() works correctly if the
1021// SessionService is deleted during processing. To verify the problematic case
1022// does the following:
1023// 1. Sends a task to the background thread that blocks.
1024// 2. Asks SessionService for the last session commands. This is blocked by 1.
1025// 3. Posts another task to the background thread, this too is blocked by 1.
1026// 4. Deletes SessionService.
1027// 5. Signals the semaphore that 2 and 3 are waiting on, allowing
1028//    GetLastSession() to continue.
1029// 6. runs the message loop, this is quit when the task scheduled in 3 posts
1030//    back to the ui thread to quit the run loop.
1031// The call to get the previous session should never be invoked because the
1032// SessionService was destroyed before SessionService could process the results.
1033TEST_F(SessionServiceTest, GetSessionsAndDestroy) {
1034  base::CancelableTaskTracker cancelable_task_tracker;
1035  base::RunLoop run_loop;
1036  base::WaitableEvent event(true, false);
1037  helper_.RunTaskOnBackendThread(FROM_HERE,
1038                                 base::Bind(&base::WaitableEvent::Wait,
1039                                            base::Unretained(&event)));
1040  service()->GetLastSession(base::Bind(&OnGotPreviousSession),
1041                            &cancelable_task_tracker);
1042  helper_.RunTaskOnBackendThread(
1043      FROM_HERE,
1044      base::Bind(&PostBackToThread,
1045                 base::Unretained(base::MessageLoop::current()),
1046                 base::Unretained(&run_loop)));
1047  delete helper_.ReleaseService();
1048  event.Signal();
1049  run_loop.Run();
1050}
1051