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 22 using content::BrowserThread; 23 24 namespace { 25 26 // The maximum length of short list of recently user selected encodings is 3. 27 const size_t kUserSelectedEncodingsMaxLength = 3; 28 29 typedef 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. 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_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 76 const int kCanonicalEncodingNamesLength = arraysize(kCanonicalEncodingNames); 77 78 typedef std::map<int, std::pair<const char*, int> > 79 IdToCanonicalEncodingNameMapType; 80 typedef std::map<const std::string, int> CanonicalEncodingNameToIdMapType; 81 82 typedef struct { 83 const char* canonical_form; 84 const char* display_form; 85 } CanonicalEncodingDisplayNamePair; 86 87 const 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 102 const int kCanonicalDisplayNameOverridesLength = 103 arraysize(kCanonicalDisplayNameOverrides); 104 105 typedef std::map<std::string, const char*> CanonicalNameDisplayNameMapType; 106 107 class 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 ¤t_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 132 const 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 149 const 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 161 const 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. 186 CanonicalEncodingMap* CanonicalEncodingMapSingleton() { 187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 188 static CanonicalEncodingMap* singleton = new CanonicalEncodingMap; 189 return singleton; 190 } 191 192 const 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 231 const 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|. 237 void 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 253 base::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 273 int 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 284 std::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 293 CharacterEncoding::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. 301 int 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. 315 std::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. 327 base::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. 342 int CharacterEncoding::GetSupportCanonicalEncodingCount() { 343 return kCanonicalEncodingNamesLength; 344 } 345 346 // Static. 347 std::string CharacterEncoding::GetCanonicalEncodingNameByIndex(int index) { 348 if (index < kCanonicalEncodingNamesLength) 349 return kCanonicalEncodingNames[index].name; 350 return std::string(); 351 } 352 353 // Static. 354 base::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. 363 int CharacterEncoding::GetEncodingCommandIdByIndex(int index) { 364 if (index < kCanonicalEncodingNamesLength) 365 return kCanonicalEncodingNames[index].resource_id; 366 return 0; 367 } 368 369 // Static. 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(); 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. 412 const 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 500 bool 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