shell_extension_loader_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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. #include "extensions/shell/browser/shell_extension_loader.h"
  5. #include <memory>
  6. #include <string>
  7. #include "apps/app_lifetime_monitor_factory.h"
  8. #include "base/memory/raw_ptr.h"
  9. #include "base/path_service.h"
  10. #include "components/crx_file/id_util.h"
  11. #include "components/keep_alive_registry/keep_alive_registry.h"
  12. #include "components/user_prefs/user_prefs.h"
  13. #include "content/public/browser/browser_context.h"
  14. #include "content/public/test/test_utils.h"
  15. #include "extensions/browser/disable_reason.h"
  16. #include "extensions/browser/extension_prefs.h"
  17. #include "extensions/browser/extension_registry.h"
  18. #include "extensions/browser/extensions_test.h"
  19. #include "extensions/browser/mock_extension_system.h"
  20. #include "extensions/browser/null_app_sorting.h"
  21. #include "extensions/browser/test_event_router.h"
  22. #include "extensions/browser/test_extensions_browser_client.h"
  23. #include "extensions/common/api/app_runtime.h"
  24. #include "extensions/common/extension.h"
  25. #include "extensions/common/extension_id.h"
  26. #include "extensions/common/extension_paths.h"
  27. #include "extensions/test/test_extension_dir.h"
  28. #if defined(USE_AURA)
  29. #include "extensions/browser/app_window/app_window.h"
  30. #include "extensions/shell/browser/shell_app_window_client.h"
  31. #include "extensions/shell/browser/shell_native_app_window_aura.h"
  32. #include "extensions/shell/test/shell_test_extensions_browser_client.h"
  33. #include "extensions/shell/test/shell_test_helper_aura.h"
  34. #endif
  35. namespace extensions {
  36. namespace OnLaunched = api::app_runtime::OnLaunched;
  37. namespace {
  38. class TestExtensionSystem : public MockExtensionSystem {
  39. public:
  40. explicit TestExtensionSystem(content::BrowserContext* context)
  41. : MockExtensionSystem(context) {}
  42. TestExtensionSystem(const TestExtensionSystem&) = delete;
  43. TestExtensionSystem& operator=(const TestExtensionSystem&) = delete;
  44. ~TestExtensionSystem() override = default;
  45. AppSorting* app_sorting() override { return &app_sorting_; }
  46. private:
  47. NullAppSorting app_sorting_;
  48. };
  49. #if defined(USE_AURA)
  50. // An AppWindowClient for use without a DesktopController.
  51. class TestAppWindowClient : public ShellAppWindowClient {
  52. public:
  53. TestAppWindowClient() = default;
  54. TestAppWindowClient(const TestAppWindowClient&) = delete;
  55. TestAppWindowClient& operator=(const TestAppWindowClient&) = delete;
  56. ~TestAppWindowClient() override = default;
  57. // ShellAppWindowClient:
  58. std::unique_ptr<NativeAppWindow> CreateNativeAppWindow(
  59. AppWindow* window,
  60. AppWindow::CreateParams* params) override {
  61. return std::make_unique<ShellNativeAppWindowAura>(window, *params);
  62. }
  63. };
  64. #endif
  65. } // namespace
  66. class ShellExtensionLoaderTest : public ExtensionsTest {
  67. public:
  68. ShellExtensionLoaderTest(const ShellExtensionLoaderTest&) = delete;
  69. ShellExtensionLoaderTest& operator=(const ShellExtensionLoaderTest&) = delete;
  70. protected:
  71. ShellExtensionLoaderTest() = default;
  72. ~ShellExtensionLoaderTest() override = default;
  73. void SetUp() override {
  74. // Register factory so it's created with the BrowserContext.
  75. apps::AppLifetimeMonitorFactory::GetInstance();
  76. ExtensionsTest::SetUp();
  77. extensions_browser_client()->set_extension_system_factory(&factory_);
  78. // ExtensionsTest sets up the ExtensionPrefs, but we still need to attach
  79. // the PrefService to the browser context.
  80. user_prefs::UserPrefs::Set(browser_context(), pref_service());
  81. event_router_ = CreateAndUseTestEventRouter(browser_context());
  82. }
  83. void TearDown() override {
  84. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  85. ExtensionsTest::TearDown();
  86. }
  87. // Returns the path to a test directory.
  88. base::FilePath GetExtensionPath(const std::string& dir) {
  89. base::FilePath test_data_dir;
  90. base::PathService::Get(DIR_TEST_DATA, &test_data_dir);
  91. return test_data_dir.AppendASCII(dir);
  92. }
  93. // Verifies the extension is correctly created and enabled.
  94. void ExpectEnabled(const Extension* extension) {
  95. ASSERT_TRUE(extension);
  96. EXPECT_EQ(mojom::ManifestLocation::kCommandLine, extension->location());
  97. ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
  98. EXPECT_TRUE(registry->enabled_extensions().Contains(extension->id()));
  99. EXPECT_FALSE(registry->GetExtensionById(
  100. extension->id(),
  101. ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::ENABLED));
  102. }
  103. // Verifies the extension with the given ID is disabled.
  104. void ExpectDisabled(const ExtensionId& extension_id) {
  105. ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
  106. EXPECT_TRUE(registry->disabled_extensions().Contains(extension_id));
  107. EXPECT_FALSE(registry->GetExtensionById(
  108. extension_id,
  109. ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::DISABLED));
  110. }
  111. TestEventRouter* event_router() { return event_router_; }
  112. private:
  113. MockExtensionSystemFactory<TestExtensionSystem> factory_;
  114. raw_ptr<TestEventRouter> event_router_ = nullptr; // Created in SetUp().
  115. };
  116. // Tests with a non-existent directory.
  117. TEST_F(ShellExtensionLoaderTest, NotFound) {
  118. ShellExtensionLoader loader(browser_context());
  119. const Extension* extension =
  120. loader.LoadExtension(GetExtensionPath("nonexistent"));
  121. ASSERT_FALSE(extension);
  122. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  123. }
  124. // Tests loading and reloading an extension.
  125. TEST_F(ShellExtensionLoaderTest, Extension) {
  126. ShellExtensionLoader loader(browser_context());
  127. const Extension* extension =
  128. loader.LoadExtension(GetExtensionPath("extension"));
  129. ExpectEnabled(extension);
  130. // Extensions shouldn't receive the onLaunched event.
  131. EXPECT_EQ(0, event_router()->GetEventCount(OnLaunched::kEventName));
  132. // No keep-alives are used for non-app extensions.
  133. const ExtensionId extension_id = extension->id();
  134. loader.ReloadExtension(extension->id());
  135. ExpectDisabled(extension_id);
  136. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  137. // Wait for load.
  138. content::RunAllTasksUntilIdle();
  139. extension = ExtensionRegistry::Get(browser_context())
  140. ->GetInstalledExtension(extension_id);
  141. ExpectEnabled(extension);
  142. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  143. // Not an app.
  144. EXPECT_EQ(0, event_router()->GetEventCount(OnLaunched::kEventName));
  145. }
  146. // Tests that the extension is added as enabled even if is disabled in
  147. // ExtensionPrefs. Unlike Chrome, AppShell doesn't have a UI surface for
  148. // re-enabling a disabled extension.
  149. TEST_F(ShellExtensionLoaderTest, LoadAfterReloadFailure) {
  150. base::FilePath extension_path = GetExtensionPath("extension");
  151. const ExtensionId extension_id =
  152. crx_file::id_util::GenerateIdForPath(extension_path);
  153. ExtensionPrefs::Get(browser_context())
  154. ->SetExtensionDisabled(extension_id, disable_reason::DISABLE_RELOAD);
  155. ShellExtensionLoader loader(browser_context());
  156. const Extension* extension = loader.LoadExtension(extension_path);
  157. ExpectEnabled(extension);
  158. }
  159. // Tests that the extension is not added if it is disabled in ExtensionPrefs
  160. // for reasons beyond reloading.
  161. TEST_F(ShellExtensionLoaderTest, LoadDisabledExtension) {
  162. base::FilePath extension_path = GetExtensionPath("extension");
  163. const ExtensionId extension_id =
  164. crx_file::id_util::GenerateIdForPath(extension_path);
  165. ExtensionPrefs::Get(browser_context())
  166. ->SetExtensionDisabled(
  167. extension_id,
  168. disable_reason::DISABLE_RELOAD | disable_reason::DISABLE_USER_ACTION);
  169. ShellExtensionLoader loader(browser_context());
  170. const Extension* extension = loader.LoadExtension(extension_path);
  171. ExpectDisabled(extension->id());
  172. }
  173. #if defined(USE_AURA)
  174. class ShellExtensionLoaderTestAura : public ShellExtensionLoaderTest {
  175. public:
  176. ShellExtensionLoaderTestAura(const ShellExtensionLoaderTestAura&) = delete;
  177. ShellExtensionLoaderTestAura& operator=(const ShellExtensionLoaderTestAura&) =
  178. delete;
  179. protected:
  180. ShellExtensionLoaderTestAura() = default;
  181. ~ShellExtensionLoaderTestAura() override = default;
  182. void SetUp() override {
  183. aura_helper_ = std::make_unique<ShellTestHelperAura>();
  184. aura_helper_->SetUp();
  185. std::unique_ptr<TestExtensionsBrowserClient>
  186. test_extensions_browser_client =
  187. std::make_unique<ShellTestExtensionsBrowserClient>();
  188. SetExtensionsBrowserClient(std::move(test_extensions_browser_client));
  189. AppWindowClient::Set(&app_window_client_);
  190. ShellExtensionLoaderTest::SetUp();
  191. }
  192. void TearDown() override {
  193. ShellExtensionLoaderTest::TearDown();
  194. AppWindowClient::Set(nullptr);
  195. aura_helper_->TearDown();
  196. }
  197. // Returns an initialized app window for the extension.
  198. // The app window deletes itself when its native window is closed.
  199. AppWindow* CreateAppWindow(const Extension* extension) {
  200. AppWindow* app_window =
  201. app_window_client_.CreateAppWindow(browser_context(), extension);
  202. aura_helper_->InitAppWindow(app_window);
  203. return app_window;
  204. }
  205. private:
  206. std::unique_ptr<ShellTestHelperAura> aura_helper_;
  207. TestAppWindowClient app_window_client_;
  208. };
  209. // Tests loading and launching a platform app.
  210. TEST_F(ShellExtensionLoaderTestAura, AppLaunch) {
  211. ShellExtensionLoader loader(browser_context());
  212. const Extension* extension =
  213. loader.LoadExtension(GetExtensionPath("platform_app"));
  214. ExpectEnabled(extension);
  215. // A keep-alive is waiting for the app to launch its first window.
  216. // (Not strictly necessary in AppShell, because DesktopWindowControllerAura
  217. // doesn't consider quitting until an already-open window is closed, but
  218. // ShellExtensionLoader and ShellKeepAliveRequester don't make that
  219. // assumption.)
  220. EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  221. {
  222. // When AppShell launches the app window, the keep-alive is removed.
  223. AppWindow* app_window = CreateAppWindow(extension);
  224. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  225. app_window->GetBaseWindow()->Close(); // Deletes |app_window|.
  226. }
  227. }
  228. // Tests loading, launching and reloading a platform app.
  229. TEST_F(ShellExtensionLoaderTestAura, AppLaunchAndReload) {
  230. ShellExtensionLoader loader(browser_context());
  231. const Extension* extension =
  232. loader.LoadExtension(GetExtensionPath("platform_app"));
  233. const ExtensionId extension_id = extension->id();
  234. ExpectEnabled(extension);
  235. EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  236. CreateAppWindow(extension);
  237. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  238. // Reload the app.
  239. loader.ReloadExtension(extension->id());
  240. ExpectDisabled(extension_id);
  241. EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  242. // Complete the reload. ShellExtensionLoader sends the onLaunched event.
  243. content::RunAllTasksUntilIdle();
  244. extension = ExtensionRegistry::Get(browser_context())
  245. ->GetInstalledExtension(extension_id);
  246. ExpectEnabled(extension);
  247. EXPECT_EQ(1, event_router()->GetEventCount(OnLaunched::kEventName));
  248. EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  249. {
  250. // The keep-alive is destroyed when an app window is launched.
  251. AppWindow* app_window = CreateAppWindow(extension);
  252. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  253. app_window->GetBaseWindow()->Close();
  254. }
  255. }
  256. // Tests failing to reload an app.
  257. // TODO(crbug.com/1166675): Flaky on Linux, Lacros, ChromeOS, and similar.
  258. TEST_F(ShellExtensionLoaderTestAura, DISABLED_ReloadFailure) {
  259. ShellExtensionLoader loader(browser_context());
  260. ExtensionId extension_id;
  261. // Create an extension in a temporary directory so we can delete it before
  262. // trying to reload it.
  263. {
  264. TestExtensionDir extension_dir;
  265. extension_dir.WriteManifest(
  266. R"({
  267. "name": "Test Platform App",
  268. "version": "1",
  269. "manifest_version": 2,
  270. "app": {
  271. "background": {
  272. "scripts": ["background.js"]
  273. }
  274. }
  275. })");
  276. extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "");
  277. const Extension* extension =
  278. loader.LoadExtension(extension_dir.UnpackedPath());
  279. ASSERT_TRUE(extension);
  280. extension_id = extension->id();
  281. ExpectEnabled(extension);
  282. EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  283. // Launch an app window.
  284. CreateAppWindow(extension);
  285. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  286. // Reload the app.
  287. loader.ReloadExtension(extension->id());
  288. EXPECT_TRUE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  289. }
  290. // Wait for load (which will fail because the directory is missing).
  291. content::RunAllTasksUntilIdle();
  292. ExpectDisabled(extension_id);
  293. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  294. // We can't reload an extension that is already disabled for reloading, so
  295. // trying to reload this extension shouldn't result in a dangling keep-alive.
  296. loader.ReloadExtension(extension_id);
  297. EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
  298. }
  299. #endif
  300. } // namespace extensions