IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_io_host.cc
blobb5f188af257e445e5695690fe474e63405acd96d
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
45 GetUIThreadStuffForInternalFileSystems(int render_process_id) {
46 PepperFileIOHost::UIThreadStuff stuff;
47 DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::UI));
69 ContentBrowserClient* client = GetContentClient()->browser();
70 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
71 return client->IsPluginAllowedToCallRequestOSFileHandle(
72 host->GetBrowserContext(), document_url);
75 bool FileOpenForWrite(int32_t open_flags) {
76 return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0;
79 } // namespace
81 PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
82 PP_Instance instance,
83 PP_Resource resource)
84 : ResourceHost(host->GetPpapiHost(), instance, resource),
85 browser_ppapi_host_(host),
86 render_process_host_(NULL),
87 file_(base::kInvalidPlatformFileValue),
88 open_flags_(0),
89 file_system_type_(PP_FILESYSTEMTYPE_INVALID),
90 max_written_offset_(0),
91 check_quota_(false),
92 weak_factory_(this) {
93 int unused;
94 if (!host->GetRenderFrameIDsForInstance(instance,
95 &render_process_id_,
96 &unused)) {
97 render_process_id_ = -1;
99 file_message_loop_ = BrowserThread::GetMessageLoopProxyForThread(
100 BrowserThread::FILE);
103 PepperFileIOHost::~PepperFileIOHost() {
106 int32_t PepperFileIOHost::OnResourceMessageReceived(
107 const IPC::Message& msg,
108 ppapi::host::HostMessageContext* context) {
109 IPC_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg)
110 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open,
111 OnHostMsgOpen)
112 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch,
113 OnHostMsgTouch)
114 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength,
115 OnHostMsgSetLength)
116 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush,
117 OnHostMsgFlush)
118 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close,
119 OnHostMsgClose)
120 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle,
121 OnHostMsgRequestOSFileHandle)
122 IPC_END_MESSAGE_MAP()
123 return PP_ERROR_FAILED;
126 PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
127 resolved_render_process_id = base::kNullProcessId;
130 PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {
133 int32_t PepperFileIOHost::OnHostMsgOpen(
134 ppapi::host::HostMessageContext* context,
135 PP_Resource file_ref_resource,
136 int32_t open_flags) {
137 int32_t rv = state_manager_.CheckOperationState(
138 FileIOStateManager::OPERATION_EXCLUSIVE, false);
139 if (rv != PP_OK)
140 return rv;
142 int platform_file_flags = 0;
143 if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags,
144 &platform_file_flags))
145 return PP_ERROR_BADARGUMENT;
147 ppapi::host::ResourceHost* resource_host =
148 host()->GetResourceHost(file_ref_resource);
149 if (!resource_host || !resource_host->IsFileRefHost())
150 return PP_ERROR_BADRESOURCE;
151 PepperFileRefHost* file_ref_host =
152 static_cast<PepperFileRefHost*>(resource_host);
153 if (file_ref_host->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID)
154 return PP_ERROR_FAILED;
156 file_system_host_ = file_ref_host->GetFileSystemHost();
158 open_flags_ = open_flags;
159 file_system_type_ = file_ref_host->GetFileSystemType();
160 file_system_url_ = file_ref_host->GetFileSystemURL();
162 if (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) {
163 if (!file_system_url_.is_valid())
164 return PP_ERROR_BADARGUMENT;
165 if (!CanOpenFileSystemURLWithPepperFlags(open_flags,
166 render_process_id_,
167 file_system_url_))
168 return PP_ERROR_NOACCESS;
169 BrowserThread::PostTaskAndReplyWithResult(
170 BrowserThread::UI,
171 FROM_HERE,
172 base::Bind(&GetUIThreadStuffForInternalFileSystems,
173 render_process_id_),
174 base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems,
175 weak_factory_.GetWeakPtr(),
176 context->MakeReplyMessageContext(),
177 platform_file_flags));
178 } else {
179 base::FilePath path = file_ref_host->GetExternalFilePath();
180 if (!CanOpenWithPepperFlags(open_flags, render_process_id_, path))
181 return PP_ERROR_NOACCESS;
182 BrowserThread::PostTaskAndReplyWithResult(
183 BrowserThread::UI,
184 FROM_HERE,
185 base::Bind(&GetResolvedRenderProcessId, render_process_id_),
186 base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId,
187 weak_factory_.GetWeakPtr(),
188 context->MakeReplyMessageContext(),
189 path,
190 platform_file_flags));
192 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
193 return PP_OK_COMPLETIONPENDING;
196 void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
197 ppapi::host::ReplyMessageContext reply_context,
198 int platform_file_flags,
199 UIThreadStuff ui_thread_stuff) {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
201 file_system_context_ = ui_thread_stuff.file_system_context;
202 resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id;
203 if (resolved_render_process_id_ == base::kNullProcessId ||
204 !file_system_context_.get()) {
205 reply_context.params.set_result(PP_ERROR_FAILED);
206 SendOpenErrorReply(reply_context);
207 return;
210 if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) {
211 reply_context.params.set_result(PP_ERROR_FAILED);
212 SendOpenErrorReply(reply_context);
213 return;
216 DCHECK(file_system_host_.get());
217 DCHECK(file_system_host_->GetFileSystemOperationRunner());
218 file_system_host_->GetFileSystemOperationRunner()->OpenFile(
219 file_system_url_,
220 platform_file_flags,
221 base::Bind(&PepperFileIOHost::DidOpenInternalFile,
222 weak_factory_.GetWeakPtr(),
223 reply_context));
226 void PepperFileIOHost::DidOpenInternalFile(
227 ppapi::host::ReplyMessageContext reply_context,
228 base::PlatformFileError result,
229 base::PlatformFile file,
230 const base::Closure& on_close_callback) {
231 if (result == base::PLATFORM_FILE_OK) {
232 on_close_callback_ = on_close_callback;
234 if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) {
235 check_quota_ = true;
236 file_system_host_->OpenQuotaFile(
237 this,
238 file_system_url_.path(),
239 base::Bind(&PepperFileIOHost::DidOpenQuotaFile,
240 weak_factory_.GetWeakPtr(),
241 reply_context,
242 file));
243 return;
247 ExecutePlatformOpenFileCallback(
248 reply_context, result, base::PassPlatformFile(&file), true);
251 void PepperFileIOHost::GotResolvedRenderProcessId(
252 ppapi::host::ReplyMessageContext reply_context,
253 base::FilePath path,
254 int platform_file_flags,
255 base::ProcessId resolved_render_process_id) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
257 resolved_render_process_id_ = resolved_render_process_id;
258 base::FileUtilProxy::CreateOrOpen(
259 file_message_loop_,
260 path,
261 platform_file_flags,
262 base::Bind(&PepperFileIOHost::ExecutePlatformOpenFileCallback,
263 weak_factory_.GetWeakPtr(),
264 reply_context));
267 int32_t PepperFileIOHost::OnHostMsgTouch(
268 ppapi::host::HostMessageContext* context,
269 PP_Time last_access_time,
270 PP_Time last_modified_time) {
271 int32_t rv = state_manager_.CheckOperationState(
272 FileIOStateManager::OPERATION_EXCLUSIVE, true);
273 if (rv != PP_OK)
274 return rv;
276 if (!base::FileUtilProxy::Touch(
277 file_message_loop_,
278 file_,
279 PPTimeToTime(last_access_time),
280 PPTimeToTime(last_modified_time),
281 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
282 weak_factory_.GetWeakPtr(),
283 context->MakeReplyMessageContext())))
284 return PP_ERROR_FAILED;
286 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
287 return PP_OK_COMPLETIONPENDING;
290 int32_t PepperFileIOHost::OnHostMsgSetLength(
291 ppapi::host::HostMessageContext* context,
292 int64_t length) {
293 int32_t rv = state_manager_.CheckOperationState(
294 FileIOStateManager::OPERATION_EXCLUSIVE, true);
295 if (rv != PP_OK)
296 return rv;
297 if (length < 0)
298 return PP_ERROR_BADARGUMENT;
300 // Quota checks are performed on the plugin side, in order to use the same
301 // quota reservation and request system as Write.
303 if (!base::FileUtilProxy::Truncate(
304 file_message_loop_,
305 file_,
306 length,
307 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
308 weak_factory_.GetWeakPtr(),
309 context->MakeReplyMessageContext())))
310 return PP_ERROR_FAILED;
312 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
313 return PP_OK_COMPLETIONPENDING;
316 int32_t PepperFileIOHost::OnHostMsgFlush(
317 ppapi::host::HostMessageContext* context) {
318 int32_t rv = state_manager_.CheckOperationState(
319 FileIOStateManager::OPERATION_EXCLUSIVE, true);
320 if (rv != PP_OK)
321 return rv;
323 if (!base::FileUtilProxy::Flush(
324 file_message_loop_,
325 file_,
326 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
327 weak_factory_.GetWeakPtr(),
328 context->MakeReplyMessageContext())))
329 return PP_ERROR_FAILED;
331 state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
332 return PP_OK_COMPLETIONPENDING;
335 int32_t PepperFileIOHost::OnHostMsgClose(
336 ppapi::host::HostMessageContext* context,
337 int64_t max_written_offset) {
338 if (check_quota_) {
339 file_system_host_->CloseQuotaFile(this, max_written_offset);
340 check_quota_ = false;
343 if (file_ != base::kInvalidPlatformFileValue) {
344 base::FileUtilProxy::Close(
345 file_message_loop_,
346 file_,
347 base::Bind(&PepperFileIOHost::DidCloseFile,
348 weak_factory_.GetWeakPtr()));
349 file_ = base::kInvalidPlatformFileValue;
351 return PP_OK;
354 void PepperFileIOHost::DidOpenQuotaFile(
355 ppapi::host::ReplyMessageContext reply_context,
356 base::PlatformFile file,
357 int64_t max_written_offset) {
358 max_written_offset_ = max_written_offset;
360 ExecutePlatformOpenFileCallback(
361 reply_context, base::PLATFORM_FILE_OK, base::PassPlatformFile(&file),
362 true);
365 void PepperFileIOHost::DidCloseFile(base::PlatformFileError error) {
366 // Silently ignore if we fail to close the file.
367 if (!on_close_callback_.is_null()) {
368 on_close_callback_.Run();
369 on_close_callback_.Reset();
373 int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
374 ppapi::host::HostMessageContext* context) {
375 if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota())
376 return PP_ERROR_FAILED;
378 GURL document_url =
379 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance());
380 BrowserThread::PostTaskAndReplyWithResult(
381 BrowserThread::UI,
382 FROM_HERE,
383 base::Bind(&GetPluginAllowedToCallRequestOSFileHandle,
384 render_process_id_,
385 document_url),
386 base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle,
387 weak_factory_.GetWeakPtr(),
388 context->MakeReplyMessageContext()));
389 return PP_OK_COMPLETIONPENDING;
392 void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
393 ppapi::host::ReplyMessageContext reply_context,
394 bool plugin_allowed) {
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
396 if (!browser_ppapi_host_->external_plugin() ||
397 host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) ||
398 plugin_allowed) {
399 if (!AddFileToReplyContext(open_flags_, &reply_context))
400 reply_context.params.set_result(PP_ERROR_FAILED);
401 } else {
402 reply_context.params.set_result(PP_ERROR_NOACCESS);
404 host()->SendReply(reply_context,
405 PpapiPluginMsg_FileIO_RequestOSFileHandleReply());
408 void PepperFileIOHost::ExecutePlatformGeneralCallback(
409 ppapi::host::ReplyMessageContext reply_context,
410 base::PlatformFileError error_code) {
411 reply_context.params.set_result(
412 ppapi::PlatformFileErrorToPepperError(error_code));
413 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
414 state_manager_.SetOperationFinished();
417 void PepperFileIOHost::ExecutePlatformOpenFileCallback(
418 ppapi::host::ReplyMessageContext reply_context,
419 base::PlatformFileError error_code,
420 base::PassPlatformFile file,
421 bool unused_created) {
422 int32_t pp_error = ppapi::PlatformFileErrorToPepperError(error_code);
423 DCHECK(file_ == base::kInvalidPlatformFileValue);
424 file_ = file.ReleaseValue();
426 if (file_ != base::kInvalidPlatformFileValue &&
427 !AddFileToReplyContext(open_flags_, &reply_context))
428 pp_error = PP_ERROR_FAILED;
430 PP_Resource quota_file_system = 0;
431 if (pp_error == PP_OK) {
432 state_manager_.SetOpenSucceed();
433 // A non-zero resource id signals the plugin side to check quota.
434 if (check_quota_)
435 quota_file_system = file_system_host_->pp_resource();
438 reply_context.params.set_result(pp_error);
439 host()->SendReply(reply_context,
440 PpapiPluginMsg_FileIO_OpenReply(quota_file_system,
441 max_written_offset_));
442 state_manager_.SetOperationFinished();
445 void PepperFileIOHost::SendOpenErrorReply(
446 ppapi::host::ReplyMessageContext reply_context) {
447 host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0));
450 bool PepperFileIOHost::AddFileToReplyContext(
451 int32_t open_flags,
452 ppapi::host::ReplyMessageContext* reply_context) const {
453 base::ProcessId plugin_process_id =
454 base::GetProcId(browser_ppapi_host_->GetPluginProcessHandle());
455 if (plugin_process_id == base::kNullProcessId)
456 plugin_process_id = resolved_render_process_id_;
458 IPC::PlatformFileForTransit transit_file = BrokerGetFileHandleForProcess(
459 file_, plugin_process_id, false);
460 if (transit_file == IPC::InvalidPlatformFileForTransit())
461 return false;
463 ppapi::proxy::SerializedHandle file_handle;
464 // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
465 PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0;
466 file_handle.set_file_handle(transit_file, open_flags, quota_file_io);
467 reply_context->params.AppendHandle(file_handle);
468 return true;
471 } // namespace content