blob: a6ad4d3993d1ab8ad63836429e5cd6024c176158 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/extension_host_test_helper.h"
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/run_loop.h"
#include "extensions/browser/extension_host.h"
namespace extensions {
ExtensionHostTestHelper::ExtensionHostTestHelper(
content::BrowserContext* browser_context)
: ExtensionHostTestHelper(browser_context, ExtensionId()) {}
ExtensionHostTestHelper::ExtensionHostTestHelper(
content::BrowserContext* browser_context,
ExtensionId extension_id)
: browser_context_(browser_context),
extension_id_(std::move(extension_id)) {
host_registry_observation_.Observe(
ExtensionHostRegistry::Get(browser_context));
}
ExtensionHostTestHelper::~ExtensionHostTestHelper() = default;
void ExtensionHostTestHelper::RestrictToType(mojom::ViewType type) {
// Restricting to both a specific host and a type is either redundant (if
// the types match) or contradictory (if they don't). Don't allow it.
DCHECK(!restrict_to_host_) << "Can't restrict to both a host and view type.";
restrict_to_type_ = type;
}
void ExtensionHostTestHelper::RestrictToHost(const ExtensionHost* host) {
// Restricting to both a specific host and a type is either redundant (if
// the types match) or contradictory (if they don't). Don't allow it.
DCHECK(!restrict_to_type_) << "Can't restrict to both a host and view type.";
restrict_to_host_ = host;
}
void ExtensionHostTestHelper::OnExtensionHostRenderProcessReady(
content::BrowserContext* browser_context,
ExtensionHost* host) {
EventSeen(host, HostEvent::kRenderProcessReady);
}
void ExtensionHostTestHelper::OnExtensionHostDocumentElementAvailable(
content::BrowserContext* browser_context,
ExtensionHost* host) {
EventSeen(host, HostEvent::kDocumentElementAvailable);
}
void ExtensionHostTestHelper::OnExtensionHostCompletedFirstLoad(
content::BrowserContext* browser_context,
ExtensionHost* host) {
EventSeen(host, HostEvent::kCompletedFirstLoad);
}
void ExtensionHostTestHelper::OnExtensionHostDestroyed(
content::BrowserContext* browser_context,
ExtensionHost* host) {
EventSeen(host, HostEvent::kDestroyed);
}
void ExtensionHostTestHelper::OnExtensionHostRenderProcessGone(
content::BrowserContext* browser_context,
ExtensionHost* host) {
EventSeen(host, HostEvent::kRenderProcessGone);
}
ExtensionHost* ExtensionHostTestHelper::WaitFor(HostEvent event) {
DCHECK(!waiting_for_);
auto iter = observed_events_.find(event);
if (iter != observed_events_.end()) {
// Note: This can be null if the host has been destroyed.
return iter->second;
}
base::RunLoop run_loop;
// Note: We use QuitWhenIdle (instead of Quit) so that any other listeners of
// the relevant events get a chance to run first.
quit_loop_ = run_loop.QuitWhenIdleClosure();
waiting_for_ = event;
run_loop.Run();
DCHECK(base::Contains(observed_events_, event));
// Note: This can still be null here if the corresponding ExtensionHost was
// destroyed. This is always true when waiting for
// OnExtensionHostDestroyed(), but can also happen if the ExtensionHost is
// destroyed while waiting for the run loop to idle.
return observed_events_[event];
}
void ExtensionHostTestHelper::EventSeen(ExtensionHost* host, HostEvent event) {
// Check if the host matches our restrictions.
// Note: We have to check the browser context explicitly because the
// ExtensionHostRegistry is shared between on- and off-the-record profiles,
// so the `host`'s browser context may not be the same as the one associated
// with this object in the case of split mode extensions.
if (host->browser_context() != browser_context_)
return;
if (!extension_id_.empty() && host->extension_id() != extension_id_)
return;
if (restrict_to_type_ && host->extension_host_type() != restrict_to_type_)
return;
if (restrict_to_host_ && host != restrict_to_host_)
return;
if (event == HostEvent::kDestroyed) {
// Clean up all old pointers to the ExtensionHost on its destruction.
for (auto& kv : observed_events_) {
if (kv.second == host)
kv.second = nullptr;
}
// Ensure we don't put a new pointer for the host into the map.
host = nullptr;
}
observed_events_[event] = host;
if (waiting_for_ == event) {
DCHECK(quit_loop_);
waiting_for_.reset();
std::move(quit_loop_).Run();
}
}
} // namespace extensions