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