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() { | |
Vladislav Kaznacheev
2012/05/15 11:11:54
Calling updateRoots from the constructor somehow f
| |
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 13 matching lines...) Expand all Loading... | |
85 }; | 93 }; |
86 | 94 |
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 = 'gdata'; // TODO(serya): return 'drive' |
104 | |
105 DirectoryModel.fakeGDataEntry_ = { | |
106 fullPath: '/' + DirectoryModel.GDATA_DIRECTORY | |
107 }; | |
96 | 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() { |
(...skipping 59 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
769 } | 789 } |
770 this.updateRootsListSelection_(); | 790 this.updateRootsListSelection_(); |
771 this.scan_(onRescanComplete); | 791 this.scan_(onRescanComplete); |
772 this.currentDirByRoot_[this.getCurrentRootPath()] = dirEntry.fullPath; | 792 this.currentDirByRoot_[this.getCurrentRootPath()] = dirEntry.fullPath; |
773 | 793 |
774 var e = new cr.Event('directory-changed'); | 794 var e = new cr.Event('directory-changed'); |
775 e.previousDirEntry = previous; | 795 e.previousDirEntry = previous; |
776 e.newDirEntry = dirEntry; | 796 e.newDirEntry = dirEntry; |
777 e.initial = initial; | 797 e.initial = initial; |
778 this.dispatchEvent(e); | 798 this.dispatchEvent(e); |
799 | |
800 if (dirEntry == DirectoryModel.fakeGDataEntry_) { | |
801 this.volumeManager_.mountGData(function() {}, function() {}); | |
802 } | |
779 }; | 803 }; |
780 | 804 |
781 /** | 805 /** |
806 * Creates an object wich could cay wether directory has changed while it has | |
Vladislav Kaznacheev
2012/05/15 11:11:54
"cay", "canncelled" = typo?
| |
807 * been active or not. Designed for long operations that should be canncelled | |
808 * if the used change current directory. | |
809 * @return {Object} Created object. | |
810 */ | |
811 DirectoryModel.prototype.createDirectoryChangeTracker = function() { | |
812 var tracker = { | |
813 dm_: this, | |
814 active_: false, | |
815 hasChanged: false, | |
816 exceptInitialChange: false, | |
817 | |
818 start: function() { | |
819 if (!this.active_) { | |
820 this.dm_.addEventListener('directory-changed', | |
821 this.onDirectoryChange_); | |
822 this.active_ = true; | |
823 this.hasChanged = false; | |
824 } | |
825 }, | |
826 | |
827 stop: function() { | |
828 if (this.active_) { | |
829 this.dm_.removeEventListener('directory-changed', | |
830 this.onDirectoryChange_); | |
831 active_ = false; | |
832 } | |
833 }, | |
834 | |
835 onDirectoryChange_: function(event) { | |
836 if (tracker.exceptInitialChange && event.initial) | |
837 return; | |
838 // This is incorrect here. | |
839 tracker.stop(); | |
840 tracker.hasChange = true; | |
841 } | |
842 }; | |
843 return tracker; | |
844 }; | |
845 | |
846 /** | |
782 * Change the state of the model to reflect the specified path (either a | 847 * Change the state of the model to reflect the specified path (either a |
783 * file or directory). | 848 * file or directory). |
784 * | 849 * |
785 * @param {string} path The root path to use | 850 * @param {string} path The root path to use |
786 * @param {function=} opt_loadedCallback Invoked when the entire directory | 851 * @param {function=} opt_loadedCallback Invoked when the entire directory |
787 * has been loaded and any default file selected. If there are any | 852 * 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 | 853 * 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 | 854 * directory loads OK on retry later). Will NOT be called if another |
790 * directory change happened while setupPath was in progress. | 855 * directory change happened while setupPath was in progress. |
791 * @param {function=} opt_pathResolveCallback Invoked as soon as the path has | 856 * @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 | 857 * 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 | 858 * name, and a flag indicating if the entry exists. Will be called even |
794 * if another directory change happened while setupPath was in progress, | 859 * if another directory change happened while setupPath was in progress, |
795 * but will pass |false| as |exist| parameter. | 860 * but will pass |false| as |exist| parameter. |
796 */ | 861 */ |
797 DirectoryModel.prototype.setupPath = function(path, opt_loadedCallback, | 862 DirectoryModel.prototype.setupPath = function(path, opt_loadedCallback, |
798 opt_pathResolveCallback) { | 863 opt_pathResolveCallback) { |
799 var overridden = false; | 864 var tracker = this.createDirectoryChangeTracker(); |
800 function onExternalDirChange() { overridden = true } | 865 tracker.start(); |
801 this.addEventListener('directory-changed', onExternalDirChange); | |
802 | 866 |
803 var resolveCallback = function(exists) { | 867 var resolveCallback = function(exists) { |
804 this.removeEventListener('directory-changed', onExternalDirChange); | 868 tracker.stop(); |
805 if (opt_pathResolveCallback) | 869 if (opt_pathResolveCallback) { |
806 opt_pathResolveCallback(baseName, leafName, exists && !overridden); | 870 opt_pathResolveCallback(baseName, leafName, |
871 exists && !tracker.hasChanged); | |
872 } | |
807 }.bind(this); | 873 }.bind(this); |
808 | 874 |
809 var changeDirectoryEntry = function(entry, initial, exists, opt_callback) { | 875 var changeDirectoryEntry = function(entry, initial, exists, opt_callback) { |
876 tracker.stop(); | |
810 resolveCallback(exists); | 877 resolveCallback(exists); |
811 if (!overridden) | 878 if (!tracker.hasChanged) |
812 this.changeDirectoryEntry_(initial, entry, opt_callback); | 879 this.changeDirectoryEntry_(initial, entry, opt_callback); |
813 }.bind(this); | 880 }.bind(this); |
814 | 881 |
815 var INITIAL = true; | 882 var INITIAL = true; |
816 var EXISTS = true; | 883 var EXISTS = true; |
817 | 884 |
818 // Split the dirname from the basename. | 885 // Split the dirname from the basename. |
819 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); | 886 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); |
820 | 887 |
821 if (!ary) { | 888 if (!ary) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
855 console.log('Unexpected error resolving default leaf: ' + err); | 922 console.log('Unexpected error resolving default leaf: ' + err); |
856 changeDirectoryEntry(baseDirEntry, INITIAL, !EXISTS); | 923 changeDirectoryEntry(baseDirEntry, INITIAL, !EXISTS); |
857 } | 924 } |
858 | 925 |
859 var onBaseError = function(err) { | 926 var onBaseError = function(err) { |
860 console.log('Unexpected error resolving default base "' + | 927 console.log('Unexpected error resolving default base "' + |
861 baseName + '": ' + err); | 928 baseName + '": ' + err); |
862 if (path != this.getDefaultDirectory()) { | 929 if (path != this.getDefaultDirectory()) { |
863 // Can't find the provided path, let's go to default one instead. | 930 // Can't find the provided path, let's go to default one instead. |
864 resolveCallback(!EXISTS); | 931 resolveCallback(!EXISTS); |
865 if (!overridden) | 932 if (!tracker.hasChanged) |
866 this.setupDefaultPath(opt_loadedCallback); | 933 this.setupDefaultPath(opt_loadedCallback); |
867 } else { | 934 } else { |
868 // Well, we can't find the downloads dir. Let's just show something, | 935 // Well, we can't find the downloads dir. Let's just show something, |
869 // or we will get an infinite recursion. | 936 // or we will get an infinite recursion. |
870 changeDirectoryEntry(this.root_, opt_loadedCallback, INITIAL, !EXISTS); | 937 changeDirectoryEntry(this.root_, opt_loadedCallback, INITIAL, !EXISTS); |
871 } | 938 } |
872 }.bind(this); | 939 }.bind(this); |
873 | 940 |
874 var onBaseFound = function(baseDirEntry) { | 941 var onBaseFound = function(baseDirEntry) { |
875 if (!leafName) { | 942 if (!leafName) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
951 callback) { | 1018 callback) { |
952 this.metadataCache_.get(entries, 'filesystem', function(properties) { | 1019 this.metadataCache_.get(entries, 'filesystem', function(properties) { |
953 callback(); | 1020 callback(); |
954 }); | 1021 }); |
955 }; | 1022 }; |
956 | 1023 |
957 /** | 1024 /** |
958 * Get root entries asynchronously. | 1025 * Get root entries asynchronously. |
959 * @private | 1026 * @private |
960 * @param {function(Array.<Entry>)} callback Called when roots are resolved. | 1027 * @param {function(Array.<Entry>)} callback Called when roots are resolved. |
961 * @param {boolean} resolveGData See comment for updateRoots. | |
962 */ | 1028 */ |
963 DirectoryModel.prototype.resolveRoots_ = function(callback, resolveGData) { | 1029 DirectoryModel.prototype.resolveRoots_ = function(callback) { |
964 var groups = { | 1030 var groups = { |
965 downloads: null, | 1031 downloads: null, |
966 archives: null, | 1032 archives: null, |
967 removables: null, | 1033 removables: null, |
968 gdata: null | 1034 gdata: null |
969 }; | 1035 }; |
970 var self = this; | 1036 var self = this; |
971 | 1037 |
972 metrics.startInterval('Load.Roots'); | 1038 metrics.startInterval('Load.Roots'); |
973 function done() { | 1039 function done() { |
974 for (var i in groups) | 1040 for (var i in groups) |
975 if (!groups[i]) | 1041 if (!groups[i]) |
976 return; | 1042 return; |
977 | 1043 |
978 self.updateVolumeReadOnlyStatus_(groups.removables); | |
979 callback(groups.downloads. | 1044 callback(groups.downloads. |
980 concat(groups.gdata). | 1045 concat(groups.gdata). |
981 concat(groups.archives). | 1046 concat(groups.archives). |
982 concat(groups.removables)); | 1047 concat(groups.removables)); |
983 metrics.recordInterval('Load.Roots'); | 1048 metrics.recordInterval('Load.Roots'); |
984 } | 1049 } |
985 | 1050 |
986 function append(index, values, opt_error) { | 1051 function append(index, values, opt_error) { |
987 groups[index] = values; | 1052 groups[index] = values; |
988 done(); | 1053 done(); |
989 } | 1054 } |
990 | 1055 |
991 function onDownloads(entry) { | 1056 function appendSingle(index, entry) { |
992 groups.downloads = [entry]; | 1057 groups[index] = [entry]; |
993 done(); | 1058 done(); |
994 } | 1059 } |
995 | 1060 |
996 function onDownloadsError(error) { | 1061 function onSingleError(index, error, defaultValue) { |
997 groups.downloads = []; | 1062 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(); | 1063 done(); |
1017 } | 1064 } |
1018 | 1065 |
1019 var root = this.root_; | 1066 var root = this.root_; |
1020 root.getDirectory(DirectoryModel.DOWNLOADS_DIRECTORY, { create: false }, | 1067 function readSingle(dir, index, defaultValue) { |
1021 onDownloads, onDownloadsError); | 1068 root.getDirectory(dir, { create: false }, |
1069 appendSingle.bind(this, index), | |
1070 onSingleError.bind(this, index, defaultValue)); | |
1071 } | |
1072 | |
1073 readSingle(DirectoryModel.DOWNLOADS_DIRECTORY, 'downloads'); | |
1022 util.readDirectory(root, DirectoryModel.ARCHIVE_DIRECTORY, | 1074 util.readDirectory(root, DirectoryModel.ARCHIVE_DIRECTORY, |
1023 append.bind(this, 'archives')); | 1075 append.bind(this, 'archives')); |
1024 util.readDirectory(root, DirectoryModel.REMOVABLE_DIRECTORY, | 1076 util.readDirectory(root, DirectoryModel.REMOVABLE_DIRECTORY, |
1025 append.bind(this, 'removables')); | 1077 append.bind(this, 'removables')); |
1026 if (this.showGData_) { | 1078 if (this.showGData_) { |
1027 if (resolveGData) { | 1079 var fake = [DirectoryModel.fakeGDataEntry_]; |
1028 root.getDirectory(DirectoryModel.GDATA_DIRECTORY, { create: false }, | 1080 if (this.isGDataMounted_()) |
1029 onGData, onGDataError); | 1081 readSingle(DirectoryModel.GDATA_DIRECTORY, 'gdata', fake); |
1030 } else { | 1082 else |
1031 onGDataError('lazy mount'); | 1083 groups.gdata = fake; |
1032 } | |
1033 } else { | 1084 } else { |
1034 groups.gdata = []; | 1085 groups.gdata = []; |
1035 } | 1086 } |
1036 }; | 1087 }; |
1037 | 1088 |
1038 /** | 1089 /** |
1039 * @param {function} opt_callback Called when all roots are resolved. | 1090 * @param {functioni?} opt_callback Called when all roots are resolved. |
1040 * @param {boolean} opt_resolveGData If true GData should be resolved for real, | |
1041 * If false a stub entry should be created. | |
1042 */ | 1091 */ |
1043 DirectoryModel.prototype.updateRoots = function(opt_callback, | 1092 DirectoryModel.prototype.updateRoots = function(opt_callback) { |
1044 opt_resolveGData) { | |
1045 var self = this; | 1093 var self = this; |
1046 this.resolveRoots_(function(rootEntries) { | 1094 this.resolveRoots_(function(rootEntries) { |
1047 var dm = self.rootsList_; | 1095 var dm = self.rootsList_; |
1048 var args = [0, dm.length].concat(rootEntries); | 1096 var args = [0, dm.length].concat(rootEntries); |
1049 dm.splice.apply(dm, args); | 1097 dm.splice.apply(dm, args); |
1050 | 1098 |
1051 self.updateRootsListSelection_(); | 1099 self.updateRootsListSelection_(); |
1052 | 1100 opt_callback && opt_callback(); |
1053 if (opt_callback) | 1101 }); |
1054 opt_callback(); | |
1055 }, opt_resolveGData); | |
1056 }; | 1102 }; |
1057 | 1103 |
1058 /** | 1104 /** |
1059 * Find roots list item by root path. | 1105 * Find roots list item by root path. |
1060 * | 1106 * |
1061 * @param {string} path Root path. | 1107 * @param {string} path Root path. |
1062 * @return {number} Index of the item. | 1108 * @return {number} Index of the item. |
1063 * @private | 1109 * @private |
1064 */ | 1110 */ |
1065 DirectoryModel.prototype.findRootsListItem_ = function(path) { | 1111 DirectoryModel.prototype.findRootsListItem_ = function(path) { |
1066 var roots = this.rootsList_; | 1112 var roots = this.rootsList_; |
1067 for (var index = 0; index < roots.length; index++) { | 1113 for (var index = 0; index < roots.length; index++) { |
1068 if (roots.item(index).fullPath == path) | 1114 if (roots.item(index).fullPath == path) |
1069 return index; | 1115 return index; |
1070 } | 1116 } |
1071 return -1; | 1117 return -1; |
1072 }; | 1118 }; |
1073 | 1119 |
1074 /** | 1120 /** |
1075 * @private | 1121 * @private |
1076 */ | 1122 */ |
1077 DirectoryModel.prototype.updateRootsListSelection_ = function() { | 1123 DirectoryModel.prototype.updateRootsListSelection_ = function() { |
1078 var rootPath = DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); | 1124 var rootPath = DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); |
1079 this.rootsListSelection_.selectedIndex = this.findRootsListItem_(rootPath); | 1125 this.rootsListSelection_.selectedIndex = this.findRootsListItem_(rootPath); |
1080 }; | 1126 }; |
1081 | 1127 |
1082 /** | 1128 DirectoryModel.prototype.isGDataMounted_ = function() { |
1083 * @param {Array.<DirectoryEntry>} roots Removable volumes entries. | 1129 return this.volumeManager_.isMounted('/' + DirectoryModel.GDATA_DIRECTORY); |
1084 * @private | 1130 }; |
1085 */ | 1131 |
1086 DirectoryModel.prototype.updateVolumeReadOnlyStatus_ = function(roots) { | 1132 DirectoryModel.prototype.onMountChanged_ = function() { |
1087 var status = this.volumeReadOnlyStatus_ = {}; | 1133 this.updateRoots(); |
1088 for (var i = 0; i < roots.length; i++) { | 1134 |
1089 status[roots[i].fullPath] = false; | 1135 if (this.getCurrentRootType() != DirectoryModel.RootType.GDATA) |
1090 chrome.fileBrowserPrivate.getVolumeMetadata(roots[i].toURL(), | 1136 return; |
1091 function(systemMetadata, path) { | 1137 |
1092 status[path] = !!(systemMetadata && systemMetadata.isReadOnly); | 1138 var mounted = this.isGDataMounted_(); |
1093 }.bind(null, roots[i].fullPath)); | 1139 if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) { |
1140 if (mounted) { | |
1141 // Change fake entry to real one and rescan. | |
1142 this.root_.getDirectory('/' + DirectoryModel.GDATA_DIRECTORY, {}, | |
1143 function(entry) { | |
1144 if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) { | |
1145 this.currentDirEntry_ = entry; | |
1146 this.rescan(); | |
1147 } | |
1148 }.bind(this)); | |
1149 } | |
1150 } else if (!mounted) { | |
1151 // Current entry unmounted. replace with fake one. | |
1152 if (this.currentDirEntry_.fullPath == | |
1153 DirectoryModel.fakeGDataEntry_.fullPath) { | |
1154 // Replace silently and rescan. | |
1155 this.currentDirEntry_ = DirectoryModel.fakeGDataEntry_; | |
1156 this.rescan(); | |
1157 } else { | |
1158 this.changeDirectoryEntry_(false, DirectoryModel.fakeGDataEntry_); | |
1159 } | |
1094 } | 1160 } |
1095 }; | 1161 }; |
1096 | 1162 |
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 /** | 1163 /** |
1125 * @param {string} path Any path. | 1164 * @param {string} path Any path. |
1126 * @return {string} The root path. | 1165 * @return {string} The root path. |
1127 */ | 1166 */ |
1128 DirectoryModel.getRootPath = function(path) { | 1167 DirectoryModel.getRootPath = function(path) { |
1129 var type = DirectoryModel.getRootType(path); | 1168 var type = DirectoryModel.getRootType(path); |
1130 | 1169 |
1131 if (type == DirectoryModel.RootType.DOWNLOADS) | 1170 if (type == DirectoryModel.RootType.DOWNLOADS) |
1132 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; | 1171 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; |
1133 if (type == DirectoryModel.RootType.GDATA) | 1172 if (type == DirectoryModel.RootType.GDATA) |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1275 /** | 1314 /** |
1276 * @private | 1315 * @private |
1277 */ | 1316 */ |
1278 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { | 1317 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { |
1279 metrics.recordInterval('DirectoryScan'); | 1318 metrics.recordInterval('DirectoryScan'); |
1280 if (this.dir_.fullPath == | 1319 if (this.dir_.fullPath == |
1281 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { | 1320 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { |
1282 metrics.recordMediumCount('DownloadsCount', this.list_.length); | 1321 metrics.recordMediumCount('DownloadsCount', this.list_.length); |
1283 } | 1322 } |
1284 }; | 1323 }; |
1324 | |
1325 function FileWatcher(root, directoryModel, volumeManager) { | |
1326 this.root_ = root; | |
1327 this.dm_ = directoryModel; | |
1328 this.vm_ = volumeManager; | |
1329 this.watchedDirectoryEntry_ = null; | |
1330 this.updateWatchedDirectoryBound_ = | |
1331 this.updateWatchedDirectory_.bind(this); | |
1332 this.onFileChangedBound_ = | |
1333 this.onFileChanged_.bind(this); | |
1334 } | |
1335 | |
1336 FileWatcher.prototype.start = function() { | |
1337 chrome.fileBrowserPrivate.onFileChanged.addListener( | |
1338 this.onFileChangedBound_); | |
1339 | |
1340 this.dm_.addEventListener('directory-changed', | |
1341 this.updateWatchedDirectoryBound_); | |
1342 this.vm_.addEventListener('changed', | |
1343 this.updateWatchedDirectoryBound_); | |
1344 | |
1345 this.updateWatchedDirectory_(); | |
1346 }; | |
1347 | |
1348 FileWatcher.prototype.stop = function() { | |
1349 chrome.fileBrowserPrivate.onFileChanged.removeListener( | |
1350 this.onFileChangedBound_); | |
1351 | |
1352 this.dm_.removeEventListener('directory-changed', | |
1353 this.updateWatchedDirectoryBound_); | |
1354 this.vm_.removeEventListener('changed', | |
1355 this.updateWatchedDirectoryBound_); | |
1356 | |
1357 if (this.watchedDirectoryEntry_) | |
1358 this.changeWatchedEntry(null); | |
1359 }; | |
1360 | |
1361 FileWatcher.prototype.onFileChanged_ = function(event) { | |
1362 if (encodeURI(event.fileUrl) == this.watchedDirectoryEntry_.toURL()) | |
1363 this.dm_.rescanLater(); | |
1364 }; | |
1365 | |
1366 FileWatcher.prototype.updateWatchedDirectory_ = function() { | |
1367 var current = this.watchedDirectoryEntry_; | |
1368 switch (this.dm_.getCurrentRootType()) { | |
1369 case DirectoryModel.RootType.GDATA: | |
1370 if (!this.vm_.isMounted('/' + DirectoryModel.GDATA_DIRECTORY)) | |
1371 break; | |
1372 case DirectoryModel.RootType.DOWNLOADS: | |
1373 case DirectoryModel.RootType.REMOVABLE: | |
1374 if (!current || current.fullPath != this.dm_.getCurrentDirPath()) { | |
1375 this.root_.getDirectory(this.dm_.getCurrentDirPath(), {}, | |
1376 this.changeWatchedEntry.bind(this)); | |
1377 } | |
1378 break; | |
1379 } | |
1380 if (current) | |
1381 this.changeWatchedEntry(null); | |
1382 }; | |
1383 | |
1384 FileWatcher.prototype.changeWatchedEntry = function(entry) { | |
1385 if (this.watchedDirectoryEntry_) { | |
1386 chrome.fileBrowserPrivate.removeFileWatch( | |
1387 this.watchedDirectoryEntry_.toURL(), | |
1388 function(result) { | |
1389 if (!result) { | |
1390 console.log('Failed to remove file watch'); | |
1391 } | |
1392 }); | |
1393 } | |
1394 this.watchedDirectoryEntry_ = entry; | |
1395 | |
1396 if (this.watchedDirectoryEntry_) { | |
1397 chrome.fileBrowserPrivate.addFileWatch( | |
1398 this.watchedDirectoryEntry_.toURL(), | |
1399 function(result) { | |
1400 if (!result) { | |
1401 console.log('Failed to add file watch'); | |
1402 this.watchedDirectoryEntry_ = null; | |
1403 } | |
1404 }.bind(this)); | |
1405 } | |
1406 }; | |
OLD | NEW |