• 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 * @constructor
33 * @extends {WebInspector.TargetAwareObject}
34 * @param {!WebInspector.Target} target
35 */
36WebInspector.FileSystemModel = function(target)
37{
38    WebInspector.TargetAwareObject.call(this, target);
39
40    this._fileSystemsForOrigin = {};
41
42    target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, this._securityOriginAdded, this);
43    target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, this._securityOriginRemoved, this);
44    this._agent = target.fileSystemAgent();
45    this._agent.enable();
46
47    this._reset();
48}
49
50WebInspector.FileSystemModel.prototype = {
51    _reset: function()
52    {
53        for (var securityOrigin in this._fileSystemsForOrigin)
54            this._removeOrigin(securityOrigin);
55        var securityOrigins = this.target().resourceTreeModel.securityOrigins();
56        for (var i = 0; i < securityOrigins.length; ++i)
57            this._addOrigin(securityOrigins[i]);
58    },
59
60    /**
61     * @param {!WebInspector.Event} event
62     */
63    _securityOriginAdded: function(event)
64    {
65        var securityOrigin = /** @type {string} */ (event.data);
66        this._addOrigin(securityOrigin);
67    },
68
69    /**
70     * @param {!WebInspector.Event} event
71     */
72    _securityOriginRemoved: function(event)
73    {
74        var securityOrigin = /** @type {string} */ (event.data);
75        this._removeOrigin(securityOrigin);
76    },
77
78    /**
79     * @param {string} securityOrigin
80     */
81    _addOrigin: function(securityOrigin)
82    {
83        this._fileSystemsForOrigin[securityOrigin] = {};
84
85        var types = ["persistent", "temporary"];
86        for (var i = 0; i < types.length; ++i)
87            this._requestFileSystemRoot(securityOrigin, types[i], this._fileSystemRootReceived.bind(this, securityOrigin, types[i], this._fileSystemsForOrigin[securityOrigin]));
88    },
89
90    /**
91     * @param {string} securityOrigin
92     */
93    _removeOrigin: function(securityOrigin)
94    {
95        for (var type in this._fileSystemsForOrigin[securityOrigin]) {
96            var fileSystem = this._fileSystemsForOrigin[securityOrigin][type];
97            delete this._fileSystemsForOrigin[securityOrigin][type];
98            this._fileSystemRemoved(fileSystem);
99        }
100        delete this._fileSystemsForOrigin[securityOrigin];
101    },
102
103    /**
104     * @param {string} origin
105     * @param {string} type
106     * @param {function(number, !FileSystemAgent.Entry=)} callback
107     */
108    _requestFileSystemRoot: function(origin, type, callback)
109    {
110        /**
111         * @param {?Protocol.Error} error
112         * @param {number} errorCode
113         * @param {!FileSystemAgent.Entry=} backendRootEntry
114         */
115        function innerCallback(error, errorCode, backendRootEntry)
116        {
117            if (error) {
118                callback(FileError.SECURITY_ERR);
119                return;
120            }
121
122            callback(errorCode, backendRootEntry);
123        }
124
125        this._agent.requestFileSystemRoot(origin, type, innerCallback);
126    },
127
128    /**
129     * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem
130     */
131    _fileSystemAdded: function(fileSystem)
132    {
133        this.dispatchEventToListeners(WebInspector.FileSystemModel.EventTypes.FileSystemAdded, fileSystem);
134    },
135
136    /**
137     * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem
138     */
139    _fileSystemRemoved: function(fileSystem)
140    {
141        this.dispatchEventToListeners(WebInspector.FileSystemModel.EventTypes.FileSystemRemoved, fileSystem);
142    },
143
144    refreshFileSystemList: function()
145    {
146        this._reset();
147    },
148
149    /**
150     * @param {string} origin
151     * @param {string} type
152     * @param {!Object.<string, !WebInspector.FileSystemModel.FileSystem>} store
153     * @param {number} errorCode
154     * @param {!FileSystemAgent.Entry=} backendRootEntry
155     */
156    _fileSystemRootReceived: function(origin, type, store, errorCode, backendRootEntry)
157    {
158        if (!errorCode && backendRootEntry && this._fileSystemsForOrigin[origin] === store) {
159            var fileSystem = new WebInspector.FileSystemModel.FileSystem(this, origin, type, backendRootEntry);
160            store[type] = fileSystem;
161            this._fileSystemAdded(fileSystem);
162        }
163    },
164
165    /**
166     * @param {!WebInspector.FileSystemModel.Directory} directory
167     * @param {function(number, !Array.<!WebInspector.FileSystemModel.Entry>=)} callback
168     */
169    requestDirectoryContent: function(directory, callback)
170    {
171        this._requestDirectoryContent(directory.url, this._directoryContentReceived.bind(this, directory, callback));
172    },
173
174    /**
175     * @param {string} url
176     * @param {function(number, !Array.<!FileSystemAgent.Entry>=)} callback
177     */
178    _requestDirectoryContent: function(url, callback)
179    {
180        /**
181         * @param {?Protocol.Error} error
182         * @param {number} errorCode
183         * @param {!Array.<!FileSystemAgent.Entry>=} backendEntries
184         */
185        function innerCallback(error, errorCode, backendEntries)
186        {
187            if (error) {
188                callback(FileError.SECURITY_ERR);
189                return;
190            }
191
192            if (errorCode !== 0) {
193                callback(errorCode);
194                return;
195            }
196
197            callback(errorCode, backendEntries);
198        }
199
200        this._agent.requestDirectoryContent(url, innerCallback);
201    },
202
203    /**
204     * @param {!WebInspector.FileSystemModel.Directory} parentDirectory
205     * @param {function(number, !Array.<!WebInspector.FileSystemModel.Entry>=)} callback
206     * @param {number} errorCode
207     * @param {!Array.<!FileSystemAgent.Entry>=} backendEntries
208     */
209    _directoryContentReceived: function(parentDirectory, callback, errorCode, backendEntries)
210    {
211        if (!backendEntries) {
212            callback(errorCode);
213            return;
214        }
215
216        var entries = [];
217        for (var i = 0; i < backendEntries.length; ++i) {
218            if (backendEntries[i].isDirectory)
219                entries.push(new WebInspector.FileSystemModel.Directory(this, parentDirectory.fileSystem, backendEntries[i]));
220            else
221                entries.push(new WebInspector.FileSystemModel.File(this, parentDirectory.fileSystem, backendEntries[i]));
222        }
223
224        callback(errorCode, entries);
225    },
226
227    /**
228     * @param {!WebInspector.FileSystemModel.Entry} entry
229     * @param {function(number, !FileSystemAgent.Metadata=)} callback
230     */
231    requestMetadata: function(entry, callback)
232    {
233        /**
234         * @param {?Protocol.Error} error
235         * @param {number} errorCode
236         * @param {!FileSystemAgent.Metadata=} metadata
237         */
238        function innerCallback(error, errorCode, metadata)
239        {
240            if (error) {
241                callback(FileError.SECURITY_ERR);
242                return;
243            }
244
245            callback(errorCode, metadata);
246        }
247
248        this._agent.requestMetadata(entry.url, innerCallback);
249    },
250
251    /**
252     * @param {!WebInspector.FileSystemModel.File} file
253     * @param {boolean} readAsText
254     * @param {number=} start
255     * @param {number=} end
256     * @param {string=} charset
257     * @param {function(number, string=, string=)=} callback
258     */
259    requestFileContent: function(file, readAsText, start, end, charset, callback)
260    {
261        this._requestFileContent(file.url, readAsText, start, end, charset, callback);
262    },
263
264    /**
265     * @param {string} url
266     * @param {boolean} readAsText
267     * @param {number=} start
268     * @param {number=} end
269     * @param {string=} charset
270     * @param {function(number, string=, string=)=} callback
271     */
272    _requestFileContent: function(url, readAsText, start, end, charset, callback)
273    {
274        /**
275         * @param {?Protocol.Error} error
276         * @param {number} errorCode
277         * @param {string=} content
278         * @param {string=} charset
279         */
280        function innerCallback(error, errorCode, content, charset)
281        {
282            if (error) {
283                if (callback)
284                    callback(FileError.SECURITY_ERR);
285                return;
286            }
287
288            if (callback)
289                callback(errorCode, content, charset);
290        }
291
292        this._agent.requestFileContent(url, readAsText, start, end, charset, innerCallback);
293    },
294    /**
295     * @param {!WebInspector.FileSystemModel.Entry} entry
296     * @param {function(number)=} callback
297     */
298    deleteEntry: function(entry, callback)
299    {
300        var fileSystemModel = this;
301        if (entry === entry.fileSystem.root)
302            this._deleteEntry(entry.url, hookFileSystemDeletion);
303        else
304            this._deleteEntry(entry.url, callback);
305
306        function hookFileSystemDeletion(errorCode)
307        {
308            callback(errorCode);
309            if (!errorCode)
310                fileSystemModel._removeFileSystem(entry.fileSystem);
311        }
312    },
313
314    /**
315     * @param {string} url
316     * @param {function(number)=} callback
317     */
318    _deleteEntry: function(url, callback)
319    {
320        /**
321         * @param {?Protocol.Error} error
322         * @param {number} errorCode
323         */
324        function innerCallback(error, errorCode)
325        {
326            if (error) {
327                if (callback)
328                    callback(FileError.SECURITY_ERR);
329                return;
330            }
331
332            if (callback)
333                callback(errorCode);
334        }
335
336        this._agent.deleteEntry(url, innerCallback);
337    },
338
339    /**
340     * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem
341     */
342    _removeFileSystem: function(fileSystem)
343    {
344        var origin = fileSystem.origin;
345        var type = fileSystem.type;
346        if (this._fileSystemsForOrigin[origin] && this._fileSystemsForOrigin[origin][type]) {
347            delete this._fileSystemsForOrigin[origin][type];
348            this._fileSystemRemoved(fileSystem);
349
350            if (Object.isEmpty(this._fileSystemsForOrigin[origin]))
351                delete this._fileSystemsForOrigin[origin];
352        }
353    },
354
355    __proto__: WebInspector.TargetAwareObject.prototype
356}
357
358
359WebInspector.FileSystemModel.EventTypes = {
360    FileSystemAdded: "FileSystemAdded",
361    FileSystemRemoved: "FileSystemRemoved"
362}
363
364/**
365 * @constructor
366 * @param {!WebInspector.FileSystemModel} fileSystemModel
367 * @param {string} origin
368 * @param {string} type
369 * @param {!FileSystemAgent.Entry} backendRootEntry
370 */
371WebInspector.FileSystemModel.FileSystem = function(fileSystemModel, origin, type, backendRootEntry)
372{
373    this.origin = origin;
374    this.type = type;
375
376    this.root = new WebInspector.FileSystemModel.Directory(fileSystemModel, this, backendRootEntry);
377}
378
379WebInspector.FileSystemModel.FileSystem.prototype = {
380    /**
381     * @type {string}
382     */
383    get name()
384    {
385        return "filesystem:" + this.origin + "/" + this.type;
386    }
387}
388
389/**
390 * @constructor
391 * @param {!WebInspector.FileSystemModel} fileSystemModel
392 * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem
393 * @param {!FileSystemAgent.Entry} backendEntry
394 */
395WebInspector.FileSystemModel.Entry = function(fileSystemModel, fileSystem, backendEntry)
396{
397    this._fileSystemModel = fileSystemModel;
398    this._fileSystem = fileSystem;
399
400    this._url = backendEntry.url;
401    this._name = backendEntry.name;
402    this._isDirectory = backendEntry.isDirectory;
403}
404
405/**
406 * @param {!WebInspector.FileSystemModel.Entry} x
407 * @param {!WebInspector.FileSystemModel.Entry} y
408 * @return {number}
409 */
410WebInspector.FileSystemModel.Entry.compare = function(x, y)
411{
412    if (x.isDirectory != y.isDirectory)
413        return y.isDirectory ? 1 : -1;
414    return x.name.compareTo(y.name);
415}
416
417WebInspector.FileSystemModel.Entry.prototype = {
418    /**
419     * @type {!WebInspector.FileSystemModel}
420     */
421    get fileSystemModel()
422    {
423        return this._fileSystemModel;
424    },
425
426    /**
427     * @type {!WebInspector.FileSystemModel.FileSystem}
428     */
429    get fileSystem()
430    {
431        return this._fileSystem;
432    },
433
434    /**
435     * @type {string}
436     */
437    get url()
438    {
439        return this._url;
440    },
441
442    /**
443     * @type {string}
444     */
445    get name()
446    {
447        return this._name;
448    },
449
450    /**
451     * @type {boolean}
452     */
453    get isDirectory()
454    {
455        return this._isDirectory;
456    },
457
458    /**
459     * @param {function(number, !FileSystemAgent.Metadata)} callback
460     */
461    requestMetadata: function(callback)
462    {
463        this.fileSystemModel.requestMetadata(this, callback);
464    },
465
466    /**
467     * @param {function(number)} callback
468     */
469    deleteEntry: function(callback)
470    {
471        this.fileSystemModel.deleteEntry(this, callback);
472    }
473}
474
475/**
476 * @constructor
477 * @extends {WebInspector.FileSystemModel.Entry}
478 * @param {!WebInspector.FileSystemModel} fileSystemModel
479 * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem
480 * @param {!FileSystemAgent.Entry} backendEntry
481 */
482WebInspector.FileSystemModel.Directory = function(fileSystemModel, fileSystem, backendEntry)
483{
484    WebInspector.FileSystemModel.Entry.call(this, fileSystemModel, fileSystem, backendEntry);
485}
486
487WebInspector.FileSystemModel.Directory.prototype = {
488    /**
489     * @param {function(number, !Array.<!WebInspector.FileSystemModel.Directory>)} callback
490     */
491    requestDirectoryContent: function(callback)
492    {
493        this.fileSystemModel.requestDirectoryContent(this, callback);
494    },
495
496    __proto__: WebInspector.FileSystemModel.Entry.prototype
497}
498
499/**
500 * @constructor
501 * @extends {WebInspector.FileSystemModel.Entry}
502 * @param {!WebInspector.FileSystemModel} fileSystemModel
503 * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem
504 * @param {!FileSystemAgent.Entry} backendEntry
505 */
506WebInspector.FileSystemModel.File = function(fileSystemModel, fileSystem, backendEntry)
507{
508    WebInspector.FileSystemModel.Entry.call(this, fileSystemModel, fileSystem, backendEntry);
509
510    this._mimeType = backendEntry.mimeType;
511    this._resourceType = WebInspector.resourceTypes[backendEntry.resourceType];
512    this._isTextFile = backendEntry.isTextFile;
513}
514
515WebInspector.FileSystemModel.File.prototype = {
516    /**
517     * @type {string}
518     */
519    get mimeType()
520    {
521        return this._mimeType;
522    },
523
524    /**
525     * @type {!WebInspector.ResourceType}
526     */
527    get resourceType()
528    {
529        return this._resourceType;
530    },
531
532    /**
533     * @type {boolean}
534     */
535    get isTextFile()
536    {
537        return this._isTextFile;
538    },
539
540    /**
541     * @param {boolean} readAsText
542     * @param {number=} start
543     * @param {number=} end
544     * @param {string=} charset
545     * @param {function(number, string=)=} callback
546     */
547    requestFileContent: function(readAsText, start, end, charset, callback)
548    {
549        this.fileSystemModel.requestFileContent(this, readAsText, start, end, charset, callback);
550    },
551
552    __proto__: WebInspector.FileSystemModel.Entry.prototype
553}
554