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/translate/translate_manager.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/memory/singleton.h" 10 #include "base/metrics/histogram.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/string_split.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/time/time.h" 15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/chrome_notification_types.h" 17 #include "chrome/browser/infobars/infobar_service.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/tab_contents/language_state.h" 20 #include "chrome/browser/tab_contents/tab_util.h" 21 #include "chrome/browser/translate/page_translated_details.h" 22 #include "chrome/browser/translate/translate_accept_languages.h" 23 #include "chrome/browser/translate/translate_browser_metrics.h" 24 #include "chrome/browser/translate/translate_error_details.h" 25 #include "chrome/browser/translate/translate_event_details.h" 26 #include "chrome/browser/translate/translate_infobar_delegate.h" 27 #include "chrome/browser/translate/translate_language_list.h" 28 #include "chrome/browser/translate/translate_prefs.h" 29 #include "chrome/browser/translate/translate_script.h" 30 #include "chrome/browser/translate/translate_tab_helper.h" 31 #include "chrome/browser/translate/translate_url_util.h" 32 #include "chrome/browser/ui/browser.h" 33 #include "chrome/browser/ui/browser_finder.h" 34 #include "chrome/browser/ui/browser_tabstrip.h" 35 #include "chrome/browser/ui/tabs/tab_strip_model.h" 36 #include "chrome/common/chrome_constants.h" 37 #include "chrome/common/chrome_switches.h" 38 #include "chrome/common/pref_names.h" 39 #include "chrome/common/render_messages.h" 40 #include "chrome/common/translate/language_detection_details.h" 41 #include "chrome/common/url_constants.h" 42 #include "content/public/browser/navigation_controller.h" 43 #include "content/public/browser/navigation_details.h" 44 #include "content/public/browser/navigation_entry.h" 45 #include "content/public/browser/notification_details.h" 46 #include "content/public/browser/notification_service.h" 47 #include "content/public/browser/notification_source.h" 48 #include "content/public/browser/notification_types.h" 49 #include "content/public/browser/render_process_host.h" 50 #include "content/public/browser/render_view_host.h" 51 #include "content/public/browser/web_contents.h" 52 #include "net/base/url_util.h" 53 #include "net/http/http_status_code.h" 54 55 #ifdef FILE_MANAGER_EXTENSION 56 #include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h" 57 #include "extensions/common/constants.h" 58 #endif 59 60 using content::NavigationController; 61 using content::NavigationEntry; 62 using content::WebContents; 63 64 namespace { 65 66 const char kReportLanguageDetectionErrorURL[] = 67 "https://translate.google.com/translate_error?client=cr&action=langidc"; 68 69 // Used in kReportLanguageDetectionErrorURL to specify the original page 70 // language. 71 const char kSourceLanguageQueryName[] = "sl"; 72 73 // Used in kReportLanguageDetectionErrorURL to specify the page URL. 74 const char kUrlQueryName[] = "u"; 75 76 // The delay in ms that we'll wait to check if a page has finished loading 77 // before attempting a translation. 78 const int kTranslateLoadCheckDelayMs = 150; 79 80 // The maximum number of attempts we'll do to see if the page has finshed 81 // loading before giving up the translation 82 const int kMaxTranslateLoadCheckAttempts = 20; 83 84 } // namespace 85 86 TranslateManager::~TranslateManager() { 87 weak_method_factory_.InvalidateWeakPtrs(); 88 } 89 90 // static 91 TranslateManager* TranslateManager::GetInstance() { 92 return Singleton<TranslateManager>::get(); 93 } 94 95 // static 96 bool TranslateManager::IsTranslatableURL(const GURL& url) { 97 // A URLs is translatable unless it is one of the following: 98 // - empty (can happen for popups created with window.open("")) 99 // - an internal URL (chrome:// and others) 100 // - the devtools (which is considered UI) 101 // - Chrome OS file manager extension 102 // - an FTP page (as FTP pages tend to have long lists of filenames that may 103 // confuse the CLD) 104 return !url.is_empty() && 105 !url.SchemeIs(chrome::kChromeUIScheme) && 106 !url.SchemeIs(chrome::kChromeDevToolsScheme) && 107 #ifdef FILE_MANAGER_EXTENSION 108 !(url.SchemeIs(extensions::kExtensionScheme) && 109 url.DomainIs(kFileBrowserDomain)) && 110 #endif 111 !url.SchemeIs(chrome::kFtpScheme); 112 } 113 114 // static 115 void TranslateManager::GetSupportedLanguages( 116 std::vector<std::string>* languages) { 117 if (GetInstance()->language_list_.get()) { 118 GetInstance()->language_list_->GetSupportedLanguages(languages); 119 return; 120 } 121 NOTREACHED(); 122 } 123 124 // static 125 base::Time TranslateManager::GetSupportedLanguagesLastUpdated() { 126 if (GetInstance()->language_list_.get()) { 127 return GetInstance()->language_list_->last_updated(); 128 } 129 NOTREACHED(); 130 return base::Time(); 131 } 132 133 // static 134 std::string TranslateManager::GetLanguageCode( 135 const std::string& chrome_locale) { 136 if (GetInstance()->language_list_.get()) 137 return GetInstance()->language_list_->GetLanguageCode(chrome_locale); 138 NOTREACHED(); 139 return chrome_locale; 140 } 141 142 // static 143 bool TranslateManager::IsSupportedLanguage(const std::string& language) { 144 if (GetInstance()->language_list_.get()) 145 return GetInstance()->language_list_->IsSupportedLanguage(language); 146 NOTREACHED(); 147 return false; 148 } 149 150 // static 151 bool TranslateManager::IsAlphaLanguage(const std::string& language) { 152 if (GetInstance()->language_list_.get()) 153 return GetInstance()->language_list_->IsAlphaLanguage(language); 154 NOTREACHED(); 155 return false; 156 } 157 158 // static 159 bool TranslateManager::IsAcceptLanguage(Profile* profile, 160 const std::string& language) { 161 if (GetInstance()->accept_languages_.get()) { 162 return GetInstance()->accept_languages_->IsAcceptLanguage( 163 profile, language); 164 } 165 NOTREACHED(); 166 return false; 167 } 168 169 void TranslateManager::SetTranslateScriptExpirationDelay(int delay_ms) { 170 if (script_.get() == NULL) { 171 NOTREACHED(); 172 return; 173 } 174 script_->set_expiration_delay(delay_ms); 175 } 176 177 void TranslateManager::Observe(int type, 178 const content::NotificationSource& source, 179 const content::NotificationDetails& details) { 180 switch (type) { 181 case content::NOTIFICATION_NAV_ENTRY_COMMITTED: { 182 NavigationController* controller = 183 content::Source<NavigationController>(source).ptr(); 184 content::LoadCommittedDetails* load_details = 185 content::Details<content::LoadCommittedDetails>(details).ptr(); 186 NavigationEntry* entry = controller->GetActiveEntry(); 187 if (!entry) { 188 NOTREACHED(); 189 return; 190 } 191 192 TranslateTabHelper* translate_tab_helper = 193 TranslateTabHelper::FromWebContents(controller->GetWebContents()); 194 if (!translate_tab_helper) 195 return; 196 197 // If the navigation happened while offline don't show the translate 198 // bar since there will be nothing to translate. 199 if (load_details->http_status_code == 0 || 200 load_details->http_status_code == net::HTTP_INTERNAL_SERVER_ERROR) { 201 return; 202 } 203 204 if (!load_details->is_main_frame && 205 translate_tab_helper->language_state().translation_declined()) { 206 // Some sites (such as Google map) may trigger sub-frame navigations 207 // when the user interacts with the page. We don't want to show a new 208 // infobar if the user already dismissed one in that case. 209 return; 210 } 211 if (entry->GetTransitionType() != content::PAGE_TRANSITION_RELOAD && 212 load_details->type != content::NAVIGATION_TYPE_SAME_PAGE) { 213 return; 214 } 215 216 // When doing a page reload, TAB_LANGUAGE_DETERMINED is not sent, 217 // so the translation needs to be explicitly initiated, but only when the 218 // page needs translation. 219 if (!translate_tab_helper->language_state().page_needs_translation()) 220 return; 221 // Note that we delay it as the TranslateManager gets this notification 222 // before the WebContents and the WebContents processing might remove the 223 // current infobars. Since InitTranslation might add an infobar, it must 224 // be done after that. 225 base::MessageLoop::current()->PostTask(FROM_HERE, 226 base::Bind( 227 &TranslateManager::InitiateTranslationPosted, 228 weak_method_factory_.GetWeakPtr(), 229 controller->GetWebContents()->GetRenderProcessHost()->GetID(), 230 controller->GetWebContents()->GetRenderViewHost()->GetRoutingID(), 231 translate_tab_helper->language_state().original_language(), 0)); 232 break; 233 } 234 case chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED: { 235 const LanguageDetectionDetails* lang_det_details = 236 content::Details<const LanguageDetectionDetails>(details).ptr(); 237 238 WebContents* tab = content::Source<WebContents>(source).ptr(); 239 if (!tab->GetBrowserContext()->IsOffTheRecord()) 240 NotifyLanguageDetection(*lang_det_details); 241 242 // We may get this notifications multiple times. Make sure to translate 243 // only once. 244 TranslateTabHelper* translate_tab_helper = 245 TranslateTabHelper::FromWebContents(tab); 246 if (!translate_tab_helper) 247 return; 248 249 LanguageState& language_state = translate_tab_helper->language_state(); 250 if (language_state.page_needs_translation() && 251 !language_state.translation_pending() && 252 !language_state.translation_declined() && 253 !language_state.IsPageTranslated()) { 254 std::string language = lang_det_details->adopted_language; 255 InitiateTranslation(tab, language); 256 } 257 break; 258 } 259 case chrome::NOTIFICATION_PAGE_TRANSLATED: { 260 // Only add translate infobar if it doesn't exist; if it already exists, 261 // just update the state, the actual infobar would have received the same 262 // notification and update the visual display accordingly. 263 WebContents* tab = content::Source<WebContents>(source).ptr(); 264 PageTranslatedDetails* page_translated_details = 265 content::Details<PageTranslatedDetails>(details).ptr(); 266 PageTranslated(tab, page_translated_details); 267 break; 268 } 269 default: 270 NOTREACHED(); 271 } 272 } 273 274 void TranslateManager::AddObserver(Observer* obs) { 275 observer_list_.AddObserver(obs); 276 } 277 278 void TranslateManager::RemoveObserver(Observer* obs) { 279 observer_list_.RemoveObserver(obs); 280 } 281 282 void TranslateManager::NotifyTranslateEvent( 283 const TranslateEventDetails& details) { 284 FOR_EACH_OBSERVER(Observer, observer_list_, OnTranslateEvent(details)); 285 } 286 287 void TranslateManager::NotifyLanguageDetection( 288 const LanguageDetectionDetails& details) { 289 FOR_EACH_OBSERVER(Observer, observer_list_, OnLanguageDetection(details)); 290 } 291 292 void TranslateManager::NotifyTranslateError( 293 const TranslateErrorDetails& details) { 294 FOR_EACH_OBSERVER(Observer, observer_list_, OnTranslateError(details)); 295 } 296 297 TranslateManager::TranslateManager() 298 : weak_method_factory_(this), 299 max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts) { 300 notification_registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, 301 content::NotificationService::AllSources()); 302 notification_registrar_.Add(this, 303 chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED, 304 content::NotificationService::AllSources()); 305 notification_registrar_.Add(this, chrome::NOTIFICATION_PAGE_TRANSLATED, 306 content::NotificationService::AllSources()); 307 language_list_.reset(new TranslateLanguageList); 308 accept_languages_.reset(new TranslateAcceptLanguages); 309 script_.reset(new TranslateScript); 310 } 311 312 void TranslateManager::InitiateTranslation(WebContents* web_contents, 313 const std::string& page_lang) { 314 Profile* profile = 315 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 316 Profile* original_profile = profile->GetOriginalProfile(); 317 PrefService* prefs = original_profile->GetPrefs(); 318 if (!prefs->GetBoolean(prefs::kEnableTranslate)) { 319 TranslateBrowserMetrics::ReportInitiationStatus( 320 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS); 321 const std::string& locale = g_browser_process->GetApplicationLocale(); 322 TranslateBrowserMetrics::ReportLocalesOnDisabledByPrefs(locale); 323 return; 324 } 325 326 // Allow disabling of translate from the command line to assist with 327 // automated browser testing. 328 if (CommandLine::ForCurrentProcess()->HasSwitch( 329 switches::kDisableTranslate)) { 330 TranslateBrowserMetrics::ReportInitiationStatus( 331 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_SWITCH); 332 return; 333 } 334 335 // MHTML pages currently cannot be translated. 336 // See bug: 217945. 337 if (web_contents->GetContentsMimeType() == "multipart/related") { 338 TranslateBrowserMetrics::ReportInitiationStatus( 339 TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED); 340 return; 341 } 342 343 // Don't translate any Chrome specific page, e.g., New Tab Page, Download, 344 // History, and so on. 345 GURL page_url = web_contents->GetURL(); 346 if (!IsTranslatableURL(page_url)) { 347 TranslateBrowserMetrics::ReportInitiationStatus( 348 TranslateBrowserMetrics::INITIATION_STATUS_URL_IS_NOT_SUPPORTED); 349 return; 350 } 351 352 std::string target_lang = GetTargetLanguage(prefs); 353 std::string language_code = GetLanguageCode(page_lang); 354 355 CommandLine* command_line = CommandLine::ForCurrentProcess(); 356 357 // Don't translate similar languages (ex: en-US to en). 358 if (language_code == target_lang) { 359 TranslateBrowserMetrics::ReportInitiationStatus( 360 TranslateBrowserMetrics::INITIATION_STATUS_SIMILAR_LANGUAGES); 361 return; 362 } 363 364 // Don't translate any language the user configured as accepted languages. 365 // When the flag --enable-translate-settings is on, the condition is 366 // different. In this case, even though a language is an Accept language, 367 // it could be translated due to the blacklist. 368 if (!command_line->HasSwitch(switches::kEnableTranslateSettings) && 369 accept_languages_->IsAcceptLanguage(original_profile, language_code)) { 370 TranslateBrowserMetrics::ReportInitiationStatus( 371 TranslateBrowserMetrics::INITIATION_STATUS_ACCEPT_LANGUAGES); 372 return; 373 } 374 375 // Nothing to do if either the language Chrome is in or the language of the 376 // page is not supported by the translation server. 377 if (target_lang.empty() || !IsSupportedLanguage(language_code)) { 378 TranslateBrowserMetrics::ReportInitiationStatus( 379 TranslateBrowserMetrics::INITIATION_STATUS_LANGUAGE_IS_NOT_SUPPORTED); 380 TranslateBrowserMetrics::ReportUnsupportedLanguageAtInitiation( 381 language_code); 382 return; 383 } 384 385 TranslatePrefs translate_prefs(prefs); 386 387 // Don't translate any user black-listed languages. 388 if (!TranslatePrefs::CanTranslateLanguage(profile, language_code)) { 389 TranslateBrowserMetrics::ReportInitiationStatus( 390 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG); 391 return; 392 } 393 394 // Don't translate any user black-listed URLs. 395 if (translate_prefs.IsSiteBlacklisted(page_url.HostNoBrackets())) { 396 TranslateBrowserMetrics::ReportInitiationStatus( 397 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG); 398 return; 399 } 400 401 // If the user has previously selected "always translate" for this language we 402 // automatically translate. Note that in incognito mode we disable that 403 // feature; the user will get an infobar, so they can control whether the 404 // page's text is sent to the translate server. 405 std::string auto_target_lang; 406 if (!web_contents->GetBrowserContext()->IsOffTheRecord() && 407 TranslatePrefs::ShouldAutoTranslate(prefs, language_code, 408 &auto_target_lang)) { 409 // We need to confirm that the saved target language is still supported. 410 // Also, GetLanguageCode will take care of removing country code if any. 411 auto_target_lang = GetLanguageCode(auto_target_lang); 412 if (IsSupportedLanguage(auto_target_lang)) { 413 TranslateBrowserMetrics::ReportInitiationStatus( 414 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_CONFIG); 415 TranslatePage(web_contents, language_code, auto_target_lang); 416 return; 417 } 418 } 419 420 TranslateTabHelper* translate_tab_helper = 421 TranslateTabHelper::FromWebContents(web_contents); 422 if (!translate_tab_helper) 423 return; 424 std::string auto_translate_to = 425 translate_tab_helper->language_state().AutoTranslateTo(); 426 if (!auto_translate_to.empty()) { 427 // This page was navigated through a click from a translated page. 428 TranslateBrowserMetrics::ReportInitiationStatus( 429 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_LINK); 430 TranslatePage(web_contents, language_code, auto_translate_to); 431 return; 432 } 433 434 // Prompts the user if he/she wants the page translated. 435 TranslateBrowserMetrics::ReportInitiationStatus( 436 TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR); 437 TranslateInfoBarDelegate::Create( 438 false, InfoBarService::FromWebContents(web_contents), 439 TranslateInfoBarDelegate::BEFORE_TRANSLATE, language_code, target_lang, 440 TranslateErrors::NONE, profile->GetPrefs(), ShortcutConfig()); 441 } 442 443 void TranslateManager::InitiateTranslationPosted(int process_id, 444 int render_id, 445 const std::string& page_lang, 446 int attempt) { 447 // The tab might have been closed. 448 WebContents* web_contents = 449 tab_util::GetWebContentsByID(process_id, render_id); 450 if (!web_contents) 451 return; 452 453 TranslateTabHelper* translate_tab_helper = 454 TranslateTabHelper::FromWebContents(web_contents); 455 if (translate_tab_helper->language_state().translation_pending()) 456 return; 457 458 // During a reload we need web content to be available before the 459 // translate script is executed. Otherwise we will run the translate script on 460 // an empty DOM which will fail. Therefore we wait a bit to see if the page 461 // has finished. 462 if ((web_contents->IsLoading()) && attempt < kMaxTranslateLoadCheckAttempts) { 463 int backoff = attempt * max_reload_check_attempts_; 464 base::MessageLoop::current()->PostDelayedTask( 465 FROM_HERE, base::Bind(&TranslateManager::InitiateTranslationPosted, 466 weak_method_factory_.GetWeakPtr(), process_id, 467 render_id, page_lang, ++attempt), 468 base::TimeDelta::FromMilliseconds(backoff)); 469 return; 470 } 471 472 InitiateTranslation(web_contents, GetLanguageCode(page_lang)); 473 } 474 475 void TranslateManager::TranslatePage(WebContents* web_contents, 476 const std::string& original_source_lang, 477 const std::string& target_lang) { 478 NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); 479 if (!entry) { 480 NOTREACHED(); 481 return; 482 } 483 484 // Translation can be kicked by context menu against unsupported languages. 485 // Unsupported language strings should be replaced with 486 // kUnknownLanguageCode in order to send a translation request with enabling 487 // server side auto language detection. 488 std::string source_lang(original_source_lang); 489 if (!IsSupportedLanguage(source_lang)) 490 source_lang = std::string(chrome::kUnknownLanguageCode); 491 492 Profile* profile = 493 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 494 TranslateInfoBarDelegate::Create( 495 true, InfoBarService::FromWebContents(web_contents), 496 TranslateInfoBarDelegate::TRANSLATING, source_lang, target_lang, 497 TranslateErrors::NONE, profile->GetPrefs(), ShortcutConfig()); 498 499 DCHECK(script_.get() != NULL); 500 501 const std::string& translate_script = script_->data(); 502 if (!translate_script.empty()) { 503 DoTranslatePage(web_contents, translate_script, source_lang, target_lang); 504 return; 505 } 506 507 // The script is not available yet. Queue that request and query for the 508 // script. Once it is downloaded we'll do the translate. 509 content::RenderViewHost* rvh = web_contents->GetRenderViewHost(); 510 PendingRequest request; 511 request.render_process_id = rvh->GetProcess()->GetID(); 512 request.render_view_id = rvh->GetRoutingID(); 513 request.page_id = entry->GetPageID(); 514 request.source_lang = source_lang; 515 request.target_lang = target_lang; 516 pending_requests_.push_back(request); 517 518 if (script_->HasPendingRequest()) 519 return; 520 521 script_->Request( 522 base::Bind(&TranslateManager::OnTranslateScriptFetchComplete, 523 base::Unretained(this))); 524 } 525 526 void TranslateManager::RevertTranslation(WebContents* web_contents) { 527 NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); 528 if (!entry) { 529 NOTREACHED(); 530 return; 531 } 532 web_contents->GetRenderViewHost()->Send(new ChromeViewMsg_RevertTranslation( 533 web_contents->GetRenderViewHost()->GetRoutingID(), entry->GetPageID())); 534 535 TranslateTabHelper* translate_tab_helper = 536 TranslateTabHelper::FromWebContents(web_contents); 537 translate_tab_helper->language_state().set_current_language( 538 translate_tab_helper->language_state().original_language()); 539 } 540 541 void TranslateManager::ReportLanguageDetectionError(WebContents* web_contents) { 542 TranslateBrowserMetrics::ReportLanguageDetectionError(); 543 // We'll open the URL in a new tab so that the user can tell us more. 544 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); 545 if (!browser) { 546 NOTREACHED(); 547 return; 548 } 549 550 GURL report_error_url = GURL(kReportLanguageDetectionErrorURL); 551 552 GURL page_url = web_contents->GetController().GetActiveEntry()->GetURL(); 553 report_error_url = net::AppendQueryParameter( 554 report_error_url, 555 kUrlQueryName, 556 page_url.spec()); 557 558 TranslateTabHelper* translate_tab_helper = 559 TranslateTabHelper::FromWebContents(web_contents); 560 report_error_url = net::AppendQueryParameter( 561 report_error_url, 562 kSourceLanguageQueryName, 563 translate_tab_helper->language_state().original_language()); 564 565 report_error_url = TranslateURLUtil::AddHostLocaleToUrl(report_error_url); 566 report_error_url = TranslateURLUtil::AddApiKeyToUrl(report_error_url); 567 568 chrome::AddSelectedTabWithURL(browser, report_error_url, 569 content::PAGE_TRANSITION_AUTO_BOOKMARK); 570 } 571 572 void TranslateManager::ClearTranslateScript() { 573 if (script_.get() == NULL) { 574 NOTREACHED(); 575 return; 576 } 577 script_->Clear(); 578 } 579 580 void TranslateManager::DoTranslatePage(WebContents* web_contents, 581 const std::string& translate_script, 582 const std::string& source_lang, 583 const std::string& target_lang) { 584 NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); 585 if (!entry) { 586 NOTREACHED(); 587 return; 588 } 589 590 TranslateTabHelper* translate_tab_helper = 591 TranslateTabHelper::FromWebContents(web_contents); 592 if (!translate_tab_helper) 593 return; 594 595 translate_tab_helper->language_state().set_translation_pending(true); 596 web_contents->GetRenderViewHost()->Send(new ChromeViewMsg_TranslatePage( 597 web_contents->GetRenderViewHost()->GetRoutingID(), entry->GetPageID(), 598 translate_script, source_lang, target_lang)); 599 } 600 601 void TranslateManager::PageTranslated(WebContents* web_contents, 602 PageTranslatedDetails* details) { 603 if ((details->error_type == TranslateErrors::NONE) && 604 details->source_language != chrome::kUnknownLanguageCode && 605 !IsSupportedLanguage(details->source_language)) { 606 details->error_type = TranslateErrors::UNSUPPORTED_LANGUAGE; 607 } 608 609 PrefService* prefs = Profile::FromBrowserContext( 610 web_contents->GetBrowserContext())->GetPrefs(); 611 TranslateInfoBarDelegate::Create( 612 true, InfoBarService::FromWebContents(web_contents), 613 (details->error_type == TranslateErrors::NONE) ? 614 TranslateInfoBarDelegate::AFTER_TRANSLATE : 615 TranslateInfoBarDelegate::TRANSLATION_ERROR, 616 details->source_language, details->target_language, details->error_type, 617 prefs, ShortcutConfig()); 618 619 if (details->error_type != TranslateErrors::NONE && 620 !web_contents->GetBrowserContext()->IsOffTheRecord()) { 621 TranslateErrorDetails error_details; 622 error_details.time = base::Time::Now(); 623 error_details.url = web_contents->GetLastCommittedURL(); 624 error_details.error = details->error_type; 625 NotifyTranslateError(error_details); 626 } 627 } 628 629 void TranslateManager::FetchLanguageListFromTranslateServer( 630 PrefService* prefs) { 631 // We don't want to do this when translate is disabled. 632 DCHECK(prefs != NULL); 633 if (CommandLine::ForCurrentProcess()->HasSwitch( 634 switches::kDisableTranslate) || 635 (prefs != NULL && !prefs->GetBoolean(prefs::kEnableTranslate))) { 636 return; 637 } 638 639 if (language_list_.get()) 640 language_list_->RequestLanguageList(); 641 else 642 NOTREACHED(); 643 } 644 645 void TranslateManager::CleanupPendingUlrFetcher() { 646 language_list_.reset(); 647 script_.reset(); 648 } 649 650 void TranslateManager::OnTranslateScriptFetchComplete( 651 bool success, const std::string& data) { 652 std::vector<PendingRequest>::const_iterator iter; 653 for (iter = pending_requests_.begin(); iter != pending_requests_.end(); 654 ++iter) { 655 const PendingRequest& request = *iter; 656 WebContents* web_contents = 657 tab_util::GetWebContentsByID(request.render_process_id, 658 request.render_view_id); 659 if (!web_contents) { 660 // The tab went away while we were retrieving the script. 661 continue; 662 } 663 NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); 664 if (!entry || entry->GetPageID() != request.page_id) { 665 // We navigated away from the page the translation was triggered on. 666 continue; 667 } 668 669 if (success) { 670 // Translate the page. 671 const std::string& translate_script = script_->data(); 672 DoTranslatePage(web_contents, translate_script, 673 request.source_lang, request.target_lang); 674 } else { 675 Profile* profile = 676 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 677 TranslateInfoBarDelegate::Create( 678 true, InfoBarService::FromWebContents(web_contents), 679 TranslateInfoBarDelegate::TRANSLATION_ERROR, request.source_lang, 680 request.target_lang, TranslateErrors::NETWORK, profile->GetPrefs(), 681 ShortcutConfig()); 682 683 if (!web_contents->GetBrowserContext()->IsOffTheRecord()) { 684 TranslateErrorDetails error_details; 685 error_details.time = base::Time::Now(); 686 error_details.url = entry->GetURL(); 687 error_details.error = TranslateErrors::NETWORK; 688 NotifyTranslateError(error_details); 689 } 690 } 691 } 692 pending_requests_.clear(); 693 } 694 695 // static 696 std::string TranslateManager::GetTargetLanguage(PrefService* prefs) { 697 std::string ui_lang = 698 GetLanguageCode(g_browser_process->GetApplicationLocale()); 699 if (IsSupportedLanguage(ui_lang)) 700 return ui_lang; 701 702 // Getting the accepted languages list 703 std::string accept_langs_str = prefs->GetString(prefs::kAcceptLanguages); 704 705 std::vector<std::string> accept_langs_list; 706 base::SplitString(accept_langs_str, ',', &accept_langs_list); 707 708 // Will translate to the first supported language on the Accepted Language 709 // list or not at all if no such candidate exists 710 std::vector<std::string>::iterator iter; 711 for (iter = accept_langs_list.begin(); 712 iter != accept_langs_list.end(); ++iter) { 713 std::string lang_code = GetLanguageCode(*iter); 714 if (IsSupportedLanguage(lang_code)) 715 return lang_code; 716 } 717 return std::string(); 718 } 719 720 // static 721 ShortcutConfiguration TranslateManager::ShortcutConfig() { 722 ShortcutConfiguration config; 723 724 // The android implementation does not offer a drop down (for space reasons), 725 // so we are more aggressive about showing the shortcut to never translate. 726 #if defined(OS_ANDROID) 727 config.never_translate_min_count = 1; 728 #else 729 config.never_translate_min_count = 3; 730 #endif // defined(OS_ANDROID) 731 732 config.always_translate_min_count = 3; 733 return config; 734 } 735