f29b31ff9007e331b0e7d08ed0c442219559ffdf
[platform/framework/web/crosswalk.git] / src / chrome / renderer / translate / translate_helper.cc
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/renderer/translate/translate_helper.h"
6
7 #if defined(CLD2_DYNAMIC_MODE)
8 #include <stdint.h>
9 #endif
10
11 #include "base/bind.h"
12 #include "base/compiler_specific.h"
13 #if defined(CLD2_DYNAMIC_MODE)
14 #include "base/files/memory_mapped_file.h"
15 #endif
16 #include "base/logging.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "chrome/renderer/isolated_world_ids.h"
22 #include "components/translate/content/common/translate_messages.h"
23 #include "components/translate/core/common/translate_constants.h"
24 #include "components/translate/core/common/translate_metrics.h"
25 #include "components/translate/core/common/translate_util.h"
26 #include "components/translate/core/language_detection/language_detection_util.h"
27 #include "content/public/renderer/render_view.h"
28 #include "extensions/common/constants.h"
29 #include "extensions/renderer/extension_groups.h"
30 #include "ipc/ipc_platform_file.h"
31 #if defined(CLD2_DYNAMIC_MODE)
32 #include "content/public/common/url_constants.h"
33 #include "third_party/cld_2/src/public/compact_lang_det.h"
34 #endif
35 #include "third_party/WebKit/public/web/WebDocument.h"
36 #include "third_party/WebKit/public/web/WebElement.h"
37 #include "third_party/WebKit/public/web/WebFrame.h"
38 #include "third_party/WebKit/public/web/WebNode.h"
39 #include "third_party/WebKit/public/web/WebNodeList.h"
40 #include "third_party/WebKit/public/web/WebScriptSource.h"
41 #include "third_party/WebKit/public/web/WebView.h"
42 #include "third_party/WebKit/public/web/WebWidget.h"
43 #include "url/gurl.h"
44 #include "v8/include/v8.h"
45
46 using base::ASCIIToUTF16;
47 using blink::WebDocument;
48 using blink::WebElement;
49 using blink::WebFrame;
50 using blink::WebNode;
51 using blink::WebNodeList;
52 using blink::WebScriptSource;
53 using blink::WebSecurityOrigin;
54 using blink::WebString;
55 using blink::WebVector;
56 using blink::WebView;
57
58 namespace {
59
60 // The delay in milliseconds that we'll wait before checking to see if the
61 // translate library injected in the page is ready.
62 const int kTranslateInitCheckDelayMs = 150;
63
64 // The maximum number of times we'll check to see if the translate library
65 // injected in the page is ready.
66 const int kMaxTranslateInitCheckAttempts = 5;
67
68 // The delay we wait in milliseconds before checking whether the translation has
69 // finished.
70 const int kTranslateStatusCheckDelayMs = 400;
71
72 // Language name passed to the Translate element for it to detect the language.
73 const char kAutoDetectionLanguage[] = "auto";
74
75 // Isolated world sets following content-security-policy.
76 const char kContentSecurityPolicy[] = "script-src 'self' 'unsafe-eval'";
77
78 }  // namespace
79
80 #if defined(CLD2_DYNAMIC_MODE)
81 // The mmap for the CLD2 data must be held forever once it is available in the
82 // process. This is declared static in the translate_helper.h.
83 base::LazyInstance<TranslateHelper::CLDMmapWrapper>::Leaky
84   TranslateHelper::s_cld_mmap_ = LAZY_INSTANCE_INITIALIZER;
85 #endif
86
87 ////////////////////////////////////////////////////////////////////////////////
88 // TranslateHelper, public:
89 //
90 TranslateHelper::TranslateHelper(content::RenderView* render_view)
91     : content::RenderViewObserver(render_view),
92       page_id_(-1),
93       translation_pending_(false),
94       weak_method_factory_(this)
95 #if defined(CLD2_DYNAMIC_MODE)
96       ,cld2_data_file_polling_started_(false),
97       cld2_data_file_polling_canceled_(false),
98       deferred_page_capture_(false),
99       deferred_page_id_(-1),
100       deferred_contents_(ASCIIToUTF16(""))
101 #endif
102   {
103 }
104
105 TranslateHelper::~TranslateHelper() {
106   CancelPendingTranslation();
107 #if defined(CLD2_DYNAMIC_MODE)
108   CancelCLD2DataFilePolling();
109 #endif
110 }
111
112 void TranslateHelper::PrepareForUrl(const GURL& url) {
113 #if defined(CLD2_DYNAMIC_MODE)
114   deferred_page_capture_ = false;
115   deferred_contents_.clear();
116   if (cld2_data_file_polling_started_)
117     return;
118
119   // TODO(andrewhayden): Refactor translate_manager.cc's IsTranslatableURL to
120   // components/translate/core/common/translate_util.cc, and ignore any URL
121   // that fails that check. This will require moving unit tests and rewiring
122   // other function calls as well, so for now replicate the logic here.
123   if (url.is_empty())
124     return;
125   if (url.SchemeIs(content::kChromeUIScheme))
126     return;
127   if (url.SchemeIs(content::kChromeDevToolsScheme))
128     return;
129   if (url.SchemeIs(url::kFtpScheme))
130     return;
131 #if defined(OS_CHROMEOS)
132   if (url.SchemeIs(extensions::kExtensionScheme) &&
133       url.DomainIs(file_manager::kFileManagerAppId))
134     return;
135 #endif
136
137   // Start polling for CLD data.
138   cld2_data_file_polling_started_ = true;
139   TranslateHelper::SendCLD2DataFileRequest(0, 1000);
140 #endif
141 }
142
143 #if defined(CLD2_DYNAMIC_MODE)
144 void TranslateHelper::DeferPageCaptured(const int page_id,
145                                         const base::string16& contents) {
146   deferred_page_capture_ = true;
147   deferred_page_id_ = page_id;
148   deferred_contents_ = contents;
149 }
150 #endif
151
152 void TranslateHelper::PageCaptured(int page_id,
153                                    const base::string16& contents) {
154   // Get the document language as set by WebKit from the http-equiv
155   // meta tag for "content-language".  This may or may not also
156   // have a value derived from the actual Content-Language HTTP
157   // header.  The two actually have different meanings (despite the
158   // original intent of http-equiv to be an equivalent) with the former
159   // being the language of the document and the latter being the
160   // language of the intended audience (a distinction really only
161   // relevant for things like langauge textbooks).  This distinction
162   // shouldn't affect translation.
163   WebFrame* main_frame = GetMainFrame();
164   if (!main_frame || render_view()->GetPageId() != page_id)
165     return;
166
167   // TODO(andrewhayden): UMA insertion point here: Track if data is available.
168   // TODO(andrewhayden): Retry insertion point here, retry till data available.
169 #if defined(CLD2_DYNAMIC_MODE)
170   if (!CLD2::isDataLoaded()) {
171     // We're in dynamic mode and CLD data isn't loaded. Retry when CLD data
172     // is loaded, if ever.
173     TranslateHelper::DeferPageCaptured(page_id, contents);
174     return;
175   }
176 #endif
177   page_id_ = page_id;
178   WebDocument document = main_frame->document();
179   std::string content_language = document.contentLanguage().utf8();
180   WebElement html_element = document.documentElement();
181   std::string html_lang;
182   // |html_element| can be null element, e.g. in
183   // BrowserTest.WindowOpenClose.
184   if (!html_element.isNull())
185     html_lang = html_element.getAttribute("lang").utf8();
186   std::string cld_language;
187   bool is_cld_reliable;
188   std::string language = translate::DeterminePageLanguage(
189       content_language, html_lang, contents, &cld_language, &is_cld_reliable);
190
191   if (language.empty())
192     return;
193
194   language_determined_time_ = base::TimeTicks::Now();
195
196   GURL url(document.url());
197   LanguageDetectionDetails details;
198   details.time = base::Time::Now();
199   details.url = url;
200   details.content_language = content_language;
201   details.cld_language = cld_language;
202   details.is_cld_reliable = is_cld_reliable;
203   details.html_root_language = html_lang;
204   details.adopted_language = language;
205
206   // TODO(hajimehoshi): If this affects performance, it should be set only if
207   // translate-internals tab exists.
208   details.contents = contents;
209
210   Send(new ChromeViewHostMsg_TranslateLanguageDetermined(
211       routing_id(),
212       details,
213       IsTranslationAllowed(&document) && !language.empty()));
214 }
215
216 void TranslateHelper::CancelPendingTranslation() {
217   weak_method_factory_.InvalidateWeakPtrs();
218   translation_pending_ = false;
219   source_lang_.clear();
220   target_lang_.clear();
221 #if defined(CLD2_DYNAMIC_MODE)
222   CancelCLD2DataFilePolling();
223 #endif
224 }
225
226 ////////////////////////////////////////////////////////////////////////////////
227 // TranslateHelper, protected:
228 //
229 bool TranslateHelper::IsTranslateLibAvailable() {
230   return ExecuteScriptAndGetBoolResult(
231       "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && "
232       "typeof cr.googleTranslate.translate == 'function'", false);
233 }
234
235 bool TranslateHelper::IsTranslateLibReady() {
236   return ExecuteScriptAndGetBoolResult("cr.googleTranslate.libReady", false);
237 }
238
239 bool TranslateHelper::HasTranslationFinished() {
240   return ExecuteScriptAndGetBoolResult("cr.googleTranslate.finished", true);
241 }
242
243 bool TranslateHelper::HasTranslationFailed() {
244   return ExecuteScriptAndGetBoolResult("cr.googleTranslate.error", true);
245 }
246
247 bool TranslateHelper::StartTranslation() {
248   std::string script = "cr.googleTranslate.translate('" +
249                        source_lang_ +
250                        "','" +
251                        target_lang_ +
252                        "')";
253   return ExecuteScriptAndGetBoolResult(script, false);
254 }
255
256 std::string TranslateHelper::GetOriginalPageLanguage() {
257   return ExecuteScriptAndGetStringResult("cr.googleTranslate.sourceLang");
258 }
259
260 base::TimeDelta TranslateHelper::AdjustDelay(int delayInMs) {
261   // Just converts |delayInMs| without any modification in practical cases.
262   // Tests will override this function to return modified value.
263   return base::TimeDelta::FromMilliseconds(delayInMs);
264 }
265
266 void TranslateHelper::ExecuteScript(const std::string& script) {
267   WebFrame* main_frame = GetMainFrame();
268   if (!main_frame)
269     return;
270
271   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
272   main_frame->executeScriptInIsolatedWorld(
273       chrome::ISOLATED_WORLD_ID_TRANSLATE,
274       &source,
275       1,
276       extensions::EXTENSION_GROUP_INTERNAL_TRANSLATE_SCRIPTS);
277 }
278
279 bool TranslateHelper::ExecuteScriptAndGetBoolResult(const std::string& script,
280                                                     bool fallback) {
281   WebFrame* main_frame = GetMainFrame();
282   if (!main_frame)
283     return fallback;
284
285   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
286   WebVector<v8::Local<v8::Value> > results;
287   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
288   main_frame->executeScriptInIsolatedWorld(
289       chrome::ISOLATED_WORLD_ID_TRANSLATE,
290       &source,
291       1,
292       extensions::EXTENSION_GROUP_INTERNAL_TRANSLATE_SCRIPTS,
293       &results);
294   if (results.size() != 1 || results[0].IsEmpty() || !results[0]->IsBoolean()) {
295     NOTREACHED();
296     return fallback;
297   }
298
299   return results[0]->BooleanValue();
300 }
301
302 std::string TranslateHelper::ExecuteScriptAndGetStringResult(
303     const std::string& script) {
304   WebFrame* main_frame = GetMainFrame();
305   if (!main_frame)
306     return std::string();
307
308   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
309   WebVector<v8::Local<v8::Value> > results;
310   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
311   main_frame->executeScriptInIsolatedWorld(
312       chrome::ISOLATED_WORLD_ID_TRANSLATE,
313       &source,
314       1,
315       extensions::EXTENSION_GROUP_INTERNAL_TRANSLATE_SCRIPTS,
316       &results);
317   if (results.size() != 1 || results[0].IsEmpty() || !results[0]->IsString()) {
318     NOTREACHED();
319     return std::string();
320   }
321
322   v8::Local<v8::String> v8_str = results[0]->ToString();
323   int length = v8_str->Utf8Length() + 1;
324   scoped_ptr<char[]> str(new char[length]);
325   v8_str->WriteUtf8(str.get(), length);
326   return std::string(str.get());
327 }
328
329 double TranslateHelper::ExecuteScriptAndGetDoubleResult(
330     const std::string& script) {
331   WebFrame* main_frame = GetMainFrame();
332   if (!main_frame)
333     return 0.0;
334
335   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
336   WebVector<v8::Local<v8::Value> > results;
337   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
338   main_frame->executeScriptInIsolatedWorld(
339       chrome::ISOLATED_WORLD_ID_TRANSLATE,
340       &source,
341       1,
342       extensions::EXTENSION_GROUP_INTERNAL_TRANSLATE_SCRIPTS,
343       &results);
344   if (results.size() != 1 || results[0].IsEmpty() || !results[0]->IsNumber()) {
345     NOTREACHED();
346     return 0.0;
347   }
348
349   return results[0]->NumberValue();
350 }
351
352 ////////////////////////////////////////////////////////////////////////////////
353 // TranslateHelper, private:
354 //
355
356 // static
357 bool TranslateHelper::IsTranslationAllowed(WebDocument* document) {
358   WebElement head = document->head();
359   if (head.isNull() || !head.hasChildNodes())
360     return true;
361
362   const WebString meta(ASCIIToUTF16("meta"));
363   const WebString name(ASCIIToUTF16("name"));
364   const WebString google(ASCIIToUTF16("google"));
365   const WebString value(ASCIIToUTF16("value"));
366   const WebString content(ASCIIToUTF16("content"));
367
368   WebNodeList children = head.childNodes();
369   for (size_t i = 0; i < children.length(); ++i) {
370     WebNode node = children.item(i);
371     if (!node.isElementNode())
372       continue;
373     WebElement element = node.to<WebElement>();
374     // Check if a tag is <meta>.
375     if (!element.hasTagName(meta))
376       continue;
377     // Check if the tag contains name="google".
378     WebString attribute = element.getAttribute(name);
379     if (attribute.isNull() || attribute != google)
380       continue;
381     // Check if the tag contains value="notranslate", or content="notranslate".
382     attribute = element.getAttribute(value);
383     if (attribute.isNull())
384       attribute = element.getAttribute(content);
385     if (attribute.isNull())
386       continue;
387     if (LowerCaseEqualsASCII(attribute, "notranslate"))
388       return false;
389   }
390   return true;
391 }
392
393 bool TranslateHelper::OnMessageReceived(const IPC::Message& message) {
394   bool handled = true;
395   IPC_BEGIN_MESSAGE_MAP(TranslateHelper, message)
396     IPC_MESSAGE_HANDLER(ChromeViewMsg_TranslatePage, OnTranslatePage)
397     IPC_MESSAGE_HANDLER(ChromeViewMsg_RevertTranslation, OnRevertTranslation)
398 #if defined(CLD2_DYNAMIC_MODE)
399     IPC_MESSAGE_HANDLER(ChromeViewMsg_CLDDataAvailable, OnCLDDataAvailable);
400 #endif
401     IPC_MESSAGE_UNHANDLED(handled = false)
402   IPC_END_MESSAGE_MAP()
403   return handled;
404 }
405
406 void TranslateHelper::OnTranslatePage(int page_id,
407                                       const std::string& translate_script,
408                                       const std::string& source_lang,
409                                       const std::string& target_lang) {
410   WebFrame* main_frame = GetMainFrame();
411   if (!main_frame ||
412       page_id_ != page_id ||
413       render_view()->GetPageId() != page_id)
414     return;  // We navigated away, nothing to do.
415
416   // A similar translation is already under way, nothing to do.
417   if (translation_pending_ && target_lang_ == target_lang)
418     return;
419
420   // Any pending translation is now irrelevant.
421   CancelPendingTranslation();
422
423   // Set our states.
424   translation_pending_ = true;
425
426   // If the source language is undetermined, we'll let the translate element
427   // detect it.
428   source_lang_ = (source_lang != translate::kUnknownLanguageCode) ?
429                   source_lang : kAutoDetectionLanguage;
430   target_lang_ = target_lang;
431
432   translate::ReportUserActionDuration(language_determined_time_,
433                                       base::TimeTicks::Now());
434
435   GURL url(main_frame->document().url());
436   translate::ReportPageScheme(url.scheme());
437
438   // Set up v8 isolated world with proper content-security-policy and
439   // security-origin.
440   WebFrame* frame = GetMainFrame();
441   if (frame) {
442     frame->setIsolatedWorldContentSecurityPolicy(
443         chrome::ISOLATED_WORLD_ID_TRANSLATE,
444         WebString::fromUTF8(kContentSecurityPolicy));
445
446     GURL security_origin = translate::GetTranslateSecurityOrigin();
447     frame->setIsolatedWorldSecurityOrigin(
448         chrome::ISOLATED_WORLD_ID_TRANSLATE,
449         WebSecurityOrigin::create(security_origin));
450   }
451
452   if (!IsTranslateLibAvailable()) {
453     // Evaluate the script to add the translation related method to the global
454     // context of the page.
455     ExecuteScript(translate_script);
456     DCHECK(IsTranslateLibAvailable());
457   }
458
459   TranslatePageImpl(0);
460 }
461
462 void TranslateHelper::OnRevertTranslation(int page_id) {
463   if (page_id_ != page_id || render_view()->GetPageId() != page_id)
464     return;  // We navigated away, nothing to do.
465
466   if (!IsTranslateLibAvailable()) {
467     NOTREACHED();
468     return;
469   }
470
471   CancelPendingTranslation();
472
473   ExecuteScript("cr.googleTranslate.revert()");
474 }
475
476 void TranslateHelper::CheckTranslateStatus() {
477   // If this is not the same page, the translation has been canceled.  If the
478   // view is gone, the page is closing.
479   if (page_id_ != render_view()->GetPageId() || !render_view()->GetWebView())
480     return;
481
482   // First check if there was an error.
483   if (HasTranslationFailed()) {
484     // TODO(toyoshim): Check |errorCode| of translate.js and notify it here.
485     NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_ERROR);
486     return;  // There was an error.
487   }
488
489   if (HasTranslationFinished()) {
490     std::string actual_source_lang;
491     // Translation was successfull, if it was auto, retrieve the source
492     // language the Translate Element detected.
493     if (source_lang_ == kAutoDetectionLanguage) {
494       actual_source_lang = GetOriginalPageLanguage();
495       if (actual_source_lang.empty()) {
496         NotifyBrowserTranslationFailed(TranslateErrors::UNKNOWN_LANGUAGE);
497         return;
498       } else if (actual_source_lang == target_lang_) {
499         NotifyBrowserTranslationFailed(TranslateErrors::IDENTICAL_LANGUAGES);
500         return;
501       }
502     } else {
503       actual_source_lang = source_lang_;
504     }
505
506     if (!translation_pending_) {
507       NOTREACHED();
508       return;
509     }
510
511     translation_pending_ = false;
512
513     // Check JavaScript performance counters for UMA reports.
514     translate::ReportTimeToTranslate(
515         ExecuteScriptAndGetDoubleResult("cr.googleTranslate.translationTime"));
516
517     // Notify the browser we are done.
518     render_view()->Send(new ChromeViewHostMsg_PageTranslated(
519         render_view()->GetRoutingID(), render_view()->GetPageId(),
520         actual_source_lang, target_lang_, TranslateErrors::NONE));
521     return;
522   }
523
524   // The translation is still pending, check again later.
525   base::MessageLoop::current()->PostDelayedTask(
526       FROM_HERE,
527       base::Bind(&TranslateHelper::CheckTranslateStatus,
528                  weak_method_factory_.GetWeakPtr()),
529       AdjustDelay(kTranslateStatusCheckDelayMs));
530 }
531
532 void TranslateHelper::TranslatePageImpl(int count) {
533   DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
534   if (page_id_ != render_view()->GetPageId() || !render_view()->GetWebView())
535     return;
536
537   if (!IsTranslateLibReady()) {
538     // The library is not ready, try again later, unless we have tried several
539     // times unsucessfully already.
540     if (++count >= kMaxTranslateInitCheckAttempts) {
541       NotifyBrowserTranslationFailed(TranslateErrors::INITIALIZATION_ERROR);
542       return;
543     }
544     base::MessageLoop::current()->PostDelayedTask(
545         FROM_HERE,
546         base::Bind(&TranslateHelper::TranslatePageImpl,
547                    weak_method_factory_.GetWeakPtr(),
548                    count),
549         AdjustDelay(count * kTranslateInitCheckDelayMs));
550     return;
551   }
552
553   // The library is loaded, and ready for translation now.
554   // Check JavaScript performance counters for UMA reports.
555   translate::ReportTimeToBeReady(
556       ExecuteScriptAndGetDoubleResult("cr.googleTranslate.readyTime"));
557   translate::ReportTimeToLoad(
558       ExecuteScriptAndGetDoubleResult("cr.googleTranslate.loadTime"));
559
560   if (!StartTranslation()) {
561     NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_ERROR);
562     return;
563   }
564   // Check the status of the translation.
565   base::MessageLoop::current()->PostDelayedTask(
566       FROM_HERE,
567       base::Bind(&TranslateHelper::CheckTranslateStatus,
568                  weak_method_factory_.GetWeakPtr()),
569       AdjustDelay(kTranslateStatusCheckDelayMs));
570 }
571
572 void TranslateHelper::NotifyBrowserTranslationFailed(
573     TranslateErrors::Type error) {
574   translation_pending_ = false;
575   // Notify the browser there was an error.
576   render_view()->Send(new ChromeViewHostMsg_PageTranslated(
577       render_view()->GetRoutingID(), page_id_, source_lang_,
578       target_lang_, error));
579 }
580
581 WebFrame* TranslateHelper::GetMainFrame() {
582   WebView* web_view = render_view()->GetWebView();
583
584   // When the tab is going to be closed, the web_view can be NULL.
585   if (!web_view)
586     return NULL;
587
588   return web_view->mainFrame();
589 }
590
591 #if defined(CLD2_DYNAMIC_MODE)
592 void TranslateHelper::CancelCLD2DataFilePolling() {
593   cld2_data_file_polling_canceled_ = true;
594 }
595
596 void TranslateHelper::SendCLD2DataFileRequest(const int delay_millis,
597                                               const int next_delay_millis) {
598   // Terminate immediately if told to stop polling.
599   if (cld2_data_file_polling_canceled_)
600     return;
601
602   // Terminate immediately if data is already loaded.
603   if (CLD2::isDataLoaded())
604     return;
605
606   // Else, send the IPC message to the browser process requesting the data...
607   Send(new ChromeViewHostMsg_NeedCLDData(routing_id()));
608
609   // ... and enqueue another delayed task to call again. This will start a
610   // chain of polling that will last until the pointer stops being NULL,
611   // which is the right thing to do.
612   // NB: In the great majority of cases, the data file will be available and
613   // the very first delayed task will be a no-op that terminates the chain.
614   // It's only while downloading the file that this will chain for a
615   // nontrivial amount of time.
616   // Use a weak pointer to avoid keeping this helper object around forever.
617   base::MessageLoop::current()->PostDelayedTask(
618       FROM_HERE,
619       base::Bind(&TranslateHelper::SendCLD2DataFileRequest,
620                  weak_method_factory_.GetWeakPtr(),
621                  next_delay_millis, next_delay_millis),
622       base::TimeDelta::FromMilliseconds(delay_millis));
623 }
624
625 void TranslateHelper::OnCLDDataAvailable(
626     const IPC::PlatformFileForTransit ipc_file_handle,
627     const uint64 data_offset,
628     const uint64 data_length) {
629   LoadCLDDData(IPC::PlatformFileForTransitToFile(ipc_file_handle), data_offset,
630                data_length);
631   if (deferred_page_capture_ && CLD2::isDataLoaded()) {
632     deferred_page_capture_ = false; // Don't do this a second time.
633     PageCaptured(deferred_page_id_, deferred_contents_);
634     deferred_page_id_ = -1; // Clean up for sanity
635     deferred_contents_.clear(); // Clean up for sanity
636   }
637 }
638
639 void TranslateHelper::LoadCLDDData(
640     base::File file,
641     const uint64 data_offset,
642     const uint64 data_length) {
643   // Terminate immediately if told to stop polling.
644   if (cld2_data_file_polling_canceled_)
645     return;
646
647   // Terminate immediately if data is already loaded.
648   if (CLD2::isDataLoaded())
649     return;
650
651   if (!file.IsValid()) {
652     LOG(ERROR) << "Can't find the CLD data file.";
653     return;
654   }
655
656   // mmap the file
657   s_cld_mmap_.Get().value = new base::MemoryMappedFile();
658   bool initialized = s_cld_mmap_.Get().value->Initialize(file.Pass());
659   if (!initialized) {
660     LOG(ERROR) << "mmap initialization failed";
661     delete s_cld_mmap_.Get().value;
662     s_cld_mmap_.Get().value = NULL;
663     return;
664   }
665
666   // Sanity checks
667   uint64 max_int32 = std::numeric_limits<int32>::max();
668   if (data_length + data_offset > s_cld_mmap_.Get().value->length()
669       || data_length > max_int32) { // max signed 32 bit integer
670     LOG(ERROR) << "Illegal mmap config: data_offset="
671         << data_offset << ", data_length=" << data_length
672         << ", mmap->length()=" << s_cld_mmap_.Get().value->length();
673     delete s_cld_mmap_.Get().value;
674     s_cld_mmap_.Get().value = NULL;
675     return;
676   }
677
678   // Initialize the CLD subsystem... and it's all done!
679   const uint8* data_ptr = s_cld_mmap_.Get().value->data() + data_offset;
680   CLD2::loadDataFromRawAddress(data_ptr, data_length);
681   DCHECK(CLD2::isDataLoaded()) << "Failed to load CLD data from mmap";
682 }
683 #endif