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"
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_
;
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.
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
) {
72 IPC_BEGIN_MESSAGE_MAP(DataFileBrowserCldDataProvider
, message
)
73 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NeedCldDataFile
, OnCldDataRequest
)
74 IPC_MESSAGE_UNHANDLED(handled
= false)
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
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
);
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(
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";
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";
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
170 render_view_host
->Send(
171 new ChromeViewMsg_CldDataFileAvailable(render_view_host
->GetRoutingID(),
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());
187 VLOG(1) << "CLD data file is already cached, aborting caching attempt";
188 return; // Already done, duplicate request
192 const base::FilePath path
= GetCldDataFilePath();
194 VLOG(1) << "CLD data file does not yet have a known location.";
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.";
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";
213 base::File::Info file_info
;
214 if (!file
->GetInfo(&file_info
)) {
215 LOG(WARNING
) << "CLD data file exists but cannot be inspected";
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());
228 // Idempotence: Racing another request on the blocking pool, abort.
229 VLOG(1) << "Another thread finished caching first, aborting.";
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