Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / translate / core / browser / translate_manager.cc
blobcaafc031a54a96b5c99eb47b4f75e0440b407425
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.
5 #include "components/translate/core/browser/translate_manager.h"
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 "components/translate/core/common/translate_util.h"
32 #include "google_apis/google_api_keys.h"
33 #include "net/base/url_util.h"
34 #include "net/http/http_status_code.h"
36 namespace translate {
38 namespace {
40 // Callbacks for translate errors.
41 TranslateManager::TranslateErrorCallbackList* g_callback_list_ = NULL;
43 const char kReportLanguageDetectionErrorURL[] =
44 "https://translate.google.com/translate_error?client=cr&action=langidc";
46 // Used in kReportLanguageDetectionErrorURL to specify the original page
47 // language.
48 const char kSourceLanguageQueryName[] = "sl";
50 // Used in kReportLanguageDetectionErrorURL to specify the page URL.
51 const char kUrlQueryName[] = "u";
53 // Notifies |g_callback_list_| of translate errors.
54 void NotifyTranslateError(const TranslateErrorDetails& details) {
55 if (!g_callback_list_)
56 return;
58 g_callback_list_->Notify(details);
61 } // namespace
63 TranslateManager::~TranslateManager() {}
65 // static
66 scoped_ptr<TranslateManager::TranslateErrorCallbackList::Subscription>
67 TranslateManager::RegisterTranslateErrorCallback(
68 const TranslateManager::TranslateErrorCallback& callback) {
69 if (!g_callback_list_)
70 g_callback_list_ = new TranslateErrorCallbackList;
71 return g_callback_list_->Add(callback);
74 TranslateManager::TranslateManager(
75 TranslateClient* translate_client,
76 const std::string& accept_languages_pref_name)
77 : page_seq_no_(0),
78 accept_languages_pref_name_(accept_languages_pref_name),
79 translate_client_(translate_client),
80 translate_driver_(translate_client_->GetTranslateDriver()),
81 language_state_(translate_driver_),
82 weak_method_factory_(this) {
85 base::WeakPtr<TranslateManager> TranslateManager::GetWeakPtr() {
86 return weak_method_factory_.GetWeakPtr();
89 void TranslateManager::InitiateTranslation(const std::string& page_lang) {
90 // Short-circuit out if not in a state where initiating translation makes
91 // sense (this method may be called muhtiple times for a given page).
92 if (!language_state_.page_needs_translation() ||
93 language_state_.translation_pending() ||
94 language_state_.translation_declined() ||
95 language_state_.IsPageTranslated()) {
96 return;
99 if (!ignore_missing_key_for_testing_ &&
100 !::google_apis::HasKeysConfigured()) {
101 // Without an API key, translate won't work, so don't offer to translate
102 // in the first place. Leave prefs::kEnableTranslate on, though, because
103 // that settings syncs and we don't want to turn off translate everywhere
104 // else.
105 TranslateBrowserMetrics::ReportInitiationStatus(
106 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_KEY);
107 return;
110 PrefService* prefs = translate_client_->GetPrefs();
111 if (!prefs->GetBoolean(prefs::kEnableTranslate)) {
112 TranslateBrowserMetrics::ReportInitiationStatus(
113 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_PREFS);
114 const std::string& locale =
115 TranslateDownloadManager::GetInstance()->application_locale();
116 TranslateBrowserMetrics::ReportLocalesOnDisabledByPrefs(locale);
117 return;
120 // Allow disabling of translate from the command line to assist with
121 // automated browser testing.
122 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
123 translate::switches::kDisableTranslate)) {
124 TranslateBrowserMetrics::ReportInitiationStatus(
125 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_SWITCH);
126 return;
129 // MHTML pages currently cannot be translated.
130 // See bug: 217945.
131 if (translate_driver_->GetContentsMimeType() == "multipart/related") {
132 TranslateBrowserMetrics::ReportInitiationStatus(
133 TranslateBrowserMetrics::INITIATION_STATUS_MIME_TYPE_IS_NOT_SUPPORTED);
134 return;
137 // Don't translate any Chrome specific page, e.g., New Tab Page, Download,
138 // History, and so on.
139 const GURL& page_url = translate_driver_->GetVisibleURL();
140 if (!translate_client_->IsTranslatableURL(page_url)) {
141 TranslateBrowserMetrics::ReportInitiationStatus(
142 TranslateBrowserMetrics::INITIATION_STATUS_URL_IS_NOT_SUPPORTED);
143 return;
146 // Get the accepted languages list.
147 std::vector<std::string> accept_languages_list = base::SplitString(
148 prefs->GetString(accept_languages_pref_name_), ",",
149 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
151 std::string target_lang = GetTargetLanguage(accept_languages_list);
152 std::string language_code =
153 TranslateDownloadManager::GetLanguageCode(page_lang);
155 // Don't translate similar languages (ex: en-US to en).
156 if (language_code == target_lang) {
157 TranslateBrowserMetrics::ReportInitiationStatus(
158 TranslateBrowserMetrics::INITIATION_STATUS_SIMILAR_LANGUAGES);
159 return;
162 // Nothing to do if either the language Chrome is in or the language of the
163 // page is not supported by the translation server.
164 if (target_lang.empty() ||
165 !TranslateDownloadManager::IsSupportedLanguage(language_code)) {
166 TranslateBrowserMetrics::ReportInitiationStatus(
167 TranslateBrowserMetrics::INITIATION_STATUS_LANGUAGE_IS_NOT_SUPPORTED);
168 TranslateBrowserMetrics::ReportUnsupportedLanguageAtInitiation(
169 language_code);
170 return;
173 scoped_ptr<TranslatePrefs> translate_prefs(
174 translate_client_->GetTranslatePrefs());
176 TranslateAcceptLanguages* accept_languages =
177 translate_client_->GetTranslateAcceptLanguages();
178 // Don't translate any user black-listed languages.
179 if (!translate_prefs->CanTranslateLanguage(accept_languages,
180 language_code)) {
181 TranslateBrowserMetrics::ReportInitiationStatus(
182 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
183 return;
186 // Don't translate any user black-listed URLs.
187 if (translate_prefs->IsSiteBlacklisted(page_url.HostNoBrackets())) {
188 TranslateBrowserMetrics::ReportInitiationStatus(
189 TranslateBrowserMetrics::INITIATION_STATUS_DISABLED_BY_CONFIG);
190 return;
193 // If the user has previously selected "always translate" for this language we
194 // automatically translate. Note that in incognito mode we disable that
195 // feature; the user will get an infobar, so they can control whether the
196 // page's text is sent to the translate server.
197 if (!translate_driver_->IsOffTheRecord()) {
198 scoped_ptr<TranslatePrefs> translate_prefs =
199 translate_client_->GetTranslatePrefs();
200 std::string auto_target_lang =
201 GetAutoTargetLanguage(language_code, translate_prefs.get());
202 if (!auto_target_lang.empty()) {
203 TranslateBrowserMetrics::ReportInitiationStatus(
204 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_CONFIG);
205 TranslatePage(language_code, auto_target_lang, false);
206 return;
210 std::string auto_translate_to = language_state_.AutoTranslateTo();
211 if (!auto_translate_to.empty()) {
212 // This page was navigated through a click from a translated page.
213 TranslateBrowserMetrics::ReportInitiationStatus(
214 TranslateBrowserMetrics::INITIATION_STATUS_AUTO_BY_LINK);
215 TranslatePage(language_code, auto_translate_to, false);
216 return;
219 TranslateBrowserMetrics::ReportInitiationStatus(
220 TranslateBrowserMetrics::INITIATION_STATUS_SHOW_INFOBAR);
222 // Prompts the user if they want the page translated.
223 translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
224 language_code,
225 target_lang,
226 TranslateErrors::NONE,
227 false);
230 void TranslateManager::TranslatePage(const std::string& original_source_lang,
231 const std::string& target_lang,
232 bool triggered_from_menu) {
233 if (!translate_driver_->HasCurrentPage()) {
234 NOTREACHED();
235 return;
238 // Translation can be kicked by context menu against unsupported languages.
239 // Unsupported language strings should be replaced with
240 // kUnknownLanguageCode in order to send a translation request with enabling
241 // server side auto language detection.
242 std::string source_lang(original_source_lang);
243 if (!TranslateDownloadManager::IsSupportedLanguage(source_lang))
244 source_lang = std::string(translate::kUnknownLanguageCode);
246 translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_TRANSLATING,
247 source_lang,
248 target_lang,
249 TranslateErrors::NONE,
250 triggered_from_menu);
252 TranslateScript* script = TranslateDownloadManager::GetInstance()->script();
253 DCHECK(script != NULL);
255 const std::string& script_data = script->data();
256 if (!script_data.empty()) {
257 DoTranslatePage(script_data, source_lang, target_lang);
258 return;
261 // The script is not available yet. Queue that request and query for the
262 // script. Once it is downloaded we'll do the translate.
263 TranslateScript::RequestCallback callback = base::Bind(
264 &TranslateManager::OnTranslateScriptFetchComplete, GetWeakPtr(),
265 source_lang, target_lang);
267 script->Request(callback);
270 void TranslateManager::RevertTranslation() {
271 translate_driver_->RevertTranslation(page_seq_no_);
272 language_state_.SetCurrentLanguage(language_state_.original_language());
275 void TranslateManager::ReportLanguageDetectionError() {
276 TranslateBrowserMetrics::ReportLanguageDetectionError();
278 GURL report_error_url = GURL(kReportLanguageDetectionErrorURL);
280 report_error_url = net::AppendQueryParameter(
281 report_error_url, kUrlQueryName,
282 translate_driver_->GetLastCommittedURL().spec());
284 report_error_url =
285 net::AppendQueryParameter(report_error_url,
286 kSourceLanguageQueryName,
287 language_state_.original_language());
289 report_error_url = translate::AddHostLocaleToUrl(report_error_url);
290 report_error_url = translate::AddApiKeyToUrl(report_error_url);
292 translate_client_->ShowReportLanguageDetectionErrorUI(report_error_url);
295 void TranslateManager::DoTranslatePage(const std::string& translate_script,
296 const std::string& source_lang,
297 const std::string& target_lang) {
298 language_state_.set_translation_pending(true);
299 translate_driver_->TranslatePage(
300 page_seq_no_, translate_script, source_lang, target_lang);
303 void TranslateManager::PageTranslated(const std::string& source_lang,
304 const std::string& target_lang,
305 TranslateErrors::Type error_type) {
306 language_state_.SetCurrentLanguage(target_lang);
307 language_state_.set_translation_pending(false);
309 if ((error_type == TranslateErrors::NONE) &&
310 source_lang != translate::kUnknownLanguageCode &&
311 !TranslateDownloadManager::IsSupportedLanguage(source_lang)) {
312 error_type = TranslateErrors::UNSUPPORTED_LANGUAGE;
315 translate_client_->ShowTranslateUI(translate::TRANSLATE_STEP_AFTER_TRANSLATE,
316 source_lang,
317 target_lang,
318 error_type,
319 false);
321 if (error_type != TranslateErrors::NONE &&
322 !translate_driver_->IsOffTheRecord()) {
323 TranslateErrorDetails error_details;
324 error_details.time = base::Time::Now();
325 error_details.url = translate_driver_->GetLastCommittedURL();
326 error_details.error = error_type;
327 NotifyTranslateError(error_details);
331 void TranslateManager::OnTranslateScriptFetchComplete(
332 const std::string& source_lang,
333 const std::string& target_lang,
334 bool success,
335 const std::string& data) {
336 if (!translate_driver_->HasCurrentPage())
337 return;
339 if (success) {
340 // Translate the page.
341 TranslateScript* translate_script =
342 TranslateDownloadManager::GetInstance()->script();
343 DCHECK(translate_script);
344 DoTranslatePage(translate_script->data(), source_lang, target_lang);
345 } else {
346 translate_client_->ShowTranslateUI(
347 translate::TRANSLATE_STEP_TRANSLATE_ERROR,
348 source_lang,
349 target_lang,
350 TranslateErrors::NETWORK,
351 false);
352 if (!translate_driver_->IsOffTheRecord()) {
353 TranslateErrorDetails error_details;
354 error_details.time = base::Time::Now();
355 error_details.url = translate_driver_->GetLastCommittedURL();
356 error_details.error = TranslateErrors::NETWORK;
357 NotifyTranslateError(error_details);
362 // static
363 std::string TranslateManager::GetTargetLanguage(
364 const std::vector<std::string>& accept_languages_list) {
365 std::string ui_lang = TranslateDownloadManager::GetLanguageCode(
366 TranslateDownloadManager::GetInstance()->application_locale());
367 translate::ToTranslateLanguageSynonym(&ui_lang);
369 if (TranslateDownloadManager::IsSupportedLanguage(ui_lang))
370 return ui_lang;
372 // Will translate to the first supported language on the Accepted Language
373 // list or not at all if no such candidate exists
374 std::vector<std::string>::const_iterator iter;
375 for (iter = accept_languages_list.begin();
376 iter != accept_languages_list.end(); ++iter) {
377 std::string lang_code = TranslateDownloadManager::GetLanguageCode(*iter);
378 if (TranslateDownloadManager::IsSupportedLanguage(lang_code))
379 return lang_code;
381 return std::string();
384 // static
385 std::string TranslateManager::GetAutoTargetLanguage(
386 const std::string& original_language,
387 TranslatePrefs* translate_prefs) {
388 std::string auto_target_lang;
389 if (translate_prefs->ShouldAutoTranslate(original_language,
390 &auto_target_lang)) {
391 // We need to confirm that the saved target language is still supported.
392 // Also, GetLanguageCode will take care of removing country code if any.
393 auto_target_lang =
394 TranslateDownloadManager::GetLanguageCode(auto_target_lang);
395 if (TranslateDownloadManager::IsSupportedLanguage(auto_target_lang))
396 return auto_target_lang;
398 return std::string();
401 LanguageState& TranslateManager::GetLanguageState() {
402 return language_state_;
405 bool TranslateManager::ignore_missing_key_for_testing_ = false;
407 // static
408 void TranslateManager::SetIgnoreMissingKeyForTesting(bool ignore) {
409 ignore_missing_key_for_testing_ = ignore;
412 } // namespace translate