Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: ui/file_manager/file_manager/foreground/js/directory_model.js

Issue 312493002: Files.app: Fix flakiness in chaining directory (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed the comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 sequence = this.changeDirectorySequence_;
284
281 this.rescanTime_ = Date.now() + delay; 285 this.rescanTime_ = Date.now() + delay;
282 this.rescanTimeoutId_ = setTimeout(this.rescan.bind(this), delay); 286 this.rescanTimeoutId_ = setTimeout(function() {
287 this.rescanTimeoutId_ = null;
288 tracker.stop();
hirono 2014/06/02 11:52:07 Remaining line.
yoshiki 2014/06/04 13:46:41 Done.
289 if (sequence !== this.changeDirectorySequence_)
290 this.rescan();
291 }.bind(this), delay);
283 }; 292 };
284 293
285 /** 294 /**
286 * Cancel a rescan on timeout if it is scheduled. 295 * Cancel a rescan on timeout if it is scheduled.
287 * @private 296 * @private
288 */ 297 */
289 DirectoryModel.prototype.clearRescanTimeout_ = function() { 298 DirectoryModel.prototype.clearRescanTimeout_ = function() {
290 this.rescanTime_ = null; 299 this.rescanTime_ = null;
291 if (this.rescanTimeoutId_) { 300 if (this.rescanTimeoutId_) {
292 clearTimeout(this.rescanTimeoutId_); 301 clearTimeout(this.rescanTimeoutId_);
(...skipping 12 matching lines...) Expand all
305 DirectoryModel.prototype.rescan = function() { 314 DirectoryModel.prototype.rescan = function() {
306 this.clearRescanTimeout_(); 315 this.clearRescanTimeout_();
307 if (this.runningScan_) { 316 if (this.runningScan_) {
308 this.pendingRescan_ = true; 317 this.pendingRescan_ = true;
309 return; 318 return;
310 } 319 }
311 320
312 var dirContents = this.currentDirContents_.clone(); 321 var dirContents = this.currentDirContents_.clone();
313 dirContents.setFileList([]); 322 dirContents.setFileList([]);
314 323
324 var sequence = this.changeDirectorySequence_;
325
315 var successCallback = (function() { 326 var successCallback = (function() {
316 this.replaceDirectoryContents_(dirContents); 327 if (sequence !== this.changeDirectorySequence_)
328 this.replaceDirectoryContents_(dirContents);
317 cr.dispatchSimpleEvent(this, 'rescan-completed'); 329 cr.dispatchSimpleEvent(this, 'rescan-completed');
318 }).bind(this); 330 }).bind(this);
319 331
320 this.scan_(dirContents, 332 this.scan_(dirContents,
321 successCallback, function() {}, function() {}, function() {}); 333 successCallback, function() {}, function() {}, function() {});
322 }; 334 };
323 335
324 /** 336 /**
325 * Run scan on the current DirectoryContents. The active fileList is cleared and 337 * Run scan on the current DirectoryContents. The active fileList is cleared and
326 * the entries are added directly. 338 * the entries are added directly.
327 * 339 *
328 * This should be used when changing directory or initiating a new search. 340 * This should be used when changing directory or initiating a new search.
329 * 341 *
330 * @param {DirectoryContentes} newDirContents New DirectoryContents instance to 342 * @param {DirectoryContentes} newDirContents New DirectoryContents instance to
331 * replace currentDirContents_. 343 * replace currentDirContents_.
332 * @param {function()=} opt_callback Called on success. 344 * @param {function(boolean)} callback Callback with result. True if the scan
345 * is completed successfully, false if the scan is failed.
333 * @private 346 * @private
334 */ 347 */
335 DirectoryModel.prototype.clearAndScan_ = function(newDirContents, 348 DirectoryModel.prototype.clearAndScan_ = function(newDirContents,
336 opt_callback) { 349 callback) {
337 if (this.currentDirContents_.isScanning()) 350 if (this.currentDirContents_.isScanning())
338 this.currentDirContents_.cancelScan(); 351 this.currentDirContents_.cancelScan();
339 this.currentDirContents_ = newDirContents; 352 this.currentDirContents_ = newDirContents;
340 this.clearRescanTimeout_(); 353 this.clearRescanTimeout_();
341 354
342 if (this.pendingScan_) 355 if (this.pendingScan_)
343 this.pendingScan_ = false; 356 this.pendingScan_ = false;
344 357
345 if (this.runningScan_) { 358 if (this.runningScan_) {
346 if (this.runningScan_.isScanning()) 359 if (this.runningScan_.isScanning())
347 this.runningScan_.cancelScan(); 360 this.runningScan_.cancelScan();
348 this.runningScan_ = null; 361 this.runningScan_ = null;
349 } 362 }
350 363
364 var sequence = this.changeDirectorySequence_;
365 var cancelled = false;
366
351 var onDone = function() { 367 var onDone = function() {
368 if (cancelled)
369 return;
370
352 cr.dispatchSimpleEvent(this, 'scan-completed'); 371 cr.dispatchSimpleEvent(this, 'scan-completed');
353 if (opt_callback) 372 callback(true);
354 opt_callback();
355 }.bind(this); 373 }.bind(this);
356 374
357 var onFailed = function() { 375 var onFailed = function() {
376 if (cancelled)
377 return;
378
358 cr.dispatchSimpleEvent(this, 'scan-failed'); 379 cr.dispatchSimpleEvent(this, 'scan-failed');
380 callback(false);
359 }.bind(this); 381 }.bind(this);
360 382
361 var onUpdated = function() { 383 var onUpdated = function() {
384 if (cancelled)
385 return;
386
387 if (this.changeDirectorySequence_ !== sequence) {
388 cancelled = true;
389 cr.dispatchSimpleEvent(this, 'scan-cancelled');
390 callback(false);
391 return;
392 }
393
362 cr.dispatchSimpleEvent(this, 'scan-updated'); 394 cr.dispatchSimpleEvent(this, 'scan-updated');
363 }.bind(this); 395 }.bind(this);
364 396
365 var onCancelled = function() { 397 var onCancelled = function() {
398 if (cancelled)
399 return;
400
401 cancelled = true;
366 cr.dispatchSimpleEvent(this, 'scan-cancelled'); 402 cr.dispatchSimpleEvent(this, 'scan-cancelled');
403 callback(false);
367 }.bind(this); 404 }.bind(this);
368 405
369 // Clear the table, and start scanning. 406 // Clear the table, and start scanning.
370 cr.dispatchSimpleEvent(this, 'scan-started'); 407 cr.dispatchSimpleEvent(this, 'scan-started');
371 var fileList = this.getFileList(); 408 var fileList = this.getFileList();
372 fileList.splice(0, fileList.length); 409 fileList.splice(0, fileList.length);
373 this.scan_(this.currentDirContents_, 410 this.scan_(this.currentDirContents_,
374 onDone, onFailed, onUpdated, onCancelled); 411 onDone, onFailed, onUpdated, onCancelled);
375 }; 412 };
376 413
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
595 errorCallback, 632 errorCallback,
596 abortCallback) { 633 abortCallback) {
597 // Obtain and check the current directory. 634 // Obtain and check the current directory.
598 var entry = this.getCurrentDirEntry(); 635 var entry = this.getCurrentDirEntry();
599 if (!entry || this.isSearching()) { 636 if (!entry || this.isSearching()) {
600 errorCallback(util.createDOMError( 637 errorCallback(util.createDOMError(
601 util.FileError.INVALID_MODIFICATION_ERR)); 638 util.FileError.INVALID_MODIFICATION_ERR));
602 return; 639 return;
603 } 640 }
604 641
605 var tracker = this.createDirectoryChangeTracker(); 642 var sequence = this.changeDirectorySequence_;
606 tracker.start();
607 643
608 new Promise(entry.getDirectory.bind( 644 new Promise(entry.getDirectory.bind(
609 entry, name, {create: true, exclusive: true})). 645 entry, name, {create: true, exclusive: true})).
610 646
611 then(function(newEntry) { 647 then(function(newEntry) {
612 // Refresh the cache. 648 // Refresh the cache.
613 this.metadataCache_.clear([newEntry], '*'); 649 this.metadataCache_.clear([newEntry], '*');
614 return new Promise(function(onFulfilled, onRejected) { 650 return new Promise(function(onFulfilled, onRejected) {
615 this.metadataCache_.get([newEntry], 651 this.metadataCache_.get([newEntry],
616 'filesystem', 652 'filesystem',
617 onFulfilled.bind(null, newEntry)); 653 onFulfilled.bind(null, newEntry));
618 }.bind(this)); 654 }.bind(this));
619 }.bind(this)). 655 }.bind(this)).
620 656
621 then(function(newEntry) { 657 then(function(newEntry) {
622 // Do not change anything or call the callback if current 658 // Do not change anything or call the callback if current
623 // directory changed. 659 // directory changed.
624 tracker.stop(); 660 if (this.changeDirectorySequence_ !== sequence) {
625 if (tracker.hasChanged) {
626 abortCallback(); 661 abortCallback();
627 return; 662 return;
628 } 663 }
629 664
630 // If target directory is already in the list, just select it. 665 // If target directory is already in the list, just select it.
631 var existing = this.getFileList().slice().filter( 666 var existing = this.getFileList().slice().filter(
632 function(e) { return e.name === name; }); 667 function(e) { return e.name === name; });
633 if (existing.length) { 668 if (existing.length) {
634 this.selectEntry(newEntry); 669 this.selectEntry(newEntry);
635 successCallback(existing[0]); 670 successCallback(existing[0]);
636 } else { 671 } else {
637 this.fileListSelection_.beginChange(); 672 this.fileListSelection_.beginChange();
638 this.getFileList().splice(0, 0, newEntry); 673 this.getFileList().splice(0, 0, newEntry);
639 this.selectEntry(newEntry); 674 this.selectEntry(newEntry);
640 this.fileListSelection_.endChange(); 675 this.fileListSelection_.endChange();
641 successCallback(newEntry); 676 successCallback(newEntry);
642 } 677 }
643 }.bind(this), function(reason) { 678 }.bind(this), function(reason) {
644 tracker.stop(); 679 tracker.stop();
hirono 2014/06/02 11:52:07 ditto.
yoshiki 2014/06/04 13:46:41 Done.
645 errorCallback(reason); 680 errorCallback(reason);
646 }); 681 });
647 }; 682 };
648 683
649 /** 684 /**
650 * Changes the current directory to the directory represented by 685 * Changes the current directory to the directory represented by
651 * a DirectoryEntry or a fake entry. 686 * a DirectoryEntry or a fake entry.
652 * 687 *
653 * Dispatches the 'directory-changed' event when the directory is successfully 688 * Dispatches the 'directory-changed' event when the directory is successfully
654 * changed. 689 * changed.
655 * 690 *
656 * Note : if this is called from UI, please consider to use DirectoryModel. 691 * Note : if this is called from UI, please consider to use DirectoryModel.
657 * activateDirectoryEntry instead of this, which is higher-level function and 692 * activateDirectoryEntry instead of this, which is higher-level function and
658 * cares about the selection. 693 * cares about the selection.
659 * 694 *
660 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to 695 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to
661 * be opened. 696 * be opened.
662 * @param {function()=} opt_callback Executed if the directory loads 697 * @param {function()=} opt_callback Executed if the directory loads
663 * successfully. 698 * successfully.
664 */ 699 */
665 DirectoryModel.prototype.changeDirectoryEntry = function( 700 DirectoryModel.prototype.changeDirectoryEntry = function(
666 dirEntry, opt_callback) { 701 dirEntry, opt_callback) {
667 // Increment the sequence value. 702 // Increment the sequence value.
668 this.changeDirectorySequence_++; 703 this.changeDirectorySequence_++;
669 this.clearSearch_(); 704 this.clearSearch_();
670 705
671 var promise = new Promise( 706 this.directoryChangeQueue_.run(function(sequence, queueTaskCallback) {
672 function(onFulfilled, onRejected) { 707 this.fileWatcher_.changeWatchedDirectory(
673 this.fileWatcher_.changeWatchedDirectory(dirEntry, onFulfilled); 708 dirEntry,
674 }.bind(this)). 709 function() {
675 710 if (this.changeDirectorySequence_ !== sequence) {
676 then(function(sequence) { 711 callback();
677 return new Promise(function(onFulfilled, onRejected) {
678 if (this.changeDirectorySequence_ !== sequence)
679 return; 712 return;
713 }
680 714
681 var newDirectoryContents = this.createDirectoryContents_( 715 var newDirectoryContents = this.createDirectoryContents_(
682 this.currentFileListContext_, dirEntry, ''); 716 this.currentFileListContext_, dirEntry, '');
683 if (!newDirectoryContents) 717 if (!newDirectoryContents) {
718 callback();
684 return; 719 return;
720 }
685 721
686 var previousDirEntry = this.currentDirContents_.getDirectoryEntry(); 722 var previousDirEntry =
687 this.clearAndScan_(newDirectoryContents, opt_callback); 723 this.currentDirContents_.getDirectoryEntry();
724 this.clearAndScan_(
725 newDirectoryContents,
726 function(result) {
727 // Calls the callback of the method when successful.
728 if (result && opt_callback)
729 opt_callback();
688 730
689 // For tests that open the dialog to empty directories, everything is 731 // Notify that the current task of this.directoryChangeQueue_
690 // loaded at this point. 732 // is completed.
733 setTimeout(queueTaskCallback);
734 });
735
736 // For tests that open the dialog to empty directories, everything
737 // is loaded at this point.
691 util.testSendMessage('directory-change-complete'); 738 util.testSendMessage('directory-change-complete');
692 739
693 var event = new Event('directory-changed'); 740 var event = new Event('directory-changed');
694 event.previousDirEntry = previousDirEntry; 741 event.previousDirEntry = previousDirEntry;
695 event.newDirEntry = dirEntry; 742 event.newDirEntry = dirEntry;
696 this.dispatchEvent(event); 743 this.dispatchEvent(event);
697 }.bind(this)); 744 }.bind(this));
698 }.bind(this, this.changeDirectorySequence_)); 745 }.bind(this, this.changeDirectorySequence_));
699 }; 746 };
700 747
701 /** 748 /**
702 * Activates the given directry. 749 * Activates the given directry.
703 * This method: 750 * This method:
704 * - Changes the current directory, if the given directory is the current 751 * - Changes the current directory, if the given directory is the current
705 * directory. 752 * directory.
706 * - Clears the selection, if the given directory is the current directory. 753 * - Clears the selection, if the given directory is the current directory.
707 * 754 *
708 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to 755 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
896 DirectoryModel.prototype.search = function(query, 943 DirectoryModel.prototype.search = function(query,
897 onSearchRescan, 944 onSearchRescan,
898 onClearSearch) { 945 onClearSearch) {
899 this.clearSearch_(); 946 this.clearSearch_();
900 var currentDirEntry = this.getCurrentDirEntry(); 947 var currentDirEntry = this.getCurrentDirEntry();
901 if (!currentDirEntry) { 948 if (!currentDirEntry) {
902 // Not yet initialized. Do nothing. 949 // Not yet initialized. Do nothing.
903 return; 950 return;
904 } 951 }
905 952
906 if (!(query || '').trimLeft()) { 953 this.changeDirectorySequence_++;
907 if (this.isSearching()) { 954 this.directoryChangeQueue_.run(function(sequence, callback) {
908 var newDirContents = this.createDirectoryContents_( 955 if (this.changeDirectorySequence_ !== sequence) {
909 this.currentFileListContext_, 956 callback();
910 currentDirEntry); 957 return;
911 this.clearAndScan_(newDirContents);
912 } 958 }
913 return;
914 }
915 959
916 var newDirContents = this.createDirectoryContents_( 960 if (!(query || '').trimLeft()) {
917 this.currentFileListContext_, currentDirEntry, query); 961 if (this.isSearching()) {
918 if (!newDirContents) 962 var newDirContents = this.createDirectoryContents_(
919 return; 963 this.currentFileListContext_,
964 currentDirEntry);
965 this.clearAndScan_(newDirContents,
966 sequence,
967 callback);
968 } else {
969 callback();
970 }
971 return;
972 }
920 973
921 this.onSearchCompleted_ = onSearchRescan; 974 var newDirContents = this.createDirectoryContents_(
922 this.onClearSearch_ = onClearSearch; 975 this.currentFileListContext_, currentDirEntry, query);
923 this.addEventListener('scan-completed', this.onSearchCompleted_); 976 if (!newDirContents) {
924 this.clearAndScan_(newDirContents); 977 callback();
978 return;
979 }
980
981 this.onSearchCompleted_ = onSearchRescan;
982 this.onClearSearch_ = onClearSearch;
983 this.addEventListener('scan-completed', this.onSearchCompleted_);
984 this.clearAndScan_(newDirContents,
985 sequence,
986 callback);
987 }.bind(this, this.changeDirectorySequence_));
925 }; 988 };
926 989
927 /** 990 /**
928 * In case the search was active, remove listeners and send notifications on 991 * In case the search was active, remove listeners and send notifications on
929 * its canceling. 992 * its canceling.
930 * @private 993 * @private
931 */ 994 */
932 DirectoryModel.prototype.clearSearch_ = function() { 995 DirectoryModel.prototype.clearSearch_ = function() {
933 if (!this.isSearching()) 996 if (!this.isSearching())
934 return; 997 return;
935 998
936 if (this.onSearchCompleted_) { 999 if (this.onSearchCompleted_) {
937 this.removeEventListener('scan-completed', this.onSearchCompleted_); 1000 this.removeEventListener('scan-completed', this.onSearchCompleted_);
938 this.onSearchCompleted_ = null; 1001 this.onSearchCompleted_ = null;
939 } 1002 }
940 1003
941 if (this.onClearSearch_) { 1004 if (this.onClearSearch_) {
942 this.onClearSearch_(); 1005 this.onClearSearch_();
943 this.onClearSearch_ = null; 1006 this.onClearSearch_ = null;
944 } 1007 }
945 }; 1008 };
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698