Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(267)

Side by Side Diff: chrome/browser/translate/translate_manager2.cc

Issue 2602003: Refactored the translate infobars. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Synced Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2010 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_manager2.h"
6
7 #include "app/resource_bundle.h"
8 #include "base/compiler_specific.h"
9 #include "base/string_util.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/pref_service.h"
12 #include "chrome/browser/profile.h"
13 #include "chrome/browser/renderer_host/render_process_host.h"
14 #include "chrome/browser/renderer_host/render_view_host.h"
15 #include "chrome/browser/tab_contents/language_state.h"
16 #include "chrome/browser/tab_contents/navigation_controller.h"
17 #include "chrome/browser/tab_contents/navigation_entry.h"
18 #include "chrome/browser/tab_contents/tab_contents.h"
19 #include "chrome/browser/tab_contents/tab_util.h"
20 #include "chrome/browser/translate/page_translated_details.h"
21 #include "chrome/browser/translate/translate_infobar_delegate2.h"
22 #include "chrome/browser/translate/translate_prefs.h"
23 #include "chrome/common/notification_details.h"
24 #include "chrome/common/notification_service.h"
25 #include "chrome/common/notification_source.h"
26 #include "chrome/common/notification_type.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/common/translate_errors.h"
29 #include "grit/browser_resources.h"
30 #include "net/url_request/url_request_status.h"
31
32 namespace {
33
34 // Mapping from a locale name to a language code name.
35 // Locale names not included are translated as is.
36 struct LocaleToCLDLanguage {
37 const char* locale_language; // Language Chrome locale is in.
38 const char* cld_language; // Language the CLD reports.
39 };
40 LocaleToCLDLanguage kLocaleToCLDLanguages[] = {
41 { "en-GB", "en" },
42 { "en-US", "en" },
43 { "es-419", "es" },
44 { "pt-BR", "pt" },
45 { "pt-PT", "pt" },
46 };
47
48 // The list of languages the Google translation server supports.
49 // For information, here is the list of languages that Chrome can be run in
50 // but that the translation server does not support:
51 // am Amharic
52 // bn Bengali
53 // gu Gujarati
54 // kn Kannada
55 // ml Malayalam
56 // mr Marathi
57 // ta Tamil
58 // te Telugu
59 const char* kSupportedLanguages[] = {
60 "af", // Afrikaans
61 "sq", // Albanian
62 "ar", // Arabic
63 "be", // Belarusian
64 "bg", // Bulgarian
65 "ca", // Catalan
66 "zh-CN", // Chinese (Simplified)
67 "zh-TW", // Chinese (Traditional)
68 "hr", // Croatian
69 "cs", // Czech
70 "da", // Danish
71 "nl", // Dutch
72 "en", // English
73 "et", // Estonian
74 "fi", // Finnish
75 "fil", // Filipino
76 "fr", // French
77 "gl", // Galician
78 "de", // German
79 "el", // Greek
80 "he", // Hebrew
81 "hi", // Hindi
82 "hu", // Hungarian
83 "is", // Icelandic
84 "id", // Indonesian
85 "it", // Italian
86 "ga", // Irish
87 "ja", // Japanese
88 "ko", // Korean
89 "lv", // Latvian
90 "lt", // Lithuanian
91 "mk", // Macedonian
92 "ms", // Malay
93 "mt", // Maltese
94 "nb", // Norwegian
95 "fa", // Persian
96 "pl", // Polish
97 "pt", // Portuguese
98 "ro", // Romanian
99 "ru", // Russian
100 "sr", // Serbian
101 "sk", // Slovak
102 "sl", // Slovenian
103 "es", // Spanish
104 "sw", // Swahili
105 "sv", // Swedish
106 "th", // Thai
107 "tr", // Turkish
108 "uk", // Ukrainian
109 "vi", // Vietnamese
110 "cy", // Welsh
111 "yi", // Yiddish
112 };
113
114 const char* const kTranslateScriptURL =
115 "http://translate.google.com/translate_a/element.js?"
116 "cb=cr.googleTranslate.onTranslateElementLoad";
117 const char* const kTranslateScriptHeader =
118 "Google-Translate-Element-Mode: library";
119
120 } // namespace
121
122 // static
123 base::LazyInstance<std::set<std::string> >
124 TranslateManager2::supported_languages_(base::LINKER_INITIALIZED);
125
126 TranslateManager2::~TranslateManager2() {
127 }
128
129 // static
130 bool TranslateManager2::IsTranslatableURL(const GURL& url) {
131 return !url.SchemeIs("chrome");
132 }
133
134 // static
135 void TranslateManager2::GetSupportedLanguages(
136 std::vector<std::string>* languages) {
137 DCHECK(languages && languages->empty());
138 for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
139 languages->push_back(kSupportedLanguages[i]);
140 }
141
142 // static
143 std::string TranslateManager2::GetLanguageCode(
144 const std::string& chrome_locale) {
145 for (size_t i = 0; i < arraysize(kLocaleToCLDLanguages); ++i) {
146 if (chrome_locale == kLocaleToCLDLanguages[i].locale_language)
147 return kLocaleToCLDLanguages[i].cld_language;
148 }
149 return chrome_locale;
150 }
151
152 // static
153 bool TranslateManager2::IsSupportedLanguage(const std::string& page_language) {
154 if (supported_languages_.Pointer()->empty()) {
155 for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
156 supported_languages_.Pointer()->insert(kSupportedLanguages[i]);
157 }
158 return supported_languages_.Pointer()->find(page_language) !=
159 supported_languages_.Pointer()->end();
160 }
161
162 void TranslateManager2::Observe(NotificationType type,
163 const NotificationSource& source,
164 const NotificationDetails& details) {
165 switch (type.value) {
166 case NotificationType::NAV_ENTRY_COMMITTED: {
167 NavigationController* controller =
168 Source<NavigationController>(source).ptr();
169 NavigationController::LoadCommittedDetails* load_details =
170 Details<NavigationController::LoadCommittedDetails>(details).ptr();
171 NavigationEntry* entry = controller->GetActiveEntry();
172 if (!entry) {
173 NOTREACHED();
174 return;
175 }
176 if (entry->transition_type() != PageTransition::RELOAD &&
177 load_details->type != NavigationType::SAME_PAGE) {
178 return;
179 }
180 // When doing a page reload, we don't get a TAB_LANGUAGE_DETERMINED
181 // notification. So we need to explictly initiate the translation.
182 // Note that we delay it as the TranslateManager2 gets this notification
183 // before the TabContents and the TabContents processing might remove the
184 // current infobars. Since InitTranslation might add an infobar, it must
185 // be done after that.
186 MessageLoop::current()->PostTask(FROM_HERE,
187 method_factory_.NewRunnableMethod(
188 &TranslateManager2::InitiateTranslationPosted,
189 controller->tab_contents()->render_view_host()->process()->id(),
190 controller->tab_contents()->render_view_host()->routing_id(),
191 controller->tab_contents()->language_state().
192 original_language()));
193 break;
194 }
195 case NotificationType::TAB_LANGUAGE_DETERMINED: {
196 TabContents* tab = Source<TabContents>(source).ptr();
197 // We may get this notifications multiple times. Make sure to translate
198 // only once.
199 LanguageState& language_state = tab->language_state();
200 if (!language_state.translation_pending() &&
201 !language_state.translation_declined() &&
202 !language_state.IsPageTranslated()) {
203 std::string language = *(Details<std::string>(details).ptr());
204 InitiateTranslation(tab, language);
205 }
206 break;
207 }
208 case NotificationType::PAGE_TRANSLATED: {
209 // Only add translate infobar if it doesn't exist; if it already exists,
210 // just update the state, the actual infobar would have received the same
211 // notification and update the visual display accordingly.
212 TabContents* tab = Source<TabContents>(source).ptr();
213 PageTranslatedDetails* page_translated_details =
214 Details<PageTranslatedDetails>(details).ptr();
215 PageTranslated(tab, page_translated_details);
216 break;
217 }
218 case NotificationType::PROFILE_DESTROYED: {
219 Profile* profile = Source<Profile>(source).ptr();
220 notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED,
221 source);
222 size_t count = accept_languages_.erase(profile->GetPrefs());
223 // We should know about this profile since we are listening for
224 // notifications on it.
225 DCHECK(count > 0);
226 profile->GetPrefs()->RemovePrefObserver(prefs::kAcceptLanguages, this);
227 break;
228 }
229 case NotificationType::PREF_CHANGED: {
230 DCHECK(*Details<std::wstring>(details).ptr() == prefs::kAcceptLanguages);
231 PrefService* prefs = Source<PrefService>(source).ptr();
232 InitAcceptLanguages(prefs);
233 break;
234 }
235 default:
236 NOTREACHED();
237 }
238 }
239
240 void TranslateManager2::OnURLFetchComplete(const URLFetcher* source,
241 const GURL& url,
242 const URLRequestStatus& status,
243 int response_code,
244 const ResponseCookies& cookies,
245 const std::string& data) {
246 scoped_ptr<const URLFetcher> delete_ptr(source);
247 DCHECK(translate_script_request_pending_);
248 translate_script_request_pending_ = false;
249 bool error =
250 (status.status() != URLRequestStatus::SUCCESS || response_code != 200);
251
252 if (!error) {
253 base::StringPiece str = ResourceBundle::GetSharedInstance().
254 GetRawDataResource(IDR_TRANSLATE_JS);
255 DCHECK(translate_script_.empty());
256 str.CopyToString(&translate_script_);
257 translate_script_ += "\n" + data;
258 }
259
260 // Process any pending requests.
261 std::vector<PendingRequest>::const_iterator iter;
262 for (iter = pending_requests_.begin(); iter != pending_requests_.end();
263 ++iter) {
264 const PendingRequest& request = *iter;
265 TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id,
266 request.render_view_id);
267 if (!tab) {
268 // The tab went away while we were retrieving the script.
269 continue;
270 }
271 NavigationEntry* entry = tab->controller().GetActiveEntry();
272 if (!entry || entry->page_id() != request.page_id) {
273 // We navigated away from the page the translation was triggered on.
274 continue;
275 }
276
277 if (error) {
278 ShowInfoBar(tab,
279 TranslateInfoBarDelegate2::CreateInstance(
280 TranslateInfoBarDelegate2::TRANSLATION_ERROR,
281 TranslateErrors::NETWORK,
282 tab, request.source_lang, request.target_lang));
283 } else {
284 // Translate the page.
285 DoTranslatePage(tab, translate_script_,
286 request.source_lang, request.target_lang);
287 }
288 }
289 pending_requests_.clear();
290 }
291
292 // static
293 bool TranslateManager2::IsShowingTranslateInfobar(TabContents* tab) {
294 return GetTranslateInfoBarDelegate2(tab) != NULL;
295 }
296
297 TranslateManager2::TranslateManager2()
298 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
299 translate_script_request_pending_(false) {
300 notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
301 NotificationService::AllSources());
302 notification_registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
303 NotificationService::AllSources());
304 notification_registrar_.Add(this, NotificationType::PAGE_TRANSLATED,
305 NotificationService::AllSources());
306 }
307
308 void TranslateManager2::InitiateTranslation(TabContents* tab,
309 const std::string& page_lang) {
310 PrefService* prefs = tab->profile()->GetPrefs();
311 if (!prefs->GetBoolean(prefs::kEnableTranslate))
312 return;
313
314 NavigationEntry* entry = tab->controller().GetActiveEntry();
315 if (!entry) {
316 // This can happen for popups created with window.open("").
317 return;
318 }
319
320 // If there is already a translate infobar showing, don't show another one.
321 if (GetTranslateInfoBarDelegate2(tab))
322 return;
323
324 std::string target_lang = GetTargetLanguage();
325 // Nothing to do if either the language Chrome is in or the language of the
326 // page is not supported by the translation server.
327 if (target_lang.empty() || !IsSupportedLanguage(page_lang)) {
328 return;
329 }
330
331 // We don't want to translate:
332 // - any Chrome specific page (New Tab Page, Download, History... pages).
333 // - similar languages (ex: en-US to en).
334 // - any user black-listed URLs or user selected language combination.
335 // - any language the user configured as accepted languages.
336 if (!IsTranslatableURL(entry->url()) || page_lang == target_lang ||
337 !TranslatePrefs::CanTranslate(prefs, page_lang, entry->url()) ||
338 IsAcceptLanguage(tab, page_lang)) {
339 return;
340 }
341
342 // If the user has previously selected "always translate" for this language we
343 // automatically translate. Note that in incognito mode we disable that
344 // feature; the user will get an infobar, so they can control whether the
345 // page's text is sent to the translate server.
346 std::string auto_target_lang;
347 if (!tab->profile()->IsOffTheRecord() &&
348 TranslatePrefs::ShouldAutoTranslate(prefs, page_lang,
349 &auto_target_lang)) {
350 TranslatePage(tab, page_lang, auto_target_lang);
351 return;
352 }
353
354 std::string auto_translate_to = tab->language_state().AutoTranslateTo();
355 if (!auto_translate_to.empty()) {
356 // This page was navigated through a click from a translated page.
357 TranslatePage(tab, page_lang, auto_translate_to);
358 return;
359 }
360
361 // Prompts the user if he/she wants the page translated.
362 tab->AddInfoBar(TranslateInfoBarDelegate2::CreateInstance(
363 TranslateInfoBarDelegate2::BEFORE_TRANSLATE,
364 TranslateErrors::NONE, tab, page_lang, target_lang));
365 }
366
367 void TranslateManager2::InitiateTranslationPosted(
368 int process_id, int render_id, const std::string& page_lang) {
369 // The tab might have been closed.
370 TabContents* tab = tab_util::GetTabContentsByID(process_id, render_id);
371 if (!tab || tab->language_state().translation_pending())
372 return;
373
374 InitiateTranslation(tab, page_lang);
375 }
376
377 void TranslateManager2::TranslatePage(TabContents* tab_contents,
378 const std::string& source_lang,
379 const std::string& target_lang) {
380 NavigationEntry* entry = tab_contents->controller().GetActiveEntry();
381 if (!entry) {
382 NOTREACHED();
383 return;
384 }
385 if (!translate_script_.empty()) {
386 DoTranslatePage(tab_contents, translate_script_, source_lang, target_lang);
387 return;
388 }
389
390 // The script is not available yet. Queue that request and query for the
391 // script. Once it is downloaded we'll do the translate.
392 RenderViewHost* rvh = tab_contents->render_view_host();
393 PendingRequest request;
394 request.render_process_id = rvh->process()->id();
395 request.render_view_id = rvh->routing_id();
396 request.page_id = entry->page_id();
397 request.source_lang = source_lang;
398 request.target_lang = target_lang;
399 pending_requests_.push_back(request);
400 RequestTranslateScript();
401 }
402
403 void TranslateManager2::RevertTranslation(TabContents* tab_contents) {
404 NavigationEntry* entry = tab_contents->controller().GetActiveEntry();
405 if (!entry) {
406 NOTREACHED();
407 return;
408 }
409 tab_contents->render_view_host()->RevertTranslation(entry->page_id());
410 tab_contents->language_state().set_current_language(
411 tab_contents->language_state().original_language());
412 }
413
414 void TranslateManager2::DoTranslatePage(TabContents* tab,
415 const std::string& translate_script,
416 const std::string& source_lang,
417 const std::string& target_lang) {
418 NavigationEntry* entry = tab->controller().GetActiveEntry();
419 if (!entry) {
420 NOTREACHED();
421 return;
422 }
423
424 TranslateInfoBarDelegate2* infobar = GetTranslateInfoBarDelegate2(tab);
425 if (infobar) {
426 // We don't show the translating infobar if no translate infobar is already
427 // showing (that is the case when the translation was triggered by the
428 // "always translate" for example).
429 infobar = TranslateInfoBarDelegate2::CreateInstance(
430 TranslateInfoBarDelegate2::TRANSLATING, TranslateErrors::NONE,
431 tab, source_lang, target_lang);
432 ShowInfoBar(tab, infobar);
433 }
434 tab->language_state().set_translation_pending(true);
435 tab->render_view_host()->TranslatePage(entry->page_id(), translate_script,
436 source_lang, target_lang);
437 }
438
439 void TranslateManager2::PageTranslated(TabContents* tab,
440 PageTranslatedDetails* details) {
441 // Create the new infobar to display.
442 TranslateInfoBarDelegate2* infobar;
443 if (details->error_type != TranslateErrors::NONE) {
444 infobar = TranslateInfoBarDelegate2::CreateInstance(
445 TranslateInfoBarDelegate2::TRANSLATION_ERROR, details->error_type,
446 tab, details->source_language, details->target_language);
447 } else {
448 infobar = TranslateInfoBarDelegate2::CreateInstance(
449 TranslateInfoBarDelegate2::AFTER_TRANSLATE, TranslateErrors::NONE,
450 tab, details->source_language, details->target_language);
451 }
452 ShowInfoBar(tab, infobar);
453 }
454
455 bool TranslateManager2::IsAcceptLanguage(TabContents* tab,
456 const std::string& language) {
457 PrefService* pref_service = tab->profile()->GetPrefs();
458 PrefServiceLanguagesMap::const_iterator iter =
459 accept_languages_.find(pref_service);
460 if (iter == accept_languages_.end()) {
461 InitAcceptLanguages(pref_service);
462 // Listen for this profile going away, in which case we would need to clear
463 // the accepted languages for the profile.
464 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
465 Source<Profile>(tab->profile()));
466 // Also start listening for changes in the accept languages.
467 tab->profile()->GetPrefs()->AddPrefObserver(prefs::kAcceptLanguages, this);
468
469 iter = accept_languages_.find(pref_service);
470 }
471
472 return iter->second.count(language) != 0;
473 }
474
475 void TranslateManager2::InitAcceptLanguages(PrefService* prefs) {
476 // We have been asked for this profile, build the languages.
477 std::wstring accept_langs_str = prefs->GetString(prefs::kAcceptLanguages);
478 std::vector<std::string> accept_langs_list;
479 LanguageSet accept_langs_set;
480 SplitString(WideToASCII(accept_langs_str), ',', &accept_langs_list);
481 std::vector<std::string>::const_iterator iter;
482 std::string ui_lang =
483 GetLanguageCode(g_browser_process->GetApplicationLocale());
484 bool is_ui_english = StartsWithASCII(ui_lang, "en-", false);
485 for (iter = accept_langs_list.begin();
486 iter != accept_langs_list.end(); ++iter) {
487 // Get rid of the locale extension if any (ex: en-US -> en), but for Chinese
488 // for which the CLD reports zh-CN and zh-TW.
489 std::string accept_lang(*iter);
490 size_t index = iter->find("-");
491 if (index != std::string::npos && *iter != "zh-CN" && *iter != "zh-TW")
492 accept_lang = iter->substr(0, index);
493 // Special-case English until we resolve bug 36182 properly.
494 // Add English only if the UI language is not English. This will annoy
495 // users of non-English Chrome who can comprehend English until English is
496 // black-listed.
497 // TODO(jungshik): Once we determine that it's safe to remove English from
498 // the default Accept-Language values for most locales, remove this
499 // special-casing.
500 if (accept_lang != "en" || is_ui_english)
501 accept_langs_set.insert(accept_lang);
502 }
503 accept_languages_[prefs] = accept_langs_set;
504 }
505
506 void TranslateManager2::RequestTranslateScript() {
507 if (translate_script_request_pending_)
508 return;
509
510 translate_script_request_pending_ = true;
511 URLFetcher* fetcher = URLFetcher::Create(0, GURL(kTranslateScriptURL),
512 URLFetcher::GET, this);
513 fetcher->set_request_context(Profile::GetDefaultRequestContext());
514 fetcher->set_extra_request_headers(kTranslateScriptHeader);
515 fetcher->Start();
516 }
517
518 void TranslateManager2::ShowInfoBar(TabContents* tab,
519 TranslateInfoBarDelegate2* infobar) {
520 TranslateInfoBarDelegate2* old_infobar = GetTranslateInfoBarDelegate2(tab);
521 infobar->UpdateBackgroundAnimation(old_infobar);
522 if (old_infobar) {
523 // There already is a translate infobar, simply replace it.
524 tab->ReplaceInfoBar(old_infobar, infobar);
525 } else {
526 tab->AddInfoBar(infobar);
527 }
528 }
529
530 // static
531 std::string TranslateManager2::GetTargetLanguage() {
532 std::string target_lang =
533 GetLanguageCode(g_browser_process->GetApplicationLocale());
534 if (IsSupportedLanguage(target_lang))
535 return target_lang;
536 return std::string();
537 }
538
539 // static
540 TranslateInfoBarDelegate2* TranslateManager2::GetTranslateInfoBarDelegate2(
541 TabContents* tab) {
542 for (int i = 0; i < tab->infobar_delegate_count(); ++i) {
543 TranslateInfoBarDelegate2* delegate =
544 tab->GetInfoBarDelegateAt(i)->AsTranslateInfoBarDelegate2();
545 if (delegate)
546 return delegate;
547 }
548 return NULL;
549 }
OLDNEW
« no previous file with comments | « chrome/browser/translate/translate_manager2.h ('k') | chrome/browser/translate/translate_manager2_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698