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

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: Bugfixing 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() {
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
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
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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698