blob: 3fa9767f789824b60018c235042cb228dd0486a3 [file] [log] [blame]
Dominick Ngc4a60b32018-04-19 05:18:491// Copyright 2018 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 "chrome/browser/extensions/bookmark_app_navigation_browsertest.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/profiles/profile_io_data.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/browser/ui/browser_finder.h"
13#include "chrome/browser/ui/tabs/tab_strip_model.h"
14#include "chrome/common/web_application_info.h"
15#include "chrome/test/base/ui_test_utils.h"
16#include "content/public/browser/render_frame_host.h"
17#include "content/public/browser/web_contents.h"
18#include "content/public/common/content_switches.h"
19#include "content/public/test/browser_test_utils.h"
20#include "content/public/test/test_navigation_observer.h"
21#include "net/base/escape.h"
22#include "net/dns/mock_host_resolver.h"
23#include "net/test/embedded_test_server/http_request.h"
24#include "net/test/embedded_test_server/http_response.h"
25
26namespace {
27
28const char kLaunchingPageHost[] = "launching-page.com";
29const char kLaunchingPagePath[] = "/index.html";
30
31const char kAppUrlHost[] = "app.com";
32const char kOtherAppUrlHost[] = "other-app.com";
33const char kAppScopePath[] = "/in_scope/";
34const char kAppUrlPath[] = "/in_scope/index.html";
35const char kInScopeUrlPath[] = "/in_scope/other.html";
36const char kOutOfScopeUrlPath[] = "/out_of_scope/index.html";
37
38const char kAppName[] = "Test app";
39
40const base::FilePath::CharType kDocRoot[] =
41 FILE_PATH_LITERAL("chrome/test/data");
42
43bool HasOpenedWindowAndOpener(content::WebContents* opener_contents,
44 content::WebContents* opened_contents) {
45 bool has_opener;
46 CHECK(content::ExecuteScriptAndExtractBool(
47 opened_contents, "window.domAutomationController.send(!!window.opener);",
48 &has_opener));
49
50 bool has_openedWindow;
51 CHECK(content::ExecuteScriptAndExtractBool(
52 opener_contents,
53 "window.domAutomationController.send(!!window.openedWindow.window)",
54 &has_openedWindow));
55
56 return has_opener && has_openedWindow;
57}
58
59// Wrapper so that we can use base::BindOnce with NavigateToURL.
60void NavigateToURLWrapper(NavigateParams* params) {
61 ui_test_utils::NavigateToURL(params);
62}
63
64} // anonymous namespace
65
66namespace extensions {
67namespace test {
68
69// static
70const char* BookmarkAppNavigationBrowserTest::GetLaunchingPageHost() {
71 return kLaunchingPageHost;
72}
73
74// static
75const char* BookmarkAppNavigationBrowserTest::GetLaunchingPagePath() {
76 return kLaunchingPagePath;
77}
78
79// static
80const char* BookmarkAppNavigationBrowserTest::GetAppUrlHost() {
81 return kAppUrlHost;
82}
83
84// static
85const char* BookmarkAppNavigationBrowserTest::GetOtherAppUrlHost() {
86 return kOtherAppUrlHost;
87}
88
89// static
90const char* BookmarkAppNavigationBrowserTest::GetAppScopePath() {
91 return kAppScopePath;
92}
93
94// static
95const char* BookmarkAppNavigationBrowserTest::GetAppUrlPath() {
96 return kAppUrlPath;
97}
98
99// static
100const char* BookmarkAppNavigationBrowserTest::GetInScopeUrlPath() {
101 return kInScopeUrlPath;
102}
103
104// static
105const char* BookmarkAppNavigationBrowserTest::GetOutOfScopeUrlPath() {
106 return kOutOfScopeUrlPath;
107}
108
109// static
110const char* BookmarkAppNavigationBrowserTest::GetAppName() {
111 return kAppName;
112}
113
114// static
115std::string BookmarkAppNavigationBrowserTest::CreateServerRedirect(
116 const GURL& target_url) {
117 const char* const kServerRedirectBase = "/server-redirect?";
118 return kServerRedirectBase +
119 net::EscapeQueryParamValue(target_url.spec(), false);
120}
121
122// static
123std::unique_ptr<content::TestNavigationObserver>
124BookmarkAppNavigationBrowserTest::GetTestNavigationObserver(
125 const GURL& target_url) {
126 auto observer = std::make_unique<content::TestNavigationObserver>(target_url);
127 observer->WatchExistingWebContents();
128 observer->StartWatchingNewWebContents();
129 return observer;
130}
131
132// static
133content::RenderFrameHost* BookmarkAppNavigationBrowserTest::GetIFrame(
134 content::WebContents* web_contents) {
135 const auto all_frames = web_contents->GetAllFrames();
136 const content::RenderFrameHost* main_frame = web_contents->GetMainFrame();
137
138 DCHECK_EQ(2u, all_frames.size());
139 auto it = std::find_if(all_frames.begin(), all_frames.end(),
140 [main_frame](content::RenderFrameHost* frame) {
141 return main_frame != frame;
142 });
143 DCHECK(it != all_frames.end());
144 return *it;
145}
146
147// static
148void BookmarkAppNavigationBrowserTest::ClickLinkWithModifiersAndWaitForURL(
149 content::WebContents* web_contents,
150 const GURL& link_url,
151 const GURL& target_url,
152 BookmarkAppNavigationBrowserTest::LinkTarget target,
153 const std::string& rel,
154 int modifiers) {
155 auto observer = GetTestNavigationObserver(target_url);
156 std::string script = base::StringPrintf(
157 "(() => {"
158 "const link = document.createElement('a');"
159 "link.href = '%s';"
160 "link.target = '%s';"
161 "link.rel = '%s';"
162 // Make a click target that covers the whole viewport.
163 "const click_target = document.createElement('textarea');"
164 "click_target.position = 'absolute';"
165 "click_target.top = 0;"
166 "click_target.left = 0;"
167 "click_target.style.height = '100vh';"
168 "click_target.style.width = '100vw';"
169 "link.appendChild(click_target);"
170 "document.body.appendChild(link);"
171 "})();",
172 link_url.spec().c_str(), target == LinkTarget::SELF ? "_self" : "_blank",
173 rel.c_str());
174 ASSERT_TRUE(content::ExecuteScript(web_contents, script));
175
176 content::SimulateMouseClick(web_contents, modifiers,
177 blink::WebMouseEvent::Button::kLeft);
178
179 observer->WaitForNavigationFinished();
180}
181
182// static
183void BookmarkAppNavigationBrowserTest::ClickLinkAndWaitForURL(
184 content::WebContents* web_contents,
185 const GURL& link_url,
186 const GURL& target_url,
187 BookmarkAppNavigationBrowserTest::LinkTarget target,
188 const std::string& rel) {
189 ClickLinkWithModifiersAndWaitForURL(
190 web_contents, link_url, target_url, target, rel,
191 blink::WebInputEvent::Modifiers::kNoModifiers);
192}
193
194// static
195void BookmarkAppNavigationBrowserTest::ClickLinkAndWait(
196 content::WebContents* web_contents,
197 const GURL& link_url,
198 BookmarkAppNavigationBrowserTest::LinkTarget target,
199 const std::string& rel) {
200 ClickLinkAndWaitForURL(web_contents, link_url, link_url, target, rel);
201}
202
203// static
204void BookmarkAppNavigationBrowserTest::ClickLinkWithModifiersAndWait(
205 content::WebContents* web_contents,
206 const GURL& link_url,
207 BookmarkAppNavigationBrowserTest::LinkTarget target,
208 const std::string& rel,
209 int modifiers) {
210 ClickLinkWithModifiersAndWaitForURL(web_contents, link_url, link_url, target,
211 rel, modifiers);
212}
213
214BookmarkAppNavigationBrowserTest::BookmarkAppNavigationBrowserTest()
215 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
216 mock_cert_verifier_(),
217 cert_verifier_(&mock_cert_verifier_) {}
218
219BookmarkAppNavigationBrowserTest::~BookmarkAppNavigationBrowserTest() = default;
220
221void BookmarkAppNavigationBrowserTest::SetUp() {
222 https_server_.AddDefaultHandlers(base::FilePath(kDocRoot));
223 // Register a request handler that will return empty pages. Tests are
224 // responsible for adding elements and firing events on these empty pages.
225 https_server_.RegisterRequestHandler(
226 base::BindRepeating([](const net::test_server::HttpRequest& request) {
227 // Let the default request handlers handle redirections.
228 if (request.GetURL().path() == "/server-redirect" ||
229 request.GetURL().path() == "/client-redirect") {
230 return std::unique_ptr<net::test_server::HttpResponse>();
231 }
232 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
233 response->set_content_type("text/html");
234 response->AddCustomHeader("Access-Control-Allow-Origin", "*");
235 return static_cast<std::unique_ptr<net::test_server::HttpResponse>>(
236 std::move(response));
237 }));
238
239 ExtensionBrowserTest::SetUp();
240}
241
242void BookmarkAppNavigationBrowserTest::SetUpInProcessBrowserTestFixture() {
243 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
244 ProfileIOData::SetCertVerifierForTesting(&mock_cert_verifier_);
245}
246
247void BookmarkAppNavigationBrowserTest::TearDownInProcessBrowserTestFixture() {
248 ExtensionBrowserTest::TearDownInProcessBrowserTestFixture();
249 ProfileIOData::SetCertVerifierForTesting(nullptr);
250}
251
252void BookmarkAppNavigationBrowserTest::SetUpCommandLine(
253 base::CommandLine* command_line) {
254 command_line->AppendSwitch(switches::kUseMockCertVerifierForTesting);
255}
256
257void BookmarkAppNavigationBrowserTest::SetUpOnMainThread() {
258 ExtensionBrowserTest::SetUpOnMainThread();
259 host_resolver()->AddRule("*", "127.0.0.1");
260 // By default, all SSL cert checks are valid. Can be overriden in tests.
261 cert_verifier_.set_default_result(net::OK);
262}
263
264void BookmarkAppNavigationBrowserTest::InstallTestBookmarkApp() {
265 test_bookmark_app_ = InstallTestBookmarkApp(GetAppUrlHost());
266}
267
268void BookmarkAppNavigationBrowserTest::InstallOtherTestBookmarkApp() {
269 InstallTestBookmarkApp(GetOtherAppUrlHost());
270}
271
272const Extension* BookmarkAppNavigationBrowserTest::InstallTestBookmarkApp(
273 const std::string& app_host) {
274 if (!https_server_.Started()) {
275 CHECK(https_server_.Start());
276 }
277
278 WebApplicationInfo web_app_info;
279 web_app_info.app_url = https_server_.GetURL(app_host, GetAppUrlPath());
280 web_app_info.scope = https_server_.GetURL(app_host, GetAppScopePath());
281 web_app_info.title = base::UTF8ToUTF16(GetAppName());
282 web_app_info.description = base::UTF8ToUTF16("Test description");
283 web_app_info.open_as_window = true;
284
285 return InstallBookmarkApp(web_app_info);
286}
287
288const Extension*
289BookmarkAppNavigationBrowserTest::InstallImmediateRedirectingApp(
290 const std::string& target_host,
291 const std::string& target_path) {
292 EXPECT_TRUE(https_server_.Start());
293 const GURL target_url = https_server_.GetURL(target_host, target_path);
294
295 WebApplicationInfo web_app_info;
296 web_app_info.app_url =
297 https_server_.GetURL(GetAppUrlHost(), CreateServerRedirect(target_url));
298 web_app_info.scope = https_server_.GetURL(GetAppUrlHost(), "/");
299 web_app_info.title = base::UTF8ToUTF16("Redirecting Test app");
300 web_app_info.description = base::UTF8ToUTF16("Test description");
301 web_app_info.open_as_window = true;
302
303 return InstallBookmarkApp(web_app_info);
304}
305
306Browser* BookmarkAppNavigationBrowserTest::OpenTestBookmarkApp() {
307 GURL app_url = https_server_.GetURL(GetAppUrlHost(), GetAppUrlPath());
308 auto observer = GetTestNavigationObserver(app_url);
309 Browser* app_browser = LaunchAppBrowser(test_bookmark_app_);
310 observer->WaitForNavigationFinished();
311
312 return app_browser;
313}
314
315void BookmarkAppNavigationBrowserTest::NavigateToLaunchingPage(
316 Browser* browser) {
317 ui_test_utils::NavigateToURL(browser, GetLaunchingPageURL());
318}
319
320void BookmarkAppNavigationBrowserTest::NavigateToLaunchingPage() {
321 NavigateToLaunchingPage(browser());
322}
323
324void BookmarkAppNavigationBrowserTest::NavigateToTestAppURL() {
325 const GURL app_url = https_server_.GetURL(GetAppUrlHost(), GetAppUrlPath());
326 NavigateParams params(browser(), app_url, ui::PAGE_TRANSITION_TYPED);
327 ASSERT_TRUE(TestTabActionDoesNotOpenAppWindow(
328 app_url, base::BindOnce(&NavigateToURLWrapper, &params)));
329}
330
331void BookmarkAppNavigationBrowserTest::
332 TestTabActionDoesNotNavigateOrOpenAppWindow(base::OnceClosure action) {
333 size_t num_browsers = chrome::GetBrowserCount(profile());
334 int num_tabs = browser()->tab_strip_model()->count();
335 content::WebContents* initial_tab =
336 browser()->tab_strip_model()->GetActiveWebContents();
337 GURL initial_url = initial_tab->GetLastCommittedURL();
338
339 std::move(action).Run();
340
341 EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
342 EXPECT_EQ(browser(), chrome::FindLastActive());
343 EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
344 EXPECT_EQ(initial_tab, browser()->tab_strip_model()->GetActiveWebContents());
345 EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
346}
347
348void BookmarkAppNavigationBrowserTest::TestTabActionOpensBackgroundTab(
349 const GURL& target_url,
350 base::OnceClosure action) {
351 size_t num_browsers = chrome::GetBrowserCount(profile());
352 int num_tabs = browser()->tab_strip_model()->count();
353 content::WebContents* initial_tab =
354 browser()->tab_strip_model()->GetActiveWebContents();
355 GURL initial_url = initial_tab->GetLastCommittedURL();
356
357 std::move(action).Run();
358
359 EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
360 EXPECT_EQ(browser(), chrome::FindLastActive());
361 EXPECT_EQ(++num_tabs, browser()->tab_strip_model()->count());
362 EXPECT_EQ(initial_tab, browser()->tab_strip_model()->GetActiveWebContents());
363 EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
364
365 content::WebContents* new_tab =
366 browser()->tab_strip_model()->GetWebContentsAt(num_tabs - 1);
367 EXPECT_NE(new_tab, initial_tab);
368 EXPECT_EQ(target_url, new_tab->GetLastCommittedURL());
369}
370
371void BookmarkAppNavigationBrowserTest::TestTabActionOpensForegroundWindow(
372 const GURL& target_url,
373 base::OnceClosure action) {
374 size_t num_browsers = chrome::GetBrowserCount(profile());
375 int num_tabs = browser()->tab_strip_model()->count();
376 content::WebContents* initial_tab =
377 browser()->tab_strip_model()->GetActiveWebContents();
378 GURL initial_url = initial_tab->GetLastCommittedURL();
379
380 std::move(action).Run();
381
382 EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
383
384 Browser* new_window = chrome::FindLastActive();
385 EXPECT_NE(new_window, browser());
386 EXPECT_FALSE(new_window->is_app());
387 EXPECT_EQ(target_url, new_window->tab_strip_model()
388 ->GetActiveWebContents()
389 ->GetLastCommittedURL());
390
391 EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
392 EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
393}
394
395void BookmarkAppNavigationBrowserTest::TestTabActionOpensAppWindow(
396 const GURL& target_url,
397 base::OnceClosure action) {
398 content::WebContents* initial_tab =
399 browser()->tab_strip_model()->GetActiveWebContents();
400 GURL initial_url = initial_tab->GetLastCommittedURL();
401 int num_tabs = browser()->tab_strip_model()->count();
402 size_t num_browsers = chrome::GetBrowserCount(profile());
403
404 std::move(action).Run();
405
406 EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
407 EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
408 EXPECT_NE(browser(), chrome::FindLastActive());
409
410 EXPECT_EQ(initial_url, initial_tab->GetLastCommittedURL());
411 EXPECT_EQ(target_url, chrome::FindLastActive()
412 ->tab_strip_model()
413 ->GetActiveWebContents()
414 ->GetLastCommittedURL());
415}
416
417void BookmarkAppNavigationBrowserTest::TestTabActionOpensAppWindowWithOpener(
418 const GURL& target_url,
419 base::OnceClosure action) {
420 TestTabActionOpensAppWindow(target_url, std::move(action));
421
422 content::WebContents* initial_web_contents =
423 browser()->tab_strip_model()->GetActiveWebContents();
424 content::WebContents* app_web_contents =
425 chrome::FindLastActive()->tab_strip_model()->GetActiveWebContents();
426
427 EXPECT_TRUE(HasOpenedWindowAndOpener(initial_web_contents, app_web_contents));
428}
429
430bool BookmarkAppNavigationBrowserTest::TestActionDoesNotOpenAppWindow(
431 Browser* browser,
432 const GURL& target_url,
433 base::OnceClosure action) {
434 content::WebContents* initial_tab =
435 browser->tab_strip_model()->GetActiveWebContents();
436 int num_tabs = browser->tab_strip_model()->count();
437 size_t num_browsers = chrome::GetBrowserCount(browser->profile());
438
439 std::move(action).Run();
440
441 EXPECT_EQ(num_tabs, browser->tab_strip_model()->count());
442 EXPECT_EQ(num_browsers, chrome::GetBrowserCount(browser->profile()));
443 EXPECT_EQ(browser, chrome::FindLastActive());
444 EXPECT_EQ(initial_tab, browser->tab_strip_model()->GetActiveWebContents());
445 EXPECT_EQ(target_url, initial_tab->GetLastCommittedURL());
446
447 return !HasFailure();
448}
449
450void BookmarkAppNavigationBrowserTest::TestAppActionOpensForegroundTab(
451 Browser* app_browser,
452 const GURL& target_url,
453 base::OnceClosure action) {
454 size_t num_browsers = chrome::GetBrowserCount(profile());
455 int num_tabs_browser = browser()->tab_strip_model()->count();
456 int num_tabs_app_browser = app_browser->tab_strip_model()->count();
457
458 content::WebContents* app_web_contents =
459 app_browser->tab_strip_model()->GetActiveWebContents();
460 content::WebContents* initial_tab =
461 browser()->tab_strip_model()->GetActiveWebContents();
462
463 GURL initial_app_url = app_web_contents->GetLastCommittedURL();
464 GURL initial_tab_url = initial_tab->GetLastCommittedURL();
465
466 std::move(action).Run();
467
468 EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
469
470 EXPECT_EQ(browser(), chrome::FindLastActive());
471
472 EXPECT_EQ(++num_tabs_browser, browser()->tab_strip_model()->count());
473 EXPECT_EQ(num_tabs_app_browser, app_browser->tab_strip_model()->count());
474
475 EXPECT_EQ(initial_app_url, app_web_contents->GetLastCommittedURL());
476
477 content::WebContents* new_tab =
478 browser()->tab_strip_model()->GetActiveWebContents();
479 EXPECT_NE(initial_tab, new_tab);
480 EXPECT_EQ(target_url, new_tab->GetLastCommittedURL());
481}
482
483void BookmarkAppNavigationBrowserTest::TestAppActionOpensAppWindowWithOpener(
484 Browser* app_browser,
485 const GURL& target_url,
486 base::OnceClosure action) {
487 size_t num_browsers = chrome::GetBrowserCount(profile());
488 int num_tabs_browser = browser()->tab_strip_model()->count();
489 int num_tabs_app_browser = app_browser->tab_strip_model()->count();
490
491 content::WebContents* app_web_contents =
492 app_browser->tab_strip_model()->GetActiveWebContents();
493 content::WebContents* initial_tab =
494 browser()->tab_strip_model()->GetActiveWebContents();
495
496 GURL initial_app_url = app_web_contents->GetLastCommittedURL();
497 GURL initial_tab_url = initial_tab->GetLastCommittedURL();
498
499 std::move(action).Run();
500
501 EXPECT_EQ(++num_browsers, chrome::GetBrowserCount(profile()));
502
503 Browser* new_app_browser = chrome::FindLastActive();
504 EXPECT_NE(new_app_browser, browser());
505 EXPECT_NE(new_app_browser, app_browser);
506 EXPECT_TRUE(new_app_browser->is_app());
507
508 EXPECT_EQ(num_tabs_browser, browser()->tab_strip_model()->count());
509 EXPECT_EQ(num_tabs_app_browser, app_browser->tab_strip_model()->count());
510
511 EXPECT_EQ(initial_app_url, app_web_contents->GetLastCommittedURL());
512
513 content::WebContents* new_app_web_contents =
514 new_app_browser->tab_strip_model()->GetActiveWebContents();
515 EXPECT_EQ(target_url, new_app_web_contents->GetLastCommittedURL());
516
517 EXPECT_TRUE(HasOpenedWindowAndOpener(app_web_contents, new_app_web_contents));
518}
519
520bool BookmarkAppNavigationBrowserTest::TestTabActionDoesNotOpenAppWindow(
521 const GURL& target_url,
522 base::OnceClosure action) {
523 return TestActionDoesNotOpenAppWindow(browser(), target_url,
524 std::move(action));
525}
526
527bool BookmarkAppNavigationBrowserTest::TestIFrameActionDoesNotOpenAppWindow(
528 const GURL& target_url,
529 base::OnceClosure action) {
530 size_t num_browsers = chrome::GetBrowserCount(profile());
531 int num_tabs = browser()->tab_strip_model()->count();
532 content::WebContents* initial_tab =
533 browser()->tab_strip_model()->GetActiveWebContents();
534
535 std::move(action).Run();
536
537 EXPECT_EQ(num_browsers, chrome::GetBrowserCount(profile()));
538 EXPECT_EQ(browser(), chrome::FindLastActive());
539 EXPECT_EQ(num_tabs, browser()->tab_strip_model()->count());
540
541 // When Site Isolation is enabled, navigating the iframe to a different
542 // origin causes the original iframe's RenderFrameHost to be deleted.
543 // So we retrieve the iframe's RenderFrameHost again.
544 EXPECT_EQ(target_url, GetIFrame(initial_tab)->GetLastCommittedURL());
545
546 return !HasFailure();
547}
548
549GURL BookmarkAppNavigationBrowserTest::GetLaunchingPageURL() {
550 return https_server_.GetURL(GetLaunchingPageHost(), GetLaunchingPagePath());
551}
552
553} // namespace test
554} // namespace extensions