1// Copyright (c) 2011 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/file_path.h"
6#include "chrome/browser/defaults.h"
7#include "chrome/browser/profiles/profile.h"
8#include "chrome/browser/sessions/tab_restore_service.h"
9#include "chrome/browser/tabs/tab_strip_model.h"
10#include "chrome/browser/ui/browser.h"
11#include "chrome/browser/ui/browser_list.h"
12#include "chrome/browser/ui/browser_window.h"
13#include "chrome/test/in_process_browser_test.h"
14#include "chrome/test/ui_test_utils.h"
15#include "content/browser/tab_contents/tab_contents.h"
16#include "content/common/page_transition_types.h"
17
18namespace {
19
20// BrowserList::Observer implementation that waits for a browser to be
21// removed.
22class BrowserListObserverImpl : public BrowserList::Observer {
23 public:
24  BrowserListObserverImpl() : did_remove_(false), running_(false) {
25    BrowserList::AddObserver(this);
26  }
27
28  ~BrowserListObserverImpl() {
29    BrowserList::RemoveObserver(this);
30  }
31
32  // Returns when a browser has been removed.
33  void Run() {
34    running_ = true;
35    if (!did_remove_)
36      ui_test_utils::RunMessageLoop();
37  }
38
39  // BrowserList::Observer
40  virtual void OnBrowserAdded(const Browser* browser) OVERRIDE {
41  }
42  virtual void OnBrowserRemoved(const Browser* browser) OVERRIDE {
43    did_remove_ = true;
44    if (running_)
45      MessageLoop::current()->Quit();
46  }
47
48 private:
49  // Was OnBrowserRemoved invoked?
50  bool did_remove_;
51
52  // Was Run invoked?
53  bool running_;
54
55  DISALLOW_COPY_AND_ASSIGN(BrowserListObserverImpl);
56};
57
58}  // namespace
59
60typedef InProcessBrowserTest SessionRestoreTest;
61
62#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
63// Crashes on Linux Views: http://crbug.com/39476
64#define MAYBE_RestoreOnNewWindowWithNoTabbedBrowsers \
65        DISABLED_RestoreOnNewWindowWithNoTabbedBrowsers
66#else
67#define MAYBE_RestoreOnNewWindowWithNoTabbedBrowsers \
68        RestoreOnNewWindowWithNoTabbedBrowsers
69#endif
70
71// Makes sure when session restore is triggered in the same process we don't end
72// up with an extra tab.
73IN_PROC_BROWSER_TEST_F(SessionRestoreTest,
74                       MAYBE_RestoreOnNewWindowWithNoTabbedBrowsers) {
75  if (browser_defaults::kRestorePopups)
76    return;
77
78  const FilePath::CharType* kTitle1File = FILE_PATH_LITERAL("title1.html");
79  GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
80                                     FilePath(kTitle1File)));
81  ui_test_utils::NavigateToURL(browser(), url);
82
83  // Turn on session restore.
84  SessionStartupPref::SetStartupPref(
85      browser()->profile(),
86      SessionStartupPref(SessionStartupPref::LAST));
87
88  // Create a new popup.
89  Profile* profile = browser()->profile();
90  Browser* popup = Browser::CreateForType(Browser::TYPE_POPUP, profile);
91  popup->window()->Show();
92
93  // Close the browser.
94  browser()->window()->Close();
95
96  // Create a new window, which should trigger session restore.
97  popup->NewWindow();
98
99  Browser* new_browser = ui_test_utils::WaitForNewBrowser();
100
101  ASSERT_TRUE(new_browser != NULL);
102
103  // The browser should only have one tab.
104  ASSERT_EQ(1, new_browser->tab_count());
105
106  // And the first url should be url.
107  EXPECT_EQ(url, new_browser->GetTabContentsAt(0)->GetURL());
108}
109
110IN_PROC_BROWSER_TEST_F(SessionRestoreTest, RestoreIndividualTabFromWindow) {
111  GURL url1(ui_test_utils::GetTestUrl(
112      FilePath(FilePath::kCurrentDirectory),
113      FilePath(FILE_PATH_LITERAL("title1.html"))));
114  GURL url2(ui_test_utils::GetTestUrl(
115      FilePath(FilePath::kCurrentDirectory),
116      FilePath(FILE_PATH_LITERAL("title2.html"))));
117  GURL url3(ui_test_utils::GetTestUrl(
118      FilePath(FilePath::kCurrentDirectory),
119      FilePath(FILE_PATH_LITERAL("title3.html"))));
120
121  // Add and navigate three tabs.
122  ui_test_utils::NavigateToURL(browser(), url1);
123  browser()->AddSelectedTabWithURL(url2, PageTransition::LINK);
124  ui_test_utils::WaitForNavigationInCurrentTab(browser());
125
126  browser()->AddSelectedTabWithURL(url3, PageTransition::LINK);
127  ui_test_utils::WaitForNavigationInCurrentTab(browser());
128
129  TabRestoreService* service = browser()->profile()->GetTabRestoreService();
130  service->ClearEntries();
131
132  browser()->window()->Close();
133
134  // Expect a window with three tabs.
135  EXPECT_EQ(1U, service->entries().size());
136  ASSERT_EQ(TabRestoreService::WINDOW, service->entries().front()->type);
137  const TabRestoreService::Window* window =
138      static_cast<TabRestoreService::Window*>(service->entries().front());
139  EXPECT_EQ(3U, window->tabs.size());
140
141  // Find the SessionID for entry2. Since the session service was destroyed,
142  // there is no guarantee that the SessionID for the tab has remained the same.
143  std::vector<TabRestoreService::Tab>::const_iterator it = window->tabs.begin();
144  for ( ; it != window->tabs.end(); ++it) {
145    const TabRestoreService::Tab& tab = *it;
146    // If this tab held url2, then restore this single tab.
147    if (tab.navigations[0].virtual_url() == url2) {
148      service->RestoreEntryById(NULL, tab.id, false);
149      break;
150    }
151  }
152
153  // Make sure that the Window got updated.
154  EXPECT_EQ(1U, service->entries().size());
155  ASSERT_EQ(TabRestoreService::WINDOW, service->entries().front()->type);
156  window = static_cast<TabRestoreService::Window*>(service->entries().front());
157  EXPECT_EQ(2U, window->tabs.size());
158}
159
160IN_PROC_BROWSER_TEST_F(SessionRestoreTest, WindowWithOneTab) {
161  GURL url(ui_test_utils::GetTestUrl(
162      FilePath(FilePath::kCurrentDirectory),
163      FilePath(FILE_PATH_LITERAL("title1.html"))));
164
165  // Add a single tab.
166  ui_test_utils::NavigateToURL(browser(), url);
167
168  TabRestoreService* service = browser()->profile()->GetTabRestoreService();
169  service->ClearEntries();
170  EXPECT_EQ(0U, service->entries().size());
171
172  // Close the window.
173  browser()->window()->Close();
174
175  // Expect the window to be converted to a tab by the TRS.
176  EXPECT_EQ(1U, service->entries().size());
177  ASSERT_EQ(TabRestoreService::TAB, service->entries().front()->type);
178  const TabRestoreService::Tab* tab =
179      static_cast<TabRestoreService::Tab*>(service->entries().front());
180
181  // Restore the tab.
182  service->RestoreEntryById(NULL, tab->id, false);
183
184  // Make sure the restore was successful.
185  EXPECT_EQ(0U, service->entries().size());
186}
187
188// Verifies we remember the last browser window when closing the last
189// non-incognito window while an incognito window is open.
190IN_PROC_BROWSER_TEST_F(SessionRestoreTest, IncognitotoNonIncognito) {
191  // Turn on session restore.
192  SessionStartupPref pref(SessionStartupPref::LAST);
193  SessionStartupPref::SetStartupPref(browser()->profile(), pref);
194
195  GURL url(ui_test_utils::GetTestUrl(
196      FilePath(FilePath::kCurrentDirectory),
197      FilePath(FILE_PATH_LITERAL("title1.html"))));
198
199  // Add a single tab.
200  ui_test_utils::NavigateToURL(browser(), url);
201
202  // Create a new incognito window.
203  Browser* incognito_browser = CreateIncognitoBrowser();
204  incognito_browser->AddBlankTab(true);
205  incognito_browser->window()->Show();
206
207  // Close the normal browser. After this we only have the incognito window
208  // open. We wait until the window closes as window closing is async.
209  {
210    BrowserListObserverImpl observer;
211    browser()->window()->Close();
212    observer.Run();
213  }
214
215  // Create a new window, which should trigger session restore.
216  incognito_browser->NewWindow();
217
218  // The first tab should have 'url' as its url.
219  Browser* new_browser = ui_test_utils::WaitForNewBrowser();
220  ASSERT_TRUE(new_browser);
221  EXPECT_EQ(url, new_browser->GetTabContentsAt(0)->GetURL());
222}
223