1// Copyright 2013 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/child/fileapi/webfilewriter_base.h" 6 7#include "base/logging.h" 8#include "storage/common/fileapi/file_system_util.h" 9#include "third_party/WebKit/public/platform/WebFileError.h" 10#include "third_party/WebKit/public/platform/WebFileWriterClient.h" 11#include "third_party/WebKit/public/platform/WebURL.h" 12 13using storage::FileErrorToWebFileError; 14 15namespace content { 16 17WebFileWriterBase::WebFileWriterBase(const GURL& path, 18 blink::WebFileWriterClient* client) 19 : path_(path), 20 client_(client), 21 operation_(kOperationNone), 22 cancel_state_(kCancelNotInProgress) {} 23 24WebFileWriterBase::~WebFileWriterBase() {} 25 26void WebFileWriterBase::truncate(long long length) { 27 DCHECK(kOperationNone == operation_); 28 DCHECK(kCancelNotInProgress == cancel_state_); 29 operation_ = kOperationTruncate; 30 DoTruncate(path_, length); 31} 32 33void WebFileWriterBase::write( 34 long long position, 35 const blink::WebString& id) { 36 DCHECK_EQ(kOperationNone, operation_); 37 DCHECK_EQ(kCancelNotInProgress, cancel_state_); 38 operation_ = kOperationWrite; 39 DoWrite(path_, id.utf8(), position); 40} 41 42// When we cancel a write/truncate, we always get back the result of the write 43// before the result of the cancel, no matter what happens. 44// So we'll get back either 45// success [of the write/truncate, in a DidWrite(XXX, true)/DidSucceed() call] 46// followed by failure [of the cancel]; or 47// failure [of the write, either from cancel or other reasons] followed by 48// the result of the cancel. 49// In the write case, there could also be queued up non-terminal DidWrite calls 50// before any of that comes back, but there will always be a terminal write 51// response [success or failure] after them, followed by the cancel result, so 52// we can ignore non-terminal write responses, take the terminal write success 53// or the first failure as the last write response, then know that the next 54// thing to come back is the cancel response. We only notify the 55// AsyncFileWriterClient when it's all over. 56void WebFileWriterBase::cancel() { 57 // Check for the cancel passing the previous operation's return in-flight. 58 if (kOperationWrite != operation_ && kOperationTruncate != operation_) 59 return; 60 if (kCancelNotInProgress != cancel_state_) 61 return; 62 cancel_state_ = kCancelSent; 63 DoCancel(); 64} 65 66void WebFileWriterBase::DidFinish(base::File::Error error_code) { 67 if (error_code == base::File::FILE_OK) 68 DidSucceed(); 69 else 70 DidFail(error_code); 71} 72 73void WebFileWriterBase::DidWrite(int64 bytes, bool complete) { 74 DCHECK(kOperationWrite == operation_); 75 switch (cancel_state_) { 76 case kCancelNotInProgress: 77 if (complete) 78 operation_ = kOperationNone; 79 client_->didWrite(bytes, complete); 80 break; 81 case kCancelSent: 82 // This is the success call of the write, which we'll eat, even though 83 // it succeeded before the cancel got there. We accepted the cancel call, 84 // so the write will eventually return an error. 85 if (complete) 86 cancel_state_ = kCancelReceivedWriteResponse; 87 break; 88 case kCancelReceivedWriteResponse: 89 default: 90 NOTREACHED(); 91 } 92} 93 94void WebFileWriterBase::DidSucceed() { 95 // Write never gets a DidSucceed call, so this is either a cancel or truncate 96 // response. 97 switch (cancel_state_) { 98 case kCancelNotInProgress: 99 // A truncate succeeded, with no complications. 100 DCHECK(kOperationTruncate == operation_); 101 operation_ = kOperationNone; 102 client_->didTruncate(); 103 break; 104 case kCancelSent: 105 DCHECK(kOperationTruncate == operation_); 106 // This is the success call of the truncate, which we'll eat, even though 107 // it succeeded before the cancel got there. We accepted the cancel call, 108 // so the truncate will eventually return an error. 109 cancel_state_ = kCancelReceivedWriteResponse; 110 break; 111 case kCancelReceivedWriteResponse: 112 // This is the success of the cancel operation. 113 FinishCancel(); 114 break; 115 default: 116 NOTREACHED(); 117 } 118} 119 120void WebFileWriterBase::DidFail(base::File::Error error_code) { 121 DCHECK(kOperationNone != operation_); 122 switch (cancel_state_) { 123 case kCancelNotInProgress: 124 // A write or truncate failed. 125 operation_ = kOperationNone; 126 client_->didFail(FileErrorToWebFileError(error_code)); 127 break; 128 case kCancelSent: 129 // This is the failure of a write or truncate; the next message should be 130 // the result of the cancel. We don't assume that it'll be a success, as 131 // the write/truncate could have failed for other reasons. 132 cancel_state_ = kCancelReceivedWriteResponse; 133 break; 134 case kCancelReceivedWriteResponse: 135 // The cancel reported failure, meaning that the write or truncate 136 // finished before the cancel got there. But we suppressed the 137 // write/truncate's response, and will now report that it was cancelled. 138 FinishCancel(); 139 break; 140 default: 141 NOTREACHED(); 142 } 143} 144 145void WebFileWriterBase::FinishCancel() { 146 DCHECK(kCancelReceivedWriteResponse == cancel_state_); 147 DCHECK(kOperationNone != operation_); 148 cancel_state_ = kCancelNotInProgress; 149 operation_ = kOperationNone; 150 client_->didFail(blink::WebFileErrorAbort); 151} 152 153} // namespace content 154