| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/speech/tts_ash.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/notreached.h" |
| #include "base/unguessable_token.h" |
| #include "chrome/browser/ash/crosapi/browser_util.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/speech/crosapi_tts_engine_delegate_ash.h" |
| #include "chrome/browser/speech/tts_crosapi_util.h" |
| #include "chromeos/crosapi/mojom/tts.mojom.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/tts_controller.h" |
| |
| namespace crosapi { |
| |
| TtsAsh::TtsAsh(ProfileManager* profile_manager) |
| : profile_manager_(profile_manager), |
| primary_profile_browser_context_id_(base::UnguessableToken::Null()) { |
| DCHECK(profile_manager_); |
| profile_manager_observation_.Observe(profile_manager); |
| voices_changed_observation_.Observe(content::TtsController::GetInstance()); |
| } |
| |
| TtsAsh::~TtsAsh() = default; |
| |
| void TtsAsh::BindReceiver(mojo::PendingReceiver<mojom::Tts> pending_receiver) { |
| receivers_.Add(this, std::move(pending_receiver)); |
| } |
| |
| bool TtsAsh::HasTtsClient() const { |
| return tts_clients_.size() > 0; |
| } |
| |
| base::UnguessableToken TtsAsh::GetPrimaryProfileBrowserContextId() const { |
| return primary_profile_browser_context_id_; |
| } |
| |
| void TtsAsh::RegisterTtsClient(mojo::PendingRemote<mojom::TtsClient> client, |
| const base::UnguessableToken& browser_context_id, |
| bool from_primary_profile) { |
| DCHECK(from_primary_profile); |
| if (from_primary_profile) |
| primary_profile_browser_context_id_ = browser_context_id; |
| |
| mojo::Remote<mojom::TtsClient> remote(std::move(client)); |
| remote.set_disconnect_handler(base::BindOnce(&TtsAsh::TtsClientDisconnected, |
| weak_ptr_factory_.GetWeakPtr(), |
| browser_context_id)); |
| tts_clients_.emplace(browser_context_id, std::move(remote)); |
| } |
| |
| void TtsAsh::VoicesChanged(const base::UnguessableToken& browser_context_id, |
| std::vector<mojom::TtsVoicePtr> lacros_voices) { |
| // TODO(crbug.com/1251979): Support secondary profile. |
| DCHECK(HasTtsClient() && |
| browser_context_id == primary_profile_browser_context_id_); |
| |
| std::vector<content::VoiceData> voices; |
| for (const auto& mojo_voice : lacros_voices) |
| voices.push_back(tts_crosapi_util::FromMojo(mojo_voice)); |
| |
| // Cache Lacros voices. |
| crosapi_voices_[browser_context_id] = std::move(voices); |
| |
| // Notify TtsController about VoicesChanged. |
| content::TtsController::GetInstance()->VoicesChanged(); |
| } |
| |
| void TtsAsh::GetCrosapiVoices(base::UnguessableToken browser_context_id, |
| std::vector<content::VoiceData>* out_voices) { |
| // Returns the cached Lacros voices. |
| auto it_voices = crosapi_voices_.find(browser_context_id); |
| if (it_voices != crosapi_voices_.end()) { |
| for (auto voice : it_voices->second) { |
| out_voices->push_back(voice); |
| } |
| } |
| } |
| |
| void TtsAsh::OnVoicesChanged() { |
| if (!HasTtsClient()) |
| return; |
| |
| // Notify Lacros about voices change in Ash's TtsController. |
| // TtsController in ash manages all the voices from both Ash and Lacros, |
| // which is the ultimate truth of source to return all the voices when |
| // asked by Lacros. |
| std::vector<content::VoiceData> all_voices; |
| content::TtsController::GetInstance()->GetVoices( |
| ProfileManager::GetActiveUserProfile(), GURL(), &all_voices); |
| |
| // Convert to mojo voices. |
| std::vector<crosapi::mojom::TtsVoicePtr> mojo_voices; |
| for (const auto& voice : all_voices) |
| mojo_voices.push_back(tts_crosapi_util::ToMojo(voice)); |
| |
| auto item = tts_clients_.find(primary_profile_browser_context_id_); |
| DCHECK(item != tts_clients_.end()); |
| item->second->VoicesChanged(std::move(mojo_voices)); |
| } |
| |
| void TtsAsh::OnProfileAdded(Profile* profile) { |
| if (tts_crosapi_util::ShouldEnableLacrosTtsSupport()) { |
| content::TtsController::GetInstance()->SetRemoteTtsEngineDelegate( |
| CrosapiTtsEngineDelegateAsh::GetInstance()); |
| } |
| } |
| |
| void TtsAsh::OnProfileManagerDestroying() { |
| profile_manager_observation_.Reset(); |
| profile_manager_ = nullptr; |
| } |
| |
| void TtsAsh::TtsClientDisconnected( |
| const base::UnguessableToken& browser_context_id) { |
| tts_clients_.erase(browser_context_id); |
| if (browser_context_id == primary_profile_browser_context_id_) |
| primary_profile_browser_context_id_ = base::UnguessableToken::Null(); |
| |
| // Remove the cached lacros voices. |
| size_t erase_count = crosapi_voices_.erase(browser_context_id); |
| if (erase_count > 0) |
| content::TtsController::GetInstance()->VoicesChanged(); |
| } |
| |
| } // namespace crosapi |