14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file.
44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "data_file_browser_cld_data_provider.h"
64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/basictypes.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/files/file.h"
94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/files/file_path.h"
104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/lazy_instance.h"
114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/logging.h"
124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/memory/weak_ptr.h"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/path_service.h"
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/synchronization/lock.h"
154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/task_runner.h"
164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "components/translate/content/common/data_file_cld_data_provider_messages.h"
17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/browser/render_process_host.h"
19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/browser/render_view_host.h"
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/browser/web_contents.h"
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ipc/ipc_message.h"
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ipc/ipc_message_macros.h"
23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ipc/ipc_platform_file.h"
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace {
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// The data file,  cached as long as the process stays alive.
274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// We also track the offset at which the data starts, and its length.
284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)base::FilePath g_cached_filepath;  // guarded by g_file_lock_
294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)base::File* g_cached_file = NULL;  // guarded by g_file_lock_
304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)uint64 g_cached_data_offset = -1;  // guarded by g_file_lock_
314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)uint64 g_cached_data_length = -1;  // guarded by g_file_lock_
324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Guards g_cached_filepath
344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)base::LazyInstance<base::Lock> g_file_lock_;
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace
364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace translate {
384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Implementation of the static factory method from BrowserCldDataProvider,
404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// hooking up this specific implementation for all of Chromium.
414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)BrowserCldDataProvider* CreateBrowserCldDataProviderFor(
424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    content::WebContents* web_contents) {
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  VLOG(1) << "Creating DataFileBrowserCldDataProvider";
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return new DataFileBrowserCldDataProvider(web_contents);
45010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void SetCldDataFilePath(const base::FilePath& path) {
48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  VLOG(1) << "Setting CLD data file path to: " << path.value();
49010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  base::AutoLock lock(g_file_lock_.Get());
50010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (g_cached_filepath == path)
51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;  // no change necessary
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  g_cached_filepath = path;
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // For sanity, clean these other values up just in case.
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  g_cached_file = NULL;
55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  g_cached_data_length = -1;
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  g_cached_data_offset = -1;
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
58010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
59010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)base::FilePath GetCldDataFilePath() {
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::AutoLock lock(g_file_lock_.Get());
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return g_cached_filepath;
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)DataFileBrowserCldDataProvider::DataFileBrowserCldDataProvider(
65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    content::WebContents* web_contents)
66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : web_contents_(web_contents), weak_pointer_factory_() {
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)DataFileBrowserCldDataProvider::~DataFileBrowserCldDataProvider() {
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool DataFileBrowserCldDataProvider::OnMessageReceived(
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const IPC::Message& message) {
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool handled = true;
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  IPC_BEGIN_MESSAGE_MAP(DataFileBrowserCldDataProvider, message)
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  IPC_MESSAGE_HANDLER(ChromeViewHostMsg_NeedCldDataFile, OnCldDataRequest)
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  IPC_MESSAGE_UNHANDLED(handled = false)
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  IPC_END_MESSAGE_MAP()
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return handled;
80010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
81010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void DataFileBrowserCldDataProvider::OnCldDataRequest() {
834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Quickly try to read g_cached_file. If valid, the file handle is
84010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // cached and can be used immediately. Else, queue the caching task to the
854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // blocking pool.
864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  VLOG(1) << "Received request for CLD data file.";
87010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  base::File* handle = NULL;
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  uint64 data_offset = 0;
89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  uint64 data_length = 0;
90010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  {
91010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    base::AutoLock lock(g_file_lock_.Get());
92010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    handle = g_cached_file;
93010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    data_offset = g_cached_data_offset;
94010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    data_length = g_cached_data_length;
954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
96
97  if (handle && handle->IsValid()) {
98    // Cached data available. Respond to the request.
99    VLOG(1) << "CLD data file is already cached, replying immediately.";
100    SendCldDataResponseInternal(handle, data_offset, data_length);
101    return;
102  }
103
104  if (weak_pointer_factory_.get() == NULL) {
105    weak_pointer_factory_.reset(
106        new base::WeakPtrFactory<DataFileBrowserCldDataProvider>(this));
107    weak_pointer_factory_.get()->GetWeakPtr().get();
108  }
109
110  // Else, we don't have the data file yet. Queue a caching attempt.
111  // The caching attempt happens in the blocking pool because it may involve
112  // arbitrary filesystem access.
113  // After the caching attempt is made, we call MaybeSendCLDDataAvailable
114  // to pass the file handle to the renderer. This only results in an IPC
115  // message if the caching attempt was successful.
116  VLOG(1) << "CLD data file not yet cached, deferring lookup";
117  content::BrowserThread::PostBlockingPoolTaskAndReply(
118      FROM_HERE,
119      base::Bind(&DataFileBrowserCldDataProvider::OnCldDataRequestInternal),
120      base::Bind(&DataFileBrowserCldDataProvider::SendCldDataResponse,
121                 weak_pointer_factory_.get()->GetWeakPtr()));
122}
123
124void DataFileBrowserCldDataProvider::SendCldDataResponse() {
125  base::File* handle = NULL;
126  uint64 data_offset = 0;
127  uint64 data_length = 0;
128  {
129    base::AutoLock lock(g_file_lock_.Get());
130    handle = g_cached_file;
131    data_offset = g_cached_data_offset;
132    data_length = g_cached_data_length;
133  }
134
135  if (handle && handle->IsValid())
136    SendCldDataResponseInternal(handle, data_offset, data_length);
137}
138
139void DataFileBrowserCldDataProvider::SendCldDataResponseInternal(
140    const base::File* handle,
141    const uint64 data_offset,
142    const uint64 data_length) {
143  VLOG(1) << "Sending CLD data file response.";
144
145  content::RenderViewHost* render_view_host =
146      web_contents_->GetRenderViewHost();
147  if (render_view_host == NULL) {
148    // Render view destroyed, no need to bother.
149    VLOG(1) << "Lost render view host, giving up";
150    return;
151  }
152
153  content::RenderProcessHost* render_process_host =
154      render_view_host->GetProcess();
155  if (render_process_host == NULL) {
156    // Render process destroyed, render view not yet dead. No need to bother.
157    VLOG(1) << "Lost render process, giving up";
158    return;
159  }
160
161  // Data available, respond to the request.
162  const int render_process_handle = render_process_host->GetHandle();
163  IPC::PlatformFileForTransit ipc_platform_file =
164      IPC::GetFileHandleForProcess(handle->GetPlatformFile(),
165                                   render_process_handle, false);
166
167  // In general, sending a response from within the code path that is processing
168  // a request is discouraged because there is potential for deadlock (if the
169  // methods are sent synchronously) or loops (if the response can trigger a
170  // new request). Neither of these concerns is relevant in this code, so
171  // sending the response from within the code path of the request handler is
172  // safe.
173  render_view_host->Send(
174      new ChromeViewMsg_CldDataFileAvailable(render_view_host->GetRoutingID(),
175                                             ipc_platform_file,
176                                             data_offset,
177                                             data_length));
178}
179
180void DataFileBrowserCldDataProvider::OnCldDataRequestInternal() {
181  // Because this function involves arbitrary file system access, it must run
182  // on the blocking pool.
183  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
184  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
185  VLOG(1) << "CLD data file caching attempt starting.";
186
187  {
188    base::AutoLock lock(g_file_lock_.Get());
189    if (g_cached_file) {
190      VLOG(1) << "CLD data file is already cached, aborting caching attempt";
191      return;  // Already done, duplicate request
192    }
193  }
194
195  const base::FilePath path = GetCldDataFilePath();
196  if (path.empty()) {
197    VLOG(1) << "CLD data file does not yet have a known location.";
198    return;
199  }
200
201  // If the file exists, we can send an IPC-safe construct back to the
202  // renderer process immediately; otherwise, nothing to do here.
203  if (!base::PathExists(path)) {
204    VLOG(1) << "CLD data file does not exist.";
205    return;
206  }
207
208  // Attempt to open the file for reading.
209  scoped_ptr<base::File> file(
210      new base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ));
211  if (!file->IsValid()) {
212    LOG(WARNING) << "CLD data file exists but cannot be opened";
213    return;
214  }
215
216  base::File::Info file_info;
217  if (!file->GetInfo(&file_info)) {
218    LOG(WARNING) << "CLD data file exists but cannot be inspected";
219    return;
220  }
221
222  // For now, our offset and length are simply 0 and the length of the file,
223  // respectively. If we later decide to include the CLD2 data file inside of
224  // a larger binary context, these params can be twiddled appropriately.
225  const uint64 data_offset = 0;
226  const uint64 data_length = file_info.size;
227
228  {
229    base::AutoLock lock(g_file_lock_.Get());
230    if (g_cached_file) {
231      // Idempotence: Racing another request on the blocking pool, abort.
232      VLOG(1) << "Another thread finished caching first, aborting.";
233    } else {
234      // Else, this request has taken care of it all. Cache all info.
235      VLOG(1) << "Caching CLD data file information.";
236      g_cached_file = file.release();
237      g_cached_data_offset = data_offset;
238      g_cached_data_length = data_length;
239    }
240  }
241}
242
243}  // namespace translate
244