| 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 |