1 // Copyright 2016 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 "build/build_config.h"
6 #include "chrome/browser/extensions/extension_browsertest.h"
7 #include "chrome/browser/extensions/extension_tab_util.h"
8 #include "chrome/browser/ui/browser.h"
9 #include "chrome/browser/ui/browser_finder.h"
10 #include "chrome/browser/ui/browser_list.h"
11 #include "chrome/browser/ui/tabs/tab_strip_model.h"
12 #include "chrome/common/webui_url_constants.h"
13 #include "chrome/test/base/ui_test_utils.h"
14 #include "content/public/test/browser_test.h"
15 #include "content/public/test/browser_test_utils.h"
16 #include "extensions/common/manifest_handlers/options_page_info.h"
17
18 namespace extensions {
19
20 namespace {
21
GetActiveUrl(Browser * browser)22 const GURL& GetActiveUrl(Browser* browser) {
23 return browser->tab_strip_model()
24 ->GetActiveWebContents()
25 ->GetLastCommittedURL();
26 }
27
28 } // namespace
29
30 using ExtensionTabUtilBrowserTest = ExtensionBrowserTest;
31
32 // Times out on Win debug. https://crbug.com/811471
33 #if defined(OS_WIN) && !defined(NDEBUG)
34 #define MAYBE_OpenExtensionsOptionsPage DISABLED_OpenExtensionsOptionsPage
35 #else
36 #define MAYBE_OpenExtensionsOptionsPage OpenExtensionsOptionsPage
37 #endif
38
IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,MAYBE_OpenExtensionsOptionsPage)39 IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,
40 MAYBE_OpenExtensionsOptionsPage) {
41 // Load an extension with an options page that opens in a tab and one that
42 // opens in the chrome://extensions page in a view.
43 const Extension* options_in_tab =
44 LoadExtension(test_data_dir_.AppendASCII("options_page"));
45 const Extension* options_in_view =
46 LoadExtension(test_data_dir_.AppendASCII("options_page_in_view"));
47 ASSERT_TRUE(options_in_tab);
48 ASSERT_TRUE(options_in_view);
49 ASSERT_TRUE(OptionsPageInfo::HasOptionsPage(options_in_tab));
50 ASSERT_TRUE(OptionsPageInfo::HasOptionsPage(options_in_view));
51
52 // Start at the new tab page, and then open the extension options page.
53 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
54 EXPECT_EQ(1, browser()->tab_strip_model()->count());
55 GURL options_url = OptionsPageInfo::GetOptionsPage(options_in_tab);
56 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(options_in_tab, browser()));
57
58 // Opening the options page should take the new tab and use it, so we should
59 // have only one tab, and it should be open to the options page.
60 EXPECT_EQ(1, browser()->tab_strip_model()->count());
61 EXPECT_TRUE(content::WaitForLoadStop(
62 browser()->tab_strip_model()->GetActiveWebContents()));
63 EXPECT_EQ(options_url, GetActiveUrl(browser()));
64
65 // Calling OpenOptionsPage again shouldn't result in any new tabs, since we
66 // re-use the existing options page.
67 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(options_in_tab, browser()));
68 EXPECT_EQ(1, browser()->tab_strip_model()->count());
69 EXPECT_TRUE(content::WaitForLoadStop(
70 browser()->tab_strip_model()->GetActiveWebContents()));
71 EXPECT_EQ(options_url, GetActiveUrl(browser()));
72
73 // Navigate to google.com (something non-newtab, non-options). Calling
74 // OpenOptionsPage() should create a new tab and navigate it to the options
75 // page. So we should have two total tabs, with the active tab pointing to
76 // options.
77 ui_test_utils::NavigateToURL(browser(), GURL("http://www.google.com/"));
78 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(options_in_tab, browser()));
79 EXPECT_EQ(2, browser()->tab_strip_model()->count());
80 EXPECT_TRUE(content::WaitForLoadStop(
81 browser()->tab_strip_model()->GetActiveWebContents()));
82 EXPECT_EQ(options_url, GetActiveUrl(browser()));
83
84 // Navigate the tab to a different extension URL, and call OpenOptionsPage().
85 // We should not reuse the current tab since it's opened to a page that isn't
86 // the options page, and we don't want to arbitrarily close extension content.
87 // Regression test for crbug.com/587581.
88 ui_test_utils::NavigateToURL(browser(),
89 options_in_tab->GetResourceURL("other.html"));
90 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(options_in_tab, browser()));
91 EXPECT_EQ(3, browser()->tab_strip_model()->count());
92 EXPECT_TRUE(content::WaitForLoadStop(
93 browser()->tab_strip_model()->GetActiveWebContents()));
94 EXPECT_EQ(options_url, GetActiveUrl(browser()));
95
96 // If the user navigates to the options page e.g. by typing in the url, it
97 // should not override the currently-open tab.
98 ui_test_utils::NavigateToURLWithDisposition(
99 browser(), options_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
100 ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
101 EXPECT_EQ(4, browser()->tab_strip_model()->count());
102 EXPECT_EQ(options_url, GetActiveUrl(browser()));
103
104 // Test the extension that has the options page open in a view inside
105 // chrome://extensions.
106 // Triggering OpenOptionsPage() should create a new tab, since there are none
107 // to override.
108 options_url = GURL(std::string(chrome::kChromeUIExtensionsURL) +
109 "?options=" + options_in_view->id());
110 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(options_in_view, browser()));
111 EXPECT_EQ(5, browser()->tab_strip_model()->count());
112 EXPECT_TRUE(content::WaitForLoadStop(
113 browser()->tab_strip_model()->GetActiveWebContents()));
114 EXPECT_EQ(options_url, GetActiveUrl(browser()));
115
116 // Calling it a second time should not create a new tab, since one already
117 // exists with that options page open.
118 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(options_in_view, browser()));
119 EXPECT_EQ(5, browser()->tab_strip_model()->count());
120 EXPECT_TRUE(content::WaitForLoadStop(
121 browser()->tab_strip_model()->GetActiveWebContents()));
122 EXPECT_EQ(options_url, GetActiveUrl(browser()));
123
124 // Navigate to chrome://extensions (no options). Calling OpenOptionsPage()
125 // should override that tab rather than opening a new tab. crbug.com/595253.
126 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIExtensionsURL));
127 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(options_in_view, browser()));
128 EXPECT_EQ(5, browser()->tab_strip_model()->count());
129 EXPECT_TRUE(content::WaitForLoadStop(
130 browser()->tab_strip_model()->GetActiveWebContents()));
131 EXPECT_EQ(options_url, GetActiveUrl(browser()));
132 }
133
134 // Flaky on Windows: http://crbug.com/745729
135 #if defined(OS_WIN)
136 #define MAYBE_OpenSplitModeExtensionOptionsPageIncognito \
137 DISABLED_OpenSplitModeExtensionOptionsPageIncognito
138 #else
139 #define MAYBE_OpenSplitModeExtensionOptionsPageIncognito \
140 OpenSplitModeExtensionOptionsPageIncognito
141 #endif
IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,MAYBE_OpenSplitModeExtensionOptionsPageIncognito)142 IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,
143 MAYBE_OpenSplitModeExtensionOptionsPageIncognito) {
144 const Extension* options_split_extension = LoadExtensionIncognito(
145 test_data_dir_.AppendASCII("options_page_split_incognito"));
146 ASSERT_TRUE(options_split_extension);
147 ASSERT_TRUE(OptionsPageInfo::HasOptionsPage(options_split_extension));
148 GURL options_url = OptionsPageInfo::GetOptionsPage(options_split_extension);
149
150 Browser* incognito = CreateIncognitoBrowser();
151
152 // There should be two browser windows open, regular and incognito.
153 EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
154
155 // In the regular browser window, start at the new tab page, and then open the
156 // extension options page.
157 ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
158 EXPECT_EQ(1, browser()->tab_strip_model()->count());
159 EXPECT_TRUE(
160 ExtensionTabUtil::OpenOptionsPage(options_split_extension, browser()));
161
162 // Opening the options page should take the new tab and use it, so we should
163 // have only one tab, and it should be open to the options page.
164 EXPECT_EQ(1, browser()->tab_strip_model()->count());
165 EXPECT_TRUE(content::WaitForLoadStop(
166 browser()->tab_strip_model()->GetActiveWebContents()));
167 EXPECT_EQ(options_url, GetActiveUrl(browser()));
168
169 // If the options page is already opened from a regular window, calling
170 // OpenOptionsPage() from an incognito window should not refocus to the
171 // options page in the regular window, but instead open the options page in
172 // the incognito window.
173 ui_test_utils::NavigateToURL(incognito, GURL(chrome::kChromeUINewTabURL));
174 EXPECT_EQ(1, incognito->tab_strip_model()->count());
175 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
176 incognito->profile()));
177 EXPECT_EQ(1, incognito->tab_strip_model()->count());
178 EXPECT_TRUE(content::WaitForLoadStop(
179 incognito->tab_strip_model()->GetActiveWebContents()));
180 EXPECT_EQ(options_url, GetActiveUrl(incognito));
181
182 // Both regular and incognito windows should have one tab each.
183 EXPECT_EQ(1, browser()->tab_strip_model()->count());
184 EXPECT_EQ(1, incognito->tab_strip_model()->count());
185
186 // Reset the incognito browser.
187 CloseBrowserSynchronously(incognito);
188 EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
189 incognito = CreateIncognitoBrowser();
190
191 // Close the regular browser.
192 CloseBrowserSynchronously(browser());
193 EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
194
195 // In the incognito browser, start at the new tab page, and then open the
196 // extension options page.
197 ui_test_utils::NavigateToURL(incognito, GURL(chrome::kChromeUINewTabURL));
198 EXPECT_EQ(1, incognito->tab_strip_model()->count());
199 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
200 incognito->profile()));
201
202 // Opening the options page should take the new tab and use it, so we should
203 // have only one tab, and it should be open to the options page.
204 EXPECT_EQ(1, incognito->tab_strip_model()->count());
205 EXPECT_TRUE(content::WaitForLoadStop(
206 incognito->tab_strip_model()->GetActiveWebContents()));
207 EXPECT_EQ(options_url, GetActiveUrl(incognito));
208
209 // Calling OpenOptionsPage again shouldn't result in any new tabs, since we
210 // re-use the existing options page.
211 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
212 incognito->profile()));
213 EXPECT_EQ(1, incognito->tab_strip_model()->count());
214 EXPECT_TRUE(content::WaitForLoadStop(
215 incognito->tab_strip_model()->GetActiveWebContents()));
216 EXPECT_EQ(options_url, GetActiveUrl(incognito));
217
218 // Navigate to google.com (something non-newtab, non-options). Calling
219 // OpenOptionsPage() should create a new tab and navigate it to the options
220 // page. So we should have two total tabs, with the active tab pointing to
221 // options.
222 ui_test_utils::NavigateToURL(incognito, GURL("http://www.google.com/"));
223 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
224 incognito->profile()));
225 EXPECT_EQ(2, incognito->tab_strip_model()->count());
226 EXPECT_TRUE(content::WaitForLoadStop(
227 incognito->tab_strip_model()->GetActiveWebContents()));
228 EXPECT_EQ(options_url, GetActiveUrl(incognito));
229 }
230
IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,OpenSpanningModeExtensionOptionsPageIncognito)231 IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,
232 OpenSpanningModeExtensionOptionsPageIncognito) {
233 const Extension* options_spanning_extension = LoadExtensionIncognito(
234 test_data_dir_.AppendASCII("options_page_spanning_incognito"));
235 ASSERT_TRUE(options_spanning_extension);
236 ASSERT_TRUE(OptionsPageInfo::HasOptionsPage(options_spanning_extension));
237 GURL options_url =
238 OptionsPageInfo::GetOptionsPage(options_spanning_extension);
239
240 // Start a regular browser window with two tabs, one that is non-options,
241 // non-newtab and the other that is the options page.
242 ui_test_utils::NavigateToURL(browser(), GURL("http://www.google.com/"));
243 EXPECT_TRUE(
244 ExtensionTabUtil::OpenOptionsPage(options_spanning_extension, browser()));
245 EXPECT_EQ(2, browser()->tab_strip_model()->count());
246 EXPECT_TRUE(content::WaitForLoadStop(
247 browser()->tab_strip_model()->GetActiveWebContents()));
248 EXPECT_EQ(options_url, GetActiveUrl(browser()));
249 // Switch to tab containing google.com such that it is the active tab.
250 browser()->tab_strip_model()->SelectPreviousTab();
251 EXPECT_EQ(GURL("http://www.google.com/"), GetActiveUrl(browser()));
252
253 // Spanning mode extensions can never open pages in incognito so a regular
254 // (non-OTR) profile must be used. If the options page is already opened from
255 // a regular window, calling OpenOptionsPage() from an incognito window should
256 // refocus to the options page in the regular window.
257 Browser* incognito = CreateIncognitoBrowser();
258 ui_test_utils::NavigateToURL(incognito, GURL(chrome::kChromeUINewTabURL));
259 EXPECT_EQ(1, incognito->tab_strip_model()->count());
260 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(
261 options_spanning_extension, profile()));
262 // There should be two browser windows open, regular and incognito.
263 EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
264 // Ensure that the regular browser is the foreground browser.
265 EXPECT_EQ(browser(), BrowserList::GetInstance()->GetLastActive());
266 // The options page in the regular window should be in focus instead of
267 // the tab pointing to www.google.com.
268 EXPECT_TRUE(content::WaitForLoadStop(
269 browser()->tab_strip_model()->GetActiveWebContents()));
270 EXPECT_EQ(options_url, GetActiveUrl(browser()));
271
272 // Only the incognito browser should be left.
273 CloseBrowserSynchronously(browser());
274 EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
275
276 // Start at the new tab page in incognito and open the extension options page.
277 ui_test_utils::NavigateToURL(incognito, GURL(chrome::kChromeUINewTabURL));
278 EXPECT_EQ(1, incognito->tab_strip_model()->count());
279 EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(
280 options_spanning_extension, profile()));
281
282 // Opening the options page from an incognito window should open a new regular
283 // profile window, which should have one tab open to the options page.
284 ASSERT_EQ(2u, chrome::GetTotalBrowserCount());
285 BrowserList* browser_list = BrowserList::GetInstance();
286 Browser* regular = !browser_list->get(0u)->profile()->IsOffTheRecord()
287 ? browser_list->get(0u)
288 : browser_list->get(1u);
289 EXPECT_EQ(1, regular->tab_strip_model()->count());
290 EXPECT_TRUE(content::WaitForLoadStop(
291 regular->tab_strip_model()->GetActiveWebContents()));
292 EXPECT_EQ(options_url, GetActiveUrl(regular));
293
294 // Leave only incognito browser open.
295 CloseBrowserSynchronously(regular);
296 EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
297
298 // Right-clicking on an extension action icon in the toolbar and selecting
299 // options should open the options page in a regular window. In this case, the
300 // profile is an OTR profile instead of a non-OTR profile, as described above.
301 ui_test_utils::NavigateToURL(incognito, GURL(chrome::kChromeUINewTabURL));
302 EXPECT_EQ(1, incognito->tab_strip_model()->count());
303 // Because the OpenOptionsPage() call originates from an OTR window via, e.g.
304 // the action menu, instead of initiated by the extension, the
305 // OpenOptionsPage() version that takes a Browser* is used.
306 EXPECT_TRUE(
307 ExtensionTabUtil::OpenOptionsPage(options_spanning_extension, incognito));
308 // There should be two browser windows open, regular and incognito.
309 EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
310 browser_list = BrowserList::GetInstance();
311 regular = !browser_list->get(0u)->profile()->IsOffTheRecord()
312 ? browser_list->get(0u)
313 : browser_list->get(1u);
314 // Ensure that the regular browser is the foreground browser.
315 EXPECT_EQ(regular, browser_list->GetLastActive());
316 EXPECT_EQ(1, regular->tab_strip_model()->count());
317 EXPECT_TRUE(content::WaitForLoadStop(
318 regular->tab_strip_model()->GetActiveWebContents()));
319 EXPECT_EQ(options_url, GetActiveUrl(regular));
320 }
321
322 } // namespace extensions
323