Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / translate / content / browser / data_file_browser_cld_data_provider.cc
blob18e7b8ea28791cb0b017acb38581eae8a8b4e3e3
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.
5 #include "components/translate/content/browser/data_file_browser_cld_data_provider.h"
7 #include "base/basictypes.h"
8 #include "base/files/file.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/synchronization/lock.h"
15 #include "base/task_runner.h"
16 #include "components/translate/content/common/cld_data_source.h"
17 #include "components/translate/content/common/data_file_cld_data_provider_messages.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/web_contents.h"
22 #include "ipc/ipc_message.h"
23 #include "ipc/ipc_message_macros.h"
24 #include "ipc/ipc_platform_file.h"
26 namespace {
27 // The data file, cached as long as the process stays alive.
28 // We also track the offset at which the data starts, and its length.
29 base::FilePath g_cached_filepath; // guarded by g_file_lock_
30 base::File* g_cached_file = NULL; // guarded by g_file_lock_
31 uint64 g_cached_data_offset = 0; // guarded by g_file_lock_
32 uint64 g_cached_data_length = 0; // guarded by g_file_lock_
34 // Guards g_cached_filepath
35 base::LazyInstance<base::Lock> g_file_lock_;
36 } // namespace
38 namespace translate {
40 void SetCldDataFilePath(const base::FilePath& path) {
41 VLOG(1) << "Setting CLD data file path to: " << path.value();
42 base::AutoLock lock(g_file_lock_.Get());
43 if (g_cached_filepath == path)
44 return; // no change necessary
45 g_cached_filepath = path;
46 // For sanity, clean these other values up just in case.
47 g_cached_file = NULL;
48 g_cached_data_length = 0;
49 g_cached_data_offset = 0;
52 base::FilePath GetCldDataFilePath() {
53 base::AutoLock lock(g_file_lock_.Get());
54 if (g_cached_filepath.empty()) {
55 g_cached_filepath = translate::CldDataSource::Get()->GetCldDataFilePath();
57 return g_cached_filepath;
60 DataFileBrowserCldDataProvider::DataFileBrowserCldDataProvider(
61 content::WebContents* web_contents)
62 : web_contents_(web_contents), weak_pointer_factory_() {
65 DataFileBrowserCldDataProvider::~DataFileBrowserCldDataProvider() {
66 // web_contents_ outlives this object
69 bool DataFileBrowserCldDataProvider::OnMessageReceived(
70 const IPC::Message& message) {
71 bool handled = true;
72 IPC_BEGIN_MESSAGE_MAP(DataFileBrowserCldDataProvider, message)
73 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NeedCldDataFile, OnCldDataRequest)
74 IPC_MESSAGE_UNHANDLED(handled = false)
75 IPC_END_MESSAGE_MAP()
76 return handled;
79 void DataFileBrowserCldDataProvider::OnCldDataRequest() {
80 // Quickly try to read g_cached_file. If valid, the file handle is
81 // cached and can be used immediately. Else, queue the caching task to the
82 // blocking pool.
83 VLOG(1) << "Received request for CLD data file.";
84 base::File* handle = NULL;
85 uint64 data_offset = 0;
86 uint64 data_length = 0;
88 base::AutoLock lock(g_file_lock_.Get());
89 handle = g_cached_file;
90 data_offset = g_cached_data_offset;
91 data_length = g_cached_data_length;
94 if (handle && handle->IsValid()) {
95 // Cached data available. Respond to the request.
96 VLOG(1) << "CLD data file is already cached, replying immediately.";
97 SendCldDataResponseInternal(handle, data_offset, data_length);
98 return;
101 if (weak_pointer_factory_.get() == NULL) {
102 weak_pointer_factory_.reset(
103 new base::WeakPtrFactory<DataFileBrowserCldDataProvider>(this));
104 weak_pointer_factory_.get()->GetWeakPtr().get();
107 // Else, we don't have the data file yet. Queue a caching attempt.
108 // The caching attempt happens in the blocking pool because it may involve
109 // arbitrary filesystem access.
110 // After the caching attempt is made, we call MaybeSendCLDDataAvailable
111 // to pass the file handle to the renderer. This only results in an IPC
112 // message if the caching attempt was successful.
113 VLOG(1) << "CLD data file not yet cached, deferring lookup";
114 content::BrowserThread::PostBlockingPoolTaskAndReply(
115 FROM_HERE,
116 base::Bind(&DataFileBrowserCldDataProvider::OnCldDataRequestInternal),
117 base::Bind(&DataFileBrowserCldDataProvider::SendCldDataResponse,
118 weak_pointer_factory_.get()->GetWeakPtr()));
121 void DataFileBrowserCldDataProvider::SendCldDataResponse() {
122 base::File* handle = NULL;
123 uint64 data_offset = 0;
124 uint64 data_length = 0;
126 base::AutoLock lock(g_file_lock_.Get());
127 handle = g_cached_file;
128 data_offset = g_cached_data_offset;
129 data_length = g_cached_data_length;
132 if (handle && handle->IsValid())
133 SendCldDataResponseInternal(handle, data_offset, data_length);
136 void DataFileBrowserCldDataProvider::SendCldDataResponseInternal(
137 const base::File* handle,
138 const uint64 data_offset,
139 const uint64 data_length) {
140 VLOG(1) << "Sending CLD data file response.";
142 content::RenderViewHost* render_view_host =
143 web_contents_->GetRenderViewHost();
144 if (render_view_host == NULL) {
145 // Render view destroyed, no need to bother.
146 VLOG(1) << "Lost render view host, giving up";
147 return;
150 content::RenderProcessHost* render_process_host =
151 render_view_host->GetProcess();
152 if (render_process_host == NULL) {
153 // Render process destroyed, render view not yet dead. No need to bother.
154 VLOG(1) << "Lost render process, giving up";
155 return;
158 // Data available, respond to the request.
159 base::ProcessHandle render_process_handle = render_process_host->GetHandle();
160 IPC::PlatformFileForTransit ipc_platform_file =
161 IPC::GetFileHandleForProcess(handle->GetPlatformFile(),
162 render_process_handle, false);
164 // In general, sending a response from within the code path that is processing
165 // a request is discouraged because there is potential for deadlock (if the
166 // methods are sent synchronously) or loops (if the response can trigger a
167 // new request). Neither of these concerns is relevant in this code, so
168 // sending the response from within the code path of the request handler is
169 // safe.
170 render_view_host->Send(
171 new ChromeViewMsg_CldDataFileAvailable(render_view_host->GetRoutingID(),
172 ipc_platform_file,
173 data_offset,
174 data_length));
177 void DataFileBrowserCldDataProvider::OnCldDataRequestInternal() {
178 // Because this function involves arbitrary file system access, it must run
179 // on the blocking pool.
180 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
181 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
182 VLOG(1) << "CLD data file caching attempt starting.";
185 base::AutoLock lock(g_file_lock_.Get());
186 if (g_cached_file) {
187 VLOG(1) << "CLD data file is already cached, aborting caching attempt";
188 return; // Already done, duplicate request
192 const base::FilePath path = GetCldDataFilePath();
193 if (path.empty()) {
194 VLOG(1) << "CLD data file does not yet have a known location.";
195 return;
198 // If the file exists, we can send an IPC-safe construct back to the
199 // renderer process immediately; otherwise, nothing to do here.
200 if (!base::PathExists(path)) {
201 VLOG(1) << "CLD data file does not exist.";
202 return;
205 // Attempt to open the file for reading.
206 scoped_ptr<base::File> file(
207 new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
208 if (!file->IsValid()) {
209 LOG(WARNING) << "CLD data file exists but cannot be opened";
210 return;
213 base::File::Info file_info;
214 if (!file->GetInfo(&file_info)) {
215 LOG(WARNING) << "CLD data file exists but cannot be inspected";
216 return;
219 // For now, our offset and length are simply 0 and the length of the file,
220 // respectively. If we later decide to include the CLD2 data file inside of
221 // a larger binary context, these params can be twiddled appropriately.
222 const uint64 data_offset = 0;
223 const uint64 data_length = file_info.size;
226 base::AutoLock lock(g_file_lock_.Get());
227 if (g_cached_file) {
228 // Idempotence: Racing another request on the blocking pool, abort.
229 VLOG(1) << "Another thread finished caching first, aborting.";
230 } else {
231 // Else, this request has taken care of it all. Cache all info.
232 VLOG(1) << "Caching CLD data file information.";
233 g_cached_file = file.release();
234 g_cached_data_offset = data_offset;
235 g_cached_data_length = data_length;
240 } // namespace translate