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.<string, !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); 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 if (!backendEntries) { 211 callback(errorCode); 212 return; 213 } 214 215 var entries = []; 216 for (var i = 0; i < backendEntries.length; ++i) { 217 if (backendEntries[i].isDirectory) 218 entries.push(new WebInspector.FileSystemModel.Directory(this, parentDirectory.fileSystem, backendEntries[i])); 219 else 220 entries.push(new WebInspector.FileSystemModel.File(this, parentDirectory.fileSystem, backendEntries[i])); 221 } 222 223 callback(errorCode, entries); 224 }, 225 226 /** 227 * @param {!WebInspector.FileSystemModel.Entry} entry 228 * @param {function(number, !FileSystemAgent.Metadata=)} callback 229 */ 230 requestMetadata: function(entry, callback) 231 { 232 /** 233 * @param {?Protocol.Error} error 234 * @param {number} errorCode 235 * @param {!FileSystemAgent.Metadata=} metadata 236 */ 237 function innerCallback(error, errorCode, metadata) 238 { 239 if (error) { 240 callback(FileError.SECURITY_ERR); 241 return; 242 } 243 244 callback(errorCode, metadata); 245 } 246 247 FileSystemAgent.requestMetadata(entry.url, innerCallback.bind(this)); 248 }, 249 250 /** 251 * @param {!WebInspector.FileSystemModel.File} file 252 * @param {boolean} readAsText 253 * @param {number=} start 254 * @param {number=} end 255 * @param {string=} charset 256 * @param {function(number, string=, string=)=} callback 257 */ 258 requestFileContent: function(file, readAsText, start, end, charset, callback) 259 { 260 this._requestFileContent(file.url, readAsText, start, end, charset, callback); 261 }, 262 263 /** 264 * @param {string} url 265 * @param {boolean} readAsText 266 * @param {number=} start 267 * @param {number=} end 268 * @param {string=} charset 269 * @param {function(number, string=, string=)=} callback 270 */ 271 _requestFileContent: function(url, readAsText, start, end, charset, callback) 272 { 273 /** 274 * @param {?Protocol.Error} error 275 * @param {number} errorCode 276 * @param {string=} content 277 * @param {string=} charset 278 */ 279 function innerCallback(error, errorCode, content, charset) 280 { 281 if (error) { 282 if (callback) 283 callback(FileError.SECURITY_ERR); 284 return; 285 } 286 287 if (callback) 288 callback(errorCode, content, charset); 289 } 290 291 FileSystemAgent.requestFileContent(url, readAsText, start, end, charset, innerCallback.bind(this)); 292 }, 293 /** 294 * @param {!WebInspector.FileSystemModel.Entry} entry 295 * @param {function(number)=} callback 296 */ 297 deleteEntry: function(entry, callback) 298 { 299 var fileSystemModel = this; 300 if (entry === entry.fileSystem.root) 301 this._deleteEntry(entry.url, hookFileSystemDeletion); 302 else 303 this._deleteEntry(entry.url, callback); 304 305 function hookFileSystemDeletion(errorCode) 306 { 307 callback(errorCode); 308 if (!errorCode) 309 fileSystemModel._removeFileSystem(entry.fileSystem); 310 } 311 }, 312 313 /** 314 * @param {string} url 315 * @param {function(number)=} callback 316 */ 317 _deleteEntry: function(url, callback) 318 { 319 /** 320 * @param {?Protocol.Error} error 321 * @param {number} errorCode 322 */ 323 function innerCallback(error, errorCode) 324 { 325 if (error) { 326 if (callback) 327 callback(FileError.SECURITY_ERR); 328 return; 329 } 330 331 if (callback) 332 callback(errorCode); 333 } 334 335 FileSystemAgent.deleteEntry(url, innerCallback.bind(this)); 336 }, 337 338 /** 339 * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem 340 */ 341 _removeFileSystem: function(fileSystem) 342 { 343 var origin = fileSystem.origin; 344 var type = fileSystem.type; 345 if (this._fileSystemsForOrigin[origin] && this._fileSystemsForOrigin[origin][type]) { 346 delete this._fileSystemsForOrigin[origin][type]; 347 this._fileSystemRemoved(fileSystem); 348 349 if (Object.isEmpty(this._fileSystemsForOrigin[origin])) 350 delete this._fileSystemsForOrigin[origin]; 351 } 352 }, 353 354 __proto__: WebInspector.Object.prototype 355} 356 357 358WebInspector.FileSystemModel.EventTypes = { 359 FileSystemAdded: "FileSystemAdded", 360 FileSystemRemoved: "FileSystemRemoved" 361} 362 363/** 364 * @constructor 365 * @param {!WebInspector.FileSystemModel} fileSystemModel 366 * @param {string} origin 367 * @param {string} type 368 * @param {!FileSystemAgent.Entry} backendRootEntry 369 */ 370WebInspector.FileSystemModel.FileSystem = function(fileSystemModel, origin, type, backendRootEntry) 371{ 372 this.origin = origin; 373 this.type = type; 374 375 this.root = new WebInspector.FileSystemModel.Directory(fileSystemModel, this, backendRootEntry); 376} 377 378WebInspector.FileSystemModel.FileSystem.prototype = { 379 /** 380 * @type {string} 381 */ 382 get name() 383 { 384 return "filesystem:" + this.origin + "/" + this.type; 385 } 386} 387 388/** 389 * @constructor 390 * @param {!WebInspector.FileSystemModel} fileSystemModel 391 * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem 392 * @param {!FileSystemAgent.Entry} backendEntry 393 */ 394WebInspector.FileSystemModel.Entry = function(fileSystemModel, fileSystem, backendEntry) 395{ 396 this._fileSystemModel = fileSystemModel; 397 this._fileSystem = fileSystem; 398 399 this._url = backendEntry.url; 400 this._name = backendEntry.name; 401 this._isDirectory = backendEntry.isDirectory; 402} 403 404/** 405 * @param {!WebInspector.FileSystemModel.Entry} x 406 * @param {!WebInspector.FileSystemModel.Entry} y 407 * @return {number} 408 */ 409WebInspector.FileSystemModel.Entry.compare = function(x, y) 410{ 411 if (x.isDirectory != y.isDirectory) 412 return y.isDirectory ? 1 : -1; 413 return x.name.compareTo(y.name); 414} 415 416WebInspector.FileSystemModel.Entry.prototype = { 417 /** 418 * @type {!WebInspector.FileSystemModel} 419 */ 420 get fileSystemModel() 421 { 422 return this._fileSystemModel; 423 }, 424 425 /** 426 * @type {!WebInspector.FileSystemModel.FileSystem} 427 */ 428 get fileSystem() 429 { 430 return this._fileSystem; 431 }, 432 433 /** 434 * @type {string} 435 */ 436 get url() 437 { 438 return this._url; 439 }, 440 441 /** 442 * @type {string} 443 */ 444 get name() 445 { 446 return this._name; 447 }, 448 449 /** 450 * @type {boolean} 451 */ 452 get isDirectory() 453 { 454 return this._isDirectory; 455 }, 456 457 /** 458 * @param {function(number, !FileSystemAgent.Metadata)} callback 459 */ 460 requestMetadata: function(callback) 461 { 462 this.fileSystemModel.requestMetadata(this, callback); 463 }, 464 465 /** 466 * @param {function(number)} callback 467 */ 468 deleteEntry: function(callback) 469 { 470 this.fileSystemModel.deleteEntry(this, callback); 471 } 472} 473 474/** 475 * @constructor 476 * @extends {WebInspector.FileSystemModel.Entry} 477 * @param {!WebInspector.FileSystemModel} fileSystemModel 478 * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem 479 * @param {!FileSystemAgent.Entry} backendEntry 480 */ 481WebInspector.FileSystemModel.Directory = function(fileSystemModel, fileSystem, backendEntry) 482{ 483 WebInspector.FileSystemModel.Entry.call(this, fileSystemModel, fileSystem, backendEntry); 484} 485 486WebInspector.FileSystemModel.Directory.prototype = { 487 /** 488 * @param {function(number, !Array.<!WebInspector.FileSystemModel.Directory>)} callback 489 */ 490 requestDirectoryContent: function(callback) 491 { 492 this.fileSystemModel.requestDirectoryContent(this, callback); 493 }, 494 495 __proto__: WebInspector.FileSystemModel.Entry.prototype 496} 497 498/** 499 * @constructor 500 * @extends {WebInspector.FileSystemModel.Entry} 501 * @param {!WebInspector.FileSystemModel} fileSystemModel 502 * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem 503 * @param {!FileSystemAgent.Entry} backendEntry 504 */ 505WebInspector.FileSystemModel.File = function(fileSystemModel, fileSystem, backendEntry) 506{ 507 WebInspector.FileSystemModel.Entry.call(this, fileSystemModel, fileSystem, backendEntry); 508 509 this._mimeType = backendEntry.mimeType; 510 this._resourceType = WebInspector.resourceTypes[backendEntry.resourceType]; 511 this._isTextFile = backendEntry.isTextFile; 512} 513 514WebInspector.FileSystemModel.File.prototype = { 515 /** 516 * @type {string} 517 */ 518 get mimeType() 519 { 520 return this._mimeType; 521 }, 522 523 /** 524 * @type {!WebInspector.ResourceType} 525 */ 526 get resourceType() 527 { 528 return this._resourceType; 529 }, 530 531 /** 532 * @type {boolean} 533 */ 534 get isTextFile() 535 { 536 return this._isTextFile; 537 }, 538 539 /** 540 * @param {boolean} readAsText 541 * @param {number=} start 542 * @param {number=} end 543 * @param {string=} charset 544 * @param {function(number, string=)=} callback 545 */ 546 requestFileContent: function(readAsText, start, end, charset, callback) 547 { 548 this.fileSystemModel.requestFileContent(this, readAsText, start, end, charset, callback); 549 }, 550 551 __proto__: WebInspector.FileSystemModel.Entry.prototype 552} 553