chromium/chrome/browser/ui/webui/ash/settings/pages/a11y/settings_with_tts_preview_handler.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ash/settings/pages/a11y/settings_with_tts_preview_handler.h"

#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
#include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/tts_controller.h"
#include "content/public/browser/web_ui.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/manifest_handlers/options_page_info.h"
#include "ui/base/l10n/l10n_util.h"

namespace ash::settings {

SettingsWithTtsPreviewHandler::SettingsWithTtsPreviewHandler() = default;

SettingsWithTtsPreviewHandler::~SettingsWithTtsPreviewHandler() {
  RemoveTtsControllerDelegates();
}

void SettingsWithTtsPreviewHandler::HandleGetAllTtsVoiceData(
    const base::Value::List& args) {
  OnVoicesChanged();
}

void SettingsWithTtsPreviewHandler::OnTtsEvent(
    content::TtsUtterance* utterance,
    content::TtsEventType event_type,
    int char_index,
    int length,
    const std::string& error_message) {
  if (event_type == content::TTS_EVENT_END ||
      event_type == content::TTS_EVENT_INTERRUPTED ||
      event_type == content::TTS_EVENT_ERROR) {
    base::Value result(false /* preview stopped */);
    FireWebUIListener("tts-preview-state-changed", result);
  }
}

void SettingsWithTtsPreviewHandler::HandlePreviewTtsVoice(
    const base::Value::List& args) {
  DCHECK_EQ(2U, args.size());
  const std::string& text = args[0].GetString();
  const std::string& voice_id = args[1].GetString();

  if (text.empty() || voice_id.empty())
    return;

  std::optional<base::Value> json = base::JSONReader::Read(voice_id);
  std::string name;
  std::string extension_id;
  if (const std::string* ptr = json->GetDict().FindString("name"))
    name = *ptr;
  if (const std::string* ptr = json->GetDict().FindString("extension"))
    extension_id = *ptr;

  std::unique_ptr<content::TtsUtterance> utterance =
      content::TtsUtterance::Create(web_ui()->GetWebContents());
  utterance->SetText(text);
  utterance->SetVoiceName(name);
  utterance->SetEngineId(extension_id);
  utterance->SetSrcUrl(GetSourceURL());
  utterance->SetEventDelegate(this);
  content::TtsController::GetInstance()->Stop();

  base::Value result(true /* preview started */);
  FireWebUIListener("tts-preview-state-changed", result);
  content::TtsController::GetInstance()->SpeakOrEnqueue(std::move(utterance));
}

void SettingsWithTtsPreviewHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "getAllTtsVoiceData",
      base::BindRepeating(
          &SettingsWithTtsPreviewHandler::HandleGetAllTtsVoiceData,
          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "refreshTtsVoices",
      base::BindRepeating(&SettingsWithTtsPreviewHandler::RefreshTtsVoices,
                          base::Unretained(this)));
}

void SettingsWithTtsPreviewHandler::OnJavascriptAllowed() {
  content::TtsController::GetInstance()->AddVoicesChangedDelegate(this);
}

void SettingsWithTtsPreviewHandler::OnJavascriptDisallowed() {
  RemoveTtsControllerDelegates();
}

void SettingsWithTtsPreviewHandler::RefreshTtsVoices(
    const base::Value::List& args) {
  content::TtsController::GetInstance()->RefreshVoices();
}

void SettingsWithTtsPreviewHandler::RemoveTtsControllerDelegates() {
  content::TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
  content::TtsController::GetInstance()->RemoveUtteranceEventDelegate(this);
}

}  // namespace ash::settings