chromium/components/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.cc

// Copyright 2024 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/facilitated_payments/content/browser/content_facilitated_payments_driver_factory.h"

#include "base/check_deref.h"
#include "components/facilitated_payments/core/browser/facilitated_payments_client.h"
#include "components/facilitated_payments/core/features/features.h"
#include "content/public/browser/navigation_handle.h"

namespace payments::facilitated {

ContentFacilitatedPaymentsDriverFactory::
    ContentFacilitatedPaymentsDriverFactory(
        content::WebContents* web_contents,
        FacilitatedPaymentsClient* client,
        optimization_guide::OptimizationGuideDecider*
            optimization_guide_decider)
    : content::WebContentsObserver(web_contents),
      client_(CHECK_DEREF(client)),
      optimization_guide_decider_(optimization_guide_decider) {}

ContentFacilitatedPaymentsDriverFactory::
    ~ContentFacilitatedPaymentsDriverFactory() {
  DCHECK(driver_map_.empty());
}

void ContentFacilitatedPaymentsDriverFactory::RenderFrameDeleted(
    content::RenderFrameHost* render_frame_host) {
  driver_map_.erase(render_frame_host);
}

void ContentFacilitatedPaymentsDriverFactory::RenderFrameHostStateChanged(
    content::RenderFrameHost* render_frame_host,
    content::RenderFrameHost::LifecycleState old_state,
    content::RenderFrameHost::LifecycleState new_state) {
  // All facilitated payments processes are run only on the outermost main
  // frame.
  if (render_frame_host != render_frame_host->GetOutermostMainFrame()) {
    return;
  }
  // User visible pages are active i.e. `LifecycleState == kActive`. An
  // RenderFrameHost state change where `old_state == kActive` represents a
  // navigation away from an active page. When navigating away, all facilitated
  // payments processes should be abandoned.
  if (old_state != content::RenderFrameHost::LifecycleState::kActive) {
    return;
  }
  if (auto iter = driver_map_.find(render_frame_host);
      iter != driver_map_.end()) {
    iter->second->DidNavigateToOrAwayFromPage();
  }
}

void ContentFacilitatedPaymentsDriverFactory::DidFinishNavigation(
    content::NavigationHandle* navigation_handle) {
  if (!navigation_handle->HasCommitted() ||
      navigation_handle->IsSameDocument() ||
      !navigation_handle->IsInPrimaryMainFrame() ||
      !navigation_handle->IsInOutermostMainFrame()) {
    return;
  }
  auto& driver = GetOrCreateForFrame(navigation_handle->GetRenderFrameHost());
  driver.DidNavigateToOrAwayFromPage();
}

void ContentFacilitatedPaymentsDriverFactory::DOMContentLoaded(
    content::RenderFrameHost* render_frame_host) {
  // The driver is only created for the outermost main frame as the PIX code
  // is only expected to be present there. PIX code detection is triggered
  // only on active frames.
  if (render_frame_host != render_frame_host->GetOutermostMainFrame() ||
      !render_frame_host->IsActive()) {
    return;
  }
  if (!base::FeatureList::IsEnabled(kEnablePixDetectionOnDomContentLoaded)) {
    return;
  }
  auto& driver = GetOrCreateForFrame(render_frame_host);
  // Initialize PIX code detection.
  driver.OnContentLoadedInThePrimaryMainFrame(
      render_frame_host->GetLastCommittedURL(),
      render_frame_host->GetPageUkmSourceId());
}

void ContentFacilitatedPaymentsDriverFactory::DidFinishLoad(
    content::RenderFrameHost* render_frame_host,
    const GURL& validated_url) {
  // The driver is only created for the outermost main frame as the PIX code is
  // only expected to be present there. PIX code detection is triggered only on
  // active frames.
  if (render_frame_host != render_frame_host->GetOutermostMainFrame() ||
      !render_frame_host->IsActive()) {
    return;
  }
  if (base::FeatureList::IsEnabled(kEnablePixDetectionOnDomContentLoaded) ||
      !base::FeatureList::IsEnabled(kEnablePixDetection)) {
    return;
  }
  auto& driver = GetOrCreateForFrame(render_frame_host);
  // Initialize PIX code detection.
  driver.OnContentLoadedInThePrimaryMainFrame(
      validated_url, render_frame_host->GetPageUkmSourceId());
}

void ContentFacilitatedPaymentsDriverFactory::OnTextCopiedToClipboard(
    content::RenderFrameHost* render_frame_host,
    const std::u16string& copied_text) {
  if (render_frame_host != render_frame_host->GetOutermostMainFrame() ||
      !render_frame_host->IsActive()) {
    return;
  }

  auto& driver = GetOrCreateForFrame(render_frame_host);

  driver.OnTextCopiedToClipboard(render_frame_host->GetLastCommittedURL(),
                                 copied_text,
                                 render_frame_host->GetPageUkmSourceId());
}

ContentFacilitatedPaymentsDriver&
ContentFacilitatedPaymentsDriverFactory::GetOrCreateForFrame(
    content::RenderFrameHost* render_frame_host) {
  auto [iter, insertion_happened] =
      driver_map_.emplace(render_frame_host, nullptr);
  std::unique_ptr<ContentFacilitatedPaymentsDriver>& driver = iter->second;
  if (!insertion_happened) {
    DCHECK(driver);
    return *iter->second;
  }
  driver = std::make_unique<ContentFacilitatedPaymentsDriver>(
      &*client_, optimization_guide_decider_, render_frame_host);
  DCHECK_EQ(driver_map_.find(render_frame_host)->second.get(), driver.get());
  return *iter->second;
}

}  // namespace payments::facilitated