Commit 5826f334 authored by evliu's avatar evliu Committed by Commit Bot

Split SODA into language components

This CL splits the SODA component into a single SODA binary component
and multiple language pack components.

Todo: Update the settings UI reflect the download progress of the
SODA binary and the language pack(s).

Todo: Verify that the SODA binary component and the language pack
component is installed before launching the Live caption feature.

Change-Id: Ia82ac3b46da2d37ce1e0fae9d6cd7fb541d67d60
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2340873Reviewed-by: default avatarJoshua Pawlicki <waffles@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarMatthew Denton <mpdenton@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Evan Liu <evliu@google.com>
Cr-Commit-Position: refs/heads/master@{#800723}
parent 87071b64
...@@ -3139,6 +3139,10 @@ static_library("browser") { ...@@ -3139,6 +3139,10 @@ static_library("browser") {
"component_updater/intervention_policy_database_component_installer.h", "component_updater/intervention_policy_database_component_installer.h",
"component_updater/soda_component_installer.cc", "component_updater/soda_component_installer.cc",
"component_updater/soda_component_installer.h", "component_updater/soda_component_installer.h",
"component_updater/soda_en_us_component_installer.cc",
"component_updater/soda_en_us_component_installer.h",
"component_updater/soda_ja_jp_component_installer.cc",
"component_updater/soda_ja_jp_component_installer.h",
"content_settings/generated_cookie_prefs.cc", "content_settings/generated_cookie_prefs.cc",
"content_settings/generated_cookie_prefs.h", "content_settings/generated_cookie_prefs.h",
"custom_handlers/register_protocol_handler_permission_request.cc", "custom_handlers/register_protocol_handler_permission_request.cc",
......
include_rules = [ include_rules = [
"+components/soda",
"+services/image_annotation", "+services/image_annotation",
] ]
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h" #include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "components/soda/constants.h"
#include "components/sync_preferences/pref_service_syncable.h" #include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/browser/browser_accessibility_state.h" #include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
...@@ -52,6 +53,10 @@ void CaptionController::RegisterProfilePrefs( ...@@ -52,6 +53,10 @@ void CaptionController::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterFilePathPref(prefs::kSodaBinaryPath, base::FilePath()); registry->RegisterFilePathPref(prefs::kSodaBinaryPath, base::FilePath());
registry->RegisterFilePathPref(prefs::kSodaEnUsConfigPath, base::FilePath()); registry->RegisterFilePathPref(prefs::kSodaEnUsConfigPath, base::FilePath());
registry->RegisterFilePathPref(prefs::kSodaJaJpConfigPath, base::FilePath());
// Initially default the language to en-US.
registry->RegisterStringPref(prefs::kLiveCaptionLanguageCode, "en-US");
} }
void CaptionController::Init() { void CaptionController::Init() {
...@@ -65,6 +70,10 @@ void CaptionController::Init() { ...@@ -65,6 +70,10 @@ void CaptionController::Init() {
prefs::kLiveCaptionEnabled, prefs::kLiveCaptionEnabled,
base::BindRepeating(&CaptionController::OnLiveCaptionEnabledChanged, base::BindRepeating(&CaptionController::OnLiveCaptionEnabledChanged,
base::Unretained(this))); base::Unretained(this)));
pref_change_registrar_->Add(
prefs::kLiveCaptionLanguageCode,
base::BindRepeating(&CaptionController::OnLiveCaptionLanguageChanged,
base::Unretained(this)));
enabled_ = IsLiveCaptionEnabled(); enabled_ = IsLiveCaptionEnabled();
if (enabled_) if (enabled_)
...@@ -83,9 +92,14 @@ void CaptionController::OnLiveCaptionEnabledChanged() { ...@@ -83,9 +92,14 @@ void CaptionController::OnLiveCaptionEnabledChanged() {
enabled_ = enabled; enabled_ = enabled;
UpdateSpeechRecognitionServiceEnabled(); UpdateSpeechRecognitionServiceEnabled();
UpdateSpeechRecognitionLanguage();
UpdateUIEnabled(); UpdateUIEnabled();
} }
void CaptionController::OnLiveCaptionLanguageChanged() {
UpdateSpeechRecognitionLanguage();
}
bool CaptionController::IsLiveCaptionEnabled() { bool CaptionController::IsLiveCaptionEnabled() {
PrefService* profile_prefs = profile_->GetPrefs(); PrefService* profile_prefs = profile_->GetPrefs();
return profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled); return profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled);
...@@ -104,6 +118,13 @@ void CaptionController::UpdateSpeechRecognitionServiceEnabled() { ...@@ -104,6 +118,13 @@ void CaptionController::UpdateSpeechRecognitionServiceEnabled() {
} }
} }
void CaptionController::UpdateSpeechRecognitionLanguage() {
if (enabled_) {
component_updater::RegisterSodaLanguageComponent(
g_browser_process->component_updater(), profile_->GetPrefs());
}
} // namespace captions
void CaptionController::UpdateUIEnabled() { void CaptionController::UpdateUIEnabled() {
if (enabled_) { if (enabled_) {
// Create captions UI in each browser view. // Create captions UI in each browser view.
......
...@@ -93,8 +93,10 @@ class CaptionController : public BrowserListObserver, public KeyedService { ...@@ -93,8 +93,10 @@ class CaptionController : public BrowserListObserver, public KeyedService {
void OnBrowserRemoved(Browser* browser) override; void OnBrowserRemoved(Browser* browser) override;
void OnLiveCaptionEnabledChanged(); void OnLiveCaptionEnabledChanged();
void OnLiveCaptionLanguageChanged();
bool IsLiveCaptionEnabled(); bool IsLiveCaptionEnabled();
void UpdateSpeechRecognitionServiceEnabled(); void UpdateSpeechRecognitionServiceEnabled();
void UpdateSpeechRecognitionLanguage();
void UpdateUIEnabled(); void UpdateUIEnabled();
void UpdateCaptionStyle(); void UpdateCaptionStyle();
......
...@@ -183,6 +183,7 @@ void RegisterComponentsForUpdate(bool is_off_the_record_profile, ...@@ -183,6 +183,7 @@ void RegisterComponentsForUpdate(bool is_off_the_record_profile,
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
component_updater::RegisterSODAComponent(cus, profile_prefs, component_updater::RegisterSODAComponent(cus, profile_prefs,
base::OnceClosure()); base::OnceClosure());
component_updater::RegisterSodaLanguageComponent(cus, profile_prefs);
#endif #endif
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/soda_en_us_component_installer.h"
#include "chrome/browser/component_updater/soda_ja_jp_component_installer.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chrome/services/speech/buildflags.h" #include "chrome/services/speech/buildflags.h"
#include "components/component_updater/component_updater_service.h" #include "components/component_updater/component_updater_service.h"
...@@ -36,9 +38,6 @@ static_assert(base::size(kSODAPublicKeySHA256) == crypto::kSHA256Length, ...@@ -36,9 +38,6 @@ static_assert(base::size(kSODAPublicKeySHA256) == crypto::kSHA256Length,
const char kSODAManifestName[] = "SODA Library"; const char kSODAManifestName[] = "SODA Library";
constexpr base::FilePath::CharType kSodaEnUsConfigFileRelativePath[] =
FILE_PATH_LITERAL("SODAFiles/en_us/dictation.ascii_proto");
} // namespace } // namespace
SODAComponentInstallerPolicy::SODAComponentInstallerPolicy( SODAComponentInstallerPolicy::SODAComponentInstallerPolicy(
...@@ -78,7 +77,7 @@ bool SODAComponentInstallerPolicy::SupportsGroupPolicyEnabledComponentUpdates() ...@@ -78,7 +77,7 @@ bool SODAComponentInstallerPolicy::SupportsGroupPolicyEnabledComponentUpdates()
} }
bool SODAComponentInstallerPolicy::RequiresNetworkEncryption() const { bool SODAComponentInstallerPolicy::RequiresNetworkEncryption() const {
return false; return true;
} }
update_client::CrxInstaller::Result update_client::CrxInstaller::Result
...@@ -127,8 +126,6 @@ void UpdateSODAInstallDirPref(PrefService* prefs, ...@@ -127,8 +126,6 @@ void UpdateSODAInstallDirPref(PrefService* prefs,
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
prefs->SetFilePath(prefs::kSodaBinaryPath, prefs->SetFilePath(prefs::kSodaBinaryPath,
install_dir.Append(speech::kSodaBinaryRelativePath)); install_dir.Append(speech::kSodaBinaryRelativePath));
prefs->SetFilePath(prefs::kSodaEnUsConfigPath,
install_dir.Append(kSodaEnUsConfigFileRelativePath));
#endif #endif
} }
...@@ -172,6 +169,32 @@ void RegisterSODAComponent(ComponentUpdateService* cus, ...@@ -172,6 +169,32 @@ void RegisterSODAComponent(ComponentUpdateService* cus,
#endif #endif
} }
void RegisterSodaLanguageComponent(ComponentUpdateService* cus,
PrefService* prefs) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(ENABLE_SODA)
speech::LanguageCode language = speech::GetLanguageCode(
prefs->GetString(prefs::kLiveCaptionLanguageCode));
switch (language) {
case speech::LanguageCode::kNone:
// Do nothing.
break;
case speech::LanguageCode::kEnUs:
RegisterSodaEnUsComponent(
cus, prefs,
base::BindOnce(&SodaEnUsComponentInstallerPolicy::
UpdateSodaEnUsComponentOnDemand));
break;
case speech::LanguageCode::kJaJp:
RegisterSodaJaJpComponent(
cus, prefs,
base::BindOnce(&SodaJaJpComponentInstallerPolicy::
UpdateSodaJaJpComponentOnDemand));
break;
}
#endif
}
bool UninstallSODAComponent(ComponentUpdateService* cus, PrefService* prefs) { bool UninstallSODAComponent(ComponentUpdateService* cus, PrefService* prefs) {
return cus->UnregisterComponent( return cus->UnregisterComponent(
SODAComponentInstallerPolicy::GetExtensionId()); SODAComponentInstallerPolicy::GetExtensionId());
......
...@@ -60,6 +60,9 @@ void RegisterSODAComponent(ComponentUpdateService* cus, ...@@ -60,6 +60,9 @@ void RegisterSODAComponent(ComponentUpdateService* cus,
PrefService* prefs, PrefService* prefs,
base::OnceClosure callback); base::OnceClosure callback);
void RegisterSodaLanguageComponent(ComponentUpdateService* cus,
PrefService* prefs);
bool UninstallSODAComponent(ComponentUpdateService* cus, PrefService* prefs); bool UninstallSODAComponent(ComponentUpdateService* cus, PrefService* prefs);
} // namespace component_updater } // namespace component_updater
......
// Copyright 2020 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/component_updater/soda_en_us_component_installer.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
#include "chrome/services/speech/buildflags.h"
#include "components/component_updater/component_updater_service.h"
#include "components/crx_file/id_util.h"
#include "components/soda/constants.h"
#include "components/update_client/update_client_errors.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/sha2.h"
using content::BrowserThread;
namespace component_updater {
namespace {
// The SHA256 of the SubjectPublicKeyInfo used to sign the component.
// The component id is: oegebmmcimckjhkhbggblnkjloogjdfg
const uint8_t kSodaEnUsPublicKeySHA256[32] = {
0xe4, 0x64, 0x1c, 0xc2, 0x8c, 0x2a, 0x97, 0xa7, 0x16, 0x61, 0xbd,
0xa9, 0xbe, 0xe6, 0x93, 0x56, 0xf5, 0x05, 0x33, 0x9b, 0x8b, 0x0b,
0x02, 0xe2, 0x6b, 0x7e, 0x6c, 0x40, 0xa1, 0xd2, 0x7e, 0x18};
static_assert(base::size(kSodaEnUsPublicKeySHA256) == crypto::kSHA256Length,
"Wrong hash length");
const char kSodaEnUsManifestName[] = "SODA en-US Models";
constexpr base::FilePath::CharType kSodaEnUsConfigFileRelativePath[] =
FILE_PATH_LITERAL("SODAModels/dictation.ascii_proto");
} // namespace
SodaEnUsComponentInstallerPolicy::SodaEnUsComponentInstallerPolicy(
OnSodaEnUsComponentReadyCallback callback)
: on_component_ready_callback_(callback) {}
SodaEnUsComponentInstallerPolicy::~SodaEnUsComponentInstallerPolicy() = default;
const std::string SodaEnUsComponentInstallerPolicy::GetExtensionId() {
return crx_file::id_util::GenerateIdFromHash(
kSodaEnUsPublicKeySHA256, sizeof(kSodaEnUsPublicKeySHA256));
}
void SodaEnUsComponentInstallerPolicy::UpdateSodaEnUsComponentOnDemand() {
const std::string crx_id =
component_updater::SodaEnUsComponentInstallerPolicy::GetExtensionId();
g_browser_process->component_updater()->GetOnDemandUpdater().OnDemandUpdate(
crx_id, component_updater::OnDemandUpdater::Priority::FOREGROUND,
base::BindOnce([](update_client::Error error) {
if (error != update_client::Error::NONE &&
error != update_client::Error::UPDATE_IN_PROGRESS)
LOG(ERROR) << "On demand update of the SODA en-US component failed "
"with error: "
<< static_cast<int>(error);
}));
}
bool SodaEnUsComponentInstallerPolicy::VerifyInstallation(
const base::DictionaryValue& manifest,
const base::FilePath& install_dir) const {
return base::PathExists(install_dir.Append(kSodaEnUsConfigFileRelativePath));
}
bool SodaEnUsComponentInstallerPolicy::
SupportsGroupPolicyEnabledComponentUpdates() const {
return true;
}
bool SodaEnUsComponentInstallerPolicy::RequiresNetworkEncryption() const {
return true;
}
update_client::CrxInstaller::Result
SodaEnUsComponentInstallerPolicy::OnCustomInstall(
const base::DictionaryValue& manifest,
const base::FilePath& install_dir) {
return update_client::CrxInstaller::Result(0); // Nothing custom here.
}
void SodaEnUsComponentInstallerPolicy::OnCustomUninstall() {}
void SodaEnUsComponentInstallerPolicy::ComponentReady(
const base::Version& version,
const base::FilePath& install_dir,
std::unique_ptr<base::DictionaryValue> manifest) {
VLOG(1) << "Component ready, version " << version.GetString() << " in "
<< install_dir.value();
on_component_ready_callback_.Run(install_dir);
}
base::FilePath SodaEnUsComponentInstallerPolicy::GetRelativeInstallDir() const {
return base::FilePath(speech::kSodaEnUsInstallationRelativePath);
}
void SodaEnUsComponentInstallerPolicy::GetHash(
std::vector<uint8_t>* hash) const {
hash->assign(kSodaEnUsPublicKeySHA256,
kSodaEnUsPublicKeySHA256 + base::size(kSodaEnUsPublicKeySHA256));
}
std::string SodaEnUsComponentInstallerPolicy::GetName() const {
return kSodaEnUsManifestName;
}
update_client::InstallerAttributes
SodaEnUsComponentInstallerPolicy::GetInstallerAttributes() const {
return update_client::InstallerAttributes();
}
std::vector<std::string> SodaEnUsComponentInstallerPolicy::GetMimeTypes()
const {
return std::vector<std::string>();
}
void UpdateSodaEnUsInstallDirPref(PrefService* prefs,
const base::FilePath& install_dir) {
#if !defined(OS_ANDROID)
prefs->SetFilePath(prefs::kSodaEnUsConfigPath,
install_dir.Append(kSodaEnUsConfigFileRelativePath));
#endif
}
void RegisterSodaEnUsComponent(ComponentUpdateService* cus,
PrefService* prefs,
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(ENABLE_SODA)
if (!prefs->GetBoolean(prefs::kLiveCaptionEnabled))
return;
auto installer = base::MakeRefCounted<ComponentInstaller>(
std::make_unique<SodaEnUsComponentInstallerPolicy>(base::BindRepeating(
[](ComponentUpdateService* cus, PrefService* prefs,
const base::FilePath& install_dir) {
if (prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
->PostTask(FROM_HERE,
base::BindOnce(&UpdateSodaEnUsInstallDirPref,
prefs, install_dir));
}
},
cus, prefs)));
installer->Register(cus, std::move(callback));
#endif
}
bool UninstallSodaEnUsComponent(ComponentUpdateService* cus,
PrefService* prefs) {
return cus->UnregisterComponent(
SodaEnUsComponentInstallerPolicy::GetExtensionId());
}
} // namespace component_updater
// Copyright 2020 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.
#ifndef CHROME_BROWSER_COMPONENT_UPDATER_SODA_EN_US_COMPONENT_INSTALLER_H_
#define CHROME_BROWSER_COMPONENT_UPDATER_SODA_EN_US_COMPONENT_INSTALLER_H_
#include <string>
#include "components/component_updater/component_installer.h"
#include "components/prefs/pref_service.h"
#include "components/update_client/update_client.h"
namespace component_updater {
// Success callback to be run after the component is downloaded.
using OnSodaEnUsComponentReadyCallback =
base::RepeatingCallback<void(const base::FilePath&)>;
class SodaEnUsComponentInstallerPolicy : public ComponentInstallerPolicy {
public:
explicit SodaEnUsComponentInstallerPolicy(
OnSodaEnUsComponentReadyCallback callback);
~SodaEnUsComponentInstallerPolicy() override;
SodaEnUsComponentInstallerPolicy(const SodaEnUsComponentInstallerPolicy&) =
delete;
SodaEnUsComponentInstallerPolicy& operator=(
const SodaEnUsComponentInstallerPolicy&) = delete;
static const std::string GetExtensionId();
static void UpdateSodaEnUsComponentOnDemand();
private:
FRIEND_TEST_ALL_PREFIXES(SodaEnUsComponentInstallerTest,
ComponentReady_CallsLambda);
// The following methods override ComponentInstallerPolicy.
bool SupportsGroupPolicyEnabledComponentUpdates() const override;
bool RequiresNetworkEncryption() const override;
update_client::CrxInstaller::Result OnCustomInstall(
const base::DictionaryValue& manifest,
const base::FilePath& install_dir) override;
void OnCustomUninstall() override;
bool VerifyInstallation(const base::DictionaryValue& manifest,
const base::FilePath& install_dir) const override;
void ComponentReady(const base::Version& version,
const base::FilePath& install_dir,
std::unique_ptr<base::DictionaryValue> manifest) override;
base::FilePath GetRelativeInstallDir() const override;
void GetHash(std::vector<uint8_t>* hash) const override;
std::string GetName() const override;
update_client::InstallerAttributes GetInstallerAttributes() const override;
std::vector<std::string> GetMimeTypes() const override;
OnSodaEnUsComponentReadyCallback on_component_ready_callback_;
};
// Call once during startup to make the component update service aware of
// the File Type Policies component.
void RegisterSodaEnUsComponent(ComponentUpdateService* cus,
PrefService* prefs,
base::OnceClosure callback);
bool UninstallSodaEnUsComponent(ComponentUpdateService* cus,
PrefService* prefs);
} // namespace component_updater
#endif // CHROME_BROWSER_COMPONENT_UPDATER_SODA_EN_US_COMPONENT_INSTALLER_H_
// Copyright 2020 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/component_updater/soda_en_us_component_installer.h"
#include "base/files/file_path.h"
#include "base/test/bind_test_util.h"
#include "base/version.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace component_updater {
class SodaEnUsComponentInstallerTest : public ::testing::Test {
public:
SodaEnUsComponentInstallerTest()
: fake_install_dir_(FILE_PATH_LITERAL("base/install/dir/")),
fake_version_("0.0.1") {}
protected:
base::FilePath fake_install_dir_;
base::Version fake_version_;
};
TEST_F(SodaEnUsComponentInstallerTest, ComponentReady_CallsLambda) {
base::FilePath given_path;
OnSodaEnUsComponentReadyCallback lambda = base::BindLambdaForTesting(
[&](const base::FilePath& path) { given_path = path; });
SodaEnUsComponentInstallerPolicy policy(std::move(lambda));
policy.ComponentReady(fake_version_, fake_install_dir_,
std::make_unique<base::DictionaryValue>());
ASSERT_EQ(fake_install_dir_, given_path);
}
} // namespace component_updater
// Copyright 2020 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/component_updater/soda_ja_jp_component_installer.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
#include "chrome/services/speech/buildflags.h"
#include "components/component_updater/component_updater_service.h"
#include "components/crx_file/id_util.h"
#include "components/soda/constants.h"
#include "components/update_client/update_client_errors.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/sha2.h"
using content::BrowserThread;
namespace component_updater {
namespace {
// The SHA256 of the SubjectPublicKeyInfo used to sign the component.
// The component id is: onhpjgkfgajmkkeniaoflicgokpaebfa
const uint8_t kSodaJaJpPublicKeySHA256[32] = {
0xed, 0x7f, 0x96, 0xa5, 0x60, 0x9c, 0xaa, 0x4d, 0x80, 0xe5, 0xb8,
0x26, 0xea, 0xf0, 0x41, 0x50, 0x09, 0x52, 0xa4, 0xb3, 0x1e, 0x6a,
0x8e, 0x24, 0x99, 0xde, 0x51, 0x14, 0xc4, 0x3c, 0xfa, 0x48};
static_assert(base::size(kSodaJaJpPublicKeySHA256) == crypto::kSHA256Length,
"Wrong hash length");
const char kSodaJaJpManifestName[] = "SODA ja-JP Models";
constexpr base::FilePath::CharType kSodaJaJpConfigFileRelativePath[] =
FILE_PATH_LITERAL("SODAModels/dictation.ascii_proto");
} // namespace
SodaJaJpComponentInstallerPolicy::SodaJaJpComponentInstallerPolicy(
OnSodaJaJpComponentReadyCallback callback)
: on_component_ready_callback_(callback) {}
SodaJaJpComponentInstallerPolicy::~SodaJaJpComponentInstallerPolicy() = default;
const std::string SodaJaJpComponentInstallerPolicy::GetExtensionId() {
return crx_file::id_util::GenerateIdFromHash(
kSodaJaJpPublicKeySHA256, sizeof(kSodaJaJpPublicKeySHA256));
}
void SodaJaJpComponentInstallerPolicy::UpdateSodaJaJpComponentOnDemand() {
const std::string crx_id =
component_updater::SodaJaJpComponentInstallerPolicy::GetExtensionId();
g_browser_process->component_updater()->GetOnDemandUpdater().OnDemandUpdate(
crx_id, component_updater::OnDemandUpdater::Priority::FOREGROUND,
base::BindOnce([](update_client::Error error) {
if (error != update_client::Error::NONE &&
error != update_client::Error::UPDATE_IN_PROGRESS)
LOG(ERROR) << "On demand update of the SODA ja-JP component failed "
"with error: "
<< static_cast<int>(error);
}));
}
bool SodaJaJpComponentInstallerPolicy::VerifyInstallation(
const base::DictionaryValue& manifest,
const base::FilePath& install_dir) const {
return base::PathExists(install_dir.Append(kSodaJaJpConfigFileRelativePath));
}
bool SodaJaJpComponentInstallerPolicy::
SupportsGroupPolicyEnabledComponentUpdates() const {
return true;
}
bool SodaJaJpComponentInstallerPolicy::RequiresNetworkEncryption() const {
return true;
}
update_client::CrxInstaller::Result
SodaJaJpComponentInstallerPolicy::OnCustomInstall(
const base::DictionaryValue& manifest,
const base::FilePath& install_dir) {
return update_client::CrxInstaller::Result(0); // Nothing custom here.
}
void SodaJaJpComponentInstallerPolicy::OnCustomUninstall() {}
void SodaJaJpComponentInstallerPolicy::ComponentReady(
const base::Version& version,
const base::FilePath& install_dir,
std::unique_ptr<base::DictionaryValue> manifest) {
VLOG(1) << "Component ready, version " << version.GetString() << " in "
<< install_dir.value();
on_component_ready_callback_.Run(install_dir);
}
base::FilePath SodaJaJpComponentInstallerPolicy::GetRelativeInstallDir() const {
return base::FilePath(speech::kSodaJaJpInstallationRelativePath);
}
void SodaJaJpComponentInstallerPolicy::GetHash(
std::vector<uint8_t>* hash) const {
hash->assign(kSodaJaJpPublicKeySHA256,
kSodaJaJpPublicKeySHA256 + base::size(kSodaJaJpPublicKeySHA256));
}
std::string SodaJaJpComponentInstallerPolicy::GetName() const {
return kSodaJaJpManifestName;
}
update_client::InstallerAttributes
SodaJaJpComponentInstallerPolicy::GetInstallerAttributes() const {
return update_client::InstallerAttributes();
}
std::vector<std::string> SodaJaJpComponentInstallerPolicy::GetMimeTypes()
const {
return std::vector<std::string>();
}
void UpdateSodaJaJpInstallDirPref(PrefService* prefs,
const base::FilePath& install_dir) {
#if !defined(OS_ANDROID)
prefs->SetFilePath(prefs::kSodaJaJpConfigPath,
install_dir.Append(kSodaJaJpConfigFileRelativePath));
#endif
}
void RegisterSodaJaJpComponent(ComponentUpdateService* cus,
PrefService* prefs,
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(ENABLE_SODA)
if (!prefs->GetBoolean(prefs::kLiveCaptionEnabled))
return;
auto installer = base::MakeRefCounted<ComponentInstaller>(
std::make_unique<SodaJaJpComponentInstallerPolicy>(base::BindRepeating(
[](ComponentUpdateService* cus, PrefService* prefs,
const base::FilePath& install_dir) {
if (prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
->PostTask(FROM_HERE,
base::BindOnce(&UpdateSodaJaJpInstallDirPref,
prefs, install_dir));
}
},
cus, prefs)));
installer->Register(cus, std::move(callback));
#endif
}
bool UninstallSodaJaJpComponent(ComponentUpdateService* cus,
PrefService* prefs) {
return cus->UnregisterComponent(
SodaJaJpComponentInstallerPolicy::GetExtensionId());
}
} // namespace component_updater
// Copyright 2020 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.
#ifndef CHROME_BROWSER_COMPONENT_UPDATER_SODA_JA_JP_COMPONENT_INSTALLER_H_
#define CHROME_BROWSER_COMPONENT_UPDATER_SODA_JA_JP_COMPONENT_INSTALLER_H_
#include <string>
#include "components/component_updater/component_installer.h"
#include "components/prefs/pref_service.h"
#include "components/update_client/update_client.h"
namespace component_updater {
// Success callback to be run after the component is downloaded.
using OnSodaJaJpComponentReadyCallback =
base::RepeatingCallback<void(const base::FilePath&)>;
class SodaJaJpComponentInstallerPolicy : public ComponentInstallerPolicy {
public:
explicit SodaJaJpComponentInstallerPolicy(
OnSodaJaJpComponentReadyCallback callback);
~SodaJaJpComponentInstallerPolicy() override;
SodaJaJpComponentInstallerPolicy(const SodaJaJpComponentInstallerPolicy&) =
delete;
SodaJaJpComponentInstallerPolicy& operator=(
const SodaJaJpComponentInstallerPolicy&) = delete;
static const std::string GetExtensionId();
static void UpdateSodaJaJpComponentOnDemand();
private:
FRIEND_TEST_ALL_PREFIXES(SodaJaJpComponentInstallerTest,
ComponentReady_CallsLambda);
// The following methods override ComponentInstallerPolicy.
bool SupportsGroupPolicyEnabledComponentUpdates() const override;
bool RequiresNetworkEncryption() const override;
update_client::CrxInstaller::Result OnCustomInstall(
const base::DictionaryValue& manifest,
const base::FilePath& install_dir) override;
void OnCustomUninstall() override;
bool VerifyInstallation(const base::DictionaryValue& manifest,
const base::FilePath& install_dir) const override;
void ComponentReady(const base::Version& version,
const base::FilePath& install_dir,
std::unique_ptr<base::DictionaryValue> manifest) override;
base::FilePath GetRelativeInstallDir() const override;
void GetHash(std::vector<uint8_t>* hash) const override;
std::string GetName() const override;
update_client::InstallerAttributes GetInstallerAttributes() const override;
std::vector<std::string> GetMimeTypes() const override;
OnSodaJaJpComponentReadyCallback on_component_ready_callback_;
};
// Call once during startup to make the component update service aware of
// the File Type Policies component.
void RegisterSodaJaJpComponent(ComponentUpdateService* cus,
PrefService* prefs,
base::OnceClosure callback);
bool UninstallSodaJaJpComponent(ComponentUpdateService* cus,
PrefService* prefs);
} // namespace component_updater
#endif // CHROME_BROWSER_COMPONENT_UPDATER_SODA_JA_JP_COMPONENT_INSTALLER_H_
// Copyright 2020 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/component_updater/soda_ja_jp_component_installer.h"
#include "base/files/file_path.h"
#include "base/test/bind_test_util.h"
#include "base/version.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace component_updater {
class SodaJaJpComponentInstallerTest : public ::testing::Test {
public:
SodaJaJpComponentInstallerTest()
: fake_install_dir_(FILE_PATH_LITERAL("base/install/dir/")),
fake_version_("0.0.1") {}
protected:
base::FilePath fake_install_dir_;
base::Version fake_version_;
};
TEST_F(SodaJaJpComponentInstallerTest, ComponentReady_CallsLambda) {
base::FilePath given_path;
OnSodaJaJpComponentReadyCallback lambda = base::BindLambdaForTesting(
[&](const base::FilePath& path) { given_path = path; });
SodaJaJpComponentInstallerPolicy policy(std::move(lambda));
policy.ComponentReady(fake_version_, fake_install_dir_,
std::make_unique<base::DictionaryValue>());
ASSERT_EQ(fake_install_dir_, given_path);
}
} // namespace component_updater
include_rules = [ include_rules = [
"+chrome/services/speech/buildflags.h", "+chrome/services/speech/buildflags.h",
"+components/soda",
"+services/network/network_context.h", "+services/network/network_context.h",
"+services/network/public/cpp" "+services/network/public/cpp"
] ]
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/soda/constants.h"
#include "components/user_prefs/user_prefs.h" #include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
#include "content/public/browser/service_process_host.h" #include "content/public/browser/service_process_host.h"
...@@ -83,4 +84,19 @@ void SpeechRecognitionService::LaunchIfNotRunning() { ...@@ -83,4 +84,19 @@ void SpeechRecognitionService::LaunchIfNotRunning() {
speech_recognition_service_client_.BindNewPipeAndPassRemote()); speech_recognition_service_client_.BindNewPipeAndPassRemote());
OnNetworkServiceDisconnect(); OnNetworkServiceDisconnect();
} }
base::FilePath SpeechRecognitionService::GetSodaConfigPath(PrefService* prefs) {
speech::LanguageCode language = speech::GetLanguageCode(
prefs->GetString(prefs::kLiveCaptionLanguageCode));
switch (language) {
case speech::LanguageCode::kEnUs:
return prefs->GetFilePath(prefs::kSodaEnUsConfigPath);
case speech::LanguageCode::kJaJp:
return prefs->GetFilePath(prefs::kSodaJaJpConfigPath);
default:
NOTREACHED();
}
return base::FilePath();
}
} // namespace speech } // namespace speech
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "media/mojo/mojom/speech_recognition_service.mojom.h" #include "media/mojo/mojom/speech_recognition_service.mojom.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
class PrefService;
namespace content { namespace content {
class BrowserContext; class BrowserContext;
} // namespace content } // namespace content
...@@ -38,6 +40,9 @@ class SpeechRecognitionService ...@@ -38,6 +40,9 @@ class SpeechRecognitionService
// Launches the speech recognition service in a sandboxed utility process. // Launches the speech recognition service in a sandboxed utility process.
void LaunchIfNotRunning(); void LaunchIfNotRunning();
// Gets the path of the SODA configuration file for the selected language.
base::FilePath GetSodaConfigPath(PrefService* prefs);
// The browser context associated with the keyed service. // The browser context associated with the keyed service.
content::BrowserContext* const context_; content::BrowserContext* const context_;
......
...@@ -1213,11 +1213,20 @@ const char kAccessibilityFocusHighlightEnabled[] = ...@@ -1213,11 +1213,20 @@ const char kAccessibilityFocusHighlightEnabled[] =
const char kLiveCaptionEnabled[] = const char kLiveCaptionEnabled[] =
"accessibility.captions.live_caption_enabled"; "accessibility.captions.live_caption_enabled";
// The language to use with the Live Caption feature.
const char kLiveCaptionLanguageCode[] =
"accessibility.captions.live_caption_language";
// The file path of the Speech On-Device API (SODA) binary. // The file path of the Speech On-Device API (SODA) binary.
const char kSodaBinaryPath[] = "accessibility.captions.soda_binary_path"; const char kSodaBinaryPath[] = "accessibility.captions.soda_binary_path";
// The file path of the en-us Speech On-Device API (SODA) configuration file. // The file path of the en-US Speech On-Device API (SODA) configuration file.
const char kSodaEnUsConfigPath[] = "accessibility.captions.soda_config_path"; const char kSodaEnUsConfigPath[] =
"accessibility.captions.soda_en_us_config_path";
// The file path of the ja-JP Speech On-Device API (SODA) configuration file.
const char kSodaJaJpConfigPath[] =
"accessibility.captions.soda_ja_jp_config_path";
#endif #endif
#if defined(OS_MAC) #if defined(OS_MAC)
......
...@@ -187,8 +187,10 @@ extern const char kAccessibilityCaptionsTextShadow[]; ...@@ -187,8 +187,10 @@ extern const char kAccessibilityCaptionsTextShadow[];
extern const char kAccessibilityCaptionsBackgroundOpacity[]; extern const char kAccessibilityCaptionsBackgroundOpacity[];
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
extern const char kLiveCaptionEnabled[]; extern const char kLiveCaptionEnabled[];
extern const char kLiveCaptionLanguageCode[];
extern const char kSodaBinaryPath[]; extern const char kSodaBinaryPath[];
extern const char kSodaEnUsConfigPath[]; extern const char kSodaEnUsConfigPath[];
extern const char kSodaJaJpConfigPath[];
#endif #endif
#if defined(OS_MAC) #if defined(OS_MAC)
extern const char kConfirmToQuitEnabled[]; extern const char kConfirmToQuitEnabled[];
......
...@@ -3739,6 +3739,8 @@ test("unit_tests") { ...@@ -3739,6 +3739,8 @@ test("unit_tests") {
"../browser/browsing_data/access_context_audit_database_unittest.cc", "../browser/browsing_data/access_context_audit_database_unittest.cc",
"../browser/browsing_data/access_context_audit_service_unittest.cc", "../browser/browsing_data/access_context_audit_service_unittest.cc",
"../browser/component_updater/soda_component_installer_unittest.cc", "../browser/component_updater/soda_component_installer_unittest.cc",
"../browser/component_updater/soda_en_us_component_installer_unittest.cc",
"../browser/component_updater/soda_ja_jp_component_installer_unittest.cc",
"../browser/content_settings/generated_cookie_prefs_unittest.cc", "../browser/content_settings/generated_cookie_prefs_unittest.cc",
"../browser/device_identity/device_oauth2_token_service_unittest.cc", "../browser/device_identity/device_oauth2_token_service_unittest.cc",
"../browser/media/feeds/media_feeds_converter_unittest.cc", "../browser/media/feeds/media_feeds_converter_unittest.cc",
......
...@@ -171,6 +171,8 @@ class OnDemandUpdater { ...@@ -171,6 +171,8 @@ class OnDemandUpdater {
friend class ::PluginObserver; friend class ::PluginObserver;
friend class SwReporterOnDemandFetcher; friend class SwReporterOnDemandFetcher;
friend class SODAComponentInstallerPolicy; friend class SODAComponentInstallerPolicy;
friend class SodaEnUsComponentInstallerPolicy;
friend class SodaJaJpComponentInstallerPolicy;
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
friend class CrOSComponentInstaller; friend class CrOSComponentInstaller;
#endif // defined(OS_CHROMEOS) #endif // defined(OS_CHROMEOS)
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "components/soda/constants.h" #include "components/soda/constants.h"
#include "base/files/file_enumerator.h" #include "base/files/file_enumerator.h"
#include "base/notreached.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "components/component_updater/component_updater_paths.h" #include "components/component_updater/component_updater_paths.h"
...@@ -22,6 +23,15 @@ constexpr base::FilePath::CharType kSodaBinaryRelativePath[] = ...@@ -22,6 +23,15 @@ constexpr base::FilePath::CharType kSodaBinaryRelativePath[] =
constexpr base::FilePath::CharType kSodaInstallationRelativePath[] = constexpr base::FilePath::CharType kSodaInstallationRelativePath[] =
FILE_PATH_LITERAL("SODA"); FILE_PATH_LITERAL("SODA");
constexpr base::FilePath::CharType kSodaLanguagePacksRelativePath[] =
FILE_PATH_LITERAL("SODALanguagePacks");
constexpr base::FilePath::CharType kSodaEnUsInstallationRelativePath[] =
FILE_PATH_LITERAL("SODALanguagePacks/en-US");
constexpr base::FilePath::CharType kSodaJaJpInstallationRelativePath[] =
FILE_PATH_LITERAL("SODALanguagePacks/ja-JP");
const base::FilePath GetSodaDirectory() { const base::FilePath GetSodaDirectory() {
base::FilePath components_dir; base::FilePath components_dir;
base::PathService::Get(component_updater::DIR_COMPONENT_USER, base::PathService::Get(component_updater::DIR_COMPONENT_USER,
...@@ -32,6 +42,16 @@ const base::FilePath GetSodaDirectory() { ...@@ -32,6 +42,16 @@ const base::FilePath GetSodaDirectory() {
: components_dir.Append(kSodaInstallationRelativePath); : components_dir.Append(kSodaInstallationRelativePath);
} }
const base::FilePath GetSodaLanguagePacksDirectory() {
base::FilePath components_dir;
base::PathService::Get(component_updater::DIR_COMPONENT_USER,
&components_dir);
return components_dir.empty()
? base::FilePath()
: components_dir.Append(kSodaLanguagePacksRelativePath);
}
const base::FilePath GetLatestSodaDirectory() { const base::FilePath GetLatestSodaDirectory() {
base::FileEnumerator enumerator(GetSodaDirectory(), false, base::FileEnumerator enumerator(GetSodaDirectory(), false,
base::FileEnumerator::DIRECTORIES); base::FileEnumerator::DIRECTORIES);
...@@ -51,4 +71,36 @@ const base::FilePath GetSodaBinaryPath() { ...@@ -51,4 +71,36 @@ const base::FilePath GetSodaBinaryPath() {
: soda_dir.Append(kSodaBinaryRelativePath); : soda_dir.Append(kSodaBinaryRelativePath);
} }
LanguageCode GetLanguageCode(std::string language) {
if (language.empty()) {
return LanguageCode::kNone;
}
if (language == "en-US") {
return LanguageCode::kEnUs;
}
if (language == "ja-JP") {
return LanguageCode::kJaJp;
}
NOTREACHED();
return LanguageCode::kNone;
}
std::vector<base::FilePath> GetSodaLanguagePackDirectories() {
std::vector<base::FilePath> paths;
base::FilePath components_dir;
base::PathService::Get(component_updater::DIR_COMPONENT_USER,
&components_dir);
if (!components_dir.empty()) {
paths.push_back(components_dir.Append(kSodaEnUsInstallationRelativePath));
paths.push_back(components_dir.Append(kSodaJaJpInstallationRelativePath));
}
return paths;
}
} // namespace speech } // namespace speech
...@@ -9,19 +9,43 @@ ...@@ -9,19 +9,43 @@
namespace speech { namespace speech {
// Location of the SODA component relative to components directory. enum class LanguageCode {
kNone = 0,
kEnUs = 1,
kJaJp = 2,
};
// Location of the SODA component relative to the components directory.
extern const base::FilePath::CharType kSodaInstallationRelativePath[]; extern const base::FilePath::CharType kSodaInstallationRelativePath[];
// Location of the SODA language packs relative to the components
// directory.
extern const base::FilePath::CharType kSodaLanguagePacksRelativePath[];
// Location of the SODA en-US language pack component relative to the components
// directory.
extern const base::FilePath::CharType kSodaEnUsInstallationRelativePath[];
// Location of the SODA ja-JP language pack component relative to the components
// directory.
extern const base::FilePath::CharType kSodaJaJpInstallationRelativePath[];
// Location of the libsoda binary within the SODA installation directory. // Location of the libsoda binary within the SODA installation directory.
extern const base::FilePath::CharType kSodaBinaryRelativePath[]; extern const base::FilePath::CharType kSodaBinaryRelativePath[];
// Location of the en_us SODA config file within the SODA installation // Location of the en-US SODA config file within the SODA installation
// directory. Note: SODA is currently only available in English. // directory. Note: SODA is currently only available in English.
extern const base::FilePath::CharType kSodaEnUsConfigFileRelativePath[]; extern const base::FilePath::CharType kSodaEnUsConfigFileRelativePath[];
// Location of the ja-JP SODA config file within the SODA installation
extern const base::FilePath::CharType kSodaJaJpConfigFileRelativePath[];
// Get the absolute path of the SODA component directory. // Get the absolute path of the SODA component directory.
const base::FilePath GetSodaDirectory(); const base::FilePath GetSodaDirectory();
// Get the absolute path of the SODA directory containing the language packs.
const base::FilePath GetSodaLanguagePacksDirectory();
// Get the directory containing the latest version of SODA. In most cases // Get the directory containing the latest version of SODA. In most cases
// there will only be one version of SODA, but it is possible for there to be // there will only be one version of SODA, but it is possible for there to be
// multiple versions if a newer version of SODA was recently downloaded before // multiple versions if a newer version of SODA was recently downloaded before
...@@ -33,6 +57,11 @@ const base::FilePath GetLatestSodaDirectory(); ...@@ -33,6 +57,11 @@ const base::FilePath GetLatestSodaDirectory();
// installed. // installed.
const base::FilePath GetSodaBinaryPath(); const base::FilePath GetSodaBinaryPath();
LanguageCode GetLanguageCode(std::string language);
// Gets a collection of paths to SODA language pack directories.
std::vector<base::FilePath> GetSodaLanguagePackDirectories();
} // namespace speech } // namespace speech
#endif // COMPONENTS_SODA_CONSTANTS_H_ #endif // COMPONENTS_SODA_CONSTANTS_H_
...@@ -18,8 +18,8 @@ namespace speech { ...@@ -18,8 +18,8 @@ namespace speech {
namespace { namespace {
// Gets the file permissions required by the Speech On-Device API (SODA). // Gets the file permissions required by the Speech On-Device API (SODA).
std::vector<BrokerFilePermission> GetSodaFilePermissions( std::vector<BrokerFilePermission> GetSodaFilePermissions() {
base::FilePath soda_dir) { auto soda_dir = GetSodaDirectory();
std::vector<BrokerFilePermission> permissions{ std::vector<BrokerFilePermission> permissions{
BrokerFilePermission::ReadOnly("/dev/urandom")}; BrokerFilePermission::ReadOnly("/dev/urandom")};
...@@ -29,6 +29,13 @@ std::vector<BrokerFilePermission> GetSodaFilePermissions( ...@@ -29,6 +29,13 @@ std::vector<BrokerFilePermission> GetSodaFilePermissions(
soda_dir.AsEndingWithSeparator().value())); soda_dir.AsEndingWithSeparator().value()));
} }
// This may happen if a user doesn't have any language packs installed.
auto language_packs_dir = GetSodaLanguagePacksDirectory();
if (!language_packs_dir.empty()) {
permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(
language_packs_dir.AsEndingWithSeparator().value()));
}
return permissions; return permissions;
} }
...@@ -47,7 +54,7 @@ bool SpeechRecognitionPreSandboxHook( ...@@ -47,7 +54,7 @@ bool SpeechRecognitionPreSandboxHook(
sandbox::syscall_broker::COMMAND_READLINK, sandbox::syscall_broker::COMMAND_READLINK,
sandbox::syscall_broker::COMMAND_STAT, sandbox::syscall_broker::COMMAND_STAT,
}), }),
GetSodaFilePermissions(GetSodaDirectory()), GetSodaFilePermissions(),
sandbox::policy::SandboxLinux::PreSandboxHook(), sandbox::policy::SandboxLinux::PreSandboxHook(),
options); options);
instance->EngageNamespaceSandboxIfPossible(); instance->EngageNamespaceSandboxIfPossible();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment