1 // 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 "base/strings/string_util.h"
6 #include "base/test/scoped_feature_list.h"
7 #include "build/build_config.h"
8 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
9 #include "chrome/browser/ui/browser_commands.h"
10 #include "chrome/browser/ui/browser_finder.h"
11 #include "chrome/browser/ui/views/frame/browser_view.h"
12 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
13 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
14 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
15 #include "chrome/browser/web_applications/components/web_application_info.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/test/base/in_process_browser_test.h"
18 #include "chrome/test/base/ui_test_utils.h"
19 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
20 #include "components/security_interstitials/content/security_interstitial_page.h"
21 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
22 #include "components/security_interstitials/content/settings_page_helper.h"
23 #include "components/security_interstitials/core/metrics_helper.h"
24 #include "content/public/browser/navigation_entry.h"
25 #include "content/public/browser/navigation_handle.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/test/browser_test.h"
28 #include "content/public/test/browser_test_utils.h"
29 #include "content/public/test/content_mock_cert_verifier.h"
30 #include "content/public/test/test_navigation_observer.h"
31 #include "net/dns/mock_host_resolver.h"
32 #include "third_party/blink/public/common/features.h"
33 #include "ui/base/clipboard/clipboard.h"
34 #include "ui/views/controls/button/image_button.h"
35 
36 namespace {
37 
38 // Waits until the title of any tab in the browser for |contents| has the title
39 // |target_title|.
40 class TestTitleObserver : public TabStripModelObserver {
41  public:
42   // Create a new TitleObserver for the browser of |contents|, waiting for
43   // |target_title|.
TestTitleObserver(content::WebContents * contents,base::string16 target_title)44   TestTitleObserver(content::WebContents* contents, base::string16 target_title)
45       : contents_(contents), target_title_(target_title) {
46     browser_ = chrome::FindBrowserWithWebContents(contents_);
47     browser_->tab_strip_model()->AddObserver(this);
48   }
49 
50   // Run a loop, blocking until a tab has the title |target_title|.
Wait()51   void Wait() {
52     if (seen_target_title_)
53       return;
54 
55     awaiter_.Run();
56   }
57 
58   // TabstripModelObserver:
TabChangedAt(content::WebContents * contents,int index,TabChangeType change_type)59   void TabChangedAt(content::WebContents* contents,
60                     int index,
61                     TabChangeType change_type) override {
62     content::NavigationEntry* entry =
63         contents->GetController().GetVisibleEntry();
64     base::string16 title = entry ? entry->GetTitle() : base::string16();
65 
66     if (title != target_title_)
67       return;
68 
69     seen_target_title_ = true;
70     awaiter_.Quit();
71   }
72 
73  private:
74   bool seen_target_title_ = false;
75 
76   content::WebContents* contents_;
77   Browser* browser_;
78   base::string16 target_title_;
79   base::RunLoop awaiter_;
80 };
81 
82 // Opens a new popup window from |web_contents| on |target_url| and returns
83 // the Browser it opened in.
OpenPopup(content::WebContents * web_contents,const GURL & target_url)84 Browser* OpenPopup(content::WebContents* web_contents, const GURL& target_url) {
85   content::TestNavigationObserver nav_observer(target_url);
86   nav_observer.StartWatchingNewWebContents();
87 
88   std::string script = "window.open('" + target_url.spec() +
89                        "', 'popup', 'width=400 height=400');";
90   EXPECT_TRUE(content::ExecuteScript(web_contents, script));
91   nav_observer.Wait();
92 
93   return chrome::FindLastActive();
94 }
95 
96 // Navigates to |target_url| and waits for navigation to complete.
NavigateAndWait(content::WebContents * web_contents,const GURL & target_url)97 void NavigateAndWait(content::WebContents* web_contents,
98                      const GURL& target_url) {
99   content::TestNavigationObserver nav_observer(web_contents);
100 
101   std::string script = "window.location = '" + target_url.spec() + "';";
102   EXPECT_TRUE(content::ExecuteScript(web_contents, script));
103   nav_observer.Wait();
104 }
105 
106 // Navigates |web_contents| to |location|, waits for navigation to complete
107 // and then sets document.title to be |title| and waits for the change
108 // to propogate.
SetTitleAndLocation(content::WebContents * web_contents,const base::string16 title,const GURL & location)109 void SetTitleAndLocation(content::WebContents* web_contents,
110                          const base::string16 title,
111                          const GURL& location) {
112   NavigateAndWait(web_contents, location);
113 
114   TestTitleObserver title_observer(web_contents, title);
115 
116   std::string script = "document.title = '" + base::UTF16ToASCII(title) + "';";
117   EXPECT_TRUE(content::ExecuteScript(web_contents, script));
118 
119   title_observer.Wait();
120 }
121 
122 // An interstitial page that requests URL hiding
123 class UrlHidingInterstitialPage
124     : public security_interstitials::SecurityInterstitialPage {
125  public:
UrlHidingInterstitialPage(content::WebContents * web_contents,const GURL & request_url)126   UrlHidingInterstitialPage(content::WebContents* web_contents,
127                             const GURL& request_url)
128       : security_interstitials::SecurityInterstitialPage(
129             web_contents,
130             request_url,
131             std::make_unique<
132                 security_interstitials::SecurityInterstitialControllerClient>(
133                 web_contents,
134                 nullptr,
135                 nullptr,
136                 base::i18n::GetConfiguredLocale(),
137                 GURL(),
138                 nullptr /* settings_page_helper */)) {}
OnInterstitialClosing()139   void OnInterstitialClosing() override {}
ShouldDisplayURL() const140   bool ShouldDisplayURL() const override { return false; }
141 
142  protected:
PopulateInterstitialStrings(base::DictionaryValue * load_time_data)143   void PopulateInterstitialStrings(
144       base::DictionaryValue* load_time_data) override {}
145 };
146 
147 // An observer that associates a URL-hiding interstitial when a page loads when
148 // |install_interstitial| is true.
149 class UrlHidingWebContentsObserver : public content::WebContentsObserver {
150  public:
UrlHidingWebContentsObserver(content::WebContents * contents)151   explicit UrlHidingWebContentsObserver(content::WebContents* contents)
152       : content::WebContentsObserver(contents), install_interstitial_(true) {}
153 
DidFinishNavigation(content::NavigationHandle * handle)154   void DidFinishNavigation(content::NavigationHandle* handle) override {
155     if (!install_interstitial_)
156       return;
157 
158     security_interstitials::SecurityInterstitialTabHelper::
159         AssociateBlockingPage(web_contents(), handle->GetNavigationId(),
160                               std::make_unique<UrlHidingInterstitialPage>(
161                                   web_contents(), handle->GetURL()));
162   }
163 
StopBlocking()164   void StopBlocking() { install_interstitial_ = false; }
165 
166  private:
167   bool install_interstitial_;
168 };
169 
170 }  // namespace
171 
172 class CustomTabBarViewBrowserTest
173     : public web_app::WebAppControllerBrowserTest {
174  public:
175   CustomTabBarViewBrowserTest() = default;
176   ~CustomTabBarViewBrowserTest() override = default;
177 
178  protected:
179 
SetUpCommandLine(base::CommandLine * command_line)180   void SetUpCommandLine(base::CommandLine* command_line) override {
181     web_app::WebAppControllerBrowserTest::SetUpCommandLine(command_line);
182     // Browser will both run and display insecure content.
183     command_line->AppendSwitch(switches::kAllowRunningInsecureContent);
184   }
185 
SetUp()186   void SetUp() override {
187     feature_list_.InitAndDisableFeature(
188         blink::features::kMixedContentAutoupgrade);
189     web_app::WebAppControllerBrowserTest::SetUp();
190   }
191 
SetUpOnMainThread()192   void SetUpOnMainThread() override {
193     web_app::WebAppControllerBrowserTest::SetUpOnMainThread();
194 
195     browser_view_ = BrowserView::GetBrowserViewForBrowser(browser());
196 
197     location_bar_ = browser_view_->GetLocationBarView();
198     custom_tab_bar_ = browser_view_->toolbar()->custom_tab_bar();
199   }
200 
InstallPWA(const GURL & start_url)201   void InstallPWA(const GURL& start_url) {
202     auto web_app_info = std::make_unique<WebApplicationInfo>();
203     web_app_info->start_url = start_url;
204     web_app_info->scope = start_url.GetWithoutFilename();
205     web_app_info->open_as_window = true;
206     Install(std::move(web_app_info));
207   }
208 
InstallBookmark(const GURL & start_url)209   void InstallBookmark(const GURL& start_url) {
210     auto web_app_info = std::make_unique<WebApplicationInfo>();
211     web_app_info->start_url = start_url;
212     web_app_info->scope = start_url.GetOrigin();
213     web_app_info->open_as_window = true;
214     Install(std::move(web_app_info));
215   }
216 
217   BrowserView* browser_view_;
218   LocationBarView* location_bar_;
219   CustomTabBarView* custom_tab_bar_;
220   Browser* app_browser_ = nullptr;
221   web_app::AppBrowserController* app_controller_ = nullptr;
222 
223  private:
Install(std::unique_ptr<WebApplicationInfo> web_app_info)224   void Install(std::unique_ptr<WebApplicationInfo> web_app_info) {
225     const GURL start_url = web_app_info->start_url;
226     web_app::AppId app_id = InstallWebApp(std::move(web_app_info));
227 
228     ui_test_utils::UrlLoadObserver url_observer(
229         start_url, content::NotificationService::AllSources());
230     app_browser_ = LaunchWebAppBrowser(app_id);
231     url_observer.Wait();
232 
233     DCHECK(app_browser_);
234     DCHECK(app_browser_ != browser());
235 
236     app_controller_ = app_browser_->app_controller();
237     DCHECK(app_controller_);
238   }
239 
240   base::test::ScopedFeatureList feature_list_;
241 
242   DISALLOW_COPY_AND_ASSIGN(CustomTabBarViewBrowserTest);
243 };
244 
245 // Check the custom tab bar is not instantiated for a tabbed browser window.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,IsNotCreatedInTabbedBrowser)246 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
247                        IsNotCreatedInTabbedBrowser) {
248   EXPECT_TRUE(browser()->is_type_normal());
249   EXPECT_TRUE(browser_view_->IsBrowserTypeNormal());
250   EXPECT_FALSE(custom_tab_bar_);
251 }
252 
253 // Check the custom tab bar is not instantiated for a popup window.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,IsNotCreatedInPopup)254 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest, IsNotCreatedInPopup) {
255   Browser* popup = OpenPopup(browser_view_->GetActiveWebContents(),
256                              GURL("http://example.com"));
257   EXPECT_TRUE(popup);
258 
259   BrowserView* popup_view = BrowserView::GetBrowserViewForBrowser(popup);
260 
261   // The popup should be in a new window.
262   EXPECT_NE(browser_view_, popup_view);
263 
264   // Popups are not the normal browser view.
265   EXPECT_FALSE(popup_view->IsBrowserTypeNormal());
266   EXPECT_TRUE(popup->is_type_popup());
267   // Popups should not have a custom tab bar view.
268   EXPECT_FALSE(popup_view->toolbar()->custom_tab_bar());
269 }
270 
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,BackToAppButtonIsNotVisibleInOutOfScopePopups)271 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
272                        BackToAppButtonIsNotVisibleInOutOfScopePopups) {
273   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
274   const GURL out_of_scope_url = GURL("https://example.com");
275 
276   InstallBookmark(app_url);
277   EXPECT_TRUE(app_browser_->is_type_app());
278 
279   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
280 
281   Browser* popup_browser =
282       OpenPopup(app_view->GetActiveWebContents(), out_of_scope_url);
283   EXPECT_TRUE(popup_browser->is_type_app_popup());
284 
285   // Out of scope, so custom tab bar should be shown.
286   EXPECT_TRUE(popup_browser->app_controller()->ShouldShowCustomTabBar());
287 
288   // As the popup was opened out of scope the close button should not be shown.
289   EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(popup_browser)
290                    ->toolbar()
291                    ->custom_tab_bar()
292                    ->close_button_for_testing()
293                    ->GetVisible());
294 }
295 
296 // Check the custom tab will be used for a Desktop PWA.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,IsUsedForDesktopPWA)297 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest, IsUsedForDesktopPWA) {
298   ASSERT_TRUE(embedded_test_server()->Start());
299 
300   const GURL url = https_server()->GetURL("app.com", "/ssl/google.html");
301   InstallPWA(url);
302 
303   EXPECT_TRUE(app_browser_);
304 
305   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
306   EXPECT_NE(app_view, browser_view_);
307 
308   EXPECT_FALSE(app_view->IsBrowserTypeNormal());
309   EXPECT_TRUE(app_browser_->is_type_app());
310 
311   // Custom tab bar should be created.
312   EXPECT_TRUE(app_view->toolbar()->custom_tab_bar());
313 }
314 
315 // Check the CustomTabBarView appears when a PWA window attempts to load
316 // insecure content.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,ShowsWithMixedContent)317 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest, ShowsWithMixedContent) {
318   ASSERT_TRUE(embedded_test_server()->Start());
319 
320   const GURL url = https_server()->GetURL("app.com", "/ssl/google.html");
321   InstallPWA(url);
322 
323   ASSERT_TRUE(app_browser_);
324   EXPECT_TRUE(app_browser_->is_type_app());
325 
326   CustomTabBarView* bar = BrowserView::GetBrowserViewForBrowser(app_browser_)
327                               ->toolbar()
328                               ->custom_tab_bar();
329   EXPECT_FALSE(bar->GetVisible());
330   EXPECT_TRUE(ExecJs(app_browser_->tab_strip_model()->GetActiveWebContents(),
331                      R"(
332       let img = document.createElement('img');
333       img.src = 'http://not-secure.com';
334       document.body.appendChild(img);
335     )"));
336   EXPECT_TRUE(bar->GetVisible());
337   EXPECT_EQ(bar->title_for_testing(), base::ASCIIToUTF16("Google"));
338   EXPECT_EQ(bar->location_for_testing() + base::ASCIIToUTF16("/"),
339             base::ASCIIToUTF16(
340                 https_server()->GetURL("app.com", "/ssl").GetOrigin().spec()));
341   EXPECT_FALSE(bar->close_button_for_testing()->GetVisible());
342 }
343 
344 // The custom tab bar should update with the title and location of the current
345 // page.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,TitleAndLocationUpdate)346 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest, TitleAndLocationUpdate) {
347   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
348 
349   // This url is out of scope, because the CustomTabBar is not updated when it
350   // is not shown.
351   const GURL navigate_to = https_server()->GetURL("app.com", "/simple.html");
352 
353   InstallPWA(app_url);
354 
355   EXPECT_TRUE(app_browser_);
356   EXPECT_TRUE(app_browser_->is_type_app());
357 
358   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
359   EXPECT_NE(app_view, browser_view_);
360 
361   SetTitleAndLocation(app_view->GetActiveWebContents(),
362                       base::ASCIIToUTF16("FooBar"), navigate_to);
363 
364   std::string expected_origin = navigate_to.GetOrigin().spec();
365   EXPECT_EQ(base::ASCIIToUTF16(expected_origin),
366             app_view->toolbar()->custom_tab_bar()->location_for_testing() +
367                 base::ASCIIToUTF16("/"));
368   EXPECT_EQ(base::ASCIIToUTF16("FooBar"),
369             app_view->toolbar()->custom_tab_bar()->title_for_testing());
370 }
371 
372 // If the page doesn't specify a title, we should use the origin.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,UsesLocationInsteadOfEmptyTitles)373 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
374                        UsesLocationInsteadOfEmptyTitles) {
375   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
376   InstallPWA(app_url);
377 
378   EXPECT_TRUE(app_browser_);
379   EXPECT_TRUE(app_browser_->is_type_app());
380 
381   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
382   EXPECT_NE(app_view, browser_view_);
383 
384   // Empty title should use location.
385   SetTitleAndLocation(app_view->GetActiveWebContents(), base::string16(),
386                       GURL("http://example.test/"));
387   EXPECT_EQ(base::ASCIIToUTF16("example.test"),
388             app_view->toolbar()->custom_tab_bar()->location_for_testing());
389   EXPECT_EQ(base::ASCIIToUTF16("example.test"),
390             app_view->toolbar()->custom_tab_bar()->title_for_testing());
391 }
392 
393 // Closing the CCT should take you back to the last in scope url.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,OutOfScopeUrlShouldBeClosable)394 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
395                        OutOfScopeUrlShouldBeClosable) {
396   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
397   InstallPWA(app_url);
398 
399   EXPECT_TRUE(app_browser_);
400   EXPECT_TRUE(app_browser_->is_type_app());
401 
402   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
403   auto* web_contents = app_view->GetActiveWebContents();
404   EXPECT_NE(app_view, browser_view_);
405 
406   // Perform an inscope navigation.
407   const GURL other_app_url =
408       https_server()->GetURL("app.com", "/ssl/blank_page.html");
409   NavigateAndWait(web_contents, other_app_url);
410   EXPECT_FALSE(app_controller_->ShouldShowCustomTabBar());
411 
412   // Navigate out of scope.
413   NavigateAndWait(web_contents, GURL("http://example.test/"));
414   EXPECT_TRUE(app_controller_->ShouldShowCustomTabBar());
415 
416   // Simulate clicking the close button and wait for navigation to finish.
417   content::TestNavigationObserver nav_observer(web_contents);
418   app_view->toolbar()->custom_tab_bar()->GoBackToAppForTesting();
419   nav_observer.Wait();
420 
421   // The app should be on the last in scope url we visited.
422   EXPECT_EQ(other_app_url, web_contents->GetLastCommittedURL());
423 }
424 
425 // Right-click menu on CustomTabBar should have Copy URL option.
426 // TODO(crbug.com/988323): Times out on Mac.
427 #if defined(OS_MAC)
428 #define MAYBE_RightClickMenuShowsCopyUrl DISABLED_RightClickMenuShowsCopyUrl
429 #else
430 #define MAYBE_RightClickMenuShowsCopyUrl RightClickMenuShowsCopyUrl
431 #endif
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,MAYBE_RightClickMenuShowsCopyUrl)432 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
433                        MAYBE_RightClickMenuShowsCopyUrl) {
434   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
435   InstallPWA(app_url);
436   EXPECT_TRUE(app_browser_->is_type_app());
437 
438   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
439   auto* web_contents = app_view->GetActiveWebContents();
440 
441   // Navigate out of scope.
442   NavigateAndWait(web_contents, GURL("http://example.test/"));
443   EXPECT_TRUE(app_controller_->ShouldShowCustomTabBar());
444 
445   // Show the right-click context menu.
446   app_view->toolbar()->custom_tab_bar()->ShowContextMenu(gfx::Point(),
447                                                          ui::MENU_SOURCE_MOUSE);
448 
449   content::BrowserTestClipboardScope test_clipboard_scope;
450   // Activate the first and only context menu item: IDC_COPY_URL.
451   app_view->toolbar()
452       ->custom_tab_bar()
453       ->context_menu_for_testing()
454       ->ActivatedAt(0);
455 
456   ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
457   base::string16 result;
458   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
459                       &result);
460   EXPECT_EQ(result, base::UTF8ToUTF16("http://example.test/"));
461 }
462 
463 // Paths above the launch url should be out of scope and should be closable from
464 // the CustomTabBar.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,ScopeAboveLaunchURLShouldBeOutOfScopeAndClosable)465 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
466                        ScopeAboveLaunchURLShouldBeOutOfScopeAndClosable) {
467   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
468   InstallPWA(app_url);
469 
470   EXPECT_TRUE(app_browser_);
471   EXPECT_TRUE(app_browser_->is_type_app());
472 
473   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
474   auto* web_contents = app_view->GetActiveWebContents();
475   EXPECT_NE(app_view, browser_view_);
476 
477   // Navigate to a different page in the app scope, so we have something to come
478   // back to.
479   const GURL other_app_url =
480       https_server()->GetURL("app.com", "/ssl/blank_page.html");
481   NavigateAndWait(web_contents, other_app_url);
482   EXPECT_FALSE(app_controller_->ShouldShowCustomTabBar());
483 
484   // Navigate above the scope of the app, on the same origin.
485   NavigateAndWait(web_contents, https_server()->GetURL(
486                                     "app.com", "/accessibility_fail.html"));
487   EXPECT_TRUE(app_controller_->ShouldShowCustomTabBar());
488 
489   // Simulate clicking the close button and wait for navigation to finish.
490   content::TestNavigationObserver nav_observer(web_contents);
491   app_view->toolbar()->custom_tab_bar()->GoBackToAppForTesting();
492   nav_observer.Wait();
493 
494   // The app should be on the last in scope url we visited.
495   EXPECT_EQ(other_app_url, web_contents->GetLastCommittedURL());
496 }
497 
498 // When there are no in scope urls to navigate back to, closing the custom tab
499 // bar should navigate to the app's launch url.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,WhenNoHistoryIsInScopeCloseShouldNavigateToAppLaunchURL)500 IN_PROC_BROWSER_TEST_F(
501     CustomTabBarViewBrowserTest,
502     WhenNoHistoryIsInScopeCloseShouldNavigateToAppLaunchURL) {
503   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
504   InstallPWA(app_url);
505 
506   EXPECT_TRUE(app_browser_);
507   EXPECT_TRUE(app_browser_->is_type_app());
508 
509   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
510   auto* web_contents = app_view->GetActiveWebContents();
511   EXPECT_NE(app_view, browser_view_);
512 
513   {
514     // Do a state replacing navigation, so we don't have any in scope urls in
515     // history.
516     content::TestNavigationObserver nav_observer(web_contents);
517     EXPECT_TRUE(content::ExecuteScript(
518         web_contents, "window.location.replace('http://example.com');"));
519     nav_observer.Wait();
520     EXPECT_TRUE(app_controller_->ShouldShowCustomTabBar());
521   }
522   {
523     // Simulate clicking the close button and wait for navigation to finish.
524     content::TestNavigationObserver nav_observer(web_contents);
525     app_view->toolbar()->custom_tab_bar()->GoBackToAppForTesting();
526     nav_observer.Wait();
527   }
528   // The app should be on the last in scope url we visited.
529   EXPECT_EQ(app_url, web_contents->GetLastCommittedURL());
530 }
531 
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,OriginsWithEmojiArePunyCoded)532 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
533                        OriginsWithEmojiArePunyCoded) {
534   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
535   const GURL navigate_to = GURL("https://��.example/ssl/blank_page.html");
536 
537   InstallPWA(app_url);
538 
539   EXPECT_TRUE(app_browser_);
540   EXPECT_TRUE(app_browser_->is_type_app());
541 
542   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
543   EXPECT_NE(app_view, browser_view_);
544 
545   SetTitleAndLocation(app_view->GetActiveWebContents(),
546                       base::ASCIIToUTF16("FooBar"), navigate_to);
547 
548   EXPECT_EQ(base::UTF8ToUTF16("https://xn--lv8h.example"),
549             app_view->toolbar()->custom_tab_bar()->location_for_testing());
550   EXPECT_EQ(base::ASCIIToUTF16("FooBar"),
551             app_view->toolbar()->custom_tab_bar()->title_for_testing());
552 }
553 
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,OriginsWithNonASCIICharactersDisplayNormally)554 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
555                        OriginsWithNonASCIICharactersDisplayNormally) {
556   const GURL app_url = https_server()->GetURL("app.com", "/ssl/google.html");
557   const GURL navigate_to = GURL("https://ΐ.example/ssl/blank_page.html");
558 
559   InstallPWA(app_url);
560 
561   EXPECT_TRUE(app_browser_);
562   EXPECT_TRUE(app_browser_->is_type_app());
563 
564   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
565   EXPECT_NE(app_view, browser_view_);
566 
567   SetTitleAndLocation(app_view->GetActiveWebContents(),
568                       base::ASCIIToUTF16("FooBar"), navigate_to);
569 
570   EXPECT_EQ(base::UTF8ToUTF16("https://ΐ.example"),
571             app_view->toolbar()->custom_tab_bar()->location_for_testing());
572   EXPECT_EQ(base::ASCIIToUTF16("FooBar"),
573             app_view->toolbar()->custom_tab_bar()->title_for_testing());
574 }
575 
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,BackToAppButtonIsNotVisibleInScope)576 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
577                        BackToAppButtonIsNotVisibleInScope) {
578   ASSERT_TRUE(embedded_test_server()->Start());
579 
580   // We install over http because it's the easiest way to get a custom tab bar
581   // in scope. A PWA won't be installed over http in the real world (it'd make a
582   // shortcut app instead).
583   const GURL app_url =
584       embedded_test_server()->GetURL("app.com", "/ssl/google.html");
585   const GURL out_of_scope_url = GURL("https://example.com");
586 
587   InstallPWA(app_url);
588 
589   EXPECT_TRUE(app_browser_);
590   EXPECT_TRUE(app_browser_->is_type_app());
591 
592   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
593   EXPECT_NE(app_view, browser_view_);
594   content::WebContents* web_contents = app_view->GetActiveWebContents();
595 
596   // Insecure site, so should show custom tab bar.
597   EXPECT_TRUE(app_view->toolbar()->custom_tab_bar()->GetVisible());
598   // In scope, so don't show close button.
599   EXPECT_FALSE(app_view->toolbar()
600                    ->custom_tab_bar()
601                    ->close_button_for_testing()
602                    ->GetVisible());
603 
604   NavigateAndWait(web_contents, out_of_scope_url);
605 
606   // Out of scope, show the custom tab bar.
607   EXPECT_TRUE(app_view->toolbar()->custom_tab_bar()->GetVisible());
608   // Out of scope, show the close button.
609   EXPECT_TRUE(app_view->toolbar()
610                   ->custom_tab_bar()
611                   ->close_button_for_testing()
612                   ->GetVisible());
613 
614   // Simulate clicking the close button and wait for navigation to finish.
615   content::TestNavigationObserver nav_observer(web_contents);
616   app_view->toolbar()->custom_tab_bar()->GoBackToAppForTesting();
617   nav_observer.Wait();
618 
619   // Insecure site, show the custom tab bar.
620   EXPECT_TRUE(app_view->toolbar()->custom_tab_bar()->GetVisible());
621   // In scope, hide the close button.
622   EXPECT_FALSE(app_view->toolbar()
623                    ->custom_tab_bar()
624                    ->close_button_for_testing()
625                    ->GetVisible());
626 }
627 
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,BackToAppButtonIsNotVisibleInBookmarkAppOnOrigin)628 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
629                        BackToAppButtonIsNotVisibleInBookmarkAppOnOrigin) {
630   ASSERT_TRUE(embedded_test_server()->Start());
631 
632   const GURL app_url =
633       embedded_test_server()->GetURL("app.com", "/ssl/google.html");
634   const GURL out_of_scope_url = GURL("https://example.com");
635 
636   InstallBookmark(app_url);
637   EXPECT_TRUE(app_browser_->is_type_app());
638 
639   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
640   EXPECT_NE(app_view, browser_view_);
641 
642   // Insecure site, so should show custom tab bar.
643   EXPECT_TRUE(app_view->toolbar()->custom_tab_bar()->GetVisible());
644   // On origin, so don't show close button.
645   EXPECT_FALSE(app_view->toolbar()
646                    ->custom_tab_bar()
647                    ->close_button_for_testing()
648                    ->GetVisible());
649 
650   NavigateAndWait(app_view->GetActiveWebContents(), out_of_scope_url);
651 
652   // Off origin, show the custom tab bar.
653   EXPECT_TRUE(app_view->toolbar()->custom_tab_bar()->GetVisible());
654   // Off origin, show the close button.
655   EXPECT_TRUE(app_view->toolbar()
656                   ->custom_tab_bar()
657                   ->close_button_for_testing()
658                   ->GetVisible());
659 }
660 
661 // Verify that interstitials that hide origin have their preference respected.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,InterstitialCanHideOrigin)662 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest, InterstitialCanHideOrigin) {
663   ASSERT_TRUE(embedded_test_server()->Start());
664 
665   InstallPWA(https_server()->GetURL("app.com", "/ssl/google.html"));
666   EXPECT_TRUE(app_browser_);
667   EXPECT_TRUE(app_browser_->is_type_app());
668 
669   BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
670   EXPECT_NE(app_view, browser_view_);
671 
672   content::WebContents* contents = app_view->GetActiveWebContents();
673 
674   // Verify origin is blanked on interstitial.
675   UrlHidingWebContentsObserver blocker(contents);
676   SetTitleAndLocation(contents, base::ASCIIToUTF16("FooBar"),
677                       https_server()->GetURL("/simple.html"));
678 
679   EXPECT_EQ(base::string16(),
680             app_view->toolbar()->custom_tab_bar()->location_for_testing());
681   EXPECT_FALSE(
682       app_view->toolbar()->custom_tab_bar()->IsShowingOriginForTesting());
683 
684   // Verify origin returns when interstitial is gone.
685   blocker.StopBlocking();
686   SetTitleAndLocation(contents, base::ASCIIToUTF16("FooBar2"),
687                       https_server()->GetURL("/title1.html"));
688 
689   EXPECT_NE(base::string16(),
690             app_view->toolbar()->custom_tab_bar()->location_for_testing());
691   EXPECT_TRUE(
692       app_view->toolbar()->custom_tab_bar()->IsShowingOriginForTesting());
693 }
694 
695 // Verify that blob URLs are displayed in the location text.
IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,BlobUrlLocation)696 IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest, BlobUrlLocation) {
697   InstallPWA(https_server()->GetURL("/simple.html"));
698   EXPECT_TRUE(app_browser_);
699   EXPECT_TRUE(app_browser_->is_type_app());
700   BrowserView* app_browser_view =
701       BrowserView::GetBrowserViewForBrowser(app_browser_);
702   EXPECT_NE(app_browser_view, browser_view_);
703   content::WebContents* web_contents =
704       app_browser_->tab_strip_model()->GetActiveWebContents();
705 
706   content::TestNavigationObserver nav_observer(web_contents,
707                                                /*number_of_navigations=*/1);
708   std::string script =
709       "window.open("
710       "    URL.createObjectURL("
711       "        new Blob([], {type: 'text/html'})"
712       "    ),"
713       "    '_self');";
714   EXPECT_TRUE(content::ExecuteScript(web_contents, script));
715   nav_observer.Wait();
716 
717   EXPECT_EQ(
718       app_browser_view->toolbar()->custom_tab_bar()->location_for_testing() +
719           base::ASCIIToUTF16("/"),
720       base::ASCIIToUTF16(https_server()->GetURL("/").spec()));
721 }
722