blob: 7ba07648176c470554af6a11113b31c7b898f95b [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_EXTENSION_HOST_TEST_HELPER_H_
#define EXTENSIONS_BROWSER_EXTENSION_HOST_TEST_HELPER_H_
#include <map>
#include "base/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "extensions/browser/extension_host_registry.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
class BrowserContext;
}
namespace extensions {
// A helper class to wait for particular events associated with ExtensionHosts.
// TODO(devlin): Add enough bits and bobs to use this class within (or to
// replace) our background page-specific test helpers.
class ExtensionHostTestHelper : public ExtensionHostRegistry::Observer {
public:
// Constructs an ExtensionHostTestHelper restricted to a given
// `extension_id`. Only hosts associated with the given `browser_context`
// (or its off/on-the-record counterpart) are considered.
ExtensionHostTestHelper(content::BrowserContext* browser_context,
ExtensionId extension_id);
// As above, but observes *all* extension hosts for the given
// `browser_context`. Prefer the above constructor when possible to eliminate
// possibilities of observing an unrelated event.
explicit ExtensionHostTestHelper(content::BrowserContext* browser_context);
ExtensionHostTestHelper(const ExtensionHostTestHelper&) = delete;
ExtensionHostTestHelper& operator=(const ExtensionHostTestHelper&) = delete;
~ExtensionHostTestHelper() override;
// Restricts this class to only observing ExtensionHosts of the specified
// `view_type`. Other extension hosts matching the event (even from the same
// extension and browser context) will be ignored. This allows tests to wait
// for, e.g., a background page or popup host event to happen.
void RestrictToType(mojom::ViewType view_type);
// Restricts this class to only observing the specified `host`.
void RestrictToHost(const ExtensionHost* host);
// Waits for an ExtensionHost matching the restrictions (if any) to fire the
// corresponding notification.
// NOTE: These WaitFor() methods can return null if the host has already been
// destroyed (which can happen if the host was closed before this method was
// called or if the host is destroyed synchronously from creation), before
// the run loop is quit.
ExtensionHost* WaitForRenderProcessReady() {
return WaitFor(HostEvent::kRenderProcessReady);
}
ExtensionHost* WaitForDocumentElementAvailable() {
return WaitFor(HostEvent::kDocumentElementAvailable);
}
ExtensionHost* WaitForHostCompletedFirstLoad() {
return WaitFor(HostEvent::kCompletedFirstLoad);
}
// NOTE: No return because the ExtensionHost is *always* (obviously)
// destroyed by the time this returns.
void WaitForHostDestroyed() { WaitFor(HostEvent::kDestroyed); }
// Technically, the host can outlive the render process, but it's unlikely to
// be for long. Similar to above, avoid returning the host object.
void WaitForRenderProcessGone() { WaitFor(HostEvent::kRenderProcessGone); }
private:
// The different types of events this class can wait for.
enum class HostEvent {
kRenderProcessReady,
kDocumentElementAvailable,
kCompletedFirstLoad,
kDestroyed,
kRenderProcessGone,
};
// ExtensionHostRegistry::Observer:
void OnExtensionHostRenderProcessReady(
content::BrowserContext* browser_context,
ExtensionHost* host) override;
void OnExtensionHostDocumentElementAvailable(
content::BrowserContext* browser_context,
ExtensionHost* host) override;
void OnExtensionHostCompletedFirstLoad(
content::BrowserContext* browser_context,
ExtensionHost* host) override;
void OnExtensionHostDestroyed(content::BrowserContext* browser_context,
ExtensionHost* host) override;
void OnExtensionHostRenderProcessGone(
content::BrowserContext* browser_context,
ExtensionHost* host) override;
// Waits for the given `event` to happen. This may return immediately if the
// event was already observed. Returns the ExtensionHost corresponding to the
// event if the host is still valid (it may not be, if it has already been
// destroyed).
ExtensionHost* WaitFor(HostEvent event);
// Called when an `event` has been seen, and quits an active run loop if
// we're currently waiting on the event.
void EventSeen(ExtensionHost* host, HostEvent event);
// The event we're currently waiting for, if any.
absl::optional<HostEvent> waiting_for_;
// A closure to quit an active run loop, if we're waiting on a given event.
base::OnceClosure quit_loop_;
// The associated browser context.
const raw_ptr<content::BrowserContext, DanglingUntriaged> browser_context_;
// The ID of the extension whose hosts this helper is watching, if it is
// restricted to a given ID.
const ExtensionId extension_id_;
// The specific type of host this helper is waiting on, if any (nullopt
// implies waiting on any kind of ExtensionHost).
absl::optional<mojom::ViewType> restrict_to_type_;
// The specific host this helper is waiting on, if any (null implies
// waiting on any host).
raw_ptr<const ExtensionHost, DanglingUntriaged> restrict_to_host_ = nullptr;
// The set of all events this helper has seen and their corresponding
// ExtensionHosts. ExtensionHosts are nulled out when they are destroyed, but
// the events stay in the map.
std::map<HostEvent, ExtensionHost*> observed_events_;
base::ScopedObservation<ExtensionHostRegistry,
ExtensionHostRegistry::Observer>
host_registry_observation_{this};
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_EXTENSION_HOST_TEST_HELPER_H_