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 // If directory files changes too often, don't rescan directory more than once | 7 // If directory files changes too often, don't rescan directory more than once |
8 // per specified interval | 8 // per specified interval |
9 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; | 9 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; |
10 // Used for operations that require almost instant rescan. | 10 // Used for operations that require almost instant rescan. |
(...skipping 14 matching lines...) Expand all Loading... | |
25 metadataCache, volumeManager) { | 25 metadataCache, volumeManager) { |
26 this.fileListSelection_ = singleSelection ? | 26 this.fileListSelection_ = singleSelection ? |
27 new cr.ui.ListSingleSelectionModel() : new cr.ui.ListSelectionModel(); | 27 new cr.ui.ListSingleSelectionModel() : new cr.ui.ListSelectionModel(); |
28 | 28 |
29 this.runningScan_ = null; | 29 this.runningScan_ = null; |
30 this.pendingScan_ = null; | 30 this.pendingScan_ = null; |
31 this.rescanTime_ = null; | 31 this.rescanTime_ = null; |
32 this.scanFailures_ = 0; | 32 this.scanFailures_ = 0; |
33 this.changeDirectorySequence_ = 0; | 33 this.changeDirectorySequence_ = 0; |
34 | 34 |
35 this.directoryChangeQueue_ = new AsyncUtil.Queue(); | |
36 | |
35 this.fileFilter_ = fileFilter; | 37 this.fileFilter_ = fileFilter; |
36 this.fileFilter_.addEventListener('changed', | 38 this.fileFilter_.addEventListener('changed', |
37 this.onFilterChanged_.bind(this)); | 39 this.onFilterChanged_.bind(this)); |
38 | 40 |
39 this.currentFileListContext_ = new FileListContext( | 41 this.currentFileListContext_ = new FileListContext( |
40 fileFilter, metadataCache); | 42 fileFilter, metadataCache); |
41 this.currentDirContents_ = | 43 this.currentDirContents_ = |
42 DirectoryContents.createForDirectory(this.currentFileListContext_, null); | 44 DirectoryContents.createForDirectory(this.currentFileListContext_, null); |
43 | 45 |
44 this.metadataCache_ = metadataCache; | 46 this.metadataCache_ = metadataCache; |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
271 * a single refresh. | 273 * a single refresh. |
272 * @param {number} delay Delay in ms after which the rescan will be performed. | 274 * @param {number} delay Delay in ms after which the rescan will be performed. |
273 */ | 275 */ |
274 DirectoryModel.prototype.scheduleRescan = function(delay) { | 276 DirectoryModel.prototype.scheduleRescan = function(delay) { |
275 if (this.rescanTime_) { | 277 if (this.rescanTime_) { |
276 if (this.rescanTime_ <= Date.now() + delay) | 278 if (this.rescanTime_ <= Date.now() + delay) |
277 return; | 279 return; |
278 clearTimeout(this.rescanTimeoutId_); | 280 clearTimeout(this.rescanTimeoutId_); |
279 } | 281 } |
280 | 282 |
283 var tracker = this.createDirectoryChangeTracker(); | |
hirono
2014/06/02 09:18:03
It looks tracker does not detect starting search,
yoshiki
2014/06/02 11:37:11
Good catch. search should be detected, since it ch
| |
284 tracker.start(); | |
285 | |
281 this.rescanTime_ = Date.now() + delay; | 286 this.rescanTime_ = Date.now() + delay; |
282 this.rescanTimeoutId_ = setTimeout(this.rescan.bind(this), delay); | 287 this.rescanTimeoutId_ = setTimeout(function() { |
288 this.rescanTimeoutId_ = null; | |
289 tracker.stop(); | |
290 if (!tracker.hasChanged) | |
291 this.rescan(); | |
292 }.bind(this), delay); | |
283 }; | 293 }; |
284 | 294 |
285 /** | 295 /** |
286 * Cancel a rescan on timeout if it is scheduled. | 296 * Cancel a rescan on timeout if it is scheduled. |
287 * @private | 297 * @private |
288 */ | 298 */ |
289 DirectoryModel.prototype.clearRescanTimeout_ = function() { | 299 DirectoryModel.prototype.clearRescanTimeout_ = function() { |
290 this.rescanTime_ = null; | 300 this.rescanTime_ = null; |
291 if (this.rescanTimeoutId_) { | 301 if (this.rescanTimeoutId_) { |
292 clearTimeout(this.rescanTimeoutId_); | 302 clearTimeout(this.rescanTimeoutId_); |
(...skipping 12 matching lines...) Expand all Loading... | |
305 DirectoryModel.prototype.rescan = function() { | 315 DirectoryModel.prototype.rescan = function() { |
306 this.clearRescanTimeout_(); | 316 this.clearRescanTimeout_(); |
307 if (this.runningScan_) { | 317 if (this.runningScan_) { |
308 this.pendingRescan_ = true; | 318 this.pendingRescan_ = true; |
309 return; | 319 return; |
310 } | 320 } |
311 | 321 |
312 var dirContents = this.currentDirContents_.clone(); | 322 var dirContents = this.currentDirContents_.clone(); |
313 dirContents.setFileList([]); | 323 dirContents.setFileList([]); |
314 | 324 |
325 var tracker = this.createDirectoryChangeTracker(); | |
326 tracker.start(); | |
327 | |
315 var successCallback = (function() { | 328 var successCallback = (function() { |
316 this.replaceDirectoryContents_(dirContents); | 329 tracker.stop(); |
330 if (!tracker.hasChanged) | |
331 this.replaceDirectoryContents_(dirContents); | |
317 cr.dispatchSimpleEvent(this, 'rescan-completed'); | 332 cr.dispatchSimpleEvent(this, 'rescan-completed'); |
318 }).bind(this); | 333 }).bind(this); |
319 | 334 |
320 this.scan_(dirContents, | 335 this.scan_(dirContents, |
321 successCallback, function() {}, function() {}, function() {}); | 336 successCallback, function() {}, function() {}, function() {}); |
322 }; | 337 }; |
323 | 338 |
324 /** | 339 /** |
325 * Run scan on the current DirectoryContents. The active fileList is cleared and | 340 * Run scan on the current DirectoryContents. The active fileList is cleared and |
326 * the entries are added directly. | 341 * the entries are added directly. |
327 * | 342 * |
328 * This should be used when changing directory or initiating a new search. | 343 * This should be used when changing directory or initiating a new search. |
329 * | 344 * |
330 * @param {DirectoryContentes} newDirContents New DirectoryContents instance to | 345 * @param {DirectoryContentes} newDirContents New DirectoryContents instance to |
331 * replace currentDirContents_. | 346 * replace currentDirContents_. |
332 * @param {function()=} opt_callback Called on success. | 347 * @param {function()} callback Callback. |
hirono
2014/06/02 09:18:03
Boolean is needed as an argument?
yoshiki
2014/06/02 11:37:11
Done.
| |
333 * @private | 348 * @private |
334 */ | 349 */ |
335 DirectoryModel.prototype.clearAndScan_ = function(newDirContents, | 350 DirectoryModel.prototype.clearAndScan_ = function(newDirContents, |
336 opt_callback) { | 351 callback) { |
337 if (this.currentDirContents_.isScanning()) | 352 if (this.currentDirContents_.isScanning()) |
338 this.currentDirContents_.cancelScan(); | 353 this.currentDirContents_.cancelScan(); |
339 this.currentDirContents_ = newDirContents; | 354 this.currentDirContents_ = newDirContents; |
340 this.clearRescanTimeout_(); | 355 this.clearRescanTimeout_(); |
341 | 356 |
342 if (this.pendingScan_) | 357 if (this.pendingScan_) |
343 this.pendingScan_ = false; | 358 this.pendingScan_ = false; |
344 | 359 |
345 if (this.runningScan_) { | 360 if (this.runningScan_) { |
346 if (this.runningScan_.isScanning()) | 361 if (this.runningScan_.isScanning()) |
347 this.runningScan_.cancelScan(); | 362 this.runningScan_.cancelScan(); |
348 this.runningScan_ = null; | 363 this.runningScan_ = null; |
349 } | 364 } |
350 | 365 |
366 var sequence = this.changeDirectorySequence_; | |
367 var cancelled = false; | |
368 | |
351 var onDone = function() { | 369 var onDone = function() { |
370 if (cancelled) | |
371 return; | |
372 | |
352 cr.dispatchSimpleEvent(this, 'scan-completed'); | 373 cr.dispatchSimpleEvent(this, 'scan-completed'); |
353 if (opt_callback) | 374 callback(true); |
354 opt_callback(); | |
355 }.bind(this); | 375 }.bind(this); |
356 | 376 |
357 var onFailed = function() { | 377 var onFailed = function() { |
378 if (cancelled) | |
379 return; | |
380 | |
358 cr.dispatchSimpleEvent(this, 'scan-failed'); | 381 cr.dispatchSimpleEvent(this, 'scan-failed'); |
382 callback(false); | |
359 }.bind(this); | 383 }.bind(this); |
360 | 384 |
361 var onUpdated = function() { | 385 var onUpdated = function() { |
386 if (cancelled) | |
387 return; | |
388 | |
389 if (this.changeDirectorySequence_ !== sequence) { | |
390 cancelled = true; | |
391 cr.dispatchSimpleEvent(this, 'scan-cancelled'); | |
392 callback(false); | |
393 return; | |
394 } | |
395 | |
362 cr.dispatchSimpleEvent(this, 'scan-updated'); | 396 cr.dispatchSimpleEvent(this, 'scan-updated'); |
363 }.bind(this); | 397 }.bind(this); |
364 | 398 |
365 var onCancelled = function() { | 399 var onCancelled = function() { |
400 if (cancelled) | |
401 return; | |
402 | |
403 cancelled = true; | |
366 cr.dispatchSimpleEvent(this, 'scan-cancelled'); | 404 cr.dispatchSimpleEvent(this, 'scan-cancelled'); |
405 callback(false); | |
367 }.bind(this); | 406 }.bind(this); |
368 | 407 |
369 // Clear the table, and start scanning. | 408 // Clear the table, and start scanning. |
370 cr.dispatchSimpleEvent(this, 'scan-started'); | 409 cr.dispatchSimpleEvent(this, 'scan-started'); |
371 var fileList = this.getFileList(); | 410 var fileList = this.getFileList(); |
372 fileList.splice(0, fileList.length); | 411 fileList.splice(0, fileList.length); |
373 this.scan_(this.currentDirContents_, | 412 this.scan_(this.currentDirContents_, |
374 onDone, onFailed, onUpdated, onCancelled); | 413 onDone, onFailed, onUpdated, onCancelled); |
375 }; | 414 }; |
376 | 415 |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
661 * be opened. | 700 * be opened. |
662 * @param {function()=} opt_callback Executed if the directory loads | 701 * @param {function()=} opt_callback Executed if the directory loads |
663 * successfully. | 702 * successfully. |
664 */ | 703 */ |
665 DirectoryModel.prototype.changeDirectoryEntry = function( | 704 DirectoryModel.prototype.changeDirectoryEntry = function( |
666 dirEntry, opt_callback) { | 705 dirEntry, opt_callback) { |
667 // Increment the sequence value. | 706 // Increment the sequence value. |
668 this.changeDirectorySequence_++; | 707 this.changeDirectorySequence_++; |
669 this.clearSearch_(); | 708 this.clearSearch_(); |
670 | 709 |
671 var promise = new Promise( | 710 this.directoryChangeQueue_.run(function(sequence, callback) { |
hirono
2014/06/02 09:18:03
Maybe more specific name is needed for callback, s
yoshiki
2014/06/02 11:37:11
Done.
| |
672 function(onFulfilled, onRejected) { | 711 this.fileWatcher_.changeWatchedDirectory( |
673 this.fileWatcher_.changeWatchedDirectory(dirEntry, onFulfilled); | 712 dirEntry, |
674 }.bind(this)). | 713 function() { |
675 | 714 if (this.changeDirectorySequence_ !== sequence) { |
676 then(function(sequence) { | 715 callback(); |
677 return new Promise(function(onFulfilled, onRejected) { | |
678 if (this.changeDirectorySequence_ !== sequence) | |
679 return; | 716 return; |
717 } | |
680 | 718 |
681 var newDirectoryContents = this.createDirectoryContents_( | 719 var newDirectoryContents = this.createDirectoryContents_( |
682 this.currentFileListContext_, dirEntry, ''); | 720 this.currentFileListContext_, dirEntry, ''); |
683 if (!newDirectoryContents) | 721 if (!newDirectoryContents) { |
722 callback(); | |
684 return; | 723 return; |
724 } | |
685 | 725 |
686 var previousDirEntry = this.currentDirContents_.getDirectoryEntry(); | 726 var previousDirEntry = |
687 this.clearAndScan_(newDirectoryContents, opt_callback); | 727 this.currentDirContents_.getDirectoryEntry(); |
728 this.clearAndScan_( | |
729 newDirectoryContents, | |
730 function(result) { | |
731 // Calls the callback when successful. | |
732 if (result && opt_callback) | |
733 opt_callback(); | |
734 callback(); | |
735 }); | |
688 | 736 |
689 // For tests that open the dialog to empty directories, everything is | 737 // For tests that open the dialog to empty directories, everything |
690 // loaded at this point. | 738 // is loaded at this point. |
691 util.testSendMessage('directory-change-complete'); | 739 util.testSendMessage('directory-change-complete'); |
692 | 740 |
693 var event = new Event('directory-changed'); | 741 var event = new Event('directory-changed'); |
694 event.previousDirEntry = previousDirEntry; | 742 event.previousDirEntry = previousDirEntry; |
695 event.newDirEntry = dirEntry; | 743 event.newDirEntry = dirEntry; |
696 this.dispatchEvent(event); | 744 this.dispatchEvent(event); |
697 }.bind(this)); | 745 }.bind(this)); |
698 }.bind(this, this.changeDirectorySequence_)); | 746 }.bind(this, this.changeDirectorySequence_)); |
699 }; | 747 }; |
700 | 748 |
701 /** | 749 /** |
702 * Activates the given directry. | 750 * Activates the given directry. |
703 * This method: | 751 * This method: |
704 * - Changes the current directory, if the given directory is the current | 752 * - Changes the current directory, if the given directory is the current |
705 * directory. | 753 * directory. |
706 * - Clears the selection, if the given directory is the current directory. | 754 * - Clears the selection, if the given directory is the current directory. |
707 * | 755 * |
708 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to | 756 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
896 DirectoryModel.prototype.search = function(query, | 944 DirectoryModel.prototype.search = function(query, |
897 onSearchRescan, | 945 onSearchRescan, |
898 onClearSearch) { | 946 onClearSearch) { |
899 this.clearSearch_(); | 947 this.clearSearch_(); |
900 var currentDirEntry = this.getCurrentDirEntry(); | 948 var currentDirEntry = this.getCurrentDirEntry(); |
901 if (!currentDirEntry) { | 949 if (!currentDirEntry) { |
902 // Not yet initialized. Do nothing. | 950 // Not yet initialized. Do nothing. |
903 return; | 951 return; |
904 } | 952 } |
905 | 953 |
906 if (!(query || '').trimLeft()) { | 954 this.changeDirectorySequence_++; |
907 if (this.isSearching()) { | 955 this.directoryChangeQueue_.run(function(sequence, callback) { |
908 var newDirContents = this.createDirectoryContents_( | 956 if (this.changeDirectorySequence_ !== sequence) { |
909 this.currentFileListContext_, | 957 callback(); |
910 currentDirEntry); | 958 return; |
911 this.clearAndScan_(newDirContents); | |
912 } | 959 } |
913 return; | |
914 } | |
915 | 960 |
916 var newDirContents = this.createDirectoryContents_( | 961 if (!(query || '').trimLeft()) { |
917 this.currentFileListContext_, currentDirEntry, query); | 962 if (this.isSearching()) { |
918 if (!newDirContents) | 963 var newDirContents = this.createDirectoryContents_( |
919 return; | 964 this.currentFileListContext_, |
965 currentDirEntry); | |
966 this.clearAndScan_(newDirContents, | |
967 sequence, | |
968 callback); | |
969 } else { | |
970 callback(); | |
971 } | |
972 return; | |
973 } | |
920 | 974 |
921 this.onSearchCompleted_ = onSearchRescan; | 975 var newDirContents = this.createDirectoryContents_( |
922 this.onClearSearch_ = onClearSearch; | 976 this.currentFileListContext_, currentDirEntry, query); |
923 this.addEventListener('scan-completed', this.onSearchCompleted_); | 977 if (!newDirContents) { |
924 this.clearAndScan_(newDirContents); | 978 callback(); |
979 return; | |
980 } | |
981 | |
982 this.onSearchCompleted_ = onSearchRescan; | |
983 this.onClearSearch_ = onClearSearch; | |
984 this.addEventListener('scan-completed', this.onSearchCompleted_); | |
985 this.clearAndScan_(newDirContents, | |
986 sequence, | |
987 callback); | |
988 }.bind(this, this.changeDirectorySequence_)); | |
925 }; | 989 }; |
926 | 990 |
927 /** | 991 /** |
928 * In case the search was active, remove listeners and send notifications on | 992 * In case the search was active, remove listeners and send notifications on |
929 * its canceling. | 993 * its canceling. |
930 * @private | 994 * @private |
931 */ | 995 */ |
932 DirectoryModel.prototype.clearSearch_ = function() { | 996 DirectoryModel.prototype.clearSearch_ = function() { |
933 if (!this.isSearching()) | 997 if (!this.isSearching()) |
934 return; | 998 return; |
935 | 999 |
936 if (this.onSearchCompleted_) { | 1000 if (this.onSearchCompleted_) { |
937 this.removeEventListener('scan-completed', this.onSearchCompleted_); | 1001 this.removeEventListener('scan-completed', this.onSearchCompleted_); |
938 this.onSearchCompleted_ = null; | 1002 this.onSearchCompleted_ = null; |
939 } | 1003 } |
940 | 1004 |
941 if (this.onClearSearch_) { | 1005 if (this.onClearSearch_) { |
942 this.onClearSearch_(); | 1006 this.onClearSearch_(); |
943 this.onClearSearch_ = null; | 1007 this.onClearSearch_ = null; |
944 } | 1008 } |
945 }; | 1009 }; |
OLD | NEW |