Changeset 96177 in webkit
- Timestamp:
- Sep 27, 2011 6:46:08 PM (13 years ago)
- Location:
- trunk
- Files:
-
- 8 added
- 5 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r96176 r96177 1 2011-09-27 Eric Uhrhane <ericu@chromium.org> 2 3 [Chromium/FileWriter] race condition in FileWriter completion can lead to assert 4 https://bugs.webkit.org/show_bug.cgi?id=67684 5 6 Reviewed by David Levin. 7 8 * fast/filesystem/file-writer-abort-continue-expected.txt: Added. 9 * fast/filesystem/file-writer-abort-continue.html: Added. 10 * fast/filesystem/file-writer-abort-expected.txt: Added. 11 * fast/filesystem/file-writer-abort.html: Added. 12 * fast/filesystem/resources/file-writer-abort-continue.js: Added. 13 * fast/filesystem/resources/file-writer-abort.js: Added. 14 * fast/filesystem/resources/file-writer-events.js: Fixed a copy-paste error. 15 1 16 2011-09-27 Tim Horton <timothy_horton@apple.com> 2 17 -
trunk/LayoutTests/fast/filesystem/resources/file-writer-abort.js
r96176 r96177 4 4 } 5 5 6 description("Test that FileWriter produces proper progress events.");6 description("Test that FileWriter handles abort properly."); 7 7 8 var fileEntry;9 8 var sawWriteStart; 10 var saw Write;9 var sawAbort; 11 10 var sawWriteEnd; 12 var sawProgress;13 11 var writer; 14 var lastProgress = 0;15 var toBeWritten;16 12 17 13 function tenXBlob(blob) { … … 28 24 assert(e.type == "writestart"); 29 25 assert(!sawWriteStart); 30 assert(!sawProgress);31 assert(!sawWrite);32 26 assert(!sawWriteEnd); 33 27 assert(!e.loaded); 34 assert(e.total == toBeWritten);35 28 sawWriteStart = true; 29 testPassed("Calling abort"); 30 writer.abort(); 36 31 } 37 32 38 function onProgress(e) { 39 assert(writer.readyState == writer.WRITING); 40 assert(sawWriteStart); 41 assert(!sawWrite); 42 assert(!sawWriteEnd); 43 assert(e.type == "progress"); 44 assert(e.loaded <= e.total); 45 assert(lastProgress < e.loaded); 46 assert(e.total == toBeWritten); 47 lastProgress = e.loaded; 48 sawProgress = true; 33 // We should always abort before completion. 34 function onWrite(e) { 35 testFailed("In onWrite."); 49 36 } 50 37 51 function on Write(e) {38 function onAbort(e) { 52 39 assert(writer.readyState == writer.DONE); 40 assert(writer.error.code == writer.error.ABORT_ERR); 53 41 assert(sawWriteStart); 54 assert(sawProgress);55 assert(lastProgress == e.total);56 assert(!sawWrite);57 42 assert(!sawWriteEnd); 58 assert( e.type == "write");59 assert(e. loaded == e.total);60 assert(e.total == toBeWritten);61 sawWrite = true;43 assert(!sawAbort); 44 assert(e.type == "abort"); 45 sawAbort = true; 46 testPassed("Saw abort"); 62 47 } 63 48 64 49 function onWriteEnd(e) { 65 50 assert(writer.readyState == writer.DONE); 51 assert(writer.error.code == writer.error.ABORT_ERR); 66 52 assert(sawWriteStart); 67 assert(sawProgress); 68 assert(sawWrite); 53 assert(sawAbort); 69 54 assert(!sawWriteEnd); 70 55 assert(e.type == "writeend"); 71 assert(e.loaded == e.total);72 assert(e.total == toBeWritten);73 56 sawWriteEnd = true; 74 testPassed("Saw all the right events."); 57 testPassed("Saw writeend."); 58 writer.abort(); // Verify that this does nothing in readyState DONE. 75 59 cleanUp(); 76 60 } … … 85 69 blob = tenXBlob(blob); 86 70 blob = tenXBlob(blob); 87 toBeWritten = blob.size;88 71 writer = fileWriter; 89 72 fileWriter.onerror = onError; 73 fileWriter.onabort = onAbort; 90 74 fileWriter.onwritestart = onWriteStart; 91 fileWriter.onprogress = onProgress;92 75 fileWriter.onwrite = onWrite; 93 76 fileWriter.onwriteend = onWriteEnd; 77 fileWriter.abort(); // Verify that this does nothing in readyState INIT. 94 78 fileWriter.write(blob); 95 79 } … … 99 83 } 100 84 var jsTestIsAsync = true; 101 setupAndRunTest(2*1024*1024, 'file-writer- gc-blob', runTest);85 setupAndRunTest(2*1024*1024, 'file-writer-abort', runTest); 102 86 var successfullyParsed = true; -
trunk/LayoutTests/fast/filesystem/resources/file-writer-events.js
r85556 r96177 99 99 } 100 100 var jsTestIsAsync = true; 101 setupAndRunTest(2*1024*1024, 'file-writer- gc-blob', runTest);101 setupAndRunTest(2*1024*1024, 'file-writer-events', runTest); 102 102 var successfullyParsed = true; -
trunk/Source/WebCore/ChangeLog
r96173 r96177 1 2011-09-27 Eric Uhrhane <ericu@chromium.org> 2 3 [Chromium/FileWriter] race condition in FileWriter completion can lead to assert 4 https://bugs.webkit.org/show_bug.cgi?id=67684 5 6 Reviewed by David Levin. 7 8 Tests: fast/filesystem/file-writer-abort-continue.html 9 fast/filesystem/file-writer-abort.html 10 11 Track the state of the backend and be prepared for reentrant user 12 requests. Limit recursion depth to an arbitrary small constant. 13 * fileapi/FileWriter.cpp: Lots of event-handling changes. 14 * fileapi/FileWriter.h: 15 1 16 2011-09-27 Mihai Parparita <mihaip@chromium.org> 2 17 -
trunk/Source/WebCore/fileapi/FileWriter.cpp
r95901 r96177 44 44 namespace WebCore { 45 45 46 static const int kMaxRecursionDepth = 3; 47 46 48 FileWriter::FileWriter(ScriptExecutionContext* context) 47 49 : ActiveDOMObject(context, this) 48 50 , m_readyState(INIT) 49 , m_startedWriting(false) 51 , m_isOperationInProgress(false) 52 , m_queuedOperation(OperationNone) 50 53 , m_bytesWritten(0) 51 54 , m_bytesToWrite(0) 52 55 , m_truncateLength(-1) 56 , m_numAborts(0) 57 , m_recursionDepth(0) 53 58 { 54 59 } … … 56 61 FileWriter::~FileWriter() 57 62 { 63 ASSERT(!m_recursionDepth); 58 64 if (m_readyState == WRITING) 59 65 stop(); … … 73 79 void FileWriter::stop() 74 80 { 75 if (writer() && m_readyState == WRITING) 81 // Make sure we've actually got something to stop, and haven't already called abort(). 82 if (writer() && m_readyState == WRITING && m_isOperationInProgress && m_queuedOperation == OperationNone) 76 83 writer()->abort(); 77 84 m_blobBeingWritten.clear(); … … 82 89 { 83 90 ASSERT(writer()); 91 ASSERT(data); 92 ASSERT(m_truncateLength == -1); 84 93 if (m_readyState == WRITING) { 85 94 setError(FileError::INVALID_STATE_ERR, ec); … … 90 99 return; 91 100 } 92 101 if (m_recursionDepth > kMaxRecursionDepth) { 102 setError(FileError::SECURITY_ERR, ec); 103 return; 104 } 93 105 m_blobBeingWritten = data; 94 106 m_readyState = WRITING; 95 m_startedWriting = false;96 107 m_bytesWritten = 0; 97 108 m_bytesToWrite = data->size(); 98 writer()->write(position(), data); 109 ASSERT(m_queuedOperation == OperationNone); 110 if (m_isOperationInProgress) // We must be waiting for an abort to complete, since m_readyState wasn't WRITING. 111 m_queuedOperation = OperationWrite; 112 else 113 writer()->write(position(), m_blobBeingWritten.get()); 114 m_isOperationInProgress = true; 115 fireEvent(eventNames().writestartEvent); 99 116 } 100 117 … … 107 124 } 108 125 109 m_bytesWritten = 0;110 m_bytesToWrite = 0;126 ASSERT(m_truncateLength == -1); 127 ASSERT(m_queuedOperation == OperationNone); 111 128 seekInternal(position); 112 129 } … … 115 132 { 116 133 ASSERT(writer()); 134 ASSERT(m_truncateLength == -1); 117 135 if (m_readyState == WRITING || position < 0) { 118 136 setError(FileError::INVALID_STATE_ERR, ec); 137 return; 138 } 139 if (m_recursionDepth > kMaxRecursionDepth) { 140 setError(FileError::SECURITY_ERR, ec); 119 141 return; 120 142 } … … 123 145 m_bytesToWrite = 0; 124 146 m_truncateLength = position; 125 writer()->truncate(position); 147 ASSERT(m_queuedOperation == OperationNone); 148 if (m_isOperationInProgress) // We must be waiting for an abort to complete. 149 m_queuedOperation = OperationTruncate; 150 else 151 writer()->truncate(m_truncateLength); 152 m_isOperationInProgress = true; 153 fireEvent(eventNames().writestartEvent); 126 154 } 127 155 … … 130 158 ASSERT(writer()); 131 159 if (m_readyState != WRITING) { 132 setError(FileError::INVALID_STATE_ERR, ec); 133 return; 134 } 135 136 writer()->abort(); 160 return; 161 } 162 ++m_numAborts; 163 164 if (m_isOperationInProgress) 165 writer()->abort(); 166 m_queuedOperation = OperationNone; 167 m_blobBeingWritten.clear(); 168 m_truncateLength = -1; 169 signalCompletion(FileError::ABORT_ERR); 137 170 } 138 171 139 172 void FileWriter::didWrite(long long bytes, bool complete) 140 173 { 174 ASSERT(m_readyState == WRITING); 175 ASSERT(m_truncateLength == -1); 176 ASSERT(m_isOperationInProgress); 141 177 ASSERT(bytes + m_bytesWritten > 0); 142 178 ASSERT(bytes + m_bytesWritten <= m_bytesToWrite); 143 if (!m_startedWriting) {144 fireEvent(eventNames().writestartEvent);145 m_startedWriting = true;146 }147 179 m_bytesWritten += bytes; 148 180 ASSERT((m_bytesWritten == m_bytesToWrite) || !complete); … … 150 182 if (position() > length()) 151 183 setLength(position()); 184 // TODO: Throttle to no more frequently than every 50ms. 185 int numAborts = m_numAborts; 152 186 fireEvent(eventNames().progressEvent); 153 if (complete) { 187 // We could get an abort in the handler for this event. If we do, it's 188 // already handled the cleanup and signalCompletion call. 189 if (complete && numAborts == m_numAborts) { 154 190 m_blobBeingWritten.clear(); 155 scriptExecutionContext()->postTask(FileWriterCompletionEventTask::create(this, FileError::OK)); 191 m_isOperationInProgress = false; 192 signalCompletion(FileError::OK); 156 193 } 157 194 } … … 160 197 { 161 198 ASSERT(m_truncateLength >= 0); 162 fireEvent(eventNames().writestartEvent);163 199 setLength(m_truncateLength); 164 200 if (position() > length()) 165 201 setPosition(length()); 202 m_isOperationInProgress = false; 203 signalCompletion(FileError::OK); 204 } 205 206 void FileWriter::didFail(FileError::ErrorCode code) 207 { 208 ASSERT(m_isOperationInProgress); 209 m_isOperationInProgress = false; 210 ASSERT(code != FileError::OK); 211 if (code == FileError::ABORT_ERR) { 212 Operation operation = m_queuedOperation; 213 m_queuedOperation = OperationNone; 214 doOperation(operation); 215 } else { 216 ASSERT(m_queuedOperation == OperationNone); 217 ASSERT(m_readyState == WRITING); 218 m_blobBeingWritten.clear(); 219 signalCompletion(code); 220 } 221 } 222 223 void FileWriter::doOperation(Operation operation) 224 { 225 ASSERT(m_queuedOperation == OperationNone); 226 ASSERT(!m_isOperationInProgress); 227 ASSERT(operation == OperationNone || operation == OperationWrite || operation == OperationTruncate); 228 switch (operation) { 229 case OperationWrite: 230 ASSERT(m_truncateLength == -1); 231 ASSERT(m_blobBeingWritten.get()); 232 ASSERT(m_readyState == WRITING); 233 writer()->write(position(), m_blobBeingWritten.get()); 234 m_isOperationInProgress = true; 235 break; 236 case OperationTruncate: 237 ASSERT(m_truncateLength >= 0); 238 ASSERT(m_readyState == WRITING); 239 writer()->truncate(m_truncateLength); 240 m_isOperationInProgress = true; 241 break; 242 case OperationNone: 243 ASSERT(m_truncateLength == -1); 244 ASSERT(!m_blobBeingWritten.get()); 245 ASSERT(m_readyState == DONE); 246 break; 247 } 248 } 249 250 void FileWriter::signalCompletion(FileError::ErrorCode code) 251 { 252 m_readyState = DONE; 166 253 m_truncateLength = -1; 167 scriptExecutionContext()->postTask(FileWriterCompletionEventTask::create(this, FileError::OK));168 }169 170 void FileWriter::didFail(FileError::ErrorCode code)171 {172 ASSERT(code != FileError::OK);173 m_blobBeingWritten.clear();174 scriptExecutionContext()->postTask(FileWriterCompletionEventTask::create(this, code));175 }176 177 void FileWriter::signalCompletion(FileError::ErrorCode code)178 {179 m_readyState = DONE;180 254 if (FileError::OK != code) { 181 255 m_error = FileError::create(code); 182 fireEvent(eventNames().errorEvent);183 256 if (FileError::ABORT_ERR == code) 184 257 fireEvent(eventNames().abortEvent); 258 else 259 fireEvent(eventNames().errorEvent); 185 260 } else 186 261 fireEvent(eventNames().writeEvent); … … 190 265 void FileWriter::fireEvent(const AtomicString& type) 191 266 { 267 ++m_recursionDepth; 192 268 dispatchEvent(ProgressEvent::create(type, true, m_bytesWritten, m_bytesToWrite)); 269 --m_recursionDepth; 270 ASSERT(m_recursionDepth >= 0); 193 271 } 194 272 -
trunk/Source/WebCore/fileapi/FileWriter.h
r95901 r96177 91 91 92 92 private: 93 enum Operation { 94 OperationNone, 95 OperationWrite, 96 OperationTruncate 97 }; 98 93 99 FileWriter(ScriptExecutionContext*); 94 100 … … 101 107 virtual EventTargetData* ensureEventTargetData() { return &m_eventTargetData; } 102 108 109 void doOperation(Operation); 110 111 void signalCompletion(FileError::ErrorCode); 112 103 113 void fireEvent(const AtomicString& type); 104 114 105 115 void setError(FileError::ErrorCode, ExceptionCode&); 106 116 107 void signalCompletion(FileError::ErrorCode);108 109 class FileWriterCompletionEventTask : public ScriptExecutionContext::Task {110 public:111 static PassOwnPtr<FileWriterCompletionEventTask> create(PassRefPtr<FileWriter> fileWriter, FileError::ErrorCode code)112 {113 return adoptPtr(new FileWriterCompletionEventTask(fileWriter, code));114 }115 116 117 virtual void performTask(ScriptExecutionContext*)118 {119 m_fileWriter->signalCompletion(m_code);120 }121 private:122 FileWriterCompletionEventTask(PassRefPtr<FileWriter> fileWriter, FileError::ErrorCode code)123 : m_fileWriter(fileWriter)124 , m_code(code)125 {126 }127 128 RefPtr<FileWriter> m_fileWriter;129 FileError::ErrorCode m_code;130 };131 132 117 RefPtr<FileError> m_error; 133 118 EventTargetData m_eventTargetData; 134 119 ReadyState m_readyState; 135 bool m_startedWriting; 120 bool m_isOperationInProgress; 121 Operation m_queuedOperation; 136 122 long long m_bytesWritten; 137 123 long long m_bytesToWrite; 138 124 long long m_truncateLength; 125 long long m_numAborts; 126 long long m_recursionDepth; 139 127 RefPtr<Blob> m_blobBeingWritten; 140 128 };
Note: See TracChangeset
for help on using the changeset viewer.