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