| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string_view> |
| |
| #include "base/strings/stringprintf.h" |
| #include "base/test/values_test_util.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/extensions/extension_action_test_helper.h" |
| #include "components/version_info/channel.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test.h" |
| #include "extensions/browser/background_script_executor.h" |
| #include "extensions/browser/extension_api_frame_id_map.h" |
| #include "extensions/browser/extension_host.h" |
| #include "extensions/browser/extension_host_test_helper.h" |
| #include "extensions/browser/script_executor.h" |
| #include "extensions/test/test_extension_dir.h" |
| |
| namespace extensions { |
| |
| // A test suite for `runtime.getContexts()` tests that need to be run as |
| // interactive UI tests (e.g. due to requiring focus). |
| class RuntimeGetContextsInteractiveApiTest : public ExtensionApiTest { |
| public: |
| RuntimeGetContextsInteractiveApiTest() = default; |
| RuntimeGetContextsInteractiveApiTest( |
| const RuntimeGetContextsInteractiveApiTest&) = delete; |
| RuntimeGetContextsInteractiveApiTest& operator=( |
| const RuntimeGetContextsInteractiveApiTest&) = delete; |
| ~RuntimeGetContextsInteractiveApiTest() override = default; |
| |
| // Runs `chrome.runtime.getContexts()` and returns the result as a |
| // base::Value. |
| base::Value GetContexts(const Extension& extension, std::string_view filter) { |
| static constexpr char kScriptTemplate[] = |
| R"((async () => { |
| chrome.test.sendScriptResult( |
| await chrome.runtime.getContexts(%s)); |
| })();)"; |
| std::string script = base::StringPrintf(kScriptTemplate, filter.data()); |
| return BackgroundScriptExecutor::ExecuteScript( |
| profile(), extension.id(), script, |
| BackgroundScriptExecutor::ResultCapture::kSendScriptResult); |
| } |
| }; |
| |
| // Tests retrieving popup contexts using `chrome.runtime.getContexts()`. |
| // This needs to run as an interactive ui test because extension popups are |
| // closed on focus loss. |
| IN_PROC_BROWSER_TEST_F(RuntimeGetContextsInteractiveApiTest, GetPopupContext) { |
| static constexpr char kManifest[] = |
| R"({ |
| "name": "Get Contexts", |
| "version": "0.1", |
| "manifest_version": 3, |
| "background": { |
| "service_worker": "background.js" |
| }, |
| "action": { "default_popup": "popup.html" } |
| })"; |
| TestExtensionDir test_dir; |
| test_dir.WriteManifest(kManifest); |
| test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), |
| "// Intentionally blank"); |
| test_dir.WriteFile(FILE_PATH_LITERAL("popup.html"), |
| "<html>I'm a popup!</html>"); |
| const Extension* extension = LoadExtension(test_dir.UnpackedPath()); |
| ASSERT_TRUE(extension); |
| |
| std::unique_ptr<ExtensionActionTestHelper> toolbar_helper = |
| ExtensionActionTestHelper::Create(browser()); |
| ExtensionHostTestHelper popup_waiter(profile(), extension->id()); |
| popup_waiter.RestrictToType(mojom::ViewType::kExtensionPopup); |
| toolbar_helper->Press(extension->id()); |
| ExtensionHost* popup_host = popup_waiter.WaitForHostCompletedFirstLoad(); |
| |
| content::RenderFrameHost* popup_frame = |
| popup_host->web_contents()->GetPrimaryMainFrame(); |
| int expected_frame_id = ExtensionApiFrameIdMap::GetFrameId(popup_frame); |
| std::string expected_context_id = |
| ExtensionApiFrameIdMap::GetContextId(popup_frame).AsLowercaseString(); |
| std::string expected_document_id = |
| ExtensionApiFrameIdMap::GetDocumentId(popup_frame).ToString(); |
| std::string expected_frame_url = |
| extension->GetResourceURL("popup.html").spec(); |
| std::string expected_origin = extension->origin().Serialize(); |
| |
| // Query for popup-based contexts. There should only be one. |
| base::Value background_contexts = |
| GetContexts(*extension, R"({"contextTypes": ["POPUP"]})"); |
| |
| // Verify the properties of the returned context. |
| // NOTE: Currently, extension popups are considered to have a window ID of -1. |
| // This makes sense (they aren't really "in" a window), but there's also a |
| // good argument for using the window ID of the Browser they're anchored to. |
| // We may want to revisit this in the future. |
| static constexpr char kExpectedTemplate[] = |
| R"([{ |
| "contextType": "POPUP", |
| "contextId": "%s", |
| "tabId": -1, |
| "windowId": -1, |
| "frameId": %d, |
| "documentId": "%s", |
| "documentUrl": "%s", |
| "documentOrigin": "%s", |
| "incognito": false |
| }])"; |
| std::string expected = |
| base::StringPrintf(kExpectedTemplate, expected_context_id.c_str(), |
| expected_frame_id, expected_document_id.c_str(), |
| expected_frame_url.c_str(), expected_origin.c_str()); |
| EXPECT_THAT(background_contexts, base::test::IsJson(expected)); |
| } |
| |
| } // namespace extensions |