5d6161e8cf2c5afae4225e9b62b2c37a6492f961
[platform/framework/web/crosswalk.git] / src / ppapi / proxy / file_system_resource.cc
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
24 FileSystemResource::QuotaRequest::QuotaRequest(
25     int64_t amount_arg,
26     const RequestQuotaCallback& callback_arg)
27     : amount(amount_arg),
28       callback(callback_arg) {
29 }
30
31 FileSystemResource::QuotaRequest::~QuotaRequest() {
32 }
33
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
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
66 FileSystemResource::~FileSystemResource() {
67 }
68
69 PPB_FileSystem_API* FileSystemResource::AsPPB_FileSystem_API() {
70   return this;
71 }
72
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
93 PP_FileSystemType FileSystemResource::GetType() {
94   return type_;
95 }
96
97 void FileSystemResource::OpenQuotaFile(PP_Resource file_io) {
98   DCHECK(!ContainsKey(files_, file_io));
99   files_.insert(file_io);
100 }
101
102 void FileSystemResource::CloseQuotaFile(PP_Resource file_io) {
103   DCHECK(ContainsKey(files_, file_io));
104   files_.erase(file_io);
105 }
106
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
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
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
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
174 void FileSystemResource::ReserveQuota(int64_t amount) {
175   DCHECK(!reserving_quota_);
176   reserving_quota_ = true;
177
178   // TODO(tzik): Use FileGrowthMap here after the IPC signature changed.
179   FileSizeMap file_sizes;
180   for (std::set<PP_Resource>::iterator it = files_.begin();
181        it != files_.end(); ++it) {
182     EnterResourceNoLock<PPB_FileIO_API> enter(*it, true);
183     if (enter.failed()) {
184       NOTREACHED();
185       continue;
186     }
187     PPB_FileIO_API* file_io_api = enter.object();
188     file_sizes[*it] =
189         file_io_api->GetMaxWrittenOffset() +
190         file_io_api->GetAppendModeWriteAmount();
191   }
192   Call<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(BROWSER,
193       PpapiHostMsg_FileSystem_ReserveQuota(amount, file_sizes),
194       base::Bind(&FileSystemResource::ReserveQuotaComplete,
195                  this));
196 }
197
198 void FileSystemResource::ReserveQuotaComplete(
199     const ResourceMessageReplyParams& params,
200     int64_t amount,
201     const FileSizeMap& max_written_offsets) {
202   DCHECK(reserving_quota_);
203   reserving_quota_ = false;
204   reserved_quota_ = amount;
205
206   for (FileSizeMap::const_iterator it = max_written_offsets.begin();
207       it != max_written_offsets.end(); ++it) {
208     EnterResourceNoLock<PPB_FileIO_API> enter(it->first, true);
209
210     // It is possible that the host has sent an offset for a file that has been
211     // destroyed in the plugin. Ignore it.
212     if (enter.failed())
213       continue;
214     PPB_FileIO_API* file_io_api = enter.object();
215     file_io_api->SetMaxWrittenOffset(it->second);
216     file_io_api->SetAppendModeWriteAmount(0);
217   }
218
219   DCHECK(!pending_quota_requests_.empty());
220   // If we can't grant the first request after refreshing reserved_quota_, then
221   // fail all pending quota requests to avoid an infinite refresh/fail loop.
222   bool fail_all = reserved_quota_ < pending_quota_requests_.front().amount;
223   while (!pending_quota_requests_.empty()) {
224     QuotaRequest& request = pending_quota_requests_.front();
225     if (fail_all) {
226       request.callback.Run(0);
227       pending_quota_requests_.pop();
228     } else if (reserved_quota_ >= request.amount) {
229       reserved_quota_ -= request.amount;
230       request.callback.Run(request.amount);
231       pending_quota_requests_.pop();
232     } else {
233       // Refresh the quota reservation for the first pending request that we
234       // can't satisfy.
235       ReserveQuota(request.amount);
236       break;
237     }
238   }
239 }
240
241 }  // namespace proxy
242 }  // namespace ppapi