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.
4
5 #include "ppapi/proxy/file_system_resource.h"
6
7 #include "base/bind.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"
16
17 using ppapi::thunk::EnterResourceNoLock;
18 using ppapi::thunk::PPB_FileIO_API;
19 using ppapi::thunk::PPB_FileSystem_API;
20
21 namespace ppapi {
22 namespace proxy {
23
QuotaRequest(int64_t amount_arg,const RequestQuotaCallback & callback_arg)24 FileSystemResource::QuotaRequest::QuotaRequest(
25 int64_t amount_arg,
26 const RequestQuotaCallback& callback_arg)
27 : amount(amount_arg),
28 callback(callback_arg) {
29 }
30
~QuotaRequest()31 FileSystemResource::QuotaRequest::~QuotaRequest() {
32 }
33
FileSystemResource(Connection connection,PP_Instance instance,PP_FileSystemType type)34 FileSystemResource::FileSystemResource(Connection connection,
35 PP_Instance instance,
36 PP_FileSystemType type)
37 : PluginResource(connection, instance),
38 type_(type),
39 called_open_(false),
40 callback_count_(0),
41 callback_result_(PP_OK),
42 reserved_quota_(0),
43 reserving_quota_(false) {
44 DCHECK(type_ != PP_FILESYSTEMTYPE_INVALID);
45 SendCreate(RENDERER, PpapiHostMsg_FileSystem_Create(type_));
46 SendCreate(BROWSER, PpapiHostMsg_FileSystem_Create(type_));
47 }
48
FileSystemResource(Connection connection,PP_Instance instance,int pending_renderer_id,int pending_browser_id,PP_FileSystemType type)49 FileSystemResource::FileSystemResource(Connection connection,
50 PP_Instance instance,
51 int pending_renderer_id,
52 int pending_browser_id,
53 PP_FileSystemType type)
54 : PluginResource(connection, instance),
55 type_(type),
56 called_open_(true),
57 callback_count_(0),
58 callback_result_(PP_OK),
59 reserved_quota_(0),
60 reserving_quota_(false) {
61 DCHECK(type_ != PP_FILESYSTEMTYPE_INVALID);
62 AttachToPendingHost(RENDERER, pending_renderer_id);
63 AttachToPendingHost(BROWSER, pending_browser_id);
64 }
65
~FileSystemResource()66 FileSystemResource::~FileSystemResource() {
67 }
68
AsPPB_FileSystem_API()69 PPB_FileSystem_API* FileSystemResource::AsPPB_FileSystem_API() {
70 return this;
71 }
72
Open(int64_t expected_size,scoped_refptr<TrackedCallback> callback)73 int32_t FileSystemResource::Open(int64_t expected_size,
74 scoped_refptr<TrackedCallback> callback) {
75 DCHECK(type_ != PP_FILESYSTEMTYPE_ISOLATED);
76 if (called_open_)
77 return PP_ERROR_FAILED;
78 called_open_ = true;
79
80 Call<PpapiPluginMsg_FileSystem_OpenReply>(RENDERER,
81 PpapiHostMsg_FileSystem_Open(expected_size),
82 base::Bind(&FileSystemResource::OpenComplete,
83 this,
84 callback));
85 Call<PpapiPluginMsg_FileSystem_OpenReply>(BROWSER,
86 PpapiHostMsg_FileSystem_Open(expected_size),
87 base::Bind(&FileSystemResource::OpenComplete,
88 this,
89 callback));
90 return PP_OK_COMPLETIONPENDING;
91 }
92
GetType()93 PP_FileSystemType FileSystemResource::GetType() {
94 return type_;
95 }
96
OpenQuotaFile(PP_Resource file_io)97 void FileSystemResource::OpenQuotaFile(PP_Resource file_io) {
98 DCHECK(!ContainsKey(files_, file_io));
99 files_.insert(file_io);
100 }
101
CloseQuotaFile(PP_Resource file_io)102 void FileSystemResource::CloseQuotaFile(PP_Resource file_io) {
103 DCHECK(ContainsKey(files_, file_io));
104 files_.erase(file_io);
105 }
106
RequestQuota(int64_t amount,const RequestQuotaCallback & callback)107 int64_t FileSystemResource::RequestQuota(
108 int64_t amount,
109 const RequestQuotaCallback& callback) {
110 DCHECK(amount >= 0);
111 if (!reserving_quota_ && reserved_quota_ >= amount) {
112 reserved_quota_ -= amount;
113 return amount;
114 }
115
116 // Queue up a pending quota request.
117 pending_quota_requests_.push(QuotaRequest(amount, callback));
118
119 // Reserve more quota if we haven't already.
120 if (!reserving_quota_)
121 ReserveQuota(amount);
122
123 return PP_OK_COMPLETIONPENDING;
124 }
125
InitIsolatedFileSystem(const std::string & fsid,PP_IsolatedFileSystemType_Private type,const base::Callback<void (int32_t)> & callback)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);
133 if (called_open_)
134 return PP_ERROR_FAILED;
135 called_open_ = true;
136
137 Call<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply>(RENDERER,
138 PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid, type),
139 base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete,
140 this,
141 callback));
142 Call<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply>(BROWSER,
143 PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid, type),
144 base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete,
145 this,
146 callback));
147 return PP_OK_COMPLETIONPENDING;
148 }
149
OpenComplete(scoped_refptr<TrackedCallback> callback,const ResourceMessageReplyParams & params)150 void FileSystemResource::OpenComplete(
151 scoped_refptr<TrackedCallback> callback,
152 const ResourceMessageReplyParams& params) {
153 ++callback_count_;
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_);
160 }
161
InitIsolatedFileSystemComplete(const base::Callback<void (int32_t)> & callback,const ResourceMessageReplyParams & params)162 void FileSystemResource::InitIsolatedFileSystemComplete(
163 const base::Callback<void(int32_t)>& callback,
164 const ResourceMessageReplyParams& params) {
165 ++callback_count_;
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_);
172 }
173
ReserveQuota(int64_t amount)174 void FileSystemResource::ReserveQuota(int64_t amount) {
175 DCHECK(!reserving_quota_);
176 reserving_quota_ = true;
177
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()) {
183 NOTREACHED();
184 continue;
185 }
186 PPB_FileIO_API* file_io_api = enter.object();
187 file_growths[*it] = FileGrowth(
188 file_io_api->GetMaxWrittenOffset(),
189 file_io_api->GetAppendModeWriteAmount());
190 }
191 Call<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(BROWSER,
192 PpapiHostMsg_FileSystem_ReserveQuota(amount, file_growths),
193 base::Bind(&FileSystemResource::ReserveQuotaComplete,
194 this));
195 }
196
ReserveQuotaComplete(const ResourceMessageReplyParams & params,int64_t amount,const FileSizeMap & file_sizes)197 void FileSystemResource::ReserveQuotaComplete(
198 const ResourceMessageReplyParams& params,
199 int64_t amount,
200 const FileSizeMap& file_sizes) {
201 DCHECK(reserving_quota_);
202 reserving_quota_ = false;
203 reserved_quota_ = amount;
204
205 for (FileSizeMap::const_iterator it = file_sizes.begin();
206 it != file_sizes.end(); ++it) {
207 EnterResourceNoLock<PPB_FileIO_API> enter(it->first, true);
208
209 // It is possible that the host has sent an offset for a file that has been
210 // destroyed in the plugin. Ignore it.
211 if (enter.failed())
212 continue;
213 PPB_FileIO_API* file_io_api = enter.object();
214 file_io_api->SetMaxWrittenOffset(it->second);
215 file_io_api->SetAppendModeWriteAmount(0);
216 }
217
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();
224 if (fail_all) {
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();
231 } else {
232 // Refresh the quota reservation for the first pending request that we
233 // can't satisfy.
234 ReserveQuota(request.amount);
235 break;
236 }
237 }
238 }
239
240 } // namespace proxy
241 } // namespace ppapi
242