chromium/ash/wm/window_restore/window_restore_controller.h

// 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.

#ifndef ASH_WM_WINDOW_RESTORE_WINDOW_RESTORE_CONTROLLER_H_
#define ASH_WM_WINDOW_RESTORE_WINDOW_RESTORE_CONTROLLER_H_

#include "ash/ash_export.h"
#include "base/cancelable_callback.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "components/account_id/account_id.h"
#include "components/app_restore/app_restore_info.h"
#include "components/app_restore/window_info.h"
#include "ui/aura/window_observer.h"
#include "ui/display/display_observer.h"

namespace aura {
class Window;
}  // namespace aura

namespace display {
enum class TabletState;
}  // namespace display

namespace views {
class Widget;
}  // namespace views

namespace ash {

class WindowState;

class ASH_EXPORT WindowRestoreController
    : public display::DisplayObserver,
      public app_restore::AppRestoreInfo::Observer,
      public aura::WindowObserver {
 public:
  using SaveWindowCallback =
      base::RepeatingCallback<void(const app_restore::WindowInfo&)>;

  WindowRestoreController();
  WindowRestoreController(const WindowRestoreController&) = delete;
  WindowRestoreController& operator=(const WindowRestoreController&) = delete;
  ~WindowRestoreController() override;

  // Convenience function to get the controller which is created and owned by
  // Shell.
  static WindowRestoreController* Get();

  // Returns whether a app restored window can be activated. Only ghost
  // windows, windows without the `app_restore::kLaunchedFromAppRestoreKey`,
  // and topmost app restored windows return true.
  static bool CanActivateRestoredWindow(const aura::Window* window);

  // When windows are restored, they're restored inactive so during tablet mode
  // a window may be restored above the app list while the app list is still
  // active. To prevent this situation, the app list is deactivated and this
  // function should be called when determining the next focus target to prevent
  // the app list from being reactivated. Returns true if we're in tablet mode,
  // |window| is the window for the app list, and the topmost window of any
  // active desk container is a restored window.
  static bool CanActivateAppList(const aura::Window* window);

  // Given a set of windows, ordered from LRU to MRU, returns where `window`
  // should be inserted. The insertion point is determined by iterating from LRU
  // to MRU, returning the an iter to the first window that has no activation
  // index or a lower activation index.
  static std::vector<raw_ptr<aura::Window, VectorExperimental>>::const_iterator
  GetWindowToInsertBefore(
      aura::Window* window,
      const std::vector<raw_ptr<aura::Window, VectorExperimental>>& windows);

  const aura::Window* to_be_snapped_window() const {
    return to_be_snapped_window_.get();
  }

  // Calls SaveWindowImpl for |window_state|. The activation index will be
  // calculated in SaveWindowImpl.
  void SaveWindow(WindowState* window_state);

  // Gets all windows on all desk in the MRU window tracker and saves them to
  // file.
  void SaveAllWindows();

  // Called from MruWindowTracker when |gained_active| gets activation. This is
  // not done as an observer to ensure changes to the MRU list get handled first
  // before this is called.
  void OnWindowActivated(aura::Window* gained_active);

  // Stacks the window according to its activation index.
  void StackWindow(aura::Window* window);

  // Returns true if `window` is currently in the process of being restored by
  // `this`.
  bool IsRestoringWindow(aura::Window* window) const;

  // display::DisplayObserver:
  void OnDisplayTabletStateChanged(display::TabletState state) override;

  // app_restore::AppRestoreInfo::Observer:
  void OnRestorePrefChanged(const AccountId& account_id,
                            bool could_restore) override;
  void OnAppLaunched(aura::Window* window) override;
  void OnWidgetInitialized(views::Widget* widget) override;
  void OnParentWindowToValidContainer(aura::Window* window) override;

  // aura::WindowObserver:
  void OnWindowPropertyChanged(aura::Window* window,
                               const void* key,
                               intptr_t old) override;
  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
  void OnWindowDestroying(aura::Window* window) override;

 private:
  friend class WindowRestoreControllerTest;

  // Updates the window state, activation and stacking of `window`. Also
  // observes `window` as we need to do further updates when the window is
  // shown.
  void UpdateAndObserveWindow(aura::Window* window);

  // Calls full_restore::FullRestoreSaveHandler to save to file. The handler has
  // timer to prevent too many writes, but we should limit calls regardless if
  // possible. Optionally passes |activation_index|, which is calculated with
  // respect to the MRU tracker. Calling SaveAllWindows will iterate through
  // the MRU tracker list, so we can pass the activation index during that loop
  // instead of building the MRU list again for each window.
  void SaveWindowImpl(WindowState* window_state,
                      std::optional<int> activation_index);

  // Retrieves the saved `WindowInfo` of `window` and restores its
  // `WindowStateType`. Also creates a post task to clear `window`s
  // `app_restore::kLaunchedFromAppRestoreKey`.
  void RestoreStateTypeAndClearLaunchedKey(aura::Window* window);

  // Calls `CancelAndRemoveRestorePropertyClearCallback()`. Also sets the
  // `window`'s `app_restore::kLaunchedFromAppRestoreKey` to false if the
  // window is not destroying.
  void ClearLaunchedKey(aura::Window* window);

  // Cancels and removes the App Restore property clear callback for `window`
  // from `restore_property_clear_callbacks_`.
  void CancelAndRemoveRestorePropertyClearCallback(aura::Window* window);

  // Sets a callback for testing that will be fired immediately when
  // `SaveWindowImpl()` is about to notify the window restore component we want
  // to write to file.
  void SetSaveWindowCallbackForTesting(SaveWindowCallback callback);

  // True whenever we are attempting to restore snap state.
  // The window that is about to be snapped by window restore. Reset to nullptr
  // if we aren't directly working with the window anymore.
  raw_ptr<aura::Window> to_be_snapped_window_ = nullptr;

  // The set of windows that have had their widgets initialized and will be
  // shown later.
  base::flat_set<raw_ptr<aura::Window, CtnExperimental>> to_be_shown_windows_;

  // When a window is restored, we post a task to clear its
  // `app_restore::kLaunchedFromAppRestoreKey` property. However, a window can
  // be closed before this task occurs, deleting the window. This map keeps
  // track of these posted tasks so we can cancel them upon window deletion.
  std::map<aura::Window*, base::CancelableOnceClosure>
      restore_property_clear_callbacks_;

  display::ScopedDisplayObserver display_observer_{this};

  base::ScopedObservation<app_restore::AppRestoreInfo,
                          app_restore::AppRestoreInfo::Observer>
      app_restore_info_observation_{this};

  // Observes windows launched by window restore.
  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
      windows_observation_{this};

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

}  // namespace ash

#endif  // ASH_WM_WINDOW_RESTORE_WINDOW_RESTORE_CONTROLLER_H_