Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: webkit/plugins/ppapi/quota_file_io.cc

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

Powered by Google App Engine
This is Rietveld 408576698