- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / translate / translate_manager.cc
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/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"
57
58 #ifdef FILE_MANAGER_EXTENSION
59 #include "chrome/browser/chromeos/file_manager/app_id.h"
60 #include "extensions/common/constants.h"
61 #endif
62
63 using content::NavigationController;
64 using content::NavigationEntry;
65 using content::WebContents;
66
67 namespace {
68
69 const char kReportLanguageDetectionErrorURL[] =
70     "https://translate.google.com/translate_error?client=cr&action=langidc";
71
72 // Used in kReportLanguageDetectionErrorURL to specify the original page
73 // language.
74 const char kSourceLanguageQueryName[] = "sl";
75
76 // Used in kReportLanguageDetectionErrorURL to specify the page URL.
77 const char kUrlQueryName[] = "u";
78
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;
82
83 // The field trial name to compare Translate infobar and bubble.
84 const char kFieldTrialNameForUX[] = "TranslateInfobarVsBubble";
85
86 bool IsEnabledTranslateNewUX() {
87   if (CommandLine::ForCurrentProcess()->HasSwitch(
88           switches::kEnableTranslateNewUX)) {
89     return true;
90   }
91
92   std::string group_name = base::FieldTrialList::FindFullName(
93       kFieldTrialNameForUX);
94   return group_name == "Bubble";
95 }
96
97 }  // namespace
98
99 TranslateManager::~TranslateManager() {
100 }
101
102 // static
103 TranslateManager* TranslateManager::GetInstance() {
104   return Singleton<TranslateManager>::get();
105 }
106
107 // static
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
115   //   confuse the CLD)
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)) &&
122 #endif
123          !url.SchemeIs(chrome::kFtpScheme);
124 }
125
126 // static
127 void TranslateManager::GetSupportedLanguages(
128     std::vector<std::string>* languages) {
129   if (GetInstance()->language_list_.get()) {
130     GetInstance()->language_list_->GetSupportedLanguages(languages);
131     return;
132   }
133   NOTREACHED();
134 }
135
136 // static
137 base::Time TranslateManager::GetSupportedLanguagesLastUpdated() {
138   if (GetInstance()->language_list_.get()) {
139     return GetInstance()->language_list_->last_updated();
140   }
141   NOTREACHED();
142   return base::Time();
143 }
144
145 // static
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);
150   NOTREACHED();
151   return chrome_locale;
152 }
153
154 // static
155 bool TranslateManager::IsSupportedLanguage(const std::string& language) {
156   if (GetInstance()->language_list_.get())
157     return GetInstance()->language_list_->IsSupportedLanguage(language);
158   NOTREACHED();
159   return false;
160 }
161
162 // static
163 bool TranslateManager::IsAlphaLanguage(const std::string& language) {
164   if (GetInstance()->language_list_.get())
165     return GetInstance()->language_list_->IsAlphaLanguage(language);
166   NOTREACHED();
167   return false;
168 }
169
170 // static
171 bool TranslateManager::IsAcceptLanguage(Profile* profile,
172                                         const std::string& language) {
173   if (GetInstance()->accept_languages_.get()) {
174     return GetInstance()->accept_languages_->IsAcceptLanguage(
175         profile, language);
176   }
177   NOTREACHED();
178   return false;
179 }
180
181 void TranslateManager::SetTranslateScriptExpirationDelay(int delay_ms) {
182   if (script_.get() == NULL) {
183     NOTREACHED();
184     return;
185   }
186   script_->set_expiration_delay(delay_ms);
187 }
188
189 void TranslateManager::Observe(int type,
190                                const content::NotificationSource& source,
191                                const content::NotificationDetails& details) {
192   switch (type) {
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();
199       if (!entry) {
200         NOTREACHED();
201         return;
202       }
203
204       TranslateTabHelper* translate_tab_helper =
205           TranslateTabHelper::FromWebContents(controller->GetWebContents());
206       if (!translate_tab_helper)
207         return;
208
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) {
213         return;
214       }
215
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.
221         return;
222       }
223       if (entry->GetTransitionType() != content::PAGE_TRANSITION_RELOAD &&
224           load_details->type != content::NAVIGATION_TYPE_SAME_PAGE) {
225         return;
226       }
227
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())
232         return;
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,
238           base::Bind(
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));
244       break;
245     }
246     case chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED: {
247       const LanguageDetectionDetails* lang_det_details =
248           content::Details<const LanguageDetectionDetails>(details).ptr();
249
250       WebContents* tab = content::Source<WebContents>(source).ptr();
251       if (!tab->GetBrowserContext()->IsOffTheRecord())
252         NotifyLanguageDetection(*lang_det_details);
253
254       // We may get this notifications multiple times.  Make sure to translate
255       // only once.
256       TranslateTabHelper* translate_tab_helper =
257           TranslateTabHelper::FromWebContents(tab);
258       if (!translate_tab_helper)
259         return;
260
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);
268       }
269       break;
270     }
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);
279       break;
280     }
281     default:
282       NOTREACHED();
283   }
284 }
285
286 void TranslateManager::AddObserver(Observer* obs) {
287   observer_list_.AddObserver(obs);
288 }
289
290 void TranslateManager::RemoveObserver(Observer* obs) {
291   observer_list_.RemoveObserver(obs);
292 }
293
294 void TranslateManager::NotifyTranslateEvent(
295     const TranslateEventDetails& details) {
296   FOR_EACH_OBSERVER(Observer, observer_list_, OnTranslateEvent(details));
297 }
298
299 void TranslateManager::NotifyLanguageDetection(
300     const LanguageDetectionDetails& details) {
301   FOR_EACH_OBSERVER(Observer, observer_list_, OnLanguageDetection(details));
302 }
303
304 void TranslateManager::NotifyTranslateError(
305     const TranslateErrorDetails& details) {
306   FOR_EACH_OBSERVER(Observer, observer_list_, OnTranslateError(details));
307 }
308
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);
322 }
323
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)
329     return;
330
331   Profile* profile =
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);
340     return;
341   }
342
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);
349     return;
350   }
351
352   // MHTML pages currently cannot be translated.
353   // See bug: 217945.
354   if (web_contents->GetContentsMimeType() == "multipart/related") {
355     TranslateBrowserMetrics::ReportInitiationStatus(
356         TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED);
357     return;
358   }
359
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);
366     return;
367   }
368
369   std::string target_lang = GetTargetLanguage(prefs);
370   std::string language_code = GetLanguageCode(page_lang);
371
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);
376     return;
377   }
378
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(
385         language_code);
386     return;
387   }
388
389   TranslatePrefs translate_prefs(prefs);
390
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);
395     return;
396   }
397
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);
402     return;
403   }
404
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);
420       return;
421     }
422   }
423
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);
431     return;
432   }
433
434   TranslateBrowserMetrics::ReportInitiationStatus(
435       TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR);
436
437   if (IsEnabledTranslateNewUX()) {
438     language_state.SetTranslateEnabled(true);
439     if (language_state.HasLanguageChanged())
440       ShowBubble(web_contents,
441                  TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE);
442   } else {
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());
448   }
449 }
450
451 void TranslateManager::InitiateTranslationPosted(int process_id,
452                                                  int render_id,
453                                                  const std::string& page_lang,
454                                                  int attempt) {
455   // The tab might have been closed.
456   WebContents* web_contents =
457       tab_util::GetWebContentsByID(process_id, render_id);
458   if (!web_contents)
459     return;
460
461   TranslateTabHelper* translate_tab_helper =
462       TranslateTabHelper::FromWebContents(web_contents);
463   if (translate_tab_helper->language_state().translation_pending())
464     return;
465
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
469   // has finished.
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));
477     return;
478   }
479
480   InitiateTranslation(web_contents, GetLanguageCode(page_lang));
481 }
482
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();
487   if (!entry) {
488     NOTREACHED();
489     return;
490   }
491
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);
499
500   if (IsEnabledTranslateNewUX()) {
501     ShowBubble(web_contents, TranslateBubbleModel::VIEW_STATE_TRANSLATING);
502   } else {
503     Profile* profile =
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());
509   }
510
511   DCHECK(script_.get() != NULL);
512
513   const std::string& translate_script = script_->data();
514   if (!translate_script.empty()) {
515     DoTranslatePage(web_contents, translate_script, source_lang, target_lang);
516     return;
517   }
518
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);
529
530   if (script_->HasPendingRequest())
531     return;
532
533   script_->Request(
534       base::Bind(&TranslateManager::OnTranslateScriptFetchComplete,
535                  base::Unretained(this)));
536 }
537
538 void TranslateManager::RevertTranslation(WebContents* web_contents) {
539   NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
540   if (!entry) {
541     NOTREACHED();
542     return;
543   }
544   web_contents->GetRenderViewHost()->Send(new ChromeViewMsg_RevertTranslation(
545       web_contents->GetRenderViewHost()->GetRoutingID(), entry->GetPageID()));
546
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());
551 }
552
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);
557   if (!browser) {
558     NOTREACHED();
559     return;
560   }
561
562   GURL report_error_url = GURL(kReportLanguageDetectionErrorURL);
563
564   GURL page_url = web_contents->GetController().GetActiveEntry()->GetURL();
565   report_error_url = net::AppendQueryParameter(
566       report_error_url,
567       kUrlQueryName,
568       page_url.spec());
569
570   TranslateTabHelper* translate_tab_helper =
571       TranslateTabHelper::FromWebContents(web_contents);
572   report_error_url = net::AppendQueryParameter(
573       report_error_url,
574       kSourceLanguageQueryName,
575       translate_tab_helper->language_state().original_language());
576
577   report_error_url = TranslateURLUtil::AddHostLocaleToUrl(report_error_url);
578   report_error_url = TranslateURLUtil::AddApiKeyToUrl(report_error_url);
579
580   chrome::AddSelectedTabWithURL(browser, report_error_url,
581                                 content::PAGE_TRANSITION_AUTO_BOOKMARK);
582 }
583
584 void TranslateManager::ClearTranslateScript() {
585   if (script_.get() == NULL) {
586     NOTREACHED();
587     return;
588   }
589   script_->Clear();
590 }
591
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();
597   if (!entry) {
598     NOTREACHED();
599     return;
600   }
601
602   TranslateTabHelper* translate_tab_helper =
603       TranslateTabHelper::FromWebContents(web_contents);
604   if (!translate_tab_helper)
605     return;
606
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));
611 }
612
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;
619   }
620
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);
627   } else {
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());
637   }
638
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);
646   }
647 }
648
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))) {
656     return;
657   }
658
659   if (language_list_.get())
660     language_list_->RequestLanguageList();
661   else
662     NOTREACHED();
663 }
664
665 void TranslateManager::CleanupPendingUlrFetcher() {
666   language_list_.reset();
667   script_.reset();
668 }
669
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();
674        ++iter) {
675     const PendingRequest& request = *iter;
676     WebContents* web_contents =
677         tab_util::GetWebContentsByID(request.render_process_id,
678                                      request.render_view_id);
679     if (!web_contents) {
680       // The tab went away while we were retrieving the script.
681       continue;
682     }
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.
686       continue;
687     }
688
689     if (success) {
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);
694     } else {
695       if (IsEnabledTranslateNewUX()) {
696         ShowBubble(web_contents, TranslateBubbleModel::VIEW_STATE_ERROR);
697       } else {
698         Profile* profile =
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(),
704             ShortcutConfig());
705       }
706
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);
713       }
714     }
715   }
716   pending_requests_.clear();
717 }
718
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);
726 #else
727   NOTREACHED();
728 #endif
729 }
730
731 // static
732 std::string TranslateManager::GetTargetLanguage(PrefService* prefs) {
733   std::string ui_lang =
734       GetLanguageCode(g_browser_process->GetApplicationLocale());
735   if (IsSupportedLanguage(ui_lang))
736     return ui_lang;
737
738   // Getting the accepted languages list
739   std::string accept_langs_str = prefs->GetString(prefs::kAcceptLanguages);
740
741   std::vector<std::string> accept_langs_list;
742   base::SplitString(accept_langs_str, ',', &accept_langs_list);
743
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))
751       return lang_code;
752   }
753   return std::string();
754 }
755
756 // static
757 ShortcutConfiguration TranslateManager::ShortcutConfig() {
758   ShortcutConfiguration config;
759
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;
764   #else
765   config.never_translate_min_count = 3;
766   #endif  // defined(OS_ANDROID)
767
768   config.always_translate_min_count = 3;
769   return config;
770 }