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