From 6d13c503c52b87d13d956f868478b453aa767933 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Sun, 11 Aug 2024 21:31:37 +0200 Subject: [PATCH] refactor: update chrome.scripting extensions api impls (#43205) --- .../extensions/api/scripting/scripting_api.cc | 363 ++++++++---------- .../extensions/api/scripting/scripting_api.h | 9 + shell/common/extensions/api/scripting.idl | 57 ++- 3 files changed, 200 insertions(+), 229 deletions(-) diff --git a/shell/browser/extensions/api/scripting/scripting_api.cc b/shell/browser/extensions/api/scripting/scripting_api.cc index a5977a70de39..8764b610af11 100644 --- a/shell/browser/extensions/api/scripting/scripting_api.cc +++ b/shell/browser/extensions/api/scripting/scripting_api.cc @@ -38,9 +38,9 @@ #include "extensions/common/mojom/execution_world.mojom-shared.h" #include "extensions/common/mojom/host_id.mojom.h" #include "extensions/common/mojom/run_location.mojom-shared.h" -#include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/script_constants.h" +#include "extensions/common/user_script.h" #include "extensions/common/utils/content_script_utils.h" #include "extensions/common/utils/extension_types_utils.h" #include "shell/browser/api/electron_api_web_contents.h" @@ -52,9 +52,10 @@ namespace { constexpr char kCouldNotLoadFileError[] = "Could not load file: '*'."; constexpr char kDuplicateFileSpecifiedError[] = "Duplicate file specified: '*'."; +constexpr char kEmptyMatchesError[] = + "Script with ID '*' must specify 'matches'."; constexpr char kExactlyOneOfCssAndFilesError[] = "Exactly one of 'css' and 'files' must be specified."; -constexpr char kNonExistentScriptIdError[] = "Nonexistent script ID '*'"; // Note: CSS always injects as soon as possible, so we default to // document_start. Because of tab loading, there's no guarantee this will @@ -483,6 +484,7 @@ ConvertRegisteredContentScriptToSerializedUserScript( std::unique_ptr ParseUserScript( content::BrowserContext* browser_context, const Extension& extension, + bool allowed_in_incognito, api::scripting::RegisteredContentScript content_script, std::u16string* error) { api::scripts_internal::SerializedUserScript serialized_script = @@ -490,23 +492,12 @@ std::unique_ptr ParseUserScript( std::move(content_script)); std::unique_ptr user_script = - script_serialization::ParseSerializedUserScript(serialized_script, - extension, error); + script_serialization::ParseSerializedUserScript( + serialized_script, extension, allowed_in_incognito, error); if (!user_script) { return nullptr; // Parsing failed. } - // Post conversion validation and values. - // TODO(https://crbug.com/1494155): See which of these can be moved into - // script_serialization::ParseSerializedUserScript(). - if (!script_parsing::ValidateMatchOriginAsFallback( - user_script->match_origin_as_fallback(), user_script->url_patterns(), - error)) { - return nullptr; - } - - user_script->set_incognito_enabled( - util::IsIncognitoEnabled(extension.id(), browser_context)); return user_script; } @@ -515,6 +506,7 @@ std::unique_ptr ParseUserScript( api::scripting::RegisteredContentScript CreateRegisteredContentScriptInfo( const UserScript& script) { CHECK_EQ(UserScript::Source::kDynamicContentScript, script.GetSource()); + // To convert a `UserScript`, we first go through our script_internal // serialization; this allows us to do simple conversions and avoid any // complex logic. @@ -631,11 +623,10 @@ ExtensionFunction::ResponseAction ScriptingExecuteScriptFunction::Run() { std::vector string_args; string_args.reserve(injection_.args->size()); for (const auto& arg : *injection_.args) { - if (auto json = base::WriteJson(arg)) { - string_args.push_back(std::move(*json)); - } else { + std::string json; + if (!base::JSONWriter::Write(arg, &json)) return RespondNow(Error("Unserializable argument passed.")); - } + string_args.push_back(std::move(json)); } args_expression = base::JoinString(string_args, ","); } @@ -929,121 +920,78 @@ ScriptingRegisterContentScriptsFunction:: ScriptingRegisterContentScriptsFunction:: ~ScriptingRegisterContentScriptsFunction() = default; -ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() { - std::optional params = - api::scripting::UpdateContentScripts::Params::Create(args()); +ExtensionFunction::ResponseAction +ScriptingRegisterContentScriptsFunction::Run() { + std::optional params = + api::scripting::RegisterContentScripts::Params::Create(args()); EXTENSION_FUNCTION_VALIDATE(params); std::vector& scripts = params->scripts; - std::string error; - - // Add the prefix for dynamic content scripts onto the IDs of all scripts in - // `scripts` before continuing. - std::set ids_to_update = scripting::CreateDynamicScriptIds( - scripts, UserScript::Source::kDynamicContentScript, - std::set(), &error); - - if (!error.empty()) { - CHECK(ids_to_update.empty()); - return RespondNow(Error(std::move(error))); - } - ExtensionUserScriptLoader* loader = ExtensionSystem::Get(browser_context()) ->user_script_manager() ->GetUserScriptLoaderForExtension(extension()->id()); - std::map - loaded_scripts_metadata; - const UserScriptList& dynamic_scripts = loader->GetLoadedDynamicScripts(); - for (const std::unique_ptr& script : dynamic_scripts) { - if (script->GetSource() == UserScript::Source::kDynamicContentScript) { - loaded_scripts_metadata.emplace( - script->id(), CreateRegisteredContentScriptInfo(*script)); - } - } - - for (const auto& script : scripts) { - std::string error_script_id = UserScript::TrimPrefixFromScriptID(script.id); - if (loaded_scripts_metadata.find(script.id) == - loaded_scripts_metadata.end()) { - return RespondNow( - Error(base::StringPrintf("Script with ID '%s' does not exist " - "or is not fully registered", - error_script_id.c_str()))); - } + // Create script ids for dynamic content scripts. + std::string error; + std::set existing_script_ids = + loader->GetDynamicScriptIDs(UserScript::Source::kDynamicContentScript); + std::set new_script_ids = scripting::CreateDynamicScriptIds( + scripts, UserScript::Source::kDynamicContentScript, existing_script_ids, + &error); + + if (!error.empty()) { + CHECK(new_script_ids.empty()); + return RespondNow(Error(std::move(error))); } + // Parse content scripts. std::u16string parse_error; UserScriptList parsed_scripts; - std::set updated_script_ids_to_persist; - std::set persistent_script_ids = - loader->GetPersistentDynamicScriptIDs(); + std::set persistent_script_ids; + + bool allowed_in_incognito = scripting::ScriptsShouldBeAllowedInIncognito( + extension()->id(), browser_context()); parsed_scripts.reserve(scripts.size()); - for (size_t i = 0; i < scripts.size(); ++i) { - api::scripting::RegisteredContentScript& update_delta = scripts[i]; - DCHECK(base::Contains(loaded_scripts_metadata, update_delta.id)); - - api::scripting::RegisteredContentScript& updated_script = - loaded_scripts_metadata[update_delta.id]; - - if (update_delta.matches) - updated_script.matches = std::move(update_delta.matches); - - if (update_delta.exclude_matches) - updated_script.exclude_matches = std::move(update_delta.exclude_matches); - - if (update_delta.js) - updated_script.js = std::move(update_delta.js); - - if (update_delta.css) - updated_script.css = std::move(update_delta.css); - - if (update_delta.all_frames) - *updated_script.all_frames = *update_delta.all_frames; - - if (update_delta.match_origin_as_fallback) { - *updated_script.match_origin_as_fallback = - *update_delta.match_origin_as_fallback; + for (auto& script : scripts) { + if (!script.matches) { + return RespondNow(Error(ErrorUtils::FormatErrorMessage( + kEmptyMatchesError, UserScript::TrimPrefixFromScriptID(script.id)))); } - if (update_delta.run_at != api::extension_types::RunAt::kNone) { - updated_script.run_at = update_delta.run_at; - } + // Scripts will persist across sessions by default. + bool persist_across_sessions = + script.persist_across_sessions.value_or(true); - // Parse/Create user script. std::unique_ptr user_script = - ParseUserScript(browser_context(), *extension(), - std::move(updated_script), &parse_error); - if (!user_script) + ParseUserScript(browser_context(), *extension(), allowed_in_incognito, + std::move(script), &parse_error); + if (!user_script) { return RespondNow(Error(base::UTF16ToASCII(parse_error))); - - // Persist the updated script if the flag is specified as true, or if the - // original script is persisted and the flag is not specified. - if ((update_delta.persist_across_sessions && - *update_delta.persist_across_sessions) || - (!update_delta.persist_across_sessions && - base::Contains(persistent_script_ids, update_delta.id))) { - updated_script_ids_to_persist.insert(update_delta.id); } + if (persist_across_sessions) { + persistent_script_ids.insert(user_script->id()); + } parsed_scripts.push_back(std::move(user_script)); } + // The contents of `scripts` have all been std::move()'d. + scripts.clear(); // Add new script IDs now in case another call with the same script IDs is // made immediately following this one. - loader->AddPendingDynamicScriptIDs(std::move(ids_to_update)); + loader->AddPendingDynamicScriptIDs(std::move(new_script_ids)); GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&scripting::ValidateParsedScriptsOnFileThread, script_parsing::GetSymlinkPolicy(extension()), std::move(parsed_scripts)), - base::BindOnce( - &ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated, - this, std::move(updated_script_ids_to_persist))); + base::BindOnce(&ScriptingRegisterContentScriptsFunction:: + OnContentScriptFilesValidated, + this, std::move(persistent_script_ids))); // Balanced in `OnContentScriptFilesValidated()` or // `OnContentScriptsRegistered()`. @@ -1141,12 +1089,15 @@ ScriptingGetRegisteredContentScriptsFunction::Run() { if (script->GetSource() != UserScript::Source::kDynamicContentScript) { continue; } + if (!id_filter.empty() && !base::Contains(id_filter, script->id())) { continue; } + auto registered_script = CreateRegisteredContentScriptInfo(*script); registered_script.persist_across_sessions = base::Contains(persistent_script_ids, script->id()); + // Remove the internally used prefix from the `script`'s ID before // returning. registered_script.id = script->GetIDWithoutPrefix(); @@ -1170,49 +1121,27 @@ ScriptingUnregisterContentScriptsFunction::Run() { EXTENSION_FUNCTION_VALIDATE(params); std::optional& filter = params->filter; - ExtensionUserScriptLoader* loader = - ExtensionSystem::Get(browser_context()) - ->user_script_manager() - ->GetUserScriptLoaderForExtension(extension()->id()); - - // TODO(crbug.com/1300657): Only clear all scripts if `filter` did not specify - // the list of scripts ids to remove. - if (!filter || !filter->ids || filter->ids->empty()) { - loader->ClearDynamicScripts( - UserScript::Source::kDynamicContentScript, - base::BindOnce(&ScriptingUnregisterContentScriptsFunction:: - OnContentScriptsUnregistered, - this)); - return RespondLater(); + std::optional> ids = std::nullopt; + // TODO(crbug.com/40216362): `ids` should have an empty list when filter ids + // is empty, instead of a nullopt. Otherwise, we are incorrectly removing all + // content scripts when ids is empty. + if (filter && filter->ids && !filter->ids->empty()) { + ids = std::move(filter->ids); } - std::set ids_to_remove; - std::set existing_script_ids = - loader->GetDynamicScriptIDs(UserScript::Source::kDynamicContentScript); - std::string error; - for (const auto& provided_id : *filter->ids) { - if (!scripting::IsScriptIdValid(provided_id, &error)) { - return RespondNow(Error(std::move(error))); - } - - // Add the dynamic content script prefix to `provided_id` before checking - // against `existing_script_ids`. - std::string id_with_prefix = scripting::AddPrefixToDynamicScriptId( - provided_id, UserScript::Source::kDynamicContentScript); - if (!base::Contains(existing_script_ids, id_with_prefix)) { - return RespondNow(Error(ErrorUtils::FormatErrorMessage( - kNonExistentScriptIdError, provided_id.c_str()))); - } - - ids_to_remove.insert(id_with_prefix); - } - - loader->RemoveDynamicScripts( - std::move(ids_to_remove), + bool removal_triggered = scripting::RemoveScripts( + ids, UserScript::Source::kDynamicContentScript, browser_context(), + extension()->id(), base::BindOnce(&ScriptingUnregisterContentScriptsFunction:: OnContentScriptsUnregistered, - this)); + this), + &error); + + if (!removal_triggered) { + CHECK(!error.empty()); + return RespondNow(Error(std::move(error))); + } return RespondLater(); } @@ -1230,76 +1159,56 @@ ScriptingUpdateContentScriptsFunction::ScriptingUpdateContentScriptsFunction() = ScriptingUpdateContentScriptsFunction:: ~ScriptingUpdateContentScriptsFunction() = default; -ExtensionFunction::ResponseAction -ScriptingRegisterContentScriptsFunction::Run() { - std::optional params = - api::scripting::RegisterContentScripts::Params::Create(args()); +ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() { + std::optional params = + api::scripting::UpdateContentScripts::Params::Create(args()); EXTENSION_FUNCTION_VALIDATE(params); - std::vector& scripts = + std::vector& scripts_to_update = params->scripts; + std::string error; + + // Add the prefix for dynamic content scripts onto the IDs of all + // `scripts_to_update` before continuing. + std::set ids_to_update = scripting::CreateDynamicScriptIds( + scripts_to_update, UserScript::Source::kDynamicContentScript, + std::set(), &error); + + if (!error.empty()) { + CHECK(ids_to_update.empty()); + return RespondNow(Error(std::move(error))); + } + ExtensionUserScriptLoader* loader = ExtensionSystem::Get(browser_context()) ->user_script_manager() ->GetUserScriptLoaderForExtension(extension()->id()); - // Create script ids for dynamic content scripts. - std::string error; - std::set existing_script_ids = - loader->GetDynamicScriptIDs(UserScript::Source::kDynamicContentScript); - std::set new_script_ids = scripting::CreateDynamicScriptIds( - scripts, UserScript::Source::kDynamicContentScript, existing_script_ids, + std::set updated_script_ids_to_persist; + UserScriptList parsed_scripts = scripting::UpdateScripts( + scripts_to_update, UserScript::Source::kDynamicContentScript, *loader, + base::BindRepeating(&CreateRegisteredContentScriptInfo), + base::BindRepeating(&ScriptingUpdateContentScriptsFunction::ApplyUpdate, + this, &updated_script_ids_to_persist), &error); if (!error.empty()) { - CHECK(new_script_ids.empty()); + CHECK(parsed_scripts.empty()); return RespondNow(Error(std::move(error))); } - // Parse content scripts. - std::u16string parse_error; - UserScriptList parsed_scripts; - std::set persistent_script_ids; - - parsed_scripts.reserve(scripts.size()); - for (auto& script : scripts) { - if (!script.matches) { - std::string error_script_id = - UserScript::TrimPrefixFromScriptID(script.id); - return RespondNow( - Error(base::StringPrintf("Script with ID '%s' must specify 'matches'", - error_script_id.c_str()))); - } - - // Scripts will persist across sessions by default. - bool persist_across_sessions = - script.persist_across_sessions.value_or(true); - std::unique_ptr user_script = ParseUserScript( - browser_context(), *extension(), std::move(script), &parse_error); - if (!user_script) - return RespondNow(Error(base::UTF16ToASCII(parse_error))); - - // Scripts will persist across sessions by default. - if (persist_across_sessions) { - persistent_script_ids.insert(user_script->id()); - } - parsed_scripts.push_back(std::move(user_script)); - } - // The contents of `scripts` have all been std::move()'d. - scripts.clear(); - // Add new script IDs now in case another call with the same script IDs is // made immediately following this one. - loader->AddPendingDynamicScriptIDs(std::move(new_script_ids)); + loader->AddPendingDynamicScriptIDs(std::move(ids_to_update)); GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&scripting::ValidateParsedScriptsOnFileThread, script_parsing::GetSymlinkPolicy(extension()), std::move(parsed_scripts)), - base::BindOnce(&ScriptingRegisterContentScriptsFunction:: - OnContentScriptFilesValidated, - this, std::move(persistent_script_ids))); + base::BindOnce( + &ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated, + this, std::move(updated_script_ids_to_persist))); // Balanced in `OnContentScriptFilesValidated()` or // `OnContentScriptsRegistered()`. @@ -1307,6 +1216,63 @@ ScriptingRegisterContentScriptsFunction::Run() { return RespondLater(); } +std::unique_ptr ScriptingUpdateContentScriptsFunction::ApplyUpdate( + std::set* script_ids_to_persist, + api::scripting::RegisteredContentScript& new_script, + api::scripting::RegisteredContentScript& original_script, + std::u16string* parse_error) { + if (new_script.matches) { + original_script.matches = std::move(new_script.matches); + } + + if (new_script.exclude_matches) { + original_script.exclude_matches = std::move(new_script.exclude_matches); + } + + if (new_script.js) { + original_script.js = std::move(new_script.js); + } + + if (new_script.css) { + original_script.css = std::move(new_script.css); + } + + if (new_script.all_frames) { + *original_script.all_frames = *new_script.all_frames; + } + + if (new_script.match_origin_as_fallback) { + *original_script.match_origin_as_fallback = + *new_script.match_origin_as_fallback; + } + + if (new_script.run_at != api::extension_types::RunAt::kNone) { + original_script.run_at = new_script.run_at; + } + + // Note: for the update application, we disregard allowed_in_incognito. + // We'll set it on the resulting scripts. + constexpr bool kAllowedInIncognito = false; + + // Parse content script. + std::unique_ptr parsed_script = + ParseUserScript(browser_context(), *extension(), kAllowedInIncognito, + std::move(original_script), parse_error); + if (!parsed_script) { + return nullptr; + } + + // Persist the updated script if the flag is specified as true, or if the + // original script is persisted and the flag is not specified. + if (new_script.persist_across_sessions.value_or(false) || + (!new_script.persist_across_sessions && + base::Contains(*script_ids_to_persist, new_script.id))) { + script_ids_to_persist->insert(new_script.id); + } + + return parsed_script; +} + void ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated( std::set persistent_script_ids, scripting::ValidateScriptsResult result) { @@ -1335,10 +1301,16 @@ void ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated( ->user_script_manager() ->GetUserScriptLoaderForExtension(extension()->id()); + bool allowed_in_incognito = scripting::ScriptsShouldBeAllowedInIncognito( + extension()->id(), browser_context()); + std::set script_ids; - for (const auto& script : scripts) + for (const auto& script : scripts) { script_ids.insert(script->id()); + script->set_incognito_enabled(allowed_in_incognito); + } + if (error.has_value()) { loader->RemovePendingDynamicScriptIDs(script_ids); Respond(Error(std::move(*error))); @@ -1346,18 +1318,9 @@ void ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated( return; } - // To guarantee that scripts are updated, they need to be removed then added - // again. It should be guaranteed that the new scripts are added after the old - // ones are removed. - loader->RemoveDynamicScripts(script_ids, /*callback=*/base::DoNothing()); - - // Since RemoveDynamicScripts will remove pending script IDs, but - // AddDynamicScripts will only add scripts that are marked as pending, we must - // mark `script_ids` as pending again here. - loader->AddPendingDynamicScriptIDs(std::move(script_ids)); - - loader->AddDynamicScripts( - std::move(scripts), std::move(persistent_script_ids), + loader->UpdateDynamicScripts( + std::move(scripts), std::move(script_ids), + std::move(persistent_script_ids), base::BindOnce( &ScriptingUpdateContentScriptsFunction::OnContentScriptsUpdated, this)); diff --git a/shell/browser/extensions/api/scripting/scripting_api.h b/shell/browser/extensions/api/scripting/scripting_api.h index c008606c45ee..3b53f5e2e92c 100644 --- a/shell/browser/extensions/api/scripting/scripting_api.h +++ b/shell/browser/extensions/api/scripting/scripting_api.h @@ -191,6 +191,15 @@ class ScriptingUpdateContentScriptsFunction : public ExtensionFunction { private: ~ScriptingUpdateContentScriptsFunction() override; + // Returns a UserScript object by updating the `original_script` with the + // `new_script` given delta. If the updated script cannot be parsed, populates + // `parse_error` and returns nullptr. + std::unique_ptr ApplyUpdate( + std::set* script_ids_to_persist, + api::scripting::RegisteredContentScript& new_script, + api::scripting::RegisteredContentScript& original_script, + std::u16string* parse_error); + // Called when script files have been checked. void OnContentScriptFilesValidated( std::set persistent_script_ids, diff --git a/shell/common/extensions/api/scripting.idl b/shell/common/extensions/api/scripting.idl index be72aa7bae6d..f029ede3ffbf 100644 --- a/shell/common/extensions/api/scripting.idl +++ b/shell/common/extensions/api/scripting.idl @@ -4,7 +4,6 @@ // Use the chrome.scripting API to execute script in different // contexts. -[modernised_enums] namespace scripting { callback InjectedFunction = void(); @@ -49,11 +48,11 @@ namespace scripting { // A JavaScript function to inject. This function will be serialized, and // then deserialized for injection. This means that any bound parameters // and execution context will be lost. - // Exactly one of files and func must be + // Exactly one of files or func must be // specified. [serializableFunction]InjectedFunction? func; - // The arguments to curry into a provided function. This is only valid if + // The arguments to pass to the provided function. This is only valid if // the func parameter is specified. These arguments must be // JSON-serializable. any[]? args; @@ -67,7 +66,7 @@ namespace scripting { // The path of the JS or CSS files to inject, relative to the extension's // root directory. - // Exactly one of files and func must be + // Exactly one of files or func must be // specified. DOMString[]? files; @@ -122,13 +121,13 @@ namespace scripting { // with a '_' as it's reserved as a prefix for generated script IDs. DOMString id; // Specifies which pages this content script will be injected into. See - // Match Patterns for more details on the - // syntax of these strings. Must be specified for + // Match Patterns for more + // details on the syntax of these strings. Must be specified for // $(ref:registerContentScripts). DOMString[]? matches; // Excludes pages that this content script would otherwise be injected into. - // See Match Patterns for more details on the - // syntax of these strings. + // See Match Patterns for + // more details on the syntax of these strings. DOMString[]? excludeMatches; // The list of CSS files to be injected into matching pages. These are // injected in the order they appear in this array, before any DOM is @@ -143,15 +142,13 @@ namespace scripting { // requirements are not met. Defaults to false, meaning that only the top // frame is matched. boolean? allFrames; - // Whether the script should inject into any frames where the URL belongs to - // a scheme that would never match a specified Match Pattern, including - // about:, data:, blob:, and filesystem: schemes. In these cases, in order - // to determine if the script should inject, the origin of the URL is - // checked. If the origin is `null` (as is the case for data: URLs), then - // the "initiator" or "creator" origin is used (i.e., the origin of the - // frame that created or navigated this frame). Note that this may not - // be the parent frame, if the frame was navigated by another frame in the - // document hierarchy. + // Indicates whether the script can be injected into frames where the URL + // contains an unsupported scheme; specifically: about:, data:, blob:, or + // filesystem:. In these cases, the URL's origin is checked to determine if + // the script should be injected. If the origin is `null` (as is the case + // for data: URLs) then the used origin is either the frame that created + // the current frame or the frame that initiated the navigation to this + // frame. Note that this may not be the parent frame. boolean? matchOriginAsFallback; // Specifies when JavaScript files are injected into the web page. The // preferred and default value is document_idle. @@ -190,20 +187,22 @@ namespace scripting { // and modify as a JS object. One instance exists per frame and is shared // between all content scripts for a given extension. This object is // initialized when the frame is created, before document_start. - // TODO(crbug.com/1054624): Enable this once implementation is complete. + // TODO(crbug.com/40119604): Enable this once implementation is complete. [nodoc, nocompile] static long globalParams(); }; interface Functions { - // Injects a script into a target context. The script will be run at - // document_idle. If the script evaluates to a promise, - // the browser will wait for the promise to settle and return the - // resulting value. + // Injects a script into a target context. By default, the script will be run + // at document_idle, or immediately if the page has already + // loaded. If the injectImmediately property is set, the script + // will inject without waiting, even if the page has not finished loading. If + // the script evaluates to a promise, the browser will wait for the promise to + // settle and return the resulting value. // |injection|: The details of the script which to inject. // |callback|: Invoked upon completion of the injection. The resulting // array contains the result of execution for each frame where the // injection succeeded. - [supportsPromises] static void executeScript( + static void executeScript( ScriptInjection injection, optional ScriptInjectionCallback callback); @@ -211,7 +210,7 @@ namespace scripting { // If multiple frames are specified, unsuccessful injections are ignored. // |injection|: The details of the styles to insert. // |callback|: Invoked upon completion of the insertion. - [supportsPromises] static void insertCSS( + static void insertCSS( CSSInjection injection, optional CSSInjectionCallback callback); @@ -222,7 +221,7 @@ namespace scripting { // must exactly match the stylesheet inserted through $(ref:insertCSS). // Attempting to remove a non-existent stylesheet is a no-op. // |callback|: A callback to be invoked upon the completion of the removal. - [supportsPromises] static void removeCSS( + static void removeCSS( CSSInjection injection, optional CSSInjectionCallback callback); @@ -232,7 +231,7 @@ namespace scripting { // already exist, then no scripts are registered. // |callback|: A callback to be invoked once scripts have been fully // registered or if an error has occurred. - [supportsPromises] static void registerContentScripts( + static void registerContentScripts( RegisteredContentScript[] scripts, optional RegisterContentScriptsCallback callback); @@ -240,7 +239,7 @@ namespace scripting { // that match the given filter. // |filter|: An object to filter the extension's dynamically registered // scripts. - [supportsPromises] static void getRegisteredContentScripts( + static void getRegisteredContentScripts( optional ContentScriptFilter filter, GetRegisteredContentScriptsCallback callback); @@ -250,7 +249,7 @@ namespace scripting { // scripts are unregistered. // |callback|: A callback to be invoked once scripts have been unregistered // or if an error has occurred. - [supportsPromises] static void unregisterContentScripts( + static void unregisterContentScripts( optional ContentScriptFilter filter, optional UnregisterContentScriptsCallback callback); @@ -262,7 +261,7 @@ namespace scripting { // are updated. // |callback|: A callback to be invoked once scripts have been updated or // if an error has occurred. - [supportsPromises] static void updateContentScripts( + static void updateContentScripts( RegisteredContentScript[] scripts, optional RegisterContentScriptsCallback callback); };