/* * Copyright (C) 2010 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "third_party/blink/renderer/modules/filesystem/file_writer_base.h" #include #include "third_party/blink/renderer/core/events/progress_event.h" #include "third_party/blink/renderer/core/fileapi/blob.h" #include "third_party/blink/renderer/core/fileapi/file_error.h" namespace blink { FileWriterBase::~FileWriterBase() = default; void FileWriterBase::Initialize(const KURL& path, long long length) { DCHECK_GE(length, 0); length_ = length; path_ = path; } FileWriterBase::FileWriterBase() : position_(0), operation_(kOperationNone), cancel_state_(kCancelNotInProgress) {} void FileWriterBase::SeekInternal(long long position) { if (position > length_) position = length_; else if (position < 0) position = length_ + position; if (position < 0) position = 0; position_ = position; } void FileWriterBase::Truncate(long long length) { DCHECK_EQ(kOperationNone, operation_); DCHECK_EQ(kCancelNotInProgress, cancel_state_); operation_ = kOperationTruncate; DoTruncate(path_, length); } void FileWriterBase::Write(long long position, const String& id) { DCHECK_EQ(kOperationNone, operation_); DCHECK_EQ(kCancelNotInProgress, cancel_state_); operation_ = kOperationWrite; DoWrite(path_, id, 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 FileWriterBase::Cancel() { // Check for the cancel passing the previous operation's return in-flight. if (operation_ != kOperationWrite && operation_ != kOperationTruncate) return; if (cancel_state_ != kCancelNotInProgress) return; cancel_state_ = kCancelSent; DoCancel(); } void FileWriterBase::DidFinish(base::File::Error error_code) { if (error_code == base::File::FILE_OK) DidSucceed(); else DidFail(error_code); } void FileWriterBase::DidWrite(int64_t bytes, bool complete) { DCHECK_EQ(kOperationWrite, operation_); switch (cancel_state_) { case kCancelNotInProgress: if (complete) operation_ = kOperationNone; DidWriteImpl(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 FileWriterBase::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_EQ(kOperationTruncate, operation_); operation_ = kOperationNone; DidTruncateImpl(); break; case kCancelSent: DCHECK_EQ(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 FileWriterBase::DidFail(base::File::Error error_code) { DCHECK_NE(kOperationNone, operation_); switch (cancel_state_) { case kCancelNotInProgress: // A write or truncate failed. operation_ = kOperationNone; DidFailImpl(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 FileWriterBase::FinishCancel() { DCHECK_EQ(kCancelReceivedWriteResponse, cancel_state_); DCHECK_NE(kOperationNone, operation_); cancel_state_ = kCancelNotInProgress; operation_ = kOperationNone; DidFailImpl(base::File::FILE_ERROR_ABORT); } } // namespace blink