Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / webkit / plugins / ppapi / quota_file_io.cc
blob961c79ab4ba3ddfa1b6ac921fe7b48db1e742e4c
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.
5 #include "webkit/plugins/ppapi/quota_file_io.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/message_loop_proxy.h"
13 #include "base/stl_util.h"
14 #include "base/task_runner_util.h"
15 #include "webkit/plugins/ppapi/host_globals.h"
16 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
17 #include "webkit/plugins/ppapi/resource_helper.h"
19 using base::PlatformFile;
20 using base::PlatformFileError;
21 using quota::StorageType;
23 namespace webkit {
24 namespace ppapi {
26 namespace {
27 StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) {
28 switch (type) {
29 case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
30 return quota::kStorageTypePersistent;
31 case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
32 return quota::kStorageTypeTemporary;
33 default:
34 return quota::kStorageTypeUnknown;
36 NOTREACHED();
37 return quota::kStorageTypeUnknown;
40 int WriteAdapter(PlatformFile file, int64 offset,
41 scoped_ptr<char[]> data, int size) {
42 return base::WritePlatformFile(file, offset, data.get(), size);
45 } // namespace
47 class QuotaFileIO::PendingOperationBase {
48 public:
49 virtual ~PendingOperationBase() {}
51 // Either one of Run() or DidFail() is called (the latter is called when
52 // there was more than one error during quota queries).
53 virtual void Run() = 0;
54 virtual void DidFail(PlatformFileError error) = 0;
56 protected:
57 PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation)
58 : quota_io_(quota_io), is_will_operation_(is_will_operation) {
59 DCHECK(quota_io_);
60 quota_io_->WillUpdate();
63 QuotaFileIO* quota_io_;
64 const bool is_will_operation_;
67 class QuotaFileIO::WriteOperation : public PendingOperationBase {
68 public:
69 WriteOperation(QuotaFileIO* quota_io,
70 bool is_will_operation,
71 int64_t offset,
72 const char* buffer,
73 int32_t bytes_to_write,
74 const WriteCallback& callback)
75 : PendingOperationBase(quota_io, is_will_operation),
76 offset_(offset),
77 bytes_to_write_(bytes_to_write),
78 callback_(callback),
79 finished_(false),
80 status_(base::PLATFORM_FILE_OK),
81 bytes_written_(0),
82 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
83 if (!is_will_operation) {
84 // TODO(kinuko): Check the API convention if we really need to keep a copy
85 // of the buffer during the async write operations.
86 buffer_.reset(new char[bytes_to_write]);
87 memcpy(buffer_.get(), buffer, bytes_to_write);
90 virtual ~WriteOperation() {}
91 virtual void Run() OVERRIDE {
92 DCHECK(quota_io_);
93 if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) {
94 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
95 return;
97 if (is_will_operation_) {
98 // Assuming the write will succeed.
99 DidFinish(base::PLATFORM_FILE_OK, bytes_to_write_);
100 return;
102 DCHECK(buffer_.get());
104 PluginDelegate* plugin_delegate = quota_io_->GetPluginDelegate();
105 if (!plugin_delegate) {
106 DidFail(base::PLATFORM_FILE_ERROR_FAILED);
107 return;
110 if (!base::PostTaskAndReplyWithResult(
111 plugin_delegate->GetFileThreadMessageLoopProxy(), FROM_HERE,
112 base::Bind(&WriteAdapter,
113 quota_io_->file_, offset_,
114 base::Passed(&buffer_), bytes_to_write_),
115 base::Bind(&WriteOperation::DidWrite,
116 weak_factory_.GetWeakPtr()))) {
117 DidFail(base::PLATFORM_FILE_ERROR_FAILED);
118 return;
122 virtual void DidFail(PlatformFileError error) OVERRIDE {
123 DidFinish(error, 0);
126 bool finished() const { return finished_; }
128 virtual void WillRunCallback() {
129 base::MessageLoopProxy::current()->PostTask(
130 FROM_HERE,
131 base::Bind(&WriteOperation::RunCallback, weak_factory_.GetWeakPtr()));
134 private:
135 void DidWrite(int bytes_written) {
136 base::PlatformFileError error = bytes_written > 0 ?
137 base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_FAILED;
138 DidFinish(error, bytes_written);
141 void DidFinish(PlatformFileError status, int bytes_written) {
142 finished_ = true;
143 status_ = status;
144 bytes_written_ = bytes_written;
145 int64_t max_offset =
146 (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written;
147 // This may delete itself by calling RunCallback.
148 quota_io_->DidWrite(this, max_offset);
151 virtual void RunCallback() {
152 DCHECK_EQ(false, callback_.is_null());
153 callback_.Run(status_, bytes_written_);
154 delete this;
157 const int64_t offset_;
158 scoped_ptr<char[]> buffer_;
159 const int32_t bytes_to_write_;
160 WriteCallback callback_;
161 bool finished_;
162 PlatformFileError status_;
163 int64_t bytes_written_;
164 base::WeakPtrFactory<WriteOperation> weak_factory_;
167 class QuotaFileIO::SetLengthOperation : public PendingOperationBase {
168 public:
169 SetLengthOperation(QuotaFileIO* quota_io,
170 bool is_will_operation,
171 int64_t length,
172 const StatusCallback& callback)
173 : PendingOperationBase(quota_io, is_will_operation),
174 length_(length),
175 callback_(callback),
176 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
178 virtual ~SetLengthOperation() {}
180 virtual void Run() OVERRIDE {
181 DCHECK(quota_io_);
182 if (quota_io_->CheckIfExceedsQuota(length_)) {
183 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
184 return;
186 if (is_will_operation_) {
187 DidFinish(base::PLATFORM_FILE_OK);
188 return;
191 PluginDelegate* plugin_delegate = quota_io_->GetPluginDelegate();
192 if (!plugin_delegate) {
193 DidFail(base::PLATFORM_FILE_ERROR_FAILED);
194 return;
197 if (!base::FileUtilProxy::Truncate(
198 plugin_delegate->GetFileThreadMessageLoopProxy(),
199 quota_io_->file_, length_,
200 base::Bind(&SetLengthOperation::DidFinish,
201 weak_factory_.GetWeakPtr()))) {
202 DidFail(base::PLATFORM_FILE_ERROR_FAILED);
203 return;
207 virtual void DidFail(PlatformFileError error) OVERRIDE {
208 DidFinish(error);
211 private:
212 void DidFinish(PlatformFileError status) {
213 quota_io_->DidSetLength(status, length_);
214 DCHECK_EQ(false, callback_.is_null());
215 callback_.Run(status);
216 delete this;
219 int64_t length_;
220 StatusCallback callback_;
221 base::WeakPtrFactory<SetLengthOperation> weak_factory_;
224 // QuotaFileIO --------------------------------------------------------------
226 QuotaFileIO::QuotaFileIO(
227 PP_Instance instance,
228 PlatformFile file,
229 const GURL& file_url,
230 PP_FileSystemType type)
231 : pp_instance_(instance),
232 file_(file),
233 file_url_(file_url),
234 storage_type_(PPFileSystemTypeToQuotaStorageType(type)),
235 cached_file_size_(0),
236 cached_available_space_(0),
237 outstanding_quota_queries_(0),
238 outstanding_errors_(0),
239 max_written_offset_(0),
240 inflight_operations_(0),
241 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
242 DCHECK_NE(base::kInvalidPlatformFileValue, file_);
243 DCHECK_NE(quota::kStorageTypeUnknown, storage_type_);
246 QuotaFileIO::~QuotaFileIO() {
247 // Note that this doesn't dispatch pending callbacks.
248 STLDeleteContainerPointers(pending_operations_.begin(),
249 pending_operations_.end());
250 STLDeleteContainerPointers(pending_callbacks_.begin(),
251 pending_callbacks_.end());
254 bool QuotaFileIO::Write(
255 int64_t offset, const char* buffer, int32_t bytes_to_write,
256 const WriteCallback& callback) {
257 if (bytes_to_write <= 0)
258 return false;
260 WriteOperation* op = new WriteOperation(
261 this, false, offset, buffer, bytes_to_write, callback);
262 return RegisterOperationForQuotaChecks(op);
265 bool QuotaFileIO::SetLength(int64_t length, const StatusCallback& callback) {
266 DCHECK(pending_operations_.empty());
267 SetLengthOperation* op = new SetLengthOperation(
268 this, false, length, callback);
269 return RegisterOperationForQuotaChecks(op);
272 bool QuotaFileIO::WillWrite(
273 int64_t offset, int32_t bytes_to_write, const WriteCallback& callback) {
274 WriteOperation* op = new WriteOperation(
275 this, true, offset, NULL, bytes_to_write, callback);
276 return RegisterOperationForQuotaChecks(op);
279 bool QuotaFileIO::WillSetLength(int64_t length,
280 const StatusCallback& callback) {
281 DCHECK(pending_operations_.empty());
282 SetLengthOperation* op = new SetLengthOperation(this, true, length, callback);
283 return RegisterOperationForQuotaChecks(op);
286 PluginDelegate* QuotaFileIO::GetPluginDelegate() const {
287 PluginInstance* instance = HostGlobals::Get()->GetInstance(pp_instance_);
288 if (instance)
289 return instance->delegate();
290 return NULL;
293 bool QuotaFileIO::RegisterOperationForQuotaChecks(
294 PendingOperationBase* op_ptr) {
295 scoped_ptr<PendingOperationBase> op(op_ptr);
296 if (pending_operations_.empty()) {
297 // This is the first pending quota check. Run querying the file size
298 // and available space.
299 outstanding_quota_queries_ = 0;
300 outstanding_errors_ = 0;
302 PluginDelegate* plugin_delegate = GetPluginDelegate();
303 if (!plugin_delegate)
304 return false;
306 // Query the file size.
307 ++outstanding_quota_queries_;
308 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
309 plugin_delegate->GetFileThreadMessageLoopProxy(), file_,
310 base::Bind(&QuotaFileIO::DidQueryInfoForQuota,
311 weak_factory_.GetWeakPtr()))) {
312 // This makes the call fail synchronously; we do not fire the callback
313 // here but just delete the operation and return false.
314 return false;
317 // Query the current available space.
318 ++outstanding_quota_queries_;
319 plugin_delegate->QueryAvailableSpace(
320 file_url_.GetOrigin(), storage_type_,
321 base::Bind(&QuotaFileIO::DidQueryAvailableSpace,
322 weak_factory_.GetWeakPtr()));
324 pending_operations_.push_back(op.release());
325 return true;
328 void QuotaFileIO::DidQueryInfoForQuota(
329 base::PlatformFileError error_code,
330 const base::PlatformFileInfo& file_info) {
331 if (error_code != base::PLATFORM_FILE_OK)
332 ++outstanding_errors_;
333 cached_file_size_ = file_info.size;
334 DCHECK_GT(outstanding_quota_queries_, 0);
335 if (--outstanding_quota_queries_ == 0)
336 DidQueryForQuotaCheck();
339 void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) {
340 cached_available_space_ = avail_space;
341 DCHECK_GT(outstanding_quota_queries_, 0);
342 if (--outstanding_quota_queries_ == 0)
343 DidQueryForQuotaCheck();
346 void QuotaFileIO::DidQueryForQuotaCheck() {
347 DCHECK(!pending_operations_.empty());
348 DCHECK_GT(inflight_operations_, 0);
349 while (!pending_operations_.empty()) {
350 PendingOperationBase* op = pending_operations_.front();
351 pending_operations_.pop_front();
352 pending_callbacks_.push_back(op);
353 if (outstanding_errors_ > 0) {
354 op->DidFail(base::PLATFORM_FILE_ERROR_FAILED);
355 continue;
357 op->Run();
361 bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const {
362 DCHECK_GE(cached_file_size_, 0);
363 DCHECK_GE(cached_available_space_, 0);
364 return new_file_size - cached_file_size_ > cached_available_space_;
367 void QuotaFileIO::WillUpdate() {
368 if (inflight_operations_++ == 0) {
369 PluginDelegate* plugin_delegate = GetPluginDelegate();
370 if (plugin_delegate)
371 plugin_delegate->WillUpdateFile(file_url_);
372 DCHECK_EQ(0, max_written_offset_);
376 void QuotaFileIO::DidWrite(WriteOperation* op,
377 int64_t written_offset_end) {
378 max_written_offset_ = std::max(max_written_offset_, written_offset_end);
379 DCHECK_GT(inflight_operations_, 0);
380 DCHECK(!pending_callbacks_.empty());
381 // Fire callbacks for finished operations.
382 while (!pending_callbacks_.empty()) {
383 WriteOperation* op = static_cast<WriteOperation*>(
384 pending_callbacks_.front());
385 if (!op->finished())
386 break;
387 pending_callbacks_.pop_front();
388 op->WillRunCallback();
390 // If we have no more pending writes, notify the browser that we did
391 // update the file.
392 if (--inflight_operations_ == 0) {
393 DCHECK(pending_operations_.empty());
394 int64_t growth = max_written_offset_ - cached_file_size_;
395 growth = growth < 0 ? 0 : growth;
397 PluginDelegate* plugin_delegate = GetPluginDelegate();
398 if (plugin_delegate)
399 plugin_delegate->DidUpdateFile(file_url_, growth);
400 max_written_offset_ = 0;
404 void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) {
405 DCHECK_EQ(1, inflight_operations_);
406 pending_callbacks_.pop_front();
407 DCHECK(pending_callbacks_.empty());
408 int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 :
409 new_file_size - cached_file_size_;
412 PluginDelegate* plugin_delegate = GetPluginDelegate();
413 if (plugin_delegate)
414 plugin_delegate->DidUpdateFile(file_url_, delta);
415 inflight_operations_ = 0;
418 } // namespace ppapi
419 } // namespace webkit