910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_COLLABORATION_INTERNAL_COLLABORATION_CONTROLLER_H_
#define COMPONENTS_COLLABORATION_INTERNAL_COLLABORATION_CONTROLLER_H_

#include <array>
#include <map>
#include <memory>

#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "components/collaboration/public/collaboration_controller_delegate.h"
#include "components/collaboration/public/collaboration_flow_type.h"
#include "components/collaboration/public/collaboration_service.h"
#include "components/data_sharing/public/data_sharing_service.h"
#include "components/data_sharing/public/group_data.h"
#include "components/saved_tab_groups/public/tab_group_sync_service.h"
#include "components/saved_tab_groups/public/types.h"
#include "components/signin/public/identity_manager/identity_manager.h"

namespace syncer {
class SyncService;
}  // namespace syncer

namespace collaboration {

class ControllerState;

// The class for managing a single collaboration group flow.
class CollaborationController
    : public tab_groups::TabGroupSyncService::Observer,
      public CollaborationService::Observer {
 public:
  // States of a collaboration group flow. All new flows starts PENDNG.
  enum class StateId {
    // Initial state. The request has been received, awaiting delegate to be
    // initialized and authentication status to be verified.
    kPending,

    // Waiting on more information about a potentially managed account.
    kWaitingForPolicyUpdate,

    // UI is showing authentication screens (sign-in/sync/access token). Waiting
    // for result.
    kAuthenticating,

    // Waiting for tab group sync service and data sharing service to be ready
    // to use.
    kWaitingForServicesToInitialize,

    // Authentication is completed. Controller will check requirements for each
    // specific flows.
    kCheckingFlowRequirements,

    // Delegate is showing invitation screen to the user.
    kAddingUserToGroup,

    // Waiting for tab group to be added in sync and people group to be added in
    // DataSharing. Loading UI should be shown.
    kWaitingForSyncAndDataSharingGroup,

    // Delegate is promoting the local tab group.
    kOpeningLocalTabGroup,

    // Delegate is showing the share sheet.
    kShowingShareScreen,

    // Delegate requested creating a shared tab group.
    kMakingTabGroupShared,

    // Delegate is sharing the tab group's url.
    kSharingTabGroupUrl,

    // Delegate is showing the manage people screen.
    kShowingManageScreen,

    // Delegate is showing the leave group screen.
    kLeavingGroup,

    // Delegate is showing the delete group screen.
    kDeletingGroup,

    // A shared tab group has been deleted, cleaning up.
    kCleaningUpSharedTabGroup,

    // The flow is cancelled.
    kCancel,

    // An error occurred and need to be shown to the user.
    kError,
  };

  class Flow {
   public:
    // Join flow constructor.
    Flow(FlowType type, const data_sharing::GroupToken& token);

    // Share/manage/leave/delete flow constructor.
    Flow(FlowType type, const tab_groups::EitherGroupID& either_id);

    ~Flow();

    Flow(const Flow&);

    const FlowType type;

    const data_sharing::GroupToken& join_token() const {
      DCHECK_EQ(type, FlowType::kJoin);
      return join_token_;
    }

    const tab_groups::EitherGroupID& either_id() const {
      return either_id_;
    }

    const data_sharing::GroupToken& share_token() const {
      DCHECK_EQ(type, FlowType::kShareOrManage);
      CHECK(share_token_.IsValid());
      return share_token_;
    }

    void set_share_token(const data_sharing::GroupToken& token) {
      share_token_ = token;
    }

   private:
    // ID for join flow.
    const data_sharing::GroupToken join_token_;

    // ID for share/manage/leave/delete flow.
    const tab_groups::EitherGroupID either_id_;
    data_sharing::GroupToken share_token_;
  };

  using FinishCallback = base::OnceCallback<void(const void*)>;

  explicit CollaborationController(
      const Flow& flow,
      CollaborationService* collaboration_service,
      data_sharing::DataSharingService* data_sharing_service,
      tab_groups::TabGroupSyncService* tab_group_sync_service,
      syncer::SyncService* sync_service,
      signin::IdentityManager* identity_manager,
      std::unique_ptr<CollaborationControllerDelegate> delegate,
      FinishCallback finish_and_delete);
  ~CollaborationController() override;

  // Disallow copy/assign.
  CollaborationController(const CollaborationController&) = delete;
  CollaborationController& operator=(const CollaborationController&) = delete;

  // Getters.
  CollaborationControllerDelegate* delegate() { return delegate_.get(); }
  data_sharing::DataSharingService* data_sharing_service() {
    return data_sharing_service_.get();
  }
  tab_groups::TabGroupSyncService* tab_group_sync_service() {
    return tab_group_sync_service_.get();
  }
  syncer::SyncService* sync_service() { return sync_service_.get(); }
  signin::IdentityManager* identity_manager() {
    return identity_manager_.get();
  }
  CollaborationService* collaboration_service() {
    return collaboration_service_.get();
  }
  Flow& flow() { return flow_; }
  base::Time flow_start_time() const { return flow_start_time_; }

  // Called to transition to another state.
  void TransitionTo(StateId state,
                    const CollaborationControllerDelegate::ErrorInfo& error =
                        CollaborationControllerDelegate::ErrorInfo());

  // Called to refocus the current flow.
  void PromoteCurrentSession();

  // Called when the flow is finished to exit and clean itself up in the
  // service.
  void Exit();

  // Cancels and exits the current flow.
  void Cancel();

  // Helper functions used in tests.
  void SetStateForTesting(StateId state);
  StateId GetStateForTesting();

  // TabGroupSyncService::Observer implementation.
  void OnTabGroupRemoved(const tab_groups::LocalTabGroupID& local_id,
                         tab_groups::TriggerSource source) override;
  void OnTabGroupRemoved(const base::Uuid& sync_id,
                         tab_groups::TriggerSource source) override;
  void OnTabGroupMigrated(const tab_groups::SavedTabGroup& new_group,
                          const base::Uuid& old_sync_id,
                          tab_groups::TriggerSource source) override;

  // CollaborationService::Observer implementation.
  void OnServiceStatusChanged(
      const CollaborationService::Observer::ServiceStatusUpdate& update)
      override;

 private:
  base::Time flow_start_time_;
  static constexpr std::array<std::pair<StateId, StateId>, 53>
      kValidTransitions = {{
          // Note: All state transition to kCancel when exiting.
          {StateId::kPending, StateId::kCancel},
          {StateId::kWaitingForPolicyUpdate, StateId::kCancel},
          {StateId::kAuthenticating, StateId::kCancel},
          {StateId::kWaitingForServicesToInitialize, StateId::kCancel},
          {StateId::kCheckingFlowRequirements, StateId::kCancel},
          {StateId::kAddingUserToGroup, StateId::kCancel},
          {StateId::kWaitingForSyncAndDataSharingGroup, StateId::kCancel},
          {StateId::kOpeningLocalTabGroup, StateId::kCancel},
          {StateId::kShowingShareScreen, StateId::kCancel},
          {StateId::kMakingTabGroupShared, StateId::kCancel},
          {StateId::kSharingTabGroupUrl, StateId::kCancel},
          {StateId::kShowingManageScreen, StateId::kCancel},
          {StateId::kLeavingGroup, StateId::kCancel},
          {StateId::kDeletingGroup, StateId::kCancel},
          {StateId::kCleaningUpSharedTabGroup, StateId::kCancel},
          {StateId::kError, StateId::kCancel},
          //
          // kPending transitions to:
          //
          //   kAuthenticating: After all initialization steps complete
          //   successfully and authentication status is not valid.
          //   kWaitingForPolicyUpdate: Current account info are not ready.
          //   kCheckingFlowRequirements: After all initialization steps
          //   complete successfully and authentication status is valid.
          //   kError: An error occurred during initialization.
          {StateId::kPending, StateId::kAuthenticating},
          {StateId::kPending, StateId::kWaitingForPolicyUpdate},
          {StateId::kPending, StateId::kWaitingForServicesToInitialize},
          {StateId::kPending, StateId::kError},

          // kWaitingForPolicyUpdate transitions to:
          //
          //   kAuthenticating: Current account is not managed and sync consent
          //   is needed.
          //   kCheckingFlowRequirements: Current account is not managed.
          //   kError: Current account is managed.
          {StateId::kWaitingForPolicyUpdate, StateId::kAuthenticating},
          {StateId::kWaitingForPolicyUpdate,
           StateId::kCheckingFlowRequirements},
          {StateId::kWaitingForPolicyUpdate, StateId::kError},

          // kAuthenticating transitions to:
          //
          //   kWaitingForPolicyUpdate: Current account info are not ready.
          //   kCheckingFlowRequirements: After all authentication steps are
          //   completed and verified.
          //   kError: An error occurred during authentication.
          {StateId::kAuthenticating, StateId::kWaitingForPolicyUpdate},
          {StateId::kAuthenticating, StateId::kWaitingForServicesToInitialize},
          {StateId::kAuthenticating, StateId::kError},

          // kWaitingForServicesToInitialize transition to:
          //
          //   kCheckingFlowRequirements: After all services finish
          //   initializing.
          //   kError: An error occurred while waiting for service
          //   initialization.
          {StateId::kWaitingForServicesToInitialize,
           StateId::kCheckingFlowRequirements},
          {StateId::kWaitingForServicesToInitialize, StateId::kError},

          // kCheckingFlowRequirements transition to:
          //
          //   kAddingUserToGroup: When user is not in current people group.
          //   kWaitingForSyncAndDataSharingGroup: When user is in current
          //   people group,
          //   but tab group not found in sync.
          //   kOpeningLocalTabGroup: When user is in current people group, and
          //   tab group found in sync.
          //   kShowingShareScreen: In share flow, when the tab group is not
          //   shared.
          //   kShowingManageScreen: In share flow, when the tab group is a
          //   shared tab group.
          //   kError: An error occurred while checking requirements. This could
          //   be due to version mismatch.
          {StateId::kCheckingFlowRequirements, StateId::kAddingUserToGroup},
          {StateId::kCheckingFlowRequirements,
           StateId::kWaitingForSyncAndDataSharingGroup},
          {StateId::kCheckingFlowRequirements, StateId::kOpeningLocalTabGroup},
          {StateId::kCheckingFlowRequirements, StateId::kShowingShareScreen},
          {StateId::kCheckingFlowRequirements, StateId::kShowingManageScreen},
          {StateId::kCheckingFlowRequirements, StateId::kLeavingGroup},
          {StateId::kCheckingFlowRequirements, StateId::kDeletingGroup},
          {StateId::kCheckingFlowRequirements, StateId::kError},

          // kAddingUserToGroup transition to:
          //
          //   kWaitingForSyncAndDataSharingGroup: After the user accept the
          //   join
          //   invitation and the tab group is not yet added in sync.
          //   kOpeningLocalTabGroup: After the user accept the join invitation
          //   and the tab group is in sync.
          //   kError: An error occurred during invitation screen.
          {StateId::kAddingUserToGroup,
           StateId::kWaitingForSyncAndDataSharingGroup},
          {StateId::kAddingUserToGroup, StateId::kOpeningLocalTabGroup},
          {StateId::kAddingUserToGroup, StateId::kError},

          // kWaitingForSyncAndDataSharingGroup transition to:
          //
          //   kOpeningLocalTabGroup: After tab group is added in sync.
          //   kError: An error occurred while waiting for sync tab group.
          {StateId::kWaitingForSyncAndDataSharingGroup,
           StateId::kOpeningLocalTabGroup},
          {StateId::kWaitingForSyncAndDataSharingGroup, StateId::kError},

          // kOpeningLocalTabGroup transition to:
          //
          //   kError: An error occurred while opening local tab group.
          {StateId::kOpeningLocalTabGroup, StateId::kError},

          // kShowingShareScreen transition to:
          //
          //   kSharingTabGroupUrl: After share screen request creating a shared
          //   tab group.
          //   kError: An error occurred while showing the share screen.
          {StateId::kShowingShareScreen, StateId::kMakingTabGroupShared},
          {StateId::kShowingShareScreen, StateId::kError},

          // kMakingTabGroupShared transition to:
          //
          //   kSharingTabGroupUrl: After shared tab group is successfully
          //   created.
          //   kError: An error occurred while creating the shared tab group.
          {StateId::kMakingTabGroupShared, StateId::kSharingTabGroupUrl},
          {StateId::kMakingTabGroupShared, StateId::kError},

          // kSharingTabGroupUrl transition to:
          //
          //   kError: An error occurred while sharing the url.
          {StateId::kSharingTabGroupUrl, StateId::kError},

          // kShowingManageScreen transition to:
          //
          //   kCleaningUpSharedTabGroup: When deletion happened on a manage
          //   screen.
          //   kError: An error occurred while showing the manage people screen.
          {StateId::kShowingManageScreen, StateId::kCleaningUpSharedTabGroup},
          {StateId::kShowingManageScreen, StateId::kError},

          // kShowingManageScreen transition to:
          //
          //   kCleaningUpSharedTabGroup: After leaving group successfully.
          //   kError: An error occurred while leaving group.
          {StateId::kLeavingGroup, StateId::kCleaningUpSharedTabGroup},
          {StateId::kLeavingGroup, StateId::kError},

          // kDeletingGroup transition to:
          //
          //   kCleaningUpSharedTabGroup: When deletion has been completed.
          //   kError: An error occurred while deleting group.
          {StateId::kDeletingGroup, StateId::kCleaningUpSharedTabGroup},
          {StateId::kDeletingGroup, StateId::kError},
      }};

  // Note: This would only cancel the flow if the flow was started with the same
  // variant of the EitherGroupID. The caller must call this method using both
  // sync ID and local ID to be sure that the flow for the tab group is
  // cancelled.
  void CancelShareOrManageFlow(const tab_groups::EitherGroupID& either_id);
  bool IsValidStateTransition(StateId from, StateId to);
  std::unique_ptr<ControllerState> CreateStateObject(StateId state);
  void Start();

  THREAD_CHECKER(thread_checker_);

  std::unique_ptr<ControllerState> current_state_;

  Flow flow_;
  bool is_deleting_{false};
  const raw_ptr<CollaborationService> collaboration_service_;
  const raw_ptr<data_sharing::DataSharingService> data_sharing_service_;
  const raw_ptr<tab_groups::TabGroupSyncService> tab_group_sync_service_;
  const raw_ptr<syncer::SyncService> sync_service_;
  const raw_ptr<signin::IdentityManager> identity_manager_;
  std::unique_ptr<CollaborationControllerDelegate> delegate_;
  FinishCallback finish_and_delete_;
  base::ScopedObservation<tab_groups::TabGroupSyncService,
                          tab_groups::TabGroupSyncService::Observer>
      tab_group_sync_service_observer_{this};
  base::ScopedObservation<CollaborationService, CollaborationService::Observer>
      collaboration_service_observer_{this};
  base::WeakPtrFactory<CollaborationController> weak_ptr_factory_{this};
};

}  // namespace collaboration

#endif  // COMPONENTS_COLLABORATION_INTERNAL_COLLABORATION_CONTROLLER_H_