1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; 32 33 /** 34 * @constructor 35 * @param {!string} dirPath 36 * @param {!string} name 37 * @param {!function(?WebInspector.TempFile)} callback 38 */ 39 WebInspector.TempFile = function(dirPath, name, callback) 40 { 41 this._fileEntry = null; 42 this._writer = null; 43 44 /** 45 * @param {!FileSystem} fs 46 * @this {WebInspector.TempFile} 47 */ 48 function didInitFs(fs) 49 { 50 fs.root.getDirectory(dirPath, { create: true }, didGetDir.bind(this), errorHandler); 51 } 52 53 /** 54 * @param {!DirectoryEntry} dir 55 * @this {WebInspector.TempFile} 56 */ 57 function didGetDir(dir) 58 { 59 dir.getFile(name, { create: true }, didCreateFile.bind(this), errorHandler); 60 } 61 62 /** 63 * @param {!FileEntry} fileEntry 64 * @this {WebInspector.TempFile} 65 */ 66 function didCreateFile(fileEntry) 67 { 68 this._fileEntry = fileEntry; 69 fileEntry.createWriter(didCreateWriter.bind(this), errorHandler); 70 } 71 72 /** 73 * @param {!FileWriter} writer 74 * @this {WebInspector.TempFile} 75 */ 76 function didCreateWriter(writer) 77 { 78 /** 79 * @this {WebInspector.TempFile} 80 */ 81 function didTruncate(e) 82 { 83 this._writer = writer; 84 writer.onwrite = null; 85 writer.onerror = null; 86 callback(this); 87 } 88 89 function onTruncateError(e) 90 { 91 WebInspector.console.error("Failed to truncate temp file " + e.code + " : " + e.message); 92 callback(null); 93 } 94 95 if (writer.length) { 96 writer.onwrite = didTruncate.bind(this); 97 writer.onerror = onTruncateError; 98 writer.truncate(0); 99 } else { 100 this._writer = writer; 101 callback(this); 102 } 103 } 104 105 function errorHandler(e) 106 { 107 WebInspector.console.error("Failed to create temp file " + e.code + " : " + e.message); 108 callback(null); 109 } 110 111 /** 112 * @this {WebInspector.TempFile} 113 */ 114 function didClearTempStorage() 115 { 116 window.requestFileSystem(window.TEMPORARY, 10, didInitFs.bind(this), errorHandler); 117 } 118 WebInspector.TempFile._ensureTempStorageCleared(didClearTempStorage.bind(this)); 119 } 120 121 WebInspector.TempFile.prototype = { 122 /** 123 * @param {!Array.<string>} strings 124 * @param {!function(boolean)} callback 125 */ 126 write: function(strings, callback) 127 { 128 var blob = new Blob(strings, {type: 'text/plain'}); 129 this._writer.onerror = function(e) 130 { 131 WebInspector.console.error("Failed to write into a temp file: " + e.message); 132 callback(false); 133 } 134 this._writer.onwrite = function(e) 135 { 136 callback(true); 137 } 138 this._writer.write(blob); 139 }, 140 141 finishWriting: function() 142 { 143 this._writer = null; 144 }, 145 146 /** 147 * @param {function(?string)} callback 148 */ 149 read: function(callback) 150 { 151 this.readRange(undefined, undefined, callback); 152 }, 153 154 /** 155 * @param {number|undefined} startOffset 156 * @param {number|undefined} endOffset 157 * @param {function(?string)} callback 158 */ 159 readRange: function(startOffset, endOffset, callback) 160 { 161 /** 162 * @param {!Blob} file 163 */ 164 function didGetFile(file) 165 { 166 var reader = new FileReader(); 167 168 if (typeof startOffset === "number" || typeof endOffset === "number") 169 file = file.slice(/** @type {number} */ (startOffset), /** @type {number} */ (endOffset)); 170 /** 171 * @this {FileReader} 172 */ 173 reader.onloadend = function(e) 174 { 175 callback(/** @type {?string} */ (this.result)); 176 } 177 reader.onerror = function(error) 178 { 179 WebInspector.console.error("Failed to read from temp file: " + error.message); 180 } 181 reader.readAsText(file); 182 } 183 function didFailToGetFile(error) 184 { 185 WebInspector.console.error("Failed to load temp file: " + error.message); 186 callback(null); 187 } 188 this._fileEntry.file(didGetFile, didFailToGetFile); 189 }, 190 191 /** 192 * @param {!WebInspector.OutputStream} outputStream 193 * @param {!WebInspector.OutputStreamDelegate} delegate 194 */ 195 writeToOutputSteam: function(outputStream, delegate) 196 { 197 /** 198 * @param {!File} file 199 */ 200 function didGetFile(file) 201 { 202 var reader = new WebInspector.ChunkedFileReader(file, 10*1000*1000, delegate); 203 reader.start(outputStream); 204 } 205 206 function didFailToGetFile(error) 207 { 208 WebInspector.console.error("Failed to load temp file: " + error.message); 209 outputStream.close(); 210 } 211 212 this._fileEntry.file(didGetFile, didFailToGetFile); 213 }, 214 215 remove: function() 216 { 217 if (this._fileEntry) 218 this._fileEntry.remove(function() {}); 219 } 220 } 221 222 /** 223 * @constructor 224 * @param {!string} dirPath 225 * @param {!string} name 226 */ 227 WebInspector.DeferredTempFile = function(dirPath, name) 228 { 229 this._chunks = []; 230 this._tempFile = null; 231 this._isWriting = false; 232 this._finishCallback = null; 233 this._finishedWriting = false; 234 this._callsPendingOpen = []; 235 this._pendingReads = []; 236 new WebInspector.TempFile(dirPath, name, this._didCreateTempFile.bind(this)); 237 } 238 239 WebInspector.DeferredTempFile.prototype = { 240 /** 241 * @param {!Array.<string>} strings 242 */ 243 write: function(strings) 244 { 245 if (!this._chunks) 246 return; 247 if (this._finishCallback) 248 throw new Error("No writes are allowed after close."); 249 this._chunks.push.apply(this._chunks, strings); 250 if (this._tempFile && !this._isWriting) 251 this._writeNextChunk(); 252 }, 253 254 /** 255 * @param {!function(?WebInspector.TempFile)} callback 256 */ 257 finishWriting: function(callback) 258 { 259 this._finishCallback = callback; 260 if (this._finishedWriting) 261 callback(this._tempFile); 262 else if (!this._isWriting && !this._chunks.length) 263 this._notifyFinished(); 264 }, 265 266 _didCreateTempFile: function(tempFile) 267 { 268 this._tempFile = tempFile; 269 var callsPendingOpen = this._callsPendingOpen; 270 this._callsPendingOpen = null; 271 for (var i = 0; i < callsPendingOpen.length; ++i) 272 callsPendingOpen[i](); 273 if (!tempFile) { 274 this._chunks = null; 275 this._notifyFinished(); 276 return; 277 } 278 if (this._chunks.length) 279 this._writeNextChunk(); 280 }, 281 282 _writeNextChunk: function() 283 { 284 var chunks = this._chunks; 285 this._chunks = []; 286 this._isWriting = true; 287 this._tempFile.write(chunks, this._didWriteChunk.bind(this)); 288 }, 289 290 _didWriteChunk: function(success) 291 { 292 this._isWriting = false; 293 if (!success) { 294 this._tempFile = null; 295 this._chunks = null; 296 this._notifyFinished(); 297 return; 298 } 299 if (this._chunks.length) 300 this._writeNextChunk(); 301 else if (this._finishCallback) 302 this._notifyFinished(); 303 }, 304 305 _notifyFinished: function() 306 { 307 this._finishedWriting = true; 308 if (this._tempFile) 309 this._tempFile.finishWriting(); 310 if (this._finishCallback) 311 this._finishCallback(this._tempFile); 312 var pendingReads = this._pendingReads; 313 for (var i = 0; i < this._pendingReads.length; ++i) 314 this._pendingReads[i](); 315 this._pendingReads = []; 316 }, 317 318 /** 319 * @param {number|undefined} startOffset 320 * @param {number|undefined} endOffset 321 * @param {function(string?)} callback 322 */ 323 readRange: function(startOffset, endOffset, callback) 324 { 325 if (!this._finishedWriting) { 326 this._pendingReads.push(this.readRange.bind(this, startOffset, endOffset, callback)); 327 return; 328 } 329 if (!this._tempFile) { 330 callback(null); 331 return; 332 } 333 this._tempFile.readRange(startOffset, endOffset, callback); 334 }, 335 336 /** 337 * @param {!WebInspector.OutputStream} outputStream 338 * @param {!WebInspector.OutputStreamDelegate} delegate 339 */ 340 writeToOutputStream: function(outputStream, delegate) 341 { 342 if (this._callsPendingOpen) { 343 this._callsPendingOpen.push(this.writeToOutputStream.bind(this, outputStream, delegate)); 344 return; 345 } 346 if (this._tempFile) 347 this._tempFile.writeToOutputSteam(outputStream, delegate); 348 }, 349 350 remove: function() 351 { 352 if (this._callsPendingOpen) { 353 this._callsPendingOpen.push(this.remove.bind(this)); 354 return; 355 } 356 if (this._tempFile) 357 this._tempFile.remove(); 358 } 359 } 360 361 /** 362 * @constructor 363 */ 364 WebInspector.TempStorageCleaner = function() 365 { 366 this._worker = Runtime.startSharedWorker("temp_storage_shared_worker", "TempStorage"); 367 this._worker.onerror = this._handleError.bind(this); 368 this._callbacks = []; 369 this._worker.port.onmessage = this._handleMessage.bind(this); 370 this._worker.port.onerror = this._handleError.bind(this); 371 } 372 373 WebInspector.TempStorageCleaner.prototype = { 374 /** 375 * @param {!function()} callback 376 */ 377 ensureStorageCleared: function(callback) 378 { 379 if (this._callbacks) 380 this._callbacks.push(callback); 381 else 382 callback(); 383 }, 384 385 _handleMessage: function(event) 386 { 387 if (event.data.type === "tempStorageCleared") { 388 if (event.data.error) 389 WebInspector.console.error(event.data.error); 390 this._notifyCallbacks(); 391 } 392 }, 393 394 _handleError: function(event) 395 { 396 WebInspector.console.error(WebInspector.UIString("Failed to clear temp storage: %s", event.data)); 397 this._notifyCallbacks(); 398 }, 399 400 _notifyCallbacks: function() 401 { 402 var callbacks = this._callbacks; 403 this._callbacks = null; 404 for (var i = 0; i < callbacks.length; i++) 405 callbacks[i](); 406 } 407 } 408 409 /** 410 * @param {!function()} callback 411 */ 412 WebInspector.TempFile._ensureTempStorageCleared = function(callback) 413 { 414 if (!WebInspector.TempFile._storageCleaner) 415 WebInspector.TempFile._storageCleaner = new WebInspector.TempStorageCleaner(); 416 WebInspector.TempFile._storageCleaner.ensureStorageCleared(callback); 417 } 418