Reland "[Extensions] Regression test: WebRequestAPI ResetURLLoaderFactories bug"
Test needs to be skipped when `kForceWebRequestProxyForTest` is enabled
(for example, in `network_service_linux` trybot).
That's because factories are never reset when this feature is enabled.
See
https://crsrc.org/c/extensions/browser/api/web_request/web_request_api.cc;drc=e1dda381f52e3a553444a434e89139e016675701;l=680
This is a reland of commit e1dda381f5
Original change's description:
> [Extensions] Regression test: WebRequestAPI ResetURLLoaderFactories bug
>
> This change adds a regression test for the referenced bug.
>
> It tests both that enabling the `DeferResetURLLoaderFactories` feature
> flag resolves the issue, and that disabling it causes the issue.
>
> Design doc: http://go/defer-reset-url-loader-factories
>
> Bug: 394523691
> Change-Id: I46671553e4c0631416b76654c476cb6e29dd65ae
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6424484
> Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
> Commit-Queue: Andrea Orru <andreaorru@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1443868}
Bug: 394523691
Change-Id: I21b26c9a60a470be52148a6c11de474b38a4b496
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6438674
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Reviewed-by: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Commit-Queue: Andrea Orru <andreaorru@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1444520}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
6afbda49d3
commit
61f048644f
chrome/browser/extensions/api/web_request
extensions
@@ -37,6 +37,7 @@
|
|||||||
#include "chrome/browser/browser_process.h"
|
#include "chrome/browser/browser_process.h"
|
||||||
#include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h"
|
#include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h"
|
||||||
#include "chrome/browser/devtools/url_constants.h"
|
#include "chrome/browser/devtools/url_constants.h"
|
||||||
|
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
|
||||||
#include "chrome/browser/extensions/error_console/error_console.h"
|
#include "chrome/browser/extensions/error_console/error_console.h"
|
||||||
#include "chrome/browser/extensions/error_console/error_console_test_observer.h"
|
#include "chrome/browser/extensions/error_console/error_console_test_observer.h"
|
||||||
#include "chrome/browser/extensions/extension_apitest.h"
|
#include "chrome/browser/extensions/extension_apitest.h"
|
||||||
@@ -128,6 +129,7 @@
|
|||||||
#include "services/network/test/test_url_loader_client.h"
|
#include "services/network/test/test_url_loader_client.h"
|
||||||
#include "third_party/blink/public/common/features.h"
|
#include "third_party/blink/public/common/features.h"
|
||||||
#include "third_party/blink/public/common/input/web_input_event.h"
|
#include "third_party/blink/public/common/input/web_input_event.h"
|
||||||
|
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
|
||||||
#include "ui/webui/untrusted_web_ui_browsertest_util.h" // nogncheck
|
#include "ui/webui/untrusted_web_ui_browsertest_util.h" // nogncheck
|
||||||
#include "url/origin.h"
|
#include "url/origin.h"
|
||||||
|
|
||||||
@@ -2609,7 +2611,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType,
|
|||||||
content::EvalJs(web_contents, "document.body.textContent.trim();"));
|
content::EvalJs(web_contents, "document.body.textContent.trim();"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A callback allow waiting for responses to complete with an expected status
|
// A callback allow waiting for a response to complete with an expected status
|
||||||
// and given content.
|
// and given content.
|
||||||
auto make_browser_request =
|
auto make_browser_request =
|
||||||
[](network::mojom::URLLoaderFactory* url_loader_factory, const GURL& url,
|
[](network::mojom::URLLoaderFactory* url_loader_factory, const GURL& url,
|
||||||
@@ -7564,5 +7566,195 @@ IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, RecordUkmOnNavigation) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allows test to wait for the failure of a worker registration.
|
||||||
|
class WorkerRegistrationFailureObserver
|
||||||
|
: public ServiceWorkerTaskQueue::TestObserver {
|
||||||
|
public:
|
||||||
|
explicit WorkerRegistrationFailureObserver(const ExtensionId extension_id)
|
||||||
|
: extension_id_(extension_id) {
|
||||||
|
ServiceWorkerTaskQueue::SetObserverForTest(this);
|
||||||
|
}
|
||||||
|
~WorkerRegistrationFailureObserver() override {
|
||||||
|
ServiceWorkerTaskQueue::SetObserverForTest(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
blink::ServiceWorkerStatusCode WaitForWorkerRegistrationFailure() {
|
||||||
|
if (!status_code_) {
|
||||||
|
SCOPED_TRACE("Waiting for worker registration to fail");
|
||||||
|
failure_loop_.Run();
|
||||||
|
}
|
||||||
|
return *status_code_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnWorkerRegistrationFailed(
|
||||||
|
const ExtensionId& extension_id,
|
||||||
|
blink::ServiceWorkerStatusCode status_code) override {
|
||||||
|
if (extension_id == extension_id_) {
|
||||||
|
status_code_ = status_code;
|
||||||
|
failure_loop_.Quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtensionId extension_id_;
|
||||||
|
base::RunLoop failure_loop_;
|
||||||
|
std::optional<blink::ServiceWorkerStatusCode> status_code_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allows test to wait for the call of `ResetURLLoaderFactories()` in
|
||||||
|
// WebRequestAPI.
|
||||||
|
class URLLoaderFactoriesResetWaiter : public WebRequestAPI::TestObserver {
|
||||||
|
public:
|
||||||
|
URLLoaderFactoriesResetWaiter() { WebRequestAPI::SetObserverForTest(this); }
|
||||||
|
~URLLoaderFactoriesResetWaiter() override {
|
||||||
|
WebRequestAPI::SetObserverForTest(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
URLLoaderFactoriesResetWaiter(const URLLoaderFactoriesResetWaiter&) = delete;
|
||||||
|
URLLoaderFactoriesResetWaiter& operator=(
|
||||||
|
const URLLoaderFactoriesResetWaiter&) = delete;
|
||||||
|
|
||||||
|
void WaitForResetURLLoaderFactoriesCalled() {
|
||||||
|
SCOPED_TRACE("Waiting for ResetURLLoaderFactories to be called");
|
||||||
|
url_loader_factory_reset_runloop_.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void OnDidResetURLLoaderFactories() override {
|
||||||
|
url_loader_factory_reset_runloop_.Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
base::RunLoop url_loader_factory_reset_runloop_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories
|
||||||
|
: public ManifestV3WebRequestApiTest,
|
||||||
|
public testing::WithParamInterface<bool> {
|
||||||
|
public:
|
||||||
|
ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories() {
|
||||||
|
feature_list_.InitWithFeatureState(
|
||||||
|
extensions_features::kDeferResetURLLoaderFactories, GetParam());
|
||||||
|
}
|
||||||
|
~ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories() override =
|
||||||
|
default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
base::test::ScopedFeatureList feature_list_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tests that the call to `ResetURLLoaderFactories()` performed by WebRequestAPI
|
||||||
|
// doesn't break the registration process of other extensions.
|
||||||
|
// Regression test for https://crbug.com/394523691.
|
||||||
|
IN_PROC_BROWSER_TEST_P(
|
||||||
|
ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories,
|
||||||
|
ResetURLLoaderFactoryDoesntBreakRegistration) {
|
||||||
|
// Skip if the proxy is forced since factories will not be reset in that case.
|
||||||
|
if (base::FeatureList::IsEnabled(
|
||||||
|
extensions_features::kForceWebRequestProxyForTest)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool feature_enabled = GetParam();
|
||||||
|
ASSERT_TRUE(StartEmbeddedTestServer());
|
||||||
|
|
||||||
|
// A simple extension that sends a message and waits for a response in its
|
||||||
|
// background script.
|
||||||
|
const ExtensionId extension_id("iegclhlplifhodhkoafiokenjoapiobj");
|
||||||
|
static constexpr const char kKey[] =
|
||||||
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjzv7dI7Ygyh67VHE1DdidudpYf8P"
|
||||||
|
"Ffv8iucWvzO+3xpF/Dm5xNo7aQhPNiEaNfHwJQ7lsp4gc+C+4bbaVewBFspTruoSJhZc5uEf"
|
||||||
|
"qxwovJwN+v1/SUFXTXQmQBv6gs0qZB4gBbl4caNQBlqrFwAMNisnu1V6UROna8rOJQ90D7Nv"
|
||||||
|
"7TCwoVPKBfVshpFjdDOTeBg4iLctO3S/06QYqaTDrwVceSyHkVkvzBY6tc6mnYX0RZu78J9i"
|
||||||
|
"L8bdqwfllOhs69cqoHHgrLdI6JdOyiuh6pBP6vxMlzSKWJ3YTNjaQTPwfOYaLMuzdl0v+Ydz"
|
||||||
|
"afIzV9zwe4Xiskk+5JNGt8b2rQIDAQAB";
|
||||||
|
static constexpr char kManifest[] =
|
||||||
|
R"({
|
||||||
|
"name": "TestExtension",
|
||||||
|
"manifest_version": 3,
|
||||||
|
"version": "0.1",
|
||||||
|
"key": "%s",
|
||||||
|
"background": {"service_worker": "background.js"}
|
||||||
|
})";
|
||||||
|
static constexpr char kBackgroundJs[] =
|
||||||
|
R"(chrome.test.sendMessage('will_receive').then(() => {
|
||||||
|
console.log('received');
|
||||||
|
}))";
|
||||||
|
TestExtensionDir extension_dir;
|
||||||
|
extension_dir.WriteManifest(base::StringPrintf(kManifest, kKey));
|
||||||
|
extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs);
|
||||||
|
|
||||||
|
ServiceWorkerTaskQueue* task_queue = ServiceWorkerTaskQueue::Get(profile());
|
||||||
|
ASSERT_TRUE(task_queue);
|
||||||
|
WebRequestAPI* web_request_api =
|
||||||
|
BrowserContextKeyedAPIFactory<WebRequestAPI>::Get(profile());
|
||||||
|
ASSERT_TRUE(web_request_api);
|
||||||
|
|
||||||
|
// Listen to "will_receive" message from the extension.
|
||||||
|
ExtensionTestMessageListener will_receive_listener("will_receive",
|
||||||
|
ReplyBehavior::kWillReply);
|
||||||
|
// Listen to the completion of the registration storage.
|
||||||
|
service_worker_test_utils::TestServiceWorkerContextObserver
|
||||||
|
registration_observer(profile());
|
||||||
|
// Listen for a failure in the worker registration.
|
||||||
|
WorkerRegistrationFailureObserver worker_failure_observer(extension_id);
|
||||||
|
|
||||||
|
// Asynchronously load the extension so we can wait for a step in the loading
|
||||||
|
// process in the test.
|
||||||
|
std::optional<base::UnguessableToken> activation_token;
|
||||||
|
ChromeTestExtensionLoader(profile()).LoadUnpackedExtensionAsync(
|
||||||
|
extension_dir.UnpackedPath(),
|
||||||
|
base::BindLambdaForTesting([&](const Extension* extension) {
|
||||||
|
ASSERT_TRUE(extension);
|
||||||
|
activation_token =
|
||||||
|
task_queue->GetCurrentActivationToken(extension->id());
|
||||||
|
ASSERT_TRUE(activation_token.has_value());
|
||||||
|
}));
|
||||||
|
// ...and wait for the moment right after the worker is requested to start
|
||||||
|
// during the registration process.
|
||||||
|
registration_observer.WaitForStartWorkerMessageSent();
|
||||||
|
URLLoaderFactoriesResetWaiter url_loader_factories_reset_waiter;
|
||||||
|
// Simulate the effect of loading an extension with WebRequestAPI permissions.
|
||||||
|
// In other words, make sure we're proxying for the current profile.
|
||||||
|
// This will cause WebRequestAPI to attempt calling
|
||||||
|
// `ResetURLLoaderFactories()`. Because the extension is still in the early
|
||||||
|
// phases of starting its worker here, this would break its registration
|
||||||
|
// before it has a chance of being completed and stored.
|
||||||
|
// Instead, the call to `ResetURLLoaderFactories()` will be deferred.
|
||||||
|
// NOTE: We simulate the call to `ResetURLLoaderFactories()` rather
|
||||||
|
// than loading an extension with WebRequestAPI permissions, as that
|
||||||
|
// would take too long and won't trigger the bug in all cases.
|
||||||
|
web_request_api->ForceProxyForTesting();
|
||||||
|
|
||||||
|
if (feature_enabled) {
|
||||||
|
// DeferResetURLLoaderFactories feature enabled: expect successful
|
||||||
|
// execution. Check that the worker is still running and functional.
|
||||||
|
registration_observer.WaitForWorkerStarted();
|
||||||
|
std::optional<WorkerId> worker_id = GetWorkerIdForExtension(extension_id);
|
||||||
|
EXPECT_TRUE(worker_id);
|
||||||
|
SCOPED_TRACE(
|
||||||
|
"Waiting for extension background to signal that it can send messages");
|
||||||
|
ASSERT_TRUE(will_receive_listener.WaitUntilSatisfied());
|
||||||
|
will_receive_listener.Reply("go");
|
||||||
|
// Check `ResetURLLoaderFactories()` is called after registration is stored.
|
||||||
|
registration_observer.WaitForRegistrationStored();
|
||||||
|
url_loader_factories_reset_waiter.WaitForResetURLLoaderFactoriesCalled();
|
||||||
|
} else {
|
||||||
|
// DeferResetURLLoaderFactories feature disabled: expect worker registration
|
||||||
|
// to fail. We have observed that the registration can fail with either
|
||||||
|
// `kErrorStartWorkerFailed` or `kErrorNetwork` depending on when exactly
|
||||||
|
// it's interrupted.
|
||||||
|
auto status_code =
|
||||||
|
worker_failure_observer.WaitForWorkerRegistrationFailure();
|
||||||
|
EXPECT_NE(status_code, blink::ServiceWorkerStatusCode::kOk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
All,
|
||||||
|
ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories,
|
||||||
|
testing::Bool());
|
||||||
|
|
||||||
#endif // !BUILDFLAG(IS_ANDROID)
|
#endif // !BUILDFLAG(IS_ANDROID)
|
||||||
|
|
||||||
} // namespace extensions
|
} // namespace extensions
|
||||||
|
@@ -93,9 +93,7 @@ namespace web_request = api::web_request;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
BASE_FEATURE(kDeferResetURLLoaderFactories,
|
WebRequestAPI::TestObserver* g_test_observer = nullptr;
|
||||||
"DeferResetURLLoaderFactories",
|
|
||||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
|
||||||
|
|
||||||
// These values are persisted to logs. Entries should not be renumbered and
|
// These values are persisted to logs. Entries should not be renumbered and
|
||||||
// numeric values should never be reused.
|
// numeric values should never be reused.
|
||||||
@@ -344,7 +342,8 @@ WebRequestAPI::WebRequestAPI(content::BrowserContext* context)
|
|||||||
// We only have to observe ServiceWorkerTaskQueue and react to
|
// We only have to observe ServiceWorkerTaskQueue and react to
|
||||||
// `OnAllRegistrationsStored` if we need to defer calls to
|
// `OnAllRegistrationsStored` if we need to defer calls to
|
||||||
// `ResetURLLoaderFactories` to after registration storage.
|
// `ResetURLLoaderFactories` to after registration storage.
|
||||||
if (base::FeatureList::IsEnabled(kDeferResetURLLoaderFactories)) {
|
if (base::FeatureList::IsEnabled(
|
||||||
|
extensions_features::kDeferResetURLLoaderFactories)) {
|
||||||
service_worker_task_queue_observation_.Observe(
|
service_worker_task_queue_observation_.Observe(
|
||||||
ServiceWorkerTaskQueue::Get(browser_context_));
|
ServiceWorkerTaskQueue::Get(browser_context_));
|
||||||
}
|
}
|
||||||
@@ -373,6 +372,15 @@ WebRequestAPI::GetFactoryInstance() {
|
|||||||
return g_factory.Pointer();
|
return g_factory.Pointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void WebRequestAPI::SetObserverForTest(TestObserver* observer) {
|
||||||
|
g_test_observer = observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRequestAPI::TestObserver::TestObserver() = default;
|
||||||
|
|
||||||
|
WebRequestAPI::TestObserver::~TestObserver() = default;
|
||||||
|
|
||||||
void WebRequestAPI::OnListenerRemoved(const EventListenerInfo& details) {
|
void WebRequestAPI::OnListenerRemoved(const EventListenerInfo& details) {
|
||||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||||
|
|
||||||
@@ -715,10 +723,18 @@ bool WebRequestAPI::HasExtraHeadersListenerForTesting() {
|
|||||||
->HasAnyExtraHeadersListener(browser_context_);
|
->HasAnyExtraHeadersListener(browser_context_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebRequestAPI::ResetURLLoaderFactories() {
|
||||||
|
browser_context_->GetDefaultStoragePartition()->ResetURLLoaderFactories();
|
||||||
|
if (g_test_observer) {
|
||||||
|
g_test_observer->OnDidResetURLLoaderFactories();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WebRequestAPI::UpdateMayHaveProxies() {
|
void WebRequestAPI::UpdateMayHaveProxies() {
|
||||||
bool may_have_proxies = MayHaveProxies();
|
bool may_have_proxies = MayHaveProxies();
|
||||||
if (!may_have_proxies_ && may_have_proxies) {
|
if (!may_have_proxies_ && may_have_proxies) {
|
||||||
if (base::FeatureList::IsEnabled(kDeferResetURLLoaderFactories) &&
|
if (base::FeatureList::IsEnabled(
|
||||||
|
extensions_features::kDeferResetURLLoaderFactories) &&
|
||||||
has_pending_worker_registrations_) {
|
has_pending_worker_registrations_) {
|
||||||
// If any service worker registration is still in flight (started, but not
|
// If any service worker registration is still in flight (started, but not
|
||||||
// stored), it's not safe to call `ResetURLLoaderFactories`, since it
|
// stored), it's not safe to call `ResetURLLoaderFactories`, since it
|
||||||
@@ -731,7 +747,7 @@ void WebRequestAPI::UpdateMayHaveProxies() {
|
|||||||
// can be reset safely.
|
// can be reset safely.
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we can safely call it now.
|
// Otherwise, we can safely call it now.
|
||||||
browser_context_->GetDefaultStoragePartition()->ResetURLLoaderFactories();
|
ResetURLLoaderFactories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
may_have_proxies_ = may_have_proxies;
|
may_have_proxies_ = may_have_proxies;
|
||||||
@@ -761,7 +777,7 @@ void WebRequestAPI::OnAllRegistrationsStored() {
|
|||||||
service_worker_context_observation_.RemoveAllObservations();
|
service_worker_context_observation_.RemoveAllObservations();
|
||||||
// ...and reset the URLLoaderFactories if we haven't done it already.
|
// ...and reset the URLLoaderFactories if we haven't done it already.
|
||||||
if (deferred_reset_url_loader_factories_) {
|
if (deferred_reset_url_loader_factories_) {
|
||||||
browser_context_->GetDefaultStoragePartition()->ResetURLLoaderFactories();
|
ResetURLLoaderFactories();
|
||||||
deferred_reset_url_loader_factories_ = false;
|
deferred_reset_url_loader_factories_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -199,6 +199,19 @@ class WebRequestAPI : public BrowserContextKeyedAPI,
|
|||||||
static BrowserContextKeyedAPIFactory<WebRequestAPI>* GetFactoryInstance();
|
static BrowserContextKeyedAPIFactory<WebRequestAPI>* GetFactoryInstance();
|
||||||
void Shutdown() override;
|
void Shutdown() override;
|
||||||
|
|
||||||
|
class TestObserver {
|
||||||
|
public:
|
||||||
|
TestObserver();
|
||||||
|
TestObserver(const TestObserver&) = delete;
|
||||||
|
TestObserver& operator=(const TestObserver&) = delete;
|
||||||
|
virtual ~TestObserver();
|
||||||
|
|
||||||
|
// Called when `ResetURLLoaderFactories()` has been performed.
|
||||||
|
virtual void OnDidResetURLLoaderFactories() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void SetObserverForTest(TestObserver* observer);
|
||||||
|
|
||||||
// EventRouter::Observer overrides:
|
// EventRouter::Observer overrides:
|
||||||
void OnListenerRemoved(const EventListenerInfo& details) override;
|
void OnListenerRemoved(const EventListenerInfo& details) override;
|
||||||
|
|
||||||
@@ -308,6 +321,8 @@ class WebRequestAPI : public BrowserContextKeyedAPI,
|
|||||||
// URLLoaderFactories if so.
|
// URLLoaderFactories if so.
|
||||||
void UpdateMayHaveProxies();
|
void UpdateMayHaveProxies();
|
||||||
|
|
||||||
|
void ResetURLLoaderFactories();
|
||||||
|
|
||||||
// ExtensionRegistryObserver implementation.
|
// ExtensionRegistryObserver implementation.
|
||||||
void OnExtensionLoaded(content::BrowserContext* browser_context,
|
void OnExtensionLoaded(content::BrowserContext* browser_context,
|
||||||
const Extension* extension) override;
|
const Extension* extension) override;
|
||||||
|
@@ -732,6 +732,10 @@ void ServiceWorkerTaskQueue::DidRegisterServiceWorker(
|
|||||||
const bool success = IsWorkerRegistrationSuccess(status_code);
|
const bool success = IsWorkerRegistrationSuccess(status_code);
|
||||||
base::UmaHistogramBoolean(
|
base::UmaHistogramBoolean(
|
||||||
"Extensions.ServiceWorkerBackground.WorkerRegistrationState2", success);
|
"Extensions.ServiceWorkerBackground.WorkerRegistrationState2", success);
|
||||||
|
if (!success && g_test_observer) {
|
||||||
|
g_test_observer->OnWorkerRegistrationFailed(context_id.extension_id,
|
||||||
|
status_code);
|
||||||
|
}
|
||||||
|
|
||||||
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
|
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
|
||||||
const ExtensionId& extension_id = context_id.extension_id;
|
const ExtensionId& extension_id = context_id.extension_id;
|
||||||
|
@@ -348,6 +348,10 @@ class ServiceWorkerTaskQueue
|
|||||||
// it ultimately succeeds or fails).
|
// it ultimately succeeds or fails).
|
||||||
virtual void RequestedWorkerStart(const ExtensionId& extension_id) {}
|
virtual void RequestedWorkerStart(const ExtensionId& extension_id) {}
|
||||||
|
|
||||||
|
virtual void OnWorkerRegistrationFailed(
|
||||||
|
const ExtensionId& extension_id,
|
||||||
|
blink::ServiceWorkerStatusCode status_code) {}
|
||||||
|
|
||||||
virtual void DidStartWorkerFail(
|
virtual void DidStartWorkerFail(
|
||||||
const ExtensionId& extension_id,
|
const ExtensionId& extension_id,
|
||||||
size_t num_pending_tasks,
|
size_t num_pending_tasks,
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
#include "content/public/browser/storage_partition.h"
|
#include "content/public/browser/storage_partition.h"
|
||||||
#include "extensions/common/constants.h"
|
#include "extensions/common/constants.h"
|
||||||
#include "extensions/common/extension.h"
|
#include "extensions/common/extension.h"
|
||||||
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
#include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom-forward.h"
|
#include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom-forward.h"
|
||||||
|
|
||||||
namespace extensions {
|
namespace extensions {
|
||||||
@@ -45,6 +46,7 @@ TestServiceWorkerContextObserver::TestServiceWorkerContextObserver(
|
|||||||
: extension_scope_(GetScopeForExtensionID(std::move(extension_id))),
|
: extension_scope_(GetScopeForExtensionID(std::move(extension_id))),
|
||||||
context_(context) {
|
context_(context) {
|
||||||
scoped_observation_.Observe(context_);
|
scoped_observation_.Observe(context_);
|
||||||
|
scoped_sync_observation_.Observe(context_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TestServiceWorkerContextObserver::TestServiceWorkerContextObserver(
|
TestServiceWorkerContextObserver::TestServiceWorkerContextObserver(
|
||||||
@@ -53,6 +55,7 @@ TestServiceWorkerContextObserver::TestServiceWorkerContextObserver(
|
|||||||
: extension_scope_(GetScopeForExtensionID(std::move(extension_id))),
|
: extension_scope_(GetScopeForExtensionID(std::move(extension_id))),
|
||||||
context_(GetServiceWorkerContext(browser_context)) {
|
context_(GetServiceWorkerContext(browser_context)) {
|
||||||
scoped_observation_.Observe(context_);
|
scoped_observation_.Observe(context_);
|
||||||
|
scoped_sync_observation_.Observe(context_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TestServiceWorkerContextObserver::~TestServiceWorkerContextObserver() = default;
|
TestServiceWorkerContextObserver::~TestServiceWorkerContextObserver() = default;
|
||||||
@@ -62,13 +65,26 @@ void TestServiceWorkerContextObserver::WaitForRegistrationStored() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCOPED_TRACE("Waiting for worker registration to be stored");
|
||||||
base::RunLoop run_loop;
|
base::RunLoop run_loop;
|
||||||
stored_quit_closure_ = run_loop.QuitClosure();
|
stored_quit_closure_ = run_loop.QuitClosure();
|
||||||
run_loop.Run();
|
run_loop.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t TestServiceWorkerContextObserver::WaitForStartWorkerMessageSent() {
|
||||||
|
if (!start_message_sent_version_id_) {
|
||||||
|
SCOPED_TRACE("Waiting for StartWorker message to be sent");
|
||||||
|
base::RunLoop run_loop;
|
||||||
|
start_message_sent_quit_closure_ = run_loop.QuitClosure();
|
||||||
|
run_loop.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *start_message_sent_version_id_;
|
||||||
|
}
|
||||||
|
|
||||||
int64_t TestServiceWorkerContextObserver::WaitForWorkerStarted() {
|
int64_t TestServiceWorkerContextObserver::WaitForWorkerStarted() {
|
||||||
if (!running_version_id_) {
|
if (!running_version_id_) {
|
||||||
|
SCOPED_TRACE("Waiting for worker to be started");
|
||||||
base::RunLoop run_loop;
|
base::RunLoop run_loop;
|
||||||
started_quit_closure_ = run_loop.QuitClosure();
|
started_quit_closure_ = run_loop.QuitClosure();
|
||||||
run_loop.Run();
|
run_loop.Run();
|
||||||
@@ -84,6 +100,7 @@ int64_t TestServiceWorkerContextObserver::WaitForWorkerStopped() {
|
|||||||
return *stopped_version_id_;
|
return *stopped_version_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCOPED_TRACE("Waiting for worker to be stopped");
|
||||||
base::RunLoop run_loop;
|
base::RunLoop run_loop;
|
||||||
stopped_quit_closure_ = run_loop.QuitClosure();
|
stopped_quit_closure_ = run_loop.QuitClosure();
|
||||||
run_loop.Run();
|
run_loop.Run();
|
||||||
@@ -93,6 +110,7 @@ int64_t TestServiceWorkerContextObserver::WaitForWorkerStopped() {
|
|||||||
|
|
||||||
int64_t TestServiceWorkerContextObserver::WaitForWorkerActivated() {
|
int64_t TestServiceWorkerContextObserver::WaitForWorkerActivated() {
|
||||||
if (!activated_version_id_) {
|
if (!activated_version_id_) {
|
||||||
|
SCOPED_TRACE("Waiting for worker to be activated");
|
||||||
base::RunLoop run_loop;
|
base::RunLoop run_loop;
|
||||||
activated_quit_closure_ = run_loop.QuitClosure();
|
activated_quit_closure_ = run_loop.QuitClosure();
|
||||||
run_loop.Run();
|
run_loop.Run();
|
||||||
@@ -123,6 +141,19 @@ void TestServiceWorkerContextObserver::OnRegistrationStored(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestServiceWorkerContextObserver::OnStartWorkerMessageSent(
|
||||||
|
int64_t version_id,
|
||||||
|
const GURL& scope) {
|
||||||
|
if (extension_scope_ && extension_scope_ != scope) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_message_sent_version_id_ = version_id;
|
||||||
|
if (start_message_sent_quit_closure_) {
|
||||||
|
std::move(start_message_sent_quit_closure_).Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestServiceWorkerContextObserver::OnVersionStartedRunning(
|
void TestServiceWorkerContextObserver::OnVersionStartedRunning(
|
||||||
int64_t version_id,
|
int64_t version_id,
|
||||||
const content::ServiceWorkerRunningInfo& running_info) {
|
const content::ServiceWorkerRunningInfo& running_info) {
|
||||||
@@ -161,6 +192,7 @@ void TestServiceWorkerContextObserver::OnVersionActivated(int64_t version_id,
|
|||||||
void TestServiceWorkerContextObserver::OnDestruct(
|
void TestServiceWorkerContextObserver::OnDestruct(
|
||||||
content::ServiceWorkerContext* context) {
|
content::ServiceWorkerContext* context) {
|
||||||
scoped_observation_.Reset();
|
scoped_observation_.Reset();
|
||||||
|
scoped_sync_observation_.Reset();
|
||||||
context_ = nullptr;
|
context_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,7 +39,8 @@ content::ServiceWorkerContext* GetServiceWorkerContext(
|
|||||||
// Note: This class only works well when there is a *single* service worker
|
// Note: This class only works well when there is a *single* service worker
|
||||||
// being registered. We could extend this to track multiple workers.
|
// being registered. We could extend this to track multiple workers.
|
||||||
class TestServiceWorkerContextObserver
|
class TestServiceWorkerContextObserver
|
||||||
: public content::ServiceWorkerContextObserver {
|
: public content::ServiceWorkerContextObserver,
|
||||||
|
public content::ServiceWorkerContextObserverSynchronous {
|
||||||
public:
|
public:
|
||||||
explicit TestServiceWorkerContextObserver(
|
explicit TestServiceWorkerContextObserver(
|
||||||
content::ServiceWorkerContext* context,
|
content::ServiceWorkerContext* context,
|
||||||
@@ -58,6 +59,11 @@ class TestServiceWorkerContextObserver
|
|||||||
// scope to be stored.
|
// scope to be stored.
|
||||||
void WaitForRegistrationStored();
|
void WaitForRegistrationStored();
|
||||||
|
|
||||||
|
// Wait for OnStartWorkerMessageSent event is triggered, so that the observer
|
||||||
|
// captures the version ID of the service worker that is about to be started.
|
||||||
|
// Returns the version ID.
|
||||||
|
int64_t WaitForStartWorkerMessageSent();
|
||||||
|
|
||||||
// Wait for OnVersionStartedRunning event is triggered, so that the observer
|
// Wait for OnVersionStartedRunning event is triggered, so that the observer
|
||||||
// captures the running service worker version ID. Returns the version ID.
|
// captures the running service worker version ID. Returns the version ID.
|
||||||
int64_t WaitForWorkerStarted();
|
int64_t WaitForWorkerStarted();
|
||||||
@@ -89,6 +95,9 @@ class TestServiceWorkerContextObserver
|
|||||||
void OnVersionActivated(int64_t version_id, const GURL& scope) override;
|
void OnVersionActivated(int64_t version_id, const GURL& scope) override;
|
||||||
void OnDestruct(content::ServiceWorkerContext* context) override;
|
void OnDestruct(content::ServiceWorkerContext* context) override;
|
||||||
|
|
||||||
|
// ServiceWorkerContextObserverSynchronous:
|
||||||
|
void OnStartWorkerMessageSent(int64_t version_id, const GURL& scope) override;
|
||||||
|
|
||||||
using RegistrationsMap = std::map<GURL, int>;
|
using RegistrationsMap = std::map<GURL, int>;
|
||||||
|
|
||||||
RegistrationsMap registrations_completed_map_;
|
RegistrationsMap registrations_completed_map_;
|
||||||
@@ -96,6 +105,7 @@ class TestServiceWorkerContextObserver
|
|||||||
// Multiple events may come in so we must wait for the specific event
|
// Multiple events may come in so we must wait for the specific event
|
||||||
// to be triggered.
|
// to be triggered.
|
||||||
base::OnceClosure activated_quit_closure_;
|
base::OnceClosure activated_quit_closure_;
|
||||||
|
base::OnceClosure start_message_sent_quit_closure_;
|
||||||
base::OnceClosure started_quit_closure_;
|
base::OnceClosure started_quit_closure_;
|
||||||
base::OnceClosure stored_quit_closure_;
|
base::OnceClosure stored_quit_closure_;
|
||||||
base::OnceClosure stopped_quit_closure_;
|
base::OnceClosure stopped_quit_closure_;
|
||||||
@@ -104,6 +114,7 @@ class TestServiceWorkerContextObserver
|
|||||||
|
|
||||||
std::optional<bool> registration_stored_;
|
std::optional<bool> registration_stored_;
|
||||||
std::optional<int64_t> activated_version_id_;
|
std::optional<int64_t> activated_version_id_;
|
||||||
|
std::optional<int64_t> start_message_sent_version_id_;
|
||||||
std::optional<int64_t> running_version_id_;
|
std::optional<int64_t> running_version_id_;
|
||||||
std::optional<int64_t> stopped_version_id_;
|
std::optional<int64_t> stopped_version_id_;
|
||||||
|
|
||||||
@@ -112,6 +123,10 @@ class TestServiceWorkerContextObserver
|
|||||||
base::ScopedObservation<content::ServiceWorkerContext,
|
base::ScopedObservation<content::ServiceWorkerContext,
|
||||||
content::ServiceWorkerContextObserver>
|
content::ServiceWorkerContextObserver>
|
||||||
scoped_observation_{this};
|
scoped_observation_{this};
|
||||||
|
|
||||||
|
base::ScopedObservation<content::ServiceWorkerContext,
|
||||||
|
content::ServiceWorkerContextObserverSynchronous>
|
||||||
|
scoped_sync_observation_{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Observes ProcessManager::UnregisterServiceWorker.
|
// Observes ProcessManager::UnregisterServiceWorker.
|
||||||
|
@@ -64,6 +64,10 @@ BASE_FEATURE(kCheckingNoExtensionIdInExtensionIpcs,
|
|||||||
"EMF_NO_EXTENSION_ID_FOR_EXTENSION_SOURCE",
|
"EMF_NO_EXTENSION_ID_FOR_EXTENSION_SOURCE",
|
||||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||||
|
|
||||||
|
BASE_FEATURE(kDeferResetURLLoaderFactories,
|
||||||
|
"DeferResetURLLoaderFactories",
|
||||||
|
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||||
|
|
||||||
BASE_FEATURE(kEnableWebHidInWebView,
|
BASE_FEATURE(kEnableWebHidInWebView,
|
||||||
"EnableWebHidInWebView",
|
"EnableWebHidInWebView",
|
||||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||||
|
@@ -85,6 +85,11 @@ BASE_DECLARE_FEATURE(kAllowWithholdingExtensionPermissionsOnInstall);
|
|||||||
// extension).
|
// extension).
|
||||||
BASE_DECLARE_FEATURE(kCheckingNoExtensionIdInExtensionIpcs);
|
BASE_DECLARE_FEATURE(kCheckingNoExtensionIdInExtensionIpcs);
|
||||||
|
|
||||||
|
// If enabled, defers the execution of WebRequestAPI call of
|
||||||
|
// `ResetURLLoaderFactories()` to when there's no extension service worker
|
||||||
|
// registrations in flight, to avoid disrupting the worker(s) registration(s).
|
||||||
|
BASE_DECLARE_FEATURE(kDeferResetURLLoaderFactories);
|
||||||
|
|
||||||
// If enabled, <webview>s will be allowed to request permission from an
|
// If enabled, <webview>s will be allowed to request permission from an
|
||||||
// embedding Chrome App to request access to Human Interface Devices.
|
// embedding Chrome App to request access to Human Interface Devices.
|
||||||
BASE_DECLARE_FEATURE(kEnableWebHidInWebView);
|
BASE_DECLARE_FEATURE(kEnableWebHidInWebView);
|
||||||
|
Reference in New Issue
Block a user