| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/autofill/content/browser/content_autofill_driver_factory.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/containers/to_vector.h" |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "components/autofill/content/browser/content_autofill_client.h" |
| #include "components/autofill/content/browser/content_autofill_driver.h" |
| #include "components/autofill/content/browser/integrators/glic/autofill_annotations_provider_impl.h" |
| #include "components/autofill/core/browser/foundations/browser_autofill_manager.h" |
| #include "components/autofill/core/browser/payments/payments_autofill_client.h" |
| #include "components/autofill/core/browser/studies/autofill_experiments.h" |
| #include "components/autofill/core/common/autofill_features.h" |
| #include "content/public/browser/global_routing_id.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "third_party/blink/public/common/features.h" |
| |
| namespace autofill { |
| |
| class ScopedAutofillManagersObservation; |
| |
| void ContentAutofillDriverFactory::Observer::OnAutofillDriverFactoryDestroyed( |
| AutofillDriverFactory& factory) { |
| OnContentAutofillDriverFactoryDestroyed( |
| static_cast<ContentAutofillDriverFactory&>(factory)); |
| } |
| |
| void ContentAutofillDriverFactory::Observer::OnAutofillDriverCreated( |
| AutofillDriverFactory& factory, |
| AutofillDriver& driver) { |
| OnContentAutofillDriverCreated( |
| static_cast<ContentAutofillDriverFactory&>(factory), |
| static_cast<ContentAutofillDriver&>(driver)); |
| } |
| |
| void ContentAutofillDriverFactory::Observer::OnAutofillDriverStateChanged( |
| AutofillDriverFactory& factory, |
| AutofillDriver& driver, |
| LifecycleState old_state, |
| LifecycleState new_state) { |
| OnContentAutofillDriverStateChanged( |
| static_cast<ContentAutofillDriverFactory&>(factory), |
| static_cast<ContentAutofillDriver&>(driver), old_state, new_state); |
| } |
| |
| // static |
| ContentAutofillDriverFactory* ContentAutofillDriverFactory::FromWebContents( |
| content::WebContents* contents) { |
| ContentAutofillClient* client = |
| ContentAutofillClient::FromWebContents(contents); |
| if (!client) { |
| return nullptr; |
| } |
| return &client->GetAutofillDriverFactory(); |
| } |
| |
| // static |
| void ContentAutofillDriverFactory::BindAutofillDriver( |
| content::RenderFrameHost* render_frame_host, |
| mojo::PendingAssociatedReceiver<mojom::AutofillDriver> pending_receiver) { |
| DCHECK(render_frame_host); |
| |
| content::WebContents* web_contents = |
| content::WebContents::FromRenderFrameHost(render_frame_host); |
| DCHECK(web_contents); |
| |
| ContentAutofillDriverFactory* factory = FromWebContents(web_contents); |
| if (!factory) { |
| // The message pipe will be closed and raise a connection error to peer |
| // side. The peer side can reconnect later when needed. |
| return; |
| } |
| |
| if (auto* driver = factory->DriverForFrame(render_frame_host)) { |
| driver->BindPendingReceiver(std::move(pending_receiver)); |
| } |
| } |
| |
| ContentAutofillDriverFactory::ContentAutofillDriverFactory( |
| content::WebContents* web_contents, |
| ContentAutofillClient* client) |
| : content::WebContentsObserver(web_contents), client_(*client) { |
| optimization_guide::AutofillAnnotationsProviderImpl::SetFor( |
| web_contents, |
| std::make_unique<optimization_guide::AutofillAnnotationsProviderImpl>()); |
| } |
| |
| ContentAutofillDriverFactory::~ContentAutofillDriverFactory() { |
| for (auto& observer : observers()) { |
| observer.OnAutofillDriverFactoryDestroyed(*this); |
| } |
| base::UmaHistogramCounts1000("Autofill.NumberOfDriversPerFactory", |
| max_drivers_); |
| } |
| |
| ContentAutofillDriver* ContentAutofillDriverFactory::DriverForFrame( |
| content::RenderFrameHost* render_frame_host) { |
| // Within fenced frames and their descendants, Password Manager should for now |
| // be disabled (crbug.com/1294378). |
| if (render_frame_host->IsNestedWithinFencedFrame() && |
| !base::FeatureList::IsEnabled(blink::features::kFencedFramesAPIChanges)) { |
| return nullptr; |
| } |
| |
| auto [iter, insertion_happened] = |
| driver_map_.emplace(render_frame_host, nullptr); |
| std::unique_ptr<ContentAutofillDriver>& driver = iter->second; |
| if (insertion_happened) { |
| // The `render_frame_host` may already be deleted (or be in the process of |
| // being deleted). In this case, we must not create a new driver. Otherwise, |
| // a driver might hold a deallocated RFH. |
| // |
| // For example, `render_frame_host` is deleted in the following sequence: |
| // 1. `render_frame_host->~RenderFrameHostImpl()` starts and marks |
| // `render_frame_host` as deleted. |
| // 2. `ContentAutofillDriverFactory::RenderFrameDeleted(render_frame_host)` |
| // destroys the driver of `render_frame_host`. |
| // 3. `SomeOtherWebContentsObserver::RenderFrameDeleted(render_frame_host)` |
| // calls `DriverForFrame(render_frame_host)`. |
| // 5. `render_frame_host->~RenderFrameHostImpl()` finishes. |
| if (!render_frame_host->IsRenderFrameLive()) { |
| driver_map_.erase(iter); |
| DCHECK_EQ(driver_map_.count(render_frame_host), 0u); |
| return nullptr; |
| } |
| driver = std::make_unique<ContentAutofillDriver>(render_frame_host, this); |
| DCHECK_EQ(driver->GetLifecycleState(), LifecycleState::kInactive); |
| for (auto& observer : observers()) { |
| observer.OnAutofillDriverCreated(*this, *driver); |
| } |
| // TODO: crbug.com/342132628 - `driver->IsActive()` is guaranteed once |
| // prerendered CADs are deferred. |
| SetLifecycleStateAndNotifyObservers( |
| *driver, driver->IsActive() ? LifecycleState::kActive |
| : LifecycleState::kInactive); |
| DCHECK_EQ(&driver_map_[render_frame_host], &driver); |
| } |
| DCHECK(driver.get()); |
| max_drivers_ = std::max(max_drivers_, driver_map_.size()); |
| return driver.get(); |
| } |
| |
| void ContentAutofillDriverFactory::RenderFrameDeleted( |
| content::RenderFrameHost* render_frame_host) { |
| auto it = driver_map_.find(render_frame_host); |
| if (it == driver_map_.end()) { |
| return; |
| } |
| |
| ContentAutofillDriver* driver = it->second.get(); |
| DCHECK(driver); |
| |
| if (!render_frame_host->IsInLifecycleState( |
| content::RenderFrameHost::LifecycleState::kPrerendering)) { |
| // TODO: crbug.com/354043809 - Move out of CADF. |
| driver->GetAutofillManager().ReportAutofillWebOTPMetrics( |
| render_frame_host->DocumentUsedWebOTP()); |
| } |
| |
| SetLifecycleStateAndNotifyObservers(*driver, |
| LifecycleState::kPendingDeletion); |
| driver_map_.erase(it); |
| } |
| |
| void ContentAutofillDriverFactory::RenderFrameHostStateChanged( |
| content::RenderFrameHost* render_frame_host, |
| content::RenderFrameHost::LifecycleState old_state, |
| content::RenderFrameHost::LifecycleState new_state) { |
| auto* driver = DriverForFrame(render_frame_host); |
| if (!driver) { |
| return; |
| } |
| auto state = |
| [new_state]() -> std::optional<ContentAutofillDriver::LifecycleState> { |
| using RFH = content::RenderFrameHost::LifecycleState; |
| using CAD = ContentAutofillDriver::LifecycleState; |
| switch (new_state) { |
| case RFH::kPendingCommit: // Handled in DidFinishNavigation(). |
| return std::nullopt; |
| |
| case RFH::kActive: // Potentially redundant with DriverForFrame(). |
| return CAD::kActive; |
| |
| case RFH::kPrerendering: |
| // TODO: crbug.com/342132628 - Unreachable once prerendered CADs are |
| // deferred. |
| case RFH::kInBackForwardCache: |
| return CAD::kInactive; |
| |
| case RFH::kPendingDeletion: // Handled in RenderFrameDeleted(). |
| return std::nullopt; |
| } |
| NOTREACHED(); |
| }(); |
| if (state) { |
| SetLifecycleStateAndNotifyObservers(*driver, *state); |
| } |
| } |
| |
| void ContentAutofillDriverFactory::DidFinishNavigation( |
| content::NavigationHandle* navigation_handle) { |
| if (!navigation_handle->HasCommitted() || |
| navigation_handle->IsSameDocument()) { |
| return; |
| } |
| auto* driver = DriverForFrame(navigation_handle->GetRenderFrameHost()); |
| if (!driver) { |
| return; |
| } |
| |
| if (navigation_handle->IsInPrimaryMainFrame() && |
| client().GetPaymentsAutofillClient() && |
| client().GetPaymentsAutofillClient()->GetAutofillOfferManager()) { |
| // If the navigation happened in the main frame and the AutofillOfferManager |
| // exists (not in Incognito windows, not in WebView), notify it about the |
| // navigation event. |
| // TODO: crbug.com/40178290 - Move out of CADF. Perhaps use the |
| // LifecycleState changes to recognize navigations. |
| client() |
| .GetPaymentsAutofillClient() |
| ->GetAutofillOfferManager() |
| ->OnDidNavigateFrame(client()); |
| } |
| |
| // If the navigation is served from BFCache, then the pre-navigation RFH is |
| // swapped with a post-navigation RFH, along with their associated CADs. We do |
| // not reset the swapped-in CAD's state so that its state continues where we |
| // left off when the CAD was swapped out. Similarly for prerendering. |
| if (navigation_handle->IsServedFromBackForwardCache() || |
| navigation_handle->IsPrerenderedPageActivation()) { |
| return; |
| } |
| |
| SetLifecycleStateAndNotifyObservers(*driver, LifecycleState::kPendingReset); |
| driver->Reset(/*pass_key=*/{}); |
| // TODO: crbug.com/342132628 - `driver->IsActive()` is guaranteed once |
| // prerendered CADs are deferred. |
| SetLifecycleStateAndNotifyObservers( |
| *driver, driver->IsActive() ? AutofillDriver::LifecycleState::kActive |
| : AutofillDriver::LifecycleState::kInactive); |
| } |
| |
| std::vector<AutofillDriver*> |
| ContentAutofillDriverFactory::GetExistingDrivers() { |
| return base::ToVector(driver_map_, [](const auto& p) -> AutofillDriver* { |
| return p.second.get(); |
| }); |
| } |
| |
| } // namespace autofill |