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
3// found in the LICENSE file.
4
5#include "data_file_renderer_cld_data_provider.h"
6
7#include "base/basictypes.h"
8#include "base/files/file.h"
9#include "base/files/memory_mapped_file.h"
10#include "base/lazy_instance.h"
11#include "base/logging.h"
12#include "components/translate/content/common/data_file_cld_data_provider_messages.h"
13#include "content/public/renderer/render_view_observer.h"
14#include "ipc/ipc_message.h"
15#include "ipc/ipc_message_macros.h"
16#include "ipc/ipc_platform_file.h"
17#include "third_party/cld_2/src/public/compact_lang_det.h"
18
19namespace {
20
21// A struct that contains the pointer to the CLD mmap. Used so that we can
22// leverage LazyInstance:Leaky to properly scope the lifetime of the mmap.
23struct CLDMmapWrapper {
24  CLDMmapWrapper() { value = NULL; }
25  base::MemoryMappedFile* value;
26};
27base::LazyInstance<CLDMmapWrapper>::Leaky g_cld_mmap =
28    LAZY_INSTANCE_INITIALIZER;
29
30}  // namespace
31
32namespace translate {
33
34// Implementation of the static factory method from RendererCldDataProvider,
35// hooking up this specific implementation for all of Chromium.
36RendererCldDataProvider* CreateRendererCldDataProviderFor(
37    content::RenderViewObserver* render_view_observer) {
38  // This log line is to help with determining which kind of provider has been
39  // configured. See also: chrome://translate-internals
40  VLOG(1) << "Creating DataFileRendererCldDataProvider";
41  return new DataFileRendererCldDataProvider(render_view_observer);
42}
43
44DataFileRendererCldDataProvider::DataFileRendererCldDataProvider(
45    content::RenderViewObserver* render_view_observer)
46    : render_view_observer_(render_view_observer) {
47}
48
49DataFileRendererCldDataProvider::~DataFileRendererCldDataProvider() {
50}
51
52bool DataFileRendererCldDataProvider::OnMessageReceived(
53    const IPC::Message& message) {
54  bool handled = true;
55  IPC_BEGIN_MESSAGE_MAP(DataFileRendererCldDataProvider, message)
56  IPC_MESSAGE_HANDLER(ChromeViewMsg_CldDataFileAvailable, OnCldDataAvailable)
57  IPC_MESSAGE_UNHANDLED(handled = false)
58  IPC_END_MESSAGE_MAP()
59  return handled;
60}
61
62void DataFileRendererCldDataProvider::SendCldDataRequest() {
63  // Else, send the IPC message to the browser process requesting the data...
64  render_view_observer_->Send(new ChromeViewHostMsg_NeedCldDataFile(
65      render_view_observer_->routing_id()));
66}
67
68bool DataFileRendererCldDataProvider::IsCldDataAvailable() {
69  // This neatly removes the need for code that depends on the generalized
70  // RendererCldDataProvider to #ifdef on CLD2_DYNAMIC_MODE
71  return CLD2::isDataLoaded();  // ground truth, independent of our state.
72}
73
74void DataFileRendererCldDataProvider::SetCldAvailableCallback(
75    base::Callback<void(void)> callback) {
76  cld_available_callback_ = callback;
77}
78
79void DataFileRendererCldDataProvider::OnCldDataAvailable(
80    const IPC::PlatformFileForTransit ipc_file_handle,
81    const uint64 data_offset,
82    const uint64 data_length) {
83  LoadCldData(IPC::PlatformFileForTransitToFile(ipc_file_handle),
84              data_offset,
85              data_length);
86}
87
88void DataFileRendererCldDataProvider::LoadCldData(base::File file,
89                                                  const uint64 data_offset,
90                                                  const uint64 data_length) {
91  // Terminate immediately if data is already loaded.
92  if (IsCldDataAvailable())
93    return;
94
95  if (!file.IsValid()) {
96    LOG(ERROR) << "Can't find the CLD data file.";
97    return;
98  }
99
100  // mmap the file
101  g_cld_mmap.Get().value = new base::MemoryMappedFile();
102  bool initialized = g_cld_mmap.Get().value->Initialize(file.Pass());
103  if (!initialized) {
104    LOG(ERROR) << "mmap initialization failed";
105    delete g_cld_mmap.Get().value;
106    g_cld_mmap.Get().value = NULL;
107    return;
108  }
109
110  // Sanity checks
111  uint64 max_int32 = std::numeric_limits<int32>::max();
112  if (data_length + data_offset > g_cld_mmap.Get().value->length() ||
113      data_length > max_int32) {  // max signed 32 bit integer
114    LOG(ERROR) << "Illegal mmap config: data_offset=" << data_offset
115               << ", data_length=" << data_length
116               << ", mmap->length()=" << g_cld_mmap.Get().value->length();
117    delete g_cld_mmap.Get().value;
118    g_cld_mmap.Get().value = NULL;
119    return;
120  }
121
122  // Initialize the CLD subsystem... and it's all done!
123  const uint8* data_ptr = g_cld_mmap.Get().value->data() + data_offset;
124  CLD2::loadDataFromRawAddress(data_ptr, data_length);
125  DCHECK(CLD2::isDataLoaded()) << "Failed to load CLD data from mmap";
126  if (!cld_available_callback_.is_null()) {
127    cld_available_callback_.Run();
128  }
129}
130
131}  // namespace translate
132