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