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

#ifndef CHROME_BROWSER_PROFILES_PROFILE_DESTROYER_H_
#define CHROME_BROWSER_PROFILES_PROFILE_DESTROYER_H_

#include <stdint.h>

#include <memory>
#include <optional>
#include <set>

#include "base/scoped_multi_source_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"

class Profile;

// We use this class to destroy the profiles so that we can make sure it gets
// done asynchronously after all render process hosts are gone.
class ProfileDestroyer : public content::RenderProcessHostObserver {
 public:
  // Destroys the given original profile either instantly, or after a short
  // delay waiting for dependent renderer process hosts to destroy.
  static void DestroyOriginalProfileWhenAppropriate(
      std::unique_ptr<Profile> profile);

  // Overrides the timeout for destroying a profile. This is used in tests to
  // prevent the timeout from firing prematurely.
  static void SetDestroyProfileTimeoutForTesting(base::TimeDelta timeout);

  // Destroys the given off-the-record profile either instantly, or after a
  // short delay waiting for dependent renderer process hosts to destroy.
  // `profile` should not be used after this call.
  //
  // OTR profiles are owned by their parent profile - the parent profile is
  // responsible for actually destroying the object.
  static void DestroyOTRProfileWhenAppropriate(Profile* profile);

  // Similar to DestroyOTRProfileWhenAppropriate(), but with an explicit
  // timeout to wait for the renderer host to be destroyed.
  static void DestroyOTRProfileWhenAppropriateWithTimeout(
      Profile* profile,
      base::TimeDelta timeout);

  // Force destroy |profile| immediately without waiting for dependent renderer
  // process hosts to be destroyed.
  static void DestroyOTRProfileImmediately(Profile* profile);

  // Force destroy all the profiles pending deletion. This is called by the
  // ProfileManager during shutdown.
  static void DestroyPendingProfilesForShutdown();

  ProfileDestroyer(const ProfileDestroyer&) = delete;
  ProfileDestroyer& operator=(const ProfileDestroyer&) = delete;

 protected:
  using HostSet = std::set<content::RenderProcessHost*>;

  // Similar to DestroyOriginalProfileWhenAppropriate(), but with an explicit
  // timeout to wait for the renderer host to be destroyed.
  static void DestroyOriginalProfileWhenAppropriateWithTimeout(
      std::unique_ptr<Profile> profile,
      base::TimeDelta timeout);

  // Destroys an Original (non-off-the-record) profile immediately.
  static void DestroyOriginalProfileNow(std::unique_ptr<Profile> profile);

  // Destroys an OffTheRecord profile immediately and removes it from all
  // pending destroyers.
  static void DestroyOffTheRecordProfileNow(Profile* profile);

  // Fetch the list of render process hosts that still point to |profile_ptr|.
  // |profile_ptr| is a void* because the Profile object may be freed. Only
  // pointer comparison is allowed, it will never be dereferenced as a Profile.
  //
  // If |include_spare_rph| is true, include spare render process hosts in the
  // output.
  static void GetHostsForProfile(HostSet* out,
                                 void* profile_ptr,
                                 bool include_spare_rph = false);

  // Returns the profile destroyer that has |profile| as the underlying profile
  // and that is not prepared for destruction if any.  Returns nullptr if such
  // a profile destroyer does not exist..
  static ProfileDestroyer* GetPendingDestroyerForProfile(
      const Profile* profile);

  ProfileDestroyer(Profile* profile, base::TimeDelta timeout);
  ~ProfileDestroyer() override;

  const base::TimeDelta& timeout() const { return timeout_; }

  // Returns the underlying profile that should be destructed.
  virtual Profile* GetProfile() = 0;

  // Destroys the underlying profile.
  virtual void DoDestroyUnderlyingProfile() = 0;

  // Retries the destruction of the underlying profile using the same timeout.
  virtual void RetryDestroyUnderlyingProfile() = 0;

 private:
  // Starts monitoring the |hosts| and the timeout. If |hosts| is empty, then
  // this function will delete the profile now.
  void Start(const HostSet& hosts);

  // Returns true when this profile destroyer was scheduled for destruction;
  bool is_prepared_for_destruction() const {
    return is_prepared_for_destruction_;
  }

  // content::RenderProcessHostObserver override.
  void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;

  // Two final state for the ProfileDestroyer instance:
  // - Timeout(): It still exists some RenderProcessHost. Force delete the
  //              profile.
  // - Retry():   Every observed RenderProcessHost have been deleted. We can try
  //              again destroying the profile.
  void Retry();
  void Timeout();

  // We don't want to wait forever, so we have a cancellation timer.
  base::OneShotTimer timer_;

  base::ScopedMultiSourceObservation<content::RenderProcessHost,
                                     content::RenderProcessHostObserver>
      observations_{this};

  // Force-destruction timeout.
  const base::TimeDelta timeout_;

  static base::TimeDelta GetDestroyProfileTimeout();

  bool is_prepared_for_destruction_ = false;

  // The initial value of |profile_| stored as uint64_t for traces. It is useful
  // for use in the destructor, because at the end, |profile_| is nullptr.
  const uint64_t profile_ptr_;

  static std::optional<base::TimeDelta> destroy_profile_timeout_override_;

  base::WeakPtrFactory<ProfileDestroyer> weak_ptr_factory_{this};
};

#endif  // CHROME_BROWSER_PROFILES_PROFILE_DESTROYER_H_
