Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 1 | // Copyright (c) 2020 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/accessibility/caption_controller.h" |
| 6 | |
Abigail Klein | 5eb53a4 | 2020-11-08 22:06:15 | [diff] [blame] | 7 | #include <memory> |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 8 | |
| 9 | #include "base/bind.h" |
evliu | 60658b2 | 2020-08-31 18:55:43 | [diff] [blame] | 10 | #include "base/command_line.h" |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 11 | #include "base/feature_list.h" |
Katie D | 31f377f3 | 2020-05-05 19:22:04 | [diff] [blame] | 12 | #include "base/metrics/histogram_functions.h" |
Abigail Klein | a81f2e2 | 2020-05-04 14:32:41 | [diff] [blame] | 13 | #include "chrome/browser/accessibility/caption_util.h" |
Abigail Klein | 5eb53a4 | 2020-11-08 22:06:15 | [diff] [blame] | 14 | #include "chrome/browser/accessibility/soda_installer.h" |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 15 | #include "chrome/browser/browser_process.h" |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 16 | #include "chrome/browser/profiles/profile.h" |
| 17 | #include "chrome/browser/ui/browser.h" |
Abigail Klein | 22b0633 | 2020-04-30 20:57:18 | [diff] [blame] | 18 | #include "chrome/browser/ui/browser_finder.h" |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 19 | #include "chrome/browser/ui/browser_list.h" |
| 20 | #include "chrome/browser/ui/caption_bubble_controller.h" |
| 21 | #include "chrome/common/pref_names.h" |
| 22 | #include "components/pref_registry/pref_registry_syncable.h" |
| 23 | #include "components/prefs/pref_change_registrar.h" |
evliu | 5826f33 | 2020-08-21 21:01:22 | [diff] [blame] | 24 | #include "components/soda/constants.h" |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 25 | #include "components/sync_preferences/pref_service_syncable.h" |
Katie D | 31f377f3 | 2020-05-05 19:22:04 | [diff] [blame] | 26 | #include "content/public/browser/browser_accessibility_state.h" |
Abigail Klein | 22b0633 | 2020-04-30 20:57:18 | [diff] [blame] | 27 | #include "content/public/browser/web_contents.h" |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 28 | #include "media/base/media_switches.h" |
| 29 | |
Abigail Klein | a81f2e2 | 2020-05-04 14:32:41 | [diff] [blame] | 30 | namespace { |
| 31 | |
| 32 | const char* const kCaptionStylePrefsToObserve[] = { |
| 33 | prefs::kAccessibilityCaptionsTextSize, |
| 34 | prefs::kAccessibilityCaptionsTextFont, |
| 35 | prefs::kAccessibilityCaptionsTextColor, |
| 36 | prefs::kAccessibilityCaptionsTextOpacity, |
| 37 | prefs::kAccessibilityCaptionsBackgroundColor, |
| 38 | prefs::kAccessibilityCaptionsTextShadow, |
| 39 | prefs::kAccessibilityCaptionsBackgroundOpacity}; |
| 40 | |
evliu | 854ee3e | 2020-11-05 00:27:54 | [diff] [blame] | 41 | constexpr int kSodaCleanUpDelayInDays = 30; |
| 42 | |
Abigail Klein | a81f2e2 | 2020-05-04 14:32:41 | [diff] [blame] | 43 | } // namespace |
| 44 | |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 45 | namespace captions { |
| 46 | |
| 47 | CaptionController::CaptionController(Profile* profile) : profile_(profile) {} |
| 48 | |
| 49 | CaptionController::~CaptionController() = default; |
| 50 | |
| 51 | // static |
| 52 | void CaptionController::RegisterProfilePrefs( |
| 53 | user_prefs::PrefRegistrySyncable* registry) { |
| 54 | registry->RegisterBooleanPref( |
| 55 | prefs::kLiveCaptionEnabled, false, |
| 56 | user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
evliu | 5826f33 | 2020-08-21 21:01:22 | [diff] [blame] | 57 | |
| 58 | // Initially default the language to en-US. |
| 59 | registry->RegisterStringPref(prefs::kLiveCaptionLanguageCode, "en-US"); |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 60 | } |
| 61 | |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 62 | void CaptionController::Init() { |
| 63 | // Hidden behind a feature flag. |
| 64 | if (!base::FeatureList::IsEnabled(media::kLiveCaption)) |
| 65 | return; |
| 66 | |
| 67 | pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); |
| 68 | pref_change_registrar_->Init(profile_->GetPrefs()); |
evliu | 60658b2 | 2020-08-31 18:55:43 | [diff] [blame] | 69 | auto* command_line = base::CommandLine::ForCurrentProcess(); |
| 70 | if (command_line && |
| 71 | command_line->HasSwitch(switches::kEnableLiveCaptionPrefForTesting)) { |
| 72 | profile_->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, true); |
| 73 | } |
| 74 | |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 75 | pref_change_registrar_->Add( |
| 76 | prefs::kLiveCaptionEnabled, |
| 77 | base::BindRepeating(&CaptionController::OnLiveCaptionEnabledChanged, |
| 78 | base::Unretained(this))); |
evliu | 5826f33 | 2020-08-21 21:01:22 | [diff] [blame] | 79 | pref_change_registrar_->Add( |
| 80 | prefs::kLiveCaptionLanguageCode, |
| 81 | base::BindRepeating(&CaptionController::OnLiveCaptionLanguageChanged, |
| 82 | base::Unretained(this))); |
Abigail Klein | 225ff31 | 2020-04-17 16:52:33 | [diff] [blame] | 83 | |
| 84 | enabled_ = IsLiveCaptionEnabled(); |
| 85 | if (enabled_) |
| 86 | UpdateUIEnabled(); |
Katie D | 31f377f3 | 2020-05-05 19:22:04 | [diff] [blame] | 87 | |
| 88 | content::BrowserAccessibilityState::GetInstance() |
| 89 | ->AddUIThreadHistogramCallback(base::BindOnce( |
| 90 | &CaptionController::UpdateAccessibilityCaptionHistograms, |
| 91 | base::Unretained(this))); |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | void CaptionController::OnLiveCaptionEnabledChanged() { |
Abigail Klein | 225ff31 | 2020-04-17 16:52:33 | [diff] [blame] | 95 | bool enabled = IsLiveCaptionEnabled(); |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 96 | if (enabled == enabled_) |
| 97 | return; |
| 98 | enabled_ = enabled; |
Abigail Klein | 225ff31 | 2020-04-17 16:52:33 | [diff] [blame] | 99 | |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 100 | if (enabled_) { |
Abigail Klein | 5eb53a4 | 2020-11-08 22:06:15 | [diff] [blame] | 101 | // Register SODA component and download speech model. |
evliu | 854ee3e | 2020-11-05 00:27:54 | [diff] [blame] | 102 | g_browser_process->local_state()->SetTime(prefs::kSodaScheduledDeletionTime, |
| 103 | base::Time()); |
Abigail Klein | 5eb53a4 | 2020-11-08 22:06:15 | [diff] [blame] | 104 | speech::SODAInstaller::GetInstance()->InstallSODA(profile_->GetPrefs()); |
| 105 | speech::SODAInstaller::GetInstance()->InstallLanguage(profile_->GetPrefs()); |
Abigail Klein | 225ff31 | 2020-04-17 16:52:33 | [diff] [blame] | 106 | } else { |
evliu | 854ee3e | 2020-11-05 00:27:54 | [diff] [blame] | 107 | // Schedule SODA to be deleted in 30 days if the feature is not enabled |
| 108 | // before then. |
| 109 | g_browser_process->local_state()->SetTime( |
| 110 | prefs::kSodaScheduledDeletionTime, |
| 111 | base::Time::Now() + base::TimeDelta::FromDays(kSodaCleanUpDelayInDays)); |
Abigail Klein | 225ff31 | 2020-04-17 16:52:33 | [diff] [blame] | 112 | } |
Abigail Klein | 5eb53a4 | 2020-11-08 22:06:15 | [diff] [blame] | 113 | UpdateUIEnabled(); |
Abigail Klein | 225ff31 | 2020-04-17 16:52:33 | [diff] [blame] | 114 | } |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 115 | |
Abigail Klein | 5eb53a4 | 2020-11-08 22:06:15 | [diff] [blame] | 116 | void CaptionController::OnLiveCaptionLanguageChanged() { |
| 117 | if (enabled_) |
| 118 | speech::SODAInstaller::GetInstance()->InstallLanguage(profile_->GetPrefs()); |
| 119 | } |
| 120 | |
| 121 | bool CaptionController::IsLiveCaptionEnabled() { |
| 122 | PrefService* profile_prefs = profile_->GetPrefs(); |
| 123 | return profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled); |
| 124 | } |
evliu | 5826f33 | 2020-08-21 21:01:22 | [diff] [blame] | 125 | |
Abigail Klein | 225ff31 | 2020-04-17 16:52:33 | [diff] [blame] | 126 | void CaptionController::UpdateUIEnabled() { |
| 127 | if (enabled_) { |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 128 | // Create captions UI in each browser view. |
| 129 | for (Browser* browser : *BrowserList::GetInstance()) { |
| 130 | OnBrowserAdded(browser); |
| 131 | } |
| 132 | |
| 133 | // Add observers to the BrowserList for new browser views being added. |
| 134 | BrowserList::GetInstance()->AddObserver(this); |
Abigail Klein | a81f2e2 | 2020-05-04 14:32:41 | [diff] [blame] | 135 | |
| 136 | // Observe caption style prefs. |
| 137 | for (const char* const pref_name : kCaptionStylePrefsToObserve) { |
| 138 | pref_change_registrar_->Add( |
| 139 | pref_name, base::BindRepeating(&CaptionController::UpdateCaptionStyle, |
| 140 | base::Unretained(this))); |
| 141 | } |
| 142 | UpdateCaptionStyle(); |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 143 | } else { |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 144 | // Destroy caption bubble controllers. |
| 145 | caption_bubble_controllers_.clear(); |
| 146 | |
| 147 | // Remove observers. |
| 148 | BrowserList::GetInstance()->RemoveObserver(this); |
Abigail Klein | a81f2e2 | 2020-05-04 14:32:41 | [diff] [blame] | 149 | |
| 150 | // Remove prefs to observe. |
| 151 | for (const char* const pref_name : kCaptionStylePrefsToObserve) { |
| 152 | pref_change_registrar_->Remove(pref_name); |
| 153 | } |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 154 | } |
| 155 | } |
| 156 | |
Katie D | 31f377f3 | 2020-05-05 19:22:04 | [diff] [blame] | 157 | void CaptionController::UpdateAccessibilityCaptionHistograms() { |
evliu | 5ac4038 | 2020-08-31 20:55:26 | [diff] [blame] | 158 | base::UmaHistogramBoolean("Accessibility.LiveCaption", enabled_); |
Katie D | 31f377f3 | 2020-05-05 19:22:04 | [diff] [blame] | 159 | } |
| 160 | |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 161 | void CaptionController::OnBrowserAdded(Browser* browser) { |
Abigail Klein | 212966a | 2020-06-22 22:50:39 | [diff] [blame] | 162 | if (browser->profile() != profile_ && |
| 163 | browser->profile()->GetOriginalProfile() != profile_) { |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 164 | return; |
Abigail Klein | 212966a | 2020-06-22 22:50:39 | [diff] [blame] | 165 | } |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 166 | |
Abigail Klein | 212966a | 2020-06-22 22:50:39 | [diff] [blame] | 167 | DCHECK(!caption_bubble_controllers_.count(browser)); |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 168 | caption_bubble_controllers_[browser] = |
| 169 | CaptionBubbleController::Create(browser); |
Abigail Klein | a81f2e2 | 2020-05-04 14:32:41 | [diff] [blame] | 170 | caption_bubble_controllers_[browser]->UpdateCaptionStyle(caption_style_); |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | void CaptionController::OnBrowserRemoved(Browser* browser) { |
Abigail Klein | 212966a | 2020-06-22 22:50:39 | [diff] [blame] | 174 | if (browser->profile() != profile_ && |
| 175 | browser->profile()->GetOriginalProfile() != profile_) { |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 176 | return; |
Abigail Klein | 212966a | 2020-06-22 22:50:39 | [diff] [blame] | 177 | } |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 178 | |
| 179 | DCHECK(caption_bubble_controllers_.count(browser)); |
| 180 | caption_bubble_controllers_.erase(browser); |
| 181 | } |
| 182 | |
Abigail Klein | 80f9ca1 | 2020-06-08 17:06:33 | [diff] [blame] | 183 | bool CaptionController::DispatchTranscription( |
Abigail Klein | 4c51ac4 | 2020-05-06 19:45:02 | [diff] [blame] | 184 | content::WebContents* web_contents, |
Abigail Klein | 2903761 | 2020-05-04 23:59:45 | [diff] [blame] | 185 | const chrome::mojom::TranscriptionResultPtr& transcription_result) { |
Abigail Klein | 4c51ac4 | 2020-05-06 19:45:02 | [diff] [blame] | 186 | Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
Katie D | de318c3 | 2020-05-21 18:09:34 | [diff] [blame] | 187 | if (!browser || !caption_bubble_controllers_.count(browser)) |
Abigail Klein | 80f9ca1 | 2020-06-08 17:06:33 | [diff] [blame] | 188 | return false; |
| 189 | return caption_bubble_controllers_[browser]->OnTranscription( |
| 190 | transcription_result, web_contents); |
Abigail Klein | 22b0633 | 2020-04-30 20:57:18 | [diff] [blame] | 191 | } |
| 192 | |
Abigail Klein | 8dc47fe6 | 2020-08-13 18:46:17 | [diff] [blame] | 193 | void CaptionController::OnError(content::WebContents* web_contents) { |
| 194 | Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
| 195 | if (!browser || !caption_bubble_controllers_.count(browser)) |
| 196 | return; |
| 197 | return caption_bubble_controllers_[browser]->OnError(web_contents); |
| 198 | } |
| 199 | |
Katie D | de318c3 | 2020-05-21 18:09:34 | [diff] [blame] | 200 | CaptionBubbleController* |
| 201 | CaptionController::GetCaptionBubbleControllerForBrowser(Browser* browser) { |
| 202 | if (!browser || !caption_bubble_controllers_.count(browser)) |
| 203 | return nullptr; |
| 204 | return caption_bubble_controllers_[browser].get(); |
| 205 | } |
| 206 | |
Abigail Klein | a81f2e2 | 2020-05-04 14:32:41 | [diff] [blame] | 207 | void CaptionController::UpdateCaptionStyle() { |
| 208 | PrefService* profile_prefs = profile_->GetPrefs(); |
| 209 | // Metrics are recorded when passing the caption prefs to the browser, so do |
| 210 | // not duplicate them here. |
| 211 | caption_style_ = GetCaptionStyleFromUserSettings(profile_prefs, |
| 212 | false /* record_metrics */); |
| 213 | |
| 214 | for (const auto& item : caption_bubble_controllers_) { |
| 215 | caption_bubble_controllers_[item.first]->UpdateCaptionStyle(caption_style_); |
| 216 | } |
| 217 | } |
| 218 | |
Abigail Klein | 4a4ac27 | 2020-04-07 17:47:15 | [diff] [blame] | 219 | } // namespace captions |