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.
5 #include "chrome/browser/translate/translate_manager.h"
8 #include "base/command_line.h"
9 #include "base/memory/singleton.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/infobars/infobar_service.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/tab_contents/language_state.h"
21 #include "chrome/browser/tab_contents/tab_util.h"
22 #include "chrome/browser/translate/page_translated_details.h"
23 #include "chrome/browser/translate/translate_accept_languages.h"
24 #include "chrome/browser/translate/translate_browser_metrics.h"
25 #include "chrome/browser/translate/translate_error_details.h"
26 #include "chrome/browser/translate/translate_event_details.h"
27 #include "chrome/browser/translate/translate_infobar_delegate.h"
28 #include "chrome/browser/translate/translate_language_list.h"
29 #include "chrome/browser/translate/translate_prefs.h"
30 #include "chrome/browser/translate/translate_script.h"
31 #include "chrome/browser/translate/translate_tab_helper.h"
32 #include "chrome/browser/translate/translate_url_util.h"
33 #include "chrome/browser/ui/browser.h"
34 #include "chrome/browser/ui/browser_finder.h"
35 #include "chrome/browser/ui/browser_tabstrip.h"
36 #include "chrome/browser/ui/browser_window.h"
37 #include "chrome/browser/ui/tabs/tab_strip_model.h"
38 #include "chrome/browser/ui/translate/translate_bubble_factory.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/pref_names.h"
41 #include "chrome/common/render_messages.h"
42 #include "chrome/common/translate/language_detection_details.h"
43 #include "chrome/common/url_constants.h"
44 #include "components/translate/common/translate_constants.h"
45 #include "content/public/browser/navigation_controller.h"
46 #include "content/public/browser/navigation_details.h"
47 #include "content/public/browser/navigation_entry.h"
48 #include "content/public/browser/notification_details.h"
49 #include "content/public/browser/notification_service.h"
50 #include "content/public/browser/notification_source.h"
51 #include "content/public/browser/notification_types.h"
52 #include "content/public/browser/render_process_host.h"
53 #include "content/public/browser/render_view_host.h"
54 #include "content/public/browser/web_contents.h"
55 #include "net/base/url_util.h"
56 #include "net/http/http_status_code.h"
58 #ifdef FILE_MANAGER_EXTENSION
59 #include "chrome/browser/chromeos/file_manager/app_id.h"
60 #include "extensions/common/constants.h"
63 using content::NavigationController;
64 using content::NavigationEntry;
65 using content::WebContents;
69 const char kReportLanguageDetectionErrorURL[] =
70 "https://translate.google.com/translate_error?client=cr&action=langidc";
72 // Used in kReportLanguageDetectionErrorURL to specify the original page
74 const char kSourceLanguageQueryName[] = "sl";
76 // Used in kReportLanguageDetectionErrorURL to specify the page URL.
77 const char kUrlQueryName[] = "u";
79 // The maximum number of attempts we'll do to see if the page has finshed
80 // loading before giving up the translation
81 const int kMaxTranslateLoadCheckAttempts = 20;
83 // The field trial name to compare Translate infobar and bubble.
84 const char kFieldTrialNameForUX[] = "TranslateInfobarVsBubble";
86 bool IsEnabledTranslateNewUX() {
87 if (CommandLine::ForCurrentProcess()->HasSwitch(
88 switches::kEnableTranslateNewUX)) {
92 std::string group_name = base::FieldTrialList::FindFullName(
93 kFieldTrialNameForUX);
94 return group_name == "Bubble";
99 TranslateManager::~TranslateManager() {
103 TranslateManager* TranslateManager::GetInstance() {
104 return Singleton<TranslateManager>::get();
108 bool TranslateManager::IsTranslatableURL(const GURL& url) {
109 // A URLs is translatable unless it is one of the following:
110 // - empty (can happen for popups created with window.open(""))
111 // - an internal URL (chrome:// and others)
112 // - the devtools (which is considered UI)
113 // - Chrome OS file manager extension
114 // - an FTP page (as FTP pages tend to have long lists of filenames that may
116 return !url.is_empty() &&
117 !url.SchemeIs(chrome::kChromeUIScheme) &&
118 !url.SchemeIs(chrome::kChromeDevToolsScheme) &&
119 #ifdef FILE_MANAGER_EXTENSION
120 !(url.SchemeIs(extensions::kExtensionScheme) &&
121 url.DomainIs(file_manager::kFileManagerAppId)) &&
123 !url.SchemeIs(chrome::kFtpScheme);
127 void TranslateManager::GetSupportedLanguages(
128 std::vector<std::string>* languages) {
129 if (GetInstance()->language_list_.get()) {
130 GetInstance()->language_list_->GetSupportedLanguages(languages);
137 base::Time TranslateManager::GetSupportedLanguagesLastUpdated() {
138 if (GetInstance()->language_list_.get()) {
139 return GetInstance()->language_list_->last_updated();
146 std::string TranslateManager::GetLanguageCode(
147 const std::string& chrome_locale) {
148 if (GetInstance()->language_list_.get())
149 return GetInstance()->language_list_->GetLanguageCode(chrome_locale);
151 return chrome_locale;
155 bool TranslateManager::IsSupportedLanguage(const std::string& language) {
156 if (GetInstance()->language_list_.get())
157 return GetInstance()->language_list_->IsSupportedLanguage(language);
163 bool TranslateManager::IsAlphaLanguage(const std::string& language) {
164 if (GetInstance()->language_list_.get())
165 return GetInstance()->language_list_->IsAlphaLanguage(language);
171 bool TranslateManager::IsAcceptLanguage(Profile* profile,
172 const std::string& language) {
173 if (GetInstance()->accept_languages_.get()) {
174 return GetInstance()->accept_languages_->IsAcceptLanguage(
181 void TranslateManager::SetTranslateScriptExpirationDelay(int delay_ms) {
182 if (script_.get() == NULL) {
186 script_->set_expiration_delay(delay_ms);
189 void TranslateManager::Observe(int type,
190 const content::NotificationSource& source,
191 const content::NotificationDetails& details) {
193 case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
194 NavigationController* controller =
195 content::Source<NavigationController>(source).ptr();
196 content::LoadCommittedDetails* load_details =
197 content::Details<content::LoadCommittedDetails>(details).ptr();
198 NavigationEntry* entry = controller->GetActiveEntry();
204 TranslateTabHelper* translate_tab_helper =
205 TranslateTabHelper::FromWebContents(controller->GetWebContents());
206 if (!translate_tab_helper)
209 // If the navigation happened while offline don't show the translate
210 // bar since there will be nothing to translate.
211 if (load_details->http_status_code == 0 ||
212 load_details->http_status_code == net::HTTP_INTERNAL_SERVER_ERROR) {
216 if (!load_details->is_main_frame &&
217 translate_tab_helper->language_state().translation_declined()) {
218 // Some sites (such as Google map) may trigger sub-frame navigations
219 // when the user interacts with the page. We don't want to show a new
220 // infobar if the user already dismissed one in that case.
223 if (entry->GetTransitionType() != content::PAGE_TRANSITION_RELOAD &&
224 load_details->type != content::NAVIGATION_TYPE_SAME_PAGE) {
228 // When doing a page reload, TAB_LANGUAGE_DETERMINED is not sent,
229 // so the translation needs to be explicitly initiated, but only when the
230 // page needs translation.
231 if (!translate_tab_helper->language_state().page_needs_translation())
233 // Note that we delay it as the TranslateManager gets this notification
234 // before the WebContents and the WebContents processing might remove the
235 // current infobars. Since InitTranslation might add an infobar, it must
236 // be done after that.
237 base::MessageLoop::current()->PostTask(FROM_HERE,
239 &TranslateManager::InitiateTranslationPosted,
240 weak_method_factory_.GetWeakPtr(),
241 controller->GetWebContents()->GetRenderProcessHost()->GetID(),
242 controller->GetWebContents()->GetRenderViewHost()->GetRoutingID(),
243 translate_tab_helper->language_state().original_language(), 0));
246 case chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED: {
247 const LanguageDetectionDetails* lang_det_details =
248 content::Details<const LanguageDetectionDetails>(details).ptr();
250 WebContents* tab = content::Source<WebContents>(source).ptr();
251 if (!tab->GetBrowserContext()->IsOffTheRecord())
252 NotifyLanguageDetection(*lang_det_details);
254 // We may get this notifications multiple times. Make sure to translate
256 TranslateTabHelper* translate_tab_helper =
257 TranslateTabHelper::FromWebContents(tab);
258 if (!translate_tab_helper)
261 LanguageState& language_state = translate_tab_helper->language_state();
262 if (language_state.page_needs_translation() &&
263 !language_state.translation_pending() &&
264 !language_state.translation_declined() &&
265 !language_state.IsPageTranslated()) {
266 std::string language = lang_det_details->adopted_language;
267 InitiateTranslation(tab, language);
271 case chrome::NOTIFICATION_PAGE_TRANSLATED: {
272 // Only add translate infobar if it doesn't exist; if it already exists,
273 // just update the state, the actual infobar would have received the same
274 // notification and update the visual display accordingly.
275 WebContents* tab = content::Source<WebContents>(source).ptr();
276 PageTranslatedDetails* page_translated_details =
277 content::Details<PageTranslatedDetails>(details).ptr();
278 PageTranslated(tab, page_translated_details);
286 void TranslateManager::AddObserver(Observer* obs) {
287 observer_list_.AddObserver(obs);
290 void TranslateManager::RemoveObserver(Observer* obs) {
291 observer_list_.RemoveObserver(obs);
294 void TranslateManager::NotifyTranslateEvent(
295 const TranslateEventDetails& details) {
296 FOR_EACH_OBSERVER(Observer, observer_list_, OnTranslateEvent(details));
299 void TranslateManager::NotifyLanguageDetection(
300 const LanguageDetectionDetails& details) {
301 FOR_EACH_OBSERVER(Observer, observer_list_, OnLanguageDetection(details));
304 void TranslateManager::NotifyTranslateError(
305 const TranslateErrorDetails& details) {
306 FOR_EACH_OBSERVER(Observer, observer_list_, OnTranslateError(details));
309 TranslateManager::TranslateManager()
310 : max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts),
311 weak_method_factory_(this) {
312 notification_registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
313 content::NotificationService::AllSources());
314 notification_registrar_.Add(this,
315 chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
316 content::NotificationService::AllSources());
317 notification_registrar_.Add(this, chrome::NOTIFICATION_PAGE_TRANSLATED,
318 content::NotificationService::AllSources());
319 language_list_.reset(new TranslateLanguageList);
320 accept_languages_.reset(new TranslateAcceptLanguages);
321 script_.reset(new TranslateScript);
324 void TranslateManager::InitiateTranslation(WebContents* web_contents,
325 const std::string& page_lang) {
326 TranslateTabHelper* translate_tab_helper =
327 TranslateTabHelper::FromWebContents(web_contents);
328 if (!translate_tab_helper)
332 Profile::FromBrowserContext(web_contents->GetBrowserContext());
333 Profile* original_profile = profile->GetOriginalProfile();
334 PrefService* prefs = original_profile->GetPrefs();
335 if (!prefs->GetBoolean(prefs::kEnableTranslate)) {
336 TranslateBrowserMetrics::ReportInitiationStatus(
337 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS);
338 const std::string& locale = g_browser_process->GetApplicationLocale();
339 TranslateBrowserMetrics::ReportLocalesOnDisabledByPrefs(locale);
343 // Allow disabling of translate from the command line to assist with
344 // automated browser testing.
345 if (CommandLine::ForCurrentProcess()->HasSwitch(
346 switches::kDisableTranslate)) {
347 TranslateBrowserMetrics::ReportInitiationStatus(
348 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_SWITCH);
352 // MHTML pages currently cannot be translated.
354 if (web_contents->GetContentsMimeType() == "multipart/related") {
355 TranslateBrowserMetrics::ReportInitiationStatus(
356 TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED);
360 // Don't translate any Chrome specific page, e.g., New Tab Page, Download,
361 // History, and so on.
362 GURL page_url = web_contents->GetURL();
363 if (!IsTranslatableURL(page_url)) {
364 TranslateBrowserMetrics::ReportInitiationStatus(
365 TranslateBrowserMetrics::INITIATION_STATUS_URL_IS_NOT_SUPPORTED);
369 std::string target_lang = GetTargetLanguage(prefs);
370 std::string language_code = GetLanguageCode(page_lang);
372 // Don't translate similar languages (ex: en-US to en).
373 if (language_code == target_lang) {
374 TranslateBrowserMetrics::ReportInitiationStatus(
375 TranslateBrowserMetrics::INITIATION_STATUS_SIMILAR_LANGUAGES);
379 // Nothing to do if either the language Chrome is in or the language of the
380 // page is not supported by the translation server.
381 if (target_lang.empty() || !IsSupportedLanguage(language_code)) {
382 TranslateBrowserMetrics::ReportInitiationStatus(
383 TranslateBrowserMetrics::INITIATION_STATUS_LANGUAGE_IS_NOT_SUPPORTED);
384 TranslateBrowserMetrics::ReportUnsupportedLanguageAtInitiation(
389 TranslatePrefs translate_prefs(prefs);
391 // Don't translate any user black-listed languages.
392 if (!TranslatePrefs::CanTranslateLanguage(profile, language_code)) {
393 TranslateBrowserMetrics::ReportInitiationStatus(
394 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
398 // Don't translate any user black-listed URLs.
399 if (translate_prefs.IsSiteBlacklisted(page_url.HostNoBrackets())) {
400 TranslateBrowserMetrics::ReportInitiationStatus(
401 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
405 // If the user has previously selected "always translate" for this language we
406 // automatically translate. Note that in incognito mode we disable that
407 // feature; the user will get an infobar, so they can control whether the
408 // page's text is sent to the translate server.
409 std::string auto_target_lang;
410 if (!web_contents->GetBrowserContext()->IsOffTheRecord() &&
411 TranslatePrefs::ShouldAutoTranslate(prefs, language_code,
412 &auto_target_lang)) {
413 // We need to confirm that the saved target language is still supported.
414 // Also, GetLanguageCode will take care of removing country code if any.
415 auto_target_lang = GetLanguageCode(auto_target_lang);
416 if (IsSupportedLanguage(auto_target_lang)) {
417 TranslateBrowserMetrics::ReportInitiationStatus(
418 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_CONFIG);
419 TranslatePage(web_contents, language_code, auto_target_lang);
424 LanguageState& language_state = translate_tab_helper->language_state();
425 std::string auto_translate_to = 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);
434 TranslateBrowserMetrics::ReportInitiationStatus(
435 TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR);
437 if (IsEnabledTranslateNewUX()) {
438 language_state.SetTranslateEnabled(true);
439 if (language_state.HasLanguageChanged())
440 ShowBubble(web_contents,
441 TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE);
443 // Prompts the user if he/she wants the page translated.
444 TranslateInfoBarDelegate::Create(
445 false, InfoBarService::FromWebContents(web_contents),
446 TranslateInfoBarDelegate::BEFORE_TRANSLATE, language_code, target_lang,
447 TranslateErrors::NONE, profile->GetPrefs(), ShortcutConfig());
451 void TranslateManager::InitiateTranslationPosted(int process_id,
453 const std::string& page_lang,
455 // The tab might have been closed.
456 WebContents* web_contents =
457 tab_util::GetWebContentsByID(process_id, render_id);
461 TranslateTabHelper* translate_tab_helper =
462 TranslateTabHelper::FromWebContents(web_contents);
463 if (translate_tab_helper->language_state().translation_pending())
466 // During a reload we need web content to be available before the
467 // translate script is executed. Otherwise we will run the translate script on
468 // an empty DOM which will fail. Therefore we wait a bit to see if the page
470 if ((web_contents->IsLoading()) && attempt < kMaxTranslateLoadCheckAttempts) {
471 int backoff = attempt * max_reload_check_attempts_;
472 base::MessageLoop::current()->PostDelayedTask(
473 FROM_HERE, base::Bind(&TranslateManager::InitiateTranslationPosted,
474 weak_method_factory_.GetWeakPtr(), process_id,
475 render_id, page_lang, ++attempt),
476 base::TimeDelta::FromMilliseconds(backoff));
480 InitiateTranslation(web_contents, GetLanguageCode(page_lang));
483 void TranslateManager::TranslatePage(WebContents* web_contents,
484 const std::string& original_source_lang,
485 const std::string& target_lang) {
486 NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
492 // Translation can be kicked by context menu against unsupported languages.
493 // Unsupported language strings should be replaced with
494 // kUnknownLanguageCode in order to send a translation request with enabling
495 // server side auto language detection.
496 std::string source_lang(original_source_lang);
497 if (!IsSupportedLanguage(source_lang))
498 source_lang = std::string(translate::kUnknownLanguageCode);
500 if (IsEnabledTranslateNewUX()) {
501 ShowBubble(web_contents, TranslateBubbleModel::VIEW_STATE_TRANSLATING);
504 Profile::FromBrowserContext(web_contents->GetBrowserContext());
505 TranslateInfoBarDelegate::Create(
506 true, InfoBarService::FromWebContents(web_contents),
507 TranslateInfoBarDelegate::TRANSLATING, source_lang, target_lang,
508 TranslateErrors::NONE, profile->GetPrefs(), ShortcutConfig());
511 DCHECK(script_.get() != NULL);
513 const std::string& translate_script = script_->data();
514 if (!translate_script.empty()) {
515 DoTranslatePage(web_contents, translate_script, source_lang, target_lang);
519 // The script is not available yet. Queue that request and query for the
520 // script. Once it is downloaded we'll do the translate.
521 content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
522 PendingRequest request;
523 request.render_process_id = rvh->GetProcess()->GetID();
524 request.render_view_id = rvh->GetRoutingID();
525 request.page_id = entry->GetPageID();
526 request.source_lang = source_lang;
527 request.target_lang = target_lang;
528 pending_requests_.push_back(request);
530 if (script_->HasPendingRequest())
534 base::Bind(&TranslateManager::OnTranslateScriptFetchComplete,
535 base::Unretained(this)));
538 void TranslateManager::RevertTranslation(WebContents* web_contents) {
539 NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
544 web_contents->GetRenderViewHost()->Send(new ChromeViewMsg_RevertTranslation(
545 web_contents->GetRenderViewHost()->GetRoutingID(), entry->GetPageID()));
547 TranslateTabHelper* translate_tab_helper =
548 TranslateTabHelper::FromWebContents(web_contents);
549 translate_tab_helper->language_state().set_current_language(
550 translate_tab_helper->language_state().original_language());
553 void TranslateManager::ReportLanguageDetectionError(WebContents* web_contents) {
554 TranslateBrowserMetrics::ReportLanguageDetectionError();
555 // We'll open the URL in a new tab so that the user can tell us more.
556 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
562 GURL report_error_url = GURL(kReportLanguageDetectionErrorURL);
564 GURL page_url = web_contents->GetController().GetActiveEntry()->GetURL();
565 report_error_url = net::AppendQueryParameter(
570 TranslateTabHelper* translate_tab_helper =
571 TranslateTabHelper::FromWebContents(web_contents);
572 report_error_url = net::AppendQueryParameter(
574 kSourceLanguageQueryName,
575 translate_tab_helper->language_state().original_language());
577 report_error_url = TranslateURLUtil::AddHostLocaleToUrl(report_error_url);
578 report_error_url = TranslateURLUtil::AddApiKeyToUrl(report_error_url);
580 chrome::AddSelectedTabWithURL(browser, report_error_url,
581 content::PAGE_TRANSITION_AUTO_BOOKMARK);
584 void TranslateManager::ClearTranslateScript() {
585 if (script_.get() == NULL) {
592 void TranslateManager::DoTranslatePage(WebContents* web_contents,
593 const std::string& translate_script,
594 const std::string& source_lang,
595 const std::string& target_lang) {
596 NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
602 TranslateTabHelper* translate_tab_helper =
603 TranslateTabHelper::FromWebContents(web_contents);
604 if (!translate_tab_helper)
607 translate_tab_helper->language_state().set_translation_pending(true);
608 web_contents->GetRenderViewHost()->Send(new ChromeViewMsg_TranslatePage(
609 web_contents->GetRenderViewHost()->GetRoutingID(), entry->GetPageID(),
610 translate_script, source_lang, target_lang));
613 void TranslateManager::PageTranslated(WebContents* web_contents,
614 PageTranslatedDetails* details) {
615 if ((details->error_type == TranslateErrors::NONE) &&
616 details->source_language != translate::kUnknownLanguageCode &&
617 !IsSupportedLanguage(details->source_language)) {
618 details->error_type = TranslateErrors::UNSUPPORTED_LANGUAGE;
621 if (IsEnabledTranslateNewUX()) {
622 TranslateBubbleModel::ViewState view_state =
623 (details->error_type == TranslateErrors::NONE) ?
624 TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE :
625 TranslateBubbleModel::VIEW_STATE_ERROR;
626 ShowBubble(web_contents, view_state);
628 PrefService* prefs = Profile::FromBrowserContext(
629 web_contents->GetBrowserContext())->GetPrefs();
630 TranslateInfoBarDelegate::Create(
631 true, InfoBarService::FromWebContents(web_contents),
632 (details->error_type == TranslateErrors::NONE) ?
633 TranslateInfoBarDelegate::AFTER_TRANSLATE :
634 TranslateInfoBarDelegate::TRANSLATION_ERROR,
635 details->source_language, details->target_language, details->error_type,
636 prefs, ShortcutConfig());
639 if (details->error_type != TranslateErrors::NONE &&
640 !web_contents->GetBrowserContext()->IsOffTheRecord()) {
641 TranslateErrorDetails error_details;
642 error_details.time = base::Time::Now();
643 error_details.url = web_contents->GetLastCommittedURL();
644 error_details.error = details->error_type;
645 NotifyTranslateError(error_details);
649 void TranslateManager::FetchLanguageListFromTranslateServer(
650 PrefService* prefs) {
651 // We don't want to do this when translate is disabled.
652 DCHECK(prefs != NULL);
653 if (CommandLine::ForCurrentProcess()->HasSwitch(
654 switches::kDisableTranslate) ||
655 (prefs != NULL && !prefs->GetBoolean(prefs::kEnableTranslate))) {
659 if (language_list_.get())
660 language_list_->RequestLanguageList();
665 void TranslateManager::CleanupPendingUlrFetcher() {
666 language_list_.reset();
670 void TranslateManager::OnTranslateScriptFetchComplete(
671 bool success, const std::string& data) {
672 std::vector<PendingRequest>::const_iterator iter;
673 for (iter = pending_requests_.begin(); iter != pending_requests_.end();
675 const PendingRequest& request = *iter;
676 WebContents* web_contents =
677 tab_util::GetWebContentsByID(request.render_process_id,
678 request.render_view_id);
680 // The tab went away while we were retrieving the script.
683 NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
684 if (!entry || entry->GetPageID() != request.page_id) {
685 // We navigated away from the page the translation was triggered on.
690 // Translate the page.
691 const std::string& translate_script = script_->data();
692 DoTranslatePage(web_contents, translate_script,
693 request.source_lang, request.target_lang);
695 if (IsEnabledTranslateNewUX()) {
696 ShowBubble(web_contents, TranslateBubbleModel::VIEW_STATE_ERROR);
699 Profile::FromBrowserContext(web_contents->GetBrowserContext());
700 TranslateInfoBarDelegate::Create(
701 true, InfoBarService::FromWebContents(web_contents),
702 TranslateInfoBarDelegate::TRANSLATION_ERROR, request.source_lang,
703 request.target_lang, TranslateErrors::NETWORK, profile->GetPrefs(),
707 if (!web_contents->GetBrowserContext()->IsOffTheRecord()) {
708 TranslateErrorDetails error_details;
709 error_details.time = base::Time::Now();
710 error_details.url = entry->GetURL();
711 error_details.error = TranslateErrors::NETWORK;
712 NotifyTranslateError(error_details);
716 pending_requests_.clear();
719 void TranslateManager::ShowBubble(WebContents* web_contents,
720 TranslateBubbleModel::ViewState view_state) {
721 // The bubble is implemented only on the desktop platforms.
722 #if !defined(OS_ANDROID) && !defined(OS_IOS)
723 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
724 BrowserWindow* window = browser ? browser->window() : NULL;
725 TranslateBubbleFactory::Show(window, web_contents, view_state);
732 std::string TranslateManager::GetTargetLanguage(PrefService* prefs) {
733 std::string ui_lang =
734 GetLanguageCode(g_browser_process->GetApplicationLocale());
735 if (IsSupportedLanguage(ui_lang))
738 // Getting the accepted languages list
739 std::string accept_langs_str = prefs->GetString(prefs::kAcceptLanguages);
741 std::vector<std::string> accept_langs_list;
742 base::SplitString(accept_langs_str, ',', &accept_langs_list);
744 // Will translate to the first supported language on the Accepted Language
745 // list or not at all if no such candidate exists
746 std::vector<std::string>::iterator iter;
747 for (iter = accept_langs_list.begin();
748 iter != accept_langs_list.end(); ++iter) {
749 std::string lang_code = GetLanguageCode(*iter);
750 if (IsSupportedLanguage(lang_code))
753 return std::string();
757 ShortcutConfiguration TranslateManager::ShortcutConfig() {
758 ShortcutConfiguration config;
760 // The android implementation does not offer a drop down (for space reasons),
761 // so we are more aggressive about showing the shortcut to never translate.
762 #if defined(OS_ANDROID)
763 config.never_translate_min_count = 1;
765 config.never_translate_min_count = 3;
766 #endif // defined(OS_ANDROID)
768 config.always_translate_min_count = 3;