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 |