#include "extensions/common/api/scripts_internal/script_serialization.h"
#include <optional>
#include "base/strings/string_util.h"
#include "base/types/optional_util.h"
#include "extensions/common/api/scripts_internal.h"
#include "extensions/common/user_script.h"
#include "extensions/common/utils/content_script_utils.h"
#include "extensions/common/utils/extension_types_utils.h"
namespace extensions::script_serialization {
std::vector<api::scripts_internal::ScriptSource> GetSourcesFromFileNames(
std::vector<std::string> file_names) {
std::vector<api::scripts_internal::ScriptSource> script_sources;
script_sources.reserve(file_names.size());
for (auto& file : file_names) {
api::scripts_internal::ScriptSource script_source;
script_source.file = std::move(file);
script_sources.push_back(std::move(script_source));
}
return script_sources;
}
api::scripts_internal::SerializedUserScript SerializeUserScript(
const UserScript& user_script) {
api::scripts_internal::SerializedUserScript serialized_script;
serialized_script.all_frames = user_script.match_all_frames();
if (!user_script.css_scripts().empty()) {
serialized_script.css.emplace();
serialized_script.css->reserve(user_script.css_scripts().size());
for (const auto& css_script : user_script.css_scripts()) {
api::scripts_internal::ScriptSource source;
source.file = css_script->relative_path().AsUTF8Unsafe();
serialized_script.css->push_back(std::move(source));
}
}
if (!user_script.exclude_url_patterns().is_empty()) {
serialized_script.exclude_matches.emplace();
serialized_script.exclude_matches->reserve(
user_script.exclude_url_patterns().size());
for (const URLPattern& pattern : user_script.exclude_url_patterns()) {
serialized_script.exclude_matches->push_back(pattern.GetAsString());
}
}
if (!user_script.exclude_globs().empty()) {
serialized_script.exclude_globs.emplace();
serialized_script.exclude_globs->reserve(
user_script.exclude_globs().size());
for (const std::string& exclude_glob : user_script.exclude_globs()) {
serialized_script.exclude_globs->push_back(exclude_glob);
}
}
serialized_script.id = user_script.id();
if (!user_script.globs().empty()) {
serialized_script.include_globs.emplace();
serialized_script.include_globs->reserve(user_script.globs().size());
for (const std::string& glob : user_script.globs()) {
serialized_script.include_globs->push_back(glob);
}
}
if (!user_script.js_scripts().empty()) {
serialized_script.js.emplace();
serialized_script.js->reserve(user_script.js_scripts().size());
for (const auto& js_script : user_script.js_scripts()) {
api::scripts_internal::ScriptSource source;
switch (js_script->source()) {
case UserScript::Content::Source::kFile:
source.file = js_script->relative_path().AsUTF8Unsafe();
break;
case UserScript::Content::Source::kInlineCode:
CHECK_EQ(user_script.GetSource(),
UserScript::Source::kDynamicUserScript);
source.code = js_script->GetContent();
break;
}
serialized_script.js->push_back(std::move(source));
}
}
serialized_script.matches.reserve(user_script.url_patterns().size());
for (const URLPattern& pattern : user_script.url_patterns()) {
serialized_script.matches.push_back(pattern.GetAsString());
}
serialized_script.match_origin_as_fallback =
user_script.match_origin_as_fallback() ==
mojom::MatchOriginAsFallbackBehavior::kAlways;
serialized_script.run_at =
ConvertRunLocationForAPI(user_script.run_location());
auto source_to_serialized_source = [](UserScript::Source source) {
switch (source) {
case UserScript::Source::kDynamicContentScript:
return api::scripts_internal::Source::kDynamicContentScript;
case UserScript::Source::kDynamicUserScript:
return api::scripts_internal::Source::kDynamicUserScript;
case UserScript::Source::kStaticContentScript:
case UserScript::Source::kWebUIScript:
NOTREACHED();
}
};
serialized_script.source =
source_to_serialized_source(user_script.GetSource());
serialized_script.world =
ConvertExecutionWorldForAPI(user_script.execution_world());
serialized_script.world_id = user_script.world_id();
return serialized_script;
}
std::unique_ptr<UserScript> ParseSerializedUserScript(
const api::scripts_internal::SerializedUserScript& serialized_script,
const Extension& extension,
bool allowed_in_incognito,
std::u16string* error_out,
bool* wants_file_access_out,
SerializedUserScriptParseOptions parse_options) {
bool source_matches_id = true;
switch (serialized_script.source) {
case api::scripts_internal::Source::kDynamicContentScript:
source_matches_id = base::StartsWith(
serialized_script.id, UserScript::kDynamicContentScriptPrefix);
break;
case api::scripts_internal::Source::kDynamicUserScript:
source_matches_id = base::StartsWith(
serialized_script.id, UserScript::kDynamicUserScriptPrefix);
break;
case api::scripts_internal::Source::kManifestContentScript:
source_matches_id = base::StartsWith(
serialized_script.id, UserScript::kManifestContentScriptPrefix);
break;
case api::scripts_internal::Source::kNone:
NOTREACHED();
}
if (!source_matches_id) {
return nullptr;
}
auto user_script = std::make_unique<UserScript>();
user_script->set_host_id(
mojom::HostID(mojom::HostID::HostType::kExtensions, extension.id()));
user_script->set_id(serialized_script.id);
std::u16string error;
if (!error_out) {
error_out = &error;
}
if (serialized_script.all_frames) {
user_script->set_match_all_frames(*serialized_script.all_frames);
}
if (!script_parsing::ParseFileSources(
&extension, base::OptionalToPtr(serialized_script.js),
base::OptionalToPtr(serialized_script.css),
parse_options.index_for_error, user_script.get(), error_out)) {
return nullptr;
}
if (!script_parsing::ParseMatchPatterns(
serialized_script.matches,
base::OptionalToPtr(serialized_script.exclude_matches),
extension.creation_flags(),
parse_options.can_execute_script_everywhere,
parse_options.all_urls_includes_chrome_urls,
parse_options.index_for_error, user_script.get(), error_out,
wants_file_access_out)) {
return nullptr;
}
script_parsing::ParseGlobs(
base::OptionalToPtr(serialized_script.include_globs),
base::OptionalToPtr(serialized_script.exclude_globs), user_script.get());
if (serialized_script.match_origin_as_fallback.has_value()) {
user_script->set_match_origin_as_fallback(
*serialized_script.match_origin_as_fallback
? mojom::MatchOriginAsFallbackBehavior::kAlways
: mojom::MatchOriginAsFallbackBehavior::kNever);
}
user_script->set_run_location(ConvertRunLocation(serialized_script.run_at));
user_script->set_execution_world(
ConvertExecutionWorld(serialized_script.world));
user_script->set_world_id(serialized_script.world_id);
if (!script_parsing::ValidateMatchOriginAsFallback(
user_script->match_origin_as_fallback(), user_script->url_patterns(),
error_out)) {
return nullptr;
}
user_script->set_incognito_enabled(allowed_in_incognito);
return user_script;
}
}