Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 'use strict'; | 5 'use strict'; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Scanner of the entries. | |
| 9 * @constructor | |
| 10 */ | |
| 11 function ContentScanner() { | |
| 12 this.cancelled_ = false; | |
| 13 } | |
| 14 | |
| 15 /** | |
| 16 * Starts to scan the entries. For example, starts to read the entries in a | |
| 17 * directory, or starts to search with some query on a file system. | |
| 18 * Derived classes must override this method. | |
| 19 * | |
| 20 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 21 * entries are read. This can be called a couple of times until the | |
| 22 * completion. | |
| 23 * @param {function()} successCallback Called when the scan is completed | |
| 24 * successfully. | |
| 25 * @param {function(FileError)} errorCallback Called an error occurs. | |
| 26 */ | |
| 27 ContentScanner.prototype.scan = function( | |
| 28 entriesCallback, successCallback, errorCallback) { | |
| 29 }; | |
| 30 | |
| 31 /** | |
| 32 * Request cancelling of the running scan. When the cancelling is done, | |
| 33 * an error will be reported from errorCallback passed to scan(). | |
| 34 */ | |
| 35 ContentScanner.prototype.cancel = function() { | |
| 36 this.cancelled_ = true; | |
| 37 }; | |
| 38 | |
| 39 /** | |
| 40 * Scanner of the entries in a directory. | |
| 41 * @param {DirectoryEntry} entry The directory to be read. | |
| 42 * @constructor | |
| 43 * @extends {ContentScanner} | |
| 44 */ | |
| 45 function DirectoryContentScanner(entry) { | |
| 46 ContentScanner.call(this); | |
| 47 this.entry_ = entry; | |
| 48 } | |
| 49 | |
| 50 /** | |
| 51 * Extends ContentScanner. | |
| 52 */ | |
| 53 DirectoryContentScanner.prototype.__proto__ = ContentScanner.prototype; | |
| 54 | |
| 55 /** | |
| 56 * Starts to read the entries in the directory. | |
| 57 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 58 * entries are read. This can be called a couple of times until the | |
| 59 * completion. | |
| 60 * @param {function()} successCallback Called when the scan is completed | |
| 61 * successfully. | |
| 62 * @param {function(FileError)} errorCallback Called an error occurs. | |
| 63 * @override | |
| 64 */ | |
| 65 DirectoryContentScanner.prototype.scan = function( | |
| 66 entriesCallback, successCallback, errorCallback) { | |
| 67 if (!this.entry_ || this.entry_ === DirectoryModel.fakeDriveEntry_) { | |
| 68 // If entry is not specified or a fake, we cannot read it. | |
| 69 errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR)); | |
| 70 return; | |
| 71 } | |
| 72 | |
| 73 metrics.startInterval('DirectoryScan'); | |
| 74 var reader = this.entry_.createReader(); | |
| 75 var readEntries = function() { | |
| 76 reader.readEntries( | |
| 77 function(entries) { | |
| 78 if (this.cancelled_) { | |
| 79 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 80 return; | |
| 81 } | |
| 82 | |
| 83 if (entries.length === 0) { | |
| 84 // All entries are read. | |
| 85 metrics.recordInterval('DirectoryScan'); | |
| 86 successCallback(); | |
| 87 return; | |
| 88 } | |
| 89 | |
| 90 entriesCallback(entries); | |
| 91 readEntries(); | |
| 92 }.bind(this), | |
| 93 errorCallback); | |
| 94 }.bind(this); | |
| 95 readEntries(); | |
| 96 }; | |
| 97 | |
| 98 /** | |
| 99 * Scanner of the entries for the search results on Drive File System. | |
| 100 * @param {string} query The query string. | |
| 101 * @constructor | |
| 102 * @extends {ContentScanner} | |
| 103 */ | |
| 104 function DriveSearchContentScanner(query) { | |
| 105 ContentScanner.call(this); | |
| 106 this.query_ = query; | |
| 107 } | |
| 108 | |
| 109 /** | |
| 110 * Extends ContentScanner. | |
| 111 */ | |
| 112 DriveSearchContentScanner.prototype.__proto__ = ContentScanner.prototype; | |
| 113 | |
| 114 /** | |
| 115 * Delay in milliseconds to be used for drive search scan, in order to reduce | |
| 116 * the number of server requests while user is typing the query. | |
| 117 * @type {number} | |
| 118 * @private | |
| 119 * @const | |
| 120 */ | |
| 121 DriveSearchContentScanner.SCAN_DELAY_ = 200; | |
| 122 | |
| 123 /** | |
| 124 * Maximum number of results which is shown on the search. | |
| 125 * @type {number} | |
| 126 * @private | |
| 127 * @const | |
| 128 */ | |
| 129 DriveSearchContentScanner.MAX_RESULTS_ = 100; | |
| 130 | |
| 131 /** | |
| 132 * Starts to search on Drive File System. | |
| 133 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 134 * entries are read. This can be called a couple of times until the | |
| 135 * completion. | |
| 136 * @param {function()} successCallback Called when the scan is completed | |
| 137 * successfully. | |
| 138 * @param {function(FileError)} errorCallback Called an error occurs. | |
| 139 * @override | |
| 140 */ | |
| 141 DriveSearchContentScanner.prototype.scan = function( | |
| 142 entriesCallback, successCallback, errorCallback) { | |
| 143 var numReadEntries = 0; | |
| 144 var readEntries = function(nextFeed) { | |
| 145 chrome.fileBrowserPrivate.searchDrive( | |
| 146 {query: this.query_, nextFeed: nextFeed}, | |
| 147 function(entries, nextFeed) { | |
| 148 if (this.cancelled_) { | |
| 149 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 150 return; | |
| 151 } | |
| 152 | |
| 153 // TODO(tbarzic): Improve error handling. | |
| 154 if (!entries) { | |
| 155 console.error('Drive search encountered an error.'); | |
| 156 errorCallback(util.createFileError( | |
| 157 FileError.INVALID_MODIFICATION_ERR)); | |
| 158 return; | |
| 159 } | |
| 160 | |
| 161 var numRemainingEntries = | |
| 162 DriveSearchContentScanner.MAX_RESULTS_ - numReadEntries; | |
| 163 if (entries.length >= numRemainingEntries) { | |
| 164 // The limit is hit, so quit the scan here. | |
| 165 entries = entries.slice(0, numRemainingEntries); | |
| 166 nextFeed = ''; | |
| 167 } | |
| 168 | |
| 169 numReadEntries += entries.length; | |
| 170 if (entries.length > 0) | |
| 171 entriesCallback(entries); | |
| 172 | |
| 173 if (nextFeed === '') | |
| 174 successCallback(); | |
| 175 else | |
| 176 readEntries(nextFeed); | |
| 177 }.bind(this)); | |
| 178 }.bind(this); | |
| 179 | |
| 180 // Let's give another search a chance to cancel us before we begin. | |
| 181 setTimeout( | |
| 182 function() { | |
| 183 // Check cancelled state before read the entries. | |
| 184 if (this.cancelled_) { | |
| 185 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 186 return; | |
| 187 } | |
| 188 readEntries(''); | |
| 189 }.bind(this), | |
| 190 DriveSearchContentScanner.SCAN_DELAY_); | |
| 191 }; | |
| 192 | |
| 193 /** | |
| 194 * Scanner of the entries of the file name search on the directory tree, whose | |
| 195 * root is entry. | |
| 196 * @param {DirectoryEntry} entry The root of the search target directory tree. | |
| 197 * @param {string} query The query of the search. | |
| 198 * @constructor | |
| 199 * @extends {ContentScanner} | |
| 200 */ | |
| 201 function LocalSearchContentScanner(entry, query) { | |
| 202 ContentScanner.call(this); | |
| 203 this.entry_ = entry; | |
| 204 this.query_ = query.toLowerCase(); | |
| 205 } | |
| 206 | |
| 207 /** | |
| 208 * Extedns ContentScanner. | |
| 209 */ | |
| 210 LocalSearchContentScanner.prototype.__proto__ = ContentScanner.prototype; | |
| 211 | |
| 212 /** | |
| 213 * Starts the file name search. | |
| 214 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 215 * entries are read. This can be called a couple of times until the | |
| 216 * completion. | |
| 217 * @param {function()} successCallback Called when the scan is completed | |
| 218 * successfully. | |
| 219 * @param {function(FileError)} errorCallback Called an error occurs. | |
| 220 * @override | |
| 221 */ | |
| 222 LocalSearchContentScanner.prototype.scan = function( | |
| 223 entriesCallback, successCallback, errorCallback) { | |
| 224 var numRunningTasks = 0; | |
| 225 var error = null; | |
| 226 var maybeRunCallback = function() { | |
| 227 if (numRunningTasks === 0) { | |
| 228 if (this.cancelled_) | |
| 229 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 230 else if (error) | |
| 231 errorCallback(error); | |
| 232 else | |
| 233 successCallback(); | |
| 234 } | |
| 235 }.bind(this); | |
| 236 | |
| 237 var processEntry = function(entry) { | |
| 238 numRunningTasks++; | |
| 239 var onError = function(fileError) { | |
| 240 if (!error) | |
| 241 error = fileError; | |
| 242 numRunningTasks--; | |
| 243 maybeRunCallback(); | |
| 244 }; | |
| 245 | |
| 246 var onSuccess = function(entries) { | |
| 247 if (this.cancelled_ || error || entries.length === 0) { | |
| 248 numRunningTasks--; | |
| 249 maybeRunCallback(); | |
| 250 return; | |
| 251 } | |
| 252 | |
| 253 // Filters by the query, and if found, run entriesCallback. | |
| 254 var foundEntries = entries.filter(function(entry) { | |
| 255 return entry.name.toLowerCase().indexOf(this.query_) >= 0; | |
| 256 }.bind(this)); | |
| 257 if (foundEntries.length > 0) | |
| 258 entriesCallback(foundEntries); | |
| 259 | |
| 260 // Start to process sub directories. | |
| 261 for (var i = 0; i < entries.length; i++) { | |
| 262 if (entries[i].isDirectory) | |
| 263 processEntry(entries[i]); | |
| 264 } | |
| 265 | |
| 266 // Read remaining entries. | |
| 267 reader.readEntries(onSuccess, onError); | |
| 268 }.bind(this); | |
| 269 | |
| 270 var reader = entry.createReader(); | |
| 271 reader.readEntries(onSuccess, onError); | |
| 272 }.bind(this); | |
| 273 | |
| 274 processEntry(this.entry_); | |
| 275 }; | |
| 276 | |
| 277 /** | |
| 278 * Scanner of the entries for the metadata seaerch on Drive File System. | |
| 279 * @param {string} query The query of the search. | |
| 280 * @param {DriveMetadataSearchContentScanner.SearchType} searchType The option | |
| 281 * of the search. | |
| 282 * @constructor | |
| 283 * @extends {ContentScanner} | |
| 284 */ | |
| 285 function DriveMetadataSearchContentScanner(query, searchType) { | |
| 286 ContentScanner.call(this); | |
| 287 this.query_ = query; | |
| 288 this.searchType_ = searchType; | |
| 289 } | |
| 290 | |
| 291 /** | |
| 292 * Extends ContentScanner. | |
| 293 */ | |
| 294 DriveMetadataSearchContentScanner.prototype.__proto__ = | |
| 295 ContentScanner.prototype; | |
| 296 | |
| 297 /** | |
| 298 * The search types on the Drive File System. | |
| 299 * @enum {string} | |
| 300 */ | |
| 301 DriveMetadataSearchContentScanner.SearchType = Object.freeze({ | |
| 302 SEARCH_ALL: 'ALL', | |
| 303 SEARCH_SHARED_WITH_ME: 'SHARED_WITH_ME', | |
| 304 SEARCH_RECENT_FILES: 'EXCLUDE_DIRECTORIES', | |
| 305 SEARCH_OFFLINE: 'OFFLINE' | |
| 306 }); | |
| 307 | |
| 308 /** | |
| 309 * Starts to metadata-search on Drive File System. | |
| 310 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 311 * entries are read. This can be called a couple of times until the | |
| 312 * completion. | |
| 313 * @param {function()} successCallback Called when the scan is completed | |
| 314 * successfully. | |
| 315 * @param {function(FileError)} errorCallback Called an error occurs. | |
| 316 * @override | |
| 317 */ | |
| 318 DriveMetadataSearchContentScanner.prototype.scan = function( | |
| 319 entriesCallback, successCallback, errorCallback) { | |
| 320 chrome.fileBrowserPrivate.searchDriveMetadata( | |
| 321 {query: this.query_, types: this.searchType_, maxResults: 500}, | |
| 322 function(results) { | |
| 323 if (this.cancelled_) { | |
| 324 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 325 return; | |
| 326 } | |
| 327 | |
| 328 if (!results) { | |
| 329 console.error('Drive search encountered an error.'); | |
| 330 errorCallback(util.createFileError( | |
| 331 FileError.INVALID_MODIFICATION_ERR)); | |
| 332 return; | |
| 333 } | |
| 334 | |
| 335 var entries = results.map(function(result) { return result.entry; }); | |
| 336 if (entries.length > 0) | |
| 337 entriesCallback(entries); | |
| 338 successCallback(); | |
| 339 }.bind(this)); | |
| 340 }; | |
| 341 | |
| 342 /** | |
| 8 * This class manages filters and determines a file should be shown or not. | 343 * This class manages filters and determines a file should be shown or not. |
| 9 * When filters are changed, a 'changed' event is fired. | 344 * When filters are changed, a 'changed' event is fired. |
| 10 * | 345 * |
| 11 * @param {MetadataCache} metadataCache Metadata cache service. | 346 * @param {MetadataCache} metadataCache Metadata cache service. |
| 12 * @param {boolean} showHidden If files starting with '.' are shown. | 347 * @param {boolean} showHidden If files starting with '.' are shown. |
| 13 * @constructor | 348 * @constructor |
| 14 * @extends {cr.EventTarget} | 349 * @extends {cr.EventTarget} |
| 15 */ | 350 */ |
| 16 function FileFilter(metadataCache, showHidden) { | 351 function FileFilter(metadataCache, showHidden) { |
| 17 /** | 352 /** |
| 18 * @type {MetadataCache} | 353 * @type {MetadataCache} |
| 19 * @private | 354 * @private |
| 20 */ | 355 */ |
| 21 this.metadataCache_ = metadataCache; | 356 this.metadataCache_ = metadataCache; |
| 357 | |
| 22 /** | 358 /** |
| 23 * @type Object.<string, Function> | 359 * @type Object.<string, Function> |
| 24 * @private | 360 * @private |
| 25 */ | 361 */ |
| 26 this.filters_ = {}; | 362 this.filters_ = {}; |
| 27 this.setFilterHidden(!showHidden); | 363 this.setFilterHidden(!showHidden); |
| 28 | 364 |
| 29 // Do not show entries marked as 'deleted'. | 365 // Do not show entries marked as 'deleted'. |
| 30 this.addFilter('deleted', function(entry) { | 366 this.addFilter('deleted', function(entry) { |
| 31 var internal = this.metadataCache_.getCached(entry, 'internal'); | 367 var internal = this.metadataCache_.getCached(entry, 'internal'); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 95 * | 431 * |
| 96 * @param {FileFilter} fileFilter The file-filter context. | 432 * @param {FileFilter} fileFilter The file-filter context. |
| 97 * @param {MetadataCache} metadataCache Metadata cache service. | 433 * @param {MetadataCache} metadataCache Metadata cache service. |
| 98 * @constructor | 434 * @constructor |
| 99 */ | 435 */ |
| 100 function FileListContext(fileFilter, metadataCache) { | 436 function FileListContext(fileFilter, metadataCache) { |
| 101 /** | 437 /** |
| 102 * @type {cr.ui.ArrayDataModel} | 438 * @type {cr.ui.ArrayDataModel} |
| 103 */ | 439 */ |
| 104 this.fileList = new cr.ui.ArrayDataModel([]); | 440 this.fileList = new cr.ui.ArrayDataModel([]); |
| 441 | |
| 105 /** | 442 /** |
| 106 * @type {MetadataCache} | 443 * @type {MetadataCache} |
| 107 */ | 444 */ |
| 108 this.metadataCache = metadataCache; | 445 this.metadataCache = metadataCache; |
| 109 | 446 |
| 110 /** | 447 /** |
| 111 * @type {FileFilter} | 448 * @type {FileFilter} |
| 112 */ | 449 */ |
| 113 this.fileFilter = fileFilter; | 450 this.fileFilter = fileFilter; |
| 114 } | 451 } |
| 115 | 452 |
| 116 /** | 453 /** |
| 117 * This class is responsible for scanning directory (or search results), | 454 * This class is responsible for scanning directory (or search results), |
| 118 * and filling the fileList. Different descendants handle various types of | 455 * and filling the fileList. Different descendants handle various types of |
| 119 * directory contents shown: basic directory, drive search results, local search | 456 * directory contents shown: basic directory, drive search results, local search |
| 120 * results. | 457 * results. |
| 121 * @param {FileListContext} context The file list context. | 458 * @param {FileListContext} context The file list context. |
| 122 * @constructor | 459 * @constructor |
| 123 * @extends {cr.EventTarget} | 460 * @extends {cr.EventTarget} |
| 124 */ | 461 */ |
| 125 function DirectoryContents(context) { | 462 function DirectoryContents(context) { |
| 126 this.context_ = context; | 463 this.context_ = context; |
| 127 this.fileList_ = context.fileList; | 464 this.fileList_ = context.fileList; |
| 128 this.scanCompletedCallback_ = null; | 465 this.prefetchMetadataQueue_ = new AsyncUtil.Queue(); |
| 129 this.scanFailedCallback_ = null; | |
| 130 this.scanCancelled_ = false; | 466 this.scanCancelled_ = false; |
| 131 this.allChunksFetched_ = false; | |
| 132 this.pendingMetadataRequests_ = 0; | |
| 133 this.fileList_.prepareSort = this.prepareSort_.bind(this); | 467 this.fileList_.prepareSort = this.prepareSort_.bind(this); |
| 134 } | 468 } |
| 135 | 469 |
| 136 /** | 470 /** |
| 137 * DirectoryContents extends cr.EventTarget. | 471 * DirectoryContents extends cr.EventTarget. |
| 138 */ | 472 */ |
| 139 DirectoryContents.prototype.__proto__ = cr.EventTarget.prototype; | 473 DirectoryContents.prototype.__proto__ = cr.EventTarget.prototype; |
| 140 | 474 |
| 141 /** | 475 /** |
| 142 * Create the copy of the object, but without scan started. | 476 * Create the copy of the object, but without scan started. |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 166 spliceArgs.unshift(0, fileList.length); | 500 spliceArgs.unshift(0, fileList.length); |
| 167 fileList.splice.apply(fileList, spliceArgs); | 501 fileList.splice.apply(fileList, spliceArgs); |
| 168 this.fileList_ = fileList; | 502 this.fileList_ = fileList; |
| 169 } | 503 } |
| 170 }; | 504 }; |
| 171 | 505 |
| 172 /** | 506 /** |
| 173 * @return {boolean} If the scan is active. | 507 * @return {boolean} If the scan is active. |
| 174 */ | 508 */ |
| 175 DirectoryContents.prototype.isScanning = function() { | 509 DirectoryContents.prototype.isScanning = function() { |
| 176 return !this.scanCancelled_ && | 510 return this.scanner_ || this.prefetchMetadataQueue_.isRunning(); |
| 177 (!this.allChunksFetched_ || this.pendingMetadataRequests_ > 0); | |
| 178 }; | 511 }; |
| 179 | 512 |
| 180 /** | 513 /** |
| 181 * @return {boolean} True if search results (drive or local). | 514 * @return {boolean} True if search results (drive or local). |
| 182 */ | 515 */ |
| 183 DirectoryContents.prototype.isSearch = function() { | 516 DirectoryContents.prototype.isSearch = function() { |
| 184 return false; | 517 return false; |
| 185 }; | 518 }; |
| 186 | 519 |
| 187 /** | 520 /** |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 201 | 534 |
| 202 /** | 535 /** |
| 203 * Start directory scan/search operation. Either 'scan-completed' or | 536 * Start directory scan/search operation. Either 'scan-completed' or |
| 204 * 'scan-failed' event will be fired upon completion. | 537 * 'scan-failed' event will be fired upon completion. |
| 205 */ | 538 */ |
| 206 DirectoryContents.prototype.scan = function() { | 539 DirectoryContents.prototype.scan = function() { |
| 207 throw 'Not implemented.'; | 540 throw 'Not implemented.'; |
| 208 }; | 541 }; |
| 209 | 542 |
| 210 /** | 543 /** |
| 211 * Read next chunk of results from DirectoryReader. | 544 * Cancels the running scan. |
| 212 * @protected | |
| 213 */ | |
| 214 DirectoryContents.prototype.readNextChunk = function() { | |
| 215 throw 'Not implemented.'; | |
| 216 }; | |
| 217 | |
| 218 /** | |
| 219 * Cancel the running scan. | |
| 220 */ | 545 */ |
| 221 DirectoryContents.prototype.cancelScan = function() { | 546 DirectoryContents.prototype.cancelScan = function() { |
| 222 if (this.scanCancelled_) | 547 if (this.scanCancelled_) |
| 223 return; | 548 return; |
| 224 this.scanCancelled_ = true; | 549 this.scanCancelled_ = true; |
| 550 if (this.scanner_) | |
| 551 this.scanner_.cancel(); | |
| 552 | |
| 553 this.prefetchMetadataQueue_.cancel(); | |
| 225 cr.dispatchSimpleEvent(this, 'scan-cancelled'); | 554 cr.dispatchSimpleEvent(this, 'scan-cancelled'); |
| 226 }; | 555 }; |
| 227 | 556 |
| 557 /** | |
| 558 * Called when the scanning by scanner_ is done. | |
| 559 * @protected | |
| 560 */ | |
| 561 DirectoryContents.prototype.onScanCompleted = function() { | |
| 562 this.scanner_ = null; | |
| 563 if (this.scanCancelled_) | |
| 564 return; | |
| 565 | |
| 566 this.prefetchMetadataQueue_.run(function(callback) { | |
| 567 cr.dispatchSimpleEvent(this, 'scan-completed'); | |
| 568 if (!this.isSearch() && | |
| 569 this.getDirectoryEntry().fullPath === RootDirectory.DOWNLOADS) | |
| 570 metrics.recordMediumCount('DownloadsCount', this.fileList_.length); | |
| 571 callback(); | |
| 572 }.bind(this)); | |
| 573 }; | |
| 228 | 574 |
| 229 /** | 575 /** |
| 230 * Called in case scan has failed. Should send the event. | 576 * Called in case scan has failed. Should send the event. |
| 231 * @protected | 577 * @protected |
| 232 */ | 578 */ |
| 233 DirectoryContents.prototype.onError = function() { | 579 DirectoryContents.prototype.onScanError = function() { |
| 234 cr.dispatchSimpleEvent(this, 'scan-failed'); | 580 this.scanner_ = null; |
| 581 if (this.scanCancelled_) | |
| 582 return; | |
| 583 | |
| 584 this.prefetchMetadataQueue_.run(function(callback) { | |
| 585 cr.dispatchSimpleEvent(this, 'scan-failed'); | |
| 586 callback(); | |
| 587 }.bind(this)); | |
| 235 }; | 588 }; |
| 236 | 589 |
| 237 /** | 590 /** |
| 238 * Called in case scan has completed succesfully. Should send the event. | 591 * Called when some chunk of entries are read by scanner. |
| 592 * @param {Array.<Entry>} entries The list of the scanned entries. | |
| 239 * @protected | 593 * @protected |
| 240 */ | 594 */ |
| 241 DirectoryContents.prototype.lastChunkReceived = function() { | 595 DirectoryContents.prototype.onNewEntries = function(entries) { |
| 242 this.allChunksFetched_ = true; | 596 if (this.scanCancelled_) |
| 243 if (!this.scanCancelled_ && this.pendingMetadataRequests_ === 0) | 597 return; |
| 244 cr.dispatchSimpleEvent(this, 'scan-completed'); | 598 |
| 599 var entriesFiltered = [].filter.call( | |
| 600 entries, this.context_.fileFilter.filter.bind(this.context_.fileFilter)); | |
| 601 | |
| 602 // Because the prefetchMetadata can be slow, throttling by splitting entries | |
| 603 // into smaller chunks to reduce UI latency. | |
| 604 // TODO(hidehiko,mtomasz): This should be handled in MetadataCache. | |
| 605 var MAX_CHUNK_SIZE = 50; | |
| 606 for (var i = 0; i < entriesFiltered.length; i += MAX_CHUNK_SIZE) { | |
| 607 var chunk = entriesFiltered.slice(i, i + MAX_CHUNK_SIZE); | |
| 608 this.prefetchMetadataQueue_.run(function(chunk, callback) { | |
| 609 this.prefetchMetadata(chunk, function() { | |
| 610 if (this.scanCancelled_) { | |
| 611 // Do nothing if the scanning is cancelled. | |
| 612 callback(); | |
| 613 return; | |
| 614 } | |
| 615 | |
| 616 this.fileList_.push.apply(this.fileList_, chunk); | |
| 617 cr.dispatchSimpleEvent(this, 'scan-updated'); | |
| 618 callback(); | |
| 619 }.bind(this)); | |
| 620 }.bind(this, chunk)); | |
| 621 } | |
| 245 }; | 622 }; |
| 246 | 623 |
| 247 /** | 624 /** |
| 248 * Cache necessary data before a sort happens. | 625 * Cache necessary data before a sort happens. |
| 249 * | 626 * |
| 250 * This is called by the table code before a sort happens, so that we can | 627 * This is called by the table code before a sort happens, so that we can |
| 251 * go fetch data for the sort field that we may not have yet. | 628 * go fetch data for the sort field that we may not have yet. |
| 252 * @param {string} field Sort field. | 629 * @param {string} field Sort field. |
| 253 * @param {function(Object)} callback Called when done. | 630 * @param {function(Object)} callback Called when done. |
| 254 * @private | 631 * @private |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 268 /** | 645 /** |
| 269 * @param {Array.<Entry>} entries Files. | 646 * @param {Array.<Entry>} entries Files. |
| 270 * @param {function(Object)} callback Callback on done. | 647 * @param {function(Object)} callback Callback on done. |
| 271 */ | 648 */ |
| 272 DirectoryContents.prototype.reloadMetadata = function(entries, callback) { | 649 DirectoryContents.prototype.reloadMetadata = function(entries, callback) { |
| 273 this.context_.metadataCache.clear(entries, '*'); | 650 this.context_.metadataCache.clear(entries, '*'); |
| 274 this.context_.metadataCache.get(entries, 'filesystem', callback); | 651 this.context_.metadataCache.get(entries, 'filesystem', callback); |
| 275 }; | 652 }; |
| 276 | 653 |
| 277 /** | 654 /** |
| 278 * @protected | |
| 279 * @param {Array.<Entry>} entries File list. | |
| 280 */ | |
| 281 DirectoryContents.prototype.onNewEntries = function(entries) { | |
| 282 if (this.scanCancelled_) | |
| 283 return; | |
| 284 | |
| 285 var entriesFiltered = [].filter.call( | |
| 286 entries, this.context_.fileFilter.filter.bind(this.context_.fileFilter)); | |
| 287 | |
| 288 var onPrefetched = function() { | |
| 289 this.pendingMetadataRequests_--; | |
| 290 if (this.scanCancelled_) | |
| 291 return; | |
| 292 this.fileList_.push.apply(this.fileList_, entriesFiltered); | |
| 293 | |
| 294 if (this.pendingMetadataRequests_ === 0 && this.allChunksFetched_) | |
| 295 cr.dispatchSimpleEvent(this, 'scan-completed'); | |
| 296 else | |
| 297 cr.dispatchSimpleEvent(this, 'scan-updated'); | |
| 298 | |
| 299 if (!this.allChunksFetched_) | |
| 300 this.readNextChunk(); | |
| 301 }; | |
| 302 | |
| 303 this.pendingMetadataRequests_++; | |
| 304 this.prefetchMetadata(entriesFiltered, onPrefetched.bind(this)); | |
| 305 }; | |
| 306 | |
| 307 /** | |
| 308 * @param {string} name Directory name. | 655 * @param {string} name Directory name. |
| 309 * @param {function(DirectoryEntry)} successCallback Called on success. | 656 * @param {function(DirectoryEntry)} successCallback Called on success. |
| 310 * @param {function(FileError)} errorCallback On error. | 657 * @param {function(FileError)} errorCallback On error. |
| 311 */ | 658 */ |
| 312 DirectoryContents.prototype.createDirectory = function( | 659 DirectoryContents.prototype.createDirectory = function( |
| 313 name, successCallback, errorCallback) { | 660 name, successCallback, errorCallback) { |
| 314 throw 'Not implemented.'; | 661 throw 'Not implemented.'; |
| 315 }; | 662 }; |
| 316 | 663 |
| 317 | 664 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 * @return {DirectoryEntry} DirectoryEntry for the currnet entry. | 697 * @return {DirectoryEntry} DirectoryEntry for the currnet entry. |
| 351 */ | 698 */ |
| 352 DirectoryContentsBasic.prototype.getLastNonSearchDirectoryEntry = function() { | 699 DirectoryContentsBasic.prototype.getLastNonSearchDirectoryEntry = function() { |
| 353 return this.entry_; | 700 return this.entry_; |
| 354 }; | 701 }; |
| 355 | 702 |
| 356 /** | 703 /** |
| 357 * Start directory scan. | 704 * Start directory scan. |
| 358 */ | 705 */ |
| 359 DirectoryContentsBasic.prototype.scan = function() { | 706 DirectoryContentsBasic.prototype.scan = function() { |
| 360 if (!this.entry_ || this.entry_ === DirectoryModel.fakeDriveEntry_) { | 707 // TODO: this scan method must be called at most once. Remove such a |
|
mtomasz
2013/10/01 05:39:38
nit: We use syntax: TODO(login): Sentence from upp
hidehiko
2013/10/01 05:54:03
Thank you for your offer. Added your and my names.
| |
| 361 this.lastChunkReceived(); | 708 // limitation. |
| 362 return; | 709 this.scanner_ = new DirectoryContentScanner(this.entry_); |
| 363 } | 710 this.scanner_.scan(this.onNewEntries.bind(this), |
| 364 | 711 this.onScanCompleted.bind(this), |
| 365 metrics.startInterval('DirectoryScan'); | 712 this.onScanError.bind(this)); |
| 366 this.reader_ = this.entry_.createReader(); | |
| 367 this.readNextChunk(); | |
| 368 }; | 713 }; |
| 369 | 714 |
| 370 /** | 715 /** |
| 371 * Read next chunk of results from DirectoryReader. | |
| 372 * @protected | |
| 373 */ | |
| 374 DirectoryContentsBasic.prototype.readNextChunk = function() { | |
| 375 this.reader_.readEntries(this.onChunkComplete_.bind(this), | |
| 376 this.onError.bind(this)); | |
| 377 }; | |
| 378 | |
| 379 /** | |
| 380 * @param {Array.<Entry>} entries File list. | |
| 381 * @private | |
| 382 */ | |
| 383 DirectoryContentsBasic.prototype.onChunkComplete_ = function(entries) { | |
| 384 if (this.scanCancelled_) | |
| 385 return; | |
| 386 | |
| 387 if (entries.length == 0) { | |
| 388 this.lastChunkReceived(); | |
| 389 this.recordMetrics_(); | |
| 390 return; | |
| 391 } | |
| 392 | |
| 393 this.onNewEntries(entries); | |
| 394 }; | |
| 395 | |
| 396 /** | |
| 397 * @private | |
| 398 */ | |
| 399 DirectoryContentsBasic.prototype.recordMetrics_ = function() { | |
| 400 metrics.recordInterval('DirectoryScan'); | |
| 401 if (this.entry_.fullPath === RootDirectory.DOWNLOADS) | |
| 402 metrics.recordMediumCount('DownloadsCount', this.fileList_.length); | |
| 403 }; | |
| 404 | |
| 405 /** | |
| 406 * @param {string} name Directory name. | 716 * @param {string} name Directory name. |
| 407 * @param {function(Entry)} successCallback Called on success. | 717 * @param {function(Entry)} successCallback Called on success. |
| 408 * @param {function(FileError)} errorCallback On error. | 718 * @param {function(FileError)} errorCallback On error. |
| 409 */ | 719 */ |
| 410 DirectoryContentsBasic.prototype.createDirectory = function( | 720 DirectoryContentsBasic.prototype.createDirectory = function( |
| 411 name, successCallback, errorCallback) { | 721 name, successCallback, errorCallback) { |
| 722 // TODO(hidehiko): createDirectory should not be the part of | |
| 723 // DirectoryContent. | |
| 412 if (!this.entry_) { | 724 if (!this.entry_) { |
| 413 errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR)); | 725 errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR)); |
| 414 return; | 726 return; |
| 415 } | 727 } |
| 416 | 728 |
| 417 var onSuccess = function(newEntry) { | 729 var onSuccess = function(newEntry) { |
| 418 this.reloadMetadata([newEntry], function() { | 730 this.reloadMetadata([newEntry], function() { |
| 419 successCallback(newEntry); | 731 successCallback(newEntry); |
| 420 }); | 732 }); |
| 421 }; | 733 }; |
| 422 | 734 |
| 423 this.entry_.getDirectory(name, {create: true, exclusive: true}, | 735 this.entry_.getDirectory(name, {create: true, exclusive: true}, |
| 424 onSuccess.bind(this), errorCallback); | 736 onSuccess.bind(this), errorCallback); |
| 425 }; | 737 }; |
| 426 | 738 |
| 427 /** | 739 /** |
| 428 * Delay to be used for drive search scan. | |
| 429 * The goal is to reduce the number of server requests when user is typing the | |
| 430 * query. | |
| 431 * | |
| 432 * @type {number} | |
| 433 * @const | |
| 434 */ | |
| 435 DirectoryContentsDriveSearch.SCAN_DELAY = 200; | |
| 436 | |
| 437 /** | |
| 438 * Maximum number of results which is shown on the search. | |
| 439 * | |
| 440 * @type {number} | |
| 441 * @const | |
| 442 */ | |
| 443 DirectoryContentsDriveSearch.MAX_RESULTS = 100; | |
| 444 | |
| 445 /** | |
| 446 * @param {FileListContext} context File list context. | 740 * @param {FileListContext} context File list context. |
| 447 * @param {DirectoryEntry} dirEntry Current directory. | 741 * @param {DirectoryEntry} dirEntry Current directory. |
| 448 * @param {DirectoryEntry} previousDirEntry DirectoryEntry that was current | 742 * @param {DirectoryEntry} previousDirEntry DirectoryEntry that was current |
| 449 * before the search. | 743 * before the search. |
| 450 * @param {string} query Search query. | 744 * @param {string} query Search query. |
| 451 * @constructor | 745 * @constructor |
| 452 * @extends {DirectoryContents} | 746 * @extends {DirectoryContents} |
| 453 */ | 747 */ |
| 454 function DirectoryContentsDriveSearch(context, | 748 function DirectoryContentsDriveSearch(context, |
| 455 dirEntry, | 749 dirEntry, |
| 456 previousDirEntry, | 750 previousDirEntry, |
| 457 query) { | 751 query) { |
| 458 DirectoryContents.call(this, context); | 752 DirectoryContents.call(this, context); |
| 459 this.directoryEntry_ = dirEntry; | 753 this.directoryEntry_ = dirEntry; |
| 460 this.previousDirectoryEntry_ = previousDirEntry; | 754 this.previousDirectoryEntry_ = previousDirEntry; |
| 461 this.query_ = query; | 755 this.query_ = query; |
| 462 this.nextFeed_ = ''; | |
| 463 this.done_ = false; | |
| 464 this.fetchedResultsNum_ = 0; | |
| 465 } | 756 } |
| 466 | 757 |
| 467 /** | 758 /** |
| 468 * Extends DirectoryContents. | 759 * Extends DirectoryContents. |
| 469 */ | 760 */ |
| 470 DirectoryContentsDriveSearch.prototype.__proto__ = DirectoryContents.prototype; | 761 DirectoryContentsDriveSearch.prototype.__proto__ = DirectoryContents.prototype; |
| 471 | 762 |
| 472 /** | 763 /** |
| 473 * Create the copy of the object, but without scan started. | 764 * Create the copy of the object, but without scan started. |
| 474 * @return {DirectoryContentsBasic} Object copy. | 765 * @return {DirectoryContentsBasic} Object copy. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 500 */ | 791 */ |
| 501 DirectoryContentsDriveSearch.prototype.getLastNonSearchDirectoryEntry = | 792 DirectoryContentsDriveSearch.prototype.getLastNonSearchDirectoryEntry = |
| 502 function() { | 793 function() { |
| 503 return this.previousDirectoryEntry_; | 794 return this.previousDirectoryEntry_; |
| 504 }; | 795 }; |
| 505 | 796 |
| 506 /** | 797 /** |
| 507 * Start directory scan. | 798 * Start directory scan. |
| 508 */ | 799 */ |
| 509 DirectoryContentsDriveSearch.prototype.scan = function() { | 800 DirectoryContentsDriveSearch.prototype.scan = function() { |
| 510 // Let's give another search a chance to cancel us before we begin. | 801 this.scanner_ = new DriveSearchContentScanner(this.query_); |
| 511 setTimeout(this.readNextChunk.bind(this), | 802 this.scanner_.scan(this.onNewEntries.bind(this), |
| 512 DirectoryContentsDriveSearch.SCAN_DELAY); | 803 this.onScanCompleted.bind(this), |
| 804 this.onScanError.bind(this)); | |
| 513 }; | 805 }; |
| 514 | 806 |
| 515 /** | 807 /** |
| 516 * All the results are read in one chunk, so when we try to read second chunk, | |
| 517 * it means we're done. | |
| 518 */ | |
| 519 DirectoryContentsDriveSearch.prototype.readNextChunk = function() { | |
| 520 if (this.scanCancelled_) | |
| 521 return; | |
| 522 | |
| 523 if (this.done_) { | |
| 524 this.lastChunkReceived(); | |
| 525 return; | |
| 526 } | |
| 527 | |
| 528 var searchCallback = (function(entries, nextFeed) { | |
| 529 // TODO(tbarzic): Improve error handling. | |
| 530 if (!entries) { | |
| 531 console.error('Drive search encountered an error.'); | |
| 532 this.lastChunkReceived(); | |
| 533 return; | |
| 534 } | |
| 535 this.nextFeed_ = nextFeed; | |
| 536 var remaining = | |
| 537 DirectoryContentsDriveSearch.MAX_RESULTS - this.fetchedResultsNum_; | |
| 538 if (entries.length >= remaining) { | |
| 539 entries = entries.slice(0, remaining); | |
| 540 this.nextFeed_ = ''; | |
| 541 } | |
| 542 this.fetchedResultsNum_ += entries.length; | |
| 543 | |
| 544 this.done_ = (this.nextFeed_ == ''); | |
| 545 | |
| 546 this.onNewEntries(entries); | |
| 547 }).bind(this); | |
| 548 | |
| 549 var searchParams = { | |
| 550 'query': this.query_, | |
| 551 'nextFeed': this.nextFeed_ | |
| 552 }; | |
| 553 chrome.fileBrowserPrivate.searchDrive(searchParams, searchCallback); | |
| 554 }; | |
| 555 | |
| 556 | |
| 557 /** | |
| 558 * @param {FileListContext} context File list context. | 808 * @param {FileListContext} context File list context. |
| 559 * @param {DirectoryEntry} dirEntry Current directory. | 809 * @param {DirectoryEntry} dirEntry Current directory. |
| 560 * @param {string} query Search query. | 810 * @param {string} query Search query. |
| 561 * @constructor | 811 * @constructor |
| 562 * @extends {DirectoryContents} | 812 * @extends {DirectoryContents} |
| 563 */ | 813 */ |
| 564 function DirectoryContentsLocalSearch(context, dirEntry, query) { | 814 function DirectoryContentsLocalSearch(context, dirEntry, query) { |
| 565 DirectoryContents.call(this, context); | 815 DirectoryContents.call(this, context); |
| 566 this.directoryEntry_ = dirEntry; | 816 this.directoryEntry_ = dirEntry; |
| 567 this.query_ = query.toLowerCase(); | 817 this.query_ = query; |
| 568 } | 818 } |
| 569 | 819 |
| 570 /** | 820 /** |
| 571 * Extends DirectoryContents | 821 * Extends DirectoryContents |
| 572 */ | 822 */ |
| 573 DirectoryContentsLocalSearch.prototype.__proto__ = DirectoryContents.prototype; | 823 DirectoryContentsLocalSearch.prototype.__proto__ = DirectoryContents.prototype; |
| 574 | 824 |
| 575 /** | 825 /** |
| 576 * Create the copy of the object, but without scan started. | 826 * Create the copy of the object, but without scan started. |
| 577 * @return {DirectoryContentsBasic} Object copy. | 827 * @return {DirectoryContentsBasic} Object copy. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 603 DirectoryContentsLocalSearch.prototype.getLastNonSearchDirectoryEntry = | 853 DirectoryContentsLocalSearch.prototype.getLastNonSearchDirectoryEntry = |
| 604 function() { | 854 function() { |
| 605 return this.directoryEntry_; | 855 return this.directoryEntry_; |
| 606 }; | 856 }; |
| 607 | 857 |
| 608 /** | 858 /** |
| 609 * Start directory scan/search operation. Either 'scan-completed' or | 859 * Start directory scan/search operation. Either 'scan-completed' or |
| 610 * 'scan-failed' event will be fired upon completion. | 860 * 'scan-failed' event will be fired upon completion. |
| 611 */ | 861 */ |
| 612 DirectoryContentsLocalSearch.prototype.scan = function() { | 862 DirectoryContentsLocalSearch.prototype.scan = function() { |
| 613 this.pendingScans_ = 0; | 863 this.scanner_ = |
| 614 this.scanDirectory_(this.directoryEntry_); | 864 new LocalSearchContentScanner(this.directoryEntry_, this.query_); |
| 865 this.scanner_.scan(this.onNewEntries.bind(this), | |
| 866 this.onScanCompleted.bind(this), | |
| 867 this.onScanError.bind(this)); | |
| 615 }; | 868 }; |
| 616 | 869 |
| 617 /** | 870 /** |
| 618 * Scan a directory. | |
| 619 * @param {DirectoryEntry} entry A directory to scan. | |
| 620 * @private | |
| 621 */ | |
| 622 DirectoryContentsLocalSearch.prototype.scanDirectory_ = function(entry) { | |
| 623 this.pendingScans_++; | |
| 624 var reader = entry.createReader(); | |
| 625 var found = []; | |
| 626 | |
| 627 var onChunkComplete = function(entries) { | |
| 628 if (this.scanCancelled_) | |
| 629 return; | |
| 630 | |
| 631 if (entries.length === 0) { | |
| 632 if (found.length > 0) | |
| 633 this.onNewEntries(found); | |
| 634 this.pendingScans_--; | |
| 635 if (this.pendingScans_ === 0) | |
| 636 this.lastChunkReceived(); | |
| 637 return; | |
| 638 } | |
| 639 | |
| 640 for (var i = 0; i < entries.length; i++) { | |
| 641 if (entries[i].name.toLowerCase().indexOf(this.query_) != -1) { | |
| 642 found.push(entries[i]); | |
| 643 } | |
| 644 | |
| 645 if (entries[i].isDirectory) | |
| 646 this.scanDirectory_(entries[i]); | |
| 647 } | |
| 648 | |
| 649 getNextChunk(); | |
| 650 }.bind(this); | |
| 651 | |
| 652 var getNextChunk = function() { | |
| 653 reader.readEntries(onChunkComplete, this.onError.bind(this)); | |
| 654 }.bind(this); | |
| 655 | |
| 656 getNextChunk(); | |
| 657 }; | |
| 658 | |
| 659 /** | |
| 660 * We get results for each directory in one go in scanDirectory_. | |
| 661 */ | |
| 662 DirectoryContentsLocalSearch.prototype.readNextChunk = function() { | |
| 663 }; | |
| 664 | |
| 665 /** | |
| 666 * List of search types for DirectoryContentsDriveSearch. | |
| 667 * TODO(haruki): SHARED_WITH_ME support for searchDriveMetadata is not yet | |
| 668 * implemented. Update this when it's done. | |
| 669 * SEARCH_ALL uses no filtering. | |
| 670 * SEARCH_SHARED_WITH_ME searches for the shared-with-me entries. | |
| 671 * SEARCH_RECENT_FILES searches for recently accessed file entries. | |
| 672 * @enum {number} | |
| 673 */ | |
| 674 DirectoryContentsDriveSearchMetadata.SearchType = { | |
| 675 SEARCH_ALL: 0, | |
| 676 SEARCH_SHARED_WITH_ME: 1, | |
| 677 SEARCH_RECENT_FILES: 2, | |
| 678 SEARCH_OFFLINE: 3 | |
| 679 }; | |
| 680 | |
| 681 /** | |
| 682 * DirectoryContents to list Drive files using searchDriveMetadata(). | 871 * DirectoryContents to list Drive files using searchDriveMetadata(). |
| 683 * | 872 * |
| 684 * @param {FileListContext} context File list context. | 873 * @param {FileListContext} context File list context. |
| 685 * @param {DirectoryEntry} driveDirEntry Directory for actual Drive. | 874 * @param {DirectoryEntry} driveDirEntry Directory for actual Drive. |
| 686 * @param {DirectoryEntry} fakeDirEntry Fake directory representing the set of | 875 * @param {DirectoryEntry} fakeDirEntry Fake directory representing the set of |
| 687 * result files. This serves as a top directory for this search. | 876 * result files. This serves as a top directory for this search. |
| 688 * @param {string} query Search query to filter the files. | 877 * @param {string} query Search query to filter the files. |
| 689 * @param {DirectoryContentsDriveSearchMetadata.SearchType} searchType | 878 * @param {DriveMetadataSearchContentScanner.SearchType} searchType |
| 690 * Type of search. searchDriveMetadata will restricts the entries based on | 879 * Type of search. searchDriveMetadata will restricts the entries based on |
| 691 * the given search type. | 880 * the given search type. |
| 692 * @constructor | 881 * @constructor |
| 693 * @extends {DirectoryContents} | 882 * @extends {DirectoryContents} |
| 694 */ | 883 */ |
| 695 function DirectoryContentsDriveSearchMetadata(context, | 884 function DirectoryContentsDriveSearchMetadata(context, |
| 696 driveDirEntry, | 885 driveDirEntry, |
| 697 fakeDirEntry, | 886 fakeDirEntry, |
| 698 query, | 887 query, |
| 699 searchType) { | 888 searchType) { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 742 DirectoryContentsDriveSearchMetadata.prototype.getLastNonSearchDirectoryEntry = | 931 DirectoryContentsDriveSearchMetadata.prototype.getLastNonSearchDirectoryEntry = |
| 743 function() { | 932 function() { |
| 744 return this.driveDirEntry_; | 933 return this.driveDirEntry_; |
| 745 }; | 934 }; |
| 746 | 935 |
| 747 /** | 936 /** |
| 748 * Start directory scan/search operation. Either 'scan-completed' or | 937 * Start directory scan/search operation. Either 'scan-completed' or |
| 749 * 'scan-failed' event will be fired upon completion. | 938 * 'scan-failed' event will be fired upon completion. |
| 750 */ | 939 */ |
| 751 DirectoryContentsDriveSearchMetadata.prototype.scan = function() { | 940 DirectoryContentsDriveSearchMetadata.prototype.scan = function() { |
| 752 this.readNextChunk(); | 941 this.scanner_ = |
| 942 new DriveMetadataSearchContentScanner(this.query_, this.searchType_); | |
| 943 this.scanner_.scan(this.onNewEntries.bind(this), | |
| 944 this.onScanCompleted.bind(this), | |
| 945 this.onScanError.bind(this)); | |
| 753 }; | 946 }; |
| 754 | |
| 755 /** | |
| 756 * All the results are read in one chunk, so when we try to read second chunk, | |
| 757 * it means we're done. | |
| 758 */ | |
| 759 DirectoryContentsDriveSearchMetadata.prototype.readNextChunk = function() { | |
| 760 if (this.scanCancelled_) | |
| 761 return; | |
| 762 | |
| 763 if (this.done_) { | |
| 764 this.lastChunkReceived(); | |
| 765 return; | |
| 766 } | |
| 767 | |
| 768 var searchCallback = (function(results, nextFeed) { | |
| 769 if (!results) { | |
| 770 console.error('Drive search encountered an error.'); | |
| 771 this.lastChunkReceived(); | |
| 772 return; | |
| 773 } | |
| 774 this.done_ = true; | |
| 775 | |
| 776 var entries = results.map(function(r) { return r.entry; }); | |
| 777 this.onNewEntries(entries); | |
| 778 this.lastChunkReceived(); | |
| 779 }).bind(this); | |
| 780 | |
| 781 var type; | |
| 782 switch (this.searchType_) { | |
| 783 case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_ALL: | |
| 784 type = 'ALL'; | |
| 785 break; | |
| 786 case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_SHARED_WITH_ME: | |
| 787 type = 'SHARED_WITH_ME'; | |
| 788 break; | |
| 789 case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_RECENT_FILES: | |
| 790 type = 'EXCLUDE_DIRECTORIES'; | |
| 791 break; | |
| 792 case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_OFFLINE: | |
| 793 type = 'OFFLINE'; | |
| 794 break; | |
| 795 default: | |
| 796 throw Error('Unknown search type: ' + this.searchType_); | |
| 797 } | |
| 798 var searchParams = { | |
| 799 'query': this.query_, | |
| 800 'types': type, | |
| 801 'maxResults': 500 | |
| 802 }; | |
| 803 chrome.fileBrowserPrivate.searchDriveMetadata(searchParams, searchCallback); | |
| 804 }; | |
| OLD | NEW |