1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
// 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/strcat.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/extensions/api/user_scripts/user_scripts_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/common/extension_id.h"
#include "extensions/test/extension_test_message_listener.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/interaction/element_identifier.h"
namespace extensions {
class UserScriptsUITest : public InteractiveBrowserTestT<UserScriptsAPITest> {
public:
// Checks that toggle is `!enabled` and then toggles it to `enabled` state.
auto CheckCurrentToggleStateAndThenToggleItInUI(
ui::ElementIdentifier page_id,
const DeepQuery toggle_dom_path,
bool enabled) {
return Steps(
// ClickElement() won't click something off screen so scroll the toggle
// into view in case it is not.
ScrollIntoView(page_id, toggle_dom_path),
// Check the toggle is `!enabled`.
EnsurePresent(page_id, toggle_dom_path),
CheckJsResultAt(page_id, toggle_dom_path, "(el) => el.checked",
!enabled),
// Click the toggle and check it is `enabled`.
ClickElement(page_id, toggle_dom_path),
CheckJsResultAt(page_id, toggle_dom_path, "(el) => el.checked",
enabled));
}
// Toggles the extensions_features::kUserScriptUserExtensionToggle toggle to
// `enabled` state.
auto TogglePerExtensionToggleInUI(ui::ElementIdentifier page_id,
const ExtensionId& extension_id,
bool enabled) {
const DeepQuery kPathToUserScriptsToggle{
"extensions-manager",
"extensions-detail-view",
"extensions-toggle-row#allow-user-scripts",
"cr-toggle#crToggle",
};
// Enable the per-extension toggle in the UI.
return Steps(
// Navigate to the extensions detail page for the extension (where the
// toggle lives).
NavigateWebContents(page_id,
GURL(base::StrCat({chrome::kChromeUIExtensionsURL,
"?id=", extension_id.c_str()}))),
CheckCurrentToggleStateAndThenToggleItInUI(
page_id, kPathToUserScriptsToggle, enabled));
}
// Toggles the (non extensions_features::kUserScriptUserExtensionToggle state)
// dev mode toggle `enabled` state.
auto ToggleDevModeInUI(ui::ElementIdentifier page_id, bool enabled) {
const DeepQuery kPathToDevModeToggle{
"extensions-manager extensions-toolbar",
"cr-toggle#devMode",
};
// Enable dev mode toggle in the UI.
return Steps(
// Navigate to the extensions detail page for the extension.
NavigateWebContents(page_id, GURL(chrome::kChromeUIExtensionsURL)),
CheckCurrentToggleStateAndThenToggleItInUI(
page_id, kPathToDevModeToggle, enabled));
}
// Checks that the chrome.userScripts API is not available in the background
// script.
auto VerifyUserScriptsIsNotAvailable(const ExtensionId& extension_id) {
// Register the user script in the extension background script and confirm
// it registered successfully.
return CheckResult(
[this, &extension_id]() -> bool {
return BackgroundScriptExecutor::ExecuteScript(
profile(), extension_id, "verifyApiIsNotAvailable();",
BackgroundScriptExecutor::ResultCapture::
kSendScriptResult) == "success";
},
true, "Checking that the userScripts API is not available");
}
// Registers a dynamic user script with the chrome.userScripts API.
auto RegisterUserScript(const ExtensionId& extension_id) {
// Register the user script in the extension background script and confirm
// it registered successfully.
return CheckResult(
[this, &extension_id]() -> bool {
return BackgroundScriptExecutor::ExecuteScript(
profile(), extension_id, "registerUserScripts();",
BackgroundScriptExecutor::ResultCapture::
kSendScriptResult) == "success";
},
true, "Registering dynamic user script and checking that it completed");
}
};
// TODO(crbug.com/416377497): Re-enable the test.
#if BUILDFLAG(IS_MAC)
#define MAYBE_ToggleControls_UserScriptsAPIUsage \
DISABLED_ToggleControls_UserScriptsAPIUsage
#else
#define MAYBE_ToggleControls_UserScriptsAPIUsage \
ToggleControls_UserScriptsAPIUsage
#endif
// Tests the toggling the UI toggle (dependent on feature) controls whether the
// user has allowed userScripts API usage.
IN_PROC_BROWSER_TEST_P(UserScriptsUITest,
MAYBE_ToggleControls_UserScriptsAPIUsage) {
// Load extension that has API permission to use the userScripts API, but not
// the per-extension toggle for userScripts enabled.
ExtensionTestMessageListener extension_background_started_listener =
ExtensionTestMessageListener("started");
const Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("user_scripts/allowed_tests"));
ASSERT_TRUE(extension);
ASSERT_TRUE(extension_background_started_listener.WaitUntilSatisfied());
const DeepQuery kPathToUserScriptInjectedDiv{
"#user-script-code",
};
// This test verifies that:
// 1) The userScripts API is initially unavailable.
// 2) Enabling the toggle allows for a dynamic user script can be registered
// and injected.
// 3) Disabling the toggle causes user scripts to no longer inject
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTab);
RunTestSequence(
InstrumentTab(kTab),
VerifyUserScriptsIsNotAvailable(extension->id()),
// Enable the toggle depending on feature state.
GetParam() ? TogglePerExtensionToggleInUI(kTab, extension->id(),
/*enabled=*/true)
: ToggleDevModeInUI(kTab, /*enabled=*/true),
RegisterUserScript(extension->id()),
// Navigate tab to a webpage where the user script should inject a <div>.
NavigateWebContents(
kTab, embedded_test_server()->GetURL("example.com", "/simple.html")),
// Ensure the user script injected its <div>.
EnsurePresent(kTab, kPathToUserScriptInjectedDiv),
// Disable the toggle depending on feature state.
GetParam() ? TogglePerExtensionToggleInUI(kTab, extension->id(),
/*enabled=*/false)
: ToggleDevModeInUI(kTab, /*enabled=*/false),
// Navigate tab to a webpage where the user script should no longer inject
// a <div>.
NavigateWebContents(
kTab, embedded_test_server()->GetURL("example.com", "/simple.html")),
// Ensure the user script no longer injects its <div>.
EnsureNotPresent(kTab, kPathToUserScriptInjectedDiv));
}
INSTANTIATE_TEST_SUITE_P(PerExtensionToggle,
UserScriptsUITest,
// extensions_features::kUserScriptUserExtensionToggle
testing::Values("true"));
INSTANTIATE_TEST_SUITE_P(DevModeToggle,
UserScriptsUITest,
// extensions_features::kUserScriptUserExtensionToggle
testing::Values("false"));
} // namespace extensions
|