| // Copyright 2021 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 "extensions/browser/process_util.h" |
| |
| #include "chrome/browser/extensions/extension_browsertest.h" |
| #include "chrome/browser/extensions/extension_util.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "content/public/test/browser_test.h" |
| #include "extensions/browser/extension_host_test_helper.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/manifest_handlers/incognito_info.h" |
| #include "extensions/common/mojom/view_type.mojom.h" |
| #include "extensions/test/extension_background_page_waiter.h" |
| #include "extensions/test/extension_test_message_listener.h" |
| #include "extensions/test/test_extension_dir.h" |
| |
| namespace extensions { |
| |
| using process_util::GetPersistentBackgroundPageState; |
| using process_util::PersistentBackgroundPageState; |
| |
| class ProcessUtilBrowserTest : public ExtensionBrowserTest { |
| public: |
| ProcessUtilBrowserTest() = default; |
| ~ProcessUtilBrowserTest() override = default; |
| |
| const Extension* EnableInIncognitoAndWaitForBackgroundPage( |
| const Extension& extension) { |
| // Cache the ID, since we'll be invalidated the extension. |
| const ExtensionId extension_id = extension.id(); |
| ExtensionHostTestHelper background_ready(profile(), extension_id); |
| background_ready.RestrictToType(mojom::ViewType::kExtensionBackgroundPage); |
| // Enable the extension in incognito, and wait for it to reload (including |
| // the background page being ready). |
| util::SetIsIncognitoEnabled(extension.id(), profile(), true); |
| background_ready.WaitForDocumentElementAvailable(); |
| // Get the reloaded version of the extension. |
| return extension_registry()->enabled_extensions().GetByID(extension_id); |
| } |
| |
| const Extension* LoadExtensionAndWaitForBackgroundPage( |
| const base::FilePath& file_path) { |
| // LoadExtension() automatically waits for the background page to load. |
| const Extension* extension = LoadExtension(file_path); |
| return extension; |
| } |
| }; |
| |
| // Tests GetPersistentBackgroundPageState() with a spanning-mode |
| // extension (which is the default extension behavior). |
| IN_PROC_BROWSER_TEST_F(ProcessUtilBrowserTest, |
| BackgroundPageLoading_SpanningMode) { |
| constexpr char kManifest[] = |
| R"({ |
| "name": "Test Extension", |
| "version": "0.1", |
| "manifest_version": 2, |
| "background": { |
| "persistent": true, |
| "scripts": ["background.js"] |
| } |
| })"; |
| TestExtensionDir test_dir; |
| test_dir.WriteManifest(kManifest); |
| test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "// Empty"); |
| |
| const Extension* extension = |
| LoadExtensionAndWaitForBackgroundPage(test_dir.UnpackedPath()); |
| EXPECT_FALSE(IncognitoInfo::IsSplitMode(extension)); |
| |
| EXPECT_EQ(PersistentBackgroundPageState::kReady, |
| GetPersistentBackgroundPageState(*extension, profile())); |
| |
| extension = EnableInIncognitoAndWaitForBackgroundPage(*extension); |
| |
| // NOTE: We deliberately use chrome::OpenURLOffTheRecord() here (instead of |
| // InProcessBrowserTest::OpenURLOffTheRecord() or CreateIncognitoBrowser()) |
| // because we need the process of opening to be asynchronous for the next |
| // assertion. |
| chrome::OpenURLOffTheRecord(profile(), GURL("about:blank")); |
| |
| Profile* incognito_profile = |
| profile()->GetPrimaryOTRProfile(/*create_if_needed=*/false); |
| ASSERT_TRUE(incognito_profile); |
| |
| // NOTE: These are actually the same background page (since it runs in |
| // spanning mode), but we check the result for both profiles. Since it refers |
| // to the same page, even though the incognito browser isn't fully ready, the |
| // extension has already loaded. |
| EXPECT_EQ(PersistentBackgroundPageState::kReady, |
| GetPersistentBackgroundPageState(*extension, profile())); |
| EXPECT_EQ(PersistentBackgroundPageState::kReady, |
| GetPersistentBackgroundPageState(*extension, incognito_profile)); |
| } |
| |
| // Tests GetPersistentBackgroundPageState() with a split-mode |
| // extension. |
| IN_PROC_BROWSER_TEST_F(ProcessUtilBrowserTest, |
| BackgroundPageLoading_SplitMode) { |
| constexpr char kManifest[] = |
| R"({ |
| "name": "Test Extension", |
| "version": "0.1", |
| "manifest_version": 2, |
| "background": { |
| "persistent": true, |
| "scripts": ["background.js"] |
| }, |
| "incognito": "split" |
| })"; |
| TestExtensionDir test_dir; |
| test_dir.WriteManifest(kManifest); |
| test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "// Empty"); |
| |
| const Extension* extension = |
| LoadExtensionAndWaitForBackgroundPage(test_dir.UnpackedPath()); |
| EXPECT_TRUE(IncognitoInfo::IsSplitMode(extension)); |
| |
| EXPECT_EQ(PersistentBackgroundPageState::kReady, |
| GetPersistentBackgroundPageState(*extension, profile())); |
| |
| extension = EnableInIncognitoAndWaitForBackgroundPage(*extension); |
| |
| // NOTE: We deliberately use chrome::OpenURLOffTheRecord() here (instead of |
| // InProcessBrowserTest::OpenURLOffTheRecord() or CreateIncognitoBrowser()) |
| // because we need the process of opening to be asynchronous for the next |
| // assertion. |
| chrome::OpenURLOffTheRecord(profile(), GURL("about:blank")); |
| |
| Profile* incognito_profile = |
| profile()->GetPrimaryOTRProfile(/*create_if_needed=*/false); |
| ASSERT_TRUE(incognito_profile); |
| |
| // The on-the-record page should be ready, but not the incognito version |
| // (since it should still be loading). |
| EXPECT_EQ(PersistentBackgroundPageState::kReady, |
| GetPersistentBackgroundPageState(*extension, profile())); |
| EXPECT_EQ(PersistentBackgroundPageState::kNotReady, |
| GetPersistentBackgroundPageState(*extension, incognito_profile)); |
| |
| // Wait for the incognito profile to finish. |
| ExtensionBackgroundPageWaiter(incognito_profile, *extension) |
| .WaitForBackgroundOpen(); |
| // Now, both the incognito and on-the-record pages should should be ready. |
| EXPECT_EQ(PersistentBackgroundPageState::kReady, |
| GetPersistentBackgroundPageState(*extension, profile())); |
| EXPECT_EQ(PersistentBackgroundPageState::kReady, |
| GetPersistentBackgroundPageState(*extension, incognito_profile)); |
| } |
| |
| // Tests that GetPersistentBackgroundPageState() returns kInvalid for |
| // non-persitent extension types. |
| IN_PROC_BROWSER_TEST_F(ProcessUtilBrowserTest, |
| BackgroundPageLoading_NonPersistent) { |
| constexpr char kEventPageManifest[] = |
| R"({ |
| "name": "Test Event Page", |
| "version": "0.1", |
| "manifest_version": 2, |
| "background": { |
| "persistent": false, |
| "scripts": ["background.js"] |
| } |
| })"; |
| TestExtensionDir test_event_page_dir; |
| test_event_page_dir.WriteManifest(kEventPageManifest); |
| test_event_page_dir.WriteFile(FILE_PATH_LITERAL("background.js"), |
| "chrome.test.sendMessage('Event Page Ready');"); |
| |
| // Load the extension and wait for the background context to spin up. Even |
| // though the background has loaded, since it's not a persistent background |
| // page, GetPersistentBackgroundPageState() should return kInvalid. |
| ExtensionTestMessageListener event_page_listener("Event Page Ready"); |
| const Extension* event_page = |
| LoadExtension(test_event_page_dir.UnpackedPath()); |
| ASSERT_TRUE(event_page); |
| ASSERT_TRUE(event_page_listener.WaitUntilSatisfied()); |
| EXPECT_EQ(PersistentBackgroundPageState::kInvalid, |
| GetPersistentBackgroundPageState(*event_page, profile())); |
| |
| // Repeat the test with a SW based extension. |
| constexpr char kServiceWorkerManifest[] = |
| R"({ |
| "name": "Test Service Worker", |
| "version": "0.1", |
| "manifest_version": 3, |
| "background": { |
| "service_worker": "background.js" |
| } |
| })"; |
| TestExtensionDir test_service_worker_dir; |
| test_service_worker_dir.WriteManifest(kServiceWorkerManifest); |
| test_service_worker_dir.WriteFile( |
| FILE_PATH_LITERAL("background.js"), |
| "chrome.test.sendMessage('Service Worker Ready');"); |
| |
| ExtensionTestMessageListener service_worker_listener("Service Worker Ready"); |
| const Extension* service_worker = |
| LoadExtension(test_service_worker_dir.UnpackedPath()); |
| ASSERT_TRUE(service_worker); |
| ASSERT_TRUE(service_worker_listener.WaitUntilSatisfied()); |
| EXPECT_EQ(PersistentBackgroundPageState::kInvalid, |
| GetPersistentBackgroundPageState(*event_page, profile())); |
| } |
| |
| } // namespace extensions |