blob: d23ef32c550a7514a27e1e2585e2f811caa46b8d [file]
// Copyright 2021 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/ash/input_method/input_method_settings.h"
#include <string_view>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/containers/fixed_flat_set.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "chrome/browser/ash/input_method/assistive_prefs.h"
#include "chrome/browser/ash/input_method/autocorrect_enums.h"
#include "chrome/browser/ash/input_method/autocorrect_prefs.h"
#include "chrome/browser/ash/input_method/input_method_settings_consts.h"
#include "chrome/browser/ash/input_method/japanese_settings.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace ash {
namespace input_method {
namespace {
namespace mojom = ::ash::ime::mojom;
constexpr std::string_view kJapaneseEngineId = "nacl_mozc_jp";
constexpr std::string_view kJapaneseUsEngineId = "nacl_mozc_us";
std::string ValueOrEmpty(const std::string* str) {
return str ? *str : "";
}
bool IsUsEnglishEngine(const std::string& engine_id) {
return engine_id == "xkb:us::eng";
}
bool IsFstEngine(const std::string& engine_id) {
return base::StartsWith(engine_id, "xkb:", base::CompareCase::SENSITIVE) ||
base::StartsWith(engine_id, "experimental_",
base::CompareCase::SENSITIVE);
}
bool IsJapaneseEngine(const std::string& engine_id) {
return engine_id == kJapaneseEngineId || engine_id == kJapaneseUsEngineId;
}
bool IsKoreanEngine(const std::string& engine_id) {
return base::StartsWith(engine_id, "ko-", base::CompareCase::SENSITIVE);
}
bool IsPinyinEngine(const std::string& engine_id) {
return engine_id == "zh-t-i0-pinyin" || engine_id == "zh-hant-t-i0-pinyin";
}
bool IsZhuyinEngine(const std::string& engine_id) {
return engine_id == "zh-hant-t-i0-und";
}
bool IsVietnameseTelexEngine(std::string_view engine_id) {
return engine_id == "vkd_vi_telex";
}
bool IsVietnameseVniEngine(std::string_view engine_id) {
return engine_id == "vkd_vi_vni";
}
void RecordSettingsMetrics(const mojom::VietnameseTelexSettings& settings) {
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseTelex.FlexibleTyping",
settings.allow_flexible_diacritics);
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseTelex.ModernToneMark",
settings.new_style_tone_mark_placement);
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseTelex.UODoubleHorn",
settings.enable_insert_double_horn_on_uo);
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseTelex.WForUHorn",
settings.enable_w_for_u_horn_shortcut);
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseTelex.ShowUnderline",
settings.show_underline_for_composition_text);
}
void RecordSettingsMetrics(const mojom::VietnameseVniSettings& settings) {
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseVNI.FlexibleTyping",
settings.allow_flexible_diacritics);
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseVNI.ModernToneMark",
settings.new_style_tone_mark_placement);
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseVNI.UODoubleHorn",
settings.enable_insert_double_horn_on_uo);
base::UmaHistogramBoolean(
"InputMethod.PhysicalKeyboard.VietnameseVNI.ShowUnderline",
settings.show_underline_for_composition_text);
}
mojom::VietnameseVniSettingsPtr CreateVietnameseVniSettings(
const base::DictValue& input_method_specific_pref) {
auto settings = mojom::VietnameseVniSettings::New();
settings->allow_flexible_diacritics =
input_method_specific_pref.FindBool(kVnPrefVniAllowFlexibleDiacritics)
.value_or(true);
settings->new_style_tone_mark_placement =
input_method_specific_pref.FindBool(kVnPrefVniNewStyleToneMarkPlacement)
.value_or(false);
settings->enable_insert_double_horn_on_uo =
input_method_specific_pref.FindBool(kVnPrefVniInsertDoubleHornOnUo)
.value_or(false);
settings->show_underline_for_composition_text =
input_method_specific_pref.FindBool(kVnPrefVniShowUnderline)
.value_or(true);
RecordSettingsMetrics(*settings);
return settings;
}
mojom::VietnameseTelexSettingsPtr CreateVietnameseTelexSettings(
const base::DictValue& input_method_specific_pref) {
auto settings = mojom::VietnameseTelexSettings::New();
settings->allow_flexible_diacritics =
input_method_specific_pref.FindBool(kVnPrefTelexAllowFlexibleDiacritics)
.value_or(true);
settings->new_style_tone_mark_placement =
input_method_specific_pref.FindBool(kVnPrefTelexNewStyleToneMarkPlacement)
.value_or(false);
settings->enable_insert_double_horn_on_uo =
input_method_specific_pref.FindBool(kVnPrefTelexInsertDoubleHornOnUo)
.value_or(false);
settings->enable_w_for_u_horn_shortcut =
input_method_specific_pref.FindBool(kVnPrefTelexInsertUHornOnW)
.value_or(true);
settings->show_underline_for_composition_text =
input_method_specific_pref.FindBool(kVnPrefTelexShowUnderline)
.value_or(true);
RecordSettingsMetrics(*settings);
return settings;
}
mojom::LatinSettingsPtr CreateLatinSettings(
const base::DictValue& input_method_specific_pref,
const PrefService& prefs,
const std::string& engine_id) {
auto settings = mojom::LatinSettings::New();
auto autocorrect_pref = GetPhysicalKeyboardAutocorrectPref(prefs, engine_id);
settings->autocorrect =
base::StartsWith(engine_id, "experimental_",
base::CompareCase::SENSITIVE) ||
base::FeatureList::IsEnabled(features::kAutocorrectParamsTuning) ||
autocorrect_pref == AutocorrectPreference::kEnabled;
settings->predictive_writing =
base::FeatureList::IsEnabled(features::kAssistMultiWord) &&
IsUsEnglishEngine(engine_id) &&
IsPredictiveWritingPrefEnabled(prefs, engine_id);
return settings;
}
mojom::KoreanLayout KoreanLayoutToMojom(const std::string& layout) {
if (layout == kKoreanPrefsLayoutDubeolsik) {
return mojom::KoreanLayout::kDubeolsik;
}
if (layout == kKoreanPrefsLayoutDubeolsikOldHangeul) {
return mojom::KoreanLayout::kDubeolsikOldHangeul;
}
if (layout == kKoreanPrefsLayoutSebeolsik390) {
return mojom::KoreanLayout::kSebeolsik390;
}
if (layout == kKoreanPrefsLayoutSebeolsikFinal) {
return mojom::KoreanLayout::kSebeolsikFinal;
}
if (layout == kKoreanPrefsLayoutSebeolsikNoShift) {
return mojom::KoreanLayout::kSebeolsikNoShift;
}
if (layout == kKoreanPrefsLayoutSebeolsikOldHangeul) {
return mojom::KoreanLayout::kSebeolsikOldHangeul;
}
return mojom::KoreanLayout::kDubeolsik;
}
mojom::KoreanSettingsPtr CreateKoreanSettings(
const base::DictValue& input_method_specific_pref) {
auto settings = mojom::KoreanSettings::New();
settings->input_multiple_syllables =
!input_method_specific_pref.FindBool(kKrPrefEnableSyllableInput)
.value_or(true);
const std::string* prefs_layout =
input_method_specific_pref.FindString(kKrPrefKeyboardLayout);
settings->layout = prefs_layout ? KoreanLayoutToMojom(*prefs_layout)
: mojom::KoreanLayout::kDubeolsik;
return settings;
}
mojom::FuzzyPinyinSettingsPtr CreateFuzzyPinyinSettings(
const base::DictValue& pref) {
auto settings = mojom::FuzzyPinyinSettings::New();
settings->an_ang = pref.FindBool(kPinyinPrefFuzzyAnAng).value_or(false);
settings->en_eng = pref.FindBool(kPinyinPrefFuzzyEnEng).value_or(false);
settings->ian_iang = pref.FindBool(kPinyinPrefFuzzyIanIang).value_or(false);
settings->k_g = pref.FindBool(kPinyinPrefFuzzyKG).value_or(false);
settings->r_l = pref.FindBool(kPinyinPrefFuzzyRL).value_or(false);
settings->uan_uang = pref.FindBool(kPinyinPrefFuzzyUanUang).value_or(false);
settings->c_ch = pref.FindBool(kPinyinPrefFuzzyCCh).value_or(false);
settings->f_h = pref.FindBool(kPinyinPrefFuzzyFH).value_or(false);
settings->in_ing = pref.FindBool(kPinyinPrefFuzzyInIng).value_or(false);
settings->l_n = pref.FindBool(kPinyinPrefFuzzyLN).value_or(false);
settings->s_sh = pref.FindBool(kPinyinPrefFuzzySSh).value_or(false);
settings->z_zh = pref.FindBool(kPinyinPrefFuzzyZZh).value_or(false);
return settings;
}
mojom::PinyinLayout PinyinLayoutToMojom(const std::string& layout) {
if (layout == kPinyinPrefsLayoutUsQwerty) {
return mojom::PinyinLayout::kUsQwerty;
}
if (layout == kPinyinPrefsLayoutDvorak) {
return mojom::PinyinLayout::kDvorak;
}
if (layout == kPinyinPrefsLayoutColemak) {
return mojom::PinyinLayout::kColemak;
}
return mojom::PinyinLayout::kUsQwerty;
}
mojom::PinyinSettingsPtr CreatePinyinSettings(
const base::DictValue& input_method_specific_pref) {
auto settings = mojom::PinyinSettings::New();
settings->fuzzy_pinyin =
CreateFuzzyPinyinSettings(input_method_specific_pref);
const std::string* prefs_layout =
input_method_specific_pref.FindString(kPinyinPrefXkbLayout);
settings->layout = prefs_layout ? PinyinLayoutToMojom(*prefs_layout)
: mojom::PinyinLayout::kUsQwerty;
settings->use_hyphen_and_equals_to_page_candidates =
input_method_specific_pref.FindBool(kPinyinPrefEnableUpperPaging)
.value_or(true);
settings->use_comma_and_period_to_page_candidates =
input_method_specific_pref.FindBool(kPinyinPrefEnableLowerPaging)
.value_or(true);
settings->default_to_chinese =
input_method_specific_pref.FindBool(kPinyinPrefDefaultChinese)
.value_or(true);
settings->default_to_full_width_characters =
input_method_specific_pref.FindBool(kPinyinPrefFullWidthCharacter)
.value_or(false);
settings->default_to_full_width_punctuation =
input_method_specific_pref.FindBool(kPinyinPrefChinesePunctuation)
.value_or(true);
return settings;
}
mojom::ZhuyinLayout ZhuyinLayoutToMojom(const std::string& layout) {
if (layout == kZhuyinPrefsLayoutStandard) {
return mojom::ZhuyinLayout::kStandard;
}
if (layout == kZhuyinPrefsLayoutIbm) {
return mojom::ZhuyinLayout::kIbm;
}
if (layout == kZhuyinPrefsLayoutEten) {
return mojom::ZhuyinLayout::kEten;
}
return mojom::ZhuyinLayout::kStandard;
}
mojom::ZhuyinSelectionKeys ZhuyinSelectionKeysToMojom(
const std::string& selection_keys) {
if (selection_keys == kZhuyinPrefsSelectionKeys1234567890) {
return mojom::ZhuyinSelectionKeys::k1234567890;
}
if (selection_keys == kZhuyinPrefsSelectionKeysAsdfghjkl) {
return mojom::ZhuyinSelectionKeys::kAsdfghjkl;
}
if (selection_keys == kZhuyinPrefsSelectionKeysAsdfzxcv89) {
return mojom::ZhuyinSelectionKeys::kAsdfzxcv89;
}
if (selection_keys == kZhuyinPrefsSelectionKeysAsdfjkl789) {
return mojom::ZhuyinSelectionKeys::kAsdfjkl789;
}
if (selection_keys == kZhuyinPrefsSelectionKeys1234Qweras) {
return mojom::ZhuyinSelectionKeys::k1234Qweras;
}
return mojom::ZhuyinSelectionKeys::k1234567890;
}
uint32_t ZhuyinPageSizeToInt(const std::string& page_size) {
if (page_size == kZhuyinPrefsPageSize10) {
return 10;
}
if (page_size == kZhuyinPrefsPageSize9) {
return 9;
}
if (page_size == kZhuyinPrefsPageSize8) {
return 8;
}
return 10;
}
mojom::ZhuyinSettingsPtr CreateZhuyinSettings(
const base::DictValue& input_method_specific_pref) {
auto settings = mojom::ZhuyinSettings::New();
settings->layout = ZhuyinLayoutToMojom(ValueOrEmpty(
input_method_specific_pref.FindString(kZhuyinPrefKeyboardLayout)));
settings->selection_keys = ZhuyinSelectionKeysToMojom(ValueOrEmpty(
input_method_specific_pref.FindString(kZhuyinPrefSelectKeys)));
settings->page_size = ZhuyinPageSizeToInt(
ValueOrEmpty(input_method_specific_pref.FindString(kZhuyinPrefPageSize)));
return settings;
}
} // namespace
mojom::InputMethodSettingsPtr CreateSettingsFromPrefs(
const PrefService& prefs,
const std::string& engine_id) {
// All input method settings are stored in a single pref whose value is a
// dictionary.
// For each input method, the dictionary contains an entry, with the key being
// a string that identifies the input method, and the value being a
// subdictionary with the specific settings for that input method. The
// subdictionary structure depends on the type of input method it's for. The
// subdictionary may be null if the user hasn't changed any settings for that
// input method.
// TODO(crbug.com/203464079): Use distinct CrOS prefs for nacl_mozc_jp
// ("Japanese [for JIS keyboard]") and nacl_mozc_us ("Japanese for US
// keyboard") input methods. Due to singleton constraints in the legacy
// implementation, unlike all other input methods whose settings were distinct
// from one another, these two input methods shared the same settings. Upon
// migration to CrOS prefs, the unintended sharing was intentionally retained
// until the issue is separately addressed outside the scope of the said
// migration. Thus a single Japanese prefs entry with key "nacl_mozc_jp" is
// currently used for both "nacl_mozc_jp" and "nacl_mozc_us" input methods.
std::string_view lookup_engine_id =
engine_id == kJapaneseUsEngineId ? kJapaneseEngineId : engine_id;
const base::DictValue* ime_prefs_ptr =
prefs.GetDict(ash::prefs::kLanguageInputMethodSpecificSettings)
.FindDict(lookup_engine_id);
base::DictValue default_dict;
const base::DictValue& input_method_specific_pref =
ime_prefs_ptr == nullptr ? default_dict : *ime_prefs_ptr;
if (IsFstEngine(engine_id)) {
return mojom::InputMethodSettings::NewLatinSettings(
CreateLatinSettings(input_method_specific_pref, prefs, engine_id));
}
if (IsKoreanEngine(engine_id)) {
return mojom::InputMethodSettings::NewKoreanSettings(
CreateKoreanSettings(input_method_specific_pref));
}
if (IsPinyinEngine(engine_id)) {
return mojom::InputMethodSettings::NewPinyinSettings(
CreatePinyinSettings(input_method_specific_pref));
}
if (IsZhuyinEngine(engine_id)) {
return mojom::InputMethodSettings::NewZhuyinSettings(
CreateZhuyinSettings(input_method_specific_pref));
}
if (IsVietnameseTelexEngine(engine_id)) {
return mojom::InputMethodSettings::NewVietnameseTelexSettings(
CreateVietnameseTelexSettings(input_method_specific_pref));
}
if (IsVietnameseVniEngine(engine_id)) {
return mojom::InputMethodSettings::NewVietnameseVniSettings(
CreateVietnameseVniSettings(input_method_specific_pref));
}
if (IsJapaneseEngine(engine_id)) {
return mojom::InputMethodSettings::NewJapaneseSettings(
ToMojomInputMethodSettings(input_method_specific_pref));
}
return nullptr;
}
bool IsAutocorrectSupported(const std::string& engine_id) {
static constexpr auto kEnabledInputMethods =
base::MakeFixedFlatSet<std::string_view>({
"xkb:be::fra", "xkb:be::ger",
"xkb:be::nld", "xkb:br::por",
"xkb:ca::fra", "xkb:ca:eng:eng",
"xkb:ca:multix:fra", "xkb:ch::ger",
"xkb:ch:fr:fra", "xkb:de::ger",
"xkb:de:neo:ger", "xkb:dk::dan",
"xkb:es::spa", "xkb:fi::fin",
"xkb:fr::fra", "xkb:fr:bepo:fra",
"xkb:gb:dvorak:eng", "xkb:gb:extd:eng",
"xkb:it::ita", "xkb:latam::spa",
"xkb:no::nob", "xkb:pl::pol",
"xkb:pt::por", "xkb:se::swe",
"xkb:tr::tur", "xkb:tr:f:tur",
"xkb:us:intl:nld", "xkb:us:intl:por",
"xkb:us:intl_pc:nld", "xkb:us:intl_pc:por",
"xkb:us::eng", "xkb:us:altgr-intl:eng",
"xkb:us:colemak:eng", "xkb:us:dvorak:eng",
"xkb:us:dvp:eng", "xkb:us:intl:eng",
"xkb:us:intl_pc:eng", "xkb:us:workman-intl:eng",
"xkb:us:workman:eng",
});
return kEnabledInputMethods.contains(engine_id);
}
bool IsPhysicalKeyboardAutocorrectAllowed(const PrefService& prefs) {
if (!prefs.FindPreference(
ash::prefs::kManagedPhysicalKeyboardAutocorrectAllowed)) {
return true;
}
return prefs.GetBoolean(
ash::prefs::kManagedPhysicalKeyboardAutocorrectAllowed);
}
bool IsPhysicalKeyboardPredictiveWritingAllowed(const PrefService& prefs) {
if (!prefs.FindPreference(
ash::prefs::kManagedPhysicalKeyboardPredictiveWritingAllowed)) {
return true;
}
return prefs.GetBoolean(
ash::prefs::kManagedPhysicalKeyboardPredictiveWritingAllowed);
}
} // namespace input_method
} // namespace ash