chrome_translate_client.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// Copyright 2014 The Chromium Authors. All rights reserved.
2b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// Use of this source code is governed by a BSD-style license that can be
3b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// found in the LICENSE file.
4b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
5b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/translate/chrome_translate_client.h"
6b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
7b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include <vector>
8b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
9b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "base/logging.h"
10b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "base/prefs/pref_service.h"
11b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "base/strings/string_split.h"
12b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/chrome_notification_types.h"
13b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/infobars/infobar_service.h"
14b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/profiles/profile.h"
15b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/translate/translate_accept_languages_factory.h"
16b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/translate/translate_service.h"
17b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/ui/browser.h"
18b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/ui/browser_finder.h"
19b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/ui/browser_tabstrip.h"
20b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/ui/browser_window.h"
21b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/ui/tabs/tab_strip_model.h"
22b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/ui/translate/translate_bubble_factory.h"
23b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/common/pref_names.h"
24b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/content/common/translate_messages.h"
25b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/core/browser/page_translated_details.h"
26b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/core/browser/translate_accept_languages.h"
27b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/core/browser/translate_download_manager.h"
28b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/core/browser/translate_infobar_delegate.h"
29b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/core/browser/translate_manager.h"
30b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/core/browser/translate_prefs.h"
31b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "components/translate/core/common/language_detection_details.h"
32b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "content/public/browser/navigation_details.h"
33b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "content/public/browser/navigation_entry.h"
34b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "content/public/browser/notification_service.h"
35b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "content/public/browser/render_view_host.h"
36b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "content/public/browser/web_contents.h"
37b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "grit/theme_resources.h"
38b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "net/http/http_status_code.h"
39b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "url/gurl.h"
40b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
41b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#if defined(CLD2_DYNAMIC_MODE)
42b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "base/files/file.h"
43b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "base/path_service.h"
44b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/common/chrome_constants.h"
45b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/common/chrome_paths.h"
46b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "content/public/browser/browser_thread.h"
47b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "content/public/browser/render_process_host.h"
48b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#endif
49b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
50b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#if defined(CLD2_IS_COMPONENT)
51b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#include "chrome/browser/component_updater/cld_component_installer.h"
52b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#endif
53b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
54b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonnamespace {
55b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
56b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// The maximum number of attempts we'll do to see if the page has finshed
57b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// loading before giving up the translation
58b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonconst int kMaxTranslateLoadCheckAttempts = 20;
59b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
60b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}  // namespace
61b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
62b0114cb9f332db144f65291211ae65f7f0e814e6Scott AndersonDEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromeTranslateClient);
63b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
64b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#if defined(CLD2_DYNAMIC_MODE)
65b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// Statics defined in the .h file:
66b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonbase::File* ChromeTranslateClient::s_cached_file_ = NULL;
67b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonuint64 ChromeTranslateClient::s_cached_data_offset_ = 0;
68b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonuint64 ChromeTranslateClient::s_cached_data_length_ = 0;
69b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonbase::LazyInstance<base::Lock> ChromeTranslateClient::s_file_lock_ =
70b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    LAZY_INSTANCE_INITIALIZER;
71b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#endif
72b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
73b0114cb9f332db144f65291211ae65f7f0e814e6Scott AndersonChromeTranslateClient::ChromeTranslateClient(content::WebContents* web_contents)
74b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    : content::WebContentsObserver(web_contents),
75b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts),
76b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      translate_driver_(&web_contents->GetController()),
77b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      translate_manager_(new TranslateManager(this, prefs::kAcceptLanguages)),
78b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      weak_pointer_factory_(this) {
79b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
80b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
81b0114cb9f332db144f65291211ae65f7f0e814e6Scott AndersonChromeTranslateClient::~ChromeTranslateClient() {
82b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
83b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
84b0114cb9f332db144f65291211ae65f7f0e814e6Scott AndersonLanguageState& ChromeTranslateClient::GetLanguageState() {
85b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  return translate_driver_.GetLanguageState();
86b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
87b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
88b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// static
89b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonscoped_ptr<TranslatePrefs> ChromeTranslateClient::CreateTranslatePrefs(
90b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    PrefService* prefs) {
91b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#if defined(OS_CHROMEOS)
92b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  const char* preferred_languages_prefs = prefs::kLanguagePreferredLanguages;
93b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#else
94b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  const char* preferred_languages_prefs = NULL;
95b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson#endif
96b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  return scoped_ptr<TranslatePrefs>(new TranslatePrefs(
97b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      prefs, prefs::kAcceptLanguages, preferred_languages_prefs));
98b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
99b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
100b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// static
101b0114cb9f332db144f65291211ae65f7f0e814e6Scott AndersonTranslateAcceptLanguages* ChromeTranslateClient::GetTranslateAcceptLanguages(
102b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    content::BrowserContext* browser_context) {
103b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  return TranslateAcceptLanguagesFactory::GetForBrowserContext(browser_context);
104b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
105b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
106b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// static
107b0114cb9f332db144f65291211ae65f7f0e814e6Scott AndersonTranslateManager* ChromeTranslateClient::GetManagerFromWebContents(
108b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    content::WebContents* web_contents) {
109b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  ChromeTranslateClient* chrome_translate_client =
110b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      FromWebContents(web_contents);
111b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  if (!chrome_translate_client)
112b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    return NULL;
113b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  return chrome_translate_client->GetTranslateManager();
114b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
115b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
116b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson// static
117b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonvoid ChromeTranslateClient::GetTranslateLanguages(
118b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    content::WebContents* web_contents,
119b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    std::string* source,
120b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    std::string* target) {
121b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  DCHECK(source != NULL);
122b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  DCHECK(target != NULL);
123b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
124b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  ChromeTranslateClient* chrome_translate_client =
125b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      FromWebContents(web_contents);
126b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  if (!chrome_translate_client)
127b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    return;
128b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
129b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  *source = chrome_translate_client->GetLanguageState().original_language();
130b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
131b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  Profile* profile =
132b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      Profile::FromBrowserContext(web_contents->GetBrowserContext());
133b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  Profile* original_profile = profile->GetOriginalProfile();
134b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  PrefService* prefs = original_profile->GetPrefs();
135b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  scoped_ptr<TranslatePrefs> translate_prefs = CreateTranslatePrefs(prefs);
136b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  if (!web_contents->GetBrowserContext()->IsOffTheRecord()) {
137b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    std::string auto_translate_language =
138b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson        TranslateManager::GetAutoTargetLanguage(*source, translate_prefs.get());
139b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    if (!auto_translate_language.empty()) {
140b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      *target = auto_translate_language;
141b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson      return;
142b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    }
143b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  }
144b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
145b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  std::string accept_languages_str = prefs->GetString(prefs::kAcceptLanguages);
146b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  std::vector<std::string> accept_languages_list;
147b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  base::SplitString(accept_languages_str, ',', &accept_languages_list);
148b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  *target = TranslateManager::GetTargetLanguage(accept_languages_list);
149b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
150b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
151b0114cb9f332db144f65291211ae65f7f0e814e6Scott AndersonTranslateManager* ChromeTranslateClient::GetTranslateManager() {
152b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  return translate_manager_.get();
153b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
154b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
155b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersoncontent::WebContents* ChromeTranslateClient::GetWebContents() {
156b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  return web_contents();
157b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson}
158b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
159b0114cb9f332db144f65291211ae65f7f0e814e6Scott Andersonvoid ChromeTranslateClient::ShowTranslateUI(translate::TranslateStep step,
160b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson                                            const std::string source_language,
161b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson                                            const std::string target_language,
162b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson                                            TranslateErrors::Type error_type,
163b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson                                            bool triggered_from_menu) {
164b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  DCHECK(web_contents());
165b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson  if (error_type != TranslateErrors::NONE)
166b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson    step = translate::TRANSLATE_STEP_TRANSLATE_ERROR;
167b0114cb9f332db144f65291211ae65f7f0e814e6Scott Anderson
168  if (TranslateService::IsTranslateBubbleEnabled()) {
169    // Bubble UI.
170    if (step == translate::TRANSLATE_STEP_BEFORE_TRANSLATE) {
171      // TODO(droger): Move this logic out of UI code.
172      GetLanguageState().SetTranslateEnabled(true);
173      if (!GetLanguageState().HasLanguageChanged())
174        return;
175    }
176    ShowBubble(step, error_type);
177    return;
178  }
179
180  // Infobar UI.
181  TranslateInfoBarDelegate::Create(
182      step != translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
183      translate_manager_->GetWeakPtr(),
184      InfoBarService::FromWebContents(web_contents()),
185      web_contents()->GetBrowserContext()->IsOffTheRecord(),
186      step,
187      source_language,
188      target_language,
189      error_type,
190      triggered_from_menu);
191}
192
193TranslateDriver* ChromeTranslateClient::GetTranslateDriver() {
194  return &translate_driver_;
195}
196
197PrefService* ChromeTranslateClient::GetPrefs() {
198  DCHECK(web_contents());
199  Profile* profile =
200      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
201  return profile->GetOriginalProfile()->GetPrefs();
202}
203
204scoped_ptr<TranslatePrefs> ChromeTranslateClient::GetTranslatePrefs() {
205  DCHECK(web_contents());
206  Profile* profile =
207      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
208  return CreateTranslatePrefs(profile->GetPrefs());
209}
210
211TranslateAcceptLanguages* ChromeTranslateClient::GetTranslateAcceptLanguages() {
212  DCHECK(web_contents());
213  return GetTranslateAcceptLanguages(web_contents()->GetBrowserContext());
214}
215
216int ChromeTranslateClient::GetInfobarIconID() const {
217  return IDR_INFOBAR_TRANSLATE;
218}
219
220// ChromeTranslateClient::CreateInfoBar() is implemented in platform-specific
221// files.
222
223bool ChromeTranslateClient::IsTranslatableURL(const GURL& url) {
224  return TranslateService::IsTranslatableURL(url);
225}
226
227void ChromeTranslateClient::ShowReportLanguageDetectionErrorUI(
228    const GURL& report_url) {
229#if defined(OS_ANDROID)
230  // Android does not support reporting language detection errors.
231  NOTREACHED();
232#else
233  // We'll open the URL in a new tab so that the user can tell us more.
234  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
235  if (!browser) {
236    NOTREACHED();
237    return;
238  }
239
240  chrome::AddSelectedTabWithURL(
241      browser, report_url, content::PAGE_TRANSITION_AUTO_BOOKMARK);
242#endif  // defined(OS_ANDROID)
243}
244
245bool ChromeTranslateClient::OnMessageReceived(const IPC::Message& message) {
246  bool handled = true;
247  IPC_BEGIN_MESSAGE_MAP(ChromeTranslateClient, message)
248  IPC_MESSAGE_HANDLER(ChromeViewHostMsg_TranslateLanguageDetermined,
249                      OnLanguageDetermined)
250  IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageTranslated, OnPageTranslated)
251#if defined(CLD2_DYNAMIC_MODE)
252  IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NeedCLDData, OnCLDDataRequested)
253#endif
254  IPC_MESSAGE_UNHANDLED(handled = false)
255  IPC_END_MESSAGE_MAP()
256
257  return handled;
258}
259
260void ChromeTranslateClient::NavigationEntryCommitted(
261    const content::LoadCommittedDetails& load_details) {
262  // Check whether this is a reload: When doing a page reload, the
263  // TranslateLanguageDetermined IPC is not sent so the translation needs to be
264  // explicitly initiated.
265
266  content::NavigationEntry* entry =
267      web_contents()->GetController().GetActiveEntry();
268  if (!entry) {
269    NOTREACHED();
270    return;
271  }
272
273  // If the navigation happened while offline don't show the translate
274  // bar since there will be nothing to translate.
275  if (load_details.http_status_code == 0 ||
276      load_details.http_status_code == net::HTTP_INTERNAL_SERVER_ERROR) {
277    return;
278  }
279
280  if (!load_details.is_main_frame &&
281      translate_driver_.GetLanguageState().translation_declined()) {
282    // Some sites (such as Google map) may trigger sub-frame navigations
283    // when the user interacts with the page.  We don't want to show a new
284    // infobar if the user already dismissed one in that case.
285    return;
286  }
287
288  // If not a reload, return.
289  if (entry->GetTransitionType() != content::PAGE_TRANSITION_RELOAD &&
290      load_details.type != content::NAVIGATION_TYPE_SAME_PAGE) {
291    return;
292  }
293
294  if (!translate_driver_.GetLanguageState().page_needs_translation())
295    return;
296
297  // Note that we delay it as the ordering of the processing of this callback
298  // by WebContentsObservers is undefined and might result in the current
299  // infobars being removed. Since the translation initiation process might add
300  // an infobar, it must be done after that.
301  base::MessageLoop::current()->PostTask(
302      FROM_HERE,
303      base::Bind(&ChromeTranslateClient::InitiateTranslation,
304                 weak_pointer_factory_.GetWeakPtr(),
305                 translate_driver_.GetLanguageState().original_language(),
306                 0));
307}
308
309void ChromeTranslateClient::DidNavigateAnyFrame(
310    const content::LoadCommittedDetails& details,
311    const content::FrameNavigateParams& params) {
312  // Let the LanguageState clear its state.
313  translate_driver_.DidNavigate(details);
314}
315
316void ChromeTranslateClient::WebContentsDestroyed() {
317  // Translation process can be interrupted.
318  // Destroying the TranslateManager now guarantees that it never has to deal
319  // with NULL WebContents.
320  translate_manager_.reset();
321}
322
323#if defined(CLD2_DYNAMIC_MODE)
324void ChromeTranslateClient::OnCLDDataRequested() {
325  // Quickly try to read s_cached_file_. If valid, the file handle is
326  // cached and can be used immediately. Else, queue the caching task to the
327  // blocking pool.
328  base::File* handle = NULL;
329  uint64 data_offset = 0;
330  uint64 data_length = 0;
331  {
332    base::AutoLock lock(s_file_lock_.Get());
333    handle = s_cached_file_;
334    data_offset = s_cached_data_offset_;
335    data_length = s_cached_data_length_;
336  }
337
338  if (handle && handle->IsValid()) {
339    // Cached data available. Respond to the request.
340    SendCLDDataAvailable(handle, data_offset, data_length);
341    return;
342  }
343
344  // Else, we don't have the data file yet. Queue a caching attempt.
345  // The caching attempt happens in the blocking pool because it may involve
346  // arbitrary filesystem access.
347  // After the caching attempt is made, we call MaybeSendCLDDataAvailable
348  // to pass the file handle to the renderer. This only results in an IPC
349  // message if the caching attempt was successful.
350  content::BrowserThread::PostBlockingPoolTaskAndReply(
351      FROM_HERE,
352      base::Bind(&ChromeTranslateClient::HandleCLDDataRequest),
353      base::Bind(&ChromeTranslateClient::MaybeSendCLDDataAvailable,
354                 weak_pointer_factory_.GetWeakPtr()));
355}
356
357void ChromeTranslateClient::MaybeSendCLDDataAvailable() {
358  base::File* handle = NULL;
359  uint64 data_offset = 0;
360  uint64 data_length = 0;
361  {
362    base::AutoLock lock(s_file_lock_.Get());
363    handle = s_cached_file_;
364    data_offset = s_cached_data_offset_;
365    data_length = s_cached_data_length_;
366  }
367
368  if (handle && handle->IsValid())
369    SendCLDDataAvailable(handle, data_offset, data_length);
370}
371
372void ChromeTranslateClient::SendCLDDataAvailable(const base::File* handle,
373                                                 const uint64 data_offset,
374                                                 const uint64 data_length) {
375  // Data available, respond to the request.
376  IPC::PlatformFileForTransit ipc_platform_file = IPC::GetFileHandleForProcess(
377      handle->GetPlatformFile(),
378      GetWebContents()->GetRenderViewHost()->GetProcess()->GetHandle(),
379      false);
380  // In general, sending a response from within the code path that is processing
381  // a request is discouraged because there is potential for deadlock (if the
382  // methods are sent synchronously) or loops (if the response can trigger a
383  // new request). Neither of these concerns is relevant in this code, so
384  // sending the response from within the code path of the request handler is
385  // safe.
386  Send(new ChromeViewMsg_CLDDataAvailable(
387      GetWebContents()->GetRenderViewHost()->GetRoutingID(),
388      ipc_platform_file,
389      data_offset,
390      data_length));
391}
392
393void ChromeTranslateClient::HandleCLDDataRequest() {
394  // Because this function involves arbitrary file system access, it must run
395  // on the blocking pool.
396  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
397  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
398
399  {
400    base::AutoLock lock(s_file_lock_.Get());
401    if (s_cached_file_)
402      return;  // Already done, duplicate request
403  }
404
405#if defined(CLD2_IS_COMPONENT)
406  base::FilePath path = component_updater::GetLatestCldDataFile();
407  if (path.empty())
408    return;
409#else  // CLD2 data is at a well-known file path
410  base::FilePath path;
411  if (!PathService::Get(chrome::DIR_USER_DATA, &path)) {
412    LOG(WARNING) << "Unable to locate user data directory";
413    return;  // Chrome isn't properly installed.
414  }
415  path = path.Append(chrome::kCLDDataFilename);
416#endif
417
418  // If the file exists, we can send an IPC-safe construct back to the
419  // renderer process immediately; otherwise, nothing to do here.
420  if (!base::PathExists(path))
421    return;
422
423  // Attempt to open the file for reading.
424  scoped_ptr<base::File> file(
425      new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
426  if (!file->IsValid()) {
427    LOG(WARNING) << "CLD data file exists but cannot be opened";
428    return;
429  }
430
431  base::File::Info file_info;
432  if (!file->GetInfo(&file_info)) {
433    LOG(WARNING) << "CLD data file exists but cannot be inspected";
434    return;
435  }
436
437  // For now, our offset and length are simply 0 and the length of the file,
438  // respectively. If we later decide to include the CLD2 data file inside of
439  // a larger binary context, these params can be twiddled appropriately.
440  const uint64 data_offset = 0;
441  const uint64 data_length = file_info.size;
442
443  {
444    base::AutoLock lock(s_file_lock_.Get());
445    if (s_cached_file_) {
446      // Idempotence: Racing another request on the blocking pool, abort.
447    } else {
448      // Else, this request has taken care of it all. Cache all info.
449      s_cached_file_ = file.release();
450      s_cached_data_offset_ = data_offset;
451      s_cached_data_length_ = data_length;
452    }
453  }
454}
455
456#endif  // defined(CLD2_DYNAMIC_MODE)
457
458void ChromeTranslateClient::InitiateTranslation(const std::string& page_lang,
459                                                int attempt) {
460  if (translate_driver_.GetLanguageState().translation_pending())
461    return;
462
463  // During a reload we need web content to be available before the
464  // translate script is executed. Otherwise we will run the translate script on
465  // an empty DOM which will fail. Therefore we wait a bit to see if the page
466  // has finished.
467  if (web_contents()->IsLoading() && attempt < max_reload_check_attempts_) {
468    int backoff = attempt * kMaxTranslateLoadCheckAttempts;
469    base::MessageLoop::current()->PostDelayedTask(
470        FROM_HERE,
471        base::Bind(&ChromeTranslateClient::InitiateTranslation,
472                   weak_pointer_factory_.GetWeakPtr(),
473                   page_lang,
474                   ++attempt),
475        base::TimeDelta::FromMilliseconds(backoff));
476    return;
477  }
478
479  translate_manager_->InitiateTranslation(
480      TranslateDownloadManager::GetLanguageCode(page_lang));
481}
482
483void ChromeTranslateClient::OnLanguageDetermined(
484    const LanguageDetectionDetails& details,
485    bool page_needs_translation) {
486  translate_driver_.GetLanguageState().LanguageDetermined(
487      details.adopted_language, page_needs_translation);
488
489  if (web_contents())
490    translate_manager_->InitiateTranslation(details.adopted_language);
491
492  content::NotificationService::current()->Notify(
493      chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
494      content::Source<content::WebContents>(web_contents()),
495      content::Details<const LanguageDetectionDetails>(&details));
496}
497
498void ChromeTranslateClient::OnPageTranslated(int32 page_id,
499                                             const std::string& original_lang,
500                                             const std::string& translated_lang,
501                                             TranslateErrors::Type error_type) {
502  DCHECK(web_contents());
503  translate_manager_->PageTranslated(
504      original_lang, translated_lang, error_type);
505
506  PageTranslatedDetails details;
507  details.source_language = original_lang;
508  details.target_language = translated_lang;
509  details.error_type = error_type;
510  content::NotificationService::current()->Notify(
511      chrome::NOTIFICATION_PAGE_TRANSLATED,
512      content::Source<content::WebContents>(web_contents()),
513      content::Details<PageTranslatedDetails>(&details));
514}
515
516void ChromeTranslateClient::ShowBubble(translate::TranslateStep step,
517                                       TranslateErrors::Type error_type) {
518// The bubble is implemented only on the desktop platforms.
519#if !defined(OS_ANDROID) && !defined(OS_IOS)
520  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
521
522  // |browser| might be NULL when testing. In this case, Show(...) should be
523  // called because the implementation for testing is used.
524  if (!browser) {
525    TranslateBubbleFactory::Show(NULL, web_contents(), step, error_type);
526    return;
527  }
528
529  if (web_contents() != browser->tab_strip_model()->GetActiveWebContents())
530    return;
531
532  // This ShowBubble function is also used for upating the existing bubble.
533  // However, with the bubble shown, any browser windows are NOT activated
534  // because the bubble takes the focus from the other widgets including the
535  // browser windows. So it is checked that |browser| is the last activated
536  // browser, not is now activated.
537  if (browser !=
538      chrome::FindLastActiveWithHostDesktopType(browser->host_desktop_type())) {
539    return;
540  }
541
542  // During auto-translating, the bubble should not be shown.
543  if (step == translate::TRANSLATE_STEP_TRANSLATING ||
544      step == translate::TRANSLATE_STEP_AFTER_TRANSLATE) {
545    if (GetLanguageState().InTranslateNavigation())
546      return;
547  }
548
549  TranslateBubbleFactory::Show(
550      browser->window(), web_contents(), step, error_type);
551#else
552  NOTREACHED();
553#endif
554}
555