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 "third_party/WebKit/public/platform/WebFileError.h" 9 #include "third_party/WebKit/public/platform/WebFileWriterClient.h" 10 #include "third_party/WebKit/public/platform/WebURL.h" 11 #include "webkit/common/fileapi/file_system_util.h" 12 13 using fileapi::PlatformFileErrorToWebFileError; 14 15 namespace content { 16 17 WebFileWriterBase::WebFileWriterBase(const GURL& path, 18 blink::WebFileWriterClient* client) 19 : path_(path), 20 client_(client), 21 operation_(kOperationNone), 22 cancel_state_(kCancelNotInProgress) {} 23 24 WebFileWriterBase::~WebFileWriterBase() {} 25 26 void WebFileWriterBase::truncate(long long length) { 27 DCHECK(kOperationNone == operation_); 28 DCHECK(kCancelNotInProgress == cancel_state_); 29 operation_ = kOperationTruncate; 30 DoTruncate(path_, length); 31 } 32 33 void 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. 56 void 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 66 void WebFileWriterBase::DidFinish(base::PlatformFileError error_code) { 67 if (error_code == base::PLATFORM_FILE_OK) 68 DidSucceed(); 69 else 70 DidFail(error_code); 71 } 72 73 void 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 94 void 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 120 void WebFileWriterBase::DidFail(base::PlatformFileError 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(PlatformFileErrorToWebFileError(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 145 void 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