1 // Copyright 2013 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/strings/stringprintf.h" 6 #include "base/strings/utf_string_conversions.h" 7 #include "base/win/windows_version.h" 8 #include "chrome/browser/apps/app_browsertest_util.h" 9 #include "chrome/browser/chrome_notification_types.h" 10 #include "chrome/browser/extensions/extension_test_message_listener.h" 11 #include "chrome/browser/ui/tabs/tab_strip_model.h" 12 #include "chrome/common/chrome_switches.h" 13 #include "chrome/test/base/ui_test_utils.h" 14 #include "content/public/common/page_transition_types.h" 15 #include "content/public/test/browser_test_base.h" 16 #include "content/public/test/browser_test_utils.h" 17 #include "net/test/embedded_test_server/embedded_test_server.h" 18 19 namespace extensions { 20 21 class PlatformAppUrlRedirectorBrowserTest : public PlatformAppBrowserTest { 22 public: 23 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; 24 25 protected: 26 // Performs the following sequence: 27 // - installs the app |handler| (a relative path under the platform_apps 28 // subdirectory); 29 // - navigates the current tab to the HTML page |lancher_page| (ditto); 30 // - then waits for |handler| to launch and send back a |handler_ack_message|; 31 // - finally checks that the resulting app window count is as expected. 32 // The |launcher_page| is supposed to trigger a navigation matching one of the 33 // url_handlers in the |handler|'s manifest, and thereby launch the |handler|. 34 void TestNavigationInTab(const char* launcher_page, 35 const char* handler, 36 const char* handler_start_message); 37 38 // The same as above, but does not expect the |handler| to launch. Verifies 39 // that it didn't, and that the navigation has opened in a new tab instead. 40 void TestMismatchingNavigationInTab(const char* launcher_page, 41 const char* success_tab_title, 42 const char* handler); 43 44 // - installs the app |handler|; 45 // - opens the page |xhr_opening_page| in the current tab; 46 // - the page sends an XHR to a local resouce, whose URL matches one of the 47 // url_handlers in |handler|; 48 // - waits until |xhr_opening_page| gets a response back and changes the tab's 49 // title to a value indicating success/failure of the XHR; 50 // - verifies that no app windows have been opened, i.e. |handler| wasn't 51 // launched even though its url_handlers match the URL. 52 void TestNegativeXhrInTab(const char* xhr_opening_page, 53 const char* success_tab_title, 54 const char* failure_tab_title, 55 const char* handler); 56 57 // Performs the following sequence: 58 // - installs the app |handler| (a relative path under the platform_apps 59 // subdirectory); 60 // - loads and launches the app |launcher| (ditto); 61 // - waits for the |launcher| to launch and send back a |launcher_ack_message| 62 // (to make sure it's not the failing entity, if the test fails overall); 63 // - waits for the |handler| to launch and send back a |handler_ack_message|; 64 // - finally checks that the resulting app window count is as expected. 65 // The |launcher| is supposed to trigger a navigation matching one of the 66 // url_handlers in the |handler|'s manifest, and thereby launch the |handler|. 67 void TestNavigationInApp(const char* launcher, 68 const char* launcher_done_message, 69 const char* handler, 70 const char* handler_start_message); 71 72 // The same as above, but does not expect the |handler| to launch. Verifies 73 // that it didn't, and that the navigation has opened in a new tab instead. 74 void TestMismatchingNavigationInApp(const char* launcher, 75 const char* launcher_done_message, 76 const char* handler); 77 78 // - installs the |handler| app; 79 // - loads and launches the |launcher| app; 80 // - waits until the |launcher| sends back a |launcher_done_message|; 81 // - the launcher performs a navigation to a URL that mismatches the 82 // |handler|'s url_handlers; 83 // - verifies that the |handler| hasn't been launched as a result of the 84 // navigation. 85 void TestNegativeNavigationInApp(const char* launcher, 86 const char* launcher_done_message, 87 const char* handler); 88 89 // - installs the app |handler|; 90 // - navigates the current tab to the HTML page |matching_target_page| with 91 // page transition |transition|; 92 // - waits for |handler| to launch and send back a |handler_start_message|; 93 // - finally checks that the resulting app window count is as expected. 94 void TestNavigationInBrowser(const char* matching_target_page, 95 content::PageTransition transition, 96 const char* handler, 97 const char* handler_start_message); 98 99 // Same as above, but does not expect |handler| to launch. This is used, e.g. 100 // for form submissions, where the URL would normally match the url_handlers 101 // but should not launch it. 102 void TestNegativeNavigationInBrowser(const char* matching_target_page, 103 content::PageTransition transition, 104 const char* success_tab_title, 105 const char* handler); 106 107 // Same as above, but expects the |mismatching_target_page| should not match 108 // any of the |handler|'s url_handlers, and therefor not launch the app. 109 void TestMismatchingNavigationInBrowser(const char* mismatching_target_page, 110 content::PageTransition transition, 111 const char* success_tab_title, 112 const char* handler); 113 }; 114 115 116 void PlatformAppUrlRedirectorBrowserTest::SetUpCommandLine( 117 CommandLine* command_line) { 118 PlatformAppBrowserTest::SetUpCommandLine(command_line); 119 command_line->AppendSwitch(::switches::kDisablePopupBlocking); 120 command_line->AppendSwitchASCII(::switches::kPrerenderMode, 121 ::switches::kPrerenderModeSwitchValueEnabled); 122 } 123 124 // TODO(sergeygs): Factor out common functionality from TestXyz, 125 // TestNegativeXyz, and TestMismatchingXyz versions. 126 127 // TODO(sergeys): Return testing::AssertionErrors from these methods to 128 // preserve line numbers and (if applicable) failure messages. 129 130 void PlatformAppUrlRedirectorBrowserTest::TestNavigationInTab( 131 const char* launcher_page, 132 const char* handler, 133 const char* handler_start_message) { 134 ASSERT_TRUE(StartEmbeddedTestServer()); 135 136 InstallPlatformApp(handler); 137 138 ExtensionTestMessageListener handler_listener(handler_start_message, false); 139 140 ui_test_utils::NavigateToURLWithDisposition( 141 browser(), 142 embedded_test_server()->GetURL(base::StringPrintf( 143 "/extensions/platform_apps/%s", launcher_page)), 144 CURRENT_TAB, 145 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 146 147 ASSERT_TRUE(handler_listener.WaitUntilSatisfied()); 148 149 ASSERT_EQ(1U, GetAppWindowCount()); 150 } 151 152 void PlatformAppUrlRedirectorBrowserTest::TestMismatchingNavigationInTab( 153 const char* launcher_page, 154 const char* success_tab_title, 155 const char* handler) { 156 ASSERT_TRUE(StartEmbeddedTestServer()); 157 158 InstallPlatformApp(handler); 159 160 const base::string16 success_title = base::ASCIIToUTF16(success_tab_title); 161 content::WebContents* tab = 162 browser()->tab_strip_model()->GetActiveWebContents(); 163 content::TitleWatcher title_watcher(tab, success_title); 164 165 ui_test_utils::NavigateToURLWithDisposition( 166 browser(), 167 embedded_test_server()->GetURL(base::StringPrintf( 168 "/extensions/platform_apps/%s", launcher_page)), 169 CURRENT_TAB, 170 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 171 172 ASSERT_EQ(success_title, title_watcher.WaitAndGetTitle()); 173 ASSERT_EQ(1, browser()->tab_strip_model()->count()); 174 ASSERT_EQ(0U, GetAppWindowCount()); 175 } 176 177 void PlatformAppUrlRedirectorBrowserTest::TestNegativeXhrInTab( 178 const char* launcher_page, 179 const char* success_tab_title, 180 const char* failure_tab_title, 181 const char* handler) { 182 ASSERT_TRUE(StartEmbeddedTestServer()); 183 184 InstallPlatformApp(handler); 185 186 const base::string16 success_title = base::ASCIIToUTF16(success_tab_title); 187 const base::string16 failure_title = base::ASCIIToUTF16(failure_tab_title); 188 content::WebContents* tab = 189 browser()->tab_strip_model()->GetActiveWebContents(); 190 content::TitleWatcher title_watcher(tab, success_title); 191 title_watcher.AlsoWaitForTitle(failure_title); 192 193 ui_test_utils::NavigateToURLWithDisposition( 194 browser(), 195 embedded_test_server()->GetURL(base::StringPrintf( 196 "/extensions/platform_apps/%s", launcher_page)), 197 CURRENT_TAB, 198 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 199 200 ASSERT_EQ(success_title, title_watcher.WaitAndGetTitle()); 201 ASSERT_EQ(1, browser()->tab_strip_model()->count()); 202 ASSERT_EQ(0U, GetAppWindowCount()); 203 } 204 205 void PlatformAppUrlRedirectorBrowserTest::TestNavigationInApp( 206 const char* launcher, 207 const char* launcher_done_message, 208 const char* handler, 209 const char* handler_start_message) { 210 ASSERT_TRUE(StartEmbeddedTestServer()); 211 212 InstallPlatformApp(handler); 213 214 ExtensionTestMessageListener handler_listener(handler_start_message, false); 215 216 LoadAndLaunchPlatformApp(launcher, launcher_done_message); 217 218 ASSERT_TRUE(handler_listener.WaitUntilSatisfied()); 219 220 ASSERT_EQ(2U, GetAppWindowCount()); 221 } 222 223 void PlatformAppUrlRedirectorBrowserTest::TestNegativeNavigationInApp( 224 const char* launcher, 225 const char* launcher_done_message, 226 const char* handler) { 227 ASSERT_TRUE(StartEmbeddedTestServer()); 228 229 InstallPlatformApp(handler); 230 231 content::WindowedNotificationObserver observer( 232 chrome::NOTIFICATION_TAB_ADDED, 233 content::Source<content::WebContentsDelegate>(browser())); 234 235 LoadAndLaunchPlatformApp(launcher, launcher_done_message); 236 237 observer.Wait(); 238 239 ASSERT_EQ(1U, GetAppWindowCount()); 240 } 241 242 void PlatformAppUrlRedirectorBrowserTest::TestMismatchingNavigationInApp( 243 const char* launcher, 244 const char* launcher_done_message, 245 const char* handler) { 246 ASSERT_TRUE(StartEmbeddedTestServer()); 247 248 InstallPlatformApp(handler); 249 250 content::WindowedNotificationObserver observer( 251 chrome::NOTIFICATION_TAB_ADDED, 252 content::Source<content::WebContentsDelegate>(browser())); 253 254 LoadAndLaunchPlatformApp(launcher, launcher_done_message); 255 256 observer.Wait(); 257 ASSERT_EQ(1U, GetAppWindowCount()); 258 ASSERT_EQ(2, browser()->tab_strip_model()->count()); 259 } 260 261 void PlatformAppUrlRedirectorBrowserTest::TestNavigationInBrowser( 262 const char* matching_target_page, 263 content::PageTransition transition, 264 const char* handler, 265 const char* handler_start_message) { 266 ASSERT_TRUE(StartEmbeddedTestServer()); 267 268 InstallPlatformApp(handler); 269 270 ExtensionTestMessageListener handler_listener(handler_start_message, false); 271 272 chrome::NavigateParams params( 273 browser(), 274 embedded_test_server()->GetURL(base::StringPrintf( 275 "/extensions/platform_apps/%s", matching_target_page)), 276 transition); 277 ui_test_utils::NavigateToURL(¶ms); 278 279 ASSERT_TRUE(handler_listener.WaitUntilSatisfied()); 280 281 ASSERT_EQ(1U, GetAppWindowCount()); 282 } 283 284 void PlatformAppUrlRedirectorBrowserTest::TestNegativeNavigationInBrowser( 285 const char* matching_target_page, 286 content::PageTransition transition, 287 const char* success_tab_title, 288 const char* handler) { 289 ASSERT_TRUE(StartEmbeddedTestServer()); 290 291 InstallPlatformApp(handler); 292 293 const base::string16 success_title = base::ASCIIToUTF16(success_tab_title); 294 content::WebContents* tab = 295 browser()->tab_strip_model()->GetActiveWebContents(); 296 content::TitleWatcher title_watcher(tab, success_title); 297 298 chrome::NavigateParams params( 299 browser(), 300 embedded_test_server()->GetURL(base::StringPrintf( 301 "/extensions/platform_apps/%s", matching_target_page)), 302 transition); 303 ui_test_utils::NavigateToURL(¶ms); 304 305 ASSERT_EQ(success_title, title_watcher.WaitAndGetTitle()); 306 ASSERT_EQ(0U, GetAppWindowCount()); 307 } 308 309 void PlatformAppUrlRedirectorBrowserTest::TestMismatchingNavigationInBrowser( 310 const char* mismatching_target_page, 311 content::PageTransition transition, 312 const char* success_tab_title, 313 const char* handler) { 314 TestNegativeNavigationInBrowser( 315 mismatching_target_page, transition, success_tab_title, handler); 316 } 317 318 // Test that a click on a regular link in a tab launches an app that has 319 // matching url_handlers. 320 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 321 ClickInTabIntercepted) { 322 #if defined (OS_WIN) 323 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 324 #endif 325 TestNavigationInTab( 326 "url_handlers/launching_pages/click_link.html", 327 "url_handlers/handlers/simple", 328 "Handler launched"); 329 } 330 331 // Test that a click on a target='_blank' link in a tab launches an app that has 332 // matching url_handlers. 333 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 334 BlankClickInTabIntercepted) { 335 #if defined (OS_WIN) 336 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 337 #endif 338 TestNavigationInTab( 339 "url_handlers/launching_pages/click_blank_link.html", 340 "url_handlers/handlers/simple", 341 "Handler launched"); 342 } 343 344 // Test that a call to window.open() in a tab launches an app that has 345 // matching url_handlers. 346 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 347 WindowOpenInTabIntercepted) { 348 #if defined (OS_WIN) 349 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 350 #endif 351 TestNavigationInTab( 352 "url_handlers/launching_pages/call_window_open.html", 353 "url_handlers/handlers/simple", 354 "Handler launched"); 355 } 356 357 // Test that a click on a regular link in a tab launches an app that has 358 // matching url_handlers. 359 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 360 MismatchingClickInTabNotIntercepted) { 361 #if defined (OS_WIN) 362 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 363 #endif 364 TestMismatchingNavigationInTab( 365 "url_handlers/launching_pages/click_mismatching_link.html", 366 "Mismatching link target loaded", 367 "url_handlers/handlers/simple"); 368 } 369 370 // Test that a click on target='_blank' link in an app's window launches 371 // another app that has matching url_handlers. 372 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 373 BlankClickInAppIntercepted) { 374 #if defined (OS_WIN) 375 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 376 #endif 377 TestNavigationInApp( 378 "url_handlers/launchers/click_blank_link", 379 "Launcher done", 380 "url_handlers/handlers/simple", 381 "Handler launched"); 382 } 383 384 // Test that a call to window.open() in the app's foreground page launches 385 // another app that has matching url_handlers. 386 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 387 WindowOpenInAppIntercepted) { 388 #if defined (OS_WIN) 389 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 390 #endif 391 TestNavigationInApp( 392 "url_handlers/launchers/call_window_open", 393 "Launcher done", 394 "url_handlers/handlers/simple", 395 "Handler launched"); 396 } 397 398 // Test that an app with url_handlers does not intercept a mismatching 399 // click on a target='_blank' link in another app's window. 400 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 401 MismatchingWindowOpenInAppNotIntercepted) { 402 #if defined (OS_WIN) 403 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 404 #endif 405 TestMismatchingNavigationInApp( 406 "url_handlers/launchers/call_mismatching_window_open", 407 "Launcher done", 408 "url_handlers/handlers/simple"); 409 } 410 411 // Test that a webview in an app can be navigated to a URL without interception 412 // even when there are other (or the same) apps that have matching url_handlers. 413 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 414 WebviewNavigationNotIntercepted) { 415 #if defined (OS_WIN) 416 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 417 #endif 418 // The launcher clicks on a link, which gets intercepted and launches the 419 // handler. The handler also redirects an embedded webview to the URL. The 420 // webview should just navigate without creating an endless loop of 421 // navigate-intercept-launch sequences with multiplying handler's windows. 422 // There should be 2 windows only: launcher's and handler's. 423 TestNavigationInApp( 424 "url_handlers/launchers/click_blank_link", 425 "Launcher done", 426 "url_handlers/handlers/navigate_webview_to_url", 427 "Handler launched"); 428 } 429 430 // Test that a webview in an app can be navigated to a URL without interception 431 // even when there are other (or the same) apps that have matching url_handlers. 432 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 433 MismatchingBlankClickInAppNotIntercepted) { 434 #if defined (OS_WIN) 435 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 436 #endif 437 // The launcher clicks on a link, which gets intercepted and launches the 438 // handler. The handler also redirects an embedded webview to the URL. The 439 // webview should just navigate without creating an endless loop of 440 // navigate-intercept-launch sequences with multiplying handler's windows. 441 // There should be 2 windows only: launcher's and handler's. 442 TestMismatchingNavigationInApp( 443 "url_handlers/launchers/click_mismatching_blank_link", 444 "Launcher done", 445 "url_handlers/handlers/simple"); 446 } 447 448 // Test that a URL entry in the omnibar launches an app that has matching 449 // url_handlers. 450 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 451 EntryInOmnibarIntercepted) { 452 #if defined (OS_WIN) 453 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 454 #endif 455 TestNavigationInBrowser( 456 "url_handlers/common/target.html", 457 content::PAGE_TRANSITION_TYPED, 458 "url_handlers/handlers/simple", 459 "Handler launched"); 460 } 461 462 // Test that an app with url_handlers does not intercept a mismatching 463 // URL entry in the omnibar. 464 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 465 MismatchingEntryInOmnibarNotIntercepted) { 466 #if defined (OS_WIN) 467 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 468 #endif 469 TestMismatchingNavigationInBrowser( 470 "url_handlers/common/mismatching_target.html", 471 content::PAGE_TRANSITION_TYPED, 472 "Mismatching link target loaded", 473 "url_handlers/handlers/simple"); 474 } 475 476 // Test that a form submission in a page is never subject to interception 477 // by apps even with matching url_handlers. 478 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 479 FormSubmissionInTabNotIntercepted) { 480 #if defined (OS_WIN) 481 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 482 #endif 483 TestMismatchingNavigationInTab( 484 "url_handlers/launching_pages/submit_form.html", 485 "Link target loaded", 486 "url_handlers/handlers/simple"); 487 } 488 489 // Test that a form submission in a page is never subject to interception 490 // by apps even with matching url_handlers. 491 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 492 XhrInTabNotIntercepted) { 493 #if defined (OS_WIN) 494 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 495 #endif 496 TestNegativeXhrInTab( 497 "url_handlers/xhr_downloader/main.html", 498 "XHR succeeded", 499 "XHR failed", 500 "url_handlers/handlers/steal_xhr_target"); 501 } 502 503 // Test that a click on a prerendered link still launches. 504 IN_PROC_BROWSER_TEST_F(PlatformAppUrlRedirectorBrowserTest, 505 PrerenderedClickInTabIntercepted) { 506 #if defined (OS_WIN) 507 if (base::win::GetVersion() < base::win::VERSION_VISTA) return; // Bug 301638 508 #endif 509 TestNavigationInTab( 510 "url_handlers/launching_pages/prerender_link.html", 511 "url_handlers/handlers/simple", 512 "Handler launched"); 513 } 514 515 } // namespace extensions 516