• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "components/translate/core/browser/translate_manager.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/time/time.h"
15 #include "components/translate/core/browser/language_state.h"
16 #include "components/translate/core/browser/page_translated_details.h"
17 #include "components/translate/core/browser/translate_accept_languages.h"
18 #include "components/translate/core/browser/translate_browser_metrics.h"
19 #include "components/translate/core/browser/translate_client.h"
20 #include "components/translate/core/browser/translate_download_manager.h"
21 #include "components/translate/core/browser/translate_driver.h"
22 #include "components/translate/core/browser/translate_error_details.h"
23 #include "components/translate/core/browser/translate_language_list.h"
24 #include "components/translate/core/browser/translate_prefs.h"
25 #include "components/translate/core/browser/translate_script.h"
26 #include "components/translate/core/browser/translate_url_util.h"
27 #include "components/translate/core/common/language_detection_details.h"
28 #include "components/translate/core/common/translate_constants.h"
29 #include "components/translate/core/common/translate_pref_names.h"
30 #include "components/translate/core/common/translate_switches.h"
31 #include "net/base/url_util.h"
32 #include "net/http/http_status_code.h"
33 
34 namespace {
35 
36 // Callbacks for translate errors.
37 TranslateManager::TranslateErrorCallbackList* g_callback_list_ = NULL;
38 
39 const char kReportLanguageDetectionErrorURL[] =
40     "https://translate.google.com/translate_error?client=cr&action=langidc";
41 
42 // Used in kReportLanguageDetectionErrorURL to specify the original page
43 // language.
44 const char kSourceLanguageQueryName[] = "sl";
45 
46 // Used in kReportLanguageDetectionErrorURL to specify the page URL.
47 const char kUrlQueryName[] = "u";
48 
49 // Notifies |g_callback_list_| of translate errors.
NotifyTranslateError(const TranslateErrorDetails & details)50 void NotifyTranslateError(const TranslateErrorDetails& details) {
51   if (!g_callback_list_)
52     return;
53 
54   g_callback_list_->Notify(details);
55 }
56 
57 }  // namespace
58 
~TranslateManager()59 TranslateManager::~TranslateManager() {}
60 
61 // static
62 scoped_ptr<TranslateManager::TranslateErrorCallbackList::Subscription>
RegisterTranslateErrorCallback(const TranslateManager::TranslateErrorCallback & callback)63 TranslateManager::RegisterTranslateErrorCallback(
64     const TranslateManager::TranslateErrorCallback& callback) {
65   if (!g_callback_list_)
66     g_callback_list_ = new TranslateErrorCallbackList;
67   return g_callback_list_->Add(callback);
68 }
69 
TranslateManager(TranslateClient * translate_client,const std::string & accept_languages_pref_name)70 TranslateManager::TranslateManager(
71     TranslateClient* translate_client,
72     const std::string& accept_languages_pref_name)
73     : accept_languages_pref_name_(accept_languages_pref_name),
74       translate_client_(translate_client),
75       translate_driver_(translate_client_->GetTranslateDriver()),
76       language_state_(translate_driver_),
77       weak_method_factory_(this) {
78 }
79 
GetWeakPtr()80 base::WeakPtr<TranslateManager> TranslateManager::GetWeakPtr() {
81   return weak_method_factory_.GetWeakPtr();
82 }
83 
InitiateTranslation(const std::string & page_lang)84 void TranslateManager::InitiateTranslation(const std::string& page_lang) {
85   // Short-circuit out if not in a state where initiating translation makes
86   // sense (this method may be called muhtiple times for a given page).
87   if (!language_state_.page_needs_translation() ||
88       language_state_.translation_pending() ||
89       language_state_.translation_declined() ||
90       language_state_.IsPageTranslated()) {
91     return;
92   }
93 
94   PrefService* prefs = translate_client_->GetPrefs();
95   if (!prefs->GetBoolean(prefs::kEnableTranslate)) {
96     TranslateBrowserMetrics::ReportInitiationStatus(
97         TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS);
98     const std::string& locale =
99         TranslateDownloadManager::GetInstance()->application_locale();
100     TranslateBrowserMetrics::ReportLocalesOnDisabledByPrefs(locale);
101     return;
102   }
103 
104   // Allow disabling of translate from the command line to assist with
105   // automated browser testing.
106   if (CommandLine::ForCurrentProcess()->HasSwitch(
107           translate::switches::kDisableTranslate)) {
108     TranslateBrowserMetrics::ReportInitiationStatus(
109         TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_SWITCH);
110     return;
111   }
112 
113   // MHTML pages currently cannot be translated.
114   // See bug: 217945.
115   if (translate_driver_->GetContentsMimeType() == "multipart/related") {
116     TranslateBrowserMetrics::ReportInitiationStatus(
117         TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED);
118     return;
119   }
120 
121   // Don't translate any Chrome specific page, e.g., New Tab Page, Download,
122   // History, and so on.
123   const GURL& page_url = translate_driver_->GetVisibleURL();
124   if (!translate_client_->IsTranslatableURL(page_url)) {
125     TranslateBrowserMetrics::ReportInitiationStatus(
126         TranslateBrowserMetrics::INITIATION_STATUS_URL_IS_NOT_SUPPORTED);
127     return;
128   }
129 
130   // Get the accepted languages list.
131   std::vector<std::string> accept_languages_list;
132   base::SplitString(prefs->GetString(accept_languages_pref_name_.c_str()), ',',
133                     &accept_languages_list);
134 
135   std::string target_lang = GetTargetLanguage(accept_languages_list);
136   std::string language_code =
137       TranslateDownloadManager::GetLanguageCode(page_lang);
138 
139   // Don't translate similar languages (ex: en-US to en).
140   if (language_code == target_lang) {
141     TranslateBrowserMetrics::ReportInitiationStatus(
142         TranslateBrowserMetrics::INITIATION_STATUS_SIMILAR_LANGUAGES);
143     return;
144   }
145 
146   // Nothing to do if either the language Chrome is in or the language of the
147   // page is not supported by the translation server.
148   if (target_lang.empty() ||
149       !TranslateDownloadManager::IsSupportedLanguage(language_code)) {
150     TranslateBrowserMetrics::ReportInitiationStatus(
151         TranslateBrowserMetrics::INITIATION_STATUS_LANGUAGE_IS_NOT_SUPPORTED);
152     TranslateBrowserMetrics::ReportUnsupportedLanguageAtInitiation(
153         language_code);
154     return;
155   }
156 
157   scoped_ptr<TranslatePrefs> translate_prefs(
158       translate_client_->GetTranslatePrefs());
159 
160   TranslateAcceptLanguages* accept_languages =
161       translate_client_->GetTranslateAcceptLanguages();
162   // Don't translate any user black-listed languages.
163   if (!translate_prefs->CanTranslateLanguage(accept_languages,
164                                              language_code)) {
165     TranslateBrowserMetrics::ReportInitiationStatus(
166         TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
167     return;
168   }
169 
170   // Don't translate any user black-listed URLs.
171   if (translate_prefs->IsSiteBlacklisted(page_url.HostNoBrackets())) {
172     TranslateBrowserMetrics::ReportInitiationStatus(
173         TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
174     return;
175   }
176 
177   // If the user has previously selected "always translate" for this language we
178   // automatically translate.  Note that in incognito mode we disable that
179   // feature; the user will get an infobar, so they can control whether the
180   // page's text is sent to the translate server.
181   if (!translate_driver_->IsOffTheRecord()) {
182     scoped_ptr<TranslatePrefs> translate_prefs =
183         translate_client_->GetTranslatePrefs();
184     std::string auto_target_lang =
185         GetAutoTargetLanguage(language_code, translate_prefs.get());
186     if (!auto_target_lang.empty()) {
187       TranslateBrowserMetrics::ReportInitiationStatus(
188           TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_CONFIG);
189       TranslatePage(language_code, auto_target_lang, false);
190       return;
191     }
192   }
193 
194   std::string auto_translate_to = language_state_.AutoTranslateTo();
195   if (!auto_translate_to.empty()) {
196     // This page was navigated through a click from a translated page.
197     TranslateBrowserMetrics::ReportInitiationStatus(
198         TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_LINK);
199     TranslatePage(language_code, auto_translate_to, false);
200     return;
201   }
202 
203   TranslateBrowserMetrics::ReportInitiationStatus(
204       TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR);
205 
206   // Prompts the user if he/she wants the page translated.
207   translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
208                                      language_code,
209                                      target_lang,
210                                      TranslateErrors::NONE,
211                                      false);
212 }
213 
TranslatePage(const std::string & original_source_lang,const std::string & target_lang,bool triggered_from_menu)214 void TranslateManager::TranslatePage(const std::string& original_source_lang,
215                                      const std::string& target_lang,
216                                      bool triggered_from_menu) {
217   if (!translate_driver_->HasCurrentPage()) {
218     NOTREACHED();
219     return;
220   }
221 
222   // Translation can be kicked by context menu against unsupported languages.
223   // Unsupported language strings should be replaced with
224   // kUnknownLanguageCode in order to send a translation request with enabling
225   // server side auto language detection.
226   std::string source_lang(original_source_lang);
227   if (!TranslateDownloadManager::IsSupportedLanguage(source_lang))
228     source_lang = std::string(translate::kUnknownLanguageCode);
229 
230   translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_TRANSLATING,
231                                      source_lang,
232                                      target_lang,
233                                      TranslateErrors::NONE,
234                                      triggered_from_menu);
235 
236   TranslateScript* script = TranslateDownloadManager::GetInstance()->script();
237   DCHECK(script != NULL);
238 
239   const std::string& script_data = script->data();
240   if (!script_data.empty()) {
241     DoTranslatePage(script_data, source_lang, target_lang);
242     return;
243   }
244 
245   // The script is not available yet.  Queue that request and query for the
246   // script.  Once it is downloaded we'll do the translate.
247   TranslateScript::RequestCallback callback = base::Bind(
248       &TranslateManager::OnTranslateScriptFetchComplete, GetWeakPtr(),
249       translate_driver_->GetCurrentPageID(), source_lang, target_lang);
250 
251   script->Request(callback);
252 }
253 
RevertTranslation()254 void TranslateManager::RevertTranslation() {
255   translate_driver_->RevertTranslation();
256   language_state_.SetCurrentLanguage(language_state_.original_language());
257 }
258 
ReportLanguageDetectionError()259 void TranslateManager::ReportLanguageDetectionError() {
260   TranslateBrowserMetrics::ReportLanguageDetectionError();
261 
262   GURL report_error_url = GURL(kReportLanguageDetectionErrorURL);
263 
264   report_error_url =
265       net::AppendQueryParameter(report_error_url,
266                                 kUrlQueryName,
267                                 translate_driver_->GetActiveURL().spec());
268 
269   report_error_url =
270       net::AppendQueryParameter(report_error_url,
271                                 kSourceLanguageQueryName,
272                                 language_state_.original_language());
273 
274   report_error_url = TranslateURLUtil::AddHostLocaleToUrl(report_error_url);
275   report_error_url = TranslateURLUtil::AddApiKeyToUrl(report_error_url);
276 
277   translate_client_->ShowReportLanguageDetectionErrorUI(report_error_url);
278 }
279 
DoTranslatePage(const std::string & translate_script,const std::string & source_lang,const std::string & target_lang)280 void TranslateManager::DoTranslatePage(const std::string& translate_script,
281                                        const std::string& source_lang,
282                                        const std::string& target_lang) {
283   language_state_.set_translation_pending(true);
284   translate_driver_->TranslatePage(translate_script, source_lang, target_lang);
285 }
286 
PageTranslated(const std::string & source_lang,const std::string & target_lang,TranslateErrors::Type error_type)287 void TranslateManager::PageTranslated(const std::string& source_lang,
288                                       const std::string& target_lang,
289                                       TranslateErrors::Type error_type) {
290   language_state_.SetCurrentLanguage(target_lang);
291   language_state_.set_translation_pending(false);
292 
293   if ((error_type == TranslateErrors::NONE) &&
294       source_lang != translate::kUnknownLanguageCode &&
295       !TranslateDownloadManager::IsSupportedLanguage(source_lang)) {
296     error_type = TranslateErrors::UNSUPPORTED_LANGUAGE;
297   }
298 
299   translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_AFTER_TRANSLATE,
300                                      source_lang,
301                                      target_lang,
302                                      error_type,
303                                      false);
304 
305   if (error_type != TranslateErrors::NONE &&
306       !translate_driver_->IsOffTheRecord()) {
307     TranslateErrorDetails error_details;
308     error_details.time = base::Time::Now();
309     error_details.url = translate_driver_->GetLastCommittedURL();
310     error_details.error = error_type;
311     NotifyTranslateError(error_details);
312   }
313 }
314 
OnTranslateScriptFetchComplete(int page_id,const std::string & source_lang,const std::string & target_lang,bool success,const std::string & data)315 void TranslateManager::OnTranslateScriptFetchComplete(
316     int page_id,
317     const std::string& source_lang,
318     const std::string& target_lang,
319     bool success,
320     const std::string& data) {
321   if (!translate_driver_->HasCurrentPage() ||
322       translate_driver_->GetCurrentPageID() != page_id) {
323     // We navigated away from the page the translation was triggered on.
324     return;
325   }
326 
327   if (success) {
328     // Translate the page.
329     TranslateScript* translate_script =
330         TranslateDownloadManager::GetInstance()->script();
331     DCHECK(translate_script);
332     DoTranslatePage(translate_script->data(), source_lang, target_lang);
333   } else {
334     translate_client_->ShowTranslateUI(
335         translate::TRANSLATE_STEP_TRANSLATE_ERROR,
336         source_lang,
337         target_lang,
338         TranslateErrors::NETWORK,
339         false);
340     if (!translate_driver_->IsOffTheRecord()) {
341       TranslateErrorDetails error_details;
342       error_details.time = base::Time::Now();
343       error_details.url = translate_driver_->GetActiveURL();
344       error_details.error = TranslateErrors::NETWORK;
345       NotifyTranslateError(error_details);
346     }
347   }
348 }
349 
350 // static
GetTargetLanguage(const std::vector<std::string> & accept_languages_list)351 std::string TranslateManager::GetTargetLanguage(
352     const std::vector<std::string>& accept_languages_list) {
353   std::string ui_lang = TranslatePrefs::ConvertLangCodeForTranslation(
354       TranslateDownloadManager::GetLanguageCode(
355           TranslateDownloadManager::GetInstance()->application_locale()));
356 
357   if (TranslateDownloadManager::IsSupportedLanguage(ui_lang))
358     return ui_lang;
359 
360   // Will translate to the first supported language on the Accepted Language
361   // list or not at all if no such candidate exists
362   std::vector<std::string>::const_iterator iter;
363   for (iter = accept_languages_list.begin();
364        iter != accept_languages_list.end(); ++iter) {
365     std::string lang_code = TranslateDownloadManager::GetLanguageCode(*iter);
366     if (TranslateDownloadManager::IsSupportedLanguage(lang_code))
367       return lang_code;
368   }
369   return std::string();
370 }
371 
372 // static
GetAutoTargetLanguage(const std::string & original_language,TranslatePrefs * translate_prefs)373 std::string TranslateManager::GetAutoTargetLanguage(
374     const std::string& original_language,
375     TranslatePrefs* translate_prefs) {
376   std::string auto_target_lang;
377   if (translate_prefs->ShouldAutoTranslate(original_language,
378                                            &auto_target_lang)) {
379     // We need to confirm that the saved target language is still supported.
380     // Also, GetLanguageCode will take care of removing country code if any.
381     auto_target_lang =
382         TranslateDownloadManager::GetLanguageCode(auto_target_lang);
383     if (TranslateDownloadManager::IsSupportedLanguage(auto_target_lang))
384       return auto_target_lang;
385   }
386   return std::string();
387 }
388 
GetLanguageState()389 LanguageState& TranslateManager::GetLanguageState() {
390   return language_state_;
391 }
392