OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/translate/chrome_translate_client.h" | 5 #include "chrome/browser/translate/chrome_translate_client.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/prefs/pref_service.h" | 10 #include "base/prefs/pref_service.h" |
(...skipping 22 matching lines...) Expand all Loading... | |
33 #include "components/translate/core/common/language_detection_details.h" | 33 #include "components/translate/core/common/language_detection_details.h" |
34 #include "content/public/browser/navigation_details.h" | 34 #include "content/public/browser/navigation_details.h" |
35 #include "content/public/browser/navigation_entry.h" | 35 #include "content/public/browser/navigation_entry.h" |
36 #include "content/public/browser/notification_service.h" | 36 #include "content/public/browser/notification_service.h" |
37 #include "content/public/browser/render_view_host.h" | 37 #include "content/public/browser/render_view_host.h" |
38 #include "content/public/browser/web_contents.h" | 38 #include "content/public/browser/web_contents.h" |
39 #include "grit/theme_resources.h" | 39 #include "grit/theme_resources.h" |
40 #include "net/http/http_status_code.h" | 40 #include "net/http/http_status_code.h" |
41 #include "url/gurl.h" | 41 #include "url/gurl.h" |
42 | 42 |
43 #if defined(CLD2_DYNAMIC_MODE) | 43 #if defined(CLD2_DYNAMIC_MODE) && !defined(CLD2_IS_COMPONENT) |
44 #include "base/files/file.h" | |
45 #include "base/path_service.h" | 44 #include "base/path_service.h" |
46 #include "chrome/common/chrome_constants.h" | |
47 #include "chrome/common/chrome_paths.h" | 45 #include "chrome/common/chrome_paths.h" |
48 #include "content/public/browser/browser_thread.h" | |
49 #include "content/public/browser/render_process_host.h" | |
50 #endif | 46 #endif |
51 | 47 |
52 #if defined(CLD2_IS_COMPONENT) | 48 namespace content { |
droger
2014/06/19 16:13:18
The namespace should be translate, not content.
Andrew Hayden (chromium.org)
2014/06/19 19:49:30
Whoops. Cleanup on aisle 12! <sweep> <sweep>
Done
| |
53 #include "chrome/browser/component_updater/cld_component_installer.h" | 49 // From: |
54 #endif | 50 // /components/translate/content/browser/data_file_browser_cld_data_provider.h |
51 void SetCldDataFilePath(const base::FilePath&); | |
52 } | |
55 | 53 |
56 namespace { | 54 namespace { |
57 | 55 |
58 // The maximum number of attempts we'll do to see if the page has finshed | 56 // The maximum number of attempts we'll do to see if the page has finshed |
59 // loading before giving up the translation | 57 // loading before giving up the translation |
60 const int kMaxTranslateLoadCheckAttempts = 20; | 58 const int kMaxTranslateLoadCheckAttempts = 20; |
61 | 59 |
60 #if defined(CLD2_DYNAMIC_MODE) && !defined(CLD2_IS_COMPONENT) | |
61 // This build uses a standalone CLD2 data file. | |
62 // TODO(andrewhayden): Make the data file path into a gyp/gn define | |
63 const base::FilePath::CharType kCLDDataFilename[] = | |
64 FILE_PATH_LITERAL("cld2_data.bin"); | |
65 bool s_cld_file_path_initialized_ = false; | |
66 void InitCldFilePath() { | |
67 base::FilePath path; | |
68 if (!PathService::Get(chrome::DIR_USER_DATA, &path)) { | |
69 LOG(WARNING) << "Unable to locate user data directory"; | |
70 return; // Chrome isn't properly installed | |
71 } | |
72 s_cld_file_path_initialized_ = true; | |
73 path = path.Append(kCLDDataFilename); | |
74 content::SetCldDataFilePath(path); | |
75 } | |
76 #endif | |
77 | |
62 } // namespace | 78 } // namespace |
63 | 79 |
64 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromeTranslateClient); | 80 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromeTranslateClient); |
65 | 81 |
66 #if defined(CLD2_DYNAMIC_MODE) | |
67 // Statics defined in the .h file: | |
68 base::File* ChromeTranslateClient::s_cached_file_ = NULL; | |
69 uint64 ChromeTranslateClient::s_cached_data_offset_ = 0; | |
70 uint64 ChromeTranslateClient::s_cached_data_length_ = 0; | |
71 base::LazyInstance<base::Lock> ChromeTranslateClient::s_file_lock_ = | |
72 LAZY_INSTANCE_INITIALIZER; | |
73 #endif | |
74 | |
75 ChromeTranslateClient::ChromeTranslateClient(content::WebContents* web_contents) | 82 ChromeTranslateClient::ChromeTranslateClient(content::WebContents* web_contents) |
76 : content::WebContentsObserver(web_contents), | 83 : content::WebContentsObserver(web_contents), |
77 max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts), | 84 max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts), |
78 translate_driver_(&web_contents->GetController()), | 85 translate_driver_(&web_contents->GetController()), |
79 translate_manager_(new TranslateManager(this, prefs::kAcceptLanguages)), | 86 translate_manager_(new TranslateManager(this, prefs::kAcceptLanguages)), |
87 cld_data_provider_(content::CreateBrowserCldDataProviderFor( | |
88 web_contents->GetRenderViewHost())), | |
80 weak_pointer_factory_(this) { | 89 weak_pointer_factory_(this) { |
90 #if defined(CLD2_DYNAMIC_MODE) && !defined(CLD2_IS_COMPONENT) | |
91 if (!s_cld_file_path_initialized_) | |
92 InitCldFilePath(); | |
93 #endif | |
81 } | 94 } |
82 | 95 |
83 ChromeTranslateClient::~ChromeTranslateClient() { | 96 ChromeTranslateClient::~ChromeTranslateClient() { |
84 } | 97 } |
85 | 98 |
86 LanguageState& ChromeTranslateClient::GetLanguageState() { | 99 LanguageState& ChromeTranslateClient::GetLanguageState() { |
87 return translate_manager_->GetLanguageState(); | 100 return translate_manager_->GetLanguageState(); |
88 } | 101 } |
89 | 102 |
90 // static | 103 // static |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
250 browser, report_url, content::PAGE_TRANSITION_AUTO_BOOKMARK); | 263 browser, report_url, content::PAGE_TRANSITION_AUTO_BOOKMARK); |
251 #endif // defined(OS_ANDROID) | 264 #endif // defined(OS_ANDROID) |
252 } | 265 } |
253 | 266 |
254 bool ChromeTranslateClient::OnMessageReceived(const IPC::Message& message) { | 267 bool ChromeTranslateClient::OnMessageReceived(const IPC::Message& message) { |
255 bool handled = true; | 268 bool handled = true; |
256 IPC_BEGIN_MESSAGE_MAP(ChromeTranslateClient, message) | 269 IPC_BEGIN_MESSAGE_MAP(ChromeTranslateClient, message) |
257 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_TranslateLanguageDetermined, | 270 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_TranslateLanguageDetermined, |
258 OnLanguageDetermined) | 271 OnLanguageDetermined) |
259 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageTranslated, OnPageTranslated) | 272 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageTranslated, OnPageTranslated) |
260 #if defined(CLD2_DYNAMIC_MODE) | |
261 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NeedCLDData, OnCLDDataRequested) | |
262 #endif | |
263 IPC_MESSAGE_UNHANDLED(handled = false) | 273 IPC_MESSAGE_UNHANDLED(handled = false) |
264 IPC_END_MESSAGE_MAP() | 274 IPC_END_MESSAGE_MAP() |
265 | 275 |
276 if (!handled) { | |
277 handled = cld_data_provider_->OnMessageReceived(message); | |
278 } | |
266 return handled; | 279 return handled; |
267 } | 280 } |
268 | 281 |
269 void ChromeTranslateClient::NavigationEntryCommitted( | 282 void ChromeTranslateClient::NavigationEntryCommitted( |
270 const content::LoadCommittedDetails& load_details) { | 283 const content::LoadCommittedDetails& load_details) { |
271 // Check whether this is a reload: When doing a page reload, the | 284 // Check whether this is a reload: When doing a page reload, the |
272 // TranslateLanguageDetermined IPC is not sent so the translation needs to be | 285 // TranslateLanguageDetermined IPC is not sent so the translation needs to be |
273 // explicitly initiated. | 286 // explicitly initiated. |
274 | 287 |
275 content::NavigationEntry* entry = | 288 content::NavigationEntry* entry = |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
326 details.is_in_page, details.is_main_frame, reload); | 339 details.is_in_page, details.is_main_frame, reload); |
327 } | 340 } |
328 | 341 |
329 void ChromeTranslateClient::WebContentsDestroyed() { | 342 void ChromeTranslateClient::WebContentsDestroyed() { |
330 // Translation process can be interrupted. | 343 // Translation process can be interrupted. |
331 // Destroying the TranslateManager now guarantees that it never has to deal | 344 // Destroying the TranslateManager now guarantees that it never has to deal |
332 // with NULL WebContents. | 345 // with NULL WebContents. |
333 translate_manager_.reset(); | 346 translate_manager_.reset(); |
334 } | 347 } |
335 | 348 |
336 #if defined(CLD2_DYNAMIC_MODE) | |
337 void ChromeTranslateClient::OnCLDDataRequested() { | |
338 // Quickly try to read s_cached_file_. If valid, the file handle is | |
339 // cached and can be used immediately. Else, queue the caching task to the | |
340 // blocking pool. | |
341 base::File* handle = NULL; | |
342 uint64 data_offset = 0; | |
343 uint64 data_length = 0; | |
344 { | |
345 base::AutoLock lock(s_file_lock_.Get()); | |
346 handle = s_cached_file_; | |
347 data_offset = s_cached_data_offset_; | |
348 data_length = s_cached_data_length_; | |
349 } | |
350 | |
351 if (handle && handle->IsValid()) { | |
352 // Cached data available. Respond to the request. | |
353 SendCLDDataAvailable(handle, data_offset, data_length); | |
354 return; | |
355 } | |
356 | |
357 // Else, we don't have the data file yet. Queue a caching attempt. | |
358 // The caching attempt happens in the blocking pool because it may involve | |
359 // arbitrary filesystem access. | |
360 // After the caching attempt is made, we call MaybeSendCLDDataAvailable | |
361 // to pass the file handle to the renderer. This only results in an IPC | |
362 // message if the caching attempt was successful. | |
363 content::BrowserThread::PostBlockingPoolTaskAndReply( | |
364 FROM_HERE, | |
365 base::Bind(&ChromeTranslateClient::HandleCLDDataRequest), | |
366 base::Bind(&ChromeTranslateClient::MaybeSendCLDDataAvailable, | |
367 weak_pointer_factory_.GetWeakPtr())); | |
368 } | |
369 | |
370 void ChromeTranslateClient::MaybeSendCLDDataAvailable() { | |
371 base::File* handle = NULL; | |
372 uint64 data_offset = 0; | |
373 uint64 data_length = 0; | |
374 { | |
375 base::AutoLock lock(s_file_lock_.Get()); | |
376 handle = s_cached_file_; | |
377 data_offset = s_cached_data_offset_; | |
378 data_length = s_cached_data_length_; | |
379 } | |
380 | |
381 if (handle && handle->IsValid()) | |
382 SendCLDDataAvailable(handle, data_offset, data_length); | |
383 } | |
384 | |
385 void ChromeTranslateClient::SendCLDDataAvailable(const base::File* handle, | |
386 const uint64 data_offset, | |
387 const uint64 data_length) { | |
388 // Data available, respond to the request. | |
389 IPC::PlatformFileForTransit ipc_platform_file = IPC::GetFileHandleForProcess( | |
390 handle->GetPlatformFile(), | |
391 GetWebContents()->GetRenderViewHost()->GetProcess()->GetHandle(), | |
392 false); | |
393 // In general, sending a response from within the code path that is processing | |
394 // a request is discouraged because there is potential for deadlock (if the | |
395 // methods are sent synchronously) or loops (if the response can trigger a | |
396 // new request). Neither of these concerns is relevant in this code, so | |
397 // sending the response from within the code path of the request handler is | |
398 // safe. | |
399 Send(new ChromeViewMsg_CLDDataAvailable( | |
400 GetWebContents()->GetRenderViewHost()->GetRoutingID(), | |
401 ipc_platform_file, | |
402 data_offset, | |
403 data_length)); | |
404 } | |
405 | |
406 void ChromeTranslateClient::HandleCLDDataRequest() { | |
407 // Because this function involves arbitrary file system access, it must run | |
408 // on the blocking pool. | |
409 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
410 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
411 | |
412 { | |
413 base::AutoLock lock(s_file_lock_.Get()); | |
414 if (s_cached_file_) | |
415 return; // Already done, duplicate request | |
416 } | |
417 | |
418 #if defined(CLD2_IS_COMPONENT) | |
419 base::FilePath path = component_updater::GetLatestCldDataFile(); | |
420 if (path.empty()) | |
421 return; | |
422 #else // CLD2 data is at a well-known file path | |
423 base::FilePath path; | |
424 if (!PathService::Get(chrome::DIR_USER_DATA, &path)) { | |
425 LOG(WARNING) << "Unable to locate user data directory"; | |
426 return; // Chrome isn't properly installed. | |
427 } | |
428 path = path.Append(chrome::kCLDDataFilename); | |
429 #endif | |
430 | |
431 // If the file exists, we can send an IPC-safe construct back to the | |
432 // renderer process immediately; otherwise, nothing to do here. | |
433 if (!base::PathExists(path)) | |
434 return; | |
435 | |
436 // Attempt to open the file for reading. | |
437 scoped_ptr<base::File> file( | |
438 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ)); | |
439 if (!file->IsValid()) { | |
440 LOG(WARNING) << "CLD data file exists but cannot be opened"; | |
441 return; | |
442 } | |
443 | |
444 base::File::Info file_info; | |
445 if (!file->GetInfo(&file_info)) { | |
446 LOG(WARNING) << "CLD data file exists but cannot be inspected"; | |
447 return; | |
448 } | |
449 | |
450 // For now, our offset and length are simply 0 and the length of the file, | |
451 // respectively. If we later decide to include the CLD2 data file inside of | |
452 // a larger binary context, these params can be twiddled appropriately. | |
453 const uint64 data_offset = 0; | |
454 const uint64 data_length = file_info.size; | |
455 | |
456 { | |
457 base::AutoLock lock(s_file_lock_.Get()); | |
458 if (s_cached_file_) { | |
459 // Idempotence: Racing another request on the blocking pool, abort. | |
460 } else { | |
461 // Else, this request has taken care of it all. Cache all info. | |
462 s_cached_file_ = file.release(); | |
463 s_cached_data_offset_ = data_offset; | |
464 s_cached_data_length_ = data_length; | |
465 } | |
466 } | |
467 } | |
468 | |
469 #endif // defined(CLD2_DYNAMIC_MODE) | |
470 | |
471 void ChromeTranslateClient::InitiateTranslation(const std::string& page_lang, | 349 void ChromeTranslateClient::InitiateTranslation(const std::string& page_lang, |
472 int attempt) { | 350 int attempt) { |
473 if (GetLanguageState().translation_pending()) | 351 if (GetLanguageState().translation_pending()) |
474 return; | 352 return; |
475 | 353 |
476 // During a reload we need web content to be available before the | 354 // During a reload we need web content to be available before the |
477 // translate script is executed. Otherwise we will run the translate script on | 355 // translate script is executed. Otherwise we will run the translate script on |
478 // an empty DOM which will fail. Therefore we wait a bit to see if the page | 356 // an empty DOM which will fail. Therefore we wait a bit to see if the page |
479 // has finished. | 357 // has finished. |
480 if (web_contents()->IsLoading() && attempt < max_reload_check_attempts_) { | 358 if (web_contents()->IsLoading() && attempt < max_reload_check_attempts_) { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
558 if (GetLanguageState().InTranslateNavigation()) | 436 if (GetLanguageState().InTranslateNavigation()) |
559 return; | 437 return; |
560 } | 438 } |
561 | 439 |
562 TranslateBubbleFactory::Show( | 440 TranslateBubbleFactory::Show( |
563 browser->window(), web_contents(), step, error_type); | 441 browser->window(), web_contents(), step, error_type); |
564 #else | 442 #else |
565 NOTREACHED(); | 443 NOTREACHED(); |
566 #endif | 444 #endif |
567 } | 445 } |
OLD | NEW |