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