chromium/chrome/browser/ash/app_restore/full_restore_service.h

// Copyright 2020 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_ASH_APP_RESTORE_FULL_RESTORE_SERVICE_H_
#define CHROME_BROWSER_ASH_APP_RESTORE_FULL_RESTORE_SERVICE_H_

#include <memory>
#include <optional>

#include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/wm/window_restore/informed_restore_contents_data.h"
#include "base/callback_list.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/crosapi/full_restore_ash.h"
#include "chrome/browser/sessions/exit_type_service.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/sessions/core/session_types.h"
#include "ui/message_center/public/cpp/notification_delegate.h"

class Profile;

namespace app_restore {
class RestoreData;
}  // namespace app_restore

namespace message_center {
class Notification;
}  // namespace message_center

namespace ash::full_restore {

class FullRestoreAppLaunchHandler;
class FullRestoreDataHandler;
class NewUserRestorePrefHandler;

extern const char kRestoreForCrashNotificationId[];
extern const char kRestoreNotificationId[];
extern const char kSetRestorePrefNotificationId[];

// The restore notification button index.
enum class RestoreNotificationButtonIndex {
  kRestore = 0,
  kCancel,
};

// This is used to record histograms, so do not remove or reorder existing
// entries.
enum class RestoreAction {
  kRestore = 0,
  kCancel = 1,
  kCloseByUser = 2,
  kCloseNotByUser = 3,

  // Add any new values above this one, and update kMaxValue to the highest
  // enumerator value.
  kMaxValue = kCloseNotByUser,
};

// Returns true if FullRestoreService can be created to restore/launch Lacros
// during the system startup phase when all of the below conditions are met:
// 1. The FullRestoreForLacros flag is enabled.
// 2. Lacros is enabled.
// 3. FullRestoreService can be created for the primary profile.
bool MaybeCreateFullRestoreServiceForLacros();

// The FullRestoreService class calls AppService and Window Management
// interfaces to restore the app launchings and app windows.
class FullRestoreService : public KeyedService,
                           public message_center::NotificationObserver,
                           public AcceleratorController::Observer,
                           public SessionObserver {
 public:
  // Delegate class that talks to ash shell. Ash shell is not created in
  // unit tests so this should be mocked out for testing those behaviors.
  class Delegate {
   public:
    virtual ~Delegate() = default;
    // Starts overview with the informed restore dialog unless overview is
    // already active.
    virtual void MaybeStartInformedRestoreOverviewSession(
        std::unique_ptr<InformedRestoreContentsData> contents_data) = 0;
    virtual void MaybeEndInformedRestoreOverviewSession() = 0;
    virtual InformedRestoreContentsData* GetInformedRestoreContentData() = 0;
    virtual void OnInformedRestoreContentsDataUpdated() = 0;
  };

  static FullRestoreService* GetForProfile(Profile* profile);
  static void MaybeCloseNotification(Profile* profile);

  explicit FullRestoreService(Profile* profile);
  FullRestoreService(const FullRestoreService&) = delete;
  FullRestoreService& operator=(const FullRestoreService&) = delete;
  ~FullRestoreService() override;

  FullRestoreAppLaunchHandler* app_launch_handler() {
    return app_launch_handler_.get();
  }

  // Initialize the full restore service. |show_notification| indicates whether
  // a full restore notification has been shown.
  void Init(bool& show_notification);

  void OnTransitionedToNewActiveUser(Profile* profile);

  // Launches the browser, When the restore data is loaded, and the user chooses
  // to restore.
  void LaunchBrowserWhenReady();

  void MaybeCloseNotification(bool allow_save = true);

  // Implement the restoration.
  void Restore();

  // message_center::NotificationObserver:
  void Close(bool by_user) override;
  void Click(const std::optional<int>& button_index,
             const std::optional<std::u16string>& reply) override;

  // AcceleratorController::Observer:
  void OnActionPerformed(AcceleratorAction action) override;
  void OnAcceleratorControllerWillBeDestroyed(
      AcceleratorController* controller) override;

  // SessionObserver:
  void OnSessionStateChanged(session_manager::SessionState state) override;

  void SetAppLaunchHandlerForTesting(
      std::unique_ptr<FullRestoreAppLaunchHandler> app_launch_handler);

 private:
  friend class FullRestoreTestHelper;
  FRIEND_TEST_ALL_PREFIXES(FullRestoreAppLaunchHandlerChromeAppBrowserTest,
                           RestoreChromeApp);
  FRIEND_TEST_ALL_PREFIXES(FullRestoreAppLaunchHandlerArcAppBrowserTest,
                           RestoreArcApp);
  using SessionWindows = std::vector<std::unique_ptr<sessions::SessionWindow>>;
  // Maps window id to an associated session window. We use a map at certain
  // points because:
  //   - The data from the full restore file is in a 2 dimensional vector. The
  //     first one is for apps, and the second one is for windows.
  //   - The data from session restore is a single vector.
  // We build a map to avoid doing a O(n) search each loop of the former.
  using SessionWindowsMap =
      base::flat_map<int, crosapi::mojom::SessionWindowPtr>;

  // KeyedService:
  void Shutdown() override;

  // Returns true if `Init` can be called to show the notification or restore
  // apps. Otherwise, returns false.
  bool CanBeInited() const;

  void InitInformedRestoreContentsData(
      InformedRestoreContentsData::DialogType dialog_type);

  // Shows the restore notification or the informed restore dialog on startup.
  void MaybeShowRestoreNotification(
      InformedRestoreContentsData::DialogType dialog_type,
      bool& show_notification);

  void RecordRestoreAction(const std::string& notification_id,
                           RestoreAction restore_action);

  // Callback used when the pref |kRestoreAppsAndPagesPrefName| changes.
  void OnPreferenceChanged(const std::string& pref_name);

  void OnAppTerminating();

  // Callbacks for the informed restore dialog buttons.
  void OnDialogRestore();
  void OnDialogCancel();

  // Callbacks run after querying for data from the session service(s).
  // `OnGotSessionAsh` is run after receiving data from either the normal
  // session service or app session service. `OnGotAllSessionsAsh` is run after
  // receiving data from both.
  void OnGotSessionAsh(base::OnceCallback<void(SessionWindows)> callback,
                       SessionWindows session_windows,
                       SessionID active_window_id,
                       bool read_error);
  void OnGotAllSessionsAsh(
      const std::vector<SessionWindows>& all_session_windows);
  void OnGotAllSessionsLacros(
      std::vector<crosapi::mojom::SessionWindowPtr> all_session_windows);

  // Called when session information is ready to be processed. Constructs the
  // object needed to show the informed restore dialog. It will be passed to ash
  // which will then use its contents to create and display the dialog.
  // `session_windows_map` is the browser info retrieved from session restore.
  void OnSessionInformationReceived(
      const SessionWindowsMap& session_windows_map);

  // Shows the informed restore onboarding dialog when there is no restore data.
  void MaybeShowInformedRestoreOnboarding(bool restore_on);

  raw_ptr<Profile> profile_ = nullptr;
  PrefChangeRegistrar pref_change_registrar_;

  // If the user of `profile_` is not the primary user, and hasn't been the
  // active user yet, don't init to restore. Because if the restore setting is
  // 'Always', the app could be launched directly after restart, and the app
  // windows could be added to the primary user's profile path. This may cause
  // the non-primary user lost some restored windows data.
  //
  // If the non primary user becomes the active user, set `can_be_inited_` as
  // true to init and restore app. Otherwise, if `can_be_inited_` is false for
  // the non primary user, defer the init and app restoration.
  bool can_be_inited_ = false;

  bool is_shut_down_ = false;
  bool close_notification_ = false;

  // Specifies whether it is the first time to run the full restore feature.
  bool first_run_full_restore_ = false;

  // If the user clicks a notification button, set
  // |skip_notification_histogram_| as true to skip the notification close
  // histogram.
  bool skip_notification_histogram_ = false;

  std::unique_ptr<NewUserRestorePrefHandler> new_user_pref_handler_;

  // |app_launch_handler_| is responsible for launching apps based on the
  // restore data.
  std::unique_ptr<FullRestoreAppLaunchHandler> app_launch_handler_;

  std::unique_ptr<FullRestoreDataHandler> restore_data_handler_;

  // The contents data that will be presented in the informed restore dialog.
  // Will pass the ownership to post-login controller when start post-login
  // session.
  std::unique_ptr<InformedRestoreContentsData> contents_data_;

  std::unique_ptr<message_center::Notification> notification_;

  std::unique_ptr<Delegate> delegate_;

  base::CallbackListSubscription on_app_terminating_subscription_;

  // Browser session restore exit type service lock. This is created when the
  // system is restored from crash to help set the browser saving flag.
  std::unique_ptr<ExitTypeService::CrashedLock> crashed_lock_;

  base::ScopedObservation<AcceleratorController,
                          AcceleratorController::Observer>
      accelerator_controller_observer_{this};

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

class ScopedRestoreForTesting {
 public:
  ScopedRestoreForTesting();
  ScopedRestoreForTesting(const ScopedRestoreForTesting&) = delete;
  ScopedRestoreForTesting& operator=(const ScopedRestoreForTesting&) = delete;
  ~ScopedRestoreForTesting();
};

}  // namespace ash::full_restore

#endif  // CHROME_BROWSER_ASH_APP_RESTORE_FULL_RESTORE_SERVICE_H_