1 // Copyright (c) 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 "ppapi/proxy/file_system_resource.h"
8 #include "base/stl_util.h"
9 #include "ipc/ipc_message.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/proxy/ppapi_messages.h"
12 #include "ppapi/shared_impl/file_growth.h"
13 #include "ppapi/shared_impl/tracked_callback.h"
14 #include "ppapi/thunk/enter.h"
15 #include "ppapi/thunk/ppb_file_io_api.h"
17 using ppapi::thunk::EnterResourceNoLock
;
18 using ppapi::thunk::PPB_FileIO_API
;
19 using ppapi::thunk::PPB_FileSystem_API
;
24 FileSystemResource::QuotaRequest::QuotaRequest(
26 const RequestQuotaCallback
& callback_arg
)
28 callback(callback_arg
) {
31 FileSystemResource::QuotaRequest::~QuotaRequest() {
34 FileSystemResource::FileSystemResource(Connection connection
,
36 PP_FileSystemType type
)
37 : PluginResource(connection
, instance
),
41 callback_result_(PP_OK
),
43 reserving_quota_(false) {
44 DCHECK(type_
!= PP_FILESYSTEMTYPE_INVALID
);
45 SendCreate(RENDERER
, PpapiHostMsg_FileSystem_Create(type_
));
46 SendCreate(BROWSER
, PpapiHostMsg_FileSystem_Create(type_
));
49 FileSystemResource::FileSystemResource(Connection connection
,
51 int pending_renderer_id
,
52 int pending_browser_id
,
53 PP_FileSystemType type
)
54 : PluginResource(connection
, instance
),
58 callback_result_(PP_OK
),
60 reserving_quota_(false) {
61 DCHECK(type_
!= PP_FILESYSTEMTYPE_INVALID
);
62 AttachToPendingHost(RENDERER
, pending_renderer_id
);
63 AttachToPendingHost(BROWSER
, pending_browser_id
);
66 FileSystemResource::~FileSystemResource() {
69 PPB_FileSystem_API
* FileSystemResource::AsPPB_FileSystem_API() {
73 int32_t FileSystemResource::Open(int64_t expected_size
,
74 scoped_refptr
<TrackedCallback
> callback
) {
75 DCHECK(type_
!= PP_FILESYSTEMTYPE_ISOLATED
);
77 return PP_ERROR_FAILED
;
80 Call
<PpapiPluginMsg_FileSystem_OpenReply
>(RENDERER
,
81 PpapiHostMsg_FileSystem_Open(expected_size
),
82 base::Bind(&FileSystemResource::OpenComplete
,
85 Call
<PpapiPluginMsg_FileSystem_OpenReply
>(BROWSER
,
86 PpapiHostMsg_FileSystem_Open(expected_size
),
87 base::Bind(&FileSystemResource::OpenComplete
,
90 return PP_OK_COMPLETIONPENDING
;
93 PP_FileSystemType
FileSystemResource::GetType() {
97 void FileSystemResource::OpenQuotaFile(PP_Resource file_io
) {
98 DCHECK(!ContainsKey(files_
, file_io
));
99 files_
.insert(file_io
);
102 void FileSystemResource::CloseQuotaFile(PP_Resource file_io
) {
103 DCHECK(ContainsKey(files_
, file_io
));
104 files_
.erase(file_io
);
107 int64_t FileSystemResource::RequestQuota(
109 const RequestQuotaCallback
& callback
) {
111 if (!reserving_quota_
&& reserved_quota_
>= amount
) {
112 reserved_quota_
-= amount
;
116 // Queue up a pending quota request.
117 pending_quota_requests_
.push(QuotaRequest(amount
, callback
));
119 // Reserve more quota if we haven't already.
120 if (!reserving_quota_
)
121 ReserveQuota(amount
);
123 return PP_OK_COMPLETIONPENDING
;
126 int32_t FileSystemResource::InitIsolatedFileSystem(
127 const std::string
& fsid
,
128 PP_IsolatedFileSystemType_Private type
,
129 const base::Callback
<void(int32_t)>& callback
) {
130 // This call is mutually exclusive with Open() above, so we can reuse the
131 // called_open state.
132 DCHECK(type_
== PP_FILESYSTEMTYPE_ISOLATED
);
134 return PP_ERROR_FAILED
;
137 Call
<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply
>(RENDERER
,
138 PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid
, type
),
139 base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete
,
142 Call
<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply
>(BROWSER
,
143 PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid
, type
),
144 base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete
,
147 return PP_OK_COMPLETIONPENDING
;
150 void FileSystemResource::OpenComplete(
151 scoped_refptr
<TrackedCallback
> callback
,
152 const ResourceMessageReplyParams
& params
) {
154 // Prioritize worse result since only one status can be returned.
155 if (params
.result() != PP_OK
)
156 callback_result_
= params
.result();
157 // Received callback from browser and renderer.
158 if (callback_count_
== 2)
159 callback
->Run(callback_result_
);
162 void FileSystemResource::InitIsolatedFileSystemComplete(
163 const base::Callback
<void(int32_t)>& callback
,
164 const ResourceMessageReplyParams
& params
) {
166 // Prioritize worse result since only one status can be returned.
167 if (params
.result() != PP_OK
)
168 callback_result_
= params
.result();
169 // Received callback from browser and renderer.
170 if (callback_count_
== 2)
171 callback
.Run(callback_result_
);
174 void FileSystemResource::ReserveQuota(int64_t amount
) {
175 DCHECK(!reserving_quota_
);
176 reserving_quota_
= true;
178 FileGrowthMap file_growths
;
179 for (std::set
<PP_Resource
>::iterator it
= files_
.begin();
180 it
!= files_
.end(); ++it
) {
181 EnterResourceNoLock
<PPB_FileIO_API
> enter(*it
, true);
182 if (enter
.failed()) {
186 PPB_FileIO_API
* file_io_api
= enter
.object();
187 file_growths
[*it
] = FileGrowth(
188 file_io_api
->GetMaxWrittenOffset(),
189 file_io_api
->GetAppendModeWriteAmount());
191 Call
<PpapiPluginMsg_FileSystem_ReserveQuotaReply
>(BROWSER
,
192 PpapiHostMsg_FileSystem_ReserveQuota(amount
, file_growths
),
193 base::Bind(&FileSystemResource::ReserveQuotaComplete
,
197 void FileSystemResource::ReserveQuotaComplete(
198 const ResourceMessageReplyParams
& params
,
200 const FileSizeMap
& file_sizes
) {
201 DCHECK(reserving_quota_
);
202 reserving_quota_
= false;
203 reserved_quota_
= amount
;
205 for (FileSizeMap::const_iterator it
= file_sizes
.begin();
206 it
!= file_sizes
.end(); ++it
) {
207 EnterResourceNoLock
<PPB_FileIO_API
> enter(it
->first
, true);
209 // It is possible that the host has sent an offset for a file that has been
210 // destroyed in the plugin. Ignore it.
213 PPB_FileIO_API
* file_io_api
= enter
.object();
214 file_io_api
->SetMaxWrittenOffset(it
->second
);
215 file_io_api
->SetAppendModeWriteAmount(0);
218 DCHECK(!pending_quota_requests_
.empty());
219 // If we can't grant the first request after refreshing reserved_quota_, then
220 // fail all pending quota requests to avoid an infinite refresh/fail loop.
221 bool fail_all
= reserved_quota_
< pending_quota_requests_
.front().amount
;
222 while (!pending_quota_requests_
.empty()) {
223 QuotaRequest
& request
= pending_quota_requests_
.front();
225 request
.callback
.Run(0);
226 pending_quota_requests_
.pop();
227 } else if (reserved_quota_
>= request
.amount
) {
228 reserved_quota_
-= request
.amount
;
229 request
.callback
.Run(request
.amount
);
230 pending_quota_requests_
.pop();
232 // Refresh the quota reservation for the first pending request that we
234 ReserveQuota(request
.amount
);