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. | |
|
mtomasz
2013/10/01 02:13:44
@constructor is missing
hidehiko
2013/10/01 04:12:23
Done.
| |
| 9 */ | |
| 10 function ContentScanner() { | |
| 11 this.cancelled_ = false; | |
| 12 } | |
| 13 | |
| 14 /** | |
| 15 * Starts to scan the entries. For example, starts to read the entries in a | |
| 16 * directory, or starts to search with some query on a file system. | |
|
mtomasz
2013/10/01 02:13:44
nit: Please add a new line after the description i
hidehiko
2013/10/01 04:12:23
Done.
| |
| 17 * Derived classes must override this method. | |
| 18 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 19 * entries are read. This can be called a couple of times until the | |
| 20 * completion. | |
| 21 * @param {function()} successCallback Called when the scan is completed | |
| 22 * successfully. | |
| 23 * @param {function(FileError)} errorCallback Called an error occurs. | |
| 24 */ | |
| 25 ContentScanner.prototype.startScan = function( | |
| 26 entriesCallback, successCallback, errorCallback) { | |
| 27 }; | |
| 28 | |
| 29 /** | |
| 30 * Request cancelling of the running scan. When the cancelling is done, | |
| 31 * an error will be reported from errorCallback passed to startScan(). | |
| 32 */ | |
| 33 ContentScanner.prototype.cancel = function() { | |
| 34 this.cancelled_ = true; | |
| 35 }; | |
| 36 | |
| 37 /** | |
| 38 * Scanner of the entries in a directory. | |
| 39 * @param {DirectoryEntry} entry The directory to be read. | |
| 40 * @extends {ContentScanner} | |
|
mtomasz
2013/10/01 02:13:44
@constructor is missing
hidehiko
2013/10/01 04:12:23
Done.
| |
| 41 */ | |
| 42 function DirectoryContentScanner(entry) { | |
| 43 ContentScanner.call(this); | |
| 44 this.entry_ = entry; | |
| 45 } | |
| 46 | |
| 47 /** | |
| 48 * Extends ContentScanner. | |
| 49 */ | |
| 50 DirectoryContentScanner.prototype.__proto__ = ContentScanner.prototype; | |
| 51 | |
| 52 /** | |
| 53 * Starts to read the entries in the directory. | |
| 54 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 55 * entries are read. This can be called a couple of times until the | |
| 56 * completion. | |
| 57 * @param {function()} successCallback Called when the scan is completed | |
| 58 * successfully. | |
| 59 * @param {function(FileError)} errorCallback Called an error occurs. | |
|
mtomasz
2013/10/01 02:13:44
@override is missing
hidehiko
2013/10/01 04:12:23
Done.
| |
| 60 */ | |
| 61 DirectoryContentScanner.prototype.startScan = function( | |
| 62 entriesCallback, successCallback, errorCallback) { | |
| 63 if (!this.entry_ || this.entry_ === DirectoryModel.fakeDriveEntry_) { | |
| 64 // If entry is not specified or a fake, we cannot read it. | |
| 65 errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR)); | |
| 66 return; | |
| 67 } | |
| 68 | |
| 69 metrics.startInterval('DirectoryScan'); | |
| 70 var reader = this.entry_.createReader(); | |
| 71 var readEntries = function() { | |
| 72 reader.readEntries( | |
| 73 function(entries) { | |
| 74 if (this.cancelled_) { | |
| 75 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 76 return; | |
| 77 } | |
| 78 | |
| 79 if (entries.length === 0) { | |
| 80 // All entries are read. | |
| 81 metrics.recordInterval('DirectoryScan'); | |
| 82 successCallback(); | |
| 83 return; | |
| 84 } | |
| 85 | |
| 86 entriesCallback(entries); | |
| 87 readEntries(); | |
| 88 }.bind(this), | |
| 89 errorCallback); | |
| 90 }.bind(this); | |
| 91 readEntries(); | |
| 92 }; | |
| 93 | |
| 94 /** | |
| 95 * Scanner of the entries for the search results on Drive File System. | |
| 96 * @param {string} query The query string. | |
| 97 * @extends {ContentScanner} | |
|
mtomasz
2013/10/01 02:13:44
ditto (@constructor)
hidehiko
2013/10/01 04:12:23
Done.
| |
| 98 */ | |
| 99 function DriveSearchContentScanner(query) { | |
| 100 ContentScanner.call(this); | |
| 101 this.query_ = query; | |
| 102 } | |
| 103 | |
| 104 /** | |
| 105 * Extends ContentScanner. | |
| 106 */ | |
| 107 DriveSearchContentScanner.prototype.__proto__ = ContentScanner.prototype; | |
| 108 | |
| 109 /** | |
| 110 * Delay to be used for drive search scan, in order to reduce the number of | |
| 111 * server requests while user is typeing the query. | |
|
mtomasz
2013/10/01 02:13:44
nit: typeing -> typing
nit: in milliseconds (we al
hidehiko
2013/10/01 04:12:23
Done.
| |
| 112 * @type {number} | |
| 113 * @private | |
| 114 * @const | |
| 115 */ | |
| 116 DriveSearchContentScanner.SCAN_DELAY_ = 200; | |
| 117 | |
| 118 /** | |
| 119 * Maximum number of results which is shown on the search. | |
| 120 * @type {number} | |
| 121 * @private | |
| 122 * @const | |
| 123 */ | |
| 124 DriveSearchContentScanner.MAX_RESULTS_ = 100; | |
| 125 | |
| 126 /** | |
| 127 * Starts to search on Drive File System. | |
| 128 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 129 * entries are read. This can be called a couple of times until the | |
| 130 * completion. | |
| 131 * @param {function()} successCallback Called when the scan is completed | |
| 132 * successfully. | |
| 133 * @param {function(FileError)} errorCallback Called an error occurs. | |
|
mtomasz
2013/10/01 02:13:44
nit: ditto (@override)
hidehiko
2013/10/01 04:12:23
Done.
| |
| 134 */ | |
| 135 DriveSearchContentScanner.prototype.startScan = function( | |
| 136 entriesCallback, successCallback, errorCallback) { | |
| 137 var numReadEntries = 0; | |
| 138 var readEntries = function(nextFeed) { | |
| 139 chrome.fileBrowserPrivate.searchDrive( | |
| 140 {query: this.query_, nextFeed: nextFeed}, | |
| 141 function(entries, nextFeed) { | |
| 142 if (this.cancelled_) { | |
| 143 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 144 return; | |
| 145 } | |
| 146 | |
| 147 // TODO(tbarzic): Improve error handling. | |
| 148 if (!entries) { | |
| 149 console.error('Drive search encountered an error.'); | |
| 150 errorCallback(util.createFileError( | |
| 151 FileError.INVALID_MODIFICATION_ERR)); | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 var numRemainingEntries = | |
| 156 DriveSearchContentScanner.MAX_RESULTS_ - numReadEntries; | |
| 157 if (entries.length >= numRemainingEntries) { | |
| 158 // The limit is hit, so quit the scan here. | |
| 159 entries = entries.slice(0, numRemainingEntries); | |
| 160 nextFeed = ''; | |
| 161 } | |
| 162 | |
| 163 numReadEntries += entries.length; | |
| 164 if (entries.length > 0) | |
| 165 entriesCallback(entries); | |
| 166 | |
| 167 if (nextFeed === '') | |
| 168 successCallback(); | |
| 169 else | |
| 170 readEntries(nextFeed); | |
| 171 }.bind(this)); | |
| 172 }.bind(this); | |
| 173 | |
| 174 // Let's give another search a chance to cancel us before we begin. | |
| 175 setTimeout( | |
| 176 function() { | |
| 177 // Check cancelled state before read the entries. | |
| 178 if (this.cancelled_) { | |
| 179 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 180 return; | |
| 181 } | |
| 182 readEntries(''); | |
| 183 }.bind(this), | |
| 184 DriveSearchContentScanner.SCAN_DELAY_); | |
| 185 }; | |
| 186 | |
| 187 /** | |
| 188 * Scanner of the entries of the file name search on the directory tree, whose | |
| 189 * root is entry. | |
| 190 * @param {DirectoryEntry} entry The root of the search target directory tree. | |
| 191 * @param {string} query The query of the search. | |
|
mtomasz
2013/10/01 02:13:44
ditto
hidehiko
2013/10/01 04:12:23
Done.
| |
| 192 */ | |
| 193 function LocalSearchContentScanner(entry, query) { | |
| 194 ContentScanner.call(this); | |
| 195 this.entry_ = entry; | |
| 196 this.query_ = query.toLowerCase(); | |
| 197 } | |
| 198 | |
| 199 /** | |
| 200 * Extedns ContentScanner. | |
| 201 */ | |
| 202 LocalSearchContentScanner.prototype.__proto__ = ContentScanner.prototype; | |
| 203 | |
| 204 /** | |
| 205 * Starts the file name search. | |
| 206 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 207 * entries are read. This can be called a couple of times until the | |
| 208 * completion. | |
| 209 * @param {function()} successCallback Called when the scan is completed | |
| 210 * successfully. | |
| 211 * @param {function(FileError)} errorCallback Called an error occurs. | |
|
mtomasz
2013/10/01 02:13:44
ditto here and in other places
hidehiko
2013/10/01 04:12:23
Done.
| |
| 212 */ | |
| 213 LocalSearchContentScanner.prototype.startScan = function( | |
| 214 entriesCallback, successCallback, errorCallback) { | |
| 215 var numRunningTasks = 0; | |
| 216 var error = null; | |
| 217 var maybeRunCallback = function() { | |
| 218 if (numRunningTasks === 0) { | |
| 219 if (this.cancelled_) | |
| 220 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 221 else if (error) | |
| 222 errorCallback(error); | |
| 223 else | |
| 224 successCallback(); | |
| 225 } | |
| 226 }.bind(this); | |
| 227 | |
| 228 var processEntry = function(entry) { | |
| 229 numRunningTasks++; | |
| 230 var onError = function(fileError) { | |
| 231 if (!error) | |
| 232 error = fileError; | |
| 233 numRunningTasks--; | |
| 234 maybeRunCallback(); | |
| 235 }; | |
| 236 | |
| 237 var onSuccess = function(entries) { | |
| 238 if (this.cancelled_ || error || entries.length === 0) { | |
| 239 numRunningTasks--; | |
| 240 maybeRunCallback(); | |
| 241 return; | |
| 242 } | |
| 243 | |
| 244 // Filters by the query, and if found, run entriesCallback. | |
| 245 var foundEntries = entries.filter(function(entry) { | |
| 246 return entry.name.toLowerCase().indexOf(this.query_) >= 0; | |
| 247 }.bind(this)); | |
| 248 if (foundEntries.length > 0) | |
| 249 entriesCallback(foundEntries); | |
| 250 | |
| 251 // Start to process sub directories. | |
| 252 for (var i = 0; i < entries.length; i++) { | |
| 253 if (entries[i].isDirectory) | |
| 254 processEntry(entries[i]); | |
| 255 } | |
| 256 | |
| 257 // Read remaining entries. | |
| 258 reader.readEntries(onSuccess, onError); | |
| 259 }.bind(this); | |
| 260 | |
| 261 var reader = entry.createReader(); | |
| 262 reader.readEntries(onSuccess, onError); | |
| 263 }.bind(this); | |
| 264 | |
| 265 processEntry(this.entry_); | |
| 266 }; | |
| 267 | |
| 268 /** | |
| 269 * Scanner of the entries for the metadata seaerch on Drive File System. | |
| 270 * @param {string} query The query of the search. | |
| 271 * @param {DriveMetadataSearchContentScanner.SearchType} searchType The option | |
| 272 * of the search. | |
| 273 * @extends {ContentScanner} | |
| 274 */ | |
| 275 function DriveMetadataSearchContentScanner(query, searchType) { | |
| 276 ContentScanner.call(this); | |
| 277 this.query_ = query; | |
| 278 this.searchType_ = searchType; | |
| 279 } | |
| 280 | |
| 281 /** | |
| 282 * Extends ContentScanner. | |
| 283 */ | |
| 284 DriveMetadataSearchContentScanner.prototype.__proto__ = | |
| 285 ContentScanner.prototype; | |
| 286 | |
| 287 /** | |
| 288 * The search types on the Drive File System. | |
|
mtomasz
2013/10/01 02:13:44
@enum {string} missing
hidehiko
2013/10/01 04:12:23
Done.
| |
| 289 */ | |
| 290 DriveMetadataSearchContentScanner.SearchType = Object.freeze({ | |
| 291 SEARCH_ALL: 'ALL', | |
| 292 SEARCH_SHARED_WITH_ME: 'SHARED_WITH_ME', | |
| 293 SEARCH_RECENT_FILES: 'EXCLUDE_DIRECTORIES', | |
| 294 SEARCH_OFFLINE: 'OFFLINE' | |
| 295 }); | |
| 296 | |
| 297 /** | |
| 298 * Starts to metadata-search on Drive File System. | |
| 299 * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of | |
| 300 * entries are read. This can be called a couple of times until the | |
| 301 * completion. | |
| 302 * @param {function()} successCallback Called when the scan is completed | |
| 303 * successfully. | |
| 304 * @param {function(FileError)} errorCallback Called an error occurs. | |
| 305 */ | |
| 306 DriveMetadataSearchContentScanner.prototype.startScan = function( | |
|
mtomasz
2013/10/01 02:13:44
optional: how about just scan() instead of startSc
hidehiko
2013/10/01 04:12:23
Done.
| |
| 307 entriesCallback, successCallback, errorCallback) { | |
| 308 chrome.fileBrowserPrivate.searchDriveMetadata( | |
| 309 {query: this.query_, types: this.searchType_, maxResults: 500}, | |
| 310 function(results) { | |
| 311 if (this.cancelled_) { | |
| 312 errorCallback(util.createFileError(FileError.ABORT_ERR)); | |
| 313 return; | |
| 314 } | |
| 315 | |
| 316 if (!results) { | |
| 317 console.error('Drive search encountered an error.'); | |
| 318 errorCallback(util.createFileError( | |
| 319 FileError.INVALID_MODIFICATION_ERR)); | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 var entries = results.map(function(result) { return result.entry; }); | |
| 324 if (entries.length > 0) | |
| 325 entriesCallback(entries); | |
| 326 successCallback(); | |
| 327 }.bind(this)); | |
| 328 }; | |
| 329 | |
| 330 /** | |
| 8 * This class manages filters and determines a file should be shown or not. | 331 * This class manages filters and determines a file should be shown or not. |
| 9 * When filters are changed, a 'changed' event is fired. | 332 * When filters are changed, a 'changed' event is fired. |
| 10 * | 333 * |
| 11 * @param {MetadataCache} metadataCache Metadata cache service. | 334 * @param {MetadataCache} metadataCache Metadata cache service. |
| 12 * @param {boolean} showHidden If files starting with '.' are shown. | 335 * @param {boolean} showHidden If files starting with '.' are shown. |
| 13 * @constructor | 336 * @constructor |
| 14 * @extends {cr.EventTarget} | 337 * @extends {cr.EventTarget} |
| 15 */ | 338 */ |
| 16 function FileFilter(metadataCache, showHidden) { | 339 function FileFilter(metadataCache, showHidden) { |
| 17 /** | 340 /** |
| 18 * @type {MetadataCache} | 341 * @type {MetadataCache} |
| 19 * @private | 342 * @private |
| 20 */ | 343 */ |
| 21 this.metadataCache_ = metadataCache; | 344 this.metadataCache_ = metadataCache; |
| 345 | |
| 22 /** | 346 /** |
| 23 * @type Object.<string, Function> | 347 * @type Object.<string, Function> |
| 24 * @private | 348 * @private |
| 25 */ | 349 */ |
| 26 this.filters_ = {}; | 350 this.filters_ = {}; |
| 27 this.setFilterHidden(!showHidden); | 351 this.setFilterHidden(!showHidden); |
| 28 | 352 |
| 29 // Do not show entries marked as 'deleted'. | 353 // Do not show entries marked as 'deleted'. |
| 30 this.addFilter('deleted', function(entry) { | 354 this.addFilter('deleted', function(entry) { |
| 31 var internal = this.metadataCache_.getCached(entry, 'internal'); | 355 var internal = this.metadataCache_.getCached(entry, 'internal'); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 95 * | 419 * |
| 96 * @param {FileFilter} fileFilter The file-filter context. | 420 * @param {FileFilter} fileFilter The file-filter context. |
| 97 * @param {MetadataCache} metadataCache Metadata cache service. | 421 * @param {MetadataCache} metadataCache Metadata cache service. |
| 98 * @constructor | 422 * @constructor |
| 99 */ | 423 */ |
| 100 function FileListContext(fileFilter, metadataCache) { | 424 function FileListContext(fileFilter, metadataCache) { |
| 101 /** | 425 /** |
| 102 * @type {cr.ui.ArrayDataModel} | 426 * @type {cr.ui.ArrayDataModel} |
| 103 */ | 427 */ |
| 104 this.fileList = new cr.ui.ArrayDataModel([]); | 428 this.fileList = new cr.ui.ArrayDataModel([]); |
| 429 | |
| 105 /** | 430 /** |
| 106 * @type {MetadataCache} | 431 * @type {MetadataCache} |
| 107 */ | 432 */ |
| 108 this.metadataCache = metadataCache; | 433 this.metadataCache = metadataCache; |
| 109 | 434 |
| 110 /** | 435 /** |
| 111 * @type {FileFilter} | 436 * @type {FileFilter} |
| 112 */ | 437 */ |
| 113 this.fileFilter = fileFilter; | 438 this.fileFilter = fileFilter; |
| 114 } | 439 } |
| 115 | 440 |
| 116 /** | 441 /** |
| 117 * This class is responsible for scanning directory (or search results), | 442 * This class is responsible for scanning directory (or search results), |
| 118 * and filling the fileList. Different descendants handle various types of | 443 * and filling the fileList. Different descendants handle various types of |
| 119 * directory contents shown: basic directory, drive search results, local search | 444 * directory contents shown: basic directory, drive search results, local search |
| 120 * results. | 445 * results. |
| 121 * @param {FileListContext} context The file list context. | 446 * @param {FileListContext} context The file list context. |
| 122 * @constructor | 447 * @constructor |
| 123 * @extends {cr.EventTarget} | 448 * @extends {cr.EventTarget} |
| 124 */ | 449 */ |
| 125 function DirectoryContents(context) { | 450 function DirectoryContents(context) { |
| 126 this.context_ = context; | 451 this.context_ = context; |
| 127 this.fileList_ = context.fileList; | 452 this.fileList_ = context.fileList; |
| 128 this.scanCompletedCallback_ = null; | 453 this.prefetchMetadataQueue_ = new AsyncUtil.Queue(); |
| 129 this.scanFailedCallback_ = null; | |
| 130 this.scanCancelled_ = false; | 454 this.scanCancelled_ = false; |
| 131 this.allChunksFetched_ = false; | |
| 132 this.pendingMetadataRequests_ = 0; | |
| 133 this.fileList_.prepareSort = this.prepareSort_.bind(this); | 455 this.fileList_.prepareSort = this.prepareSort_.bind(this); |
| 134 } | 456 } |
| 135 | 457 |
| 136 /** | 458 /** |
| 137 * DirectoryContents extends cr.EventTarget. | 459 * DirectoryContents extends cr.EventTarget. |
| 138 */ | 460 */ |
| 139 DirectoryContents.prototype.__proto__ = cr.EventTarget.prototype; | 461 DirectoryContents.prototype.__proto__ = cr.EventTarget.prototype; |
| 140 | 462 |
| 141 /** | 463 /** |
| 142 * Create the copy of the object, but without scan started. | 464 * Create the copy of the object, but without scan started. |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 166 spliceArgs.unshift(0, fileList.length); | 488 spliceArgs.unshift(0, fileList.length); |
| 167 fileList.splice.apply(fileList, spliceArgs); | 489 fileList.splice.apply(fileList, spliceArgs); |
| 168 this.fileList_ = fileList; | 490 this.fileList_ = fileList; |
| 169 } | 491 } |
| 170 }; | 492 }; |
| 171 | 493 |
| 172 /** | 494 /** |
| 173 * @return {boolean} If the scan is active. | 495 * @return {boolean} If the scan is active. |
| 174 */ | 496 */ |
| 175 DirectoryContents.prototype.isScanning = function() { | 497 DirectoryContents.prototype.isScanning = function() { |
| 176 return !this.scanCancelled_ && | 498 return this.scanner_ || this.prefetchMetadataQueue_.isRunning(); |
| 177 (!this.allChunksFetched_ || this.pendingMetadataRequests_ > 0); | |
| 178 }; | 499 }; |
| 179 | 500 |
| 180 /** | 501 /** |
| 181 * @return {boolean} True if search results (drive or local). | 502 * @return {boolean} True if search results (drive or local). |
| 182 */ | 503 */ |
| 183 DirectoryContents.prototype.isSearch = function() { | 504 DirectoryContents.prototype.isSearch = function() { |
| 184 return false; | 505 return false; |
| 185 }; | 506 }; |
| 186 | 507 |
| 187 /** | 508 /** |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 201 | 522 |
| 202 /** | 523 /** |
| 203 * Start directory scan/search operation. Either 'scan-completed' or | 524 * Start directory scan/search operation. Either 'scan-completed' or |
| 204 * 'scan-failed' event will be fired upon completion. | 525 * 'scan-failed' event will be fired upon completion. |
| 205 */ | 526 */ |
| 206 DirectoryContents.prototype.scan = function() { | 527 DirectoryContents.prototype.scan = function() { |
| 207 throw 'Not implemented.'; | 528 throw 'Not implemented.'; |
| 208 }; | 529 }; |
| 209 | 530 |
| 210 /** | 531 /** |
| 211 * Read next chunk of results from DirectoryReader. | 532 * 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 */ | 533 */ |
| 221 DirectoryContents.prototype.cancelScan = function() { | 534 DirectoryContents.prototype.cancelScan = function() { |
| 222 if (this.scanCancelled_) | 535 if (this.scanCancelled_) |
| 223 return; | 536 return; |
| 224 this.scanCancelled_ = true; | 537 this.scanCancelled_ = true; |
| 538 if (this.scanner_) | |
| 539 this.scanner_.cancel(); | |
| 540 | |
| 541 this.prefetchMetadataQueue_.cancel(); | |
| 225 cr.dispatchSimpleEvent(this, 'scan-cancelled'); | 542 cr.dispatchSimpleEvent(this, 'scan-cancelled'); |
| 226 }; | 543 }; |
| 227 | 544 |
| 545 /** | |
| 546 * Called when the scanning by scanner_ is done. | |
| 547 * @protected | |
| 548 */ | |
| 549 DirectoryContents.prototype.onScanCompleted = function() { | |
| 550 this.scanner_ = null; | |
| 551 if (this.scanCancelled_) | |
| 552 return; | |
| 553 | |
| 554 this.prefetchMetadataQueue_.run(function(callback) { | |
| 555 cr.dispatchSimpleEvent(this, 'scan-completed'); | |
| 556 if (!this.isSearch() && | |
| 557 this.getDirectoryEntry().fullPath === RootDirectory.DOWNLOADS) | |
| 558 metrics.recordMediumCount('DownloadsCount', this.fileList_.length); | |
| 559 callback(); | |
| 560 }.bind(this)); | |
| 561 }; | |
| 228 | 562 |
| 229 /** | 563 /** |
| 230 * Called in case scan has failed. Should send the event. | 564 * Called in case scan has failed. Should send the event. |
| 231 * @protected | 565 * @protected |
| 232 */ | 566 */ |
| 233 DirectoryContents.prototype.onError = function() { | 567 DirectoryContents.prototype.onScanError = function() { |
| 234 cr.dispatchSimpleEvent(this, 'scan-failed'); | 568 this.scanner_ = null; |
| 569 if (this.scanCancelled_) | |
| 570 return; | |
| 571 | |
| 572 this.prefetchMetadataQueue_.run(function(callback) { | |
| 573 cr.dispatchSimpleEvent(this, 'scan-failed'); | |
| 574 callback(); | |
| 575 }.bind(this)); | |
| 235 }; | 576 }; |
| 236 | 577 |
| 237 /** | 578 /** |
| 238 * Called in case scan has completed succesfully. Should send the event. | 579 * Called when some chunk of entries are read by scanner. |
| 580 * @param {Array.<Entry>} entries The list of the scanned entries. | |
| 239 * @protected | 581 * @protected |
| 240 */ | 582 */ |
| 241 DirectoryContents.prototype.lastChunkReceived = function() { | 583 DirectoryContents.prototype.onNewEntries = function(entries) { |
| 242 this.allChunksFetched_ = true; | 584 if (this.scanCancelled_) |
| 243 if (!this.scanCancelled_ && this.pendingMetadataRequests_ === 0) | 585 return; |
| 244 cr.dispatchSimpleEvent(this, 'scan-completed'); | 586 |
| 587 var entriesFiltered = [].filter.call( | |
| 588 entries, this.context_.fileFilter.filter.bind(this.context_.fileFilter)); | |
| 589 | |
| 590 // Because the prefetchMetadata can be slow, throttling by splitting entries | |
| 591 // into smaller chunks to reduce UI latency. | |
| 592 // TODO(hidehiko,mtomasz): This should be handled in MetadataCache. | |
| 593 var MAX_CHUNK_SIZE = 50; | |
| 594 for (var i = 0; i < entriesFiltered.length; i += MAX_CHUNK_SIZE) { | |
| 595 var chunk = entriesFiltered.slice(i, i + MAX_CHUNK_SIZE); | |
| 596 this.prefetchMetadataQueue_.run(function(chunk, callback) { | |
| 597 this.prefetchMetadata(chunk, function() { | |
| 598 if (this.scanCancelled_) { | |
| 599 // Do nothing if the scanning is cancelled. | |
| 600 callback(); | |
| 601 return; | |
| 602 } | |
| 603 | |
| 604 this.fileList_.push.apply(this.fileList_, chunk); | |
| 605 cr.dispatchSimpleEvent(this, 'scan-updated'); | |
| 606 callback(); | |
| 607 }.bind(this)); | |
| 608 }.bind(this, chunk)); | |
| 609 } | |
| 245 }; | 610 }; |
| 246 | 611 |
| 247 /** | 612 /** |
| 248 * Cache necessary data before a sort happens. | 613 * Cache necessary data before a sort happens. |
| 249 * | 614 * |
| 250 * This is called by the table code before a sort happens, so that we can | 615 * 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. | 616 * go fetch data for the sort field that we may not have yet. |
| 252 * @param {string} field Sort field. | 617 * @param {string} field Sort field. |
| 253 * @param {function(Object)} callback Called when done. | 618 * @param {function(Object)} callback Called when done. |
| 254 * @private | 619 * @private |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 268 /** | 633 /** |
| 269 * @param {Array.<Entry>} entries Files. | 634 * @param {Array.<Entry>} entries Files. |
| 270 * @param {function(Object)} callback Callback on done. | 635 * @param {function(Object)} callback Callback on done. |
| 271 */ | 636 */ |
| 272 DirectoryContents.prototype.reloadMetadata = function(entries, callback) { | 637 DirectoryContents.prototype.reloadMetadata = function(entries, callback) { |
| 273 this.context_.metadataCache.clear(entries, '*'); | 638 this.context_.metadataCache.clear(entries, '*'); |
| 274 this.context_.metadataCache.get(entries, 'filesystem', callback); | 639 this.context_.metadataCache.get(entries, 'filesystem', callback); |
| 275 }; | 640 }; |
| 276 | 641 |
| 277 /** | 642 /** |
| 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. | 643 * @param {string} name Directory name. |
| 309 * @param {function(DirectoryEntry)} successCallback Called on success. | 644 * @param {function(DirectoryEntry)} successCallback Called on success. |
| 310 * @param {function(FileError)} errorCallback On error. | 645 * @param {function(FileError)} errorCallback On error. |
| 311 */ | 646 */ |
| 312 DirectoryContents.prototype.createDirectory = function( | 647 DirectoryContents.prototype.createDirectory = function( |
| 313 name, successCallback, errorCallback) { | 648 name, successCallback, errorCallback) { |
| 314 throw 'Not implemented.'; | 649 throw 'Not implemented.'; |
| 315 }; | 650 }; |
| 316 | 651 |
| 317 | 652 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 * @return {DirectoryEntry} DirectoryEntry for the currnet entry. | 685 * @return {DirectoryEntry} DirectoryEntry for the currnet entry. |
| 351 */ | 686 */ |
| 352 DirectoryContentsBasic.prototype.getLastNonSearchDirectoryEntry = function() { | 687 DirectoryContentsBasic.prototype.getLastNonSearchDirectoryEntry = function() { |
| 353 return this.entry_; | 688 return this.entry_; |
| 354 }; | 689 }; |
| 355 | 690 |
| 356 /** | 691 /** |
| 357 * Start directory scan. | 692 * Start directory scan. |
| 358 */ | 693 */ |
| 359 DirectoryContentsBasic.prototype.scan = function() { | 694 DirectoryContentsBasic.prototype.scan = function() { |
| 360 if (!this.entry_ || this.entry_ === DirectoryModel.fakeDriveEntry_) { | 695 this.scanner_ = new DirectoryContentScanner(this.entry_); |
|
mtomasz
2013/10/01 02:36:39
Do we need to create a new instance of scanner eve
hidehiko
2013/10/01 04:12:23
Good point.
In the existing code, scan must be cal
mtomasz
2013/10/01 04:39:55
Can you please add either (1) a TODO line or (2):
hidehiko
2013/10/01 05:28:17
Done.
| |
| 361 this.lastChunkReceived(); | 696 this.scanner_.startScan(this.onNewEntries.bind(this), |
| 362 return; | 697 this.onScanCompleted.bind(this), |
| 363 } | 698 this.onScanError.bind(this)); |
| 364 | |
| 365 metrics.startInterval('DirectoryScan'); | |
| 366 this.reader_ = this.entry_.createReader(); | |
| 367 this.readNextChunk(); | |
| 368 }; | 699 }; |
| 369 | 700 |
| 370 /** | 701 /** |
| 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. | 702 * @param {string} name Directory name. |
| 407 * @param {function(Entry)} successCallback Called on success. | 703 * @param {function(Entry)} successCallback Called on success. |
| 408 * @param {function(FileError)} errorCallback On error. | 704 * @param {function(FileError)} errorCallback On error. |
| 409 */ | 705 */ |
| 410 DirectoryContentsBasic.prototype.createDirectory = function( | 706 DirectoryContentsBasic.prototype.createDirectory = function( |
| 411 name, successCallback, errorCallback) { | 707 name, successCallback, errorCallback) { |
| 708 // TODO(hidehiko): createDirectory should not be the part of | |
| 709 // DirectoryContent. | |
| 412 if (!this.entry_) { | 710 if (!this.entry_) { |
| 413 errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR)); | 711 errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR)); |
| 414 return; | 712 return; |
| 415 } | 713 } |
| 416 | 714 |
| 417 var onSuccess = function(newEntry) { | 715 var onSuccess = function(newEntry) { |
| 418 this.reloadMetadata([newEntry], function() { | 716 this.reloadMetadata([newEntry], function() { |
| 419 successCallback(newEntry); | 717 successCallback(newEntry); |
| 420 }); | 718 }); |
| 421 }; | 719 }; |
| 422 | 720 |
| 423 this.entry_.getDirectory(name, {create: true, exclusive: true}, | 721 this.entry_.getDirectory(name, {create: true, exclusive: true}, |
| 424 onSuccess.bind(this), errorCallback); | 722 onSuccess.bind(this), errorCallback); |
| 425 }; | 723 }; |
| 426 | 724 |
| 427 /** | 725 /** |
| 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. | 726 * @param {FileListContext} context File list context. |
| 447 * @param {DirectoryEntry} dirEntry Current directory. | 727 * @param {DirectoryEntry} dirEntry Current directory. |
| 448 * @param {DirectoryEntry} previousDirEntry DirectoryEntry that was current | 728 * @param {DirectoryEntry} previousDirEntry DirectoryEntry that was current |
| 449 * before the search. | 729 * before the search. |
| 450 * @param {string} query Search query. | 730 * @param {string} query Search query. |
| 451 * @constructor | 731 * @constructor |
| 452 * @extends {DirectoryContents} | 732 * @extends {DirectoryContents} |
| 453 */ | 733 */ |
| 454 function DirectoryContentsDriveSearch(context, | 734 function DirectoryContentsDriveSearch(context, |
| 455 dirEntry, | 735 dirEntry, |
| 456 previousDirEntry, | 736 previousDirEntry, |
| 457 query) { | 737 query) { |
| 458 DirectoryContents.call(this, context); | 738 DirectoryContents.call(this, context); |
| 459 this.directoryEntry_ = dirEntry; | 739 this.directoryEntry_ = dirEntry; |
| 460 this.previousDirectoryEntry_ = previousDirEntry; | 740 this.previousDirectoryEntry_ = previousDirEntry; |
| 461 this.query_ = query; | 741 this.query_ = query; |
| 462 this.nextFeed_ = ''; | |
| 463 this.done_ = false; | |
| 464 this.fetchedResultsNum_ = 0; | |
| 465 } | 742 } |
| 466 | 743 |
| 467 /** | 744 /** |
| 468 * Extends DirectoryContents. | 745 * Extends DirectoryContents. |
| 469 */ | 746 */ |
| 470 DirectoryContentsDriveSearch.prototype.__proto__ = DirectoryContents.prototype; | 747 DirectoryContentsDriveSearch.prototype.__proto__ = DirectoryContents.prototype; |
| 471 | 748 |
| 472 /** | 749 /** |
| 473 * Create the copy of the object, but without scan started. | 750 * Create the copy of the object, but without scan started. |
| 474 * @return {DirectoryContentsBasic} Object copy. | 751 * @return {DirectoryContentsBasic} Object copy. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 500 */ | 777 */ |
| 501 DirectoryContentsDriveSearch.prototype.getLastNonSearchDirectoryEntry = | 778 DirectoryContentsDriveSearch.prototype.getLastNonSearchDirectoryEntry = |
| 502 function() { | 779 function() { |
| 503 return this.previousDirectoryEntry_; | 780 return this.previousDirectoryEntry_; |
| 504 }; | 781 }; |
| 505 | 782 |
| 506 /** | 783 /** |
| 507 * Start directory scan. | 784 * Start directory scan. |
| 508 */ | 785 */ |
| 509 DirectoryContentsDriveSearch.prototype.scan = function() { | 786 DirectoryContentsDriveSearch.prototype.scan = function() { |
| 510 // Let's give another search a chance to cancel us before we begin. | 787 this.scanner_ = new DriveSearchContentScanner(this.query_); |
|
mtomasz
2013/10/01 02:36:39
Same here.
hidehiko
2013/10/01 04:12:23
Ack.
| |
| 511 setTimeout(this.readNextChunk.bind(this), | 788 this.scanner_.startScan(this.onNewEntries.bind(this), |
| 512 DirectoryContentsDriveSearch.SCAN_DELAY); | 789 this.onScanCompleted.bind(this), |
| 790 this.onScanError.bind(this)); | |
| 513 }; | 791 }; |
| 514 | 792 |
| 515 /** | 793 /** |
| 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. | 794 * @param {FileListContext} context File list context. |
| 559 * @param {DirectoryEntry} dirEntry Current directory. | 795 * @param {DirectoryEntry} dirEntry Current directory. |
| 560 * @param {string} query Search query. | 796 * @param {string} query Search query. |
| 561 * @constructor | 797 * @constructor |
| 562 * @extends {DirectoryContents} | 798 * @extends {DirectoryContents} |
| 563 */ | 799 */ |
| 564 function DirectoryContentsLocalSearch(context, dirEntry, query) { | 800 function DirectoryContentsLocalSearch(context, dirEntry, query) { |
| 565 DirectoryContents.call(this, context); | 801 DirectoryContents.call(this, context); |
| 566 this.directoryEntry_ = dirEntry; | 802 this.directoryEntry_ = dirEntry; |
| 567 this.query_ = query.toLowerCase(); | 803 this.query_ = query; |
| 568 } | 804 } |
| 569 | 805 |
| 570 /** | 806 /** |
| 571 * Extends DirectoryContents | 807 * Extends DirectoryContents |
| 572 */ | 808 */ |
| 573 DirectoryContentsLocalSearch.prototype.__proto__ = DirectoryContents.prototype; | 809 DirectoryContentsLocalSearch.prototype.__proto__ = DirectoryContents.prototype; |
| 574 | 810 |
| 575 /** | 811 /** |
| 576 * Create the copy of the object, but without scan started. | 812 * Create the copy of the object, but without scan started. |
| 577 * @return {DirectoryContentsBasic} Object copy. | 813 * @return {DirectoryContentsBasic} Object copy. |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 603 DirectoryContentsLocalSearch.prototype.getLastNonSearchDirectoryEntry = | 839 DirectoryContentsLocalSearch.prototype.getLastNonSearchDirectoryEntry = |
| 604 function() { | 840 function() { |
| 605 return this.directoryEntry_; | 841 return this.directoryEntry_; |
| 606 }; | 842 }; |
| 607 | 843 |
| 608 /** | 844 /** |
| 609 * Start directory scan/search operation. Either 'scan-completed' or | 845 * Start directory scan/search operation. Either 'scan-completed' or |
| 610 * 'scan-failed' event will be fired upon completion. | 846 * 'scan-failed' event will be fired upon completion. |
| 611 */ | 847 */ |
| 612 DirectoryContentsLocalSearch.prototype.scan = function() { | 848 DirectoryContentsLocalSearch.prototype.scan = function() { |
| 613 this.pendingScans_ = 0; | 849 this.scanner_ = |
| 614 this.scanDirectory_(this.directoryEntry_); | 850 new LocalSearchContentScanner(this.directoryEntry_, this.query_); |
| 851 this.scanner_.startScan(this.onNewEntries.bind(this), | |
| 852 this.onScanCompleted.bind(this), | |
| 853 this.onScanError.bind(this)); | |
| 615 }; | 854 }; |
| 616 | 855 |
| 617 /** | 856 /** |
| 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(). | 857 * DirectoryContents to list Drive files using searchDriveMetadata(). |
| 683 * | 858 * |
| 684 * @param {FileListContext} context File list context. | 859 * @param {FileListContext} context File list context. |
| 685 * @param {DirectoryEntry} driveDirEntry Directory for actual Drive. | 860 * @param {DirectoryEntry} driveDirEntry Directory for actual Drive. |
| 686 * @param {DirectoryEntry} fakeDirEntry Fake directory representing the set of | 861 * @param {DirectoryEntry} fakeDirEntry Fake directory representing the set of |
| 687 * result files. This serves as a top directory for this search. | 862 * result files. This serves as a top directory for this search. |
| 688 * @param {string} query Search query to filter the files. | 863 * @param {string} query Search query to filter the files. |
| 689 * @param {DirectoryContentsDriveSearchMetadata.SearchType} searchType | 864 * @param {DriveMetadataSearchContentScanner.SearchType} searchType |
| 690 * Type of search. searchDriveMetadata will restricts the entries based on | 865 * Type of search. searchDriveMetadata will restricts the entries based on |
| 691 * the given search type. | 866 * the given search type. |
| 692 * @constructor | 867 * @constructor |
| 693 * @extends {DirectoryContents} | 868 * @extends {DirectoryContents} |
| 694 */ | 869 */ |
| 695 function DirectoryContentsDriveSearchMetadata(context, | 870 function DirectoryContentsDriveSearchMetadata(context, |
| 696 driveDirEntry, | 871 driveDirEntry, |
| 697 fakeDirEntry, | 872 fakeDirEntry, |
| 698 query, | 873 query, |
| 699 searchType) { | 874 searchType) { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 742 DirectoryContentsDriveSearchMetadata.prototype.getLastNonSearchDirectoryEntry = | 917 DirectoryContentsDriveSearchMetadata.prototype.getLastNonSearchDirectoryEntry = |
| 743 function() { | 918 function() { |
| 744 return this.driveDirEntry_; | 919 return this.driveDirEntry_; |
| 745 }; | 920 }; |
| 746 | 921 |
| 747 /** | 922 /** |
| 748 * Start directory scan/search operation. Either 'scan-completed' or | 923 * Start directory scan/search operation. Either 'scan-completed' or |
| 749 * 'scan-failed' event will be fired upon completion. | 924 * 'scan-failed' event will be fired upon completion. |
| 750 */ | 925 */ |
| 751 DirectoryContentsDriveSearchMetadata.prototype.scan = function() { | 926 DirectoryContentsDriveSearchMetadata.prototype.scan = function() { |
| 752 this.readNextChunk(); | 927 this.scanner_ = |
| 928 new DriveMetadataSearchContentScanner(this.query_, this.searchType_); | |
| 929 this.scanner_.startScan(this.onNewEntries.bind(this), | |
| 930 this.onScanCompleted.bind(this), | |
| 931 this.onScanError.bind(this)); | |
| 753 }; | 932 }; |
| 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 |