// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "android_webview/browser/aw_render_process_keep_alive.h"

#include <memory>
#include <utility>

#include "android_webview/browser/aw_render_process.h"
#include "android_webview/common/aw_features.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"

namespace android_webview {

namespace {

const void* const kAwRenderProcessKeepAliveKey = &kAwRenderProcessKeepAliveKey;

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// LINT.IfChange(RendererKeepAliveEvent)
enum class RendererKeepAliveEvent {
  kReused = 0,
  kTimedOut = 1,
  kPendingReuse = 2,
  kMaxValue = kPendingReuse,
};
// LINT.ThenChange(/tools/metrics/histograms/metadata/android/enums.xml:WebViewRendererKeepAliveEvent)

void RecordKeepAliveEvent(RendererKeepAliveEvent event) {
  base::UmaHistogramEnumeration("Android.WebView.RendererKeepAlive.Event",
                                event);
}

}  // namespace

// static
AwRenderProcessKeepAlive*
AwRenderProcessKeepAlive::GetInstanceForRenderProcessHost(
    content::RenderProcessHost* host) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  AwRenderProcessKeepAlive* instance = static_cast<AwRenderProcessKeepAlive*>(
      host->GetUserData(kAwRenderProcessKeepAliveKey));
  if (!instance) {
    auto created_instance =
        base::WrapUnique(new AwRenderProcessKeepAlive(host));
    instance = created_instance.get();
    host->SetUserData(kAwRenderProcessKeepAliveKey,
                      std::move(created_instance));
  }
  return instance;
}

AwRenderProcessKeepAlive::AwRenderProcessKeepAlive(
    content::RenderProcessHost* render_process_host)
    : render_process_host_(render_process_host) {}

AwRenderProcessKeepAlive::~AwRenderProcessKeepAlive() = default;

void AwRenderProcessKeepAlive::AddAwContents() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  aw_contents_count_++;
  if (keep_alive_timer_.IsRunning()) {
    RecordKeepAliveEvent(RendererKeepAliveEvent::kReused);
    base::UmaHistogramLongTimes100(
        "Android.WebView.RendererKeepAlive.TimeToReuse",
        base::TimeTicks::Now() - keep_alive_start_time_);
    if (base::FeatureList::IsEnabled(features::kWebViewPrefetchNativeLibrary) &&
        features::kWebViewPrefetchFromRenderer.Get() &&
        base::FeatureList::IsEnabled(
            features::kWebViewPrefetchOnRendererReuse)) {
      AwRenderProcess* aw_render_process =
          AwRenderProcess::GetInstanceForRenderProcessHost(
              render_process_host_);
      if (aw_render_process) {
        aw_render_process->PrefetchNativeLibrary();
      }
    }
  }
  keep_alive_timer_.Stop();
  if (!kept_alive_) {
    render_process_host_->IncrementPendingReuseRefCount();
    kept_alive_ = true;
  }
}

void AwRenderProcessKeepAlive::RemoveAwContents() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  CHECK_GT(aw_contents_count_, 0);
  aw_contents_count_--;
  if (aw_contents_count_ == 0 && kept_alive_) {
    RecordKeepAliveEvent(RendererKeepAliveEvent::kPendingReuse);
    keep_alive_start_time_ = base::TimeTicks::Now();
    keep_alive_timer_.Start(
        FROM_HERE, features::kWebViewRendererKeepAliveDuration.Get(),
        base::BindOnce(&AwRenderProcessKeepAlive::OnKeepAliveTimerFired,
                       weak_factory_.GetWeakPtr()));
  }
}

void AwRenderProcessKeepAlive::OnKeepAliveTimerFired() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  CHECK(kept_alive_);
  RecordKeepAliveEvent(RendererKeepAliveEvent::kTimedOut);
  render_process_host_->DecrementPendingReuseRefCount();
  kept_alive_ = false;
}

}  // namespace android_webview
