blob: 9faf958621071f0c651f68e8a38c46bf61dd252e [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/message_tracker.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/with_feature_override.h"
#include "chrome/browser/extensions/browsertest_util.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/service_worker/service_worker_test_utils.h"
#include "extensions/common/extension_features.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
class MessageTrackerMessagingTest : public ExtensionApiTest {
public:
MessageTrackerMessagingTest() = default;
MessageTrackerMessagingTest(const MessageTrackerMessagingTest&) = delete;
MessageTrackerMessagingTest& operator=(const MessageTrackerMessagingTest&) =
delete;
void SetUpOnMainThread() override {
ExtensionApiTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
message_tracker_ = MessageTracker::Get(profile());
}
void TearDownOnMainThread() override {
message_tracker_ = nullptr;
ExtensionApiTest::TearDownOnMainThread();
}
MessageTracker* message_tracker() { return message_tracker_; }
private:
raw_ptr<MessageTracker> message_tracker_;
};
class MessageTrackerMessagingTestWithOptimizeServiceWorkerStart
: public MessageTrackerMessagingTest,
public base::test::WithFeatureOverride {
public:
MessageTrackerMessagingTestWithOptimizeServiceWorkerStart()
: WithFeatureOverride(
extensions_features::kOptimizeServiceWorkerStartRequests) {}
};
// Tests the tracking of messages when sent from a tab to a SW extension
// background context.
IN_PROC_BROWSER_TEST_P(
MessageTrackerMessagingTestWithOptimizeServiceWorkerStart,
SendMessageToWorker) {
const bool wakeup_optimization_enabled = IsParamFeatureEnabled();
const int kExpectedWakeUps = wakeup_optimization_enabled ? 0 : 1;
ExtensionTestMessageListener worker_listener("WORKER_RUNNING");
const Extension* extension = LoadExtension(test_data_dir_.AppendASCII(
"service_worker/messaging/send_message_tab_to_worker"));
ASSERT_TRUE(extension);
EXPECT_TRUE(worker_listener.WaitUntilSatisfied());
ExtensionTestMessageListener reply_listener("CONTENT_SCRIPT_RECEIVED_REPLY");
reply_listener.set_failure_message("FAILURE");
base::HistogramTester histogram_tester;
{
ASSERT_TRUE(StartEmbeddedTestServer());
const GURL url =
embedded_test_server()->GetURL("/extensions/test_file.html");
content::WebContents* new_web_contents =
browsertest_util::AddTab(browser(), url);
EXPECT_TRUE(new_web_contents);
}
EXPECT_TRUE(reply_listener.WaitUntilSatisfied());
// Overall channel open metrics expectations.
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
"SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
"SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
"SendMessageChannel",
/*expected_count=*/kExpectedWakeUps);
// Per connect IPC dispatch metrics expectations.
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
"SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
"SendMessageChannel",
/*expected_count=*/0);
// Overall channel open metrics expectations.
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
"SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
"SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
"SendMessageChannel",
/*sample=*/
MessageTracker::OpenChannelMessagePipelineResult::kWorkerStarted,
/*expected_count=*/kExpectedWakeUps);
// Per connect IPC dispatch metrics expectations.
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
/*sample=*/
MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
"SendMessageChannel",
/*sample=*/
MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
/*expected_count=*/1);
}
class MessageTrackerMessagingTestNonWorker
: public ExtensionApiTest,
public testing::WithParamInterface<const char*> {};
// Tests the tracking of messages when sent from a tab to a extension background
// page context.
IN_PROC_BROWSER_TEST_P(MessageTrackerMessagingTestNonWorker,
SendMessageToNonWorker) {
ExtensionTestMessageListener background_listener("BACKGROUND_RUNNING");
constexpr char kManifest[] =
R"(
{
"name": "runtime.sendMessage from content script to extension",
"version": "0.1",
"manifest_version": 2,
"description": "non service worker",
"content_scripts": [{
"matches": ["*://*/*"],
"js": ["content.js"]
}],
"background": {
"scripts": ["background.js"],
"persistent": %s
}
}
)";
constexpr char kContentScript[] =
R"(
chrome.runtime.sendMessage('tab->background', response => {
if (response != 'tab->background->tab') {
chrome.test.sendMessage('FAILURE');
return;
}
chrome.test.sendMessage('CONTENT_SCRIPT_RECEIVED_REPLY');
});
)";
constexpr char kBackground[] =
R"(
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg != 'tab->background') {
chrome.test.sendMessage('FAILURE');
return;
}
sendResponse('tab->background->tab');
});
chrome.test.sendMessage('BACKGROUND_RUNNING');
)";
extensions::TestExtensionDir test_dir;
test_dir.WriteManifest(
base::StringPrintf(kManifest, /*persistent=*/GetParam()));
test_dir.WriteFile(FILE_PATH_LITERAL("content.js"), kContentScript);
test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground);
const Extension* extension = LoadExtension(test_dir.UnpackedPath());
ASSERT_TRUE(extension);
EXPECT_TRUE(background_listener.WaitUntilSatisfied());
ExtensionTestMessageListener reply_listener("CONTENT_SCRIPT_RECEIVED_REPLY");
reply_listener.set_failure_message("FAILURE");
base::HistogramTester histogram_tester;
{
ASSERT_TRUE(StartEmbeddedTestServer());
const GURL url =
embedded_test_server()->GetURL("/extensions/test_file.html");
content::WebContents* new_web_contents =
browsertest_util::AddTab(browser(), url);
EXPECT_TRUE(new_web_contents);
}
EXPECT_TRUE(reply_listener.WaitUntilSatisfied());
// Overall channel open metrics expectations.
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithNoWorker."
"SendMessageChannel",
/*expected_count=*/1);
// No worker metrics should emitted.
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithIdleWorker",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
"SendMessageChannel",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
"SendMessageChannel",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
"SendMessageChannel",
/*expected_count=*/0);
// Per connect IPC dispatch metrics expectations.
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
"SendMessageChannel",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
"SendMessageChannel",
/*expected_count=*/1);
// Overall channel open metrics expectations.
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelStatusWithNoWorker."
"SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
// Per connect IPC dispatch metrics expectations.
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
/*sample=*/
MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
"SendMessageChannel",
/*sample=*/
MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
/*expected_count=*/1);
}
INSTANTIATE_TEST_SUITE_P(EventPage,
MessageTrackerMessagingTestNonWorker,
testing::Values("false"));
INSTANTIATE_TEST_SUITE_P(PersistentBackgroundPage,
MessageTrackerMessagingTestNonWorker,
testing::Values("true"));
// Tests the tracking of messages when sent from a tab content script to a
// extension background page context and an extension tab script.
IN_PROC_BROWSER_TEST_P(
MessageTrackerMessagingTestWithOptimizeServiceWorkerStart,
SendMessageToTabAndWorker) {
const bool wakeup_optimization_enabled = IsParamFeatureEnabled();
const int kExpectedWakeUps = wakeup_optimization_enabled ? 0 : 1;
constexpr char kManifest[] =
R"(
{
"name": "runtime.sendMessage from content script to SW extension",
"version": "0.1",
"manifest_version": 3,
"description": "service worker",
"content_scripts": [{
"matches": ["*://*/*"],
"js": ["content_script.js"]
}],
"background": {"service_worker": "background.js"}
}
)";
constexpr char kContentScript[] =
R"(
chrome.runtime.sendMessage('tab->worker', response => {
if (response != 'tab->worker->tab') {
chrome.test.sendMessage('FAILURE');
return;
}
chrome.test.sendMessage('CONTENT_SCRIPT_RECEIVED_REPLY');
});
)";
constexpr char kTestExtTabHtml[] =
R"(
<!DOCTYPE html>
<body>
<script src="test_tab.js"></script>
</body>
</html>
)";
constexpr char kTestExtTab[] =
R"(
chrome.runtime.onMessage.addListener((details) => {
chrome.test.sendMessage('TAB_LISTENER_RECEIVED_MESSAGE');
});
)";
constexpr char kBackground[] =
R"(
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg != 'tab->worker') {
chrome.test.sendMessage('FAILURE');
return;
}
sendResponse('tab->worker->tab');
});
chrome.runtime.onInstalled.addListener((details) => {
chrome.test.sendMessage('WORKER_RUNNING');
});
)";
extensions::TestExtensionDir test_dir;
test_dir.WriteManifest(kManifest);
test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"), kContentScript);
test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground);
test_dir.WriteFile(FILE_PATH_LITERAL("test_ext_tab.html"), kTestExtTabHtml);
test_dir.WriteFile(FILE_PATH_LITERAL("test_tab.js"), kTestExtTab);
ExtensionTestMessageListener background_listener("WORKER_RUNNING");
const Extension* extension = LoadExtension(test_dir.UnpackedPath());
ASSERT_TRUE(extension);
EXPECT_TRUE(background_listener.WaitUntilSatisfied());
ExtensionTestMessageListener reply_listener("CONTENT_SCRIPT_RECEIVED_REPLY");
reply_listener.set_failure_message("REPLY_FAILURE");
ExtensionTestMessageListener ext_tab_listener_fired(
"TAB_LISTENER_RECEIVED_MESSAGE");
ext_tab_listener_fired.set_failure_message("TAB_LISTENER_FAILURE");
base::HistogramTester histogram_tester;
{
ASSERT_TRUE(StartEmbeddedTestServer());
// Load the extension tab (and it's script).
GURL ext_url = extension->GetResourceURL("test_ext_tab.html");
content::WebContents* new_ext_tab_web_contents = GetActiveWebContents();
ASSERT_TRUE(new_ext_tab_web_contents);
ASSERT_TRUE(NavigateToURL(new_ext_tab_web_contents, GURL(ext_url)));
// Must load the tab content script second since it's loading sends a
// message to the extension tab.
const GURL url =
embedded_test_server()->GetURL("/extensions/test_file.html");
content::WebContents* new_web_contents =
browsertest_util::AddTab(browser(), url);
ASSERT_TRUE(new_web_contents);
}
ASSERT_TRUE(reply_listener.WaitUntilSatisfied());
ASSERT_TRUE(ext_tab_listener_fired.WaitUntilSatisfied());
// Overall channel open metrics expectations.
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
"SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
"SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
"SendMessageChannel",
/*expected_count=*/kExpectedWakeUps);
// Per connect IPC dispatch metrics expectations.
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
"SendMessageChannel",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
"SendMessageChannel",
/*expected_count=*/1);
// Overall channel open metrics expectations.
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
"SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
"SendMessageChannel",
/*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
"Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
"SendMessageChannel",
/*sample=*/
MessageTracker::OpenChannelMessagePipelineResult::kWorkerStarted,
/*expected_count=*/kExpectedWakeUps);
// Per connect IPC dispatch metrics expectations cannot be specified in this
// test because the channel will be closed by the first port responder to the
// IPC and that will can change the value emitted for the other port.
}
// TODO(crbug.com/371011217): Once we start tracking message dispatch metrics
// add a test case for a worker that never responds to the message.
// Toggle `extensions_features::OptimizeServiceWorkerStartRequests`.
INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
MessageTrackerMessagingTestWithOptimizeServiceWorkerStart);
} // namespace extensions