Skip to content

Commit

Permalink
[Extensions] Move BackgroundScriptExecutor to its own file
Browse files Browse the repository at this point in the history
Move BackgroundScriptExecutor from browsertest_util.[h|cc] to its own
file in background_script_executor.[h|cc]. It's a large enough and
self-contained class that it doesn't need to be bundled in with other
util methods.

Update all callers, includes, and tests.

This CL has no behavior change.

Bug: 1319642
Change-Id: Id71576f0af30ede2bce225e16daae18d9d52dd7b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3636097
Commit-Queue: Devlin Cronin <rdevlin.cronin@chromium.org>
Reviewed-by: Tim <tjudkins@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1001173}
  • Loading branch information
rdcronin authored and Chromium LUCI CQ committed May 9, 2022
1 parent c7d0eea commit 7a08f92
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 316 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "chrome/test/base/ui_test_utils.h"
#include "components/version_info/channel.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/extension_action.h"
#include "extensions/browser/extension_action_manager.h"
#include "extensions/browser/extension_host_registry.h"
Expand Down Expand Up @@ -44,8 +44,8 @@ class ActionAPIInteractiveUITest : public ExtensionApiTest {
// `extension`, and waits for a corresponding test success notification.
void RunScriptTest(const std::string& script, const Extension& extension) {
ResultCatcher result_catcher;
browsertest_util::BackgroundScriptExecutor::ExecuteScriptAsync(
profile(), extension.id(), script);
BackgroundScriptExecutor::ExecuteScriptAsync(profile(), extension.id(),
script);
EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
}

Expand Down
8 changes: 4 additions & 4 deletions chrome/browser/extensions/api/scripting/scripting_apitest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/test/extension_test_message_listener.h"
Expand Down Expand Up @@ -391,10 +392,9 @@ IN_PROC_BROWSER_TEST_F(ScriptingAPITest, InjectImmediately) {

// A helper function to run the script in the worker context.
auto run_script_in_worker = [this, extension](const std::string& script) {
return browsertest_util::BackgroundScriptExecutor::ExecuteScript(
return BackgroundScriptExecutor::ExecuteScript(
profile(), extension->id(), script,
browsertest_util::BackgroundScriptExecutor::ResultCapture::
kSendScriptResult);
BackgroundScriptExecutor::ResultCapture::kSendScriptResult);
};

auto get_default_result = [run_script_in_worker]() {
Expand All @@ -419,7 +419,7 @@ IN_PROC_BROWSER_TEST_F(ScriptingAPITest, InjectImmediately) {
/*will_reply=*/false);
ExtensionTestMessageListener default_listener("default complete",
/*will_reply=*/false);
browsertest_util::BackgroundScriptExecutor::ExecuteScriptAsync(
BackgroundScriptExecutor::ExecuteScriptAsync(
profile(), extension->id(), base::StringPrintf(kInjectScripts, tab_id));

// The script with immediate injection should finish (but it's still round
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@
#include "base/test/values_test_util.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"

namespace extensions {

using browsertest_util::BackgroundScriptExecutor;

using BrowserTestUtilBrowserTest = ExtensionBrowserTest;
using BackgroundScriptExecutorBrowserTest = ExtensionBrowserTest;

// Tests the ability to run JS in an extension-registered service worker.
IN_PROC_BROWSER_TEST_F(BrowserTestUtilBrowserTest,
IN_PROC_BROWSER_TEST_F(BackgroundScriptExecutorBrowserTest,
ExecuteScriptInServiceWorker) {
constexpr char kManifest[] =
R"({
Expand Down Expand Up @@ -60,7 +58,7 @@ IN_PROC_BROWSER_TEST_F(BrowserTestUtilBrowserTest,
}

// Tests the ability to run JS in an extension background page.
IN_PROC_BROWSER_TEST_F(BrowserTestUtilBrowserTest,
IN_PROC_BROWSER_TEST_F(BackgroundScriptExecutorBrowserTest,
ExecuteScriptInBackgroundPage) {
constexpr char kManifest[] =
R"({
Expand Down
2 changes: 1 addition & 1 deletion chrome/test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -2858,9 +2858,9 @@ if (!is_android) {
"../browser/extensions/back_forward_cache_browsertest.cc",
"../browser/extensions/background_header_browsertest.cc",
"../browser/extensions/background_page_apitest.cc",
"../browser/extensions/background_script_executor_browsertest.cc",
"../browser/extensions/background_scripts_apitest.cc",
"../browser/extensions/background_xhr_browsertest.cc",
"../browser/extensions/browsertest_util_browsertest.cc",
"../browser/extensions/calculator_app_browsertest.cc",
"../browser/extensions/chrome_app_api_browsertest.cc",
"../browser/extensions/chrome_test_extension_loader_browsertest.cc",
Expand Down
2 changes: 2 additions & 0 deletions extensions/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,8 @@ source_set("test_support") {
"api/idle/test_idle_provider.h",
"api/sockets_udp/test_udp_echo_server.cc",
"api/sockets_udp/test_udp_echo_server.h",
"background_script_executor.cc",
"background_script_executor.h",
"browsertest_util.cc",
"browsertest_util.h",
"extension_host_test_helper.cc",
Expand Down
203 changes: 203 additions & 0 deletions extensions/browser/background_script_executor.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/background_script_executor.h"

#include "base/callback.h"
#include "base/json/json_reader.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/service_worker_test_helpers.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/script_result_queue.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace extensions {

namespace {

// Returns a log-friendly script string.
std::string GetScriptToLog(const std::string& script) {
// The maximum script size for which to print on failure.
static constexpr int kMaxFailingScriptSizeToLog = 1000;
return (script.size() < kMaxFailingScriptSizeToLog) ? script
: "<script too large>";
}

} // namespace

BackgroundScriptExecutor::BackgroundScriptExecutor(
content::BrowserContext* browser_context)
: browser_context_(browser_context),
registry_(ExtensionRegistry::Get(browser_context_)),
process_manager_(ProcessManager::Get(browser_context_)) {}

BackgroundScriptExecutor::~BackgroundScriptExecutor() = default;

base::Value BackgroundScriptExecutor::ExecuteScript(
const ExtensionId& extension_id,
const std::string& script,
ResultCapture result_capture,
browsertest_util::ScriptUserActivation script_user_activation) {
if (result_capture == ResultCapture::kNone) {
AddTestFailure(
"Cannot wait for a result with no result capture. "
"Use ExecuteScriptAsync() instead");
return base::Value();
}

ExecuteScriptAsync(extension_id, script, result_capture,
script_user_activation);
return WaitForResult();
}

// static
base::Value BackgroundScriptExecutor::ExecuteScript(
content::BrowserContext* browser_context,
const ExtensionId& extension_id,
const std::string& script,
ResultCapture result_capture,
browsertest_util::ScriptUserActivation script_user_activation) {
return BackgroundScriptExecutor(browser_context)
.ExecuteScript(extension_id, script, result_capture,
script_user_activation);
}

bool BackgroundScriptExecutor::ExecuteScriptAsync(
const ExtensionId& extension_id,
const std::string& script,
ResultCapture result_capture,
browsertest_util::ScriptUserActivation script_user_activation) {
extension_ = registry_->enabled_extensions().GetByID(extension_id);
script_ = script;
result_capture_method_ = result_capture;
if (!extension_) {
AddTestFailure("No enabled extension with id: " + extension_id);
return false;
}

if (BackgroundInfo::IsServiceWorkerBased(extension_)) {
background_type_ = BackgroundType::kServiceWorker;
DCHECK_NE(ResultCapture::kWindowDomAutomationController,
result_capture_method_)
<< "Cannot use domAutomationController in a worker.";
DCHECK_EQ(browsertest_util::ScriptUserActivation::kDontActivate,
script_user_activation)
<< "Cannot provide a user gesture to service worker scripts";
return ExecuteScriptInServiceWorker();
}

if (BackgroundInfo::HasBackgroundPage(extension_)) {
background_type_ = BackgroundType::kPage;
return ExecuteScriptInBackgroundPage(script_user_activation);
}

AddTestFailure(
"Attempting to execute a background script for an extension"
" with no background context");
return false;
}

// static
bool BackgroundScriptExecutor::ExecuteScriptAsync(
content::BrowserContext* browser_context,
const ExtensionId& extension_id,
const std::string& script,
browsertest_util::ScriptUserActivation script_user_activation) {
return BackgroundScriptExecutor(browser_context)
.ExecuteScriptAsync(extension_id, script, ResultCapture::kNone,
script_user_activation);
}

base::Value BackgroundScriptExecutor::WaitForResult() {
DCHECK(background_type_);
DCHECK_NE(ResultCapture::kNone, result_capture_method_)
<< "Trying to wait for a result when no result was expected.";

if (result_capture_method_ == ResultCapture::kSendScriptResult) {
DCHECK(script_result_queue_);
return script_result_queue_->GetNextResult();
}

DCHECK_EQ(ResultCapture::kWindowDomAutomationController,
result_capture_method_);
DCHECK(message_queue_);
std::string next_message;
if (!message_queue_->WaitForMessage(&next_message)) {
AddTestFailure("Failed to wait for message");
return base::Value();
}
absl::optional<base::Value> value =
base::JSONReader::Read(next_message, base::JSON_ALLOW_TRAILING_COMMAS);
if (!value) {
AddTestFailure("Received bad message: " + next_message);
return base::Value();
}
return std::move(*value);
}

bool BackgroundScriptExecutor::ExecuteScriptInServiceWorker() {
std::vector<WorkerId> worker_ids =
process_manager_->GetServiceWorkersForExtension(extension_->id());
if (worker_ids.size() != 1u) {
AddTestFailure("Incorrect number of workers registered for extension");
return false;
}

if (result_capture_method_ == ResultCapture::kSendScriptResult)
script_result_queue_ = std::make_unique<ScriptResultQueue>();

content::ServiceWorkerContext* service_worker_context =
util::GetStoragePartitionForExtensionId(extension_->id(),
browser_context_)
->GetServiceWorkerContext();
service_worker_context->ExecuteScriptForTest( // IN-TEST
script_, worker_ids[0].version_id, base::DoNothing());
return true;
}

bool BackgroundScriptExecutor::ExecuteScriptInBackgroundPage(
browsertest_util::ScriptUserActivation script_user_activation) {
ExtensionHost* host =
process_manager_->GetBackgroundHostForExtension(extension_->id());
if (!host) {
AddTestFailure("Extension does not have an active background page");
return false;
}

switch (result_capture_method_) {
case ResultCapture::kNone:
break;
case ResultCapture::kSendScriptResult:
script_result_queue_ = std::make_unique<ScriptResultQueue>();
break;
case ResultCapture::kWindowDomAutomationController:
message_queue_ =
std::make_unique<content::DOMMessageQueue>(host->host_contents());
break;
}

if (script_user_activation ==
browsertest_util::ScriptUserActivation::kActivate) {
content::ExecuteScriptAsync(host->host_contents(), script_);
} else {
NOTREACHED() << "Not yet supported. Use ExecuteScriptInBackgroundPage().";
}
return true;
}

void BackgroundScriptExecutor::AddTestFailure(const std::string& message) {
ADD_FAILURE() << "Background script execution failed: " << message
<< ". Extension: "
<< (extension_ ? extension_->name() : "<not found>")
<< ", script: " << GetScriptToLog(script_);
}

} // namespace extensions

0 comments on commit 7a08f92

Please sign in to comment.