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