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.messageSink.addErrorMessage("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.messageSink.addErrorMessage("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 {!string} data 124 * @param {!function(boolean)} callback 125 */ 126 write: function(data, callback) 127 { 128 var blob = new Blob([data], {type: 'text/plain'}); 129 this._writer.onerror = function(e) 130 { 131 WebInspector.messageSink.addErrorMessage("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 /** 152 * @param {!File} file 153 */ 154 function didGetFile(file) 155 { 156 var reader = new FileReader(); 157 158 /** 159 * @this {FileReader} 160 */ 161 reader.onloadend = function(e) 162 { 163 callback(/** @type {?string} */ (this.result)); 164 } 165 reader.onerror = function(error) 166 { 167 WebInspector.messageSink.addErrorMessage("Failed to read from temp file: " + error.message); 168 } 169 reader.readAsText(file); 170 } 171 function didFailToGetFile(error) 172 { 173 WebInspector.messageSink.addErrorMessage("Failed to load temp file: " + error.message); 174 callback(null); 175 } 176 this._fileEntry.file(didGetFile, didFailToGetFile); 177 }, 178 179 /** 180 * @param {!WebInspector.OutputStream} outputStream 181 * @param {!WebInspector.OutputStreamDelegate} delegate 182 */ 183 writeToOutputSteam: function(outputStream, delegate) 184 { 185 /** 186 * @param {!File} file 187 */ 188 function didGetFile(file) 189 { 190 var reader = new WebInspector.ChunkedFileReader(file, 10*1000*1000, delegate); 191 reader.start(outputStream); 192 } 193 194 function didFailToGetFile(error) 195 { 196 WebInspector.messageSink.addErrorMessage("Failed to load temp file: " + error.message); 197 outputStream.close(); 198 } 199 200 this._fileEntry.file(didGetFile, didFailToGetFile); 201 }, 202 203 remove: function() 204 { 205 if (this._fileEntry) 206 this._fileEntry.remove(function() {}); 207 } 208 } 209 210 /** 211 * @constructor 212 * @param {!string} dirPath 213 * @param {!string} name 214 */ 215 WebInspector.BufferedTempFileWriter = function(dirPath, name) 216 { 217 this._chunks = []; 218 this._tempFile = null; 219 this._isWriting = false; 220 this._finishCallback = null; 221 this._isFinished = false; 222 new WebInspector.TempFile(dirPath, name, this._didCreateTempFile.bind(this)); 223 } 224 225 WebInspector.BufferedTempFileWriter.prototype = { 226 /** 227 * @param {!string} data 228 */ 229 write: function(data) 230 { 231 if (!this._chunks) 232 return; 233 if (this._finishCallback) 234 throw new Error("No writes are allowed after close."); 235 this._chunks.push(data); 236 if (this._tempFile && !this._isWriting) 237 this._writeNextChunk(); 238 }, 239 240 /** 241 * @param {!function(?WebInspector.TempFile)} callback 242 */ 243 close: function(callback) 244 { 245 this._finishCallback = callback; 246 if (this._isFinished) 247 callback(this._tempFile); 248 else if (!this._isWriting && !this._chunks.length) 249 this._notifyFinished(); 250 }, 251 252 _didCreateTempFile: function(tempFile) 253 { 254 this._tempFile = tempFile; 255 if (!tempFile) { 256 this._chunks = null; 257 this._notifyFinished(); 258 return; 259 } 260 if (this._chunks.length) 261 this._writeNextChunk(); 262 }, 263 264 _writeNextChunk: function() 265 { 266 var chunkSize = 0; 267 var endIndex = 0; 268 for (; endIndex < this._chunks.length; endIndex++) { 269 chunkSize += this._chunks[endIndex].length; 270 if (chunkSize > 10 * 1000 * 1000) 271 break; 272 } 273 var chunk = this._chunks.slice(0, endIndex + 1).join(""); 274 this._chunks.splice(0, endIndex + 1); 275 this._isWriting = true; 276 this._tempFile.write(chunk, this._didWriteChunk.bind(this)); 277 }, 278 279 _didWriteChunk: function(success) 280 { 281 this._isWriting = false; 282 if (!success) { 283 this._tempFile = null; 284 this._chunks = null; 285 this._notifyFinished(); 286 return; 287 } 288 if (this._chunks.length) 289 this._writeNextChunk(); 290 else if (this._finishCallback) 291 this._notifyFinished(); 292 }, 293 294 _notifyFinished: function() 295 { 296 this._isFinished = true; 297 if (this._tempFile) 298 this._tempFile.finishWriting(); 299 if (this._finishCallback) 300 this._finishCallback(this._tempFile); 301 } 302 } 303 304 /** 305 * @constructor 306 */ 307 WebInspector.TempStorageCleaner = function() 308 { 309 this._worker = new SharedWorker("temp_storage_shared_worker/TempStorageSharedWorker.js", "TempStorage"); 310 this._callbacks = []; 311 this._worker.port.onmessage = this._handleMessage.bind(this); 312 this._worker.port.onerror = this._handleError.bind(this); 313 } 314 315 WebInspector.TempStorageCleaner.prototype = { 316 /** 317 * @param {!function()} callback 318 */ 319 ensureStorageCleared: function(callback) 320 { 321 if (this._callbacks) 322 this._callbacks.push(callback); 323 else 324 callback(); 325 }, 326 327 _handleMessage: function(event) 328 { 329 if (event.data.type === "tempStorageCleared") { 330 if (event.data.error) 331 WebInspector.messageSink.addErrorMessage(event.data.error); 332 this._notifyCallbacks(); 333 } 334 }, 335 336 _handleError: function(event) 337 { 338 WebInspector.messageSink.addErrorMessage(WebInspector.UIString("Failed to clear temp storage: %s", event.data)); 339 this._notifyCallbacks(); 340 }, 341 342 _notifyCallbacks: function() 343 { 344 var callbacks = this._callbacks; 345 this._callbacks = null; 346 for (var i = 0; i < callbacks.length; i++) 347 callbacks[i](); 348 } 349 } 350 351 /** 352 * @param {!function()} callback 353 */ 354 WebInspector.TempFile._ensureTempStorageCleared = function(callback) 355 { 356 if (!WebInspector.TempFile._storageCleaner) 357 WebInspector.TempFile._storageCleaner = new WebInspector.TempStorageCleaner(); 358 WebInspector.TempFile._storageCleaner.ensureStorageCleared(callback); 359 } 360