- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / quota_file_io.cc
1 // Copyright (c) 2012 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 "content/renderer/pepper/quota_file_io.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/stl_util.h"
14 #include "base/task_runner_util.h"
15
16 using base::PlatformFile;
17 using base::PlatformFileError;
18 using quota::StorageType;
19
20 namespace content {
21
22 namespace {
23 StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) {
24   switch (type) {
25     case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
26       return quota::kStorageTypePersistent;
27     case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
28       return quota::kStorageTypeTemporary;
29     default:
30       return quota::kStorageTypeUnknown;
31   }
32   NOTREACHED();
33   return quota::kStorageTypeUnknown;
34 }
35
36 int WriteAdapter(PlatformFile file, int64 offset,
37                  scoped_ptr<char[]> data, int size) {
38   return base::WritePlatformFile(file, offset, data.get(), size);
39 }
40
41 }  // namespace
42
43 class QuotaFileIO::PendingOperationBase {
44  public:
45   virtual ~PendingOperationBase() {}
46
47   // Either one of Run() or DidFail() is called (the latter is called when
48   // there was more than one error during quota queries).
49   virtual void Run() = 0;
50   virtual void DidFail(PlatformFileError error) = 0;
51
52  protected:
53   explicit PendingOperationBase(QuotaFileIO* quota_io) : quota_io_(quota_io) {
54     DCHECK(quota_io_);
55     quota_io_->WillUpdate();
56   }
57
58   QuotaFileIO* quota_io_;
59 };
60
61 class QuotaFileIO::WriteOperation : public PendingOperationBase {
62  public:
63   WriteOperation(QuotaFileIO* quota_io,
64                  int64_t offset,
65                  const char* buffer,
66                  int32_t bytes_to_write,
67                  const WriteCallback& callback)
68       : PendingOperationBase(quota_io),
69         offset_(offset),
70         bytes_to_write_(bytes_to_write),
71         callback_(callback),
72         finished_(false),
73         status_(base::PLATFORM_FILE_OK),
74         bytes_written_(0),
75         weak_factory_(this) {
76     // TODO(kinuko): Check the API convention if we really need to keep a copy
77     // of the buffer during the async write operations.
78     buffer_.reset(new char[bytes_to_write]);
79     memcpy(buffer_.get(), buffer, bytes_to_write);
80   }
81   virtual ~WriteOperation() {}
82   virtual void Run() OVERRIDE {
83     DCHECK(quota_io_);
84     if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) {
85       DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
86       return;
87     }
88     DCHECK(buffer_.get());
89
90     if (!base::PostTaskAndReplyWithResult(
91             quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(),
92             FROM_HERE,
93             base::Bind(&WriteAdapter,
94                        quota_io_->file_,
95                        offset_,
96                        base::Passed(&buffer_),
97                        bytes_to_write_),
98             base::Bind(&WriteOperation::DidWrite,
99                        weak_factory_.GetWeakPtr()))) {
100       DidFail(base::PLATFORM_FILE_ERROR_FAILED);
101       return;
102     }
103   }
104
105   virtual void DidFail(PlatformFileError error) OVERRIDE {
106     DidFinish(error, 0);
107   }
108
109   bool finished() const { return finished_; }
110
111   virtual void WillRunCallback() {
112     base::MessageLoopProxy::current()->PostTask(
113         FROM_HERE,
114         base::Bind(&WriteOperation::RunCallback, weak_factory_.GetWeakPtr()));
115   }
116
117  private:
118   void DidWrite(int bytes_written) {
119     base::PlatformFileError error = bytes_written > 0 ?
120         base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_FAILED;
121     DidFinish(error, bytes_written);
122   }
123
124   void DidFinish(PlatformFileError status, int bytes_written) {
125     finished_ = true;
126     status_ = status;
127     bytes_written_ = bytes_written;
128     int64_t max_offset =
129         (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written;
130     // This may delete itself by calling RunCallback.
131     quota_io_->DidWrite(this, max_offset);
132   }
133
134   virtual void RunCallback() {
135     DCHECK_EQ(false, callback_.is_null());
136     callback_.Run(status_, bytes_written_);
137     delete this;
138   }
139
140   const int64_t offset_;
141   scoped_ptr<char[]> buffer_;
142   const int32_t bytes_to_write_;
143   WriteCallback callback_;
144   bool finished_;
145   PlatformFileError status_;
146   int64_t bytes_written_;
147   base::WeakPtrFactory<WriteOperation> weak_factory_;
148 };
149
150 class QuotaFileIO::SetLengthOperation : public PendingOperationBase {
151  public:
152   SetLengthOperation(QuotaFileIO* quota_io,
153                      int64_t length,
154                      const StatusCallback& callback)
155       : PendingOperationBase(quota_io),
156         length_(length),
157         callback_(callback),
158         weak_factory_(this) {}
159
160   virtual ~SetLengthOperation() {}
161
162   virtual void Run() OVERRIDE {
163     DCHECK(quota_io_);
164     if (quota_io_->CheckIfExceedsQuota(length_)) {
165       DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
166       return;
167     }
168
169     if (!base::FileUtilProxy::Truncate(
170             quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(),
171             quota_io_->file_,
172             length_,
173             base::Bind(&SetLengthOperation::DidFinish,
174                        weak_factory_.GetWeakPtr()))) {
175       DidFail(base::PLATFORM_FILE_ERROR_FAILED);
176       return;
177     }
178   }
179
180   virtual void DidFail(PlatformFileError error) OVERRIDE {
181     DidFinish(error);
182   }
183
184  private:
185   void DidFinish(PlatformFileError status) {
186     quota_io_->DidSetLength(status, length_);
187     DCHECK_EQ(false, callback_.is_null());
188     callback_.Run(status);
189     delete this;
190   }
191
192   int64_t length_;
193   StatusCallback callback_;
194   base::WeakPtrFactory<SetLengthOperation> weak_factory_;
195 };
196
197 // QuotaFileIO --------------------------------------------------------------
198
199 QuotaFileIO::QuotaFileIO(
200     Delegate* delegate,
201     PlatformFile file,
202     const GURL& file_url,
203     PP_FileSystemType type)
204     : delegate_(delegate),
205       file_(file),
206       file_url_(file_url),
207       storage_type_(PPFileSystemTypeToQuotaStorageType(type)),
208       cached_file_size_(0),
209       cached_available_space_(0),
210       outstanding_quota_queries_(0),
211       outstanding_errors_(0),
212       max_written_offset_(0),
213       inflight_operations_(0),
214       weak_factory_(this) {
215   DCHECK_NE(base::kInvalidPlatformFileValue, file_);
216   DCHECK_NE(quota::kStorageTypeUnknown, storage_type_);
217 }
218
219 QuotaFileIO::~QuotaFileIO() {
220   // Note that this doesn't dispatch pending callbacks.
221   STLDeleteContainerPointers(pending_operations_.begin(),
222                              pending_operations_.end());
223   STLDeleteContainerPointers(pending_callbacks_.begin(),
224                              pending_callbacks_.end());
225 }
226
227 bool QuotaFileIO::Write(
228     int64_t offset, const char* buffer, int32_t bytes_to_write,
229     const WriteCallback& callback) {
230   if (bytes_to_write <= 0)
231     return false;
232
233   WriteOperation* op = new WriteOperation(
234       this, offset, buffer, bytes_to_write, callback);
235   return RegisterOperationForQuotaChecks(op);
236 }
237
238 bool QuotaFileIO::SetLength(int64_t length, const StatusCallback& callback) {
239   DCHECK(pending_operations_.empty());
240   SetLengthOperation* op = new SetLengthOperation(this, length, callback);
241   return RegisterOperationForQuotaChecks(op);
242 }
243
244 bool QuotaFileIO::RegisterOperationForQuotaChecks(
245     PendingOperationBase* op_ptr) {
246   scoped_ptr<PendingOperationBase> op(op_ptr);
247   if (pending_operations_.empty()) {
248     // This is the first pending quota check. Run querying the file size
249     // and available space.
250     outstanding_quota_queries_ = 0;
251     outstanding_errors_ = 0;
252
253     // Query the file size.
254     ++outstanding_quota_queries_;
255     if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
256             delegate_->GetFileThreadMessageLoopProxy().get(),
257             file_,
258             base::Bind(&QuotaFileIO::DidQueryInfoForQuota,
259                        weak_factory_.GetWeakPtr()))) {
260       // This makes the call fail synchronously; we do not fire the callback
261       // here but just delete the operation and return false.
262       return false;
263     }
264
265     // Query the current available space.
266     ++outstanding_quota_queries_;
267     delegate_->QueryAvailableSpace(
268         file_url_.GetOrigin(), storage_type_,
269         base::Bind(&QuotaFileIO::DidQueryAvailableSpace,
270                    weak_factory_.GetWeakPtr()));
271   }
272   pending_operations_.push_back(op.release());
273   return true;
274 }
275
276 void QuotaFileIO::DidQueryInfoForQuota(
277     base::PlatformFileError error_code,
278     const base::PlatformFileInfo& file_info) {
279   if (error_code != base::PLATFORM_FILE_OK)
280     ++outstanding_errors_;
281   cached_file_size_ = file_info.size;
282   DCHECK_GT(outstanding_quota_queries_, 0);
283   if (--outstanding_quota_queries_ == 0)
284     DidQueryForQuotaCheck();
285 }
286
287 void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) {
288   cached_available_space_ = avail_space;
289   DCHECK_GT(outstanding_quota_queries_, 0);
290   if (--outstanding_quota_queries_ == 0)
291     DidQueryForQuotaCheck();
292 }
293
294 void QuotaFileIO::DidQueryForQuotaCheck() {
295   DCHECK(!pending_operations_.empty());
296   DCHECK_GT(inflight_operations_, 0);
297   while (!pending_operations_.empty()) {
298     PendingOperationBase* op = pending_operations_.front();
299     pending_operations_.pop_front();
300     pending_callbacks_.push_back(op);
301     if (outstanding_errors_ > 0) {
302       op->DidFail(base::PLATFORM_FILE_ERROR_FAILED);
303       continue;
304     }
305     op->Run();
306   }
307 }
308
309 bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const {
310   DCHECK_GE(cached_file_size_, 0);
311   DCHECK_GE(cached_available_space_, 0);
312   return new_file_size - cached_file_size_ > cached_available_space_;
313 }
314
315 void QuotaFileIO::WillUpdate() {
316   if (inflight_operations_++ == 0) {
317     delegate_->WillUpdateFile(file_url_);
318     DCHECK_EQ(0, max_written_offset_);
319   }
320 }
321
322 void QuotaFileIO::DidWrite(WriteOperation* op,
323                            int64_t written_offset_end) {
324   max_written_offset_ = std::max(max_written_offset_, written_offset_end);
325   DCHECK_GT(inflight_operations_, 0);
326   DCHECK(!pending_callbacks_.empty());
327   // Fire callbacks for finished operations.
328   while (!pending_callbacks_.empty()) {
329     WriteOperation* op = static_cast<WriteOperation*>(
330         pending_callbacks_.front());
331     if (!op->finished())
332       break;
333     pending_callbacks_.pop_front();
334     op->WillRunCallback();
335   }
336   // If we have no more pending writes, notify the browser that we did
337   // update the file.
338   if (--inflight_operations_ == 0) {
339     DCHECK(pending_operations_.empty());
340     int64_t growth = max_written_offset_ - cached_file_size_;
341     growth = growth < 0 ? 0 : growth;
342
343     delegate_->DidUpdateFile(file_url_, growth);
344     max_written_offset_ = 0;
345   }
346 }
347
348 void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) {
349   DCHECK_EQ(1, inflight_operations_);
350   pending_callbacks_.pop_front();
351   DCHECK(pending_callbacks_.empty());
352   int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 :
353       new_file_size - cached_file_size_;
354
355   delegate_->DidUpdateFile(file_url_, delta);
356   inflight_operations_ = 0;
357 }
358
359 }  // namespace content