chromium/chrome/browser/extensions/service_worker_tracking_browsertest.cc

// 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 <tuple>
#include <utility>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.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/browser_test_utils.h"
#include "content/public/test/service_worker_test_helpers.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/service_worker/service_worker_host.h"
#include "extensions/browser/service_worker/service_worker_task_queue.h"
#include "extensions/browser/service_worker/service_worker_test_utils.h"
#include "extensions/browser/service_worker/worker_id_set.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/mojom/service_worker_host.mojom-test-utils.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
#include "url/gurl.h"

// Tests for validating the logic for keeping track of extension service
// workers. This is intentionally broad to include things like:
//   * Starting and stopping state of the service worker
//   * Keeping track of information for the running worker instance

namespace extensions {

namespace {

TestServiceWorkerTaskQueueObserver;

// A helper class that intercepts the
// `ServiceWorkerHost::DidStopServiceWorkerContext()` mojom receiver method and
// does *not* forward the call onto the real `ServiceWorkerHost` implementation.
class ServiceWorkerHostInterceptorForWorkerStop
    : public mojom::ServiceWorkerHostInterceptorForTesting {};

class ServiceWorkerTrackingBrowserTest : public ExtensionBrowserTest {};

// Test class to help verify the tracking of `WorkerId`s in
// `ServiceWorkerTaskQueue` and `WorkerIdSet`.
class ServiceWorkerIdTrackingBrowserTest
    : public ServiceWorkerTrackingBrowserTest {};

// TODO(crbug.com/40936639): improve the stall test by using similar logic to
// ServiceWorkerVersionTest.StallInStopping_DetachThenStart to more closely
// simulate a worker thread delayed in stopping. This will also allow testing
// when the delay causes ProcessManager::RenderProcessExited() to be called
// before ServiceWorkerTaskQueue::OnStopped().

// Tests that when:
//   1) something, other than a worker, keeps the extension renderer process
//     alive (e.g. a tab is open to a page hosted inside the extension) and
//   2) simultaneously the worker is stopped but is stalled/blocked in
//     terminating (preventing notification to //extensions that it has stopped)
//     and
//   3) sometime later a new worker instance is started (e.g. by a new extension
//     event that is sent)
//
// (a.k.a a "delayed worker stop") the //extensions browser layer should only
// track (`WorkerIdSet`) one worker instance (`WorkerId`) (the new worker
// instance). This avoids tracking one or more instances of stopped workers.
// Regression test for crbug.com/40936639.
IN_PROC_BROWSER_TEST_F(
    ServiceWorkerIdTrackingBrowserTest,
    WorkerStalledInStopping_RemovedByBrowserStopNotification) {}

// Test that when a worker is stopped and then restarted we only track one
// instance of `WorkerId` in `WorkerIdSet`. This specific test removes it via
// the renderer stop notification first (but it could also happen in other ways)
// and then ensures the browser stop notification doesn't try to doubly remove
// the `WorkerId`.
IN_PROC_BROWSER_TEST_F(
    ServiceWorkerIdTrackingBrowserTest,
    WorkerNotStalledInStopping_RemovedByRenderStopNotificationFirst) {}

// Test that when a worker is stopped and then restarted we only track one
// instance of `WorkerId` in `WorkerIdSet`. This specific test removes it via
// the browser stop notification first and then ensures the renderer stop
// notification doesn't try to doubly remove the `WorkerId`.
IN_PROC_BROWSER_TEST_F(
    ServiceWorkerIdTrackingBrowserTest,
    WorkerNotStalledInStopping_RemovedByBrowserStopNotificationFirst) {}

ServiceWorkerStopTrackingBrowserTest;

// Test that if a browser stop notification is received before the render stop
// notification (since these things can be triggered independently) the worker's
// browser readiness remains not ready.
IN_PROC_BROWSER_TEST_F(
    ServiceWorkerStopTrackingBrowserTest,
    OnStoppedUpdatesBrowserState_BeforeRenderStopNotification) {}

// Test that if a browser stop notification is received after the render stop
// notification (since these things can be triggered independently)
// it updates the worker's browser readiness information to not ready.
IN_PROC_BROWSER_TEST_F(
    ServiceWorkerStopTrackingBrowserTest,
    OnStoppedUpdatesBrowserState_AfterRenderStopNotification) {}

// Test that if a browser stop notification is received after a worker is
// deactivated (since they can be triggered independently) we don't update the
// worker's browser readiness information.
IN_PROC_BROWSER_TEST_F(ServiceWorkerStopTrackingBrowserTest,
                       OnStoppedRunsAfterDeactivatingWorker) {}

}  // namespace

}  // namespace extensions