// Copyright (c) 2012 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 #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/launch_util.h" #include "chrome/browser/profiles/profile.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/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/extensions/bookmark_app_util.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_constants.h" #include "extensions/browser/api/management/management_api.h" #include "extensions/browser/extension_dialog_auto_confirm.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/test_management_policy.h" #include "extensions/common/manifest.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" #include "extensions/test/test_extension_dir.h" #include "net/dns/mock_host_resolver.h" using extensions::Extension; using extensions::Manifest; namespace { // Find a browser other than |browser|. Browser* FindOtherBrowser(Browser* browser) { Browser* found = NULL; for (auto* b : *BrowserList::GetInstance()) { if (b == browser) continue; found = b; } return found; } } // namespace class ExtensionManagementApiTest : public extensions::ExtensionApiTest { public: virtual void LoadExtensions() { base::FilePath basedir = test_data_dir_.AppendASCII("management"); // Load 5 enabled items. LoadNamedExtension(basedir, "enabled_extension"); LoadNamedExtension(basedir, "enabled_app"); LoadNamedExtension(basedir, "description"); LoadNamedExtension(basedir, "permissions"); LoadNamedExtension(basedir, "short_name"); // Load 2 disabled items. LoadNamedExtension(basedir, "disabled_extension"); DisableExtension(extension_ids_["disabled_extension"]); LoadNamedExtension(basedir, "disabled_app"); DisableExtension(extension_ids_["disabled_app"]); } // Load an app, and wait for a message from app "management/launch_on_install" // indicating that the new app has been launched. void LoadAndWaitForLaunch(const std::string& app_path, std::string* out_app_id) { ExtensionTestMessageListener launched_app("launched app", false); ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(app_path))); if (out_app_id) *out_app_id = last_loaded_extension_id(); ASSERT_TRUE(launched_app.WaitUntilSatisfied()); } protected: void LoadNamedExtension(const base::FilePath& path, const std::string& name) { const Extension* extension = LoadExtension(path.AppendASCII(name)); ASSERT_TRUE(extension); extension_ids_[name] = extension->id(); } void InstallNamedExtension(const base::FilePath& path, const std::string& name, Manifest::Location install_source) { const Extension* extension = InstallExtension(path.AppendASCII(name), 1, install_source); ASSERT_TRUE(extension); extension_ids_[name] = extension->id(); } // Maps installed extension names to their IDs. std::map extension_ids_; }; IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, Basics) { LoadExtensions(); base::FilePath basedir = test_data_dir_.AppendASCII("management"); InstallNamedExtension(basedir, "internal_extension", Manifest::INTERNAL); InstallNamedExtension(basedir, "external_extension", Manifest::EXTERNAL_PREF); InstallNamedExtension(basedir, "admin_extension", Manifest::EXTERNAL_POLICY_DOWNLOAD); InstallNamedExtension(basedir, "version_name", Manifest::INTERNAL); ASSERT_TRUE(RunExtensionSubtest("management/test", "basics.html")); } IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, NoPermission) { LoadExtensions(); ASSERT_TRUE(RunExtensionSubtest("management/no_permission", "test.html")); } // Disabled: http://crbug.com/174411 #if defined(OS_WIN) #define MAYBE_Uninstall DISABLED_Uninstall #else #define MAYBE_Uninstall Uninstall #endif IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_Uninstall) { LoadExtensions(); // Confirmation dialog will be shown for uninstallations except for self. extensions::ScopedTestDialogAutoConfirm auto_confirm( extensions::ScopedTestDialogAutoConfirm::ACCEPT); ASSERT_TRUE(RunExtensionSubtest("management/test", "uninstall.html")); } IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, CreateAppShortcut) { LoadExtensions(); base::FilePath basedir = test_data_dir_.AppendASCII("management"); LoadNamedExtension(basedir, "packaged_app"); extensions::ManagementCreateAppShortcutFunction::SetAutoConfirmForTest(true); ASSERT_TRUE(RunExtensionSubtest("management/test", "createAppShortcut.html")); } IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, GenerateAppForLink) { ASSERT_TRUE(RunExtensionSubtest("management/test", "generateAppForLink.html")); } class InstallReplacementWebAppApiTest : public ExtensionManagementApiTest { public: InstallReplacementWebAppApiTest() : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {} ~InstallReplacementWebAppApiTest() override = default; protected: void SetUpOnMainThread() override { ExtensionManagementApiTest::SetUpOnMainThread(); https_test_server_.ServeFilesFromDirectory(test_data_dir_); ASSERT_TRUE(https_test_server_.Start()); } void RunTest(const char* web_app_path, const char* background_script, bool from_webstore) { static constexpr char kManifest[] = R"({ "name": "Management API Test", "version": "0.1", "manifest_version": 2, "background": { "scripts": ["background.js"] }, "replacement_web_app": "%s" })"; extensions::TestExtensionDir extension_dir; extension_dir.WriteManifest(base::StringPrintf( kManifest, https_test_server_.GetURL(web_app_path).spec().c_str())); extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), background_script); extensions::ResultCatcher catcher; if (from_webstore) { // |expected_change| is the expected change in the number of installed // extensions. ASSERT_TRUE(InstallExtensionFromWebstore(extension_dir.UnpackedPath(), 1 /* expected_change */)); } else { ASSERT_TRUE(LoadExtension(extension_dir.UnpackedPath())); } ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); } net::EmbeddedTestServer https_test_server_; }; IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NotWebstore) { static constexpr char kBackground[] = R"( chrome.management.installReplacementWebApp(function() { chrome.test.assertLastError( 'Only extensions from the web store can install replacement web apps.'); chrome.test.notifyPass(); });)"; RunTest("/management/install_replacement_web_app/good_web_app/index.html", kBackground, false /* from_webstore */); } IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NoGesture) { static constexpr char kBackground[] = R"( chrome.management.installReplacementWebApp(function() { chrome.test.assertLastError( 'chrome.management.installReplacementWebApp requires a user gesture.'); chrome.test.notifyPass(); });)"; RunTest("/management/install_replacement_web_app/good_web_app/index.html", kBackground, true /* from_webstore */); } IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NotInstallableWebApp) { static constexpr char kBackground[] = R"(chrome.test.runWithUserGesture(function() { chrome.management.installReplacementWebApp(function() { chrome.test.assertLastError( 'Web app is not a valid installable web app.'); chrome.test.notifyPass(); }); });)"; RunTest("/management/install_replacement_web_app/bad_web_app/index.html", kBackground, true /* from_webstore */); } IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, InstallableWebApp) { static constexpr char kBackground[] = R"(chrome.test.runTests([ function runInstall() { chrome.test.runWithUserGesture(function() { chrome.management.installReplacementWebApp(function() { chrome.test.assertNoLastError(); chrome.test.succeed(); }); }); }, function runInstallWhenAlreadyInstalled() { chrome.test.runWithUserGesture(function() { chrome.management.installReplacementWebApp(function() { chrome.test.assertLastError( 'Web app is already installed.'); chrome.test.succeed(); }); }); } ]);)"; static constexpr char kGoodWebAppURL[] = "/management/install_replacement_web_app/good_web_app/index.html"; chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true); const GURL good_web_app_url = https_test_server_.GetURL(kGoodWebAppURL); EXPECT_FALSE(extensions::BookmarkOrHostedAppInstalled(browser()->profile(), good_web_app_url)); RunTest(kGoodWebAppURL, kBackground, true /* from_webstore */); EXPECT_TRUE(extensions::BookmarkOrHostedAppInstalled(browser()->profile(), good_web_app_url)); chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false); } // Fails often on Windows dbg bots. http://crbug.com/177163 #if defined(OS_WIN) #define MAYBE_ManagementPolicyAllowed DISABLED_ManagementPolicyAllowed #else #define MAYBE_ManagementPolicyAllowed ManagementPolicyAllowed #endif // defined(OS_WIN) // Tests actions on extensions when no management policy is in place. IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_ManagementPolicyAllowed) { LoadExtensions(); extensions::ScopedTestDialogAutoConfirm auto_confirm( extensions::ScopedTestDialogAutoConfirm::ACCEPT); extensions::ExtensionService* service = extensions::ExtensionSystem::Get(browser()->profile()) ->extension_service(); EXPECT_TRUE(service->GetExtensionById(extension_ids_["enabled_extension"], false)); // Ensure that all actions are allowed. extensions::ExtensionSystem::Get( browser()->profile())->management_policy()->UnregisterAllProviders(); ASSERT_TRUE(RunExtensionSubtest("management/management_policy", "allowed.html")); // The last thing the test does is uninstall the "enabled_extension". EXPECT_FALSE(service->GetExtensionById(extension_ids_["enabled_extension"], true)); } // Fails often on Windows dbg bots. http://crbug.com/177163 #if defined(OS_WIN) #define MAYBE_ManagementPolicyProhibited DISABLED_ManagementPolicyProhibited #else #define MAYBE_ManagementPolicyProhibited ManagementPolicyProhibited #endif // defined(OS_WIN) // Tests actions on extensions when management policy prohibits those actions. IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_ManagementPolicyProhibited) { LoadExtensions(); extensions::ExtensionService* service = extensions::ExtensionSystem::Get(browser()->profile()) ->extension_service(); EXPECT_TRUE(service->GetExtensionById(extension_ids_["enabled_extension"], false)); // Prohibit status changes. extensions::ManagementPolicy* policy = extensions::ExtensionSystem::Get( browser()->profile())->management_policy(); policy->UnregisterAllProviders(); extensions::TestManagementPolicyProvider provider( extensions::TestManagementPolicyProvider::PROHIBIT_MODIFY_STATUS); policy->RegisterProvider(&provider); ASSERT_TRUE(RunExtensionSubtest("management/management_policy", "prohibited.html")); } IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, LaunchPanelApp) { extensions::ExtensionService* service = extensions::ExtensionSystem::Get(browser()->profile()) ->extension_service(); // Load an extension that calls launchApp() on any app that gets // installed. ExtensionTestMessageListener launcher_loaded("launcher loaded", false); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("management/launch_on_install"))); ASSERT_TRUE(launcher_loaded.WaitUntilSatisfied()); // Load an app with app.launch.container = "panel". std::string app_id; LoadAndWaitForLaunch("management/launch_app_panel", &app_id); ASSERT_FALSE(HasFatalFailure()); // Stop the test if any ASSERT failed. // Find the app's browser. Check that it is a popup. ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile())); Browser* app_browser = FindOtherBrowser(browser()); ASSERT_TRUE(app_browser->is_type_popup()); ASSERT_TRUE(app_browser->is_app()); // Close the app panel. CloseBrowserSynchronously(app_browser); // Unload the extension. UninstallExtension(app_id); ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile())); ASSERT_FALSE(service->GetExtensionById(app_id, true)); // Set a pref indicating that the user wants to launch in a regular tab. // This should be ignored, because panel apps always load in a popup. extensions::SetLaunchType(browser()->profile(), app_id, extensions::LAUNCH_TYPE_REGULAR); // Load the extension again. std::string app_id_new; LoadAndWaitForLaunch("management/launch_app_panel", &app_id_new); ASSERT_FALSE(HasFatalFailure()); // If the ID changed, then the pref will not apply to the app. ASSERT_EQ(app_id, app_id_new); // Find the app's browser. Apps that should load in a panel ignore // prefs, so we should still see the launch in a popup. ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile())); app_browser = FindOtherBrowser(browser()); ASSERT_TRUE(app_browser->is_type_popup()); ASSERT_TRUE(app_browser->is_app()); } // Disabled: crbug.com/230165, crbug.com/915339 #if defined(OS_WIN) || defined(OS_MACOSX) #define MAYBE_LaunchTabApp DISABLED_LaunchTabApp #else #define MAYBE_LaunchTabApp LaunchTabApp #endif IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_LaunchTabApp) { extensions::ExtensionService* service = extensions::ExtensionSystem::Get(browser()->profile()) ->extension_service(); // Load an extension that calls launchApp() on any app that gets // installed. ExtensionTestMessageListener launcher_loaded("launcher loaded", false); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("management/launch_on_install"))); ASSERT_TRUE(launcher_loaded.WaitUntilSatisfied()); // Code below assumes that the test starts with a single browser window // hosting one tab. ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile())); ASSERT_EQ(1, browser()->tab_strip_model()->count()); // Load an app with app.launch.container = "tab". std::string app_id; LoadAndWaitForLaunch("management/launch_app_tab", &app_id); ASSERT_FALSE(HasFatalFailure()); // Check that the app opened in a new tab of the existing browser. ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile())); ASSERT_EQ(2, browser()->tab_strip_model()->count()); // Unload the extension. UninstallExtension(app_id); ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile())); ASSERT_FALSE(service->GetExtensionById(app_id, true)); // Set a pref indicating that the user wants to launch in a window. extensions::SetLaunchType(browser()->profile(), app_id, extensions::LAUNCH_TYPE_WINDOW); std::string app_id_new; LoadAndWaitForLaunch("management/launch_app_tab", &app_id_new); ASSERT_FALSE(HasFatalFailure()); // If the ID changed, then the pref will not apply to the app. ASSERT_EQ(app_id, app_id_new); // Find the app's browser. Opening in a new window will create // a new browser. ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile())); Browser* app_browser = FindOtherBrowser(browser()); ASSERT_TRUE(app_browser->is_app()); } // Flaky on MacOS: crbug.com/915339 #if defined(OS_MACOSX) #define MAYBE_LaunchType DISABLED_LaunchType #else #define MAYBE_LaunchType LaunchType #endif IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_LaunchType) { LoadExtensions(); base::FilePath basedir = test_data_dir_.AppendASCII("management"); LoadNamedExtension(basedir, "packaged_app"); ASSERT_TRUE(RunExtensionSubtest("management/test", "launchType.html")); }