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/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/translate_accept_languages.h"
22 #include "chrome/browser/translate/translate_infobar_delegate.h"
23 #include "chrome/browser/translate/translate_language_list.h"
24 #include "chrome/browser/translate/translate_prefs.h"
25 #include "chrome/browser/translate/translate_script.h"
26 #include "chrome/browser/translate/translate_tab_helper.h"
27 #include "chrome/browser/translate/translate_url_util.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/browser_tabstrip.h"
31 #include "chrome/browser/ui/browser_window.h"
32 #include "chrome/browser/ui/tabs/tab_strip_model.h"
33 #include "chrome/browser/ui/translate/translate_bubble_factory.h"
34 #include "chrome/common/chrome_switches.h"
35 #include "chrome/common/pref_names.h"
36 #include "chrome/common/render_messages.h"
37 #include "chrome/common/url_constants.h"
38 #include "components/translate/core/browser/page_translated_details.h"
39 #include "components/translate/core/browser/translate_browser_metrics.h"
40 #include "components/translate/core/browser/translate_error_details.h"
41 #include "components/translate/core/browser/translate_event_details.h"
42 #include "components/translate/core/common/language_detection_details.h"
43 #include "components/translate/core/common/translate_constants.h"
44 #include "content/public/browser/navigation_controller.h"
45 #include "content/public/browser/navigation_details.h"
46 #include "content/public/browser/navigation_entry.h"
47 #include "content/public/browser/notification_details.h"
48 #include "content/public/browser/notification_service.h"
49 #include "content/public/browser/notification_source.h"
50 #include "content/public/browser/notification_types.h"
51 #include "content/public/browser/render_process_host.h"
52 #include "content/public/browser/render_view_host.h"
53 #include "content/public/browser/web_contents.h"
54 #include "net/base/url_util.h"
55 #include "net/http/http_status_code.h"
57 #ifdef FILE_MANAGER_EXTENSION
58 #include "chrome/browser/chromeos/file_manager/app_id.h"
59 #include "extensions/common/constants.h"
62 using content::NavigationController
;
63 using content::NavigationEntry
;
64 using content::WebContents
;
68 const char kReportLanguageDetectionErrorURL
[] =
69 "https://translate.google.com/translate_error?client=cr&action=langidc";
71 // Used in kReportLanguageDetectionErrorURL to specify the original page
73 const char kSourceLanguageQueryName
[] = "sl";
75 // Used in kReportLanguageDetectionErrorURL to specify the page URL.
76 const char kUrlQueryName
[] = "u";
78 // The maximum number of attempts we'll do to see if the page has finshed
79 // loading before giving up the translation
80 const int kMaxTranslateLoadCheckAttempts
= 20;
82 // The field trial name to compare Translate infobar and bubble.
83 const char kFieldTrialNameForUX
[] = "TranslateInfobarVsBubble";
87 TranslateManager::~TranslateManager() {
91 TranslateManager
* TranslateManager::GetInstance() {
92 return Singleton
<TranslateManager
>::get();
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
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(file_manager::kFileManagerAppId
)) &&
111 !url
.SchemeIs(content::kFtpScheme
);
115 void TranslateManager::GetSupportedLanguages(
116 std::vector
<std::string
>* languages
) {
117 if (GetInstance()->language_list_
.get()) {
118 GetInstance()->language_list_
->GetSupportedLanguages(languages
);
125 base::Time
TranslateManager::GetSupportedLanguagesLastUpdated() {
126 if (GetInstance()->language_list_
.get()) {
127 return GetInstance()->language_list_
->last_updated();
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
);
139 return chrome_locale
;
143 bool TranslateManager::IsSupportedLanguage(const std::string
& language
) {
144 if (GetInstance()->language_list_
.get())
145 return GetInstance()->language_list_
->IsSupportedLanguage(language
);
151 bool TranslateManager::IsAlphaLanguage(const std::string
& language
) {
152 if (GetInstance()->language_list_
.get())
153 return GetInstance()->language_list_
->IsAlphaLanguage(language
);
159 bool TranslateManager::IsAcceptLanguage(Profile
* profile
,
160 const std::string
& language
) {
161 if (GetInstance()->accept_languages_
.get()) {
162 return GetInstance()->accept_languages_
->IsAcceptLanguage(
169 void TranslateManager::SetTranslateScriptExpirationDelay(int delay_ms
) {
170 if (script_
.get() == NULL
) {
174 script_
->set_expiration_delay(delay_ms
);
177 void TranslateManager::Observe(int type
,
178 const content::NotificationSource
& source
,
179 const content::NotificationDetails
& details
) {
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();
192 TranslateTabHelper
* translate_tab_helper
=
193 TranslateTabHelper::FromWebContents(controller
->GetWebContents());
194 if (!translate_tab_helper
)
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
) {
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.
211 if (entry
->GetTransitionType() != content::PAGE_TRANSITION_RELOAD
&&
212 load_details
->type
!= content::NAVIGATION_TYPE_SAME_PAGE
) {
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())
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
,
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));
234 case chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED
: {
235 const LanguageDetectionDetails
* lang_det_details
=
236 content::Details
<const LanguageDetectionDetails
>(details
).ptr();
238 WebContents
* tab
= content::Source
<WebContents
>(source
).ptr();
239 if (!tab
->GetBrowserContext()->IsOffTheRecord())
240 NotifyLanguageDetection(*lang_det_details
);
242 // We may get this notifications multiple times. Make sure to translate
244 TranslateTabHelper
* translate_tab_helper
=
245 TranslateTabHelper::FromWebContents(tab
);
246 if (!translate_tab_helper
)
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
);
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
);
274 void TranslateManager::AddObserver(Observer
* obs
) {
275 observer_list_
.AddObserver(obs
);
278 void TranslateManager::RemoveObserver(Observer
* obs
) {
279 observer_list_
.RemoveObserver(obs
);
282 void TranslateManager::NotifyTranslateEvent(
283 const TranslateEventDetails
& details
) {
284 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnTranslateEvent(details
));
287 void TranslateManager::NotifyLanguageDetection(
288 const LanguageDetectionDetails
& details
) {
289 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnLanguageDetection(details
));
292 void TranslateManager::NotifyTranslateError(
293 const TranslateErrorDetails
& details
) {
294 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnTranslateError(details
));
297 TranslateManager::TranslateManager()
298 : max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts
),
299 weak_method_factory_(this) {
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
);
312 void TranslateManager::InitiateTranslation(WebContents
* web_contents
,
313 const std::string
& page_lang
) {
314 TranslateTabHelper
* translate_tab_helper
=
315 TranslateTabHelper::FromWebContents(web_contents
);
316 if (!translate_tab_helper
)
320 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
321 Profile
* original_profile
= profile
->GetOriginalProfile();
322 PrefService
* prefs
= original_profile
->GetPrefs();
323 if (!prefs
->GetBoolean(prefs::kEnableTranslate
)) {
324 TranslateBrowserMetrics::ReportInitiationStatus(
325 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS
);
326 const std::string
& locale
= g_browser_process
->GetApplicationLocale();
327 TranslateBrowserMetrics::ReportLocalesOnDisabledByPrefs(locale
);
331 // Allow disabling of translate from the command line to assist with
332 // automated browser testing.
333 if (CommandLine::ForCurrentProcess()->HasSwitch(
334 switches::kDisableTranslate
)) {
335 TranslateBrowserMetrics::ReportInitiationStatus(
336 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_SWITCH
);
340 // MHTML pages currently cannot be translated.
342 if (web_contents
->GetContentsMimeType() == "multipart/related") {
343 TranslateBrowserMetrics::ReportInitiationStatus(
344 TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED
);
348 // Don't translate any Chrome specific page, e.g., New Tab Page, Download,
349 // History, and so on.
350 GURL page_url
= web_contents
->GetURL();
351 if (!IsTranslatableURL(page_url
)) {
352 TranslateBrowserMetrics::ReportInitiationStatus(
353 TranslateBrowserMetrics::INITIATION_STATUS_URL_IS_NOT_SUPPORTED
);
357 std::string target_lang
= GetTargetLanguage(prefs
);
358 std::string language_code
= GetLanguageCode(page_lang
);
360 // Don't translate similar languages (ex: en-US to en).
361 if (language_code
== target_lang
) {
362 TranslateBrowserMetrics::ReportInitiationStatus(
363 TranslateBrowserMetrics::INITIATION_STATUS_SIMILAR_LANGUAGES
);
367 // Nothing to do if either the language Chrome is in or the language of the
368 // page is not supported by the translation server.
369 if (target_lang
.empty() || !IsSupportedLanguage(language_code
)) {
370 TranslateBrowserMetrics::ReportInitiationStatus(
371 TranslateBrowserMetrics::INITIATION_STATUS_LANGUAGE_IS_NOT_SUPPORTED
);
372 TranslateBrowserMetrics::ReportUnsupportedLanguageAtInitiation(
377 TranslatePrefs
translate_prefs(prefs
);
379 // Don't translate any user black-listed languages.
380 if (!TranslatePrefs::CanTranslateLanguage(profile
, language_code
)) {
381 TranslateBrowserMetrics::ReportInitiationStatus(
382 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG
);
386 // Don't translate any user black-listed URLs.
387 if (translate_prefs
.IsSiteBlacklisted(page_url
.HostNoBrackets())) {
388 TranslateBrowserMetrics::ReportInitiationStatus(
389 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG
);
393 // If the user has previously selected "always translate" for this language we
394 // automatically translate. Note that in incognito mode we disable that
395 // feature; the user will get an infobar, so they can control whether the
396 // page's text is sent to the translate server.
397 if (!web_contents
->GetBrowserContext()->IsOffTheRecord()) {
398 std::string auto_target_lang
= GetAutoTargetLanguage(language_code
, prefs
);
399 if (!auto_target_lang
.empty()) {
400 TranslateBrowserMetrics::ReportInitiationStatus(
401 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_CONFIG
);
402 TranslatePage(web_contents
, language_code
, auto_target_lang
);
407 LanguageState
& language_state
= translate_tab_helper
->language_state();
408 std::string auto_translate_to
= language_state
.AutoTranslateTo();
409 if (!auto_translate_to
.empty()) {
410 // This page was navigated through a click from a translated page.
411 TranslateBrowserMetrics::ReportInitiationStatus(
412 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_LINK
);
413 TranslatePage(web_contents
, language_code
, auto_translate_to
);
417 TranslateBrowserMetrics::ReportInitiationStatus(
418 TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR
);
420 if (IsTranslateBubbleEnabled()) {
421 language_state
.SetTranslateEnabled(true);
422 if (language_state
.HasLanguageChanged()) {
423 ShowBubble(web_contents
,
424 TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE
,
425 TranslateErrors::NONE
);
428 // Prompts the user if he/she wants the page translated.
429 TranslateInfoBarDelegate::Create(
430 false, web_contents
, TranslateInfoBarDelegate::BEFORE_TRANSLATE
,
431 language_code
, target_lang
, TranslateErrors::NONE
, profile
->GetPrefs(),
436 void TranslateManager::InitiateTranslationPosted(int process_id
,
438 const std::string
& page_lang
,
440 // The tab might have been closed.
441 WebContents
* web_contents
=
442 tab_util::GetWebContentsByID(process_id
, render_id
);
446 TranslateTabHelper
* translate_tab_helper
=
447 TranslateTabHelper::FromWebContents(web_contents
);
448 if (translate_tab_helper
->language_state().translation_pending())
451 // During a reload we need web content to be available before the
452 // translate script is executed. Otherwise we will run the translate script on
453 // an empty DOM which will fail. Therefore we wait a bit to see if the page
455 if ((web_contents
->IsLoading()) && attempt
< kMaxTranslateLoadCheckAttempts
) {
456 int backoff
= attempt
* max_reload_check_attempts_
;
457 base::MessageLoop::current()->PostDelayedTask(
458 FROM_HERE
, base::Bind(&TranslateManager::InitiateTranslationPosted
,
459 weak_method_factory_
.GetWeakPtr(), process_id
,
460 render_id
, page_lang
, ++attempt
),
461 base::TimeDelta::FromMilliseconds(backoff
));
465 InitiateTranslation(web_contents
, GetLanguageCode(page_lang
));
468 void TranslateManager::TranslatePage(WebContents
* web_contents
,
469 const std::string
& original_source_lang
,
470 const std::string
& target_lang
) {
471 NavigationEntry
* entry
= web_contents
->GetController().GetActiveEntry();
477 // Translation can be kicked by context menu against unsupported languages.
478 // Unsupported language strings should be replaced with
479 // kUnknownLanguageCode in order to send a translation request with enabling
480 // server side auto language detection.
481 std::string
source_lang(original_source_lang
);
482 if (!IsSupportedLanguage(source_lang
))
483 source_lang
= std::string(translate::kUnknownLanguageCode
);
485 if (IsTranslateBubbleEnabled()) {
486 ShowBubble(web_contents
, TranslateBubbleModel::VIEW_STATE_TRANSLATING
,
487 TranslateErrors::NONE
);
490 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
491 TranslateInfoBarDelegate::Create(
492 true, web_contents
, TranslateInfoBarDelegate::TRANSLATING
, source_lang
,
493 target_lang
, TranslateErrors::NONE
, profile
->GetPrefs(),
497 DCHECK(script_
.get() != NULL
);
499 const std::string
& translate_script
= script_
->data();
500 if (!translate_script
.empty()) {
501 DoTranslatePage(web_contents
, translate_script
, source_lang
, target_lang
);
505 // The script is not available yet. Queue that request and query for the
506 // script. Once it is downloaded we'll do the translate.
507 content::RenderViewHost
* rvh
= web_contents
->GetRenderViewHost();
508 PendingRequest request
;
509 request
.render_process_id
= rvh
->GetProcess()->GetID();
510 request
.render_view_id
= rvh
->GetRoutingID();
511 request
.page_id
= entry
->GetPageID();
512 request
.source_lang
= source_lang
;
513 request
.target_lang
= target_lang
;
514 pending_requests_
.push_back(request
);
516 if (script_
->HasPendingRequest())
520 base::Bind(&TranslateManager::OnTranslateScriptFetchComplete
,
521 base::Unretained(this)));
524 void TranslateManager::RevertTranslation(WebContents
* web_contents
) {
525 NavigationEntry
* entry
= web_contents
->GetController().GetActiveEntry();
530 web_contents
->GetRenderViewHost()->Send(new ChromeViewMsg_RevertTranslation(
531 web_contents
->GetRenderViewHost()->GetRoutingID(), entry
->GetPageID()));
533 TranslateTabHelper
* translate_tab_helper
=
534 TranslateTabHelper::FromWebContents(web_contents
);
535 translate_tab_helper
->language_state().SetCurrentLanguage(
536 translate_tab_helper
->language_state().original_language());
539 void TranslateManager::ReportLanguageDetectionError(WebContents
* web_contents
) {
540 TranslateBrowserMetrics::ReportLanguageDetectionError();
541 // We'll open the URL in a new tab so that the user can tell us more.
542 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
548 GURL report_error_url
= GURL(kReportLanguageDetectionErrorURL
);
550 GURL page_url
= web_contents
->GetController().GetActiveEntry()->GetURL();
551 report_error_url
= net::AppendQueryParameter(
556 TranslateTabHelper
* translate_tab_helper
=
557 TranslateTabHelper::FromWebContents(web_contents
);
558 report_error_url
= net::AppendQueryParameter(
560 kSourceLanguageQueryName
,
561 translate_tab_helper
->language_state().original_language());
563 report_error_url
= TranslateURLUtil::AddHostLocaleToUrl(report_error_url
);
564 report_error_url
= TranslateURLUtil::AddApiKeyToUrl(report_error_url
);
566 chrome::AddSelectedTabWithURL(browser
, report_error_url
,
567 content::PAGE_TRANSITION_AUTO_BOOKMARK
);
570 void TranslateManager::ClearTranslateScript() {
571 if (script_
.get() == NULL
) {
578 void TranslateManager::DoTranslatePage(WebContents
* web_contents
,
579 const std::string
& translate_script
,
580 const std::string
& source_lang
,
581 const std::string
& target_lang
) {
582 NavigationEntry
* entry
= web_contents
->GetController().GetActiveEntry();
588 TranslateTabHelper
* translate_tab_helper
=
589 TranslateTabHelper::FromWebContents(web_contents
);
590 if (!translate_tab_helper
)
593 translate_tab_helper
->language_state().set_translation_pending(true);
594 web_contents
->GetRenderViewHost()->Send(new ChromeViewMsg_TranslatePage(
595 web_contents
->GetRenderViewHost()->GetRoutingID(), entry
->GetPageID(),
596 translate_script
, source_lang
, target_lang
));
599 void TranslateManager::PageTranslated(WebContents
* web_contents
,
600 PageTranslatedDetails
* details
) {
601 if ((details
->error_type
== TranslateErrors::NONE
) &&
602 details
->source_language
!= translate::kUnknownLanguageCode
&&
603 !IsSupportedLanguage(details
->source_language
)) {
604 details
->error_type
= TranslateErrors::UNSUPPORTED_LANGUAGE
;
607 if (IsTranslateBubbleEnabled()) {
608 TranslateBubbleModel::ViewState view_state
=
609 (details
->error_type
== TranslateErrors::NONE
) ?
610 TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE
:
611 TranslateBubbleModel::VIEW_STATE_ERROR
;
612 ShowBubble(web_contents
, view_state
, details
->error_type
);
614 PrefService
* prefs
= Profile::FromBrowserContext(
615 web_contents
->GetBrowserContext())->GetPrefs();
616 TranslateInfoBarDelegate::Create(
618 (details
->error_type
== TranslateErrors::NONE
) ?
619 TranslateInfoBarDelegate::AFTER_TRANSLATE
:
620 TranslateInfoBarDelegate::TRANSLATION_ERROR
,
621 details
->source_language
, details
->target_language
, details
->error_type
,
622 prefs
, ShortcutConfig());
625 if (details
->error_type
!= TranslateErrors::NONE
&&
626 !web_contents
->GetBrowserContext()->IsOffTheRecord()) {
627 TranslateErrorDetails error_details
;
628 error_details
.time
= base::Time::Now();
629 error_details
.url
= web_contents
->GetLastCommittedURL();
630 error_details
.error
= details
->error_type
;
631 NotifyTranslateError(error_details
);
635 void TranslateManager::FetchLanguageListFromTranslateServer(
636 PrefService
* prefs
) {
637 // We don't want to do this when translate is disabled.
638 DCHECK(prefs
!= NULL
);
639 if (CommandLine::ForCurrentProcess()->HasSwitch(
640 switches::kDisableTranslate
) ||
641 (prefs
!= NULL
&& !prefs
->GetBoolean(prefs::kEnableTranslate
))) {
645 if (language_list_
.get())
646 language_list_
->RequestLanguageList();
651 void TranslateManager::CleanupPendingUlrFetcher() {
652 language_list_
.reset();
656 void TranslateManager::OnTranslateScriptFetchComplete(
657 bool success
, const std::string
& data
) {
658 std::vector
<PendingRequest
>::const_iterator iter
;
659 for (iter
= pending_requests_
.begin(); iter
!= pending_requests_
.end();
661 const PendingRequest
& request
= *iter
;
662 WebContents
* web_contents
=
663 tab_util::GetWebContentsByID(request
.render_process_id
,
664 request
.render_view_id
);
666 // The tab went away while we were retrieving the script.
669 NavigationEntry
* entry
= web_contents
->GetController().GetActiveEntry();
670 if (!entry
|| entry
->GetPageID() != request
.page_id
) {
671 // We navigated away from the page the translation was triggered on.
676 // Translate the page.
677 const std::string
& translate_script
= script_
->data();
678 DoTranslatePage(web_contents
, translate_script
,
679 request
.source_lang
, request
.target_lang
);
681 if (IsTranslateBubbleEnabled()) {
682 ShowBubble(web_contents
, TranslateBubbleModel::VIEW_STATE_ERROR
,
683 TranslateErrors::NETWORK
);
686 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
687 TranslateInfoBarDelegate::Create(
688 true, web_contents
, TranslateInfoBarDelegate::TRANSLATION_ERROR
,
689 request
.source_lang
, request
.target_lang
, TranslateErrors::NETWORK
,
690 profile
->GetPrefs(), ShortcutConfig());
693 if (!web_contents
->GetBrowserContext()->IsOffTheRecord()) {
694 TranslateErrorDetails error_details
;
695 error_details
.time
= base::Time::Now();
696 error_details
.url
= entry
->GetURL();
697 error_details
.error
= TranslateErrors::NETWORK
;
698 NotifyTranslateError(error_details
);
702 pending_requests_
.clear();
705 void TranslateManager::ShowBubble(WebContents
* web_contents
,
706 TranslateBubbleModel::ViewState view_state
,
707 TranslateErrors::Type error_type
) {
708 // The bubble is implemented only on the desktop platforms.
709 #if !defined(OS_ANDROID) && !defined(OS_IOS)
710 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
712 // |browser| might be NULL when testing. In this case, Show(...) should be
713 // called because the implementation for testing is used.
715 TranslateBubbleFactory::Show(NULL
, web_contents
, view_state
, error_type
);
719 if (web_contents
!= browser
->tab_strip_model()->GetActiveWebContents())
722 // This ShowBubble function is also used for upating the existing bubble.
723 // However, with the bubble shown, any browser windows are NOT activated
724 // because the bubble takes the focus from the other widgets including the
725 // browser windows. So it is checked that |browser| is the last activated
726 // browser, not is now activated.
728 chrome::FindLastActiveWithHostDesktopType(browser
->host_desktop_type())) {
732 // During auto-translating, the bubble should not be shown.
733 if (view_state
== TranslateBubbleModel::VIEW_STATE_TRANSLATING
||
734 view_state
== TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE
) {
735 TranslateTabHelper
* translate_tab_helper
=
736 TranslateTabHelper::FromWebContents(web_contents
);
737 if (!translate_tab_helper
||
738 translate_tab_helper
->language_state().InTranslateNavigation()) {
743 TranslateBubbleFactory::Show(browser
->window(), web_contents
, view_state
,
751 std::string
TranslateManager::GetTargetLanguage(PrefService
* prefs
) {
752 std::string ui_lang
=
753 TranslatePrefs::ConvertLangCodeForTranslation(
754 GetLanguageCode(g_browser_process
->GetApplicationLocale()));
756 if (IsSupportedLanguage(ui_lang
))
759 // Getting the accepted languages list
760 std::string accept_langs_str
= prefs
->GetString(prefs::kAcceptLanguages
);
762 std::vector
<std::string
> accept_langs_list
;
763 base::SplitString(accept_langs_str
, ',', &accept_langs_list
);
765 // Will translate to the first supported language on the Accepted Language
766 // list or not at all if no such candidate exists
767 std::vector
<std::string
>::iterator iter
;
768 for (iter
= accept_langs_list
.begin();
769 iter
!= accept_langs_list
.end(); ++iter
) {
770 std::string lang_code
= GetLanguageCode(*iter
);
771 if (IsSupportedLanguage(lang_code
))
774 return std::string();
778 std::string
TranslateManager::GetAutoTargetLanguage(
779 const std::string
& original_language
,
780 PrefService
* prefs
) {
781 std::string auto_target_lang
;
782 if (TranslatePrefs::ShouldAutoTranslate(prefs
, original_language
,
783 &auto_target_lang
)) {
784 // We need to confirm that the saved target language is still supported.
785 // Also, GetLanguageCode will take care of removing country code if any.
786 auto_target_lang
= GetLanguageCode(auto_target_lang
);
787 if (IsSupportedLanguage(auto_target_lang
))
788 return auto_target_lang
;
790 return std::string();
794 bool TranslateManager::IsTranslateBubbleEnabled() {
795 if (CommandLine::ForCurrentProcess()->HasSwitch(
796 switches::kEnableTranslateNewUX
)) {
800 std::string group_name
= base::FieldTrialList::FindFullName(
801 kFieldTrialNameForUX
);
802 return group_name
== "Bubble";
806 ShortcutConfiguration
TranslateManager::ShortcutConfig() {
807 ShortcutConfiguration config
;
809 // The android implementation does not offer a drop down (for space reasons),
810 // so we are more aggressive about showing the shortcut to never translate.
811 #if defined(OS_ANDROID)
812 config
.never_translate_min_count
= 1;
814 config
.never_translate_min_count
= 3;
815 #endif // defined(OS_ANDROID)
817 config
.always_translate_min_count
= 3;