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

#include "content/browser/site_instance_group_manager.h"

#include "base/feature_list.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/site_instance_group.h"
#include "content/browser/site_instance_impl.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_features.h"

namespace content {

SiteInstanceGroupManager::SiteInstanceGroupManager() = default;
SiteInstanceGroupManager::~SiteInstanceGroupManager() {
  ClearDefaultProcess();
}

RenderProcessHost* SiteInstanceGroupManager::GetExistingGroupProcess(
    SiteInstanceImpl* site_instance) {
  if (!base::FeatureList::IsEnabled(
          features::kProcessSharingWithStrictSiteInstances) ||
      !default_process_) {
    return nullptr;
  }

  if (site_instance->RequiresDedicatedProcess() ||
      !RenderProcessHostImpl::MayReuseAndIsSuitable(default_process_,
                                                    site_instance)) {
    return nullptr;
  }

  return default_process_;
}

scoped_refptr<SiteInstanceGroup>
SiteInstanceGroupManager::GetOrCreateGroupForNewSiteInstance(
    SiteInstanceImpl* site_instance,
    RenderProcessHost* process) {
  DCHECK(!site_instance->group());

  // TODO(crbug.com/1291351, yangsharon): For now, each SiteInstance gets its
  // own SiteInstanceGroup, and we can always create a new group for each new
  // SiteInstance here. When grouping policies are introduced, this function may
  // return an existing SiteInstanceGroup for a new SiteInstance.
  scoped_refptr<SiteInstanceGroup> site_instance_group = base::WrapRefCounted(
      new SiteInstanceGroup(site_instance->browsing_instance(), process));
  site_instance_group->AddSiteInstance(site_instance);
  return site_instance_group;
}

void SiteInstanceGroupManager::OnSiteInfoSet(SiteInstanceImpl* site_instance,
                                             bool has_process) {
  if (!default_process_ && has_process)
    MaybeSetDefaultProcess(site_instance);
}

void SiteInstanceGroupManager::OnProcessSet(SiteInstanceImpl* site_instance) {
  if (default_process_) {
    if (RenderProcessHostImpl::MayReuseAndIsSuitable(default_process_,
                                                     site_instance)) {
      // Make sure the default process was actually used if it is appropriate
      // for this SiteInstance.
      DCHECK_EQ(site_instance->GetProcess(), default_process_);
    }
    return;
  }

  MaybeSetDefaultProcess(site_instance);
}

void SiteInstanceGroupManager::MaybeSetDefaultProcess(
    SiteInstanceImpl* site_instance) {
  if (!base::FeatureList::IsEnabled(
          features::kProcessSharingWithStrictSiteInstances)) {
    return;
  }

  // Wait until this SiteInstance both has a site and a process
  // assigned, so that we can be sure that RequiresDedicatedProcess()
  // is accurate and we actually have a process to set.
  DCHECK(site_instance->HasProcess());
  if (!site_instance->HasSite() || site_instance->RequiresDedicatedProcess())
    return;

  DCHECK(!default_process_);
  default_process_ = site_instance->GetProcess();
  default_process_->AddObserver(this);
}

void SiteInstanceGroupManager::RenderProcessHostDestroyed(
    RenderProcessHost* host) {
  DCHECK_EQ(default_process_, host);
  // Only clear the default process if the RenderProcessHost object goes away,
  // not if the renderer process goes away while the RenderProcessHost remains.
  ClearDefaultProcess();
}

void SiteInstanceGroupManager::ClearDefaultProcess() {
  if (!default_process_)
    return;

  default_process_->RemoveObserver(this);
  default_process_ = nullptr;
}

}  // namespace content