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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_SCRIPTING_UTILS_H_
#define EXTENSIONS_BROWSER_SCRIPTING_UTILS_H_
#include <string>
#include "base/containers/contains.h"
#include "base/functional/callback_forward.h"
#include "base/strings/utf_string_conversions.h"
#include "extensions/browser/extension_user_script_loader.h"
#include "extensions/browser/script_executor.h"
#include "extensions/common/api/scripts_internal.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/extension_resource.h"
#include "extensions/common/url_pattern_set.h"
#include "extensions/common/user_script.h"
#include "extensions/common/utils/content_script_utils.h"
namespace content {
class BrowserContext;
} // namespace content
namespace extensions::scripting {
// Details specifying the target into which to inject the script.
struct InjectionTarget {
InjectionTarget();
InjectionTarget(InjectionTarget&& other);
~InjectionTarget();
// Whether the script should inject into all frames within the tab.
std::optional<bool> all_frames;
// The IDs of specific documentIds to inject into.
std::optional<std::vector<std::string>> document_ids;
// The IDs of specific frames to inject into.
std::optional<std::vector<int>> frame_ids;
// The ID of the tab into which to inject.
int tab_id;
};
// Details specifying the read file (either CSS or JS) for the script to be
// injected.
struct InjectedFileSource {
InjectedFileSource(std::string file_name, std::unique_ptr<std::string> data);
InjectedFileSource(InjectedFileSource&&);
~InjectedFileSource();
std::string file_name;
std::unique_ptr<std::string> data;
};
using ResourcesLoadedCallback =
base::OnceCallback<void(std::vector<InjectedFileSource>,
std::optional<std::string>)>;
// Appends the prefix corresponding to the dynamic script `source` to
// `script_id`.
std::string AddPrefixToDynamicScriptId(const std::string& script_id,
UserScript::Source source);
// Returns whether the extension provided `script_id` (without an internal
// prefix) is valid. Populates `error` if invalid.
bool IsScriptIdValid(const std::string& script_id, std::string* error);
// Returns whether new scripts added for the extension with the given
// `extension_id` should be allowed in incognito contexts.
bool ScriptsShouldBeAllowedInIncognito(
const ExtensionId& extension_id,
content::BrowserContext* browser_context);
// Returns a set of unique dynamic script IDs (with an added prefix
// corresponding to `source`) for all given `scripts`. If the script is invalid
// or duplicated in `existing_script_ids` or the new ids, populates error and
// returns an empty set.
template <typename Script>
std::set<std::string> CreateDynamicScriptIds(
std::vector<Script>& scripts,
UserScript::Source source,
const std::set<std::string>& existing_script_ids,
std::string* error) {
std::set<std::string> new_script_ids;
for (auto& script : scripts) {
if (!IsScriptIdValid(script.id, error)) {
return std::set<std::string>();
}
std::string new_script_id =
scripting::AddPrefixToDynamicScriptId(script.id, source);
if (base::Contains(existing_script_ids, new_script_id) ||
base::Contains(new_script_ids, new_script_id)) {
*error = ErrorUtils::FormatErrorMessage("Duplicate script ID '*'",
script.id.c_str());
return std::set<std::string>();
}
script.id = new_script_id;
new_script_ids.insert(script.id);
}
return new_script_ids;
}
// Returns a list of UserScript objects for each `scripts_to_update` by
// retrieving the metadata of all loaded scripts with `source` using
// `create_script_callback` and updating them with the given delta using
// `apply_update_callback`. If any of the `scripts_to_update` hasn't been
// previously loaded or parsing fails, populates error and returns nullptr.
template <typename Script>
UserScriptList UpdateScripts(
std::vector<Script>& scripts_to_update,
UserScript::Source source,
ExtensionUserScriptLoader& loader,
base::RepeatingCallback<Script(const UserScript& script)>
create_script_metadata_callback,
base::RepeatingCallback<std::unique_ptr<UserScript>(
Script& new_script,
Script& existent_script,
std::u16string* parse_error)> apply_update_callback,
std::string* error) {
// Retrieve the metadata of all loaded scripts with `source`.
std::map<std::string, Script> loaded_scripts_metadata;
const UserScriptList& dynamic_scripts = loader.GetLoadedDynamicScripts();
for (const std::unique_ptr<UserScript>& script : dynamic_scripts) {
if (script->GetSource() != source) {
continue;
}
Script script_metadata = create_script_metadata_callback.Run(*script);
loaded_scripts_metadata.emplace(script->id(), std::move(script_metadata));
}
// Verify scripts to update have previously been loaded.
for (const auto& script : scripts_to_update) {
if (!loaded_scripts_metadata.contains(script.id)) {
*error = ErrorUtils::FormatErrorMessage(
"Script with ID '*' does not exist or is not fully registered",
UserScript::TrimPrefixFromScriptID(script.id));
return {};
}
}
// Update the scripts.
std::u16string parse_error;
UserScriptList parsed_scripts;
parsed_scripts.reserve(scripts_to_update.size());
for (Script& new_script : scripts_to_update) {
CHECK(base::Contains(loaded_scripts_metadata, new_script.id));
Script& existent_script = loaded_scripts_metadata[new_script.id];
// Note: `new_script` and `existent_script` may be unsafe to use after this.
std::unique_ptr<UserScript> script =
apply_update_callback.Run(new_script, existent_script, &parse_error);
if (!script) {
CHECK(!parse_error.empty());
*error = base::UTF16ToASCII(parse_error);
return {};
}
parsed_scripts.push_back(std::move(script));
}
return parsed_scripts;
}
// Removes all scripts with `ids` of `extension_id`. If `ids` has no value,
// clears all scripts with `source` and `extension_id`. If any of the `ids`
// provided is invalid, populates `error` and returns false. Otherwise, returns
// true and removes the script from the UserScriptLoader invoking
// `remove_callback` on completion.
bool RemoveScripts(
const std::optional<std::vector<std::string>>& ids,
UserScript::Source source,
content::BrowserContext* browser_context,
const ExtensionId& extension_id,
ExtensionUserScriptLoader::DynamicScriptsModifiedCallback remove_callback,
std::string* error);
// Returns the set of URL patterns from persistent dynamic content scripts.
// Patterns are stored in prefs so UserScriptListener can access them
// synchronously as the persistent scripts themselves are stored in a
// StateStore.
URLPatternSet GetPersistentScriptURLPatterns(
content::BrowserContext* browser_context,
const ExtensionId& extension_id);
// Updates the set of URL patterns from persistent dynamic content scripts. This
// preference gets cleared on extension update.
void SetPersistentScriptURLPatterns(content::BrowserContext* browser_context,
const ExtensionId& extension_id,
const URLPatternSet& patterns);
// Clears the set of URL patterns from persistent dynamic content scripts.
void ClearPersistentScriptURLPatterns(content::BrowserContext* browser_context,
const ExtensionId& extension_id);
// Holds a list of user scripts as the first item, or an error string as the
// second item when the user scripts are invalid.
using ValidateScriptsResult =
std::pair<UserScriptList, std::optional<std::string>>;
// Validates that `scripts` resources exist and are properly encoded.
ValidateScriptsResult ValidateParsedScriptsOnFileThread(
ExtensionResource::SymlinkPolicy symlink_policy,
UserScriptList scripts);
// Returns whether the `target` can be accessed with the given `permissions`.
// If the target can be accessed, populates `script_executor_out`,
// `frame_scope_out`, and `frame_ids_out` with the appropriate values;
// if the target cannot be accessed, populates `error_out`.
bool CanAccessTarget(const PermissionsData& permissions,
const scripting::InjectionTarget& target,
content::BrowserContext* browser_context,
bool include_incognito_information,
ScriptExecutor** script_executor_out,
ScriptExecutor::FrameScope* frame_scope_out,
std::set<int>* frame_ids_out,
std::string* error_out);
// Checks the specified `files` for validity, and attempts to load and localize
// them, invoking `callback` with the result. Returns true on success; on
// failure, populates `error_out`.
bool CheckAndLoadFiles(std::vector<std::string> files,
script_parsing::ContentScriptType resources_type,
const Extension& extension,
bool requires_localization,
ResourcesLoadedCallback callback,
std::string* error_out);
// Checks `files` and populates `resources_out` with the appropriate extension
// resource. Returns true on success; on failure, populates `error_out`.
bool GetFileResources(const std::vector<std::string>& files,
script_parsing::ContentScriptType resources_type,
const Extension& extension,
std::vector<ExtensionResource>* resources_out,
std::string* error_out);
// Executes script with `sources` in the frames identified by `frame_ids`
void ExecuteScript(const ExtensionId& extension_id,
std::vector<mojom::JSSourcePtr> sources,
mojom::ExecutionWorld execution_world,
const std::optional<std::string>& world_id,
ScriptExecutor* script_executor,
ScriptExecutor::FrameScope frame_scope,
std::set<int> frame_ids,
bool inject_immediately,
bool user_gesture,
ScriptExecutor::ScriptFinishedCallback callback);
} // namespace extensions::scripting
#endif // EXTENSIONS_BROWSER_SCRIPTING_UTILS_H_
|