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

Side by Side Diff: chrome/browser/resources/file_manager/js/directory_model.js

Issue 10310163: Refactoring file manager: moving volume mounting related code to a separate class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // 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
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
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
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
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
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
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
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
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
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
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698