chromium/chrome/browser/speech/tts_client_lacros.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 CHROME_BROWSER_SPEECH_TTS_CLIENT_LACROS_H_
#define CHROME_BROWSER_SPEECH_TTS_CLIENT_LACROS_H_

#include <memory>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/unguessable_token.h"
#include "chromeos/crosapi/mojom/tts.mojom.h"
#include "content/public/browser/tts_controller.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/base/network_change_notifier.h"

namespace content {
class BrowserContext;
}

// Implements crosapi::mojom::TtsClient, which is called by ash to handle
// TTS requests to Lacros such as retrieving voice data, etc. It also manages
// To send TTS requests ash. TtsClientLacros is created per BrowserContext.
class TtsClientLacros
    : public extensions::BrowserContextKeyedAPI,
      public crosapi::mojom::TtsClient,
      public net::NetworkChangeNotifier::NetworkChangeObserver,
      public extensions::EventRouter::Observer {
 public:
  explicit TtsClientLacros(content::BrowserContext* context);
  TtsClientLacros(const TtsClientLacros&) = delete;
  TtsClientLacros& operator=(const TtsClientLacros&) = delete;
  ~TtsClientLacros() override;

  // crosapi::mojom::TtsClient:
  void VoicesChanged(
      std::vector<crosapi::mojom::TtsVoicePtr> mojo_all_voices) override;
  void SpeakWithLacrosVoice(
      crosapi::mojom::TtsUtterancePtr utterance,
      crosapi::mojom::TtsVoicePtr voice,
      mojo::PendingRemote<crosapi::mojom::TtsUtteranceClient>
          ash_utterance_client) override;
  void Stop(const std::string& engine_id) override;
  void Pause(const std::string& engine_id) override;
  void Resume(const std::string& engine_id) override;

  const base::UnguessableToken& browser_context_id() const {
    return browser_context_id_;
  }

  // Returns the cached voices in |out_voices|, which are the voices available
  // for Lacros including the ones provided by both Ash and Lacros.
  void GetAllVoices(std::vector<content::VoiceData>* out_voices);

  // Forwards the given utterance to Ash to be processed by Ash TtsController.
  void SpeakOrEnqueue(std::unique_ptr<content::TtsUtterance> utterance);

  // Forwards the Stop request (for stopping the current utterance if it matches
  // the given |source_url|) to Ash.
  void RequestStop(const GURL& source_url);

  // Forwards the Pause request from Lacros Tts client (Tts extension api or
  // speechSynthesis web api) to Ash, so that the request will be processed by
  // Ash's TtsController.
  void RequestPause();

  // Forwards the Resume request from Lacros Tts client (Tts extension api or
  // speechSynthesis web api) to Ash, so that the request will be processed by
  // Ash's TtsController.
  void RequestResume();

  // Forwards the request to query Ash TtsController's IsSpeaking() state and
  // returns the result in callback.
  void IsSpeaking(base::OnceCallback<void(bool)> callback);

  // Handle events received from the Lacros speech engine.
  void OnLacrosSpeechEngineTtsEvent(int utterance_id,
                                    content::TtsEventType event_type,
                                    int char_index,
                                    int length,
                                    const std::string& error_message);
  void OnAshUtteranceFinished(int utterance_id);
  void OnAshUtteranceBecameInvalid(int utterance_id);

  void DeletePendingUtteranceClient(int utterance_id);

  content::BrowserContext* browser_context() { return browser_context_; }

  static TtsClientLacros* GetForBrowserContext(
      content::BrowserContext* context);

 private:
  class TtsUtteraneClient;
  class AshUtteranceEventDelegate;
  friend class extensions::BrowserContextKeyedAPIFactory<TtsClientLacros>;

  // net::NetworkChangeNotifier::NetworkChangeObserver:
  void OnNetworkChanged(
      net::NetworkChangeNotifier::ConnectionType type) override;

  // extensions::EventRouter::Observer:
  void OnListenerAdded(const extensions::EventListenerInfo& details) override;
  void OnListenerRemoved(const extensions::EventListenerInfo& details) override;

  bool IsLoadedTtsEngine(const std::string& extension_id) const;
  // Notifies Ash about Lacros voices change.
  void NotifyLacrosVoicesChanged();

  void OnGetAllVoices(std::vector<crosapi::mojom::TtsVoicePtr> mojo_voices);

  // KeyedServivce:
  void Shutdown() override;

  raw_ptr<content::BrowserContext> browser_context_;  // not owned.
  base::UnguessableToken browser_context_id_;
  mojo::Receiver<crosapi::mojom::TtsClient> receiver_{this};

  // Cached voices for |browser_context_|, including both ash and lacros voices.
  std::vector<content::VoiceData> all_voices_;

  bool is_offline_;

  // Pending Lacros Tts Utterance clients by by utterance id.
  std::map<int, std::unique_ptr<TtsUtteraneClient>> pending_utterance_clients_;

  // Pending Ash utterance to be spoken with Lacros speech engine.
  std::unique_ptr<content::TtsUtterance> pending_ash_utterance_;
  std::unique_ptr<AshUtteranceEventDelegate> ash_utterance_event_delegate_;

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

#endif  // CHROME_BROWSER_SPEECH_TTS_CLIENT_LACROS_H_