OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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_manager.h" | 5 #include "chrome/browser/translate/translate_manager.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/compiler_specific.h" | 8 #include "base/compiler_specific.h" |
9 #include "base/json/json_reader.h" | |
9 #include "base/memory/singleton.h" | 10 #include "base/memory/singleton.h" |
10 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
11 #include "base/string_split.h" | 12 #include "base/string_split.h" |
12 #include "base/string_util.h" | 13 #include "base/string_util.h" |
14 #include "base/values.h" | |
13 #include "chrome/browser/autofill/autofill_manager.h" | 15 #include "chrome/browser/autofill/autofill_manager.h" |
14 #include "chrome/browser/browser_process.h" | 16 #include "chrome/browser/browser_process.h" |
15 #include "chrome/browser/prefs/pref_service.h" | 17 #include "chrome/browser/prefs/pref_service.h" |
16 #include "chrome/browser/profiles/profile.h" | 18 #include "chrome/browser/profiles/profile.h" |
17 #include "chrome/browser/tab_contents/language_state.h" | 19 #include "chrome/browser/tab_contents/language_state.h" |
18 #include "chrome/browser/tab_contents/tab_util.h" | 20 #include "chrome/browser/tab_contents/tab_util.h" |
19 #include "chrome/browser/tabs/tab_strip_model.h" | 21 #include "chrome/browser/tabs/tab_strip_model.h" |
20 #include "chrome/browser/translate/page_translated_details.h" | 22 #include "chrome/browser/translate/page_translated_details.h" |
21 #include "chrome/browser/translate/translate_infobar_delegate.h" | 23 #include "chrome/browser/translate/translate_infobar_delegate.h" |
22 #include "chrome/browser/translate/translate_tab_helper.h" | 24 #include "chrome/browser/translate/translate_tab_helper.h" |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
117 "yi", // Yiddish | 119 "yi", // Yiddish |
118 }; | 120 }; |
119 | 121 |
120 const char* const kTranslateScriptURL = | 122 const char* const kTranslateScriptURL = |
121 "http://translate.google.com/translate_a/element.js?" | 123 "http://translate.google.com/translate_a/element.js?" |
122 "cb=cr.googleTranslate.onTranslateElementLoad"; | 124 "cb=cr.googleTranslate.onTranslateElementLoad"; |
123 const char* const kTranslateScriptHeader = | 125 const char* const kTranslateScriptHeader = |
124 "Google-Translate-Element-Mode: library"; | 126 "Google-Translate-Element-Mode: library"; |
125 const char* const kReportLanguageDetectionErrorURL = | 127 const char* const kReportLanguageDetectionErrorURL = |
126 "http://translate.google.com/translate_error"; | 128 "http://translate.google.com/translate_error"; |
127 | 129 const char* const kLanguageListFetchURL = |
130 "http://translate.googleapis.com/translate_a/l?client=chrome&cb=sl"; | |
131 // This must be kept in sync with the &cb= value in the kLanguageListFetchURL. | |
132 const char* const kLaguangeListCallbackName = "sl("; | |
Jay Civelli
2011/06/16 07:11:13
Typo: kLaguange
MAD
2011/06/16 14:22:17
Done.
| |
133 const size_t kLaguangeListCallbackNameLength = | |
134 strlen(kLaguangeListCallbackName); | |
135 const char* const kTargetLanguagesKey = "tl"; | |
136 const int kMaxRetryLanguageListFetch = 5; | |
128 const int kTranslateScriptExpirationDelayMS = 24 * 60 * 60 * 1000; // 1 day. | 137 const int kTranslateScriptExpirationDelayMS = 24 * 60 * 60 * 1000; // 1 day. |
129 | 138 |
130 } // namespace | 139 } // namespace |
131 | 140 |
132 // static | 141 // static |
133 base::LazyInstance<std::set<std::string> > | 142 base::LazyInstance<std::set<std::string> > |
134 TranslateManager::supported_languages_(base::LINKER_INITIALIZED); | 143 TranslateManager::supported_languages_(base::LINKER_INITIALIZED); |
135 | 144 |
136 TranslateManager::~TranslateManager() { | 145 TranslateManager::~TranslateManager() { |
137 } | 146 } |
138 | 147 |
139 // static | 148 // static |
140 TranslateManager* TranslateManager::GetInstance() { | 149 TranslateManager* TranslateManager::GetInstance() { |
141 return Singleton<TranslateManager>::get(); | 150 return Singleton<TranslateManager>::get(); |
142 } | 151 } |
143 | 152 |
144 // static | 153 // static |
145 bool TranslateManager::IsTranslatableURL(const GURL& url) { | 154 bool TranslateManager::IsTranslatableURL(const GURL& url) { |
146 // A URLs is translatable unless it is one of the following: | 155 // A URLs is translatable unless it is one of the following: |
147 // - an internal URL (chrome:// and others) | 156 // - an internal URL (chrome:// and others) |
148 // - the devtools (which is considered UI) | 157 // - the devtools (which is considered UI) |
149 // - an FTP page (as FTP pages tend to have long lists of filenames that may | 158 // - an FTP page (as FTP pages tend to have long lists of filenames that may |
150 // confuse the CLD) | 159 // confuse the CLD) |
151 return !url.SchemeIs(chrome::kChromeUIScheme) && | 160 return !url.SchemeIs(chrome::kChromeUIScheme) && |
152 !url.SchemeIs(chrome::kChromeDevToolsScheme) && | 161 !url.SchemeIs(chrome::kChromeDevToolsScheme) && |
153 !url.SchemeIs(chrome::kFtpScheme); | 162 !url.SchemeIs(chrome::kFtpScheme); |
154 } | 163 } |
155 | 164 |
156 // static | 165 // static |
166 void TranslateManager::SetSupportedLanguages(const std::string& language_list) { | |
167 // The format is: | |
168 // sl({'sl': {'XX': 'LanguageName', ...}, 'tl': {'XX': 'LanguageName', ...}}) | |
169 // Where "sl(" is set in kLaguangeListCallbackName | |
170 // and 'tl' is kTargetLanguagesKey | |
171 if (!StartsWithASCII(language_list, kLaguangeListCallbackName, false) || | |
172 !EndsWith(language_list, ")", false)) { | |
173 // We don't DCHECK here since this can happen in ui_tests, even though the | |
174 // the BrowserMain function won't call us with parameters.ui_task is NULL | |
175 // some tests don't set it, so we must bail here. | |
176 return; | |
177 } | |
178 std::string languages_json = language_list.substr( | |
179 kLaguangeListCallbackNameLength, | |
180 language_list.size() - kLaguangeListCallbackNameLength - 1); | |
181 // JSON doesn't support single quotes though this is what is used on the | |
182 // translate server so we must replace them with double quotes. | |
183 ReplaceSubstringsAfterOffset(&languages_json, 0, "'", "\""); | |
184 scoped_ptr<Value> json_value(base::JSONReader::Read(languages_json, true)); | |
185 DCHECK(json_value != NULL && json_value->IsType(Value::TYPE_DICTIONARY)); | |
186 if (json_value == NULL || !json_value->IsType(Value::TYPE_DICTIONARY)) | |
Jay Civelli
2011/06/16 07:11:13
May be instead of repeating the condition in the D
MAD
2011/06/16 14:22:17
Done.
| |
187 return; | |
188 // The first level dictionary contains two sub-dict, one for source languages | |
189 // and the other for target languages, we want to use the target languages. | |
190 DictionaryValue* language_dict = | |
191 static_cast<DictionaryValue*>(json_value.get()); | |
192 DCHECK(language_dict->HasKey(kTargetLanguagesKey)); | |
Jay Civelli
2011/06/16 07:11:13
Couldn't you replace these lines up 203 with a cal
MAD
2011/06/16 14:22:17
Done.
Thanks, I forgot about this shortcut...
| |
193 Value* target_languages_value = NULL; | |
194 if (!language_dict->Get(kTargetLanguagesKey, &target_languages_value)) | |
195 return; | |
196 DCHECK(target_languages_value != NULL && | |
197 target_languages_value->IsType(Value::TYPE_DICTIONARY)); | |
198 if (target_languages_value == NULL || | |
199 !target_languages_value->IsType(Value::TYPE_DICTIONARY)) { | |
200 return; | |
201 } | |
202 DictionaryValue* target_languages = | |
203 static_cast<DictionaryValue*>(target_languages_value); | |
204 // Now we can clear our current state... | |
205 std::set<std::string>* supported_languages = supported_languages_.Pointer(); | |
206 supported_languages->clear(); | |
207 // ... and replace it with the values we just fetched from the server. | |
208 DictionaryValue::key_iterator iter = target_languages->begin_keys(); | |
209 for (; iter != target_languages->end_keys(); ++iter) | |
210 supported_languages_.Pointer()->insert(*iter); | |
211 } | |
212 | |
213 // static | |
214 void TranslateManager::InitSupportedLanguages() { | |
215 // If our list of supported languages have not been set yet, we default | |
216 // to our hard coded list of languages in kSupportedLanguages. | |
217 if (supported_languages_.Pointer()->empty()) { | |
218 for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i) | |
219 supported_languages_.Pointer()->insert(kSupportedLanguages[i]); | |
220 } | |
221 } | |
222 | |
223 // static | |
157 void TranslateManager::GetSupportedLanguages( | 224 void TranslateManager::GetSupportedLanguages( |
158 std::vector<std::string>* languages) { | 225 std::vector<std::string>* languages) { |
159 DCHECK(languages && languages->empty()); | 226 DCHECK(languages && languages->empty()); |
160 for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i) | 227 InitSupportedLanguages(); |
161 languages->push_back(kSupportedLanguages[i]); | 228 std::set<std::string>* supported_languages = supported_languages_.Pointer(); |
229 std::set<std::string>::const_iterator iter = supported_languages->begin(); | |
230 for (; iter != supported_languages->end(); ++iter) | |
231 languages->push_back(*iter); | |
162 } | 232 } |
163 | 233 |
164 // static | 234 // static |
165 std::string TranslateManager::GetLanguageCode( | 235 std::string TranslateManager::GetLanguageCode( |
166 const std::string& chrome_locale) { | 236 const std::string& chrome_locale) { |
167 // Only remove the country code for country specific languages we don't | 237 // Only remove the country code for country specific languages we don't |
168 // support specifically yet. | 238 // support specifically yet. |
169 if (IsSupportedLanguage(chrome_locale)) | 239 if (IsSupportedLanguage(chrome_locale)) |
170 return chrome_locale; | 240 return chrome_locale; |
171 | 241 |
172 size_t hypen_index = chrome_locale.find('-'); | 242 size_t hypen_index = chrome_locale.find('-'); |
173 if (hypen_index == std::string::npos) | 243 if (hypen_index == std::string::npos) |
174 return chrome_locale; | 244 return chrome_locale; |
175 return chrome_locale.substr(0, hypen_index); | 245 return chrome_locale.substr(0, hypen_index); |
176 } | 246 } |
177 | 247 |
178 // static | 248 // static |
179 bool TranslateManager::IsSupportedLanguage(const std::string& page_language) { | 249 bool TranslateManager::IsSupportedLanguage(const std::string& page_language) { |
180 std::set<std::string>* supported_languages = supported_languages_.Pointer(); | 250 InitSupportedLanguages(); |
181 if (supported_languages->empty()) { | 251 return supported_languages_.Pointer()->count(page_language) != 0; |
182 for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i) | |
183 supported_languages->insert(kSupportedLanguages[i]); | |
184 } | |
185 return supported_languages->find(page_language) != supported_languages->end(); | |
186 } | 252 } |
187 | 253 |
188 void TranslateManager::Observe(NotificationType type, | 254 void TranslateManager::Observe(NotificationType type, |
189 const NotificationSource& source, | 255 const NotificationSource& source, |
190 const NotificationDetails& details) { | 256 const NotificationDetails& details) { |
191 switch (type.value) { | 257 switch (type.value) { |
192 case NotificationType::NAV_ENTRY_COMMITTED: { | 258 case NotificationType::NAV_ENTRY_COMMITTED: { |
193 NavigationController* controller = | 259 NavigationController* controller = |
194 Source<NavigationController>(source).ptr(); | 260 Source<NavigationController>(source).ptr(); |
195 content::LoadCommittedDetails* load_details = | 261 content::LoadCommittedDetails* load_details = |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
281 } | 347 } |
282 } | 348 } |
283 | 349 |
284 void TranslateManager::OnURLFetchComplete(const URLFetcher* source, | 350 void TranslateManager::OnURLFetchComplete(const URLFetcher* source, |
285 const GURL& url, | 351 const GURL& url, |
286 const net::URLRequestStatus& status, | 352 const net::URLRequestStatus& status, |
287 int response_code, | 353 int response_code, |
288 const net::ResponseCookies& cookies, | 354 const net::ResponseCookies& cookies, |
289 const std::string& data) { | 355 const std::string& data) { |
290 scoped_ptr<const URLFetcher> delete_ptr(source); | 356 scoped_ptr<const URLFetcher> delete_ptr(source); |
291 DCHECK(translate_script_request_pending_); | 357 DCHECK(translate_script_request_pending_ || language_list_request_pending_); |
292 translate_script_request_pending_ = false; | 358 // We quickly recognize that we are handling a translate script request |
359 // if we don't have a language_list_request_pending_. Otherwise we do the | |
360 // more expensive check of confirming we got the kTranslateScriptURL in the | |
361 // rare case where we would have both requests pending at the same time. | |
362 bool translate_script_request = !language_list_request_pending_ || | |
363 url == GURL(kTranslateScriptURL); | |
364 // Here we make sure that if we didn't get the translate_script_request, | |
365 // we actually got a language_list_request. | |
366 DCHECK(translate_script_request || url == GURL(kLanguageListFetchURL)); | |
367 if (translate_script_request) | |
368 translate_script_request_pending_ = false; | |
369 else | |
370 language_list_request_pending_ = false; | |
371 | |
293 bool error = | 372 bool error = |
294 (status.status() != net::URLRequestStatus::SUCCESS || | 373 (status.status() != net::URLRequestStatus::SUCCESS || |
295 response_code != 200); | 374 response_code != 200); |
296 | 375 |
297 if (!error) { | 376 if (translate_script_request) { |
298 base::StringPiece str = ResourceBundle::GetSharedInstance(). | 377 if (!error) { |
299 GetRawDataResource(IDR_TRANSLATE_JS); | 378 base::StringPiece str = ResourceBundle::GetSharedInstance(). |
300 DCHECK(translate_script_.empty()); | 379 GetRawDataResource(IDR_TRANSLATE_JS); |
301 str.CopyToString(&translate_script_); | 380 DCHECK(translate_script_.empty()); |
302 translate_script_ += "\n" + data; | 381 str.CopyToString(&translate_script_); |
303 // We'll expire the cached script after some time, to make sure long running | 382 translate_script_ += "\n" + data; |
304 // browsers still get fixes that might get pushed with newer scripts. | 383 // We'll expire the cached script after some time, to make sure long |
305 MessageLoop::current()->PostDelayedTask(FROM_HERE, | 384 // running browsers still get fixes that might get pushed with newer |
306 method_factory_.NewRunnableMethod( | 385 // scripts. |
307 &TranslateManager::ClearTranslateScript), | 386 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
308 translate_script_expiration_delay_); | 387 method_factory_.NewRunnableMethod( |
388 &TranslateManager::ClearTranslateScript), | |
389 translate_script_expiration_delay_); | |
390 } | |
391 // Process any pending requests. | |
392 std::vector<PendingRequest>::const_iterator iter; | |
393 for (iter = pending_requests_.begin(); iter != pending_requests_.end(); | |
394 ++iter) { | |
395 const PendingRequest& request = *iter; | |
396 TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id, | |
397 request.render_view_id); | |
398 if (!tab) { | |
399 // The tab went away while we were retrieving the script. | |
400 continue; | |
401 } | |
402 NavigationEntry* entry = tab->controller().GetActiveEntry(); | |
403 if (!entry || entry->page_id() != request.page_id) { | |
404 // We navigated away from the page the translation was triggered on. | |
405 continue; | |
406 } | |
407 | |
408 if (error) { | |
409 ShowInfoBar(tab, TranslateInfoBarDelegate::CreateErrorDelegate( | |
410 TranslateErrors::NETWORK, tab, | |
411 request.source_lang, request.target_lang)); | |
412 } else { | |
413 // Translate the page. | |
414 DoTranslatePage(tab, translate_script_, | |
415 request.source_lang, request.target_lang); | |
416 } | |
417 } | |
418 pending_requests_.clear(); | |
419 } else { // if (translate_script_request) | |
420 if (!error) | |
421 SetSupportedLanguages(data); | |
422 else | |
423 VLOG(1) << "Failed to Fetch languages from: " << kLanguageListFetchURL; | |
309 } | 424 } |
310 | |
311 // Process any pending requests. | |
312 std::vector<PendingRequest>::const_iterator iter; | |
313 for (iter = pending_requests_.begin(); iter != pending_requests_.end(); | |
314 ++iter) { | |
315 const PendingRequest& request = *iter; | |
316 TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id, | |
317 request.render_view_id); | |
318 if (!tab) { | |
319 // The tab went away while we were retrieving the script. | |
320 continue; | |
321 } | |
322 NavigationEntry* entry = tab->controller().GetActiveEntry(); | |
323 if (!entry || entry->page_id() != request.page_id) { | |
324 // We navigated away from the page the translation was triggered on. | |
325 continue; | |
326 } | |
327 | |
328 if (error) { | |
329 ShowInfoBar(tab, TranslateInfoBarDelegate::CreateErrorDelegate( | |
330 TranslateErrors::NETWORK, tab, | |
331 request.source_lang, request.target_lang)); | |
332 } else { | |
333 // Translate the page. | |
334 DoTranslatePage(tab, translate_script_, | |
335 request.source_lang, request.target_lang); | |
336 } | |
337 } | |
338 pending_requests_.clear(); | |
339 } | 425 } |
340 | 426 |
341 // static | 427 // static |
342 bool TranslateManager::IsShowingTranslateInfobar(TabContents* tab) { | 428 bool TranslateManager::IsShowingTranslateInfobar(TabContents* tab) { |
343 return GetTranslateInfoBarDelegate(tab) != NULL; | 429 return GetTranslateInfoBarDelegate(tab) != NULL; |
344 } | 430 } |
345 | 431 |
346 TranslateManager::TranslateManager() | 432 TranslateManager::TranslateManager() |
347 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), | 433 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), |
348 translate_script_expiration_delay_(kTranslateScriptExpirationDelayMS), | 434 translate_script_expiration_delay_(kTranslateScriptExpirationDelayMS), |
349 translate_script_request_pending_(false) { | 435 translate_script_request_pending_(false), |
436 language_list_request_pending_(false) { | |
350 notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, | 437 notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, |
351 NotificationService::AllSources()); | 438 NotificationService::AllSources()); |
352 notification_registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED, | 439 notification_registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED, |
353 NotificationService::AllSources()); | 440 NotificationService::AllSources()); |
354 notification_registrar_.Add(this, NotificationType::PAGE_TRANSLATED, | 441 notification_registrar_.Add(this, NotificationType::PAGE_TRANSLATED, |
355 NotificationService::AllSources()); | 442 NotificationService::AllSources()); |
356 } | 443 } |
357 | 444 |
358 void TranslateManager::InitiateTranslation(TabContents* tab, | 445 void TranslateManager::InitiateTranslation(TabContents* tab, |
359 const std::string& page_lang) { | 446 const std::string& page_lang) { |
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
612 // black-listed. | 699 // black-listed. |
613 // TODO(jungshik): Once we determine that it's safe to remove English from | 700 // TODO(jungshik): Once we determine that it's safe to remove English from |
614 // the default Accept-Language values for most locales, remove this | 701 // the default Accept-Language values for most locales, remove this |
615 // special-casing. | 702 // special-casing. |
616 if (accept_lang != "en" || is_ui_english) | 703 if (accept_lang != "en" || is_ui_english) |
617 accept_langs_set.insert(accept_lang); | 704 accept_langs_set.insert(accept_lang); |
618 } | 705 } |
619 accept_languages_[prefs] = accept_langs_set; | 706 accept_languages_[prefs] = accept_langs_set; |
620 } | 707 } |
621 | 708 |
709 void TranslateManager::FetchLanguageListFromTranslateServer( | |
710 PrefService* prefs) { | |
711 if (language_list_request_pending_) | |
712 return; | |
713 | |
714 // We don't want to do this when translate is disabled. | |
715 DCHECK(prefs != NULL); | |
716 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
717 switches::kDisableTranslate) || | |
718 (prefs != NULL && !prefs->GetBoolean(prefs::kEnableTranslate))) { | |
719 return; | |
720 } | |
721 | |
722 language_list_request_pending_ = true; | |
723 URLFetcher* fetcher = URLFetcher::Create(0, GURL(kLanguageListFetchURL), | |
724 URLFetcher::GET, this); | |
725 fetcher->set_request_context(Profile::GetDefaultRequestContext()); | |
726 fetcher->set_max_retries(kMaxRetryLanguageListFetch); | |
727 fetcher->Start(); | |
728 } | |
729 | |
622 void TranslateManager::RequestTranslateScript() { | 730 void TranslateManager::RequestTranslateScript() { |
623 if (translate_script_request_pending_) | 731 if (translate_script_request_pending_) |
624 return; | 732 return; |
625 | 733 |
626 translate_script_request_pending_ = true; | 734 translate_script_request_pending_ = true; |
627 URLFetcher* fetcher = URLFetcher::Create(0, GURL(kTranslateScriptURL), | 735 URLFetcher* fetcher = URLFetcher::Create(0, GURL(kTranslateScriptURL), |
628 URLFetcher::GET, this); | 736 URLFetcher::GET, this); |
629 fetcher->set_request_context(Profile::GetDefaultRequestContext()); | 737 fetcher->set_request_context(Profile::GetDefaultRequestContext()); |
630 fetcher->set_extra_request_headers(kTranslateScriptHeader); | 738 fetcher->set_extra_request_headers(kTranslateScriptHeader); |
631 fetcher->Start(); | 739 fetcher->Start(); |
(...skipping 26 matching lines...) Expand all Loading... | |
658 TabContentsWrapper* wrapper = | 766 TabContentsWrapper* wrapper = |
659 TabContentsWrapper::GetCurrentWrapperForContents(tab); | 767 TabContentsWrapper::GetCurrentWrapperForContents(tab); |
660 for (size_t i = 0; i < wrapper->infobar_count(); ++i) { | 768 for (size_t i = 0; i < wrapper->infobar_count(); ++i) { |
661 TranslateInfoBarDelegate* delegate = | 769 TranslateInfoBarDelegate* delegate = |
662 wrapper->GetInfoBarDelegateAt(i)->AsTranslateInfoBarDelegate(); | 770 wrapper->GetInfoBarDelegateAt(i)->AsTranslateInfoBarDelegate(); |
663 if (delegate) | 771 if (delegate) |
664 return delegate; | 772 return delegate; |
665 } | 773 } |
666 return NULL; | 774 return NULL; |
667 } | 775 } |
OLD | NEW |