| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/strings/string16.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/metrics/user_action_tester.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/sessions/tab_restore_service_factory.h" |
| #include "chrome/browser/themes/theme_service.h" |
| #include "chrome/browser/themes/theme_service_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_tabstrip.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_test.h" |
| #include "chrome/browser/ui/page_info/page_info_dialog.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/toolbar/app_menu_model.h" |
| #include "chrome/browser/ui/web_applications/app_browser_controller.h" |
| #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" |
| #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h" |
| #include "chrome/browser/ui/web_applications/web_app_launch_manager.h" |
| #include "chrome/browser/ui/web_applications/web_app_launch_utils.h" |
| #include "chrome/browser/ui/web_applications/web_app_menu_model.h" |
| #include "chrome/browser/web_applications/components/app_registrar.h" |
| #include "chrome/browser/web_applications/components/app_registry_controller.h" |
| #include "chrome/browser/web_applications/components/external_install_options.h" |
| #include "chrome/browser/web_applications/components/web_app_constants.h" |
| #include "chrome/browser/web_applications/components/web_app_helpers.h" |
| #include "chrome/browser/web_applications/components/web_app_provider_base.h" |
| #include "chrome/browser/web_applications/test/web_app_install_observer.h" |
| #include "chrome/browser/web_applications/test/web_app_test.h" |
| #include "chrome/common/web_application_info.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/sessions/core/tab_restore_service.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/base/clipboard/clipboard.h" |
| #include "ui/base/clipboard/clipboard_buffer.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" |
| #endif |
| |
| #if defined(OS_MACOSX) |
| #include "ui/base/test/scoped_fake_nswindow_fullscreen.h" |
| #endif |
| |
| namespace { |
| |
| constexpr const char kExampleURL[] = "http://example.org/"; |
| |
| constexpr char kLaunchWebAppDisplayModeHistogram[] = "Launch.WebAppDisplayMode"; |
| |
| // Opens |url| in a new popup window with the dimensions |popup_size|. |
| Browser* OpenPopupAndWait(Browser* browser, |
| const GURL& url, |
| const gfx::Size& popup_size) { |
| content::WebContents* const web_contents = |
| browser->tab_strip_model()->GetActiveWebContents(); |
| |
| content::WebContentsAddedObserver new_contents_observer; |
| std::string open_window_script = base::StringPrintf( |
| "window.open('%s', '_blank', 'toolbar=none,width=%i,height=%i')", |
| url.spec().c_str(), popup_size.width(), popup_size.height()); |
| |
| EXPECT_TRUE(content::ExecJs(web_contents, open_window_script)); |
| |
| content::WebContents* popup_contents = new_contents_observer.GetWebContents(); |
| content::WaitForLoadStop(popup_contents); |
| Browser* popup_browser = chrome::FindBrowserWithWebContents(popup_contents); |
| |
| // The navigation should happen in a new window. |
| EXPECT_NE(browser, popup_browser); |
| |
| return popup_browser; |
| } |
| |
| } // namespace |
| |
| namespace web_app { |
| |
| class WebAppBrowserTest : public WebAppControllerBrowserTest { |
| public: |
| GURL GetSecureAppURL() { |
| return https_server()->GetURL("app.com", "/ssl/google.html"); |
| } |
| |
| GURL GetURLForPath(const std::string& path) { |
| return https_server()->GetURL("app.com", path); |
| } |
| |
| AppId InstallPwaForCurrentUrl() { |
| chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true); |
| WebAppInstallObserver observer(profile()); |
| CHECK(chrome::ExecuteCommand(browser(), IDC_INSTALL_PWA)); |
| AppId app_id = observer.AwaitNextInstall(); |
| chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false); |
| return app_id; |
| } |
| }; |
| |
| using WebAppTabRestoreBrowserTest = WebAppBrowserTest; |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, ThemeColor) { |
| { |
| const SkColor theme_color = SkColorSetA(SK_ColorBLUE, 0xF0); |
| auto web_app_info = std::make_unique<WebApplicationInfo>(); |
| web_app_info->app_url = GURL(kExampleURL); |
| web_app_info->scope = GURL(kExampleURL); |
| web_app_info->theme_color = theme_color; |
| AppId app_id = InstallWebApp(std::move(web_app_info)); |
| Browser* app_browser = LaunchWebAppBrowser(app_id); |
| |
| EXPECT_EQ(GetAppIdFromApplicationName(app_browser->app_name()), app_id); |
| EXPECT_EQ(SkColorSetA(theme_color, SK_AlphaOPAQUE), |
| app_browser->app_controller()->GetThemeColor()); |
| } |
| { |
| auto web_app_info = std::make_unique<WebApplicationInfo>(); |
| web_app_info->app_url = GURL("http://example.org/2"); |
| web_app_info->scope = GURL("http://example.org/"); |
| web_app_info->theme_color = base::Optional<SkColor>(); |
| AppId app_id = InstallWebApp(std::move(web_app_info)); |
| Browser* app_browser = LaunchWebAppBrowser(app_id); |
| |
| EXPECT_EQ(GetAppIdFromApplicationName(app_browser->app_name()), app_id); |
| EXPECT_EQ(base::nullopt, app_browser->app_controller()->GetThemeColor()); |
| } |
| } |
| |
| // This tests that we don't crash when launching a PWA window with an |
| // autogenerated user theme set. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, AutoGeneratedUserThemeCrash) { |
| ThemeServiceFactory::GetForProfile(browser()->profile()) |
| ->BuildAutogeneratedThemeFromColor(SK_ColorBLUE); |
| |
| auto web_app_info = std::make_unique<WebApplicationInfo>(); |
| web_app_info->app_url = GURL(kExampleURL); |
| AppId app_id = InstallWebApp(std::move(web_app_info)); |
| |
| LaunchWebAppBrowser(app_id); |
| } |
| |
| // Check the 'Open in Chrome' menu button for web app windows. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, OpenInChrome) { |
| const GURL app_url(kExampleURL); |
| const AppId app_id = InstallPWA(app_url); |
| |
| { |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| |
| EXPECT_EQ(1, app_browser->tab_strip_model()->count()); |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile())); |
| |
| chrome::ExecuteCommand(app_browser, IDC_OPEN_IN_CHROME); |
| |
| // The browser frame is closed next event loop so it's still safe to access |
| // here. |
| EXPECT_EQ(0, app_browser->tab_strip_model()->count()); |
| |
| EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
| EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); |
| EXPECT_EQ( |
| app_url, |
| browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL()); |
| } |
| |
| // Wait until the browser actually gets closed. This invalidates |
| // |app_browser|. |
| content::RunAllPendingInMessageLoop(); |
| ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile())); |
| } |
| |
| // Check the 'App info' menu button for web app windows. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, AppInfoOpensPageInfo) { |
| const GURL app_url(kExampleURL); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| |
| bool dialog_created = false; |
| |
| GetPageInfoDialogCreatedCallbackForTesting() = base::BindOnce( |
| [](bool* dialog_created) { *dialog_created = true; }, &dialog_created); |
| |
| chrome::ExecuteCommand(app_browser, IDC_WEB_APP_MENU_APP_INFO); |
| |
| EXPECT_TRUE(dialog_created); |
| |
| // The test closure should have run. But clear the global in case it hasn't. |
| EXPECT_FALSE(GetPageInfoDialogCreatedCallbackForTesting()); |
| GetPageInfoDialogCreatedCallbackForTesting().Reset(); |
| } |
| |
| // Check that last launch time is set after launch. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, AppLastLaunchTime) { |
| const GURL app_url(kExampleURL); |
| const AppId app_id = InstallPWA(app_url); |
| auto* provider = WebAppProviderBase::GetProviderBase(profile()); |
| |
| // last_launch_time is not set before launch |
| EXPECT_TRUE(provider->registrar().GetAppLastLaunchTime(app_id).is_null()); |
| |
| auto before_launch = base::Time::Now(); |
| LaunchWebAppBrowser(app_id); |
| |
| EXPECT_TRUE(provider->registrar().GetAppLastLaunchTime(app_id) >= |
| before_launch); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, HasMinimalUiButtons) { |
| int index = 0; |
| auto has_buttons = [this, &index](DisplayMode display_mode, |
| bool open_as_window) -> bool { |
| base::HistogramTester tester; |
| const std::string base_url = "https://example.com/path"; |
| auto web_app_info = std::make_unique<WebApplicationInfo>(); |
| web_app_info->app_url = GURL(base_url + base::NumberToString(index++)); |
| web_app_info->scope = web_app_info->app_url; |
| web_app_info->display_mode = display_mode; |
| web_app_info->open_as_window = open_as_window; |
| AppId app_id = InstallWebApp(std::move(web_app_info)); |
| Browser* app_browser = LaunchWebAppBrowser(app_id); |
| tester.ExpectUniqueSample(kLaunchWebAppDisplayModeHistogram, display_mode, |
| 1); |
| |
| return app_browser->app_controller()->HasMinimalUiButtons(); |
| }; |
| |
| EXPECT_TRUE(has_buttons(DisplayMode::kMinimalUi, |
| /*open_as_window=*/true)); |
| EXPECT_FALSE(has_buttons(DisplayMode::kStandalone, |
| /*open_as_window=*/true)); |
| EXPECT_FALSE(has_buttons(DisplayMode::kMinimalUi, |
| /*open_as_window=*/false)); |
| } |
| |
| // Tests that desktop PWAs open links in the browser. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, DesktopPWAsOpenLinksInApp) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| NavigateToURLAndWait(app_browser, app_url); |
| ASSERT_TRUE(app_browser->app_controller()); |
| NavigateAndCheckForToolbar(app_browser, GURL(kExampleURL), true); |
| } |
| |
| // Tests that desktop PWAs open links in a new tab at the end of the tabstrip of |
| // the last active browser. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, DesktopPWAsOpenLinksInNewTab) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| NavigateToURLAndWait(app_browser, app_url); |
| ASSERT_TRUE(app_browser->app_controller()); |
| |
| EXPECT_EQ(chrome::GetTotalBrowserCount(), 2u); |
| Browser* browser2 = CreateBrowser(app_browser->profile()); |
| EXPECT_EQ(chrome::GetTotalBrowserCount(), 3u); |
| |
| TabStripModel* model2 = browser2->tab_strip_model(); |
| chrome::AddTabAt(browser2, GURL(), -1, true); |
| EXPECT_EQ(model2->count(), 2); |
| model2->SelectPreviousTab(); |
| EXPECT_EQ(model2->active_index(), 0); |
| |
| NavigateParams param(app_browser, GURL("http://www.google.com/"), |
| ui::PAGE_TRANSITION_LINK); |
| param.window_action = NavigateParams::SHOW_WINDOW; |
| param.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; |
| |
| ui_test_utils::NavigateToURL(¶m); |
| |
| EXPECT_EQ(chrome::GetTotalBrowserCount(), 3u); |
| EXPECT_EQ(model2->count(), 3); |
| EXPECT_EQ(param.browser, browser2); |
| EXPECT_EQ(model2->active_index(), 2); |
| EXPECT_EQ(param.navigated_or_inserted_contents, |
| model2->GetActiveWebContents()); |
| } |
| |
| // Tests that desktop PWAs are opened at the correct size. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, PWASizeIsCorrectlyRestored) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| EXPECT_TRUE(AppBrowserController::IsForWebAppBrowser(app_browser)); |
| NavigateToURLAndWait(app_browser, app_url); |
| |
| const gfx::Rect bounds = gfx::Rect(50, 50, 500, 500); |
| app_browser->window()->SetBounds(bounds); |
| app_browser->window()->Close(); |
| |
| Browser* const new_browser = LaunchWebAppBrowser(app_id); |
| EXPECT_EQ(new_browser->window()->GetBounds(), bounds); |
| } |
| |
| // Tests that desktop PWAs are reopened at the correct size. |
| IN_PROC_BROWSER_TEST_P(WebAppTabRestoreBrowserTest, |
| ReopenedPWASizeIsCorrectlyRestored) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| EXPECT_TRUE(AppBrowserController::IsForWebAppBrowser(app_browser)); |
| NavigateToURLAndWait(app_browser, app_url); |
| |
| const gfx::Rect bounds = gfx::Rect(50, 50, 500, 500); |
| app_browser->window()->SetBounds(bounds); |
| app_browser->window()->Close(); |
| |
| content::WebContentsAddedObserver new_contents_observer; |
| |
| sessions::TabRestoreService* const service = |
| TabRestoreServiceFactory::GetForProfile(profile()); |
| ASSERT_GT(service->entries().size(), 0U); |
| service->RestoreMostRecentEntry(nullptr); |
| |
| content::WebContents* const restored_web_contents = |
| new_contents_observer.GetWebContents(); |
| Browser* const restored_browser = |
| chrome::FindBrowserWithWebContents(restored_web_contents); |
| EXPECT_EQ(restored_browser->window()->GetBounds(), bounds); |
| } |
| |
| // Tests that using window.open to create a popup window out of scope results in |
| // a correctly sized window. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, OffScopePWAPopupsHaveCorrectSize) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| |
| EXPECT_TRUE(AppBrowserController::IsForWebAppBrowser(app_browser)); |
| |
| const GURL offscope_url("https://example.com"); |
| const gfx::Size size(500, 500); |
| |
| Browser* const popup_browser = |
| OpenPopupAndWait(app_browser, offscope_url, size); |
| |
| // The navigation should have happened in a new window. |
| EXPECT_NE(popup_browser, app_browser); |
| |
| // The popup browser should be a PWA. |
| EXPECT_TRUE(AppBrowserController::IsForWebAppBrowser(popup_browser)); |
| |
| // Toolbar should be shown, as the popup is out of scope. |
| EXPECT_TRUE(popup_browser->app_controller()->ShouldShowCustomTabBar()); |
| |
| // Skip animating the toolbar visibility. |
| popup_browser->app_controller()->UpdateCustomTabBarVisibility(false); |
| |
| // The popup window should be the size we specified. |
| EXPECT_EQ(size, popup_browser->window()->GetContentsSize()); |
| } |
| |
| // Tests that using window.open to create a popup window in scope results in |
| // a correctly sized window. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, InScopePWAPopupsHaveCorrectSize) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| |
| EXPECT_TRUE(AppBrowserController::IsForWebAppBrowser(app_browser)); |
| |
| const gfx::Size size(500, 500); |
| Browser* const popup_browser = OpenPopupAndWait(app_browser, app_url, size); |
| |
| // The navigation should have happened in a new window. |
| EXPECT_NE(popup_browser, app_browser); |
| |
| // The popup browser should be a PWA. |
| EXPECT_TRUE(AppBrowserController::IsForWebAppBrowser(popup_browser)); |
| |
| // Toolbar should not be shown, as the popup is in scope. |
| EXPECT_FALSE(popup_browser->app_controller()->ShouldShowCustomTabBar()); |
| |
| // Skip animating the toolbar visibility. |
| popup_browser->app_controller()->UpdateCustomTabBarVisibility(false); |
| |
| // The popup window should be the size we specified. |
| EXPECT_EQ(size, popup_browser->window()->GetContentsSize()); |
| } |
| |
| // Tests that app windows are correctly restored. |
| IN_PROC_BROWSER_TEST_P(WebAppTabRestoreBrowserTest, RestoreAppWindow) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| ASSERT_TRUE(app_browser->is_type_app()); |
| app_browser->window()->Close(); |
| |
| content::WebContentsAddedObserver new_contents_observer; |
| |
| sessions::TabRestoreService* const service = |
| TabRestoreServiceFactory::GetForProfile(profile()); |
| service->RestoreMostRecentEntry(nullptr); |
| |
| content::WebContents* const restored_web_contents = |
| new_contents_observer.GetWebContents(); |
| Browser* const restored_browser = |
| chrome::FindBrowserWithWebContents(restored_web_contents); |
| |
| EXPECT_TRUE(restored_browser->is_type_app()); |
| } |
| |
| // Test navigating to an out of scope url on the same origin causes the url |
| // to be shown to the user. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, |
| LocationBarIsVisibleOffScopeOnSameOrigin) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| // Toolbar should not be visible in the app. |
| ASSERT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar()); |
| |
| // The installed PWA's scope is app.com:{PORT}/ssl, |
| // so app.com:{PORT}/accessibility_fail.html is out of scope. |
| const GURL out_of_scope = GetURLForPath("/accessibility_fail.html"); |
| NavigateToURLAndWait(app_browser, out_of_scope); |
| |
| // Location should be visible off scope. |
| ASSERT_TRUE(app_browser->app_controller()->ShouldShowCustomTabBar()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, UpgradeWithoutCustomTabBar) { |
| const GURL secure_app_url = |
| https_server()->GetURL("app.site.com", "/empty.html"); |
| GURL::Replacements rep; |
| rep.SetSchemeStr(url::kHttpScheme); |
| const GURL app_url = secure_app_url.ReplaceComponents(rep); |
| |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| NavigateToURLAndWait(app_browser, secure_app_url); |
| |
| EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar()); |
| |
| const GURL off_origin_url = |
| https_server()->GetURL("example.org", "/empty.html"); |
| NavigateToURLAndWait(app_browser, off_origin_url); |
| EXPECT_EQ(app_browser->app_controller()->ShouldShowCustomTabBar(), true); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, OverscrollEnabled) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| // Overscroll is only enabled on Aura platforms currently. |
| #if defined(USE_AURA) |
| EXPECT_TRUE(app_browser->CanOverscrollContent()); |
| #else |
| EXPECT_FALSE(app_browser->CanOverscrollContent()); |
| #endif |
| } |
| |
| // Check the 'Copy URL' menu button for Web App windows. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, CopyURL) { |
| const GURL app_url(kExampleURL); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| content::BrowserTestClipboardScope test_clipboard_scope; |
| chrome::ExecuteCommand(app_browser, IDC_COPY_URL); |
| |
| ui::Clipboard* const clipboard = ui::Clipboard::GetForCurrentThread(); |
| base::string16 result; |
| clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, &result); |
| EXPECT_EQ(result, base::UTF8ToUTF16(kExampleURL)); |
| } |
| |
| // Tests that the command for popping a tab out to a PWA window is disabled in |
| // incognito. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, PopOutDisabledInIncognito) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| |
| Browser* const incognito_browser = OpenURLOffTheRecord(profile(), app_url); |
| auto app_menu_model = |
| std::make_unique<AppMenuModel>(nullptr, incognito_browser); |
| app_menu_model->Init(); |
| ui::MenuModel* model = app_menu_model.get(); |
| int index = -1; |
| ASSERT_TRUE(app_menu_model->GetModelAndIndexForCommandId( |
| IDC_OPEN_IN_PWA_WINDOW, &model, &index)); |
| EXPECT_FALSE(model->IsEnabledAt(index)); |
| } |
| |
| // Tests that PWA menus have an uninstall option. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, UninstallMenuOption) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| auto app_menu_model = std::make_unique<WebAppMenuModel>(nullptr, app_browser); |
| app_menu_model->Init(); |
| ui::MenuModel* model = app_menu_model.get(); |
| int index = -1; |
| const bool found = app_menu_model->GetModelAndIndexForCommandId( |
| WebAppMenuModel::kUninstallAppCommandId, &model, &index); |
| #if defined(OS_CHROMEOS) |
| EXPECT_FALSE(found); |
| #else |
| EXPECT_TRUE(found); |
| EXPECT_TRUE(model->IsEnabledAt(index)); |
| |
| base::HistogramTester tester; |
| app_menu_model->ExecuteCommand(WebAppMenuModel::kUninstallAppCommandId, |
| /*event_flags=*/0); |
| tester.ExpectUniqueSample("HostedAppFrame.WrenchMenu.MenuAction", |
| MENU_ACTION_UNINSTALL_APP, 1); |
| tester.ExpectUniqueSample("WrenchMenu.MenuAction", MENU_ACTION_UNINSTALL_APP, |
| 1); |
| #endif // defined(OS_CHROMEOS) |
| } |
| |
| // Tests that both installing a PWA and creating a shortcut app are disabled for |
| // incognito windows. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, ShortcutMenuOptionsInIncognito) { |
| Browser* const incognito_browser = CreateIncognitoBrowser(profile()); |
| EXPECT_FALSE(NavigateAndAwaitInstallabilityCheck(incognito_browser, |
| GetSecureAppURL())); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, incognito_browser), |
| kDisabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, incognito_browser), |
| kNotPresent); |
| } |
| |
| // Tests that both installing a PWA and creating a shortcut app are disabled for |
| // an error page. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, ShortcutMenuOptionsForErrorPage) { |
| EXPECT_FALSE(NavigateAndAwaitInstallabilityCheck( |
| browser(), https_server()->GetURL("/invalid_path.html"))); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, browser()), kDisabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kNotPresent); |
| } |
| |
| // Tests that both installing a PWA and creating a shortcut app are available |
| // for an installable PWA. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, |
| ShortcutMenuOptionsForInstallablePWA) { |
| EXPECT_TRUE( |
| NavigateAndAwaitInstallabilityCheck(browser(), GetInstallableAppURL())); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, browser()), kEnabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kEnabled); |
| } |
| |
| // Tests that both installing a PWA and creating a shortcut app are disabled |
| // when page crashes. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, ShortcutMenuOptionsForCrashedTab) { |
| EXPECT_TRUE( |
| NavigateAndAwaitInstallabilityCheck(browser(), GetInstallableAppURL())); |
| content::WebContents* tab_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| tab_contents->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| ASSERT_TRUE(tab_contents->IsCrashed()); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, browser()), kDisabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, browser()), kDisabled); |
| } |
| |
| // Tests that an installed PWA is not used when out of scope by one path level. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, MenuOptionsOutsideInstalledPwaScope) { |
| NavigateToURLAndWait( |
| browser(), |
| https_server()->GetURL("/banners/scope_is_start_url/index.html")); |
| InstallPwaForCurrentUrl(); |
| |
| // Open a page that is one directory up from the installed PWA. |
| Browser* const new_browser = NavigateInNewWindowAndAwaitInstallabilityCheck( |
| https_server()->GetURL("/banners/no_manifest_test_page.html")); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, new_browser), kEnabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser), kNotPresent); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, new_browser), |
| kNotPresent); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, InstallInstallableSite) { |
| base::Time before_install_time = base::Time::Now(); |
| base::UserActionTester user_action_tester; |
| NavigateToURLAndWait(browser(), GetInstallableAppURL()); |
| |
| const AppId app_id = InstallPwaForCurrentUrl(); |
| auto* provider = WebAppProviderBase::GetProviderBase(profile()); |
| EXPECT_EQ(provider->registrar().GetAppShortName(app_id), |
| GetInstallableAppName()); |
| |
| // Installed PWAs should launch in their own window. |
| EXPECT_EQ(provider->registrar().GetAppUserDisplayMode(app_id), |
| blink::mojom::DisplayMode::kStandalone); |
| |
| // Installed PWAs should have install time set. |
| EXPECT_TRUE(provider->registrar().GetAppInstallTime(app_id) >= |
| before_install_time); |
| |
| EXPECT_EQ(1, user_action_tester.GetActionCount("InstallWebAppFromMenu")); |
| EXPECT_EQ(0, user_action_tester.GetActionCount("CreateShortcut")); |
| |
| #if defined(OS_CHROMEOS) |
| // Apps on Chrome OS should not be pinned after install. |
| EXPECT_FALSE(ChromeLauncherController::instance()->IsAppPinned(app_id)); |
| #endif |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, CanInstallOverTabPwa) { |
| NavigateToURLAndWait(browser(), GetInstallableAppURL()); |
| const AppId app_id = InstallPwaForCurrentUrl(); |
| |
| // Change display mode to open in tab. |
| auto* provider = WebAppProviderBase::GetProviderBase(profile()); |
| provider->registry_controller().SetAppUserDisplayMode( |
| app_id, blink::mojom::DisplayMode::kBrowser); |
| |
| Browser* const new_browser = |
| NavigateInNewWindowAndAwaitInstallabilityCheck(GetInstallableAppURL()); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, new_browser), kEnabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser), kEnabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, new_browser), |
| kNotPresent); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, CannotInstallOverWindowPwa) { |
| NavigateToURLAndWait(browser(), GetInstallableAppURL()); |
| InstallPwaForCurrentUrl(); |
| |
| // Avoid any interference if active browser was changed by PWA install. |
| Browser* const new_browser = |
| NavigateInNewWindowAndAwaitInstallabilityCheck(GetInstallableAppURL()); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, new_browser), kEnabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser), kNotPresent); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, new_browser), |
| kEnabled); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, CannotInstallOverPolicyPwa) { |
| ExternalInstallOptions options = CreateInstallOptions(GetInstallableAppURL()); |
| options.install_source = ExternalInstallSource::kExternalPolicy; |
| PendingAppManagerInstall(profile(), options); |
| |
| // Avoid any interference if active browser was changed by PWA install. |
| Browser* const new_browser = |
| NavigateInNewWindowAndAwaitInstallabilityCheck(GetInstallableAppURL()); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_CREATE_SHORTCUT, new_browser), |
| kDisabled); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_INSTALL_PWA, new_browser), kNotPresent); |
| EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, new_browser), |
| kEnabled); |
| } |
| |
| // Tests that the command for OpenActiveTabInPwaWindow is available for secure |
| // pages in an app's scope. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, ReparentWebAppForSecureActiveTab) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| |
| NavigateToURLAndWait(browser(), app_url); |
| content::WebContents* tab_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_EQ(tab_contents->GetLastCommittedURL(), app_url); |
| |
| EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()), |
| kEnabled); |
| |
| Browser* const app_browser = ReparentWebAppForActiveTab(browser()); |
| ASSERT_EQ(app_browser->app_controller()->GetAppId(), app_id); |
| } |
| |
| // Tests that reparenting the last browser tab doesn't close the browser window. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, ReparentLastBrowserTab) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| NavigateToURLAndWait(browser(), app_url); |
| |
| Browser* const app_browser = ReparentWebAppForActiveTab(browser()); |
| ASSERT_EQ(app_browser->app_controller()->GetAppId(), app_id); |
| |
| ASSERT_TRUE(IsBrowserOpen(browser())); |
| EXPECT_EQ(browser()->tab_strip_model()->count(), 1); |
| } |
| |
| // Tests that the manifest name of the current installable site is used in the |
| // installation menu text. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, InstallToShelfContainsAppName) { |
| EXPECT_TRUE( |
| NavigateAndAwaitInstallabilityCheck(browser(), GetInstallableAppURL())); |
| |
| auto app_menu_model = std::make_unique<AppMenuModel>(nullptr, browser()); |
| app_menu_model->Init(); |
| ui::MenuModel* model = app_menu_model.get(); |
| int index = -1; |
| EXPECT_TRUE(app_menu_model->GetModelAndIndexForCommandId(IDC_INSTALL_PWA, |
| &model, &index)); |
| EXPECT_EQ(app_menu_model.get(), model); |
| EXPECT_EQ(model->GetLabelAt(index), |
| base::UTF8ToUTF16("Install Manifest test app\xE2\x80\xA6")); |
| } |
| |
| // Check that no assertions are hit when showing a permission request bubble. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, PermissionBubble) { |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| content::RenderFrameHost* const render_frame_host = |
| app_browser->tab_strip_model()->GetActiveWebContents()->GetMainFrame(); |
| EXPECT_TRUE(content::ExecuteScript( |
| render_frame_host, |
| "navigator.geolocation.getCurrentPosition(function(){});")); |
| } |
| |
| // Ensure that web app windows with blank titles don't display the URL as a |
| // default window title. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, EmptyTitlesDoNotDisplayUrl) { |
| const GURL app_url = https_server()->GetURL("app.site.com", "/empty.html"); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| content::WebContents* const web_contents = |
| app_browser->tab_strip_model()->GetActiveWebContents(); |
| content::WaitForLoadStop(web_contents); |
| EXPECT_EQ(base::string16(), app_browser->GetWindowTitleForCurrentTab(false)); |
| NavigateToURLAndWait(app_browser, |
| https_server()->GetURL("app.site.com", "/simple.html")); |
| EXPECT_EQ(base::ASCIIToUTF16("OK"), |
| app_browser->GetWindowTitleForCurrentTab(false)); |
| } |
| |
| // Ensure that web app windows display the app title instead of the page |
| // title when off scope. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, OffScopeUrlsDisplayAppTitle) { |
| const GURL app_url = GetSecureAppURL(); |
| const base::string16 app_title = base::ASCIIToUTF16("A Web App"); |
| |
| auto web_app_info = std::make_unique<WebApplicationInfo>(); |
| web_app_info->app_url = app_url; |
| web_app_info->scope = app_url.GetWithoutFilename(); |
| web_app_info->title = app_title; |
| const AppId app_id = InstallWebApp(std::move(web_app_info)); |
| |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| content::WebContents* const web_contents = |
| app_browser->tab_strip_model()->GetActiveWebContents(); |
| content::WaitForLoadStop(web_contents); |
| |
| // When we are within scope, show the page title. |
| EXPECT_EQ(base::ASCIIToUTF16("Google"), |
| app_browser->GetWindowTitleForCurrentTab(false)); |
| |
| NavigateToURLAndWait(app_browser, |
| https_server()->GetURL("app.site.com", "/simple.html")); |
| |
| // When we are off scope, show the app title. |
| EXPECT_EQ(app_title, app_browser->GetWindowTitleForCurrentTab(false)); |
| } |
| |
| // Ensure that web app windows display the app title instead of the page |
| // title when using http. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, InScopeHttpUrlsDisplayAppTitle) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| const GURL app_url = |
| embedded_test_server()->GetURL("app.site.com", "/simple.html"); |
| const base::string16 app_title = base::ASCIIToUTF16("A Web App"); |
| |
| auto web_app_info = std::make_unique<WebApplicationInfo>(); |
| web_app_info->app_url = app_url; |
| web_app_info->title = app_title; |
| const AppId app_id = InstallWebApp(std::move(web_app_info)); |
| |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| content::WebContents* const web_contents = |
| app_browser->tab_strip_model()->GetActiveWebContents(); |
| content::WaitForLoadStop(web_contents); |
| |
| // The page title is "OK" but the page is being served over HTTP, so the app |
| // title should be used instead. |
| EXPECT_EQ(app_title, app_browser->GetWindowTitleForCurrentTab(false)); |
| } |
| |
| // Check that a subframe on a regular web page can navigate to a URL that |
| // redirects to a web app. https://crbug.com/721949. |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, SubframeRedirectsToWebApp) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Set up a web app which covers app.com URLs. |
| GURL app_url = embedded_test_server()->GetURL("app.com", "/title1.html"); |
| const AppId app_id = InstallPWA(app_url); |
| |
| // Navigate a regular tab to a page with a subframe. |
| const GURL url = embedded_test_server()->GetURL("foo.com", "/iframe.html"); |
| content::WebContents* const tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| NavigateToURLAndWait(browser(), url); |
| |
| // Navigate the subframe to a URL that redirects to a URL in the web app's |
| // web extent. |
| const GURL redirect_url = embedded_test_server()->GetURL( |
| "bar.com", "/server-redirect?" + app_url.spec()); |
| EXPECT_TRUE(NavigateIframeToURL(tab, "test", redirect_url)); |
| |
| // Ensure that the frame navigated successfully and that it has correct |
| // content. |
| content::RenderFrameHost* const subframe = |
| content::ChildFrameAt(tab->GetMainFrame(), 0); |
| EXPECT_EQ(app_url, subframe->GetLastCommittedURL()); |
| EXPECT_EQ( |
| "This page has no title.", |
| EvalJs(subframe, "document.body.innerText.trim();").ExtractString()); |
| } |
| |
| #if defined(OS_MACOSX) |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, NewAppWindow) { |
| BrowserList* const browser_list = BrowserList::GetInstance(); |
| const GURL app_url = GetSecureAppURL(); |
| const AppId app_id = InstallPWA(app_url); |
| Browser* const app_browser = LaunchWebAppBrowser(app_id); |
| |
| EXPECT_EQ(browser_list->size(), 2U); |
| EXPECT_TRUE(chrome::ExecuteCommand(app_browser, IDC_NEW_WINDOW)); |
| EXPECT_EQ(browser_list->size(), 3U); |
| Browser* const new_browser = browser_list->GetLastActive(); |
| EXPECT_NE(new_browser, browser()); |
| EXPECT_NE(new_browser, app_browser); |
| EXPECT_TRUE(new_browser->is_type_app()); |
| EXPECT_EQ(new_browser->app_controller()->GetAppId(), app_id); |
| |
| WebAppProviderBase::GetProviderBase(profile()) |
| ->registry_controller() |
| .SetAppUserDisplayMode(app_id, DisplayMode::kBrowser); |
| EXPECT_EQ(browser()->tab_strip_model()->count(), 1); |
| EXPECT_TRUE(chrome::ExecuteCommand(app_browser, IDC_NEW_WINDOW)); |
| EXPECT_EQ(browser_list->GetLastActive(), browser()); |
| EXPECT_EQ(browser()->tab_strip_model()->count(), 2); |
| EXPECT_EQ( |
| browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(), |
| app_url); |
| } |
| |
| #endif |
| |
| IN_PROC_BROWSER_TEST_P(WebAppBrowserTest, PopupLocationBar) { |
| #if defined(OS_MACOSX) |
| ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen; |
| #endif |
| const GURL app_url = GetSecureAppURL(); |
| const GURL in_scope = |
| https_server()->GetURL("app.com", "/ssl/page_with_subresource.html"); |
| const AppId app_id = InstallPWA(app_url); |
| |
| Browser* const popup_browser = web_app::CreateWebApplicationWindow( |
| profile(), app_id, WindowOpenDisposition::NEW_POPUP); |
| |
| EXPECT_TRUE( |
| popup_browser->CanSupportWindowFeature(Browser::FEATURE_LOCATIONBAR)); |
| EXPECT_TRUE( |
| popup_browser->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)); |
| |
| FullscreenNotificationObserver waiter(popup_browser); |
| chrome::ToggleFullscreenMode(popup_browser); |
| waiter.Wait(); |
| |
| EXPECT_TRUE( |
| popup_browser->CanSupportWindowFeature(Browser::FEATURE_LOCATIONBAR)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| WebAppBrowserTest, |
| ::testing::Values(ProviderType::kBookmarkApps, |
| ProviderType::kWebApps), |
| ProviderTypeParamToString); |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| WebAppTabRestoreBrowserTest, |
| ::testing::Values(ProviderType::kBookmarkApps, |
| ProviderType::kWebApps), |
| ProviderTypeParamToString); |
| |
| } // namespace web_app |