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 // 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 66 }; | 66 }; |
| 67 | 67 |
| 68 /** | 68 /** |
| 69 * @return {cr.ui.ArrayDataModel} Files in the current directory. | 69 * @return {cr.ui.ArrayDataModel} Files in the current directory. |
| 70 */ | 70 */ |
| 71 DirectoryModel.prototype.getFileList = function() { | 71 DirectoryModel.prototype.getFileList = function() { |
| 72 return this.currentFileListContext_.fileList; | 72 return this.currentFileListContext_.fileList; |
| 73 }; | 73 }; |
| 74 | 74 |
| 75 /** | 75 /** |
| 76 * Sort the file list. | |
| 77 * @param {string} sortField Sort field. | |
| 78 * @param {string} sortDirection "asc" or "desc". | |
| 79 */ | |
| 80 DirectoryModel.prototype.sortFileList = function(sortField, sortDirection) { | |
| 81 this.getFileList().sort(sortField, sortDirection); | |
| 82 }; | |
| 83 | |
| 84 /** | |
| 85 * @return {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} Selection | 76 * @return {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} Selection |
| 86 * in the fileList. | 77 * in the fileList. |
| 87 */ | 78 */ |
| 88 DirectoryModel.prototype.getFileListSelection = function() { | 79 DirectoryModel.prototype.getFileListSelection = function() { |
| 89 return this.fileListSelection_; | 80 return this.fileListSelection_; |
| 90 }; | 81 }; |
| 91 | 82 |
| 92 /** | 83 /** |
| 93 * @return {?RootType} Root type of current root, or null if not found. | 84 * @return {?RootType} Root type of current root, or null if not found. |
| 94 */ | 85 */ |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 188 }; | 179 }; |
| 189 | 180 |
| 190 /** | 181 /** |
| 191 * @return {DirectoryEntry} Current directory. | 182 * @return {DirectoryEntry} Current directory. |
| 192 */ | 183 */ |
| 193 DirectoryModel.prototype.getCurrentDirEntry = function() { | 184 DirectoryModel.prototype.getCurrentDirEntry = function() { |
| 194 return this.currentDirContents_.getDirectoryEntry(); | 185 return this.currentDirContents_.getDirectoryEntry(); |
| 195 }; | 186 }; |
| 196 | 187 |
| 197 /** | 188 /** |
| 198 * @return {Array.<Entry>} Array of selected entries.. | 189 * @return {Array.<Entry>} Array of selected entries. |
| 199 * @private | 190 * @private |
| 200 */ | 191 */ |
| 201 DirectoryModel.prototype.getSelectedEntries_ = function() { | 192 DirectoryModel.prototype.getSelectedEntries_ = function() { |
| 202 var indexes = this.fileListSelection_.selectedIndexes; | 193 var indexes = this.fileListSelection_.selectedIndexes; |
| 203 var fileList = this.getFileList(); | 194 var fileList = this.getFileList(); |
| 204 if (fileList) { | 195 if (fileList) { |
| 205 return indexes.map(function(i) { | 196 return indexes.map(function(i) { |
| 206 return fileList.item(i); | 197 return fileList.item(i); |
| 207 }); | 198 }); |
| 208 } | 199 } |
| (...skipping 427 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 636 this.fileListSelection_.endChange(); | 627 this.fileListSelection_.endChange(); |
| 637 successCallback(newEntry); | 628 successCallback(newEntry); |
| 638 } | 629 } |
| 639 }.bind(this), function(reason) { | 630 }.bind(this), function(reason) { |
| 640 tracker.stop(); | 631 tracker.stop(); |
| 641 errorCallback(reason); | 632 errorCallback(reason); |
| 642 }); | 633 }); |
| 643 }; | 634 }; |
| 644 | 635 |
| 645 /** | 636 /** |
| 646 * @param {DirectoryEntry} dirEntry The entry of the new directory. | |
| 647 * @param {function()=} opt_callback Executed if the directory loads | |
| 648 * successfully. | |
| 649 * @private | |
| 650 */ | |
| 651 DirectoryModel.prototype.changeDirectoryEntrySilent_ = function(dirEntry, | |
| 652 opt_callback) { | |
| 653 var onScanComplete = function() { | |
| 654 if (opt_callback) | |
| 655 opt_callback(); | |
| 656 // For tests that open the dialog to empty directories, everything | |
| 657 // is loaded at this point. | |
| 658 chrome.test.sendMessage('directory-change-complete'); | |
| 659 }; | |
| 660 this.clearAndScan_( | |
| 661 DirectoryContents.createForDirectory(this.currentFileListContext_, | |
| 662 dirEntry), | |
| 663 onScanComplete.bind(this)); | |
| 664 }; | |
| 665 | |
| 666 /** | |
| 667 * Change the current directory to the directory represented by | 637 * Change the current directory to the directory represented by |
| 668 * a DirectoryEntry or a fake entry. | 638 * a DirectoryEntry or a fake entry. |
| 669 * | 639 * |
| 670 * Dispatches the 'directory-changed' event when the directory is successfully | 640 * Dispatches the 'directory-changed' event when the directory is successfully |
| 671 * changed. | 641 * changed. |
| 672 * | 642 * |
| 673 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to | 643 * @param {DirectoryEntry|Object} dirEntry The entry of the new directory to |
| 674 * be opened. | 644 * be opened. |
| 675 * @param {function()=} opt_callback Executed if the directory loads | 645 * @param {function()=} opt_callback Executed if the directory loads |
| 676 * successfully. | 646 * successfully. |
| 677 */ | 647 */ |
| 678 DirectoryModel.prototype.changeDirectoryEntry = function( | 648 DirectoryModel.prototype.changeDirectoryEntry = function( |
| 679 dirEntry, opt_callback) { | 649 dirEntry, opt_callback) { |
| 680 if (util.isFakeEntry(dirEntry)) { | |
| 681 this.specialSearch(dirEntry); | |
| 682 if (opt_callback) | |
| 683 opt_callback(); | |
| 684 return; | |
| 685 } | |
| 686 | |
| 687 // Increment the sequence value. | 650 // Increment the sequence value. |
| 688 this.changeDirectorySequence_++; | 651 this.changeDirectorySequence_++; |
| 652 this.clearSearch_(); | |
| 689 | 653 |
| 690 this.fileWatcher_.changeWatchedDirectory(dirEntry, function(sequence) { | 654 var promise = new Promise( |
| 691 if (this.changeDirectorySequence_ !== sequence) | 655 function(onFulfilled, onRejected) { |
| 692 return; | 656 this.fileWatcher_.changeWatchedDirectory(dirEntry, onFulfilled); |
| 693 var previous = this.currentDirContents_.getDirectoryEntry(); | 657 }.bind(this)). |
| 694 this.clearSearch_(); | |
| 695 this.changeDirectoryEntrySilent_(dirEntry, opt_callback); | |
| 696 | 658 |
| 697 var e = new Event('directory-changed'); | 659 then(function(sequence) { |
| 698 e.previousDirEntry = previous; | 660 return new Promise(function(onFulfilled, onRejected) { |
| 699 e.newDirEntry = dirEntry; | 661 if (this.changeDirectorySequence_ !== sequence) |
| 700 this.dispatchEvent(e); | 662 return; |
| 701 }.bind(this, this.changeDirectorySequence_)); | 663 |
| 664 var newDirectoryContents = this.createDirectoryContents_( | |
| 665 this.currentFileListContext_, dirEntry, ''); | |
| 666 if (!newDirectoryContents) | |
| 667 return; | |
| 668 | |
| 669 var event = new Event('directory-changed'); | |
| 670 event.previousDirEntry = this.currentDirContents_.getDirectoryEntry(); | |
| 671 event.newDirEntry = dirEntry; | |
| 672 | |
| 673 this.clearAndScan_(newDirectoryContents, opt_callback); | |
| 674 this.dispatchEvent(event); | |
| 675 }.bind(this)); | |
| 676 }.bind(this, this.changeDirectorySequence_)); | |
| 702 }; | 677 }; |
| 703 | 678 |
| 704 /** | 679 /** |
| 705 * Creates an object which could say whether directory has changed while it has | 680 * Creates an object which could say whether directory has changed while it has |
| 706 * been active or not. Designed for long operations that should be cancelled | 681 * been active or not. Designed for long operations that should be cancelled |
| 707 * if the used change current directory. | 682 * if the used change current directory. |
| 708 * @return {Object} Created object. | 683 * @return {Object} Created object. |
| 709 */ | 684 */ |
| 710 DirectoryModel.prototype.createDirectoryChangeTracker = function() { | 685 DirectoryModel.prototype.createDirectoryChangeTracker = function() { |
| 711 var tracker = { | 686 var tracker = { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 773 DirectoryModel.prototype.selectIndex = function(index) { | 748 DirectoryModel.prototype.selectIndex = function(index) { |
| 774 // this.focusCurrentList_(); | 749 // this.focusCurrentList_(); |
| 775 if (index >= this.getFileList().length) | 750 if (index >= this.getFileList().length) |
| 776 return; | 751 return; |
| 777 | 752 |
| 778 // If a list bound with the model it will do scrollIndexIntoView(index). | 753 // If a list bound with the model it will do scrollIndexIntoView(index). |
| 779 this.fileListSelection_.selectedIndex = index; | 754 this.fileListSelection_.selectedIndex = index; |
| 780 }; | 755 }; |
| 781 | 756 |
| 782 /** | 757 /** |
| 783 * Called when VolumeInfoList is updated. | 758 * Handles update of VolumeInfoList. |
| 784 * @param {Event} event Event of VolumeInfoList's 'splice'. | 759 * @param {Event} event Event of VolumeInfoList's 'splice'. |
| 785 * @private | 760 * @private |
| 786 */ | 761 */ |
| 787 DirectoryModel.prototype.onVolumeInfoListUpdated_ = function(event) { | 762 DirectoryModel.prototype.onVolumeInfoListUpdated_ = function(event) { |
| 788 // When the volume where we are is unmounted, fallback to the default volume's | 763 // When the volume where we are is unmounted, fallback to the default volume's |
| 789 // root. If current directory path is empty, stop the fallback | 764 // root. If current directory path is empty, stop the fallback |
| 790 // since the current directory is initializing now. | 765 // since the current directory is initializing now. |
| 791 if (this.getCurrentDirEntry() && | 766 if (this.getCurrentDirEntry() && |
| 792 !this.volumeManager_.getVolumeInfo(this.getCurrentDirEntry())) { | 767 !this.volumeManager_.getVolumeInfo(this.getCurrentDirEntry())) { |
| 793 this.volumeManager_.getDefaultDisplayRoot(function(displayRoot) { | 768 this.volumeManager_.getDefaultDisplayRoot(function(displayRoot) { |
| 794 this.changeDirectoryEntry(displayRoot); | 769 this.changeDirectoryEntry(displayRoot); |
| 795 }.bind(this)); | 770 }.bind(this)); |
| 796 } | 771 } |
| 797 }; | 772 }; |
| 798 | 773 |
| 799 /** | 774 /** |
| 775 * Creates directory contents for the entry and query. | |
| 776 * | |
| 777 * @param {FileListContext} context File list context. | |
| 778 * @param {DirectoryEntry} entry Current directory. | |
| 779 * @param {opt_query} opt_query Search query string. | |
|
mtomasz
2014/02/04 02:20:12
nit: opt_query -> string=
hirono
2014/02/04 05:53:11
Done.
| |
| 780 * @return {DirectoryEntry} Directory contents. | |
|
mtomasz
2014/02/04 02:20:12
nit: DirectoryEntry -> DirectoryContents
hirono
2014/02/04 05:53:11
Done.
| |
| 781 * @private | |
| 782 */ | |
| 783 DirectoryModel.prototype.createDirectoryContents_ = | |
| 784 function(context, entry, opt_query) { | |
| 785 var query = (opt_query || '').trimLeft(); | |
| 786 var locationInfo = this.volumeManager_.getLocationInfo(entry); | |
| 787 if (!locationInfo) | |
| 788 return null; | |
| 789 var canUseDriveSearch = this.volumeManager_.getDriveConnectionState().type !== | |
| 790 util.DriveConnectionType.OFFLINE && | |
| 791 locationInfo.isDriveBased; | |
| 792 | |
| 793 if (query && canUseDriveSearch) { | |
| 794 // Drive search. | |
| 795 return DirectoryContents.createForDriveSearch(context, entry, query); | |
| 796 } else if (query) { | |
| 797 // Local search. | |
| 798 return DirectoryContents.createForLocalSearch(context, entry, query); | |
| 799 } if (util.isFakeEntry(entry)) { | |
|
mtomasz
2014/02/04 02:20:12
nit: isFakeEntry -> locationInfo.isSpecialSearchRo
hirono
2014/02/04 05:53:11
Done.
| |
| 800 // Drive special search. | |
| 801 var searchType; | |
| 802 switch (locationInfo.rootType) { | |
| 803 case RootType.DRIVE_OFFLINE: | |
| 804 searchType = | |
| 805 DriveMetadataSearchContentScanner.SearchType.SEARCH_OFFLINE; | |
| 806 break; | |
| 807 case RootType.DRIVE_SHARED_WITH_ME: | |
| 808 searchType = | |
| 809 DriveMetadataSearchContentScanner.SearchType.SEARCH_SHARED_WITH_ME; | |
| 810 break; | |
| 811 case RootType.DRIVE_RECENT: | |
| 812 searchType = | |
| 813 DriveMetadataSearchContentScanner.SearchType.SEARCH_RECENT_FILES; | |
| 814 break; | |
| 815 default: | |
| 816 // Unknown special search entry. | |
| 817 throw new Error('Unknown special search type.'); | |
| 818 } | |
| 819 return DirectoryContents.createForDriveMetadataSearch( | |
| 820 context, | |
| 821 entry, | |
| 822 searchType); | |
| 823 } else { | |
| 824 // Local fetch or search. | |
| 825 return DirectoryContents.createForDirectory(context, entry); | |
| 826 } | |
| 827 }; | |
| 828 | |
| 829 /** | |
| 800 * Performs search and displays results. The search type is dependent on the | 830 * Performs search and displays results. The search type is dependent on the |
| 801 * current directory. If we are currently on drive, server side content search | 831 * current directory. If we are currently on drive, server side content search |
| 802 * over drive mount point. If the current directory is not on the drive, file | 832 * over drive mount point. If the current directory is not on the drive, file |
| 803 * name search over current directory will be performed. | 833 * name search over current directory will be performed. |
| 804 * | 834 * |
| 805 * @param {string} query Query that will be searched for. | 835 * @param {string} query Query that will be searched for. |
| 806 * @param {function(Event)} onSearchRescan Function that will be called when the | 836 * @param {function(Event)} onSearchRescan Function that will be called when the |
| 807 * search directory is rescanned (i.e. search results are displayed). | 837 * search directory is rescanned (i.e. search results are displayed). |
| 808 * @param {function()} onClearSearch Function to be called when search state | 838 * @param {function()} onClearSearch Function to be called when search state |
| 809 * gets cleared. | 839 * gets cleared. |
| 810 * TODO(olege): Change callbacks to events. | 840 * TODO(olege): Change callbacks to events. |
| 811 */ | 841 */ |
| 812 DirectoryModel.prototype.search = function(query, | 842 DirectoryModel.prototype.search = function(query, |
| 813 onSearchRescan, | 843 onSearchRescan, |
| 814 onClearSearch) { | 844 onClearSearch) { |
| 815 query = query.trimLeft(); | |
| 816 | |
| 817 this.clearSearch_(); | 845 this.clearSearch_(); |
| 818 | |
| 819 var currentDirEntry = this.getCurrentDirEntry(); | 846 var currentDirEntry = this.getCurrentDirEntry(); |
| 820 if (!currentDirEntry) { | 847 if (!currentDirEntry) { |
| 821 // Not yet initialized. Do nothing. | 848 // Not yet initialized. Do nothing. |
| 822 return; | 849 return; |
| 823 } | 850 } |
| 824 | 851 |
| 825 if (!query) { | 852 if (!(query || '').trimLeft()) { |
| 826 if (this.isSearching()) { | 853 if (this.isSearching()) { |
| 827 var newDirContents = DirectoryContents.createForDirectory( | 854 var newDirContents = DirectoryContents.createForDirectory( |
| 828 this.currentFileListContext_, | 855 this.currentFileListContext_, |
| 829 this.currentDirContents_.getLastNonSearchDirectoryEntry()); | 856 currentDirEntry); |
| 830 this.clearAndScan_(newDirContents); | 857 this.clearAndScan_(newDirContents); |
| 831 } | 858 } |
| 832 return; | 859 return; |
| 833 } | 860 } |
| 834 | 861 |
| 862 var newDirContents = this.createDirectoryContents_( | |
| 863 this.currentFileListContext_, currentDirEntry, query); | |
| 864 if (!newDirContents) | |
| 865 return; | |
| 866 | |
| 835 this.onSearchCompleted_ = onSearchRescan; | 867 this.onSearchCompleted_ = onSearchRescan; |
| 836 this.onClearSearch_ = onClearSearch; | 868 this.onClearSearch_ = onClearSearch; |
| 837 | |
| 838 this.addEventListener('scan-completed', this.onSearchCompleted_); | 869 this.addEventListener('scan-completed', this.onSearchCompleted_); |
| 839 | |
| 840 // If we are offline, let's fallback to file name search inside dir. | |
| 841 // A search initiated from directories in Drive or special search results | |
| 842 // should trigger Drive search. | |
| 843 var newDirContents; | |
| 844 var isDriveOffline = this.volumeManager_.getDriveConnectionState().type === | |
| 845 util.DriveConnectionType.OFFLINE; | |
| 846 var locationInfo = this.volumeManager_.getLocationInfo(currentDirEntry); | |
| 847 if (!isDriveOffline && locationInfo && locationInfo.isDriveBased) { | |
| 848 // Drive search is performed over the whole drive, so pass drive root as | |
| 849 // |directoryEntry|. | |
| 850 newDirContents = DirectoryContents.createForDriveSearch( | |
| 851 this.currentFileListContext_, | |
| 852 currentDirEntry, | |
| 853 this.currentDirContents_.getLastNonSearchDirectoryEntry(), | |
| 854 query); | |
| 855 } else { | |
| 856 newDirContents = DirectoryContents.createForLocalSearch( | |
| 857 this.currentFileListContext_, currentDirEntry, query); | |
| 858 } | |
| 859 this.clearAndScan_(newDirContents); | 870 this.clearAndScan_(newDirContents); |
| 860 }; | 871 }; |
| 861 | 872 |
| 862 /** | 873 /** |
| 863 * Performs special search and displays results. e.g. Drive files available | |
| 864 * offline, shared-with-me files, recently modified files. | |
| 865 * @param {Object} fakeEntry Fake entry representing a special search. | |
| 866 * @param {string=} opt_query Query string used for the search. | |
| 867 */ | |
| 868 DirectoryModel.prototype.specialSearch = function(fakeEntry, opt_query) { | |
| 869 var query = opt_query || ''; | |
| 870 | |
| 871 // Increment the sequence value. | |
| 872 this.changeDirectorySequence_++; | |
| 873 | |
| 874 this.clearSearch_(); | |
| 875 this.onSearchCompleted_ = null; | |
| 876 this.onClearSearch_ = null; | |
| 877 | |
| 878 // Obtains a volume information. | |
| 879 // TODO(hirono): Obtain the proper profile's volume information. | |
| 880 var volumeInfo = this.volumeManager_.getCurrentProfileVolumeInfo( | |
| 881 util.VolumeType.DRIVE); | |
| 882 if (!volumeInfo) { | |
| 883 // It seems that the volume is already unmounted or drive is disable. | |
| 884 return; | |
| 885 } | |
| 886 | |
| 887 var onDriveDirectoryResolved = function(sequence, driveRoot) { | |
| 888 if (this.changeDirectorySequence_ !== sequence) | |
| 889 return; | |
| 890 | |
| 891 var locationInfo = this.volumeManager_.getLocationInfo(fakeEntry); | |
| 892 if (!locationInfo) | |
| 893 return; | |
| 894 | |
| 895 var searchOption; | |
| 896 switch (locationInfo.rootType) { | |
| 897 case RootType.DRIVE_OFFLINE: | |
| 898 searchOption = | |
| 899 DriveMetadataSearchContentScanner.SearchType.SEARCH_OFFLINE; | |
| 900 break; | |
| 901 case RootType.DRIVE_SHARED_WITH_ME: | |
| 902 searchOption = | |
| 903 DriveMetadataSearchContentScanner.SearchType.SEARCH_SHARED_WITH_ME; | |
| 904 break; | |
| 905 case RootType.DRIVE_RECENT: | |
| 906 searchOption = | |
| 907 DriveMetadataSearchContentScanner.SearchType.SEARCH_RECENT_FILES; | |
| 908 break; | |
| 909 default: | |
| 910 // Unknown special search entry. | |
| 911 throw new Error('Unknown special search type.'); | |
| 912 } | |
| 913 | |
| 914 var newDirContents = DirectoryContents.createForDriveMetadataSearch( | |
| 915 this.currentFileListContext_, | |
| 916 fakeEntry, driveRoot, query, searchOption); | |
| 917 var previous = this.currentDirContents_.getDirectoryEntry(); | |
| 918 this.clearAndScan_(newDirContents); | |
| 919 | |
| 920 var e = new Event('directory-changed'); | |
| 921 e.previousDirEntry = previous; | |
| 922 e.newDirEntry = fakeEntry; | |
| 923 this.dispatchEvent(e); | |
| 924 }.bind(this, this.changeDirectorySequence_); | |
| 925 | |
| 926 volumeInfo.resolveDisplayRoot( | |
| 927 onDriveDirectoryResolved /* success */, function() {} /* failed */); | |
| 928 }; | |
| 929 | |
| 930 /** | |
| 931 * In case the search was active, remove listeners and send notifications on | 874 * In case the search was active, remove listeners and send notifications on |
| 932 * its canceling. | 875 * its canceling. |
| 933 * @private | 876 * @private |
| 934 */ | 877 */ |
| 935 DirectoryModel.prototype.clearSearch_ = function() { | 878 DirectoryModel.prototype.clearSearch_ = function() { |
| 936 if (!this.isSearching()) | 879 if (!this.isSearching()) |
| 937 return; | 880 return; |
| 938 | 881 |
| 939 if (this.onSearchCompleted_) { | 882 if (this.onSearchCompleted_) { |
| 940 this.removeEventListener('scan-completed', this.onSearchCompleted_); | 883 this.removeEventListener('scan-completed', this.onSearchCompleted_); |
| 941 this.onSearchCompleted_ = null; | 884 this.onSearchCompleted_ = null; |
| 942 } | 885 } |
| 943 | 886 |
| 944 if (this.onClearSearch_) { | 887 if (this.onClearSearch_) { |
| 945 this.onClearSearch_(); | 888 this.onClearSearch_(); |
| 946 this.onClearSearch_ = null; | 889 this.onClearSearch_ = null; |
| 947 } | 890 } |
| 948 }; | 891 }; |
| OLD | NEW |