Update broken references to image assets
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / input_method_syncer.cc
blob71904768fab765f27c51bcc30c844d98589b1251
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.
5 #include "chrome/browser/chromeos/input_method/input_method_syncer.h"
7 #include <algorithm>
8 #include <set>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/task_runner.h"
15 #include "base/task_runner_util.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/prefs/pref_service_syncable.h"
18 #include "chrome/common/pref_names.h"
19 #include "components/pref_registry/pref_registry_syncable.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
22 #include "ui/base/ime/chromeos/extension_ime_util.h"
23 #include "ui/base/l10n/l10n_util.h"
25 namespace chromeos {
26 namespace input_method {
27 namespace {
29 // Checks input method IDs, converting engine IDs to input method IDs and
30 // removing unsupported IDs from |values|.
31 void CheckAndResolveInputMethodIDs(
32 const input_method::InputMethodDescriptors& supported_descriptors,
33 std::vector<std::string>* values) {
34 // Extract the supported input method IDs into a set.
35 std::set<std::string> supported_input_method_ids;
36 for (const auto& descriptor : supported_descriptors)
37 supported_input_method_ids.insert(descriptor.id());
39 // Convert engine IDs to input method extension IDs.
40 std::transform(values->begin(), values->end(), values->begin(),
41 extension_ime_util::GetInputMethodIDByEngineID);
43 // Remove values that aren't found in the set of supported input method IDs.
44 std::vector<std::string>::iterator it = values->begin();
45 while (it != values->end()) {
46 if (it->size() && supported_input_method_ids.find(*it) !=
47 supported_input_method_ids.end()) {
48 ++it;
49 } else {
50 it = values->erase(it);
55 // Checks whether each language is supported, replacing locales with variants
56 // if they are available. Must be called on a thread that allows IO.
57 std::string CheckAndResolveLocales(const std::string& languages) {
58 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
59 if (languages.empty())
60 return languages;
61 std::vector<std::string> values = base::SplitString(
62 languages, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
64 const std::string app_locale = g_browser_process->GetApplicationLocale();
66 std::vector<std::string> accept_language_codes;
67 l10n_util::GetAcceptLanguagesForLocale(app_locale, &accept_language_codes);
68 std::sort(accept_language_codes.begin(), accept_language_codes.end());
70 // Remove unsupported language values.
71 std::vector<std::string>::iterator value_iter = values.begin();
72 while (value_iter != values.end()) {
73 if (binary_search(accept_language_codes.begin(),
74 accept_language_codes.end(),
75 *value_iter)) {
76 ++value_iter;
77 continue;
80 // If a language code resolves to a supported backup locale, replace it
81 // with the resolved locale.
82 std::string resolved_locale;
83 if (l10n_util::CheckAndResolveLocale(*value_iter, &resolved_locale)) {
84 if (binary_search(accept_language_codes.begin(),
85 accept_language_codes.end(),
86 resolved_locale)) {
87 *value_iter = resolved_locale;
88 ++value_iter;
89 continue;
92 value_iter = values.erase(value_iter);
95 return base::JoinString(values, ",");
98 // Appends tokens from |src| that are not in |dest| to |dest|.
99 void MergeLists(std::vector<std::string>* dest,
100 const std::vector<std::string>& src) {
101 // Keep track of already-added tokens.
102 std::set<std::string> unique_tokens(dest->begin(), dest->end());
104 for (const std::string& token : src) {
105 // Skip token if it's already in |dest|.
106 if (binary_search(unique_tokens.begin(), unique_tokens.end(), token))
107 continue;
108 dest->push_back(token);
109 unique_tokens.insert(token);
113 } // anonymous namespace
115 InputMethodSyncer::InputMethodSyncer(
116 PrefServiceSyncable* prefs,
117 scoped_refptr<input_method::InputMethodManager::State> ime_state)
118 : prefs_(prefs),
119 ime_state_(ime_state),
120 merging_(false),
121 weak_factory_(this) {
124 InputMethodSyncer::~InputMethodSyncer() {
125 prefs_->RemoveObserver(this);
128 // static
129 void InputMethodSyncer::RegisterProfilePrefs(
130 user_prefs::PrefRegistrySyncable* registry) {
131 registry->RegisterStringPref(
132 prefs::kLanguagePreferredLanguagesSyncable,
134 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
135 registry->RegisterStringPref(
136 prefs::kLanguagePreloadEnginesSyncable,
138 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
139 registry->RegisterStringPref(
140 prefs::kLanguageEnabledExtensionImesSyncable,
142 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
143 registry->RegisterBooleanPref(prefs::kLanguageShouldMergeInputMethods, false);
146 void InputMethodSyncer::Initialize() {
147 // This causes OnIsSyncingChanged to be called when the value of
148 // PrefService::IsSyncing() changes.
149 prefs_->AddObserver(this);
151 preferred_languages_syncable_.Init(prefs::kLanguagePreferredLanguagesSyncable,
152 prefs_);
153 preload_engines_syncable_.Init(prefs::kLanguagePreloadEnginesSyncable,
154 prefs_);
155 enabled_extension_imes_syncable_.Init(
156 prefs::kLanguageEnabledExtensionImesSyncable, prefs_);
158 BooleanPrefMember::NamedChangeCallback callback =
159 base::Bind(&InputMethodSyncer::OnPreferenceChanged,
160 base::Unretained(this));
161 preferred_languages_.Init(prefs::kLanguagePreferredLanguages,
162 prefs_, callback);
163 preload_engines_.Init(prefs::kLanguagePreloadEngines,
164 prefs_, callback);
165 enabled_extension_imes_.Init(
166 prefs::kLanguageEnabledExtensionImes, prefs_, callback);
168 // If we have already synced but haven't merged input methods yet, do so now.
169 if (prefs_->GetBoolean(prefs::kLanguageShouldMergeInputMethods) &&
170 !(preferred_languages_syncable_.GetValue().empty() &&
171 preload_engines_syncable_.GetValue().empty() &&
172 enabled_extension_imes_syncable_.GetValue().empty())) {
173 MergeSyncedPrefs();
177 void InputMethodSyncer::MergeSyncedPrefs() {
178 // This should only be done after the first ever sync.
179 DCHECK(prefs_->GetBoolean(prefs::kLanguageShouldMergeInputMethods));
180 prefs_->SetBoolean(prefs::kLanguageShouldMergeInputMethods, false);
181 merging_ = true;
183 std::string preferred_languages_syncable =
184 preferred_languages_syncable_.GetValue();
185 std::string preload_engines_syncable =
186 preload_engines_syncable_.GetValue();
187 std::string enabled_extension_imes_syncable =
188 enabled_extension_imes_syncable_.GetValue();
190 std::vector<std::string> synced_tokens;
191 std::vector<std::string> new_tokens;
193 // First, set the syncable prefs to the union of the local and synced prefs.
194 synced_tokens =
195 base::SplitString(preferred_languages_syncable_.GetValue(), ",",
196 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
197 new_tokens = base::SplitString(preferred_languages_.GetValue(), ",",
198 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
200 // Append the synced values to the current values.
201 MergeLists(&new_tokens, synced_tokens);
202 preferred_languages_syncable_.SetValue(base::JoinString(new_tokens, ","));
204 synced_tokens =
205 base::SplitString(enabled_extension_imes_syncable_.GetValue(), ",",
206 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
207 new_tokens = base::SplitString(enabled_extension_imes_.GetValue(), ",",
208 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
210 MergeLists(&new_tokens, synced_tokens);
211 enabled_extension_imes_syncable_.SetValue(base::JoinString(new_tokens, ","));
213 // Revert preload engines to legacy component IDs.
214 new_tokens = base::SplitString(preload_engines_.GetValue(), ",",
215 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
216 std::transform(new_tokens.begin(), new_tokens.end(), new_tokens.begin(),
217 extension_ime_util::GetComponentIDByInputMethodID);
218 synced_tokens =
219 base::SplitString(preload_engines_syncable_.GetValue(), ",",
220 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
222 MergeLists(&new_tokens, synced_tokens);
223 preload_engines_syncable_.SetValue(base::JoinString(new_tokens, ","));
225 // Second, set the local prefs, incorporating new values from the sync server.
226 preload_engines_.SetValue(
227 AddSupportedInputMethodValues(preload_engines_.GetValue(),
228 preload_engines_syncable,
229 prefs::kLanguagePreloadEngines));
230 enabled_extension_imes_.SetValue(
231 AddSupportedInputMethodValues(enabled_extension_imes_.GetValue(),
232 enabled_extension_imes_syncable,
233 prefs::kLanguageEnabledExtensionImes));
235 // Remove unsupported locales before updating the local languages preference.
236 std::string languages(
237 AddSupportedInputMethodValues(preferred_languages_.GetValue(),
238 preferred_languages_syncable,
239 prefs::kLanguagePreferredLanguages));
240 base::PostTaskAndReplyWithResult(
241 content::BrowserThread::GetBlockingPool(),
242 FROM_HERE,
243 base::Bind(&CheckAndResolveLocales, languages),
244 base::Bind(&InputMethodSyncer::FinishMerge,
245 weak_factory_.GetWeakPtr()));
248 std::string InputMethodSyncer::AddSupportedInputMethodValues(
249 const std::string& pref,
250 const std::string& synced_pref,
251 const char* pref_name) {
252 std::vector<std::string> old_tokens =
253 base::SplitString(pref, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
254 std::vector<std::string> new_tokens = base::SplitString(
255 synced_pref, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
257 // Check and convert the new tokens.
258 if (pref_name == prefs::kLanguagePreloadEngines ||
259 pref_name == prefs::kLanguageEnabledExtensionImes) {
260 input_method::InputMethodManager* manager =
261 input_method::InputMethodManager::Get();
262 scoped_ptr<input_method::InputMethodDescriptors> supported_descriptors;
264 if (pref_name == prefs::kLanguagePreloadEngines) {
265 // Set the known input methods.
266 supported_descriptors = manager->GetSupportedInputMethods();
267 // Add the available component extension IMEs.
268 ComponentExtensionIMEManager* component_extension_manager =
269 manager->GetComponentExtensionIMEManager();
270 input_method::InputMethodDescriptors component_descriptors =
271 component_extension_manager->GetAllIMEAsInputMethodDescriptor();
272 supported_descriptors->insert(supported_descriptors->end(),
273 component_descriptors.begin(),
274 component_descriptors.end());
275 } else {
276 supported_descriptors.reset(new input_method::InputMethodDescriptors);
277 ime_state_->GetInputMethodExtensions(supported_descriptors.get());
279 CheckAndResolveInputMethodIDs(*supported_descriptors, &new_tokens);
280 } else if (pref_name != prefs::kLanguagePreferredLanguages) {
281 NOTREACHED() << "Attempting to merge an invalid preference.";
282 // kLanguagePreferredLanguages is checked in CheckAndResolveLocales().
285 // Do the actual merging.
286 MergeLists(&old_tokens, new_tokens);
287 return base::JoinString(old_tokens, ",");
290 void InputMethodSyncer::FinishMerge(const std::string& languages) {
291 // Since the merge only removed locales that are unsupported on this system,
292 // we don't need to update the syncable prefs. If the local preference changes
293 // later, the sync server will lose the values we dropped. That's okay since
294 // the values from this device should then become the new defaults anyway.
295 preferred_languages_.SetValue(languages);
297 // We've finished merging.
298 merging_ = false;
301 void InputMethodSyncer::OnPreferenceChanged(const std::string& pref_name) {
302 DCHECK(pref_name == prefs::kLanguagePreferredLanguages ||
303 pref_name == prefs::kLanguagePreloadEngines ||
304 pref_name == prefs::kLanguageEnabledExtensionImes);
306 if (merging_ || prefs_->GetBoolean(prefs::kLanguageShouldMergeInputMethods))
307 return;
309 // Set the language and input prefs at the same time. Otherwise we may,
310 // e.g., use a stale languages setting but push a new preload engines setting.
311 preferred_languages_syncable_.SetValue(preferred_languages_.GetValue());
312 enabled_extension_imes_syncable_.SetValue(enabled_extension_imes_.GetValue());
314 // For preload engines, use legacy xkb IDs so the preference can sync
315 // across Chrome OS and Chromium OS.
316 std::vector<std::string> engines =
317 base::SplitString(preload_engines_.GetValue(), ",", base::TRIM_WHITESPACE,
318 base::SPLIT_WANT_ALL);
319 std::transform(engines.begin(), engines.end(), engines.begin(),
320 extension_ime_util::GetComponentIDByInputMethodID);
321 preload_engines_syncable_.SetValue(base::JoinString(engines, ","));
324 void InputMethodSyncer::OnIsSyncingChanged() {
325 if (prefs_->GetBoolean(prefs::kLanguageShouldMergeInputMethods) &&
326 prefs_->IsSyncing()) {
327 MergeSyncedPrefs();
331 } // namespace input_method
332 } // namespace chromeos