blob: f3706693f0a2e3666f8a6f6ab94f149979f6820d [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/service_worker_test_helpers.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/service_worker/service_worker_test_utils.h"
#include "extensions/common/extension_features.h"
#include "extensions/test/extension_background_page_waiter.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 {
namespace {
// TODO(crbug.com/1441221): Create test cases where we test "failures" like
// events not acking.
using ContextType = ExtensionBrowserTest::ContextType;
using EventMetricsBrowserTest = ExtensionBrowserTest;
// TODO(crbug.com/1441221): combine this observer with
// extensions/browser/service_worker/service_worker_test_utils.h and
// chrome/browser/extensions/service_worker_event_dispatching_browsertest.cc
// observers.
class TestWorkerStatusObserver : public content::ServiceWorkerContextObserver {
public:
TestWorkerStatusObserver(content::BrowserContext* browser_context,
const ExtensionId& extension_id)
: extension_url_(Extension::GetBaseURLFromExtensionId(extension_id)),
sw_context_(service_worker_test_utils::GetServiceWorkerContext(
browser_context)) {
scoped_observation_.Observe(sw_context_);
}
TestWorkerStatusObserver(const TestWorkerStatusObserver&) = delete;
TestWorkerStatusObserver& operator=(const TestWorkerStatusObserver&) = delete;
void WaitForWorkerStarted() { started_worker_run_loop_.Run(); }
void WaitForWorkerStopped() { stopped_worker_run_loop_.Run(); }
int64_t test_worker_version_id() const { return test_worker_version_id_; }
private:
// ServiceWorkerContextObserver:
// Called when a worker has entered the
// `blink::EmbeddedWorkerStatus::kRunning` status. Used to indicate when our
// test extension is now running.
void OnVersionStartedRunning(
int64_t version_id,
const content::ServiceWorkerRunningInfo& running_info) override {
if (running_info.scope != extension_url_) {
return;
}
test_worker_version_id_ = version_id;
started_worker_run_loop_.Quit();
}
// Called when a worker has entered the
// `blink::EmbeddedWorkerStatus::kStopping` status. Used to indicate when our
// test extension has stopped.
void OnVersionStoppedRunning(int64_t version_id) override {
// `test_worker_version_id_` is the previously running version's id.
if (test_worker_version_id_ != version_id) {
return;
}
stopped_worker_run_loop_.Quit();
}
int64_t test_worker_version_id_ =
blink::mojom::kInvalidServiceWorkerVersionId;
base::RunLoop started_worker_run_loop_;
base::RunLoop stopped_worker_run_loop_;
const GURL extension_url_;
const raw_ptr<content::ServiceWorkerContext> sw_context_;
base::ScopedObservation<content::ServiceWorkerContext,
content::ServiceWorkerContextObserver>
scoped_observation_{this};
};
// Tests that the only the dispatch time histogram provided to the test is
// emitted with a sane value, and that other provided metrics are not emitted.
// TODO(crbug.com/1484659): Disabled on ASAN due to leak caused by renderer gin
// objects which are intended to be leaked.
#if defined(ADDRESS_SANITIZER)
#define MAYBE_DispatchMetricTest DISABLED_DispatchMetricTest
#else
#define MAYBE_DispatchMetricTest DispatchMetricTest
#endif
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest, MAYBE_DispatchMetricTest) {
ASSERT_TRUE(embedded_test_server()->Start());
struct {
const std::string event_metric_emitted;
ContextType context_type;
const std::vector<std::string> event_metrics_not_emitted;
} test_cases[] = {
// DispatchToAckTime
{"Extensions.Events.DispatchToAckTime.ExtensionEventPage3",
ContextType::kFromManifest, // event page
{"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2",
"Extensions.Events.DispatchToAckTime."
"ExtensionPersistentBackgroundPage"}},
{"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2",
ContextType::kServiceWorker,
{"Extensions.Events.DispatchToAckTime.ExtensionEventPage3",
"Extensions.Events.DispatchToAckTime."
"ExtensionPersistentBackgroundPage"}},
// TODO(crbug.com/1441221): Add `event_metrics_not_emitted` when other
// versions are created.
// DispatchToAckLongTime
{"Extensions.Events.DispatchToAckLongTime.ExtensionServiceWorker2",
ContextType::kServiceWorker,
{}},
// DidDispatchToAckSucceed
{"Extensions.Events.DidDispatchToAckSucceed.ExtensionPage",
ContextType::kFromManifest, // event page
{"Extensions.Events.DidDispatchToAckSucceed.ExtensionServiceWorker2"}},
{"Extensions.Events.DidDispatchToAckSucceed.ExtensionServiceWorker2",
ContextType::kServiceWorker,
{"Extensions.Events.DidDispatchToAckSucceed.ExtensionPage"}},
};
for (const auto& test_case : test_cases) {
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
// Load the extension for the particular context type. The manifest
// file is for a legacy event page-based extension. LoadExtension will
// modify the extension for the kServiceWorker case.
scoped_refptr<const Extension> extension = LoadExtension(
test_data_dir_.AppendASCII("events/metrics/web_navigation"),
{.context_type = test_case.context_type});
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// Call to webNavigation.onCompleted expected.
histogram_tester.ExpectTotalCount(test_case.event_metric_emitted,
/*expected_count=*/1);
// Verify that the recorded values are sane -- that is, that they are less
// than the maximum bucket.
histogram_tester.ExpectBucketCount(
test_case.event_metric_emitted,
/*sample=*/base::Minutes(5).InMicroseconds(), /*expected_count=*/0);
// Verify other extension context types are not logged.
for (const auto& event_metric_not_emitted :
test_case.event_metrics_not_emitted) {
SCOPED_TRACE(event_metric_not_emitted);
histogram_tester.ExpectTotalCount(event_metric_not_emitted,
/*expected_count=*/0);
}
// Prevent extensions persisting across test cases and emitting extra
// metrics for events.
UninstallExtension(extension->id());
}
}
// Tests that only the dispatch time histogram for a persistent background page
// extension is emitted with a sane value, and that the same metric for other
// background context types are not emitted.
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest,
PersistentBackgroundDispatchMetricTest) {
ASSERT_TRUE(embedded_test_server()->Start());
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
// Load the extension for a persistent background page
scoped_refptr<const Extension> extension = LoadExtension(
test_data_dir_.AppendASCII("events/metrics/persistent_background"));
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// Call to webNavigation.onCompleted expected.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionPersistentBackgroundPage",
/*expected_count=*/1);
// Verify that the recorded values are sane -- that is, that they are less
// than the maximum bucket.
histogram_tester.ExpectBucketCount(
"Extensions.Events.DispatchToAckTime.ExtensionPersistentBackgroundPage",
/*sample=*/base::Minutes(5).InMicroseconds(), /*expected_count=*/0);
// Verify other extension background context types are not logged.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2",
/*expected_count=*/0);
}
// Tests that only the dispatch time histogram for a persistent background page
// extension is emitted with a sane value, and that the same metric for other
// background context types are not emitted.
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest,
PersistentBackgroundStaleEventsMetricTest) {
ASSERT_TRUE(embedded_test_server()->Start());
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
// Load the extension for a persistent background page
scoped_refptr<const Extension> extension = LoadExtension(
test_data_dir_.AppendASCII("events/metrics/persistent_background"));
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// Call to webNavigation.onCompleted expected.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DidDispatchToAckSucceed.ExtensionPersistentPage",
/*expected_count=*/1);
// Verify that the value is `true` since the event wasn't delayed in acking.
histogram_tester.ExpectBucketCount(
"Extensions.Events.DidDispatchToAckSucceed.ExtensionPersistentPage",
/*sample=*/true, /*expected_count=*/1);
// Verify other extension background context types are not logged.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DidDispatchToAckSucceed.ExtensionPage",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DidDispatchToAckSucceed.ExtensionServiceWorker2",
/*expected_count=*/0);
}
// Tests that for every event received there is a corresponding emit of starting
// and finishing status of the service worker external request.
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest, ExternalRequestMetrics) {
ASSERT_TRUE(embedded_test_server()->Start());
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
// Load the extension for the particular context type. The manifest
// file is for a legacy event page-based extension. LoadExtension will
// modify the extension for the kServiceWorker case.
base::HistogramTester histogram_tester_oninstalled;
const Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("events/metrics/web_navigation"),
{.context_type = ContextType::kServiceWorker});
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
// Call to runtime.onInstalled expected.
histogram_tester_oninstalled.ExpectTotalCount(
"Extensions.ServiceWorkerBackground.StartingExternalRequest_Result",
/*expected_count=*/1);
histogram_tester_oninstalled.ExpectTotalCount(
"Extensions.ServiceWorkerBackground.FinishedExternalRequest_Result",
/*expected_count=*/1);
histogram_tester_oninstalled.ExpectTotalCount(
"Extensions.ServiceWorkerBackground.FinishedExternalRequest_Result_"
"PostReturn",
/*expected_count=*/1);
}
// Tests that an active event page will emit the proper dispatch time metric.
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest,
EventPageDispatchToAckTimeActive) {
ASSERT_TRUE(embedded_test_server()->Start());
// Extend background page expiration time so that the event page will be
// active for the test.
ProcessManager::SetEventPageIdleTimeForTesting(60000);
ProcessManager::SetEventPageSuspendingTimeForTesting(60000);
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
scoped_refptr<const Extension> extension =
LoadExtension(test_data_dir_.AppendASCII("events/metrics/web_navigation"),
{.context_type = ContextType::kEventPage});
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
ExtensionBackgroundPageWaiter(profile(), *extension).WaitForBackgroundOpen();
ProcessManager* process_manager = ProcessManager::Get(profile());
ASSERT_FALSE(process_manager->IsEventPageSuspended(extension->id()));
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// Call to webNavigation.onCompleted expected.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3.Active",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3.Inactive",
/*expected_count=*/0);
// Verify that the recorded values are sane -- that is, that they are less
// than the maximum bucket.
histogram_tester.ExpectBucketCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3.Active",
/*sample=*/base::Minutes(5).InMicroseconds(),
/*expected_count=*/0);
}
// Tests that an inactive event page will emit the proper dispatch time metric.
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest,
EventPageDispatchToAckTimeInactive) {
ASSERT_TRUE(embedded_test_server()->Start());
// Minimize background page expiration time so that the event page will
// suspend/idle quickly for the test.
ProcessManager::SetEventPageIdleTimeForTesting(1);
ProcessManager::SetEventPageSuspendingTimeForTesting(1);
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
scoped_refptr<const Extension> extension =
LoadExtension(test_data_dir_.AppendASCII("events/metrics/web_navigation"),
{.context_type = ContextType::kEventPage});
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
ExtensionBackgroundPageWaiter(profile(), *extension)
.WaitForBackgroundClosed();
ProcessManager* process_manager = ProcessManager::Get(profile());
ASSERT_TRUE(process_manager->IsEventPageSuspended(extension->id()));
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// Call to webNavigation.onCompleted expected.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3.Inactive",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3.Active",
/*expected_count=*/0);
// Verify that the recorded values are sane -- that is, that they are less
// than the maximum bucket.
histogram_tester.ExpectBucketCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3.Inactive",
/*sample=*/base::Minutes(5).InMicroseconds(),
/*expected_count=*/0);
}
// Tests that an active service worker will emit the proper dispatch time
// metric.
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest,
ServiceWorkerDispatchToAckTimeActive) {
ASSERT_TRUE(embedded_test_server()->Start());
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
// Load the extension for the particular context type. The manifest
// file is for a legacy event page-based extension. LoadExtension will
// modify the extension for the kServiceWorker case.
const Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("events/metrics/web_navigation"),
{.context_type = ContextType::kServiceWorker});
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
ASSERT_TRUE(content::CheckServiceWorkerIsRunning(
// The first SW version ID is always 0.
GetServiceWorkerContext(), /*service_worker_version_id=*/0));
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// Call to webNavigation.onCompleted expected.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2.Active",
/*expected_count=*/1);
// Verify that the recorded values are sane -- that is, that they are less
// than the maximum bucket.
histogram_tester.ExpectBucketCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2.Active",
/*sample=*/base::Minutes(5).InMicroseconds(), /*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2.Inactive",
/*expected_count=*/0);
}
// Tests that an inactive service worker will emit the proper dispatch time
// metric.
IN_PROC_BROWSER_TEST_F(EventMetricsBrowserTest,
ServiceWorkerDispatchToAckTimeInactive) {
ASSERT_TRUE(embedded_test_server()->Start());
constexpr char kTestExtensionId[] = "iegclhlplifhodhkoafiokenjoapiobj";
// Stop the service worker to make it inactive.
TestWorkerStatusObserver test_worker_start_stop_observer(profile(),
kTestExtensionId);
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
// We need to load an extension where we know the extensions ID so that we
// can correctly observe when the worker starts and stops.
const Extension* extension = LoadExtension(
test_data_dir_.AppendASCII("events/reliability/service_worker"),
{.wait_for_registration_stored = true});
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
test_worker_start_stop_observer.WaitForWorkerStarted();
browsertest_util::StopServiceWorkerForExtensionGlobalScope(profile(),
kTestExtensionId);
test_worker_start_stop_observer.WaitForWorkerStopped();
ASSERT_TRUE(content::CheckServiceWorkerIsStopped(
GetServiceWorkerContext(),
test_worker_start_stop_observer.test_worker_version_id()));
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// Call to webNavigation.onCompleted expected.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2.Inactive",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2.Active",
/*expected_count=*/0);
// Verify that the recorded values are sane -- that is, that they are less
// than the maximum bucket.
histogram_tester.ExpectBucketCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2.Inactive",
/*sample=*/base::Minutes(5).InMicroseconds(),
/*expected_count=*/0);
}
// TODO: refactor to be generic for this feature, then do these two metrics with
// using to avoid code duplication.
class ServiceWorkerRedundantWorkerStartMetricsBrowserTest
: public EventMetricsBrowserTest,
public testing::WithParamInterface<bool> {
public:
ServiceWorkerRedundantWorkerStartMetricsBrowserTest() {
if (GetParam()) {
scoped_feature_list_.InitAndEnableFeature(
extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch);
} else {
scoped_feature_list_.InitAndDisableFeature(
extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch);
}
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests that a running service worker will be redundantly started when it
// receives an event while it is already started if
// extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch is
// disabled. If enabled, the worker is not redundantly started.
IN_PROC_BROWSER_TEST_P(ServiceWorkerRedundantWorkerStartMetricsBrowserTest,
ServiceWorkerRedundantStartCountTest) {
ASSERT_TRUE(embedded_test_server()->Start());
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
// Load the extension for the particular context type. The manifest
// file is for a legacy event page-based extension. LoadExtension will
// modify the extension for the kServiceWorker case.
const Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("events/metrics/web_navigation"),
{.wait_for_registration_stored = true,
.context_type = ContextType::kServiceWorker});
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
ASSERT_TRUE(content::CheckServiceWorkerIsRunning(
// The first SW version ID is always 0.
GetServiceWorkerContext(), /*service_worker_version_id=*/0));
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger the webNavigation.onBeforeRequest event to
// the extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
ASSERT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
if (GetParam()) {
// extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch true.
// Since feature prevents starting a worker when it is running, the
// event/task will not be added as pending and therefore this UMA is not
// emitted. But as per the assertions, we still run the event successfully.
histogram_tester.ExpectTotalCount(
"Extensions.ServiceWorkerBackground."
"RequestedWorkerStartForStartedWorker",
/*expected_count=*/0);
} else {
// extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch false
// Since the feature is disabled, we will redundantly attempt to start the
// worker.
histogram_tester.ExpectTotalCount(
"Extensions.ServiceWorkerBackground."
"RequestedWorkerStartForStartedWorker",
/*expected_count=*/1);
// Verify that the value is `true` since the without the feature the worker
// will be redundantly started.
histogram_tester.ExpectBucketCount(
"Extensions.ServiceWorkerBackground."
"RequestedWorkerStartForStartedWorker",
/*sample=*/true, /*expected_count=*/1);
}
}
INSTANTIATE_TEST_SUITE_P(
All,
ServiceWorkerRedundantWorkerStartMetricsBrowserTest,
/* extensions_features::kExtensionsServiceWorkerOptimizedEventDispatch
enabled status */
testing::Bool());
using ServiceWorkerPendingTasksForRunningWorkerMetricsBrowserTest =
ServiceWorkerRedundantWorkerStartMetricsBrowserTest;
class EventMetricsDispatchToSenderBrowserTest
: public ExtensionBrowserTest,
public testing::WithParamInterface<ContextType> {
public:
EventMetricsDispatchToSenderBrowserTest() = default;
EventMetricsDispatchToSenderBrowserTest(
const EventMetricsDispatchToSenderBrowserTest&) = delete;
EventMetricsDispatchToSenderBrowserTest& operator=(
const EventMetricsDispatchToSenderBrowserTest&) = delete;
};
// Tests that the we do not emit event dispatch time metrics for webRequest
// events with active listeners.
IN_PROC_BROWSER_TEST_P(EventMetricsDispatchToSenderBrowserTest,
DispatchToSenderMetricTest) {
ASSERT_TRUE(embedded_test_server()->Start());
// Load either a persistent background page or a service worker extension
// with webRequest permission.
static constexpr char kManifestPersistentBackgroundScript[] =
R"({"scripts": ["background.js"], "persistent": true})";
static constexpr char kManifestServiceWorkerBackgroundScript[] =
R"({"service_worker": "background.js"})";
static constexpr char kManifestPersistentBackgroundPermissions[] =
R"("permissions": ["webRequest", "http://example.com/*"])";
static constexpr char kManifestServiceWorkerPermissions[] =
R"(
"host_permissions": [
"http://example.com/*"
],
"permissions": ["webRequest"]
)";
static constexpr char kManifest[] =
R"({
"name": "Test Extension",
"manifest_version": %s,
"version": "0.1",
"background": %s,
%s
})";
bool persistent_background_extension =
GetParam() == ContextType::kPersistentBackground;
const char* background_script = persistent_background_extension
? kManifestPersistentBackgroundScript
: kManifestServiceWorkerBackgroundScript;
const char* manifest_version = persistent_background_extension ? "2" : "3";
const char* permissions = persistent_background_extension
? kManifestPersistentBackgroundPermissions
: kManifestServiceWorkerPermissions;
std::string manifest = base::StringPrintf(kManifest, manifest_version,
background_script, permissions);
// The extensions script listens for runtime.onInstalled and
// webRequest.onBeforeRequest.
static constexpr char kScript[] =
R"({
chrome.runtime.onInstalled.addListener((details) => {
// Asynchronously send the message that the listener fired so that the
// event is considered ack'd in the browser C++ code.
setTimeout(() => {
chrome.test.sendMessage('installed listener fired');
}, 0);
});
chrome.webRequest.onBeforeRequest.addListener(
(details) => {
setTimeout(() => {
chrome.test.sendMessage('listener fired');
}, 0);
},
{urls: ['<all_urls>'], types: ['main_frame']},
[]
);
})";
TestExtensionDir test_dir;
test_dir.WriteManifest(manifest);
test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kScript);
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
const Extension* extension = LoadExtension(test_dir.UnpackedPath());
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
base::HistogramTester histogram_tester;
ExtensionTestMessageListener test_event_listener_fired("listener fired");
// Navigate somewhere to trigger webRequest.onBeforeRequest event to the
// extension listener.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("example.com", "/simple.html")));
EXPECT_TRUE(test_event_listener_fired.WaitUntilSatisfied());
// We do not emit any dispatch histograms for webRequest events to active
// listeners.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckLongTime.ExtensionServiceWorker2",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DidDispatchToAckSucceed.ExtensionServiceWorker2",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"Extensions.Events.DidDispatchToAckSucceed.ExtensionPage",
/*expected_count=*/0);
// We do always log starting/finishing an external request.
if (!persistent_background_extension) { // service worker
histogram_tester.ExpectTotalCount(
"Extensions.ServiceWorkerBackground.StartingExternalRequest_Result",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.ServiceWorkerBackground.FinishedExternalRequest_Result",
/*expected_count=*/1);
histogram_tester.ExpectTotalCount(
"Extensions.ServiceWorkerBackground.FinishedExternalRequest_Result_"
"PostReturn",
/*expected_count=*/1);
}
}
INSTANTIATE_TEST_SUITE_P(PersistentBackground,
EventMetricsDispatchToSenderBrowserTest,
::testing::Values(ContextType::kPersistentBackground));
INSTANTIATE_TEST_SUITE_P(ServiceWorker,
EventMetricsDispatchToSenderBrowserTest,
::testing::Values(ContextType::kServiceWorker));
class LazyBackgroundEventMetricsApiTest : public ExtensionApiTest {
public:
LazyBackgroundEventMetricsApiTest() = default;
LazyBackgroundEventMetricsApiTest(const LazyBackgroundEventMetricsApiTest&) =
delete;
LazyBackgroundEventMetricsApiTest& operator=(
const LazyBackgroundEventMetricsApiTest&) = delete;
void SetUpOnMainThread() override {
ExtensionApiTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(StartEmbeddedTestServer());
}
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
};
// Tests that if there is a listener in the extension renderer process, but that
// listener is not in the lazy background page script, then do not emit
// background context event dispatching histograms.
IN_PROC_BROWSER_TEST_F(
LazyBackgroundEventMetricsApiTest,
ContextsOutsideLazyBackgroundDoNotEmitBackgroundContextMetrics) {
// Load an extension with a page script that runs in the extension renderer
// process, and has the only chrome.storage.onChanged listener.
static constexpr char kManifest[] =
R"({
"name": "Event page",
"version": "0.1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": ["storage"]
})";
TestExtensionDir test_dir;
test_dir.WriteManifest(kManifest);
constexpr char kPageHtml[] = R"(<script src="page.js"></script>)";
test_dir.WriteFile(FILE_PATH_LITERAL("page.html"), kPageHtml);
constexpr char kPageScriptJs[] =
R"(
chrome.storage.onChanged.addListener((details) => {
// Asynchronously send the message that the listener fired so that the
// event is considered ack'd in the browser C++ code.
setTimeout(() => {
chrome.test.sendMessage('listener fired');
}, 0);
});
chrome.test.sendMessage('page script loaded');
)";
test_dir.WriteFile(FILE_PATH_LITERAL("page.js"), kPageScriptJs);
constexpr char kBackgroundJs[] =
R"(
chrome.runtime.onInstalled.addListener((details) => {
// Asynchronously send the message that the listener fired so that the
// event is considered ack'd in the browser C++ code.
setTimeout(() => {
chrome.test.sendMessage('installed listener fired');
}, 0);
});
)";
test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs);
ExtensionTestMessageListener extension_oninstall_listener_fired(
"installed listener fired");
const Extension* extension = LoadExtension(test_dir.UnpackedPath());
ASSERT_TRUE(extension);
// This ensures that we wait until the the browser receives the ack from the
// renderer. This prevents unexpected histogram emits later.
ASSERT_TRUE(extension_oninstall_listener_fired.WaitUntilSatisfied());
ExtensionTestMessageListener page_script_loaded("page script loaded");
// Navigate to page.html to get the content_script to load.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), extension->GetResourceURL("page.html")));
ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
ASSERT_TRUE(page_script_loaded.WaitUntilSatisfied());
// Set storage value which should fire chrome.storage.onChanged listener in
// the page.
base::HistogramTester histogram_tester;
ExtensionTestMessageListener page_script_event_listener_fired(
"listener fired");
static constexpr char kScript[] =
R"(chrome.storage.local.set({"key" : "value"});)";
BackgroundScriptExecutor::ExecuteScriptAsync(profile(), extension->id(),
kScript);
// Confirm that the listener in the page script was fired, but that we do not
// emit a histogram for it.
EXPECT_TRUE(page_script_event_listener_fired.WaitUntilSatisfied());
// Call to storage.onChanged expected.
histogram_tester.ExpectTotalCount(
"Extensions.Events.DispatchToAckTime.ExtensionEventPage3",
/*expected_count=*/0);
}
} // namespace
} // namespace extensions