Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_io_host.cc
blobe031eb1dad8234ebb033e961cd4c068c906bd08d
1 // Copyright 2013 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 "content/browser/renderer_host/pepper/pepper_file_io_host.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/files/file_util_proxy.h"
11 #include "base/memory/weak_ptr.h"
12 #include "content/browser/renderer_host/pepper/pepper_file_ref_host.h"
13 #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
14 #include "content/browser/renderer_host/pepper/pepper_security_helper.h"
15 #include "content/common/fileapi/file_system_messages.h"
16 #include "content/common/sandbox_util.h"
17 #include "content/common/view_messages.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/storage_partition.h"
22 #include "content/public/common/content_client.h"
23 #include "ppapi/c/pp_errors.h"
24 #include "ppapi/c/ppb_file_io.h"
25 #include "ppapi/host/dispatch_host_message.h"
26 #include "ppapi/host/ppapi_host.h"
27 #include "ppapi/proxy/ppapi_messages.h"
28 #include "ppapi/shared_impl/file_system_util.h"
29 #include "ppapi/shared_impl/file_type_conversion.h"
30 #include "ppapi/shared_impl/time_conversion.h"
31 #include "webkit/browser/fileapi/file_observers.h"
32 #include "webkit/browser/fileapi/file_system_context.h"
33 #include "webkit/browser/fileapi/file_system_operation_runner.h"
34 #include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
35 #include "webkit/common/fileapi/file_system_util.h"
37 namespace content {
39 using ppapi::FileIOStateManager;
40 using ppapi::PPTimeToTime;
42 namespace {
44 PepperFileIOHost::UIThreadStuff GetUIThreadStuffForInternalFileSystems(
45 int render_process_id) {
46 PepperFileIOHost::UIThreadStuff stuff;
47 DCHECK_CURRENTLY_ON(BrowserThread::UI);
48 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
49 if (host) {
50 stuff.resolved_render_process_id = base::GetProcId(host->GetHandle());
51 StoragePartition* storage_partition = host->GetStoragePartition();
52 if (storage_partition)
53 stuff.file_system_context = storage_partition->GetFileSystemContext();
55 return stuff;
58 base::ProcessId GetResolvedRenderProcessId(int render_process_id) {
59 DCHECK_CURRENTLY_ON(BrowserThread::UI);
60 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
61 if (!host)
62 return base::kNullProcessId;
63 return base::GetProcId(host->GetHandle());
66 bool GetPluginAllowedToCallRequestOSFileHandle(int render_process_id,
67 const GURL& document_url) {
68 DCHECK_CURRENTLY_ON(BrowserThread::UI);
69 ContentBrowserClient* client = GetContentClient()->browser();
70 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
71 if (!host)
72 return false;
73 return client->IsPluginAllowedToCallRequestOSFileHandle(
74 host->GetBrowserContext(), document_url);
77 bool FileOpenForWrite(int32_t open_flags) {
78 return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0;
81 void FileCloser(base::File auto_close) {
84 void DidCloseFile(const base::Closure& on_close_callback) {
85 if (!on_close_callback.is_null())
86 on_close_callback.Run();
89 void DidOpenFile(base::WeakPtr<PepperFileIOHost> file_host,
90 storage::FileSystemOperation::OpenFileCallback callback,
91 base::File file,
92 const base::Closure& on_close_callback) {
93 if (file_host) {
94 callback.Run(file.Pass(), on_close_callback);
95 } else {
96 BrowserThread::PostTaskAndReply(
97 BrowserThread::FILE,
98 FROM_HERE,
99 base::Bind(&FileCloser, base::Passed(&file)),
100 base::Bind(&DidCloseFile, on_close_callback));
104 } // namespace
106 PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
107 PP_Instance instance,
108 PP_Resource resource)
109 : ResourceHost(host->GetPpapiHost(), instance, resource),
110 browser_ppapi_host_(host),
111 render_process_host_(NULL),
112 file_(BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)),
113 open_flags_(0),
114 file_system_type_(PP_FILESYSTEMTYPE_INVALID),
115 max_written_offset_(0),
116 check_quota_(false) {
117 int unused;
118 if (!host->GetRenderFrameIDsForInstance(
119 instance, &render_process_id_, &unused)) {
120 render_process_id_ = -1;
124 PepperFileIOHost::~PepperFileIOHost() {}
126 int32_t PepperFileIOHost::OnResourceMessageReceived(
127 const IPC::Message& msg,
128 ppapi::host::HostMessageContext* context) {
129 PPAPI_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg)
130 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open, OnHostMsgOpen)
131 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch, OnHostMsgTouch)
132 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength,
133 OnHostMsgSetLength)
134 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush,
135 OnHostMsgFlush)
136 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close, OnHostMsgClose)
137 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle,
138 OnHostMsgRequestOSFileHandle)
139 PPAPI_END_MESSAGE_MAP()
140 return PP_ERROR_FAILED;
143 PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
144 resolved_render_process_id = base::kNullProcessId;
147 PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {}
149 int32_t PepperFileIOHost::OnHostMsgOpen(
150 ppapi::host::HostMessageContext* context,
151 PP_Resource file_ref_resource,
152 int32_t open_flags) {
153 int32_t rv = state_manager_.CheckOperationState(
154 FileIOStateManager::OPERATION_EXCLUSIVE, false);
155 if (rv != PP_OK)
156 return rv;
158 int platform_file_flags = 0;
159 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags,
160 &platform_file_flags))
161 return PP_ERROR_BADARGUMENT;
163 ppapi::host::ResourceHost* resource_host =
164 host()->GetResourceHost(file_ref_resource);
165 if (!resource_host || !resource_host->IsFileRefHost())
166 return PP_ERROR_BADRESOURCE;
167 PepperFileRefHost* file_ref_host =
168 static_cast<PepperFileRefHost*>(resource_host);
169 if (file_ref_host->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID)
170 return PP_ERROR_FAILED;
172 file_system_host_ = file_ref_host->GetFileSystemHost();
174 open_flags_ = open_flags;
175 file_system_type_ = file_ref_host->GetFileSystemType();
176 file_system_url_ = file_ref_host->GetFileSystemURL();
178 // For external file systems, if there is a valid FileSystemURL, then treat
179 // it like internal file systems and access it via the FileSystemURL.
180 bool is_internal_type = (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) ||
181 file_system_url_.is_valid();
183 if (is_internal_type) {
184 if (!file_system_url_.is_valid())
185 return PP_ERROR_BADARGUMENT;
187 // Not all external file systems are fully supported yet.
188 // Whitelist the supported ones.
189 if (file_system_url_.mount_type() == storage::kFileSystemTypeExternal) {
190 switch (file_system_url_.type()) {
191 case storage::kFileSystemTypeNativeMedia:
192 case storage::kFileSystemTypeDeviceMedia:
193 case storage::kFileSystemTypePicasa:
194 case storage::kFileSystemTypeItunes:
195 case storage::kFileSystemTypeIphoto:
196 break;
197 default:
198 return PP_ERROR_NOACCESS;
201 if (!CanOpenFileSystemURLWithPepperFlags(
202 open_flags, render_process_id_, file_system_url_))
203 return PP_ERROR_NOACCESS;
204 BrowserThread::PostTaskAndReplyWithResult(
205 BrowserThread::UI,
206 FROM_HERE,
207 base::Bind(&GetUIThreadStuffForInternalFileSystems, render_process_id_),
208 base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems,
209 AsWeakPtr(),
210 context->MakeReplyMessageContext(),
211 platform_file_flags));
212 } else {
213 base::FilePath path = file_ref_host->GetExternalFilePath();
214 if (!CanOpenWithPepperFlags(open_flags, render_process_id_, path))
215 return PP_ERROR_NOACCESS;
216 BrowserThread::PostTaskAndReplyWithResult(
217 BrowserThread::UI,
218 FROM_HERE,
219 base::Bind(&GetResolvedRenderProcessId, render_process_id_),
220 base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId,
221 AsWeakPtr(),
222 context->MakeReplyMessageContext(),
223 path,
224 platform_file_flags));
226 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
227 return PP_OK_COMPLETIONPENDING;
230 void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
231 ppapi::host::ReplyMessageContext reply_context,
232 int platform_file_flags,
233 UIThreadStuff ui_thread_stuff) {
234 DCHECK_CURRENTLY_ON(BrowserThread::IO);
235 file_system_context_ = ui_thread_stuff.file_system_context;
236 resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id;
237 if (resolved_render_process_id_ == base::kNullProcessId ||
238 !file_system_context_.get()) {
239 reply_context.params.set_result(PP_ERROR_FAILED);
240 SendOpenErrorReply(reply_context);
241 return;
244 if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) {
245 reply_context.params.set_result(PP_ERROR_FAILED);
246 SendOpenErrorReply(reply_context);
247 return;
250 DCHECK(file_system_host_.get());
251 DCHECK(file_system_host_->GetFileSystemOperationRunner());
253 file_system_host_->GetFileSystemOperationRunner()->OpenFile(
254 file_system_url_,
255 platform_file_flags,
256 base::Bind(&DidOpenFile,
257 AsWeakPtr(),
258 base::Bind(&PepperFileIOHost::DidOpenInternalFile,
259 AsWeakPtr(),
260 reply_context)));
263 void PepperFileIOHost::DidOpenInternalFile(
264 ppapi::host::ReplyMessageContext reply_context,
265 base::File file,
266 const base::Closure& on_close_callback) {
267 if (file.IsValid()) {
268 on_close_callback_ = on_close_callback;
270 if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) {
271 check_quota_ = true;
272 file_system_host_->OpenQuotaFile(
273 this,
274 file_system_url_,
275 base::Bind(&PepperFileIOHost::DidOpenQuotaFile,
276 AsWeakPtr(),
277 reply_context,
278 base::Passed(&file)));
279 return;
283 DCHECK(!file_.IsValid());
284 base::File::Error error =
285 file.IsValid() ? base::File::FILE_OK : file.error_details();
286 file_.SetFile(file.Pass());
287 OnOpenProxyCallback(reply_context, error);
290 void PepperFileIOHost::GotResolvedRenderProcessId(
291 ppapi::host::ReplyMessageContext reply_context,
292 base::FilePath path,
293 int file_flags,
294 base::ProcessId resolved_render_process_id) {
295 DCHECK_CURRENTLY_ON(BrowserThread::IO);
296 resolved_render_process_id_ = resolved_render_process_id;
297 file_.CreateOrOpen(
298 path,
299 file_flags,
300 base::Bind(&PepperFileIOHost::OnOpenProxyCallback,
301 AsWeakPtr(),
302 reply_context));
305 int32_t PepperFileIOHost::OnHostMsgTouch(
306 ppapi::host::HostMessageContext* context,
307 PP_Time last_access_time,
308 PP_Time last_modified_time) {
309 int32_t rv = state_manager_.CheckOperationState(
310 FileIOStateManager::OPERATION_EXCLUSIVE, true);
311 if (rv != PP_OK)
312 return rv;
314 if (!file_.SetTimes(
315 PPTimeToTime(last_access_time),
316 PPTimeToTime(last_modified_time),
317 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
318 AsWeakPtr(),
319 context->MakeReplyMessageContext()))) {
320 return PP_ERROR_FAILED;
323 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
324 return PP_OK_COMPLETIONPENDING;
327 int32_t PepperFileIOHost::OnHostMsgSetLength(
328 ppapi::host::HostMessageContext* context,
329 int64_t length) {
330 int32_t rv = state_manager_.CheckOperationState(
331 FileIOStateManager::OPERATION_EXCLUSIVE, true);
332 if (rv != PP_OK)
333 return rv;
334 if (length < 0)
335 return PP_ERROR_BADARGUMENT;
337 // Quota checks are performed on the plugin side, in order to use the same
338 // quota reservation and request system as Write.
340 if (!file_.SetLength(
341 length,
342 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
343 AsWeakPtr(),
344 context->MakeReplyMessageContext()))) {
345 return PP_ERROR_FAILED;
348 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
349 return PP_OK_COMPLETIONPENDING;
352 int32_t PepperFileIOHost::OnHostMsgFlush(
353 ppapi::host::HostMessageContext* context) {
354 int32_t rv = state_manager_.CheckOperationState(
355 FileIOStateManager::OPERATION_EXCLUSIVE, true);
356 if (rv != PP_OK)
357 return rv;
359 if (!file_.Flush(
360 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
361 AsWeakPtr(),
362 context->MakeReplyMessageContext()))) {
363 return PP_ERROR_FAILED;
366 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
367 return PP_OK_COMPLETIONPENDING;
370 int32_t PepperFileIOHost::OnHostMsgClose(
371 ppapi::host::HostMessageContext* context,
372 const ppapi::FileGrowth& file_growth) {
373 if (check_quota_) {
374 file_system_host_->CloseQuotaFile(this, file_growth);
375 check_quota_ = false;
378 if (file_.IsValid()) {
379 file_.Close(base::Bind(&PepperFileIOHost::DidCloseFile,
380 AsWeakPtr()));
382 return PP_OK;
385 void PepperFileIOHost::DidOpenQuotaFile(
386 ppapi::host::ReplyMessageContext reply_context,
387 base::File file,
388 int64_t max_written_offset) {
389 DCHECK(!file_.IsValid());
390 DCHECK(file.IsValid());
391 max_written_offset_ = max_written_offset;
392 file_.SetFile(file.Pass());
394 OnOpenProxyCallback(reply_context, base::File::FILE_OK);
397 void PepperFileIOHost::DidCloseFile(base::File::Error /*error*/) {
398 // Silently ignore if we fail to close the file.
399 if (!on_close_callback_.is_null()) {
400 on_close_callback_.Run();
401 on_close_callback_.Reset();
405 int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
406 ppapi::host::HostMessageContext* context) {
407 if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota())
408 return PP_ERROR_FAILED;
410 GURL document_url =
411 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance());
412 BrowserThread::PostTaskAndReplyWithResult(
413 BrowserThread::UI,
414 FROM_HERE,
415 base::Bind(&GetPluginAllowedToCallRequestOSFileHandle,
416 render_process_id_,
417 document_url),
418 base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle,
419 AsWeakPtr(),
420 context->MakeReplyMessageContext()));
421 return PP_OK_COMPLETIONPENDING;
424 void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
425 ppapi::host::ReplyMessageContext reply_context,
426 bool plugin_allowed) {
427 DCHECK_CURRENTLY_ON(BrowserThread::IO);
428 if (!browser_ppapi_host_->external_plugin() ||
429 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) ||
430 plugin_allowed) {
431 if (!AddFileToReplyContext(open_flags_, &reply_context))
432 reply_context.params.set_result(PP_ERROR_FAILED);
433 } else {
434 reply_context.params.set_result(PP_ERROR_NOACCESS);
436 host()->SendReply(reply_context,
437 PpapiPluginMsg_FileIO_RequestOSFileHandleReply());
440 void PepperFileIOHost::ExecutePlatformGeneralCallback(
441 ppapi::host::ReplyMessageContext reply_context,
442 base::File::Error error_code) {
443 reply_context.params.set_result(ppapi::FileErrorToPepperError(error_code));
444 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
445 state_manager_.SetOperationFinished();
448 void PepperFileIOHost::OnOpenProxyCallback(
449 ppapi::host::ReplyMessageContext reply_context,
450 base::File::Error error_code) {
451 int32_t pp_error = ppapi::FileErrorToPepperError(error_code);
452 if (file_.IsValid() && !AddFileToReplyContext(open_flags_, &reply_context))
453 pp_error = PP_ERROR_FAILED;
455 PP_Resource quota_file_system = 0;
456 if (pp_error == PP_OK) {
457 state_manager_.SetOpenSucceed();
458 // A non-zero resource id signals the plugin side to check quota.
459 if (check_quota_)
460 quota_file_system = file_system_host_->pp_resource();
463 reply_context.params.set_result(pp_error);
464 host()->SendReply(
465 reply_context,
466 PpapiPluginMsg_FileIO_OpenReply(quota_file_system, max_written_offset_));
467 state_manager_.SetOperationFinished();
470 void PepperFileIOHost::SendOpenErrorReply(
471 ppapi::host::ReplyMessageContext reply_context) {
472 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0));
475 bool PepperFileIOHost::AddFileToReplyContext(
476 int32_t open_flags,
477 ppapi::host::ReplyMessageContext* reply_context) const {
478 base::ProcessId plugin_process_id =
479 base::GetProcId(browser_ppapi_host_->GetPluginProcessHandle());
480 if (plugin_process_id == base::kNullProcessId)
481 plugin_process_id = resolved_render_process_id_;
483 IPC::PlatformFileForTransit transit_file =
484 BrokerGetFileHandleForProcess(file_.GetPlatformFile(), plugin_process_id,
485 false);
486 if (transit_file == IPC::InvalidPlatformFileForTransit())
487 return false;
489 ppapi::proxy::SerializedHandle file_handle;
490 // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
491 PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0;
492 file_handle.set_file_handle(transit_file, open_flags, quota_file_io);
493 reply_context->params.AppendHandle(file_handle);
494 return true;
497 } // namespace content