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(&params);
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(&params);
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