Home | History | Annotate | Download | only in bindings
      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