| // Copyright 2025 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/strings/stringprintf.h" | 
 | #include "chrome/browser/extensions/extension_browsertest.h" | 
 | #include "chrome/browser/profiles/profile.h" | 
 | #include "content/public/test/browser_test.h" | 
 | #include "extensions/browser/extension_registry.h" | 
 | #include "extensions/test/extension_test_message_listener.h" | 
 | #include "extensions/test/result_catcher.h" | 
 | #include "extensions/test/test_extension_dir.h" | 
 |  | 
 | namespace extensions { | 
 |  | 
 | using ComponentExtensionBrowserTest = ExtensionBrowserTest; | 
 |  | 
 | #if BUILDFLAG(IS_CHROMEOS) | 
 | // Tests that MojoJS is enabled for component extensions that need it. | 
 | // Note the test currently only runs for ChromeOS because the test extension | 
 | // uses `mojoPrivate` to test and `mojoPrivate` is ChromeOS only. | 
 | IN_PROC_BROWSER_TEST_F(ComponentExtensionBrowserTest, MojoJS) { | 
 |   ResultCatcher result_catcher; | 
 |  | 
 |   auto* extension = | 
 |       LoadExtension(test_data_dir_.AppendASCII("service_worker/mojo"), | 
 |                     {.load_as_component = true}); | 
 |   ASSERT_TRUE(extension); | 
 |  | 
 |   ASSERT_TRUE(result_catcher.GetNextResult()); | 
 | } | 
 | #endif  // BUILDFLAG(IS_CHROMEOS) | 
 |  | 
 | const ExtensionId kExtensionId = "iegclhlplifhodhkoafiokenjoapiobj"; | 
 | constexpr char kExtensionKey[] = | 
 |     "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjzv7dI7Ygyh67VHE1DdidudpYf8P" | 
 |     "Ffv8iucWvzO+3xpF/Dm5xNo7aQhPNiEaNfHwJQ7lsp4gc+C+4bbaVewBFspTruoSJhZc5uEf" | 
 |     "qxwovJwN+v1/SUFXTXQmQBv6gs0qZB4gBbl4caNQBlqrFwAMNisnu1V6UROna8rOJQ90D7Nv" | 
 |     "7TCwoVPKBfVshpFjdDOTeBg4iLctO3S/06QYqaTDrwVceSyHkVkvzBY6tc6mnYX0RZu78J9i" | 
 |     "L8bdqwfllOhs69cqoHHgrLdI6JdOyiuh6pBP6vxMlzSKWJ3YTNjaQTPwfOYaLMuzdl0v+Ydz" | 
 |     "afIzV9zwe4Xiskk+5JNGt8b2rQIDAQAB"; | 
 |  | 
 | // Tests updating a Service Worker-based component extension across a restart. | 
 | // This simulates a browser update where a component extension might change. | 
 | class ComponentExtensionServiceWorkerUpdateBrowserTest | 
 |     : public ComponentExtensionBrowserTest { | 
 |  public: | 
 |   void WriteExtension(TestExtensionDir* dir, int version) { | 
 |     constexpr char kManifestTemplate[] = | 
 |         R"({ | 
 |          "name": "Component SW Update Test", | 
 |          "manifest_version": 3, | 
 |          "version": "%d", | 
 |          "background": {"service_worker": "sw.js"}, | 
 |          "key": "%s" | 
 |        })"; | 
 |     dir->WriteManifest( | 
 |         base::StringPrintf(kManifestTemplate, version, kExtensionKey)); | 
 |  | 
 |     constexpr char kBackgroundScriptTemplate[] = | 
 |         R"(self.version = %d; | 
 |        chrome.test.sendMessage(`v${self.version} ready`);)"; | 
 |     dir->WriteFile(FILE_PATH_LITERAL("sw.js"), | 
 |                    base::StringPrintf(kBackgroundScriptTemplate, version)); | 
 |   } | 
 |  | 
 |   int GetWorkerVersion(const ExtensionId& id) { | 
 |     constexpr char kGetVersionScript[] = | 
 |         "chrome.test.sendScriptResult(self.version);"; | 
 |     base::Value version = ExecuteScriptInBackgroundPage(id, kGetVersionScript); | 
 |     if (!version.is_int()) { | 
 |       ADD_FAILURE() << "Script did not return an integer. Value: " << version; | 
 |       return -1; | 
 |     } | 
 |     return version.GetInt(); | 
 |   } | 
 |  | 
 |   TestExtensionDir test_dir_v1_; | 
 |   TestExtensionDir test_dir_v2_; | 
 | }; | 
 |  | 
 | // PRE_ test: Installs V1 of the component extension. Verifies it runs. | 
 | IN_PROC_BROWSER_TEST_F(ComponentExtensionServiceWorkerUpdateBrowserTest, | 
 |                        PRE_Update) { | 
 |   WriteExtension(&test_dir_v1_, 1); | 
 |  | 
 |   // Load V1 of the component extension. | 
 |   ExtensionTestMessageListener v1_ready("v1 ready"); | 
 |   const Extension* extension_v1 = | 
 |       LoadExtension(test_dir_v1_.UnpackedPath(), {.load_as_component = true}); | 
 |   ASSERT_TRUE(extension_v1); | 
 |  | 
 |   // Ensure it has the correct ID and wait for it to start. | 
 |   const ExtensionId id = extension_v1->id(); | 
 |   ASSERT_EQ(kExtensionId, id); | 
 |   ASSERT_TRUE(v1_ready.WaitUntilSatisfied()); | 
 |  | 
 |   // Check service worker version. | 
 |   EXPECT_EQ("1", extension_v1->version().GetString()); | 
 |   EXPECT_EQ(1, GetWorkerVersion(id)); | 
 | } | 
 |  | 
 | // Main test: Installs V2 of the component extension. Verifies V2 runs. | 
 | IN_PROC_BROWSER_TEST_F(ComponentExtensionServiceWorkerUpdateBrowserTest, | 
 |                        Update) { | 
 |   ASSERT_FALSE( | 
 |       extension_registry()->enabled_extensions().GetByID(kExtensionId)); | 
 |   WriteExtension(&test_dir_v2_, 2); | 
 |  | 
 |   // Load V2 of the component extension. | 
 |   ExtensionTestMessageListener v2_ready("v2 ready"); | 
 |   const Extension* extension_v2 = | 
 |       LoadExtension(test_dir_v2_.UnpackedPath(), {.load_as_component = true}); | 
 |   ASSERT_TRUE(extension_v2); | 
 |  | 
 |   // Ensure it has the correct ID (same as V1) and wait for it to start. | 
 |   const ExtensionId id = extension_v2->id(); | 
 |   EXPECT_EQ(kExtensionId, id); | 
 |   ASSERT_TRUE(v2_ready.WaitUntilSatisfied()); | 
 |  | 
 |   // Check service worker version. | 
 |   EXPECT_EQ("2", extension_v2->version().GetString()); | 
 |   EXPECT_EQ(2, GetWorkerVersion(id)); | 
 | } | 
 |  | 
 | }  // namespace extensions |