blob: 3df4d99fbc314e10d2c4df55b11ea390758c0170 [file] [log] [blame]
// Copyright 2020 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/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/banners/test_app_banner_manager_desktop.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/tabs/tab_strip_model.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/web_applications/test/web_app_test_observers.h"
#include "chrome/browser/web_applications/user_display_mode.h"
#include "chrome/browser/web_applications/web_app_command_manager.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_prefs_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/common/extension.h"
#include "url/gurl.h"
namespace {
std::string LoadExtension(Profile* profile, const base::FilePath& path) {
extensions::ChromeTestExtensionLoader loader(profile);
scoped_refptr<const extensions::Extension> extension =
loader.LoadExtension(path);
EXPECT_TRUE(extension);
return extension->id();
}
} // namespace
namespace web_app {
class CreateShortcutBrowserTest : public WebAppControllerBrowserTest {
public:
AppId InstallShortcutAppForCurrentUrl(bool open_as_window = false) {
chrome::SetAutoAcceptWebAppDialogForTesting(true, open_as_window);
WebAppTestInstallObserver observer(profile());
observer.BeginListening();
CHECK(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
AppId app_id = observer.Wait();
chrome::SetAutoAcceptWebAppDialogForTesting(false, false);
return app_id;
}
WebAppRegistrar& registrar() {
auto* provider = WebAppProvider::GetForTest(profile());
CHECK(provider);
return provider->registrar();
}
WebAppSyncBridge& sync_bridge() {
auto* provider = WebAppProvider::GetForTest(profile());
CHECK(provider);
return provider->sync_bridge();
}
};
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest,
CreateShortcutForInstallableSite) {
base::UserActionTester user_action_tester;
NavigateToURLAndWait(browser(), GetInstallableAppURL());
AppId app_id = InstallShortcutAppForCurrentUrl();
EXPECT_EQ(registrar().GetAppShortName(app_id), GetInstallableAppName());
// Shortcut apps to PWAs should launch in a tab.
EXPECT_EQ(registrar().GetAppUserDisplayMode(app_id),
UserDisplayMode::kBrowser);
EXPECT_EQ(0, user_action_tester.GetActionCount("InstallWebAppFromMenu"));
EXPECT_EQ(1, user_action_tester.GetActionCount("CreateShortcut"));
}
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest, InstallSourceRecorded) {
ASSERT_TRUE(embedded_test_server()->Start());
// LatestWebAppInstallSource should be correctly set and reported to UMA for
// both installable and non-installable (shortcut) sites.
for (const GURL& url :
{GetInstallableAppURL(),
embedded_test_server()->GetURL(
"/web_apps/get_manifest.html?theme_color_only.json")}) {
base::HistogramTester histogram_tester;
NavigateToURLAndWait(browser(), url);
AppId app_id = InstallShortcutAppForCurrentUrl();
EXPECT_EQ(webapps::WebappInstallSource::MENU_CREATE_SHORTCUT,
*registrar().GetAppInstallSourceForMetrics(app_id));
histogram_tester.ExpectUniqueSample(
"Webapp.Install.InstallEvent",
static_cast<int>(webapps::WebappInstallSource::MENU_CREATE_SHORTCUT),
1);
}
}
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest,
CanInstallOverTabShortcutApp) {
NavigateToURLAndWait(browser(), GetInstallableAppURL());
InstallShortcutAppForCurrentUrl();
Browser* 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_F(CreateShortcutBrowserTest,
CannotInstallOverWindowShortcutApp) {
NavigateToURLAndWait(browser(), GetInstallableAppURL());
AppId app_id = InstallShortcutAppForCurrentUrl();
// Change launch container to open in window.
sync_bridge().SetAppUserDisplayMode(app_id, UserDisplayMode::kStandalone,
/*is_user_action=*/false);
Browser* 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);
}
// Check that toolbar is not shown for shortcut apps within extensions pages.
// This simulates a case where the user has manually navigated to a page hosted
// within an extension, then added it as a shortcut app.
// Regression test for https://crbug.com/828233.
//
// TODO(crbug.com/1253234): Remove chrome-extension scheme for web apps.
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest,
ShouldShowCustomTabBarForExtensionPage) {
// This involves the creation of a regular (non-app) extension with a popup
// page, and the creation of a shortcut app created from the popup page URL
// (allowing the extension's popup page to be loaded in a window).
base::FilePath test_data_dir_;
base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
// Install the extension that has the popup page.
std::string extension_id =
LoadExtension(profile(), test_data_dir_.AppendASCII("extensions")
.AppendASCII("ui")
.AppendASCII("browser_action_popup"));
base::RunLoop().RunUntilIdle(); // Ensure the extension is fully loaded.
// Install the shortcut app that links to the extension's popup page.
const GURL popup_url("chrome-extension://" + extension_id + "/popup.html");
NavigateToURLAndWait(browser(), popup_url);
// TODO(crbug.com/1253234): IDC_CREATE_SHORTCUT command must become disabled.
ASSERT_TRUE(chrome::IsCommandEnabled(browser(), IDC_CREATE_SHORTCUT));
const AppId app_id = InstallShortcutAppForCurrentUrl();
Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id);
CHECK(app_browser);
CHECK(app_browser != browser());
// Navigate to the app's launch page; the toolbar should not be visible,
// because extensions pages are secure.
NavigateAndCheckForToolbar(app_browser, popup_url, false);
}
// Tests that Create Shortcut doesn't timeout on a page that has a delayed
// iframe load. Context: crbug.com/1046883
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest, WorksAfterDelayedIFrameLoad) {
ASSERT_TRUE(embedded_test_server()->Start());
NavigateToURLAndWait(browser(), embedded_test_server()->GetURL(
"/favicon/page_with_favicon.html"));
// Append an iframe and wait for it to finish loading.
const char script[] = R"(
const iframe = document.createElement('iframe');
iframe.onload = _ => domAutomationController.send('success');
iframe.srcdoc = 'inner page';
document.body.appendChild(iframe);
)";
EXPECT_EQ(
content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
script, content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
.ExtractString(),
"success");
InstallShortcutAppForCurrentUrl();
}
// Tests that Create Shortcut on non-promotable sites still uses available
// manifest data.
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest,
UseNonPromotableManifestData) {
ASSERT_TRUE(embedded_test_server()->Start());
NavigateToURLAndWait(
browser(), embedded_test_server()->GetURL(
"/web_apps/get_manifest.html?theme_color_only.json"));
AppId app_id = InstallShortcutAppForCurrentUrl();
EXPECT_EQ(registrar().GetAppThemeColor(app_id),
SkColorSetRGB(0x12, 0x34, 0x56));
}
// Tests that Create Shortcut won't use manifest data that's invalid.
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest, IgnoreInvalidManifestData) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL(
"/web_apps/get_manifest.html?invalid_start_url.json");
NavigateToURLAndWait(browser(), url);
AppId app_id = InstallShortcutAppForCurrentUrl();
EXPECT_EQ(registrar().GetAppStartUrl(app_id), url);
}
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest,
CreateShortcutAgainOverwriteUserDisplayMode) {
base::UserActionTester user_action_tester;
NavigateToURLAndWait(browser(), GetInstallableAppURL());
AppId app_id = InstallShortcutAppForCurrentUrl();
EXPECT_EQ(registrar().GetAppShortName(app_id), GetInstallableAppName());
// Shortcut apps to PWAs should launch in a tab.
EXPECT_EQ(registrar().GetAppUserDisplayMode(app_id),
UserDisplayMode::kBrowser);
// TODO(crbug.com/1275945): We need to wait a bit longer for the
// WebAppInstallTask to complete before starting another install.
// Move the install/update/uninstall events out of
// AppRegistrarObserver and into a WebAppInstallManagerObserver
// interface so they can be guaranteed to fire after the
// WebAppInstallTask's lifetime has ended.
base::RunLoop().RunUntilIdle();
InstallShortcutAppForCurrentUrl(/*open_as_window=*/true);
// Re-install with enabling open_as_window should update user display mode.
EXPECT_EQ(registrar().GetAppUserDisplayMode(app_id),
UserDisplayMode::kStandalone);
}
IN_PROC_BROWSER_TEST_F(CreateShortcutBrowserTest, OpenShortcutWindowOnlyOnce) {
base::UserActionTester user_action_tester;
NavigateToURLAndWait(browser(), GetInstallableAppURL());
WebAppTestInstallObserver observer(profile());
// The "Create shortcut" call is executed twice, but the dialog
// must be shown only once.
ASSERT_TRUE(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
ASSERT_TRUE(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
EXPECT_EQ(1u, provider().command_manager().GetCommandCountForTesting());
}
} // namespace web_app