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