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