Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / translate / translate_manager.cc
blob50349afe50b89aad12ea529d74035e456299cb5d
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"
7 #include "base/bind.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"
60 #endif
62 using content::NavigationController;
63 using content::NavigationEntry;
64 using content::WebContents;
66 namespace {
68 const char kReportLanguageDetectionErrorURL[] =
69 "https://translate.google.com/translate_error?client=cr&action=langidc";
71 // Used in kReportLanguageDetectionErrorURL to specify the original page
72 // language.
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";
85 } // namespace
87 TranslateManager::~TranslateManager() {
90 // static
91 TranslateManager* TranslateManager::GetInstance() {
92 return Singleton<TranslateManager>::get();
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(file_manager::kFileManagerAppId)) &&
110 #endif
111 !url.SchemeIs(content::kFtpScheme);
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;
121 NOTREACHED();
124 // static
125 base::Time TranslateManager::GetSupportedLanguagesLastUpdated() {
126 if (GetInstance()->language_list_.get()) {
127 return GetInstance()->language_list_->last_updated();
129 NOTREACHED();
130 return base::Time();
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;
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;
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;
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);
165 NOTREACHED();
166 return false;
169 void TranslateManager::SetTranslateScriptExpirationDelay(int delay_ms) {
170 if (script_.get() == NULL) {
171 NOTREACHED();
172 return;
174 script_->set_expiration_delay(delay_ms);
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;
192 TranslateTabHelper* translate_tab_helper =
193 TranslateTabHelper::FromWebContents(controller->GetWebContents());
194 if (!translate_tab_helper)
195 return;
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;
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;
211 if (entry->GetTransitionType() != content::PAGE_TRANSITION_RELOAD &&
212 load_details->type != content::NAVIGATION_TYPE_SAME_PAGE) {
213 return;
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;
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
243 // only once.
244 TranslateTabHelper* translate_tab_helper =
245 TranslateTabHelper::FromWebContents(tab);
246 if (!translate_tab_helper)
247 return;
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);
257 break;
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;
269 default:
270 NOTREACHED();
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)
317 return;
319 Profile* profile =
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);
328 return;
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);
337 return;
340 // MHTML pages currently cannot be translated.
341 // See bug: 217945.
342 if (web_contents->GetContentsMimeType() == "multipart/related") {
343 TranslateBrowserMetrics::ReportInitiationStatus(
344 TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED);
345 return;
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);
354 return;
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);
364 return;
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(
373 language_code);
374 return;
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);
383 return;
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);
390 return;
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);
403 return;
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);
414 return;
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);
427 } else {
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(),
432 ShortcutConfig());
436 void TranslateManager::InitiateTranslationPosted(int process_id,
437 int render_id,
438 const std::string& page_lang,
439 int attempt) {
440 // The tab might have been closed.
441 WebContents* web_contents =
442 tab_util::GetWebContentsByID(process_id, render_id);
443 if (!web_contents)
444 return;
446 TranslateTabHelper* translate_tab_helper =
447 TranslateTabHelper::FromWebContents(web_contents);
448 if (translate_tab_helper->language_state().translation_pending())
449 return;
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
454 // has finished.
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));
462 return;
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();
472 if (!entry) {
473 NOTREACHED();
474 return;
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);
488 } else {
489 Profile* profile =
490 Profile::FromBrowserContext(web_contents->GetBrowserContext());
491 TranslateInfoBarDelegate::Create(
492 true, web_contents, TranslateInfoBarDelegate::TRANSLATING, source_lang,
493 target_lang, TranslateErrors::NONE, profile->GetPrefs(),
494 ShortcutConfig());
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);
502 return;
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())
517 return;
519 script_->Request(
520 base::Bind(&TranslateManager::OnTranslateScriptFetchComplete,
521 base::Unretained(this)));
524 void TranslateManager::RevertTranslation(WebContents* web_contents) {
525 NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
526 if (!entry) {
527 NOTREACHED();
528 return;
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);
543 if (!browser) {
544 NOTREACHED();
545 return;
548 GURL report_error_url = GURL(kReportLanguageDetectionErrorURL);
550 GURL page_url = web_contents->GetController().GetActiveEntry()->GetURL();
551 report_error_url = net::AppendQueryParameter(
552 report_error_url,
553 kUrlQueryName,
554 page_url.spec());
556 TranslateTabHelper* translate_tab_helper =
557 TranslateTabHelper::FromWebContents(web_contents);
558 report_error_url = net::AppendQueryParameter(
559 report_error_url,
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) {
572 NOTREACHED();
573 return;
575 script_->Clear();
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();
583 if (!entry) {
584 NOTREACHED();
585 return;
588 TranslateTabHelper* translate_tab_helper =
589 TranslateTabHelper::FromWebContents(web_contents);
590 if (!translate_tab_helper)
591 return;
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);
613 } else {
614 PrefService* prefs = Profile::FromBrowserContext(
615 web_contents->GetBrowserContext())->GetPrefs();
616 TranslateInfoBarDelegate::Create(
617 true, web_contents,
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))) {
642 return;
645 if (language_list_.get())
646 language_list_->RequestLanguageList();
647 else
648 NOTREACHED();
651 void TranslateManager::CleanupPendingUlrFetcher() {
652 language_list_.reset();
653 script_.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();
660 ++iter) {
661 const PendingRequest& request = *iter;
662 WebContents* web_contents =
663 tab_util::GetWebContentsByID(request.render_process_id,
664 request.render_view_id);
665 if (!web_contents) {
666 // The tab went away while we were retrieving the script.
667 continue;
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.
672 continue;
675 if (success) {
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);
680 } else {
681 if (IsTranslateBubbleEnabled()) {
682 ShowBubble(web_contents, TranslateBubbleModel::VIEW_STATE_ERROR,
683 TranslateErrors::NETWORK);
684 } else {
685 Profile* profile =
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.
714 if (!browser) {
715 TranslateBubbleFactory::Show(NULL, web_contents, view_state, error_type);
716 return;
719 if (web_contents != browser->tab_strip_model()->GetActiveWebContents())
720 return;
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.
727 if (browser !=
728 chrome::FindLastActiveWithHostDesktopType(browser->host_desktop_type())) {
729 return;
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()) {
739 return;
743 TranslateBubbleFactory::Show(browser->window(), web_contents, view_state,
744 error_type);
745 #else
746 NOTREACHED();
747 #endif
750 // static
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))
757 return 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))
772 return lang_code;
774 return std::string();
777 // static
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();
793 // static
794 bool TranslateManager::IsTranslateBubbleEnabled() {
795 if (CommandLine::ForCurrentProcess()->HasSwitch(
796 switches::kEnableTranslateNewUX)) {
797 return true;
800 std::string group_name = base::FieldTrialList::FindFullName(
801 kFieldTrialNameForUX);
802 return group_name == "Bubble";
805 // static
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;
813 #else
814 config.never_translate_min_count = 3;
815 #endif // defined(OS_ANDROID)
817 config.always_translate_min_count = 3;
818 return config;