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.
5 #include "chrome/browser/character_encoding.h"
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"
22 using content::BrowserThread
;
26 // The maximum length of short list of recently user selected encodings is 3.
27 const size_t kUserSelectedEncodingsMaxLength
= 3;
32 int category_string_id
;
33 } CanonicalEncodingData
;
35 // An array of all supported canonical encoding names.
36 const CanonicalEncodingData kCanonicalEncodingNames
[] = {
37 { IDC_ENCODING_UTF8
, "UTF-8", IDS_ENCODING_UNICODE
},
38 { IDC_ENCODING_UTF16LE
, "UTF-16LE", IDS_ENCODING_UNICODE
},
39 { IDC_ENCODING_WINDOWS1252
, "windows-1252", IDS_ENCODING_WESTERN
},
40 { IDC_ENCODING_GBK
, "GBK", IDS_ENCODING_SIMP_CHINESE
},
41 { IDC_ENCODING_GB18030
, "gb18030", IDS_ENCODING_SIMP_CHINESE
},
42 { IDC_ENCODING_BIG5
, "Big5", IDS_ENCODING_TRAD_CHINESE
},
43 { IDC_ENCODING_BIG5HKSCS
, "Big5-HKSCS", IDS_ENCODING_TRAD_CHINESE
},
44 { IDC_ENCODING_KOREAN
, "EUC-KR", IDS_ENCODING_KOREAN
},
45 { IDC_ENCODING_SHIFTJIS
, "Shift_JIS", IDS_ENCODING_JAPANESE
},
46 { IDC_ENCODING_EUCJP
, "EUC-JP", IDS_ENCODING_JAPANESE
},
47 { IDC_ENCODING_ISO2022JP
, "ISO-2022-JP", IDS_ENCODING_JAPANESE
},
48 { IDC_ENCODING_THAI
, "windows-874", IDS_ENCODING_THAI
},
49 { IDC_ENCODING_ISO885915
, "ISO-8859-15", IDS_ENCODING_WESTERN
},
50 { IDC_ENCODING_MACINTOSH
, "macintosh", IDS_ENCODING_WESTERN
},
51 { IDC_ENCODING_ISO88592
, "ISO-8859-2", IDS_ENCODING_CENTRAL_EUROPEAN
},
52 { IDC_ENCODING_WINDOWS1250
, "windows-1250", IDS_ENCODING_CENTRAL_EUROPEAN
},
53 { IDC_ENCODING_ISO88595
, "ISO-8859-5", IDS_ENCODING_CYRILLIC
},
54 { IDC_ENCODING_WINDOWS1251
, "windows-1251", IDS_ENCODING_CYRILLIC
},
55 { IDC_ENCODING_KOI8R
, "KOI8-R", IDS_ENCODING_CYRILLIC
},
56 { IDC_ENCODING_KOI8U
, "KOI8-U", IDS_ENCODING_CYRILLIC
},
57 { IDC_ENCODING_ISO88597
, "ISO-8859-7", IDS_ENCODING_GREEK
},
58 { IDC_ENCODING_WINDOWS1253
, "windows-1253", IDS_ENCODING_GREEK
},
59 { IDC_ENCODING_WINDOWS1254
, "windows-1254", IDS_ENCODING_TURKISH
},
60 { IDC_ENCODING_WINDOWS1256
, "windows-1256", IDS_ENCODING_ARABIC
},
61 { IDC_ENCODING_ISO88596
, "ISO-8859-6", IDS_ENCODING_ARABIC
},
62 { IDC_ENCODING_WINDOWS1255
, "windows-1255", IDS_ENCODING_HEBREW
},
63 { IDC_ENCODING_ISO88598I
, "ISO-8859-8-I", IDS_ENCODING_HEBREW
},
64 { IDC_ENCODING_ISO88598
, "ISO-8859-8", IDS_ENCODING_HEBREW
},
65 { IDC_ENCODING_WINDOWS1258
, "windows-1258", IDS_ENCODING_VIETNAMESE
},
66 { IDC_ENCODING_ISO88594
, "ISO-8859-4", IDS_ENCODING_BALTIC
},
67 { IDC_ENCODING_ISO885913
, "ISO-8859-13", IDS_ENCODING_BALTIC
},
68 { IDC_ENCODING_WINDOWS1257
, "windows-1257", IDS_ENCODING_BALTIC
},
69 { IDC_ENCODING_ISO88593
, "ISO-8859-3", IDS_ENCODING_SOUTH_EUROPEAN
},
70 { IDC_ENCODING_ISO885910
, "ISO-8859-10", IDS_ENCODING_NORDIC
},
71 { IDC_ENCODING_ISO885914
, "ISO-8859-14", IDS_ENCODING_CELTIC
},
72 { IDC_ENCODING_ISO885916
, "ISO-8859-16", IDS_ENCODING_ROMANIAN
},
75 const int kCanonicalEncodingNamesLength
= arraysize(kCanonicalEncodingNames
);
77 typedef std::map
<int, std::pair
<const char*, int> >
78 IdToCanonicalEncodingNameMapType
;
79 typedef std::map
<const std::string
, int> CanonicalEncodingNameToIdMapType
;
82 const char* canonical_form
;
83 const char* display_form
;
84 } CanonicalEncodingDisplayNamePair
;
86 const CanonicalEncodingDisplayNamePair kCanonicalDisplayNameOverrides
[] = {
87 // Only lists the canonical names where we want a different form for display.
88 { "macintosh", "Macintosh" },
89 { "windows-874", "Windows-874" },
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" },
101 const int kCanonicalDisplayNameOverridesLength
=
102 arraysize(kCanonicalDisplayNameOverrides
);
104 typedef std::map
<std::string
, const char*> CanonicalNameDisplayNameMapType
;
106 class CanonicalEncodingMap
{
108 CanonicalEncodingMap() {}
109 const IdToCanonicalEncodingNameMapType
* GetIdToCanonicalEncodingNameMapData();
110 const CanonicalEncodingNameToIdMapType
* GetCanonicalEncodingNameToIdMapData();
111 const CanonicalNameDisplayNameMapType
* GetCanonicalNameDisplayNameMapData();
112 std::vector
<int>* locale_dependent_encoding_ids() {
113 return &locale_dependent_encoding_ids_
;
116 std::vector
<CharacterEncoding::EncodingInfo
>* current_display_encodings() {
117 return ¤t_display_encodings_
;
121 scoped_ptr
<IdToCanonicalEncodingNameMapType
> id_to_encoding_name_map_
;
122 scoped_ptr
<CanonicalEncodingNameToIdMapType
> encoding_name_to_id_map_
;
123 scoped_ptr
<CanonicalNameDisplayNameMapType
>
124 encoding_name_to_display_name_map_
;
125 std::vector
<int> locale_dependent_encoding_ids_
;
126 std::vector
<CharacterEncoding::EncodingInfo
> current_display_encodings_
;
128 DISALLOW_COPY_AND_ASSIGN(CanonicalEncodingMap
);
131 const IdToCanonicalEncodingNameMapType
*
132 CanonicalEncodingMap::GetIdToCanonicalEncodingNameMapData() {
133 // Testing and building map is not thread safe, this function is supposed to
134 // only run in UI thread. Myabe I should add a lock in here for making it as
136 if (!id_to_encoding_name_map_
.get()) {
137 id_to_encoding_name_map_
.reset(new IdToCanonicalEncodingNameMapType
);
138 for (int i
= 0; i
< kCanonicalEncodingNamesLength
; ++i
) {
139 int resource_id
= kCanonicalEncodingNames
[i
].resource_id
;
140 (*id_to_encoding_name_map_
)[resource_id
] =
141 std::make_pair(kCanonicalEncodingNames
[i
].name
,
142 kCanonicalEncodingNames
[i
].category_string_id
);
145 return id_to_encoding_name_map_
.get();
148 const CanonicalEncodingNameToIdMapType
*
149 CanonicalEncodingMap::GetCanonicalEncodingNameToIdMapData() {
150 if (!encoding_name_to_id_map_
.get()) {
151 encoding_name_to_id_map_
.reset(new CanonicalEncodingNameToIdMapType
);
152 for (int i
= 0; i
< kCanonicalEncodingNamesLength
; ++i
) {
153 (*encoding_name_to_id_map_
)[kCanonicalEncodingNames
[i
].name
] =
154 kCanonicalEncodingNames
[i
].resource_id
;
157 return encoding_name_to_id_map_
.get();
160 const CanonicalNameDisplayNameMapType
*
161 CanonicalEncodingMap::GetCanonicalNameDisplayNameMapData() {
162 if (!encoding_name_to_display_name_map_
.get()) {
163 encoding_name_to_display_name_map_
.reset(
164 new CanonicalNameDisplayNameMapType
);
165 // First store the names in the kCanonicalEncodingNames list.
166 for (int i
= 0; i
< kCanonicalEncodingNamesLength
; ++i
) {
167 (*encoding_name_to_display_name_map_
)[kCanonicalEncodingNames
[i
].name
] =
168 kCanonicalEncodingNames
[i
].name
;
170 // Then save in the overrides.
171 for (int i
= 0; i
< kCanonicalDisplayNameOverridesLength
; ++i
) {
172 (*encoding_name_to_display_name_map_
)
173 [kCanonicalDisplayNameOverrides
[i
].canonical_form
] =
174 kCanonicalDisplayNameOverrides
[i
].display_form
;
176 DCHECK(static_cast<int>(encoding_name_to_display_name_map_
->size()) ==
177 kCanonicalEncodingNamesLength
)
178 << "Got an override that wasn't in the encoding list";
180 return encoding_name_to_display_name_map_
.get();
183 // A static map object which contains all resourceid-nonsequenced canonical
185 CanonicalEncodingMap
* CanonicalEncodingMapSingleton() {
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
187 static CanonicalEncodingMap
* singleton
= new CanonicalEncodingMap
;
191 const int kDefaultEncodingMenus
[] = {
192 IDC_ENCODING_UTF16LE
,
193 IDC_ENCODING_WINDOWS1252
,
195 IDC_ENCODING_GB18030
,
197 IDC_ENCODING_BIG5HKSCS
,
199 IDC_ENCODING_SHIFTJIS
,
201 IDC_ENCODING_ISO2022JP
,
203 IDC_ENCODING_ISO885915
,
204 IDC_ENCODING_MACINTOSH
,
205 IDC_ENCODING_ISO88592
,
206 IDC_ENCODING_WINDOWS1250
,
207 IDC_ENCODING_ISO88595
,
208 IDC_ENCODING_WINDOWS1251
,
211 IDC_ENCODING_ISO88597
,
212 IDC_ENCODING_WINDOWS1253
,
213 IDC_ENCODING_WINDOWS1254
,
214 IDC_ENCODING_WINDOWS1256
,
215 IDC_ENCODING_ISO88596
,
216 IDC_ENCODING_WINDOWS1255
,
217 IDC_ENCODING_ISO88598I
,
218 IDC_ENCODING_ISO88598
,
219 IDC_ENCODING_WINDOWS1258
,
220 IDC_ENCODING_ISO88594
,
221 IDC_ENCODING_ISO885913
,
222 IDC_ENCODING_WINDOWS1257
,
223 IDC_ENCODING_ISO88593
,
224 IDC_ENCODING_ISO885910
,
225 IDC_ENCODING_ISO885914
,
226 IDC_ENCODING_ISO885916
,
229 const int kDefaultEncodingMenusLength
= arraysize(kDefaultEncodingMenus
);
231 // Parse the input |encoding_list| which is a encoding list separated with
232 // comma, get available encoding ids and save them to |available_list|.
233 // The parameter |maximum_size| indicates maximum size of encoding items we
234 // want to get from the |encoding_list|.
235 void ParseEncodingListSeparatedWithComma(
236 const std::string
& encoding_list
, std::vector
<int>* const available_list
,
237 size_t maximum_size
) {
238 base::StringTokenizer
tokenizer(encoding_list
, ",");
239 while (tokenizer
.GetNext()) {
240 int id
= CharacterEncoding::GetCommandIdByCanonicalEncodingName(
242 // Ignore invalid encoding.
245 available_list
->push_back(id
);
246 if (available_list
->size() == maximum_size
)
251 base::string16
GetEncodingDisplayName(const std::string
& encoding_name
,
252 int category_string_id
) {
253 base::string16 category_name
= l10n_util::GetStringUTF16(category_string_id
);
254 if (category_string_id
!= IDS_ENCODING_KOREAN
&&
255 category_string_id
!= IDS_ENCODING_THAI
&&
256 category_string_id
!= IDS_ENCODING_TURKISH
&&
257 category_string_id
!= IDS_ENCODING_VIETNAMESE
&&
258 category_string_id
!= IDS_ENCODING_ROMANIAN
) {
259 const CanonicalNameDisplayNameMapType
* map
=
260 CanonicalEncodingMapSingleton()->GetCanonicalNameDisplayNameMapData();
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
,
268 base::ASCIIToUTF16(found_name
->second
));
270 return category_name
;
273 int GetEncodingCategoryStringIdByCommandId(int id
) {
274 const IdToCanonicalEncodingNameMapType
* map
=
275 CanonicalEncodingMapSingleton()->GetIdToCanonicalEncodingNameMapData();
278 IdToCanonicalEncodingNameMapType::const_iterator found_name
= map
->find(id
);
279 if (found_name
!= map
->end())
280 return found_name
->second
.second
;
284 std::string
GetEncodingCategoryStringByCommandId(int id
) {
285 int category_id
= GetEncodingCategoryStringIdByCommandId(id
);
287 return l10n_util::GetStringUTF8(category_id
);
288 return std::string();
293 CharacterEncoding::EncodingInfo::EncodingInfo(int id
)
295 encoding_category_name
=
296 base::UTF8ToUTF16(GetEncodingCategoryStringByCommandId(id
));
297 encoding_display_name
= GetCanonicalEncodingDisplayNameByCommandId(id
);
301 int CharacterEncoding::GetCommandIdByCanonicalEncodingName(
302 const std::string
& encoding_name
) {
303 const CanonicalEncodingNameToIdMapType
* map
=
304 CanonicalEncodingMapSingleton()->GetCanonicalEncodingNameToIdMapData();
307 CanonicalEncodingNameToIdMapType::const_iterator found_id
=
308 map
->find(encoding_name
);
309 if (found_id
!= map
->end())
310 return found_id
->second
;
315 std::string
CharacterEncoding::GetCanonicalEncodingNameByCommandId(int id
) {
316 const IdToCanonicalEncodingNameMapType
* map
=
317 CanonicalEncodingMapSingleton()->GetIdToCanonicalEncodingNameMapData();
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();
327 base::string16
CharacterEncoding::GetCanonicalEncodingDisplayNameByCommandId(
329 const IdToCanonicalEncodingNameMapType
* map
=
330 CanonicalEncodingMapSingleton()->GetIdToCanonicalEncodingNameMapData();
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();
341 // Return count number of all supported canonical encoding.
342 int CharacterEncoding::GetSupportCanonicalEncodingCount() {
343 return kCanonicalEncodingNamesLength
;
347 std::string
CharacterEncoding::GetCanonicalEncodingNameByIndex(int index
) {
348 if (index
< kCanonicalEncodingNamesLength
)
349 return kCanonicalEncodingNames
[index
].name
;
350 return std::string();
354 base::string16
CharacterEncoding::GetCanonicalEncodingDisplayNameByIndex(
356 if (index
< kCanonicalEncodingNamesLength
)
357 return GetEncodingDisplayName(kCanonicalEncodingNames
[index
].name
,
358 kCanonicalEncodingNames
[index
].category_string_id
);
359 return base::string16();
363 int CharacterEncoding::GetEncodingCommandIdByIndex(int index
) {
364 if (index
< kCanonicalEncodingNamesLength
)
365 return kCanonicalEncodingNames
[index
].resource_id
;
370 std::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();
377 CanonicalEncodingNameToIdMapType::const_iterator found_id
=
378 map
->find(alias_name
);
379 if (found_id
!= map
->end())
382 const char* standards
[3] = { "HTML", "MIME", "IANA" };
383 for (size_t i
= 0; i
< arraysize(standards
); ++i
) {
384 UErrorCode error_code
= U_ZERO_ERROR
;
385 const char* canonical_name
= ucnv_getCanonicalName(
386 alias_name
.c_str(), standards
[i
], &error_code
);
387 if (U_SUCCESS(error_code
) && canonical_name
)
388 return canonical_name
;
390 return std::string();
394 // According to the behavior of user recently selected encoding short list in
395 // Firefox, we always put UTF-8 as top position, after then put user
396 // recent selected encodings, then put local dependent encoding items.
397 // At last, we put all remaining encoding items.
398 const std::vector
<CharacterEncoding::EncodingInfo
>*
399 CharacterEncoding::GetCurrentDisplayEncodings(
400 const std::string
& locale
,
401 const std::string
& locale_encodings
,
402 const std::string
& recently_select_encodings
) {
403 std::vector
<int>* const locale_dependent_encoding_list
=
404 CanonicalEncodingMapSingleton()->locale_dependent_encoding_ids();
405 std::vector
<CharacterEncoding::EncodingInfo
>* const encoding_list
=
406 CanonicalEncodingMapSingleton()->current_display_encodings();
408 // Initialize locale dependent static encoding list.
409 if (locale_dependent_encoding_list
->empty() && !locale_encodings
.empty())
410 ParseEncodingListSeparatedWithComma(locale_encodings
,
411 locale_dependent_encoding_list
,
412 kUserSelectedEncodingsMaxLength
);
414 CR_DEFINE_STATIC_LOCAL(std::string
, cached_user_selected_encodings
, ());
415 // Build current display encoding list.
416 if (encoding_list
->empty() ||
417 cached_user_selected_encodings
!= recently_select_encodings
) {
418 // Update user recently selected encodings.
419 cached_user_selected_encodings
= recently_select_encodings
;
420 // Clear old encoding list since user recently selected encodings changed.
421 encoding_list
->clear();
422 // Always add UTF-8 to first encoding position.
423 encoding_list
->push_back(EncodingInfo(IDC_ENCODING_UTF8
));
424 std::set
<int> inserted_encoding
;
425 inserted_encoding
.insert(IDC_ENCODING_UTF8
);
427 // Parse user recently selected encodings and get list
428 std::vector
<int> recently_select_encoding_list
;
429 ParseEncodingListSeparatedWithComma(recently_select_encodings
,
430 &recently_select_encoding_list
,
431 kUserSelectedEncodingsMaxLength
);
433 // Put 'cached encodings' (dynamic encoding list) after 'local dependent
435 recently_select_encoding_list
.insert(recently_select_encoding_list
.begin(),
436 locale_dependent_encoding_list
->begin(),
437 locale_dependent_encoding_list
->end());
438 for (std::vector
<int>::iterator it
= recently_select_encoding_list
.begin();
439 it
!= recently_select_encoding_list
.end(); ++it
) {
440 // Test whether we have met this encoding id.
441 bool ok
= inserted_encoding
.insert(*it
).second
;
442 // Duplicated encoding, ignore it. Ideally, this situation should not
443 // happened, but just in case some one manually edit preference file.
446 encoding_list
->push_back(EncodingInfo(*it
));
448 // Append a separator;
449 encoding_list
->push_back(EncodingInfo(0));
451 // We need to keep "Unicode (UTF-16LE)" always at the top (among the rest
452 // of encodings) instead of being sorted along with other encodings. So if
453 // "Unicode (UTF-16LE)" is already in previous encodings, sort the rest
454 // of encodings. Otherwise Put "Unicode (UTF-16LE)" on the first of the
455 // rest of encodings, skip "Unicode (UTF-16LE)" and sort all left encodings.
456 int start_sorted_index
= encoding_list
->size();
457 if (inserted_encoding
.find(IDC_ENCODING_UTF16LE
) ==
458 inserted_encoding
.end()) {
459 encoding_list
->push_back(EncodingInfo(IDC_ENCODING_UTF16LE
));
460 inserted_encoding
.insert(IDC_ENCODING_UTF16LE
);
461 start_sorted_index
++;
464 // Add the rest of encodings that are neither in the static encoding list
465 // nor in the list of recently selected encodings.
466 // Build the encoding list sorted in the current locale sorting order.
467 for (int i
= 0; i
< kDefaultEncodingMenusLength
; ++i
) {
468 int id
= kDefaultEncodingMenus
[i
];
469 // We have inserted this encoding, skip it.
470 if (inserted_encoding
.find(id
) != inserted_encoding
.end())
472 encoding_list
->push_back(EncodingInfo(id
));
474 // Sort the encoding list.
475 l10n_util::SortVectorWithStringKey(locale
,
478 encoding_list
->size(),
481 DCHECK(!encoding_list
->empty());
482 return encoding_list
;
486 bool CharacterEncoding::UpdateRecentlySelectedEncoding(
487 const std::string
& original_selected_encodings
,
488 int new_selected_encoding_id
,
489 std::string
* selected_encodings
) {
490 // Get encoding name.
491 std::string encoding_name
=
492 GetCanonicalEncodingNameByCommandId(new_selected_encoding_id
);
493 DCHECK(!encoding_name
.empty());
494 // Check whether the new encoding is in local dependent encodings or original
495 // recently selected encodings. If yes, do not add it.
496 std::vector
<int>* locale_dependent_encoding_list
=
497 CanonicalEncodingMapSingleton()->locale_dependent_encoding_ids();
498 DCHECK(locale_dependent_encoding_list
);
499 std::vector
<int> selected_encoding_list
;
500 ParseEncodingListSeparatedWithComma(original_selected_encodings
,
501 &selected_encoding_list
,
502 kUserSelectedEncodingsMaxLength
);
503 // Put 'cached encodings' (dynamic encoding list) after 'local dependent
504 // encoding list' for check.
505 std::vector
<int> top_encoding_list(*locale_dependent_encoding_list
);
506 // UTF8 is always in our optimized encoding list.
507 top_encoding_list
.insert(top_encoding_list
.begin(), IDC_ENCODING_UTF8
);
508 top_encoding_list
.insert(top_encoding_list
.end(),
509 selected_encoding_list
.begin(),
510 selected_encoding_list
.end());
511 for (std::vector
<int>::const_iterator it
= top_encoding_list
.begin();
512 it
!= top_encoding_list
.end(); ++it
)
513 if (*it
== new_selected_encoding_id
)
515 // Need to add the encoding id to recently selected encoding list.
516 // Remove the last encoding in original list.
517 if (selected_encoding_list
.size() == kUserSelectedEncodingsMaxLength
)
518 selected_encoding_list
.pop_back();
519 // Insert new encoding to head of selected encoding list.
520 *selected_encodings
= encoding_name
;
521 // Generate the string for rest selected encoding list.
522 for (std::vector
<int>::const_iterator it
= selected_encoding_list
.begin();
523 it
!= selected_encoding_list
.end(); ++it
) {
524 selected_encodings
->append(1, L
',');
525 selected_encodings
->append(GetCanonicalEncodingNameByCommandId(*it
));