Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/translate/translate_language_list.h" | 5 #include "chrome/browser/translate/translate_language_list.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 | 8 |
| 9 #include "base/bind.h" | |
| 9 #include "base/json/json_reader.h" | 10 #include "base/json/json_reader.h" |
| 10 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 11 #include "base/logging.h" | 12 #include "base/logging.h" |
| 12 #include "base/string_util.h" | 13 #include "base/string_util.h" |
| 13 #include "base/stringprintf.h" | 14 #include "base/stringprintf.h" |
| 14 #include "base/values.h" | 15 #include "base/values.h" |
| 15 #include "chrome/browser/browser_process.h" | 16 #include "chrome/browser/browser_process.h" |
| 16 #include "chrome/browser/translate/translate_event_details.h" | 17 #include "chrome/browser/translate/translate_event_details.h" |
| 17 #include "chrome/browser/translate/translate_manager.h" | 18 #include "chrome/browser/translate/translate_manager.h" |
| 18 #include "chrome/browser/translate/translate_url_util.h" | 19 #include "chrome/browser/translate/translate_url_util.h" |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 // Constant URL string to fetch server supporting language list. | 98 // Constant URL string to fetch server supporting language list. |
| 98 const char kLanguageListFetchURL[] = | 99 const char kLanguageListFetchURL[] = |
| 99 "https://translate.googleapis.com/translate_a/l?client=chrome&cb=sl"; | 100 "https://translate.googleapis.com/translate_a/l?client=chrome&cb=sl"; |
| 100 | 101 |
| 101 // Used in kTranslateScriptURL to request supporting languages list including | 102 // Used in kTranslateScriptURL to request supporting languages list including |
| 102 // "alpha languages". | 103 // "alpha languages". |
| 103 const char kAlphaLanguageQueryName[] = "alpha"; | 104 const char kAlphaLanguageQueryName[] = "alpha"; |
| 104 const char kAlphaLanguageQueryValue[] = "1"; | 105 const char kAlphaLanguageQueryValue[] = "1"; |
| 105 | 106 |
| 106 // Retry parameter for fetching supporting language list. | 107 // Retry parameter for fetching supporting language list. |
| 107 const int kMaxRetryLanguageListFetch = 5; | 108 const int kMaxRetryLanguageListFetchOn5xx = 5; |
| 108 | 109 |
| 110 // Retry parameter for LanguageListFetcher. | |
| 111 const int kMaxRetryLanguageListFetch = 16; | |
| 112 | |
| 113 // Assign following IDs to URLFetchers so that tests can distinguish each | |
| 114 // request in order to simiulate respectively. | |
| 109 const int kFetcherIdForLanguageList = 1; | 115 const int kFetcherIdForLanguageList = 1; |
| 110 const int kFetcherIdForAlphaLanguageList = 2; | 116 const int kFetcherIdForAlphaLanguageList = 2; |
| 111 | 117 |
| 112 // Show a message in chrome:://translate-internals Event Logs. | 118 // Show a message in chrome:://translate-internals Event Logs. |
| 113 void NotifyEvent(int line, const std::string& message) { | 119 void NotifyEvent(int line, const std::string& message) { |
| 114 TranslateManager* manager = TranslateManager::GetInstance(); | 120 TranslateManager* manager = TranslateManager::GetInstance(); |
| 115 DCHECK(manager); | 121 DCHECK(manager); |
| 116 | 122 |
| 117 TranslateEventDetails details(__FILE__, line, message); | 123 TranslateEventDetails details(__FILE__, line, message); |
| 118 manager->NotifyTranslateEvent(details); | 124 manager->NotifyTranslateEvent(details); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 169 // TODO(toyoshim): Check if UI libraries support adding locale. | 175 // TODO(toyoshim): Check if UI libraries support adding locale. |
| 170 set->insert(iter.key()); | 176 set->insert(iter.key()); |
| 171 if (message.empty()) | 177 if (message.empty()) |
| 172 message += iter.key(); | 178 message += iter.key(); |
| 173 else | 179 else |
| 174 message += ", " + iter.key(); | 180 message += ", " + iter.key(); |
| 175 } | 181 } |
| 176 NotifyEvent(__LINE__, message); | 182 NotifyEvent(__LINE__, message); |
| 177 } | 183 } |
| 178 | 184 |
| 179 // Creates URLFetcher, sets arguments to start, and returns the object. | 185 // Check if |n| is 2^n (n >= 0). |
| 180 net::URLFetcher* CreateAndStartFetch(int id, | 186 bool IsOneHot(int n) { |
|
MAD
2013/06/10 18:28:10
I would prefer to name this: IsPowerOfTwo.
And he
Takashi Toyoshima
2013/06/12 05:20:43
I'll stop to use exponential backoff here.
Because
| |
| 181 const GURL& url, | 187 int hot_count = 0; |
| 182 net::URLFetcherDelegate* delegate) { | 188 for (int bit = 1; bit != 0; bit <<= 1) |
| 183 DCHECK(delegate); | 189 if (n & bit) |
| 184 std::string message = base::StringPrintf( | 190 hot_count++; |
| 185 "%s list fetch starts (URL: %s)", | 191 return hot_count == 1; |
| 186 (id == kFetcherIdForLanguageList) ? "Language" : "Alpha language", | |
| 187 url.spec().c_str()); | |
| 188 NotifyEvent(__LINE__, message); | |
| 189 | |
| 190 scoped_ptr<net::URLFetcher> fetcher; | |
| 191 fetcher.reset(net::URLFetcher::Create(id, | |
| 192 url, | |
| 193 net::URLFetcher::GET, | |
| 194 delegate)); | |
| 195 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
| 196 net::LOAD_DO_NOT_SAVE_COOKIES); | |
| 197 fetcher->SetRequestContext(g_browser_process->system_request_context()); | |
| 198 fetcher->SetMaxRetriesOn5xx(kMaxRetryLanguageListFetch); | |
| 199 fetcher->Start(); | |
| 200 | |
| 201 return fetcher.release(); | |
| 202 } | 192 } |
| 203 | 193 |
| 204 } // namespace | 194 } // namespace |
| 205 | 195 |
| 196 TranslateLanguageList::LanguageListFetcher::LanguageListFetcher( | |
| 197 bool include_alpha_languages) | |
| 198 : include_alpha_languages_(include_alpha_languages), | |
| 199 state_(IDLE), | |
| 200 retry_count_(0) { | |
|
MAD
2013/06/10 18:28:10
You never reset it to 0, so it's not just retries,
Takashi Toyoshima
2013/06/12 05:20:43
If I reset retry_count whenever a request is issue
| |
| 201 } | |
| 202 | |
| 203 TranslateLanguageList::LanguageListFetcher::~LanguageListFetcher() { | |
| 204 } | |
| 205 | |
| 206 bool TranslateLanguageList::LanguageListFetcher::Request( | |
| 207 const TranslateLanguageList::LanguageListFetcher::Callback& callback) { | |
| 208 // This function is not supporsed to be called before previous operaion is not | |
|
MAD
2013/06/10 18:28:10
supporsed -> supposed
Takashi Toyoshima
2013/06/12 05:20:43
Done.
| |
| 209 // finished. | |
| 210 if (state_ == REQUESTING) { | |
| 211 NOTREACHED(); | |
| 212 return false; | |
| 213 } | |
| 214 | |
| 215 // Omit requests to realize an exponential backoff'd retries. | |
| 216 // Request will be issued when |retry_count_| is 2^n (n >= 0) and it doesn't | |
| 217 // reach to limitation. | |
| 218 if (retry_count_ >= (1 << kMaxRetryLanguageListFetch)) { | |
| 219 NotifyEvent(__LINE__, "Request is omitted due to retry limitation"); | |
| 220 return false; | |
| 221 } | |
| 222 if (!IsOneHot(++retry_count_)) { | |
|
MAD
2013/06/10 18:28:10
Actually, the URLFetcher already supports backoff
Takashi Toyoshima
2013/06/12 05:20:43
Yes. But it works only against HTTP status 5xx.
Th
| |
| 223 NotifyEvent(__LINE__, "Request is omitted due to exponential backoff"); | |
| 224 return false; | |
| 225 } | |
| 226 | |
| 227 state_ = REQUESTING; | |
| 228 callback_ = callback; | |
| 229 | |
| 230 GURL url = GURL(kLanguageListFetchURL); | |
| 231 url = TranslateURLUtil::AddHostLocaleToUrl(url); | |
| 232 url = TranslateURLUtil::AddApiKeyToUrl(url); | |
| 233 if (include_alpha_languages_) { | |
| 234 url = net::AppendQueryParameter(url, | |
| 235 kAlphaLanguageQueryName, | |
| 236 kAlphaLanguageQueryValue); | |
| 237 } | |
| 238 | |
| 239 std::string message = base::StringPrintf( | |
| 240 "%s list fetch starts (URL: %s)", | |
| 241 include_alpha_languages_ ? "Language" : "Alpha language", | |
| 242 url.spec().c_str()); | |
| 243 NotifyEvent(__LINE__, message); | |
| 244 | |
| 245 fetcher_.reset(net::URLFetcher::Create( | |
| 246 include_alpha_languages_ ? kFetcherIdForAlphaLanguageList : | |
| 247 kFetcherIdForLanguageList, | |
| 248 url, | |
| 249 net::URLFetcher::GET, | |
| 250 this)); | |
| 251 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
| 252 net::LOAD_DO_NOT_SAVE_COOKIES); | |
| 253 fetcher_->SetRequestContext(g_browser_process->system_request_context()); | |
| 254 fetcher_->SetMaxRetriesOn5xx(kMaxRetryLanguageListFetchOn5xx); | |
| 255 fetcher_->Start(); | |
| 256 | |
| 257 return true; | |
| 258 } | |
| 259 | |
| 260 void TranslateLanguageList::LanguageListFetcher::OnURLFetchComplete( | |
| 261 const net::URLFetcher* source) { | |
| 262 DCHECK(fetcher_.get() == source); | |
| 263 | |
| 264 std::string data; | |
| 265 if (source->GetStatus().status() == net::URLRequestStatus::SUCCESS && | |
| 266 source->GetResponseCode() == net::HTTP_OK) { | |
| 267 state_ = COMPLETED; | |
| 268 source->GetResponseAsString(&data); | |
| 269 std::string message = base::StringPrintf( | |
| 270 "%s list is updated", | |
| 271 include_alpha_languages_ ? "Alpha language" : "Language"); | |
| 272 NotifyEvent(__LINE__, message); | |
| 273 } else { | |
| 274 state_ = FAILED; | |
| 275 std::string message = base::StringPrintf( | |
| 276 "Failed to Fetch languages from: %s", | |
| 277 source->GetURL().spec().c_str()); | |
| 278 NotifyEvent(__LINE__, message); | |
| 279 } | |
| 280 | |
| 281 scoped_ptr<const net::URLFetcher> delete_ptr(fetcher_.release()); | |
| 282 callback_.Run(include_alpha_languages_, state_ == COMPLETED, data); | |
| 283 } | |
| 284 | |
| 285 // Transfer URLFetcher's ownership before invoking a callback. | |
| 206 // This must be kept in sync with the &cb= value in the kLanguageListFetchURL. | 286 // This must be kept in sync with the &cb= value in the kLanguageListFetchURL. |
| 207 const char TranslateLanguageList::kLanguageListCallbackName[] = "sl("; | 287 const char TranslateLanguageList::kLanguageListCallbackName[] = "sl("; |
| 208 const char TranslateLanguageList::kTargetLanguagesKey[] = "tl"; | 288 const char TranslateLanguageList::kTargetLanguagesKey[] = "tl"; |
| 209 | 289 |
| 210 TranslateLanguageList::TranslateLanguageList() { | 290 TranslateLanguageList::TranslateLanguageList() { |
| 211 // We default to our hard coded list of languages in | 291 // We default to our hard coded list of languages in |
| 212 // |kDefaultSupportedLanguages|. This list will be overriden by a server | 292 // |kDefaultSupportedLanguages|. This list will be overriden by a server |
| 213 // providing supported langauges list. | 293 // providing supported langauges list. |
| 214 for (size_t i = 0; i < arraysize(kDefaultSupportedLanguages); ++i) | 294 for (size_t i = 0; i < arraysize(kDefaultSupportedLanguages); ++i) |
| 215 supported_languages_.insert(kDefaultSupportedLanguages[i]); | 295 supported_languages_.insert(kDefaultSupportedLanguages[i]); |
| 216 UpdateSupportedLanguages(); | 296 UpdateSupportedLanguages(); |
| 297 | |
| 298 language_list_fetcher_.reset(new LanguageListFetcher(false)); | |
| 299 alpha_language_list_fetcher_.reset(new LanguageListFetcher(true)); | |
| 300 | |
| 301 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); | |
| 217 } | 302 } |
| 218 | 303 |
| 219 TranslateLanguageList::~TranslateLanguageList() {} | 304 TranslateLanguageList::~TranslateLanguageList() { |
| 220 | 305 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
| 221 void TranslateLanguageList::OnURLFetchComplete(const net::URLFetcher* source) { | |
| 222 scoped_ptr<const net::URLFetcher> delete_ptr; | |
| 223 | |
| 224 if (source->GetStatus().status() == net::URLRequestStatus::SUCCESS && | |
| 225 source->GetResponseCode() == net::HTTP_OK) { | |
| 226 std::string data; | |
| 227 source->GetResponseAsString(&data); | |
| 228 if (language_list_fetcher_.get() == source) { | |
| 229 NotifyEvent(__LINE__, "Language list is updated"); | |
| 230 delete_ptr.reset(language_list_fetcher_.release()); | |
| 231 SetSupportedLanguages(data, &supported_languages_); | |
| 232 } else if (alpha_language_list_fetcher_.get() == source) { | |
| 233 NotifyEvent(__LINE__, "Alpha language list is updated"); | |
| 234 delete_ptr.reset(alpha_language_list_fetcher_.release()); | |
| 235 SetSupportedLanguages(data, &supported_alpha_languages_); | |
| 236 } else { | |
| 237 NOTREACHED(); | |
| 238 } | |
| 239 UpdateSupportedLanguages(); | |
| 240 } else { | |
| 241 // TODO(toyoshim): Try again. http://crbug.com/244202 . | |
| 242 // Also In CrOS, FetchLanguageList is not called at launching Chrome. It | |
| 243 // will solve this problem that check if FetchLanguageList is already | |
| 244 // called, and call it if needed in InitSupportedLanguage(). | |
| 245 std::string message = base::StringPrintf( | |
| 246 "%s list update fails (Status: %d, URL: %s)", | |
| 247 (language_list_fetcher_.get() == source) ? "Language" : | |
| 248 "Alpha language", | |
| 249 source->GetResponseCode(), | |
| 250 source->GetOriginalURL().spec().c_str()); | |
| 251 NotifyEvent(__LINE__, message); | |
| 252 } | |
| 253 } | 306 } |
| 254 | 307 |
| 255 void TranslateLanguageList::GetSupportedLanguages( | 308 void TranslateLanguageList::GetSupportedLanguages( |
| 256 std::vector<std::string>* languages) { | 309 std::vector<std::string>* languages) { |
| 257 DCHECK(languages && languages->empty()); | 310 DCHECK(languages && languages->empty()); |
| 258 std::set<std::string>::const_iterator iter = all_supported_languages_.begin(); | 311 std::set<std::string>::const_iterator iter = all_supported_languages_.begin(); |
| 259 for (; iter != all_supported_languages_.end(); ++iter) | 312 for (; iter != all_supported_languages_.end(); ++iter) |
| 260 languages->push_back(*iter); | 313 languages->push_back(*iter); |
| 314 | |
| 315 // Update language lists if they are not updated after Chrome was launched | |
| 316 // for later requests. | |
| 317 RequestLanguageList(); | |
| 261 } | 318 } |
| 262 | 319 |
| 263 std::string TranslateLanguageList::GetLanguageCode( | 320 std::string TranslateLanguageList::GetLanguageCode( |
| 264 const std::string& chrome_locale) { | 321 const std::string& chrome_locale) { |
| 265 // Only remove the country code for country specific languages we don't | 322 // Only remove the country code for country specific languages we don't |
| 266 // support specifically yet. | 323 // support specifically yet. |
| 267 if (IsSupportedLanguage(chrome_locale)) | 324 if (IsSupportedLanguage(chrome_locale)) |
| 268 return chrome_locale; | 325 return chrome_locale; |
| 269 | 326 |
| 270 size_t hypen_index = chrome_locale.find('-'); | 327 size_t hypen_index = chrome_locale.find('-'); |
| 271 if (hypen_index == std::string::npos) | 328 if (hypen_index == std::string::npos) |
| 272 return chrome_locale; | 329 return chrome_locale; |
| 273 return chrome_locale.substr(0, hypen_index); | 330 return chrome_locale.substr(0, hypen_index); |
| 274 } | 331 } |
| 275 | 332 |
| 276 bool TranslateLanguageList::IsSupportedLanguage(const std::string& language) { | 333 bool TranslateLanguageList::IsSupportedLanguage(const std::string& language) { |
| 277 return all_supported_languages_.count(language) != 0; | 334 return all_supported_languages_.count(language) != 0; |
| 278 } | 335 } |
| 279 | 336 |
| 280 bool TranslateLanguageList::IsAlphaLanguage(const std::string& language) { | 337 bool TranslateLanguageList::IsAlphaLanguage(const std::string& language) { |
| 281 // |language| should exist only in the alpha language list. | 338 // |language| should exist only in the alpha language list. |
| 282 return supported_alpha_languages_.count(language) != 0 && | 339 return supported_alpha_languages_.count(language) != 0 && |
| 283 supported_languages_.count(language) == 0; | 340 supported_languages_.count(language) == 0; |
| 284 } | 341 } |
| 285 | 342 |
| 286 void TranslateLanguageList::RequestLanguageList() { | 343 void TranslateLanguageList::RequestLanguageList() { |
| 287 if (language_list_fetcher_.get() || alpha_language_list_fetcher_.get()) | 344 if (language_list_fetcher_.get() && |
| 345 (language_list_fetcher_->state() == LanguageListFetcher::IDLE || | |
| 346 language_list_fetcher_->state() == LanguageListFetcher::FAILED)) { | |
| 347 language_list_fetcher_->Request( | |
| 348 base::Bind(&TranslateLanguageList::OnLanguageListFetchComplete, | |
| 349 base::Unretained(this))); | |
| 350 } | |
| 351 | |
| 352 if (alpha_language_list_fetcher_.get() && | |
| 353 (alpha_language_list_fetcher_->state() == LanguageListFetcher::IDLE || | |
| 354 alpha_language_list_fetcher_->state() == LanguageListFetcher::FAILED)) { | |
| 355 alpha_language_list_fetcher_->Request( | |
| 356 base::Bind(&TranslateLanguageList::OnLanguageListFetchComplete, | |
| 357 base::Unretained(this))); | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 void TranslateLanguageList::OnNetworkChanged( | |
| 362 net::NetworkChangeNotifier::ConnectionType type) { | |
| 363 std::string message = base::StringPrintf("OnNetworkChanged: %d, %s", type, | |
| 364 net::NetworkChangeNotifier::IsOffline() ? "offline" : "online"); | |
| 365 NotifyEvent(__LINE__, message); | |
| 366 | |
| 367 if (net::NetworkChangeNotifier::IsOffline()) | |
| 288 return; | 368 return; |
| 289 | 369 |
| 290 // Fetch the stable language list. | 370 RequestLanguageList(); |
| 291 GURL language_list_fetch_url = GURL(kLanguageListFetchURL); | 371 } |
| 292 language_list_fetch_url = | |
| 293 TranslateURLUtil::AddHostLocaleToUrl(language_list_fetch_url); | |
| 294 language_list_fetch_url = | |
| 295 TranslateURLUtil::AddApiKeyToUrl(language_list_fetch_url); | |
| 296 | 372 |
| 297 language_list_fetcher_.reset( | 373 void TranslateLanguageList::OnLanguageListFetchComplete( |
| 298 CreateAndStartFetch(kFetcherIdForLanguageList, | 374 bool include_alpha_languages, |
| 299 language_list_fetch_url, | 375 bool success, |
| 300 this)); | 376 const std::string& data) { |
| 377 if (!success) | |
|
MAD
2013/06/10 18:28:10
You don't actually retry on failures, and you don'
Takashi Toyoshima
2013/06/12 05:20:43
No. It was invoked in GetSupportedLanguages() if t
| |
| 378 return; | |
| 301 | 379 |
| 302 // Fetch the alpha language list. | 380 if (!include_alpha_languages) { |
| 303 language_list_fetch_url = net::AppendQueryParameter( | 381 SetSupportedLanguages(data, &supported_languages_); |
| 304 language_list_fetch_url, | 382 language_list_fetcher_.reset(); |
| 305 kAlphaLanguageQueryName, | 383 } else { |
| 306 kAlphaLanguageQueryValue); | 384 SetSupportedLanguages(data, &supported_alpha_languages_); |
| 307 | 385 alpha_language_list_fetcher_.reset(); |
| 308 alpha_language_list_fetcher_.reset( | 386 } |
| 309 CreateAndStartFetch(kFetcherIdForAlphaLanguageList, | 387 UpdateSupportedLanguages(); |
| 310 language_list_fetch_url, | |
| 311 this)); | |
| 312 } | 388 } |
| 313 | 389 |
| 314 void TranslateLanguageList::UpdateSupportedLanguages() { | 390 void TranslateLanguageList::UpdateSupportedLanguages() { |
| 315 all_supported_languages_.clear(); | 391 all_supported_languages_.clear(); |
| 316 std::set<std::string>::const_iterator iter; | 392 std::set<std::string>::const_iterator iter; |
| 317 for (iter = supported_languages_.begin(); | 393 for (iter = supported_languages_.begin(); |
| 318 iter != supported_languages_.end(); | 394 iter != supported_languages_.end(); |
| 319 ++iter) | 395 ++iter) |
| 320 all_supported_languages_.insert(*iter); | 396 all_supported_languages_.insert(*iter); |
| 321 for (iter = supported_alpha_languages_.begin(); | 397 for (iter = supported_alpha_languages_.begin(); |
| 322 iter != supported_alpha_languages_.end(); | 398 iter != supported_alpha_languages_.end(); |
| 323 ++iter) | 399 ++iter) |
| 324 all_supported_languages_.insert(*iter); | 400 all_supported_languages_.insert(*iter); |
| 325 } | 401 } |
| OLD | NEW |