Remove Blob.webkitSlice
[WebKit-https.git] / Source / WebCore / inspector / front-end / FileUtils.js
1 /*
2  * Copyright (C) 2012 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 /**
32  * @interface
33  */
34 WebInspector.OutputStreamDelegate = function()
35 {
36 }
37
38 WebInspector.OutputStreamDelegate.prototype = {
39     onTransferStarted: function(source) { },
40
41     onChunkTransferred: function(source) { },
42
43     onTransferFinished: function(source) { },
44
45     onError: function(source, event) { }
46 }
47
48 /**
49  * @interface
50  */
51 WebInspector.OutputStream = function()
52 {
53 }
54
55 WebInspector.OutputStream.prototype = {
56     startTransfer: function() { },
57
58     /**
59      * @param {string} chunk
60      */
61     transferChunk: function(chunk) { },
62
63     finishTransfer: function() { },
64
65     dispose: function() { }
66 }
67
68 /**
69  * @constructor
70  * @param {!File} file
71  * @param {number} chunkSize
72  * @param {!WebInspector.OutputStreamDelegate} delegate
73  */
74 WebInspector.ChunkedFileReader = function(file, chunkSize, delegate)
75 {
76     this._file = file;
77     this._fileSize = file.size;
78     this._loadedSize = 0;
79     this._chunkSize = chunkSize;
80     this._delegate = delegate;
81     this._isCanceled = false;
82 }
83
84 WebInspector.ChunkedFileReader.prototype = {
85     /**
86      * @param {!WebInspector.OutputStream} output
87      */
88     start: function(output)
89     {
90         this._output = output;
91
92         this._reader = new FileReader();
93         this._reader.onload = this._onChunkLoaded.bind(this);
94         this._reader.onerror = this._delegate.onError.bind(this._delegate, this);
95         this._output.startTransfer();
96         this._delegate.onTransferStarted(this);
97         this._loadChunk();
98     },
99
100     cancel: function()
101     {
102         this._isCanceled = true;
103     },
104
105     loadedSize: function()
106     {
107         return this._loadedSize;
108     },
109
110     fileSize: function()
111     {
112         return this._fileSize;
113     },
114
115     fileName: function()
116     {
117         return this._file.name;
118     },
119
120     /**
121      * @param {Event} event
122      */
123     _onChunkLoaded: function(event)
124     {
125         if (this._isCanceled)
126             return;
127
128         if (event.target.readyState !== FileReader.DONE)
129             return;
130
131         var data = event.target.result;
132         this._loadedSize += data.length;
133
134         this._output.transferChunk(data);
135         if (this._isCanceled)
136             return;
137         this._delegate.onChunkTransferred(this);
138
139         if (this._loadedSize === this._fileSize) {
140             this._file = null;
141             this._reader = null;
142             this._output.finishTransfer();
143             this._delegate.onTransferFinished(this);
144             return;
145         }
146
147         this._loadChunk();
148     },
149
150     _loadChunk: function()
151     {
152         var chunkStart = this._loadedSize;
153         var chunkEnd = Math.min(this._fileSize, chunkStart + this._chunkSize)
154         var nextPart = this._file.slice(chunkStart, chunkEnd);
155         this._reader.readAsText(nextPart);
156     }
157 }
158
159 /**
160  * @constructor
161  * @param {string} url
162  * @param {!WebInspector.OutputStreamDelegate} delegate
163  */
164 WebInspector.ChunkedXHRReader = function(url, delegate)
165 {
166     this._url = url;
167     this._delegate = delegate;
168     this._fileSize = 0;
169     this._loadedSize = 0;
170     this._isCanceled = false;
171 }
172
173 WebInspector.ChunkedXHRReader.prototype = {
174     /**
175      * @param {!WebInspector.OutputStream} output
176      */
177     start: function(output)
178     {
179         this._output = output;
180
181         this._xhr = new XMLHttpRequest();
182         this._xhr.open("GET", this._url, true);
183         this._xhr.onload = this._onLoad.bind(this);
184         this._xhr.onprogress = this._onProgress.bind(this);
185         this._xhr.onerror = this._delegate.onError.bind(this._delegate, this);
186         this._xhr.send(null);
187
188         this._output.startTransfer();
189         this._delegate.onTransferStarted(this);
190     },
191
192     cancel: function()
193     {
194         this._isCanceled = true;
195         this._xhr.abort();
196     },
197
198     loadedSize: function()
199     {
200         return this._loadedSize;
201     },
202
203     fileSize: function()
204     {
205         return this._fileSize;
206     },
207
208     fileName: function()
209     {
210         return this._url;
211     },
212
213     /**
214      * @param {Event} event
215      */
216     _onProgress: function(event)
217     {
218         if (this._isCanceled)
219             return;
220
221         if (event.lengthComputable)
222             this._fileSize = event.total;
223
224         var data = this._xhr.responseText.substring(this._loadedSize);
225         if (!data.length)
226             return;
227
228         this._loadedSize += data.length;
229         this._output.transferChunk(data);
230         if (this._isCanceled)
231             return;
232         this._delegate.onChunkTransferred(this);
233     },
234
235     /**
236      * @param {Event} event
237      */
238     _onLoad: function(event)
239     {
240         this._onProgress(event);
241
242         if (this._isCanceled)
243             return;
244
245         this._output.finishTransfer();
246         this._delegate.onTransferFinished(this);
247     }
248 }
249
250 /**
251  * @param {function(File)} callback
252  * @return {Node}
253  */
254 WebInspector.createFileSelectorElement = function(callback) {
255     var fileSelectorElement = document.createElement("input");
256     fileSelectorElement.type = "file";
257     fileSelectorElement.style.zIndex = -1;
258     fileSelectorElement.style.position = "absolute";
259     fileSelectorElement.onchange = function(event) {
260         callback(fileSelectorElement.files[0]);
261     };
262     return fileSelectorElement;
263 }
264
265 /**
266  * @param {string} source
267  * @param {number=} startIndex
268  * @param {number=} lastIndex
269  */
270 WebInspector.findBalancedCurlyBrackets = function(source, startIndex, lastIndex) {
271     lastIndex = lastIndex || source.length;
272     startIndex = startIndex || 0;
273     var counter = 0;
274     var inString = false;
275
276     for (var index = startIndex; index < lastIndex; ++index) {
277         var character = source[index];
278         if (inString) {
279             if (character === "\\")
280                 ++index;
281             else if (character === "\"")
282                 inString = false;
283         } else {
284             if (character === "\"")
285                 inString = true;
286             else if (character === "{")
287                 ++counter;
288             else if (character === "}") {
289                 if (--counter === 0)
290                     return index + 1;
291             }
292         }
293     }
294     return -1;
295 }
296
297 /**
298  * @constructor
299  * @implements {WebInspector.OutputStream}
300  * @param {string} fileName
301  * @param {!WebInspector.OutputStreamDelegate} delegate
302  */
303 WebInspector.FileOutputStream = function(fileName, delegate)
304 {
305     this._fileName = fileName;
306     this._delegate = delegate;
307 }
308
309 WebInspector.FileOutputStream.prototype = {
310     /**
311      * @override
312      */
313     startTransfer: function()
314     {
315         WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.SavedURL, this._onTransferStarted, this);
316         WebInspector.fileManager.save(this._fileName, "", true);
317     },
318
319     /**
320      * @override
321      * @param {string} chunk
322      */
323     transferChunk: function(chunk)
324     {
325         WebInspector.fileManager.append(this._fileName, chunk);
326     },
327
328     /**
329      * @override
330      */
331     finishTransfer: function()
332     {
333         WebInspector.fileManager.removeEventListener(WebInspector.FileManager.EventTypes.AppendedToURL, this._onChunkTransferred, this);
334         this._delegate.onTransferFinished(this);
335     },
336
337     dispose: function()
338     {
339     },
340
341     /**
342      * @param {WebInspector.Event} event
343      */
344     _onTransferStarted: function(event)
345     {
346         if (event.data !== this._fileName)
347             return;
348         this._delegate.onTransferStarted(this);
349         WebInspector.fileManager.removeEventListener(WebInspector.FileManager.EventTypes.SavedURL, this._onTransferStarted, this);
350         WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.AppendedToURL, this._onChunkTransferred, this);
351     },
352
353     /**
354      * @param {WebInspector.Event} event
355      */
356     _onChunkTransferred: function(event)
357     {
358         if (event.data !== this._fileName)
359             return;
360         this._delegate.onChunkTransferred(this);
361     }
362 }