// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/child/fileapi/webfilewriter_base.h" #include "base/logging.h" #include "storage/common/fileapi/file_system_util.h" #include "third_party/WebKit/public/platform/WebFileError.h" #include "third_party/WebKit/public/platform/WebFileWriterClient.h" #include "third_party/WebKit/public/platform/WebURL.h" using storage::FileErrorToWebFileError; namespace content { WebFileWriterBase::WebFileWriterBase(const GURL& path, blink::WebFileWriterClient* client) : path_(path), client_(client), operation_(kOperationNone), cancel_state_(kCancelNotInProgress) {} WebFileWriterBase::~WebFileWriterBase() {} void WebFileWriterBase::truncate(long long length) { DCHECK(kOperationNone == operation_); DCHECK(kCancelNotInProgress == cancel_state_); operation_ = kOperationTruncate; DoTruncate(path_, length); } void WebFileWriterBase::write( long long position, const blink::WebString& id) { DCHECK_EQ(kOperationNone, operation_); DCHECK_EQ(kCancelNotInProgress, cancel_state_); operation_ = kOperationWrite; DoWrite(path_, id.utf8(), position); } // When we cancel a write/truncate, we always get back the result of the write // before the result of the cancel, no matter what happens. // So we'll get back either // success [of the write/truncate, in a DidWrite(XXX, true)/DidSucceed() call] // followed by failure [of the cancel]; or // failure [of the write, either from cancel or other reasons] followed by // the result of the cancel. // In the write case, there could also be queued up non-terminal DidWrite calls // before any of that comes back, but there will always be a terminal write // response [success or failure] after them, followed by the cancel result, so // we can ignore non-terminal write responses, take the terminal write success // or the first failure as the last write response, then know that the next // thing to come back is the cancel response. We only notify the // AsyncFileWriterClient when it's all over. void WebFileWriterBase::cancel() { // Check for the cancel passing the previous operation's return in-flight. if (kOperationWrite != operation_ && kOperationTruncate != operation_) return; if (kCancelNotInProgress != cancel_state_) return; cancel_state_ = kCancelSent; DoCancel(); } void WebFileWriterBase::DidFinish(base::File::Error error_code) { if (error_code == base::File::FILE_OK) DidSucceed(); else DidFail(error_code); } void WebFileWriterBase::DidWrite(int64 bytes, bool complete) { DCHECK(kOperationWrite == operation_); switch (cancel_state_) { case kCancelNotInProgress: if (complete) operation_ = kOperationNone; client_->didWrite(bytes, complete); break; case kCancelSent: // This is the success call of the write, which we'll eat, even though // it succeeded before the cancel got there. We accepted the cancel call, // so the write will eventually return an error. if (complete) cancel_state_ = kCancelReceivedWriteResponse; break; case kCancelReceivedWriteResponse: default: NOTREACHED(); } } void WebFileWriterBase::DidSucceed() { // Write never gets a DidSucceed call, so this is either a cancel or truncate // response. switch (cancel_state_) { case kCancelNotInProgress: // A truncate succeeded, with no complications. DCHECK(kOperationTruncate == operation_); operation_ = kOperationNone; client_->didTruncate(); break; case kCancelSent: DCHECK(kOperationTruncate == operation_); // This is the success call of the truncate, which we'll eat, even though // it succeeded before the cancel got there. We accepted the cancel call, // so the truncate will eventually return an error. cancel_state_ = kCancelReceivedWriteResponse; break; case kCancelReceivedWriteResponse: // This is the success of the cancel operation. FinishCancel(); break; default: NOTREACHED(); } } void WebFileWriterBase::DidFail(base::File::Error error_code) { DCHECK(kOperationNone != operation_); switch (cancel_state_) { case kCancelNotInProgress: // A write or truncate failed. operation_ = kOperationNone; client_->didFail(FileErrorToWebFileError(error_code)); break; case kCancelSent: // This is the failure of a write or truncate; the next message should be // the result of the cancel. We don't assume that it'll be a success, as // the write/truncate could have failed for other reasons. cancel_state_ = kCancelReceivedWriteResponse; break; case kCancelReceivedWriteResponse: // The cancel reported failure, meaning that the write or truncate // finished before the cancel got there. But we suppressed the // write/truncate's response, and will now report that it was cancelled. FinishCancel(); break; default: NOTREACHED(); } } void WebFileWriterBase::FinishCancel() { DCHECK(kCancelReceivedWriteResponse == cancel_state_); DCHECK(kOperationNone != operation_); cancel_state_ = kCancelNotInProgress; operation_ = kOperationNone; client_->didFail(blink::WebFileErrorAbort); } } // namespace content