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
21namespace {
22
23// The maximum length of short list of recently user selected encodings is 3.
24const size_t kUserSelectedEncodingsMaxLength = 3;
25
26typedef 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.
33static 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
73static const int canonical_encoding_names_length =
74    arraysize(canonical_encoding_names);
75
76typedef std::map<int, std::pair<const char*, int> >
77    IdToCanonicalEncodingNameMapType;
78typedef std::map<const std::string, int> CanonicalEncodingNameToIdMapType;
79
80typedef struct {
81  const char* canonical_form;
82  const char* display_form;
83} CanonicalEncodingDisplayNamePair;
84
85static 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
101static const int canonical_display_name_overrides_length =
102    arraysize(canonical_display_name_overrides);
103
104typedef std::map<std::string, const char*>
105    CanonicalNameDisplayNameMapType;
106
107class CanonicalEncodingMap {
108 public:
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();
116  std::vector<int>* locale_dependent_encoding_ids() {
117    return &locale_dependent_encoding_ids_;
118  }
119
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
135const IdToCanonicalEncodingNameMapType*
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
152const CanonicalEncodingNameToIdMapType*
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
164const CanonicalNameDisplayNameMapType*
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.
189static CanonicalEncodingMap canonical_encoding_name_map_singleton;
190
191const 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
230const 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|.
236void 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
252string16 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
273int 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
285std::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
294CharacterEncoding::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.
302int 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.
317std::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.
330string16 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.
346int CharacterEncoding::GetSupportCanonicalEncodingCount() {
347  return canonical_encoding_names_length;
348}
349
350// Static.
351std::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.
358string16 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.
367int 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.
374std::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.
417const std::vector<CharacterEncoding::EncodingInfo>*
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
505bool 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