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/ui/tabs/tab_strip_model.h" 11#include "chrome/common/chrome_switches.h" 12#include "chrome/test/base/ui_test_utils.h" 13#include "content/public/test/browser_test_base.h" 14#include "content/public/test/browser_test_utils.h" 15#include "extensions/test/extension_test_message_listener.h" 16#include "net/test/embedded_test_server/embedded_test_server.h" 17#include "ui/base/page_transition_types.h" 18 19namespace extensions { 20 21class 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 ui::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 ui::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 ui::PageTransition transition, 111 const char* success_tab_title, 112 const char* handler); 113}; 114 115 116void 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 130void 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 152void 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 177void 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 205void 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 223void 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 242void 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 261void PlatformAppUrlRedirectorBrowserTest::TestNavigationInBrowser( 262 const char* matching_target_page, 263 ui::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 284void PlatformAppUrlRedirectorBrowserTest::TestNegativeNavigationInBrowser( 285 const char* matching_target_page, 286 ui::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 309void PlatformAppUrlRedirectorBrowserTest::TestMismatchingNavigationInBrowser( 310 const char* mismatching_target_page, 311 ui::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. 320IN_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. 333IN_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. 346IN_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. 359IN_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. 372IN_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. 386IN_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. 400IN_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. 413IN_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. 432IN_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. 450IN_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 ui::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. 464IN_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 ui::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. 478IN_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. 491IN_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. 504IN_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