1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/speech/tts_controller_delegate_impl.h"
6 
7 #include <stddef.h>
8 
9 #include <string>
10 
11 #include "base/json/json_reader.h"
12 #include "base/values.h"
13 #include "build/build_config.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/pref_names.h"
17 #include "components/prefs/pref_service.h"
18 #include "content/public/browser/tts_controller.h"
19 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
20 #include "ui/base/l10n/l10n_util.h"
21 
22 namespace {
23 
24 base::Optional<content::TtsControllerDelegate::PreferredVoiceId>
PreferredVoiceIdFromString(const base::DictionaryValue * pref,const std::string & pref_key)25 PreferredVoiceIdFromString(const base::DictionaryValue* pref,
26                            const std::string& pref_key) {
27   std::string voice_id;
28   pref->GetString(l10n_util::GetLanguage(pref_key), &voice_id);
29   if (voice_id.empty())
30     return base::nullopt;
31 
32   std::unique_ptr<base::DictionaryValue> json =
33       base::DictionaryValue::From(base::JSONReader::ReadDeprecated(voice_id));
34   std::string name;
35   std::string id;
36   json->GetString("name", &name);
37   json->GetString("extension", &id);
38   return base::Optional<content::TtsControllerDelegate::PreferredVoiceId>(
39       {name, id});
40 }
41 
42 }  // namespace
43 
44 //
45 // TtsControllerDelegateImpl
46 //
47 
48 // static
GetInstance()49 TtsControllerDelegateImpl* TtsControllerDelegateImpl::GetInstance() {
50   return base::Singleton<TtsControllerDelegateImpl>::get();
51 }
52 
53 TtsControllerDelegateImpl::TtsControllerDelegateImpl() = default;
54 
55 TtsControllerDelegateImpl::~TtsControllerDelegateImpl() = default;
56 
57 std::unique_ptr<content::TtsControllerDelegate::PreferredVoiceIds>
GetPreferredVoiceIdsForUtterance(content::TtsUtterance * utterance)58 TtsControllerDelegateImpl::GetPreferredVoiceIdsForUtterance(
59     content::TtsUtterance* utterance) {
60   const base::DictionaryValue* lang_to_voice_pref =
61       GetLangToVoicePref(utterance);
62   if (!lang_to_voice_pref)
63     return nullptr;
64 
65   std::unique_ptr<PreferredVoiceIds> preferred_ids =
66       std::make_unique<PreferredVoiceIds>();
67 
68   if (!utterance->GetLang().empty()) {
69     preferred_ids->lang_voice_id = PreferredVoiceIdFromString(
70         lang_to_voice_pref, l10n_util::GetLanguage(utterance->GetLang()));
71   }
72 
73   const std::string app_lang = g_browser_process->GetApplicationLocale();
74   preferred_ids->locale_voice_id = PreferredVoiceIdFromString(
75       lang_to_voice_pref, l10n_util::GetLanguage(app_lang));
76 
77   preferred_ids->any_locale_voice_id =
78       PreferredVoiceIdFromString(lang_to_voice_pref, "noLanguageCode");
79   return preferred_ids;
80 }
81 
UpdateUtteranceDefaultsFromPrefs(content::TtsUtterance * utterance,double * rate,double * pitch,double * volume)82 void TtsControllerDelegateImpl::UpdateUtteranceDefaultsFromPrefs(
83     content::TtsUtterance* utterance,
84     double* rate,
85     double* pitch,
86     double* volume) {
87   // Update pitch, rate and volume from user prefs if not set explicitly
88   // on this utterance.
89   const PrefService* prefs = GetPrefService(utterance);
90   if (*rate == blink::mojom::kSpeechSynthesisDoublePrefNotSet) {
91     *rate = prefs ? prefs->GetDouble(prefs::kTextToSpeechRate)
92                   : blink::mojom::kSpeechSynthesisDefaultRate;
93   }
94   if (*pitch == blink::mojom::kSpeechSynthesisDoublePrefNotSet) {
95     *pitch = prefs ? prefs->GetDouble(prefs::kTextToSpeechPitch)
96                    : blink::mojom::kSpeechSynthesisDefaultPitch;
97   }
98   if (*volume == blink::mojom::kSpeechSynthesisDoublePrefNotSet) {
99     *volume = prefs ? prefs->GetDouble(prefs::kTextToSpeechVolume)
100                     : blink::mojom::kSpeechSynthesisDefaultVolume;
101   }
102 }
103 
GetPrefService(content::TtsUtterance * utterance)104 const PrefService* TtsControllerDelegateImpl::GetPrefService(
105     content::TtsUtterance* utterance) {
106   // The utterance->GetBrowserContext() is null in tests.
107   if (!utterance->GetBrowserContext())
108     return nullptr;
109 
110   const Profile* profile =
111       Profile::FromBrowserContext(utterance->GetBrowserContext());
112   return profile ? profile->GetPrefs() : nullptr;
113 }
114 
GetLangToVoicePref(content::TtsUtterance * utterance)115 const base::DictionaryValue* TtsControllerDelegateImpl::GetLangToVoicePref(
116     content::TtsUtterance* utterance) {
117   const PrefService* prefs = GetPrefService(utterance);
118   return prefs == nullptr
119              ? nullptr
120              : prefs->GetDictionary(prefs::kTextToSpeechLangToVoiceName);
121 }
122