chromium/chrome/browser/ash/mahi/mahi_manager_impl.h

// 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 CHROME_BROWSER_ASH_MAHI_MAHI_MANAGER_IMPL_H_
#define CHROME_BROWSER_ASH_MAHI_MAHI_MANAGER_IMPL_H_

#include <memory>
#include <string>

#include "ash/system/mahi/mahi_ui_controller.h"
#include "base/memory/raw_ptr.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ash/mahi/mahi_browser_delegate_ash.h"
#include "chrome/browser/ash/mahi/mahi_cache_manager.h"
#include "chromeos/components/magic_boost/public/cpp/magic_boost_state.h"
#include "chromeos/components/mahi/public/cpp/mahi_manager.h"
#include "chromeos/crosapi/mojom/mahi.mojom.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/manta/mahi_provider.h"
#include "ui/gfx/image/image_skia.h"

namespace gfx {
class Rect;
}  // namespace gfx

namespace ash {

class MahiNudgeController;

// Implementation of `MahiManager`.
class MahiManagerImpl : public chromeos::MahiManager,
                        public chromeos::MagicBoostState::Observer,
                        public history::HistoryServiceObserver {
 public:
  MahiManagerImpl();

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

  ~MahiManagerImpl() override;

  // chromeos::MahiManager:
  std::u16string GetContentTitle() override;
  gfx::ImageSkia GetContentIcon() override;
  GURL GetContentUrl() override;
  void GetSummary(MahiSummaryCallback callback) override;
  void GetOutlines(MahiOutlinesCallback callback) override;
  void GoToOutlineContent(int outline_id) override;
  void AnswerQuestion(const std::u16string& question,
                      bool current_panel_content,
                      MahiAnswerQuestionCallback callback) override;
  void AnswerQuestionRepeating(
      const std::u16string& question,
      bool current_panel_content,
      MahiAnswerQuestionCallbackRepeating callback) override;
  void GetSuggestedQuestion(MahiGetSuggestedQuestionCallback callback) override;
  void SetCurrentFocusedPageInfo(crosapi::mojom::MahiPageInfoPtr info) override;
  void OnContextMenuClicked(
      crosapi::mojom::MahiContextMenuRequestPtr context_menu_request) override;
  void OpenFeedbackDialog() override;
  void OpenMahiPanel(int64_t display_id,
                     const gfx::Rect& mahi_menu_bounds) override;
  bool IsEnabled() override;
  void SetMediaAppPDFFocused() override;
  void MediaAppPDFClosed(
      const base::UnguessableToken media_app_client_id) override;
  std::optional<base::UnguessableToken> GetMediaAppPDFClientId() const override;
  void ClearCache() override;
  bool AllowRepeatingAnswers() override;

  // Called when availability for a refresh changes based on the shown content.
  void NotifyRefreshAvailability(bool available);

  MahiUiController* ui_controller_for_test() { return &ui_controller_; }

  // history::HistoryServiceObserver:
  void OnHistoryDeletions(history::HistoryService* history_service,
                          const history::DeletionInfo& deletion_info) override;

 private:
  friend class MahiManagerImplTest;
  friend class MahiManagerImplFeatureKeyTest;

  // chromeos::MagicBoostState::Observer:
  void OnHMREnabledUpdated(bool enabled) override;
  void OnIsDeleting() override;

  // Interrupts the flow of `context_menu_request` handling by showing a
  // disclaimer view. The original flow will be resumed if the consent status
  // becomes approved. NOTE: This function should be called only if the magic
  // boost feature is enabled.
  void InterrputRequestHandlingWithDisclaimerView(
      crosapi::mojom::MahiContextMenuRequestPtr context_menu_request);

  // Initialize required provider if it is not initialized yet, and discard
  // pending requests to avoid racing condition.
  // Returns true if successfully initialized.
  bool MaybeInitializeAndDiscardPendingRequests();

  void MaybeObserveHistoryService();

  void OnGetPageContentForSummary(
      crosapi::mojom::MahiPageInfoPtr request_page_info,
      MahiSummaryCallback callback,
      crosapi::mojom::MahiPageContentPtr mahi_content_ptr);

  void OnGetPageContentForQA(
      crosapi::mojom::MahiPageInfoPtr request_page_info,
      const std::u16string& question,
      MahiAnswerQuestionCallback callback,
      crosapi::mojom::MahiPageContentPtr mahi_content_ptr);

  void OnMahiProviderSummaryResponse(
      crosapi::mojom::MahiPageInfoPtr request_page_info,
      MahiSummaryCallback summary_callback,
      base::Value::Dict dict,
      manta::MantaStatus status);

  void OnMahiProviderQAResponse(
      crosapi::mojom::MahiPageInfoPtr request_page_info,
      const std::u16string& question,
      MahiAnswerQuestionCallback callback,
      base::Value::Dict dict,
      manta::MantaStatus status);

  base::ScopedObservation<chromeos::MagicBoostState,
                          chromeos::MagicBoostState::Observer>
      magic_boost_state_observation_{this};

  // These `Ptr`s should never be null. To invalidate them, assign them a
  // `New()` instead of calling `reset()`.
  crosapi::mojom::MahiPageInfoPtr current_page_info_ =
      crosapi::mojom::MahiPageInfo::New();

  crosapi::mojom::MahiPageContentPtr current_panel_content_ =
      crosapi::mojom::MahiPageContent::New();

  // Stores metadata of the current content in the panel.
  crosapi::mojom::MahiPageInfoPtr current_panel_info_ =
      crosapi::mojom::MahiPageInfo::New();

  // Pair of question and their corresponding answer for the current panel
  // content
  std::vector<std::pair<std::string, std::string>> current_panel_qa_;

  std::unique_ptr<manta::MahiProvider> mahi_provider_;

  raw_ptr<MahiBrowserDelegateAsh> mahi_browser_delegate_ash_ = nullptr;

  // Keeps track of the latest result and code, used for feedback.
  std::u16string latest_summary_;
  chromeos::MahiResponseStatus latest_response_status_;

  MahiUiController ui_controller_;

  std::unique_ptr<MahiCacheManager> cache_manager_;

  std::unique_ptr<MahiNudgeController> mahi_nudge_controller_;

  // If true, tries to get content from MediaAppContentManager instead.
  bool media_app_pdf_focused_ = false;
  base::UnguessableToken media_app_client_id_;

  // Runs the specified closures when the consent state becomes approved or
  // declined. Built when handling particular context menu actions without the
  // Mahi feature approved by user. Destroyed when user responds to the
  // disclaimer view.
  // NOTE: It is used only when the magic boost feature is enabled.
  std::unique_ptr<chromeos::MagicBoostState::Observer>
      on_consent_state_update_closure_runner_;

  base::ScopedObservation<history::HistoryService,
                          history::HistoryServiceObserver>
      scoped_history_service_observer_{this};

  base::WeakPtrFactory<MahiManagerImpl> weak_ptr_factory_for_closure_runner_{
      this};

  base::WeakPtrFactory<MahiManagerImpl> weak_ptr_factory_for_requests_{this};
};

// ScopedMahiBrowserDelegateOverrider ------------------------------------------

// A helper class to override the Mahi browser delegate during its life cycle.
// NOTE: This class should have at most one instance.
class ScopedMahiBrowserDelegateOverrider {
 public:
  explicit ScopedMahiBrowserDelegateOverrider(MahiBrowserDelegateAsh* delegate);
  ScopedMahiBrowserDelegateOverrider(
      const ScopedMahiBrowserDelegateOverrider&) = delete;
  ScopedMahiBrowserDelegateOverrider& operator=(
      const ScopedMahiBrowserDelegateOverrider&) = delete;
  ~ScopedMahiBrowserDelegateOverrider();
};

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_MAHI_MAHI_MANAGER_IMPL_H_