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