blob: 2dbdb4cca9db78c6559639a104b316b89af3b839 [file] [log] [blame]
// Copyright 2020 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/test/extension_background_page_waiter.h"
#include "base/scoped_observer.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_host_observer.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/process_manager_observer.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/manifest_handlers/incognito_info.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
ExtensionBackgroundPageWaiter::ExtensionBackgroundPageWaiter(
content::BrowserContext* browser_context,
const Extension& extension)
: browser_context_(browser_context),
extension_(base::WrapRefCounted(&extension)) {}
ExtensionBackgroundPageWaiter::~ExtensionBackgroundPageWaiter() = default;
void ExtensionBackgroundPageWaiter::Wait() {
if (browser_context_->IsOffTheRecord() &&
!IncognitoInfo::IsSplitMode(extension_.get())) {
ADD_FAILURE() << "Trying to wait for an incognito background page from a "
<< "spanning mode extension. Use the on-the-record context.";
}
if (!BackgroundInfo::HasBackgroundPage(extension_.get()))
return; // No background page to wait for!
if (extension_->is_hosted_app()) {
// Little known fact: hosted apps can have background pages. They are
// handled separately in BackgroundContents[Service], and don't use the
// same infrastructure. They're also deprecated. Don't worry about them.
// (If we see flakiness in loading hosted apps, we could potentially
// rejigger this to accommodate for them as well, but it's unclear if it's
// a problem that needs solving.)
return;
}
ProcessManager* process_manager = ProcessManager::Get(browser_context_);
ExtensionHost* extension_host =
process_manager->GetBackgroundHostForExtension(extension_->id());
if (!extension_host) {
// If the extension has a lazy background page, it's possible that it's
// already been loaded and unloaded. As a proxy for this, check if there
// are registered events.
// This isn't a perfect solution, because
// a) We might be waiting on a subsequent background page load, and
// b) The extension might not register any events (which would normally be
// a bug in event page-based extensions, but not always).
// But, it's a decent proxy for now.
if (BackgroundInfo::HasLazyBackgroundPage(extension_.get()) &&
EventRouter::Get(browser_context_)
->HasRegisteredEvents(extension_->id())) {
return;
}
WaitForExtensionHostCreation();
extension_host =
process_manager->GetBackgroundHostForExtension(extension_->id());
}
ASSERT_TRUE(extension_host);
if (extension_host->has_loaded_once()) {
// The background host exists and has loaded; we're done.
return;
}
WaitForExtensionHostReady(extension_host);
}
void ExtensionBackgroundPageWaiter::WaitForExtensionHostCreation() {
process_manager_observer_.Add(ProcessManager::Get(browser_context_));
host_created_run_loop_.Run();
}
void ExtensionBackgroundPageWaiter::WaitForExtensionHostReady(
ExtensionHost* host) {
extension_host_observer_.Add(host);
host_ready_run_loop_.Run();
}
void ExtensionBackgroundPageWaiter::OnBackgroundHostCreated(
ExtensionHost* host) {
if (host->extension_id() != extension_->id() ||
host->browser_context() != browser_context_) {
return;
}
process_manager_observer_.RemoveAll();
host_created_run_loop_.QuitWhenIdle();
}
void ExtensionBackgroundPageWaiter::OnExtensionHostDidStopFirstLoad(
const ExtensionHost* host) {
ASSERT_EQ(extension_->id(), host->extension_id());
ASSERT_TRUE(host->has_loaded_once());
extension_host_observer_.RemoveAll();
host_ready_run_loop_.QuitWhenIdle();
}
void ExtensionBackgroundPageWaiter::OnExtensionHostDestroyed(
ExtensionHost* host) {
// This is only called while we're waiting for the host to be ready (since
// we remove ourselves as an observer when it's done).
DCHECK(host_ready_run_loop_.running());
ASSERT_EQ(extension_->id(), host->extension_id());
ADD_FAILURE() << "Extension host for " << extension_->name()
<< "was destroyed before it finished loading.";
ASSERT_TRUE(extension_host_observer_.IsObserving(host));
extension_host_observer_.Remove(host);
}
} // namespace extensions