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 // If directory files changes too often, don't rescan directory more than once | 5 // If directory files changes too often, don't rescan directory more than once |
6 // per specified interval | 6 // per specified interval |
7 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; | 7 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; |
8 // Used for operations that require almost instant rescan. | 8 // Used for operations that require almost instant rescan. |
9 var SHORT_RESCAN_INTERVAL = 100; | 9 var SHORT_RESCAN_INTERVAL = 100; |
10 | 10 |
11 /** | 11 /** |
12 * Data model of the file manager. | 12 * Data model of the file manager. |
13 * | 13 * |
14 * @constructor | 14 * @constructor |
15 * @param {DirectoryEntry} root File system root. | 15 * @param {DirectoryEntry} root File system root. |
16 * @param {boolean} singleSelection True if only one file could be selected | 16 * @param {boolean} singleSelection True if only one file could be selected |
17 * at the time. | 17 * at the time. |
18 * @param {boolean} showGData Defines whether GData root should be should | 18 * @param {boolean} showGData Defines whether GData root should be should |
19 * (regardless of its mounts status). | 19 * (regardless of its mounts status). |
20 * @param {MetadataCache} metadataCache The metadata cache service. | 20 * @param {MetadataCache} metadataCache The metadata cache service. |
21 */ | 21 */ |
22 function DirectoryModel(root, singleSelection, showGData, metadataCache) { | 22 function DirectoryModel(root, singleSelection, showGData, |
23 metadataCache, volumeManager) { | |
23 this.root_ = root; | 24 this.root_ = root; |
24 this.metadataCache_ = metadataCache; | 25 this.metadataCache_ = metadataCache; |
25 this.fileList_ = new cr.ui.ArrayDataModel([]); | 26 this.fileList_ = new cr.ui.ArrayDataModel([]); |
26 this.fileListSelection_ = singleSelection ? | 27 this.fileListSelection_ = singleSelection ? |
27 new cr.ui.ListSingleSelectionModel() : new cr.ui.ListSelectionModel(); | 28 new cr.ui.ListSingleSelectionModel() : new cr.ui.ListSelectionModel(); |
28 | 29 |
29 this.showGData_ = showGData; | 30 this.showGData_ = showGData; |
30 | 31 |
31 this.runningScan_ = null; | 32 this.runningScan_ = null; |
32 this.pendingScan_ = null; | 33 this.pendingScan_ = null; |
(...skipping 14 matching lines...) Expand all Loading... | |
47 * A map root.fullPath -> currentDirectory.fullPath. | 48 * A map root.fullPath -> currentDirectory.fullPath. |
48 * @private | 49 * @private |
49 * @type {Object.<string, string>} | 50 * @type {Object.<string, string>} |
50 */ | 51 */ |
51 this.currentDirByRoot_ = {}; | 52 this.currentDirByRoot_ = {}; |
52 | 53 |
53 // The map 'name' -> callback. Callbacks are function(entry) -> boolean. | 54 // The map 'name' -> callback. Callbacks are function(entry) -> boolean. |
54 this.filters_ = {}; | 55 this.filters_ = {}; |
55 this.setFilterHidden(true); | 56 this.setFilterHidden(true); |
56 | 57 |
57 /** | 58 if (this.showGData_) { |
58 * @private | 59 this.unmountedGDataEntry_ = { |
59 * @type {Object.<string, boolean>} | 60 unmouted: true, |
60 */ | 61 fullPath: '/' + DirectoryModel.GDATA_DIRECTORY |
61 this.volumeReadOnlyStatus_ = {}; | 62 }; |
63 } | |
64 | |
65 this.volumeManager_ = volumeManager; | |
66 var volumesChangeHandler = this.onMountChanged_.bind(this); | |
67 this.updateRoots(function() { | |
68 volumeManager.addEventListener('change', volumesChangeHandler); | |
69 }); | |
62 } | 70 } |
63 | 71 |
64 /** | 72 /** |
65 * The name of the directory containing externally | 73 * The name of the directory containing externally |
66 * mounted removable storage volumes. | 74 * mounted removable storage volumes. |
67 */ | 75 */ |
68 DirectoryModel.REMOVABLE_DIRECTORY = 'removable'; | 76 DirectoryModel.REMOVABLE_DIRECTORY = 'removable'; |
69 | 77 |
70 /** | 78 /** |
71 * The name of the directory containing externally | 79 * The name of the directory containing externally |
(...skipping 15 matching lines...) Expand all Loading... | |
87 /** | 95 /** |
88 * The name of the downloads directory. | 96 * The name of the downloads directory. |
89 */ | 97 */ |
90 DirectoryModel.DOWNLOADS_DIRECTORY = 'Downloads'; | 98 DirectoryModel.DOWNLOADS_DIRECTORY = 'Downloads'; |
91 | 99 |
92 /** | 100 /** |
93 * The name of the gdata provider directory. | 101 * The name of the gdata provider directory. |
94 */ | 102 */ |
95 DirectoryModel.GDATA_DIRECTORY = 'drive'; | 103 DirectoryModel.GDATA_DIRECTORY = 'drive'; |
96 | 104 |
105 DirectoryModel.fakeGDataEntry_ = { | |
106 fullPath: '/' + DirectoryModel.GDATA_DIRECTORY | |
107 }; | |
108 | |
97 /** | 109 /** |
98 * DirectoryModel extends cr.EventTarget. | 110 * DirectoryModel extends cr.EventTarget. |
99 */ | 111 */ |
100 DirectoryModel.prototype.__proto__ = cr.EventTarget.prototype; | 112 DirectoryModel.prototype.__proto__ = cr.EventTarget.prototype; |
101 | 113 |
102 /** | 114 /** |
103 * @return {cr.ui.ArrayDataModel} Files in the current directory. | 115 * @return {cr.ui.ArrayDataModel} Files in the current directory. |
104 */ | 116 */ |
105 DirectoryModel.prototype.getFileList = function() { | 117 DirectoryModel.prototype.getFileList = function() { |
106 return this.fileList_; | 118 return this.fileList_; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
165 return this.isPathReadOnly(this.getCurrentRootPath()); | 177 return this.isPathReadOnly(this.getCurrentRootPath()); |
166 }; | 178 }; |
167 | 179 |
168 /** | 180 /** |
169 * @param {string} path Path to check. | 181 * @param {string} path Path to check. |
170 * @return {boolean} True if the |path| is read only. | 182 * @return {boolean} True if the |path| is read only. |
171 */ | 183 */ |
172 DirectoryModel.prototype.isPathReadOnly = function(path) { | 184 DirectoryModel.prototype.isPathReadOnly = function(path) { |
173 switch (DirectoryModel.getRootType(path)) { | 185 switch (DirectoryModel.getRootType(path)) { |
174 case DirectoryModel.RootType.REMOVABLE: | 186 case DirectoryModel.RootType.REMOVABLE: |
175 return !!this.volumeReadOnlyStatus_[DirectoryModel.getRootPath(path)]; | 187 return !!this.volumeManager_.isReadOnly(DirectoryModel.getRootPath(path)); |
176 case DirectoryModel.RootType.ARCHIVE: | 188 case DirectoryModel.RootType.ARCHIVE: |
177 return true; | 189 return true; |
178 case DirectoryModel.RootType.DOWNLOADS: | 190 case DirectoryModel.RootType.DOWNLOADS: |
179 return false; | 191 return false; |
180 case DirectoryModel.RootType.GDATA: | 192 case DirectoryModel.RootType.GDATA: |
181 return this.isOffline(); | 193 return this.isOffline(); |
182 default: | 194 default: |
183 return true; | 195 return true; |
184 } | 196 } |
185 }; | 197 }; |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
306 }; | 318 }; |
307 | 319 |
308 /** | 320 /** |
309 * @return {string} Root path for the current directory (parent directory is | 321 * @return {string} Root path for the current directory (parent directory is |
310 * not navigatable for the user). | 322 * not navigatable for the user). |
311 */ | 323 */ |
312 DirectoryModel.prototype.getCurrentRootPath = function() { | 324 DirectoryModel.prototype.getCurrentRootPath = function() { |
313 return DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); | 325 return DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); |
314 }; | 326 }; |
315 | 327 |
328 DirectoryModel.prototype.getCurrentRootType = function() { | |
329 return DirectoryModel.getRootType(this.currentDirEntry_.fullPath); | |
330 }; | |
331 | |
316 /** | 332 /** |
317 * @return {cr.ui.ListSingleSelectionModel} Root list selection model. | 333 * @return {cr.ui.ListSingleSelectionModel} Root list selection model. |
318 */ | 334 */ |
319 DirectoryModel.prototype.getRootsListSelectionModel = function() { | 335 DirectoryModel.prototype.getRootsListSelectionModel = function() { |
320 return this.rootsListSelection_; | 336 return this.rootsListSelection_; |
321 }; | 337 }; |
322 | 338 |
323 /** | 339 /** |
324 * Add a filter for directory contents. | 340 * Add a filter for directory contents. |
325 * @param {string} name An identifier of the filter (used when removing it). | 341 * @param {string} name An identifier of the filter (used when removing it). |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
481 } | 497 } |
482 | 498 |
483 var onDone = function() { | 499 var onDone = function() { |
484 cr.dispatchSimpleEvent(this, 'scan-completed'); | 500 cr.dispatchSimpleEvent(this, 'scan-completed'); |
485 callback(); | 501 callback(); |
486 }.bind(this); | 502 }.bind(this); |
487 | 503 |
488 // Clear the table first. | 504 // Clear the table first. |
489 this.fileList_.splice(0, this.fileList_.length); | 505 this.fileList_.splice(0, this.fileList_.length); |
490 cr.dispatchSimpleEvent(this, 'scan-started'); | 506 cr.dispatchSimpleEvent(this, 'scan-started'); |
491 if (this.currentDirEntry_ == this.unmountedGDataEntry_) { | 507 if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) { |
492 onDone(); | 508 onDone(); |
493 return; | 509 return; |
494 } | 510 } |
495 this.runningScan_ = this.createScanner_(this.fileList_, onDone); | 511 this.runningScan_ = this.createScanner_(this.fileList_, onDone); |
496 this.runningScan_.run(); | 512 this.runningScan_.run(); |
497 }; | 513 }; |
498 | 514 |
499 /** | 515 /** |
500 * @private | 516 * @private |
501 * @param {Array.<Entry>} entries Files. | 517 * @param {Array.<Entry>} entries Files. |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
684 }; | 700 }; |
685 | 701 |
686 /** | 702 /** |
687 * Resolves absolute directory path. Handles GData stub. | 703 * Resolves absolute directory path. Handles GData stub. |
688 * @param {string} path Path to the directory. | 704 * @param {string} path Path to the directory. |
689 * @param {function(DirectoryEntry} successCallback Success callback. | 705 * @param {function(DirectoryEntry} successCallback Success callback. |
690 * @param {function(FileError} errorCallback Error callback. | 706 * @param {function(FileError} errorCallback Error callback. |
691 */ | 707 */ |
692 DirectoryModel.prototype.resolveDirectory = function(path, successCallback, | 708 DirectoryModel.prototype.resolveDirectory = function(path, successCallback, |
693 errorCallback) { | 709 errorCallback) { |
694 if (this.unmountedGDataEntry_ && | 710 if (DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) { |
695 DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) { | |
696 // TODO(kaznacheeev): Currently if path points to some GData subdirectory | 711 // TODO(kaznacheeev): Currently if path points to some GData subdirectory |
697 // and GData is not mounted we will change to the fake GData root and | 712 // and GData is not mounted we will change to the fake GData root and |
698 // ignore the rest of the path. Consider remembering the path and | 713 // ignore the rest of the path. Consider remembering the path and |
699 // changing to it once GDdata is mounted. This is only relevant for cases | 714 // changing to it once GDdata is mounted. This is only relevant for cases |
700 // when we open the File Manager with an URL pointing to GData (e.g. via | 715 // when we open the File Manager with an URL pointing to GData (e.g. via |
701 // a bookmark). | 716 // a bookmark). |
702 successCallback(this.unmountedGDataEntry_); | 717 if (!this.isGDataMounted_()) { |
703 return; | 718 if (path == DirectoryModel.fakeGDataEntry_.fullPath) |
719 successCallback(DirectoryModel.fakeGDataEntry_); | |
720 else // Subdirectory. | |
721 errorCallback({ code: FileError.NOT_FOUND_ERR }); | |
722 return; | |
723 } | |
704 } | 724 } |
705 | 725 |
706 if (path == '/') { | 726 if (path == '/') { |
707 successCallback(this.root_); | 727 successCallback(this.root_); |
708 return; | 728 return; |
709 } | 729 } |
710 | 730 |
711 this.root_.getDirectory(path, {create: false}, | 731 this.root_.getDirectory(path, {create: false}, |
712 successCallback, errorCallback); | 732 successCallback, errorCallback); |
713 }; | 733 }; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
751 * changed. | 771 * changed. |
752 * | 772 * |
753 * @private | 773 * @private |
754 * @param {boolean} initial True if it comes from setupPath and | 774 * @param {boolean} initial True if it comes from setupPath and |
755 * false if caused by an user action. | 775 * false if caused by an user action. |
756 * @param {DirectoryEntry} dirEntry The absolute path to the new directory. | 776 * @param {DirectoryEntry} dirEntry The absolute path to the new directory. |
757 * @param {function} opt_callback Executed if the directory loads successfully. | 777 * @param {function} opt_callback Executed if the directory loads successfully. |
758 */ | 778 */ |
759 DirectoryModel.prototype.changeDirectoryEntry_ = function(initial, dirEntry, | 779 DirectoryModel.prototype.changeDirectoryEntry_ = function(initial, dirEntry, |
760 opt_callback) { | 780 opt_callback) { |
781 if (dirEntry == DirectoryModel.fakeGDataEntry_) | |
782 this.volumeManager_.mountGData(function() {}, function() {}); | |
783 | |
761 var previous = this.currentDirEntry_; | 784 var previous = this.currentDirEntry_; |
762 this.currentDirEntry_ = dirEntry; | 785 this.currentDirEntry_ = dirEntry; |
763 function onRescanComplete() { | 786 function onRescanComplete() { |
764 if (opt_callback) | 787 if (opt_callback) |
765 opt_callback(); | 788 opt_callback(); |
766 // For tests that open the dialog to empty directories, everything | 789 // For tests that open the dialog to empty directories, everything |
767 // is loaded at this point. | 790 // is loaded at this point. |
768 chrome.test.sendMessage('directory-change-complete'); | 791 chrome.test.sendMessage('directory-change-complete'); |
769 } | 792 } |
770 this.updateRootsListSelection_(); | 793 this.updateRootsListSelection_(); |
771 this.scan_(onRescanComplete); | 794 this.scan_(onRescanComplete); |
772 this.currentDirByRoot_[this.getCurrentRootPath()] = dirEntry.fullPath; | 795 this.currentDirByRoot_[this.getCurrentRootPath()] = dirEntry.fullPath; |
773 | 796 |
774 var e = new cr.Event('directory-changed'); | 797 var e = new cr.Event('directory-changed'); |
775 e.previousDirEntry = previous; | 798 e.previousDirEntry = previous; |
776 e.newDirEntry = dirEntry; | 799 e.newDirEntry = dirEntry; |
777 e.initial = initial; | 800 e.initial = initial; |
778 this.dispatchEvent(e); | 801 this.dispatchEvent(e); |
779 }; | 802 }; |
780 | 803 |
781 /** | 804 /** |
805 * Creates an object wich could cay wether directory has changed while it has | |
dgozman
2012/05/16 14:31:46
typo: cay -> say
| |
806 * been active or not. Designed for long operations that should be canncelled | |
807 * if the used change current directory. | |
dgozman
2012/05/16 14:31:46
typo: used -> user
| |
808 * @return {Object} Created object. | |
809 */ | |
810 DirectoryModel.prototype.createDirectoryChangeTracker = function() { | |
811 var tracker = { | |
812 dm_: this, | |
813 active_: false, | |
814 hasChanged: false, | |
815 exceptInitialChange: false, | |
816 | |
817 start: function() { | |
818 if (!this.active_) { | |
819 this.dm_.addEventListener('directory-changed', | |
820 this.onDirectoryChange_); | |
821 this.active_ = true; | |
822 this.hasChanged = false; | |
823 } | |
824 }, | |
825 | |
826 stop: function() { | |
827 if (this.active_) { | |
828 this.dm_.removeEventListener('directory-changed', | |
829 this.onDirectoryChange_); | |
830 active_ = false; | |
831 } | |
832 }, | |
833 | |
834 onDirectoryChange_: function(event) { | |
835 if (tracker.exceptInitialChange && event.initial) | |
836 return; | |
837 // This is incorrect here. | |
dgozman
2012/05/16 14:31:46
I agree that this is incorrect. Fix?
| |
838 tracker.stop(); | |
839 tracker.hasChange = true; | |
840 } | |
841 }; | |
842 return tracker; | |
843 }; | |
844 | |
845 /** | |
782 * Change the state of the model to reflect the specified path (either a | 846 * Change the state of the model to reflect the specified path (either a |
783 * file or directory). | 847 * file or directory). |
784 * | 848 * |
785 * @param {string} path The root path to use | 849 * @param {string} path The root path to use |
786 * @param {function=} opt_loadedCallback Invoked when the entire directory | 850 * @param {function=} opt_loadedCallback Invoked when the entire directory |
787 * has been loaded and any default file selected. If there are any | 851 * has been loaded and any default file selected. If there are any |
788 * errors loading the directory this will not get called (even if the | 852 * errors loading the directory this will not get called (even if the |
789 * directory loads OK on retry later). Will NOT be called if another | 853 * directory loads OK on retry later). Will NOT be called if another |
790 * directory change happened while setupPath was in progress. | 854 * directory change happened while setupPath was in progress. |
791 * @param {function=} opt_pathResolveCallback Invoked as soon as the path has | 855 * @param {function=} opt_pathResolveCallback Invoked as soon as the path has |
792 * been resolved, and called with the base and leaf portions of the path | 856 * been resolved, and called with the base and leaf portions of the path |
793 * name, and a flag indicating if the entry exists. Will be called even | 857 * name, and a flag indicating if the entry exists. Will be called even |
794 * if another directory change happened while setupPath was in progress, | 858 * if another directory change happened while setupPath was in progress, |
795 * but will pass |false| as |exist| parameter. | 859 * but will pass |false| as |exist| parameter. |
796 */ | 860 */ |
797 DirectoryModel.prototype.setupPath = function(path, opt_loadedCallback, | 861 DirectoryModel.prototype.setupPath = function(path, opt_loadedCallback, |
798 opt_pathResolveCallback) { | 862 opt_pathResolveCallback) { |
799 var overridden = false; | 863 var tracker = this.createDirectoryChangeTracker(); |
800 function onExternalDirChange() { overridden = true } | 864 tracker.start(); |
801 this.addEventListener('directory-changed', onExternalDirChange); | |
802 | 865 |
803 var resolveCallback = function(exists) { | 866 var resolveCallback = function(exists) { |
804 this.removeEventListener('directory-changed', onExternalDirChange); | 867 tracker.stop(); |
805 if (opt_pathResolveCallback) | 868 if (opt_pathResolveCallback) { |
806 opt_pathResolveCallback(baseName, leafName, exists && !overridden); | 869 opt_pathResolveCallback(baseName, leafName, |
870 exists && !tracker.hasChanged); | |
871 } | |
807 }.bind(this); | 872 }.bind(this); |
808 | 873 |
809 var changeDirectoryEntry = function(entry, initial, exists, opt_callback) { | 874 var changeDirectoryEntry = function(entry, initial, exists, opt_callback) { |
875 tracker.stop(); | |
810 resolveCallback(exists); | 876 resolveCallback(exists); |
811 if (!overridden) | 877 if (!tracker.hasChanged) |
812 this.changeDirectoryEntry_(initial, entry, opt_callback); | 878 this.changeDirectoryEntry_(initial, entry, opt_callback); |
813 }.bind(this); | 879 }.bind(this); |
814 | 880 |
815 var INITIAL = true; | 881 var INITIAL = true; |
816 var EXISTS = true; | 882 var EXISTS = true; |
817 | 883 |
818 // Split the dirname from the basename. | 884 // Split the dirname from the basename. |
819 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); | 885 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); |
820 | 886 |
821 if (!ary) { | 887 if (!ary) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
855 console.log('Unexpected error resolving default leaf: ' + err); | 921 console.log('Unexpected error resolving default leaf: ' + err); |
856 changeDirectoryEntry(baseDirEntry, INITIAL, !EXISTS); | 922 changeDirectoryEntry(baseDirEntry, INITIAL, !EXISTS); |
857 } | 923 } |
858 | 924 |
859 var onBaseError = function(err) { | 925 var onBaseError = function(err) { |
860 console.log('Unexpected error resolving default base "' + | 926 console.log('Unexpected error resolving default base "' + |
861 baseName + '": ' + err); | 927 baseName + '": ' + err); |
862 if (path != this.getDefaultDirectory()) { | 928 if (path != this.getDefaultDirectory()) { |
863 // Can't find the provided path, let's go to default one instead. | 929 // Can't find the provided path, let's go to default one instead. |
864 resolveCallback(!EXISTS); | 930 resolveCallback(!EXISTS); |
865 if (!overridden) | 931 if (!tracker.hasChanged) |
866 this.setupDefaultPath(opt_loadedCallback); | 932 this.setupDefaultPath(opt_loadedCallback); |
867 } else { | 933 } else { |
868 // Well, we can't find the downloads dir. Let's just show something, | 934 // Well, we can't find the downloads dir. Let's just show something, |
869 // or we will get an infinite recursion. | 935 // or we will get an infinite recursion. |
870 changeDirectoryEntry(this.root_, opt_loadedCallback, INITIAL, !EXISTS); | 936 changeDirectoryEntry(this.root_, opt_loadedCallback, INITIAL, !EXISTS); |
871 } | 937 } |
872 }.bind(this); | 938 }.bind(this); |
873 | 939 |
874 var onBaseFound = function(baseDirEntry) { | 940 var onBaseFound = function(baseDirEntry) { |
875 if (!leafName) { | 941 if (!leafName) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
951 callback) { | 1017 callback) { |
952 this.metadataCache_.get(entries, 'filesystem', function(properties) { | 1018 this.metadataCache_.get(entries, 'filesystem', function(properties) { |
953 callback(); | 1019 callback(); |
954 }); | 1020 }); |
955 }; | 1021 }; |
956 | 1022 |
957 /** | 1023 /** |
958 * Get root entries asynchronously. | 1024 * Get root entries asynchronously. |
959 * @private | 1025 * @private |
960 * @param {function(Array.<Entry>)} callback Called when roots are resolved. | 1026 * @param {function(Array.<Entry>)} callback Called when roots are resolved. |
961 * @param {boolean} resolveGData See comment for updateRoots. | |
962 */ | 1027 */ |
963 DirectoryModel.prototype.resolveRoots_ = function(callback, resolveGData) { | 1028 DirectoryModel.prototype.resolveRoots_ = function(callback) { |
964 var groups = { | 1029 var groups = { |
965 downloads: null, | 1030 downloads: null, |
966 archives: null, | 1031 archives: null, |
967 removables: null, | 1032 removables: null, |
968 gdata: null | 1033 gdata: null |
969 }; | 1034 }; |
970 var self = this; | 1035 var self = this; |
971 | 1036 |
972 metrics.startInterval('Load.Roots'); | 1037 metrics.startInterval('Load.Roots'); |
973 function done() { | 1038 function done() { |
974 for (var i in groups) | 1039 for (var i in groups) |
975 if (!groups[i]) | 1040 if (!groups[i]) |
976 return; | 1041 return; |
977 | 1042 |
978 self.updateVolumeReadOnlyStatus_(groups.removables); | |
979 callback(groups.downloads. | 1043 callback(groups.downloads. |
980 concat(groups.gdata). | 1044 concat(groups.gdata). |
981 concat(groups.archives). | 1045 concat(groups.archives). |
982 concat(groups.removables)); | 1046 concat(groups.removables)); |
983 metrics.recordInterval('Load.Roots'); | 1047 metrics.recordInterval('Load.Roots'); |
984 } | 1048 } |
985 | 1049 |
986 function append(index, values, opt_error) { | 1050 function append(index, values, opt_error) { |
987 groups[index] = values; | 1051 groups[index] = values; |
988 done(); | 1052 done(); |
989 } | 1053 } |
990 | 1054 |
991 function onDownloads(entry) { | 1055 function appendSingle(index, entry) { |
992 groups.downloads = [entry]; | 1056 groups[index] = [entry]; |
993 done(); | 1057 done(); |
994 } | 1058 } |
995 | 1059 |
996 function onDownloadsError(error) { | 1060 function onSingleError(index, error, defaultValue) { |
997 groups.downloads = []; | 1061 groups[index] = defailtValue || []; |
998 done(); | |
999 } | |
1000 | |
1001 function onGData(entry) { | |
1002 console.log('GData found:', entry); | |
1003 self.unmountedGDataEntry_ = null; | |
1004 groups.gdata = [entry]; | |
1005 done(); | |
1006 } | |
1007 | |
1008 function onGDataError(error) { | |
1009 console.log('GData error: ' + error); | |
1010 self.unmountedGDataEntry_ = { | |
1011 unmounted: true, // Clients use this field to distinguish a fake root. | |
1012 toURL: function() { return '' }, | |
1013 fullPath: '/' + DirectoryModel.GDATA_DIRECTORY | |
1014 }; | |
1015 groups.gdata = [self.unmountedGDataEntry_]; | |
1016 done(); | 1062 done(); |
1017 } | 1063 } |
1018 | 1064 |
1019 var root = this.root_; | 1065 var root = this.root_; |
1020 root.getDirectory(DirectoryModel.DOWNLOADS_DIRECTORY, { create: false }, | 1066 function readSingle(dir, index, defaultValue) { |
1021 onDownloads, onDownloadsError); | 1067 root.getDirectory(dir, { create: false }, |
1068 appendSingle.bind(this, index), | |
1069 onSingleError.bind(this, index, defaultValue)); | |
1070 } | |
1071 | |
1072 readSingle(DirectoryModel.DOWNLOADS_DIRECTORY, 'downloads'); | |
1022 util.readDirectory(root, DirectoryModel.ARCHIVE_DIRECTORY, | 1073 util.readDirectory(root, DirectoryModel.ARCHIVE_DIRECTORY, |
1023 append.bind(this, 'archives')); | 1074 append.bind(this, 'archives')); |
1024 util.readDirectory(root, DirectoryModel.REMOVABLE_DIRECTORY, | 1075 util.readDirectory(root, DirectoryModel.REMOVABLE_DIRECTORY, |
1025 append.bind(this, 'removables')); | 1076 append.bind(this, 'removables')); |
1026 if (this.showGData_) { | 1077 if (this.showGData_) { |
1027 if (resolveGData) { | 1078 var fake = [DirectoryModel.fakeGDataEntry_]; |
1028 root.getDirectory(DirectoryModel.GDATA_DIRECTORY, { create: false }, | 1079 if (this.isGDataMounted_()) |
1029 onGData, onGDataError); | 1080 readSingle(DirectoryModel.GDATA_DIRECTORY, 'gdata', fake); |
1030 } else { | 1081 else |
1031 onGDataError('lazy mount'); | 1082 groups.gdata = fake; |
1032 } | |
1033 } else { | 1083 } else { |
1034 groups.gdata = []; | 1084 groups.gdata = []; |
1035 } | 1085 } |
1036 }; | 1086 }; |
1037 | 1087 |
1038 /** | 1088 /** |
1039 * @param {function} opt_callback Called when all roots are resolved. | 1089 * @param {functioni?} opt_callback Called when all roots are resolved. |
dgozman
2012/05/16 14:31:46
typo: functioni?
| |
1040 * @param {boolean} opt_resolveGData If true GData should be resolved for real, | |
1041 * If false a stub entry should be created. | |
1042 */ | 1090 */ |
1043 DirectoryModel.prototype.updateRoots = function(opt_callback, | 1091 DirectoryModel.prototype.updateRoots = function(opt_callback) { |
1044 opt_resolveGData) { | |
1045 var self = this; | 1092 var self = this; |
1046 this.resolveRoots_(function(rootEntries) { | 1093 this.resolveRoots_(function(rootEntries) { |
1047 var dm = self.rootsList_; | 1094 var dm = self.rootsList_; |
1048 var args = [0, dm.length].concat(rootEntries); | 1095 var args = [0, dm.length].concat(rootEntries); |
1049 dm.splice.apply(dm, args); | 1096 dm.splice.apply(dm, args); |
1050 | 1097 |
1051 self.updateRootsListSelection_(); | 1098 self.updateRootsListSelection_(); |
1052 | 1099 opt_callback && opt_callback(); |
dgozman
2012/05/16 14:31:46
I like the |if| construction much more.
| |
1053 if (opt_callback) | 1100 }); |
1054 opt_callback(); | |
1055 }, opt_resolveGData); | |
1056 }; | 1101 }; |
1057 | 1102 |
1058 /** | 1103 /** |
1059 * Find roots list item by root path. | 1104 * Find roots list item by root path. |
1060 * | 1105 * |
1061 * @param {string} path Root path. | 1106 * @param {string} path Root path. |
1062 * @return {number} Index of the item. | 1107 * @return {number} Index of the item. |
1063 * @private | 1108 * @private |
1064 */ | 1109 */ |
1065 DirectoryModel.prototype.findRootsListItem_ = function(path) { | 1110 DirectoryModel.prototype.findRootsListItem_ = function(path) { |
1066 var roots = this.rootsList_; | 1111 var roots = this.rootsList_; |
1067 for (var index = 0; index < roots.length; index++) { | 1112 for (var index = 0; index < roots.length; index++) { |
1068 if (roots.item(index).fullPath == path) | 1113 if (roots.item(index).fullPath == path) |
1069 return index; | 1114 return index; |
1070 } | 1115 } |
1071 return -1; | 1116 return -1; |
1072 }; | 1117 }; |
1073 | 1118 |
1074 /** | 1119 /** |
1075 * @private | 1120 * @private |
1076 */ | 1121 */ |
1077 DirectoryModel.prototype.updateRootsListSelection_ = function() { | 1122 DirectoryModel.prototype.updateRootsListSelection_ = function() { |
1078 var rootPath = DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); | 1123 var rootPath = DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); |
1079 this.rootsListSelection_.selectedIndex = this.findRootsListItem_(rootPath); | 1124 this.rootsListSelection_.selectedIndex = this.findRootsListItem_(rootPath); |
1080 }; | 1125 }; |
1081 | 1126 |
1082 /** | 1127 DirectoryModel.prototype.isGDataMounted_ = function() { |
1083 * @param {Array.<DirectoryEntry>} roots Removable volumes entries. | 1128 return this.volumeManager_.isMounted('/' + DirectoryModel.GDATA_DIRECTORY); |
1084 * @private | 1129 }; |
1085 */ | 1130 |
1086 DirectoryModel.prototype.updateVolumeReadOnlyStatus_ = function(roots) { | 1131 DirectoryModel.prototype.onMountChanged_ = function() { |
1087 var status = this.volumeReadOnlyStatus_ = {}; | 1132 this.updateRoots(); |
1088 for (var i = 0; i < roots.length; i++) { | 1133 |
1089 status[roots[i].fullPath] = false; | 1134 if (this.getCurrentRootType() != DirectoryModel.RootType.GDATA) |
1090 chrome.fileBrowserPrivate.getVolumeMetadata(roots[i].toURL(), | 1135 return; |
1091 function(systemMetadata, path) { | 1136 |
1092 status[path] = !!(systemMetadata && systemMetadata.isReadOnly); | 1137 var mounted = this.isGDataMounted_(); |
1093 }.bind(null, roots[i].fullPath)); | 1138 if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) { |
1139 if (mounted) { | |
1140 // Change fake entry to real one and rescan. | |
1141 this.root_.getDirectory('/' + DirectoryModel.GDATA_DIRECTORY, {}, | |
1142 function(entry) { | |
1143 if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) { | |
1144 this.currentDirEntry_ = entry; | |
1145 this.rescan(); | |
1146 } | |
1147 }.bind(this)); | |
1148 } | |
1149 } else if (!mounted) { | |
1150 // Current entry unmounted. replace with fake one. | |
1151 if (this.currentDirEntry_.fullPath == | |
1152 DirectoryModel.fakeGDataEntry_.fullPath) { | |
1153 // Replace silently and rescan. | |
1154 this.currentDirEntry_ = DirectoryModel.fakeGDataEntry_; | |
1155 this.rescan(); | |
1156 } else { | |
1157 this.changeDirectoryEntry_(false, DirectoryModel.fakeGDataEntry_); | |
1158 } | |
1094 } | 1159 } |
1095 }; | 1160 }; |
1096 | 1161 |
1097 /** | |
1098 * Prepare the root for the unmount. | |
1099 * | |
1100 * @param {string} rootPath The path to the root. | |
1101 */ | |
1102 DirectoryModel.prototype.prepareUnmount = function(rootPath) { | |
1103 var index = this.findRootsListItem_(rootPath); | |
1104 if (index == -1) { | |
1105 console.error('Unknown root entry', rootPath); | |
1106 return; | |
1107 } | |
1108 var entry = this.rootsList_.item(index); | |
1109 | |
1110 // We never need to remove this attribute because even if the unmount fails | |
1111 // the onMountCompleted handler calls updateRoots which creates a new entry | |
1112 // object for this volume. | |
1113 entry.unmounting = true; | |
1114 | |
1115 // Re-place the entry into the roots data model to force re-rendering. | |
1116 this.rootsList_.splice(index, 1, entry); | |
1117 | |
1118 if (rootPath == this.rootPath) { | |
1119 // TODO(kaznacheev): Consider changing to the most recently used root. | |
1120 this.changeDirectory(this.getDefaultDirectory()); | |
1121 } | |
1122 }; | |
1123 | |
1124 /** | 1162 /** |
1125 * @param {string} path Any path. | 1163 * @param {string} path Any path. |
1126 * @return {string} The root path. | 1164 * @return {string} The root path. |
1127 */ | 1165 */ |
1128 DirectoryModel.getRootPath = function(path) { | 1166 DirectoryModel.getRootPath = function(path) { |
1129 var type = DirectoryModel.getRootType(path); | 1167 var type = DirectoryModel.getRootType(path); |
1130 | 1168 |
1131 if (type == DirectoryModel.RootType.DOWNLOADS) | 1169 if (type == DirectoryModel.RootType.DOWNLOADS) |
1132 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; | 1170 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; |
1133 if (type == DirectoryModel.RootType.GDATA) | 1171 if (type == DirectoryModel.RootType.GDATA) |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1275 /** | 1313 /** |
1276 * @private | 1314 * @private |
1277 */ | 1315 */ |
1278 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { | 1316 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { |
1279 metrics.recordInterval('DirectoryScan'); | 1317 metrics.recordInterval('DirectoryScan'); |
1280 if (this.dir_.fullPath == | 1318 if (this.dir_.fullPath == |
1281 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { | 1319 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { |
1282 metrics.recordMediumCount('DownloadsCount', this.list_.length); | 1320 metrics.recordMediumCount('DownloadsCount', this.list_.length); |
1283 } | 1321 } |
1284 }; | 1322 }; |
1323 | |
1324 function FileWatcher(root, directoryModel, volumeManager) { | |
1325 this.root_ = root; | |
1326 this.dm_ = directoryModel; | |
1327 this.vm_ = volumeManager; | |
1328 this.watchedDirectoryEntry_ = null; | |
1329 this.updateWatchedDirectoryBound_ = | |
1330 this.updateWatchedDirectory_.bind(this); | |
1331 this.onFileChangedBound_ = | |
1332 this.onFileChanged_.bind(this); | |
1333 } | |
1334 | |
1335 FileWatcher.prototype.start = function() { | |
1336 chrome.fileBrowserPrivate.onFileChanged.addListener( | |
1337 this.onFileChangedBound_); | |
1338 | |
1339 this.dm_.addEventListener('directory-changed', | |
1340 this.updateWatchedDirectoryBound_); | |
1341 this.vm_.addEventListener('changed', | |
1342 this.updateWatchedDirectoryBound_); | |
1343 | |
1344 this.updateWatchedDirectory_(); | |
1345 }; | |
1346 | |
1347 FileWatcher.prototype.stop = function() { | |
1348 chrome.fileBrowserPrivate.onFileChanged.removeListener( | |
1349 this.onFileChangedBound_); | |
1350 | |
1351 this.dm_.removeEventListener('directory-changed', | |
1352 this.updateWatchedDirectoryBound_); | |
1353 this.vm_.removeEventListener('changed', | |
1354 this.updateWatchedDirectoryBound_); | |
1355 | |
1356 if (this.watchedDirectoryEntry_) | |
1357 this.changeWatchedEntry(null); | |
1358 }; | |
1359 | |
1360 FileWatcher.prototype.onFileChanged_ = function(event) { | |
1361 if (encodeURI(event.fileUrl) == this.watchedDirectoryEntry_.toURL()) | |
1362 this.dm_.rescanLater(); | |
1363 }; | |
1364 | |
1365 FileWatcher.prototype.updateWatchedDirectory_ = function() { | |
1366 var current = this.watchedDirectoryEntry_; | |
1367 switch (this.dm_.getCurrentRootType()) { | |
1368 case DirectoryModel.RootType.GDATA: | |
1369 if (!this.vm_.isMounted('/' + DirectoryModel.GDATA_DIRECTORY)) | |
1370 break; | |
dgozman
2012/05/16 14:31:46
I think, you mean |return| instead of |break| here
SeRya
2012/05/18 11:31:48
Break is correct. The code below need to be execut
| |
1371 case DirectoryModel.RootType.DOWNLOADS: | |
1372 case DirectoryModel.RootType.REMOVABLE: | |
dgozman
2012/05/16 14:31:46
What about RootType.ARCHIVE?
SeRya
2012/05/18 11:31:48
I assume they never change so we don't need to wat
| |
1373 if (!current || current.fullPath != this.dm_.getCurrentDirPath()) { | |
1374 this.root_.getDirectory(this.dm_.getCurrentDirPath(), {}, | |
dgozman
2012/05/16 14:31:46
Why not use dm_.getCurrentDirectory ?
SeRya
2012/05/18 11:31:48
getCurrentDirectory not always returns a valid Ent
| |
1375 this.changeWatchedEntry.bind(this)); | |
1376 } | |
1377 break; | |
1378 } | |
1379 if (current) | |
1380 this.changeWatchedEntry(null); | |
1381 }; | |
1382 | |
1383 FileWatcher.prototype.changeWatchedEntry = function(entry) { | |
1384 if (this.watchedDirectoryEntry_) { | |
1385 chrome.fileBrowserPrivate.removeFileWatch( | |
1386 this.watchedDirectoryEntry_.toURL(), | |
1387 function(result) { | |
1388 if (!result) { | |
1389 console.log('Failed to remove file watch'); | |
1390 } | |
1391 }); | |
1392 } | |
1393 this.watchedDirectoryEntry_ = entry; | |
1394 | |
1395 if (this.watchedDirectoryEntry_) { | |
1396 chrome.fileBrowserPrivate.addFileWatch( | |
1397 this.watchedDirectoryEntry_.toURL(), | |
1398 function(result) { | |
1399 if (!result) { | |
1400 console.log('Failed to add file watch'); | |
1401 this.watchedDirectoryEntry_ = null; | |
1402 } | |
1403 }.bind(this)); | |
1404 } | |
1405 }; | |
OLD | NEW |