blob: 9f7ce0df38ea6b1bfebb2742b664a51ae6ed9821 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_AUTOFILL_CONTENT_BROWSER_TEST_AUTOFILL_DRIVER_INJECTOR_H_
#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_TEST_AUTOFILL_DRIVER_INJECTOR_H_
#include "components/autofill/content/browser/content_autofill_client.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/content/browser/content_autofill_driver_factory_test_api.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test_utils.h"
namespace autofill {
// Asserts that at construction time, no other TestAutofillDriverInjector and no
// TestAutofillManagerInjector are alive.
class TestAutofillDriverInjectorBase {
public:
static bool some_instance_is_alive() { return num_instances_ > 0; }
TestAutofillDriverInjectorBase(const TestAutofillDriverInjectorBase&) =
delete;
TestAutofillDriverInjectorBase& operator=(
const TestAutofillDriverInjectorBase&) = delete;
protected:
TestAutofillDriverInjectorBase();
~TestAutofillDriverInjectorBase();
private:
static size_t num_instances_;
};
// RAII type that installs new AutofillDrivers of type `T` in all newly
// navigated frames in all newly created WebContents.
//
// The injector only injects an AutofillDriver if a driver would also be created
// Especially in unit tests it may be necessary to do a navigation to create the
// driver, for example with
// NavigateAndCommit(GURL("about:blank"))
// or force-create the driver manually with
// client->GetAutofillDriverFactory()->DriverForFrame(rfh).
//
// The driver's AutofillManager is a fresh BrowserAutofillManager.
//
// To prevent hard-to-find bugs, only one TestAutofillDriverInjector may be
// alive at a time. It must not be created before a TestAutofillClientInjector
// and not after a TestAutofillManagerInjector. These conditions are CHECKed.
//
// Usage:
//
// class AutofillFooTest : public ... {
// public:
// TestAutofillDriver* autofill_driver(content::RenderFrameHost* rfh) {
// return autofill_driver_injector_[rfh];
// }
//
// private:
// TestAutofillDriverInjector<TestAutofillDriver> autofill_driver_injector_;
// };
template <typename T>
class TestAutofillDriverInjector : public TestAutofillDriverInjectorBase {
public:
static_assert(std::is_base_of_v<ContentAutofillDriver, T>);
TestAutofillDriverInjector() = default;
TestAutofillDriverInjector(const TestAutofillDriverInjector&) = delete;
TestAutofillDriverInjector& operator=(const TestAutofillDriverInjector&) =
delete;
~TestAutofillDriverInjector() = default;
T* operator[](content::WebContents* web_contents) const {
return (*this)[web_contents->GetPrimaryMainFrame()];
}
T* operator[](content::RenderFrameHost* rfh) const {
auto it = drivers_.find(rfh);
return it != drivers_.end() ? it->second : nullptr;
}
private:
// Replaces every newly created production-code ContentAutofillDriver for the
// given WebContents with a test driver created by
// `T(content::RenderFrameHost*, ContentAutofillRouter*)` and sets its
// AutofillManager to a new `BrowserAutofillManager` with locale "en-US".
//
// One challenge is that the ContentAutofillClient may not exist yet at the
// time the Injector is created. (Because TabHelpers::AttachTabHelpers() is
// run later.)
//
// We therefore defer registering the ContentAutofillDriverFactory::Observer
// until the first RenderFrameCreated() event. This event comes late enough
// that ContentAutofillClient has been created but no ContentAutofillDriver
// has been created yet.
class Injector : public content::WebContentsObserver,
public ContentAutofillDriverFactory::Observer {
public:
Injector(TestAutofillDriverInjector* owner,
content::WebContents* web_contents)
: WebContentsObserver(web_contents), owner_(owner) {}
Injector(const Injector&) = delete;
Injector& operator=(const Injector&) = delete;
~Injector() override {
if (factory_) {
factory_->RemoveObserver(this);
}
}
void RenderFrameCreated(content::RenderFrameHost* rfh) override {
if (factory_) {
return;
}
auto* client = ContentAutofillClient::FromWebContents(web_contents());
if (!client) {
return;
}
factory_ = client->GetAutofillDriverFactory();
// The injectors' observers should come first so that production-code
// observers affect the injected objects.
ContentAutofillDriverFactoryTestApi(factory_).AddObserverAtIndex(this, 0);
}
void OnContentAutofillDriverFactoryDestroyed(
ContentAutofillDriverFactory& factory) override {
if (factory_) {
factory_->RemoveObserver(this);
factory_ = nullptr;
}
}
// Replaces the just created `driver` with a new test driver.
void OnContentAutofillDriverCreated(
ContentAutofillDriverFactory& factory,
ContentAutofillDriver& driver) override {
content::RenderFrameHost* rfh = driver.render_frame_host();
ContentAutofillDriverFactoryTestApi test_api(&factory);
ContentAutofillRouter* router = &test_api.router();
std::unique_ptr<T> new_driver = CreateDriver(rfh, router);
owner_->drivers_[rfh] = new_driver.get();
// This is spectacularly hacky as it relies on an implementation detail of
// ContentAutofillDriverFactory::DriverForFrame(), which fires this event:
//
// DriverForFrame() has just created `driver` and still holds a reference
// to the std::unique_ptr<> that owns `driver`. By calling SetDriver(), we
// mutate that reference. This looks sketchy, but it is "safe" because
// std::[unordered_]map<>::operator[]() does not invalidate references.
test_api.SetDriver(driver.render_frame_host(), std::move(new_driver));
}
void OnContentAutofillDriverWillBeDeleted(
ContentAutofillDriverFactory& factory,
ContentAutofillDriver& driver) override {
owner_->drivers_.erase(driver.render_frame_host());
}
private:
std::unique_ptr<T> CreateDriver(content::RenderFrameHost* rfh,
ContentAutofillRouter* router) {
auto* client = ContentAutofillClient::FromWebContents(web_contents());
auto driver = std::make_unique<T>(rfh, router);
driver->set_autofill_manager(std::make_unique<BrowserAutofillManager>(
driver.get(), client, "en-US"));
return driver;
}
raw_ptr<TestAutofillDriverInjector> owner_;
// Observed source. We can't use a ScopedObservation because we use
// ContentAutofillDriverFactoryTestApi::AddObserverAtIndex() instead of
// ContentAutofillDriverFactory::AddObserver().
raw_ptr<ContentAutofillDriverFactory> factory_ = nullptr;
};
void ObserveWebContentsAndInjectDriver(content::WebContents* web_contents) {
injectors_.push_back(std::make_unique<Injector>(this, web_contents));
}
std::vector<std::unique_ptr<Injector>> injectors_;
std::map<content::RenderFrameHost*, T*> drivers_;
// Registers the lambda for the lifetime of `subscription_`.
base::CallbackListSubscription subscription_ =
content::RegisterWebContentsCreationCallback(base::BindRepeating(
&TestAutofillDriverInjector::ObserveWebContentsAndInjectDriver,
base::Unretained(this)));
};
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_TEST_AUTOFILL_DRIVER_INJECTOR_H_