• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/character_encoding.h"
6 
7 #include <map>
8 #include <set>
9 
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/string_tokenizer.h"
13 #include "base/string_util.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/app/chrome_command_ids.h"
16 #include "grit/generated_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/l10n/l10n_util_collator.h"
19 #include "unicode/ucnv.h"
20 
21 namespace {
22 
23 // The maximum length of short list of recently user selected encodings is 3.
24 const size_t kUserSelectedEncodingsMaxLength = 3;
25 
26 typedef struct {
27   int resource_id;
28   const char* name;
29   int category_string_id;
30 } CanonicalEncodingData;
31 
32 // An array of all supported canonical encoding names.
33 static CanonicalEncodingData canonical_encoding_names[] = {
34   { IDC_ENCODING_UTF8, "UTF-8", IDS_ENCODING_UNICODE },
35   { IDC_ENCODING_UTF16LE, "UTF-16LE", IDS_ENCODING_UNICODE },
36   { IDC_ENCODING_ISO88591, "ISO-8859-1", IDS_ENCODING_WESTERN },
37   { IDC_ENCODING_WINDOWS1252, "windows-1252", IDS_ENCODING_WESTERN },
38   { IDC_ENCODING_GBK, "GBK", IDS_ENCODING_SIMP_CHINESE },
39   { IDC_ENCODING_GB18030, "gb18030", IDS_ENCODING_SIMP_CHINESE },
40   { IDC_ENCODING_BIG5, "Big5", IDS_ENCODING_TRAD_CHINESE },
41   { IDC_ENCODING_BIG5HKSCS, "Big5-HKSCS", IDS_ENCODING_TRAD_CHINESE },
42   { IDC_ENCODING_KOREAN, "windows-949", IDS_ENCODING_KOREAN },
43   { IDC_ENCODING_SHIFTJIS, "Shift_JIS", IDS_ENCODING_JAPANESE },
44   { IDC_ENCODING_EUCJP, "EUC-JP", IDS_ENCODING_JAPANESE },
45   { IDC_ENCODING_ISO2022JP, "ISO-2022-JP", IDS_ENCODING_JAPANESE },
46   { IDC_ENCODING_THAI, "windows-874", IDS_ENCODING_THAI },
47   { IDC_ENCODING_ISO885915, "ISO-8859-15", IDS_ENCODING_WESTERN },
48   { IDC_ENCODING_MACINTOSH, "macintosh", IDS_ENCODING_WESTERN },
49   { IDC_ENCODING_ISO88592, "ISO-8859-2", IDS_ENCODING_CENTRAL_EUROPEAN },
50   { IDC_ENCODING_WINDOWS1250, "windows-1250", IDS_ENCODING_CENTRAL_EUROPEAN },
51   { IDC_ENCODING_ISO88595, "ISO-8859-5", IDS_ENCODING_CYRILLIC },
52   { IDC_ENCODING_WINDOWS1251, "windows-1251", IDS_ENCODING_CYRILLIC },
53   { IDC_ENCODING_KOI8R, "KOI8-R", IDS_ENCODING_CYRILLIC },
54   { IDC_ENCODING_KOI8U, "KOI8-U", IDS_ENCODING_CYRILLIC },
55   { IDC_ENCODING_ISO88597, "ISO-8859-7", IDS_ENCODING_GREEK },
56   { IDC_ENCODING_WINDOWS1253, "windows-1253", IDS_ENCODING_GREEK },
57   { IDC_ENCODING_WINDOWS1254, "windows-1254", IDS_ENCODING_TURKISH },
58   { IDC_ENCODING_WINDOWS1256, "windows-1256", IDS_ENCODING_ARABIC },
59   { IDC_ENCODING_ISO88596, "ISO-8859-6", IDS_ENCODING_ARABIC },
60   { IDC_ENCODING_WINDOWS1255, "windows-1255", IDS_ENCODING_HEBREW },
61   { IDC_ENCODING_ISO88598I, "ISO-8859-8-I", IDS_ENCODING_HEBREW },
62   { IDC_ENCODING_ISO88598, "ISO-8859-8", IDS_ENCODING_HEBREW },
63   { IDC_ENCODING_WINDOWS1258, "windows-1258", IDS_ENCODING_VIETNAMESE },
64   { IDC_ENCODING_ISO88594, "ISO-8859-4", IDS_ENCODING_BALTIC },
65   { IDC_ENCODING_ISO885913, "ISO-8859-13", IDS_ENCODING_BALTIC },
66   { IDC_ENCODING_WINDOWS1257, "windows-1257", IDS_ENCODING_BALTIC },
67   { IDC_ENCODING_ISO88593, "ISO-8859-3", IDS_ENCODING_SOUTH_EUROPEAN },
68   { IDC_ENCODING_ISO885910, "ISO-8859-10", IDS_ENCODING_NORDIC },
69   { IDC_ENCODING_ISO885914, "ISO-8859-14", IDS_ENCODING_CELTIC },
70   { IDC_ENCODING_ISO885916, "ISO-8859-16", IDS_ENCODING_ROMANIAN },
71 };
72 
73 static const int canonical_encoding_names_length =
74     arraysize(canonical_encoding_names);
75 
76 typedef std::map<int, std::pair<const char*, int> >
77     IdToCanonicalEncodingNameMapType;
78 typedef std::map<const std::string, int> CanonicalEncodingNameToIdMapType;
79 
80 typedef struct {
81   const char* canonical_form;
82   const char* display_form;
83 } CanonicalEncodingDisplayNamePair;
84 
85 static CanonicalEncodingDisplayNamePair canonical_display_name_overrides[] = {
86   // Only lists the canonical names where we want a different form for display.
87   { "macintosh", "Macintosh" },
88   { "windows-874", "Windows-874" },
89   { "windows-949", "Windows-949" },
90   { "windows-1250", "Windows-1250" },
91   { "windows-1251", "Windows-1251" },
92   { "windows-1252", "Windows-1252" },
93   { "windows-1253", "Windows-1253" },
94   { "windows-1254", "Windows-1254" },
95   { "windows-1255", "Windows-1255" },
96   { "windows-1256", "Windows-1256" },
97   { "windows-1257", "Windows-1257" },
98   { "windows-1258", "Windows-1258" },
99 };
100 
101 static const int canonical_display_name_overrides_length =
102     arraysize(canonical_display_name_overrides);
103 
104 typedef std::map<std::string, const char*>
105     CanonicalNameDisplayNameMapType;
106 
107 class CanonicalEncodingMap {
108  public:
CanonicalEncodingMap()109   CanonicalEncodingMap()
110       : id_to_encoding_name_map_(NULL),
111         encoding_name_to_id_map_(NULL),
112         encoding_name_to_display_name_map_(NULL) { }
113   const IdToCanonicalEncodingNameMapType* GetIdToCanonicalEncodingNameMapData();
114   const CanonicalEncodingNameToIdMapType* GetCanonicalEncodingNameToIdMapData();
115   const CanonicalNameDisplayNameMapType* GetCanonicalNameDisplayNameMapData();
locale_dependent_encoding_ids()116   std::vector<int>* locale_dependent_encoding_ids() {
117     return &locale_dependent_encoding_ids_;
118   }
119 
current_display_encodings()120   std::vector<CharacterEncoding::EncodingInfo>* current_display_encodings() {
121     return &current_display_encodings_;
122   }
123 
124  private:
125   scoped_ptr<IdToCanonicalEncodingNameMapType> id_to_encoding_name_map_;
126   scoped_ptr<CanonicalEncodingNameToIdMapType> encoding_name_to_id_map_;
127   scoped_ptr<CanonicalNameDisplayNameMapType>
128       encoding_name_to_display_name_map_;
129   std::vector<int> locale_dependent_encoding_ids_;
130   std::vector<CharacterEncoding::EncodingInfo> current_display_encodings_;
131 
132   DISALLOW_COPY_AND_ASSIGN(CanonicalEncodingMap);
133 };
134 
135 const IdToCanonicalEncodingNameMapType*
GetIdToCanonicalEncodingNameMapData()136     CanonicalEncodingMap::GetIdToCanonicalEncodingNameMapData() {
137   // Testing and building map is not thread safe, this function is supposed to
138   // only run in UI thread. Myabe I should add a lock in here for making it as
139   // thread safe.
140   if (!id_to_encoding_name_map_.get()) {
141     id_to_encoding_name_map_.reset(new IdToCanonicalEncodingNameMapType);
142     for (int i = 0; i < canonical_encoding_names_length; ++i) {
143       int resource_id = canonical_encoding_names[i].resource_id;
144       (*id_to_encoding_name_map_)[resource_id] =
145         std::make_pair(canonical_encoding_names[i].name,
146                        canonical_encoding_names[i].category_string_id);
147     }
148   }
149   return id_to_encoding_name_map_.get();
150 }
151 
152 const CanonicalEncodingNameToIdMapType*
GetCanonicalEncodingNameToIdMapData()153     CanonicalEncodingMap::GetCanonicalEncodingNameToIdMapData() {
154   if (!encoding_name_to_id_map_.get()) {
155     encoding_name_to_id_map_.reset(new CanonicalEncodingNameToIdMapType);
156     for (int i = 0; i < canonical_encoding_names_length; ++i) {
157       (*encoding_name_to_id_map_)[canonical_encoding_names[i].name] =
158           canonical_encoding_names[i].resource_id;
159     }
160   }
161   return encoding_name_to_id_map_.get();
162 }
163 
164 const CanonicalNameDisplayNameMapType*
GetCanonicalNameDisplayNameMapData()165     CanonicalEncodingMap::GetCanonicalNameDisplayNameMapData() {
166   if (!encoding_name_to_display_name_map_.get()) {
167     encoding_name_to_display_name_map_.reset(
168         new CanonicalNameDisplayNameMapType);
169     // First store the names in the canonical_encoding_names list.
170     for (int i = 0; i < canonical_encoding_names_length; ++i) {
171       (*encoding_name_to_display_name_map_)[canonical_encoding_names[i].name] =
172           canonical_encoding_names[i].name;
173     }
174     // Then save in the overrides.
175     for (int i = 0; i < canonical_display_name_overrides_length; ++i) {
176       (*encoding_name_to_display_name_map_)
177           [canonical_display_name_overrides[i].canonical_form] =
178           canonical_display_name_overrides[i].display_form;
179     }
180     DCHECK(static_cast<int>(encoding_name_to_display_name_map_->size()) ==
181            canonical_encoding_names_length)
182         << "Got an override that wasn't in the encoding list";
183   }
184   return encoding_name_to_display_name_map_.get();
185 }
186 
187 // A static map object which contains all resourceid-nonsequenced canonical
188 // encoding names.
189 static CanonicalEncodingMap canonical_encoding_name_map_singleton;
190 
191 const int default_encoding_menus[] = {
192   IDC_ENCODING_UTF16LE,
193   IDC_ENCODING_ISO88591,
194   IDC_ENCODING_WINDOWS1252,
195   IDC_ENCODING_GBK,
196   IDC_ENCODING_GB18030,
197   IDC_ENCODING_BIG5,
198   IDC_ENCODING_BIG5HKSCS,
199   IDC_ENCODING_KOREAN,
200   IDC_ENCODING_SHIFTJIS,
201   IDC_ENCODING_EUCJP,
202   IDC_ENCODING_ISO2022JP,
203   IDC_ENCODING_THAI,
204   IDC_ENCODING_ISO885915,
205   IDC_ENCODING_MACINTOSH,
206   IDC_ENCODING_ISO88592,
207   IDC_ENCODING_WINDOWS1250,
208   IDC_ENCODING_ISO88595,
209   IDC_ENCODING_WINDOWS1251,
210   IDC_ENCODING_KOI8R,
211   IDC_ENCODING_KOI8U,
212   IDC_ENCODING_ISO88597,
213   IDC_ENCODING_WINDOWS1253,
214   IDC_ENCODING_WINDOWS1254,
215   IDC_ENCODING_WINDOWS1256,
216   IDC_ENCODING_ISO88596,
217   IDC_ENCODING_WINDOWS1255,
218   IDC_ENCODING_ISO88598I,
219   IDC_ENCODING_ISO88598,
220   IDC_ENCODING_WINDOWS1258,
221   IDC_ENCODING_ISO88594,
222   IDC_ENCODING_ISO885913,
223   IDC_ENCODING_WINDOWS1257,
224   IDC_ENCODING_ISO88593,
225   IDC_ENCODING_ISO885910,
226   IDC_ENCODING_ISO885914,
227   IDC_ENCODING_ISO885916,
228 };
229 
230 const int default_encoding_menus_length = arraysize(default_encoding_menus);
231 
232 // Parse the input |encoding_list| which is a encoding list separated with
233 // comma, get available encoding ids and save them to |available_list|.
234 // The parameter |maximum_size| indicates maximum size of encoding items we
235 // want to get from the |encoding_list|.
ParseEncodingListSeparatedWithComma(const std::string & encoding_list,std::vector<int> * const available_list,size_t maximum_size)236 void ParseEncodingListSeparatedWithComma(
237     const std::string& encoding_list, std::vector<int>* const available_list,
238     size_t maximum_size) {
239   StringTokenizer tokenizer(encoding_list, ",");
240   while (tokenizer.GetNext()) {
241     int id = CharacterEncoding::GetCommandIdByCanonicalEncodingName(
242         tokenizer.token());
243     // Ignore invalid encoding.
244     if (!id)
245       continue;
246     available_list->push_back(id);
247     if (available_list->size() == maximum_size)
248       return;
249   }
250 }
251 
GetEncodingDisplayName(std::string encoding_name,int category_string_id)252 string16 GetEncodingDisplayName(std::string encoding_name,
253                                 int category_string_id) {
254   string16 category_name = l10n_util::GetStringUTF16(category_string_id);
255   if (category_string_id != IDS_ENCODING_KOREAN &&
256       category_string_id != IDS_ENCODING_THAI &&
257       category_string_id != IDS_ENCODING_TURKISH) {
258     const CanonicalNameDisplayNameMapType* map =
259         canonical_encoding_name_map_singleton.
260             GetCanonicalNameDisplayNameMapData();
261     DCHECK(map);
262 
263     CanonicalNameDisplayNameMapType::const_iterator found_name =
264         map->find(encoding_name);
265     DCHECK(found_name != map->end());
266     return l10n_util::GetStringFUTF16(IDS_ENCODING_DISPLAY_TEMPLATE,
267                                       category_name,
268                                       ASCIIToUTF16(found_name->second));
269   }
270   return category_name;
271 }
272 
GetEncodingCategoryStringIdByCommandId(int id)273 int GetEncodingCategoryStringIdByCommandId(int id) {
274   const IdToCanonicalEncodingNameMapType* map =
275       canonical_encoding_name_map_singleton.
276           GetIdToCanonicalEncodingNameMapData();
277   DCHECK(map);
278 
279   IdToCanonicalEncodingNameMapType::const_iterator found_name = map->find(id);
280   if (found_name != map->end())
281     return found_name->second.second;
282   return 0;
283 }
284 
GetEncodingCategoryStringByCommandId(int id)285 std::string GetEncodingCategoryStringByCommandId(int id) {
286   int category_id = GetEncodingCategoryStringIdByCommandId(id);
287   if (category_id)
288     return l10n_util::GetStringUTF8(category_id);
289   return std::string();
290 }
291 
292 }  // namespace
293 
EncodingInfo(int id)294 CharacterEncoding::EncodingInfo::EncodingInfo(int id)
295     : encoding_id(id) {
296   encoding_category_name =
297       UTF8ToUTF16(GetEncodingCategoryStringByCommandId(id));
298   encoding_display_name = GetCanonicalEncodingDisplayNameByCommandId(id);
299 }
300 
301 // Static.
GetCommandIdByCanonicalEncodingName(const std::string & encoding_name)302 int CharacterEncoding::GetCommandIdByCanonicalEncodingName(
303     const std::string& encoding_name) {
304   const CanonicalEncodingNameToIdMapType* map =
305       canonical_encoding_name_map_singleton.
306           GetCanonicalEncodingNameToIdMapData();
307   DCHECK(map);
308 
309   CanonicalEncodingNameToIdMapType::const_iterator found_id =
310       map->find(encoding_name);
311   if (found_id != map->end())
312     return found_id->second;
313   return 0;
314 }
315 
316 // Static.
GetCanonicalEncodingNameByCommandId(int id)317 std::string CharacterEncoding::GetCanonicalEncodingNameByCommandId(int id) {
318   const IdToCanonicalEncodingNameMapType* map =
319       canonical_encoding_name_map_singleton.
320           GetIdToCanonicalEncodingNameMapData();
321   DCHECK(map);
322 
323   IdToCanonicalEncodingNameMapType::const_iterator found_name = map->find(id);
324   if (found_name != map->end())
325     return found_name->second.first;
326   return std::string();
327 }
328 
329 // Static.
GetCanonicalEncodingDisplayNameByCommandId(int id)330 string16 CharacterEncoding::GetCanonicalEncodingDisplayNameByCommandId(
331     int id) {
332   const IdToCanonicalEncodingNameMapType* map =
333       canonical_encoding_name_map_singleton.
334           GetIdToCanonicalEncodingNameMapData();
335   DCHECK(map);
336 
337   IdToCanonicalEncodingNameMapType::const_iterator found_name = map->find(id);
338   if (found_name != map->end())
339     return GetEncodingDisplayName(found_name->second.first,
340                                   found_name->second.second);
341   return string16();
342 }
343 
344 // Static.
345 // Return count number of all supported canonical encoding.
GetSupportCanonicalEncodingCount()346 int CharacterEncoding::GetSupportCanonicalEncodingCount() {
347   return canonical_encoding_names_length;
348 }
349 
350 // Static.
GetCanonicalEncodingNameByIndex(int index)351 std::string CharacterEncoding::GetCanonicalEncodingNameByIndex(int index) {
352   if (index < canonical_encoding_names_length)
353     return canonical_encoding_names[index].name;
354   return std::string();
355 }
356 
357 // Static.
GetCanonicalEncodingDisplayNameByIndex(int index)358 string16 CharacterEncoding::GetCanonicalEncodingDisplayNameByIndex(
359     int index) {
360   if (index < canonical_encoding_names_length)
361     return GetEncodingDisplayName(canonical_encoding_names[index].name,
362         canonical_encoding_names[index].category_string_id);
363   return string16();
364 }
365 
366 // Static.
GetEncodingCommandIdByIndex(int index)367 int CharacterEncoding::GetEncodingCommandIdByIndex(int index) {
368   if (index < canonical_encoding_names_length)
369     return canonical_encoding_names[index].resource_id;
370   return 0;
371 }
372 
373 // Static.
GetCanonicalEncodingNameByAliasName(const std::string & alias_name)374 std::string CharacterEncoding::GetCanonicalEncodingNameByAliasName(
375     const std::string& alias_name) {
376   // If the input alias_name is already canonical encoding name, just return it.
377   const CanonicalEncodingNameToIdMapType* map =
378       canonical_encoding_name_map_singleton.
379           GetCanonicalEncodingNameToIdMapData();
380   DCHECK(map);
381 
382   CanonicalEncodingNameToIdMapType::const_iterator found_id =
383       map->find(alias_name);
384   if (found_id != map->end())
385     return alias_name;
386 
387   UErrorCode error_code = U_ZERO_ERROR;
388 
389   const char* canonical_name = ucnv_getCanonicalName(
390       alias_name.c_str(), "MIME", &error_code);
391   // If failed,  then try IANA next.
392   if (U_FAILURE(error_code) || !canonical_name) {
393     error_code = U_ZERO_ERROR;
394     canonical_name = ucnv_getCanonicalName(
395       alias_name.c_str(), "IANA", &error_code);
396   }
397 
398   if (canonical_name) {
399     // TODO(jnd) use a map to handle all customized {alias, canonical}
400     // encoding mappings if we have more than one pair.
401     // We don't want to add an unnecessary charset to the encoding menu, so we
402     // alias 'US-ASCII' to 'ISO-8859-1' in our UI without touching WebKit.
403     // http://crbug.com/15801.
404     if (alias_name == "US-ASCII")
405       return GetCanonicalEncodingNameByCommandId(IDC_ENCODING_ISO88591);
406     return canonical_name;
407   } else {
408     return std::string();
409   }
410 }
411 
412 // Static
413 // According to the behavior of user recently selected encoding short list in
414 // FireFox, we always put UTF-8 as toppest position, after then put user
415 // recently selected encodings, then put local dependent encoding items.
416 // At last, we put all rest encoding items.
417 const std::vector<CharacterEncoding::EncodingInfo>*
GetCurrentDisplayEncodings(const std::string & locale,const std::string & locale_encodings,const std::string & recently_select_encodings)418     CharacterEncoding::GetCurrentDisplayEncodings(
419     const std::string& locale,
420     const std::string& locale_encodings,
421     const std::string& recently_select_encodings) {
422   std::vector<int>* const locale_dependent_encoding_list =
423       canonical_encoding_name_map_singleton.locale_dependent_encoding_ids();
424   std::vector<CharacterEncoding::EncodingInfo>* const encoding_list =
425       canonical_encoding_name_map_singleton.current_display_encodings();
426 
427   // Initialize locale dependent static encoding list.
428   if (locale_dependent_encoding_list->empty() && !locale_encodings.empty())
429     ParseEncodingListSeparatedWithComma(locale_encodings,
430                                         locale_dependent_encoding_list,
431                                         kUserSelectedEncodingsMaxLength);
432 
433   static std::string cached_user_selected_encodings;
434   // Build current display encoding list.
435   if (encoding_list->empty() ||
436       cached_user_selected_encodings != recently_select_encodings) {
437     // Update user recently selected encodings.
438     cached_user_selected_encodings = recently_select_encodings;
439     // Clear old encoding list since user recently selected encodings changed.
440     encoding_list->clear();
441     // Always add UTF-8 to first encoding position.
442     encoding_list->push_back(EncodingInfo(IDC_ENCODING_UTF8));
443     std::set<int> inserted_encoding;
444     inserted_encoding.insert(IDC_ENCODING_UTF8);
445 
446     // Parse user recently selected encodings and get list
447     std::vector<int> recently_select_encoding_list;
448     ParseEncodingListSeparatedWithComma(recently_select_encodings,
449                                         &recently_select_encoding_list,
450                                         kUserSelectedEncodingsMaxLength);
451 
452     // Put 'cached encodings' (dynamic encoding list) after 'local dependent
453     // encoding list'.
454     recently_select_encoding_list.insert(recently_select_encoding_list.begin(),
455         locale_dependent_encoding_list->begin(),
456         locale_dependent_encoding_list->end());
457     for (std::vector<int>::iterator it = recently_select_encoding_list.begin();
458          it != recently_select_encoding_list.end(); ++it) {
459         // Test whether we have met this encoding id.
460         bool ok = inserted_encoding.insert(*it).second;
461         // Duplicated encoding, ignore it. Ideally, this situation should not
462         // happened, but just in case some one manually edit preference file.
463         if (!ok)
464           continue;
465         encoding_list->push_back(EncodingInfo(*it));
466     }
467     // Append a separator;
468     encoding_list->push_back(EncodingInfo(0));
469 
470     // We need to keep "Unicode (UTF-16LE)" always at the top (among the rest
471     // of encodings) instead of being sorted along with other encodings. So if
472     // "Unicode (UTF-16LE)" is already in previous encodings, sort the rest
473     // of encodings. Otherwise Put "Unicode (UTF-16LE)" on the first of the
474     // rest of encodings, skip "Unicode (UTF-16LE)" and sort all left encodings.
475     int start_sorted_index = encoding_list->size();
476     if (inserted_encoding.find(IDC_ENCODING_UTF16LE) ==
477         inserted_encoding.end()) {
478       encoding_list->push_back(EncodingInfo(IDC_ENCODING_UTF16LE));
479       inserted_encoding.insert(IDC_ENCODING_UTF16LE);
480       start_sorted_index++;
481     }
482 
483     // Add the rest of encodings that are neither in the static encoding list
484     // nor in the list of recently selected encodings.
485     // Build the encoding list sorted in the current locale sorting order.
486     for (int i = 0; i < default_encoding_menus_length; ++i) {
487       int id = default_encoding_menus[i];
488       // We have inserted this encoding, skip it.
489       if (inserted_encoding.find(id) != inserted_encoding.end())
490         continue;
491       encoding_list->push_back(EncodingInfo(id));
492     }
493     // Sort the encoding list.
494     l10n_util::SortVectorWithStringKey(locale,
495                                        encoding_list,
496                                        start_sorted_index,
497                                        encoding_list->size(),
498                                        true);
499   }
500   DCHECK(!encoding_list->empty());
501   return encoding_list;
502 }
503 
504 // Static
UpdateRecentlySelectedEncoding(const std::string & original_selected_encodings,int new_selected_encoding_id,std::string * selected_encodings)505 bool CharacterEncoding::UpdateRecentlySelectedEncoding(
506     const std::string& original_selected_encodings,
507     int new_selected_encoding_id,
508     std::string* selected_encodings) {
509   // Get encoding name.
510   std::string encoding_name =
511       GetCanonicalEncodingNameByCommandId(new_selected_encoding_id);
512   DCHECK(!encoding_name.empty());
513   // Check whether the new encoding is in local dependent encodings or original
514   // recently selected encodings. If yes, do not add it.
515   std::vector<int>* locale_dependent_encoding_list =
516       canonical_encoding_name_map_singleton.locale_dependent_encoding_ids();
517   DCHECK(locale_dependent_encoding_list);
518   std::vector<int> selected_encoding_list;
519   ParseEncodingListSeparatedWithComma(original_selected_encodings,
520                                       &selected_encoding_list,
521                                       kUserSelectedEncodingsMaxLength);
522   // Put 'cached encodings' (dynamic encoding list) after 'local dependent
523   // encoding list' for check.
524   std::vector<int> top_encoding_list(*locale_dependent_encoding_list);
525   // UTF8 is always in our optimized encoding list.
526   top_encoding_list.insert(top_encoding_list.begin(), IDC_ENCODING_UTF8);
527   top_encoding_list.insert(top_encoding_list.end(),
528                            selected_encoding_list.begin(),
529                            selected_encoding_list.end());
530   for (std::vector<int>::const_iterator it = top_encoding_list.begin();
531        it != top_encoding_list.end(); ++it)
532     if (*it == new_selected_encoding_id)
533       return false;
534   // Need to add the encoding id to recently selected encoding list.
535   // Remove the last encoding in original list.
536   if (selected_encoding_list.size() == kUserSelectedEncodingsMaxLength)
537     selected_encoding_list.pop_back();
538   // Insert new encoding to head of selected encoding list.
539   *selected_encodings = encoding_name;
540   // Generate the string for rest selected encoding list.
541   for (std::vector<int>::const_iterator it = selected_encoding_list.begin();
542        it != selected_encoding_list.end(); ++it) {
543     selected_encodings->append(1, L',');
544     selected_encodings->append(GetCanonicalEncodingNameByCommandId(*it));
545   }
546   return true;
547 }
548