| 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 const SIMULTANEOUS_RESCAN_INTERVAL = 1000; | 7 const SIMULTANEOUS_RESCAN_INTERVAL = 1000; |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * Data model of the file manager. | 10 * Data model of the file manager. |
| (...skipping 550 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 561 }, | 561 }, |
| 562 | 562 |
| 563 /** | 563 /** |
| 564 * Change the state of the model to reflect the specified path (either a | 564 * Change the state of the model to reflect the specified path (either a |
| 565 * file or directory). | 565 * file or directory). |
| 566 * | 566 * |
| 567 * @param {string} path The root path to use | 567 * @param {string} path The root path to use |
| 568 * @param {Function=} opt_loadedCallback Invoked when the entire directory | 568 * @param {Function=} opt_loadedCallback Invoked when the entire directory |
| 569 * has been loaded and any default file selected. If there are any | 569 * has been loaded and any default file selected. If there are any |
| 570 * errors loading the directory this will not get called (even if the | 570 * errors loading the directory this will not get called (even if the |
| 571 * directory loads OK on retry later). | 571 * directory loads OK on retry later). Will NOT be called if another |
| 572 * directory change happened while setupPath was in progress. |
| 572 * @param {Function=} opt_pathResolveCallback Invoked as soon as the path has | 573 * @param {Function=} opt_pathResolveCallback Invoked as soon as the path has |
| 573 * been resolved, and called with the base and leaf portions of the path | 574 * been resolved, and called with the base and leaf portions of the path |
| 574 * name, and a flag indicating if the entry exists. | 575 * name, and a flag indicating if the entry exists. Will be called even |
| 576 * if another directory change happened while setupPath was in progress, |
| 577 * but will pass |false| as |exist| parameter. |
| 575 */ | 578 */ |
| 576 setupPath: function(path, opt_loadedCallback, opt_pathResolveCallback) { | 579 setupPath: function(path, opt_loadedCallback, opt_pathResolveCallback) { |
| 580 var overridden = false; |
| 581 function onExternalDirChange() { overridden = true } |
| 582 this.addEventListener('directory-changed', onExternalDirChange); |
| 583 |
| 584 var resolveCallback = function(exists) { |
| 585 this.removeEventListener('directory-changed', onExternalDirChange); |
| 586 if (opt_pathResolveCallback) |
| 587 opt_pathResolveCallback(baseName, leafName, exists && !overridden); |
| 588 }.bind(this); |
| 589 |
| 590 var changeDirectoryEntry = function(entry, callback, initial, exists) { |
| 591 resolveCallback(exists); |
| 592 if (!overridden) |
| 593 this.changeDirectoryEntry_(entry, callback, initial); |
| 594 }.bind(this); |
| 595 |
| 596 var INITIAL = true; |
| 597 var EXISTS = true; |
| 598 |
| 577 // Split the dirname from the basename. | 599 // Split the dirname from the basename. |
| 578 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); | 600 var ary = path.match(/^(?:(.*)\/)?([^\/]*)$/); |
| 579 var autoSelect = function() { | 601 var autoSelect = function() { |
| 580 this.selectIndex(this.autoSelectIndex_); | 602 this.selectIndex(this.autoSelectIndex_); |
| 581 if (opt_loadedCallback) | 603 if (opt_loadedCallback) |
| 582 opt_loadedCallback(); | 604 opt_loadedCallback(); |
| 583 }.bind(this); | 605 }.bind(this); |
| 584 | 606 |
| 585 if (!ary) { | 607 if (!ary) { |
| 586 console.warn('Unable to split default path: ' + path); | 608 console.warn('Unable to split default path: ' + path); |
| 587 this.changeDirectoryEntry_(this.root_, autoSelect, true); | 609 changeDirectoryEntry(this.root_, autoSelect, INITIAL, !EXISTS); |
| 588 return; | 610 return; |
| 589 } | 611 } |
| 590 | 612 |
| 591 var baseName = ary[1]; | 613 var baseName = ary[1]; |
| 592 var leafName = ary[2]; | 614 var leafName = ary[2]; |
| 593 | 615 |
| 594 function resolveCallback(exists) { | |
| 595 if (opt_pathResolveCallback) | |
| 596 opt_pathResolveCallback(baseName, leafName, exists); | |
| 597 } | |
| 598 | |
| 599 function onLeafFound(baseDirEntry, leafEntry) { | 616 function onLeafFound(baseDirEntry, leafEntry) { |
| 600 if (leafEntry.isDirectory) { | 617 if (leafEntry.isDirectory) { |
| 601 baseName = path; | 618 baseName = path; |
| 602 leafName = ''; | 619 leafName = ''; |
| 603 resolveCallback(true); | 620 changeDirectoryEntry(leafEntry, autoSelect, INITIAL, EXISTS); |
| 604 this.changeDirectoryEntry_(leafEntry, autoSelect, true); | |
| 605 return; | 621 return; |
| 606 } | 622 } |
| 607 | 623 |
| 608 resolveCallback(true); | |
| 609 // Leaf is an existing file, cd to its parent directory and select it. | 624 // Leaf is an existing file, cd to its parent directory and select it. |
| 610 this.changeDirectoryEntry_(baseDirEntry, | 625 changeDirectoryEntry(baseDirEntry, |
| 611 function() { | 626 function() { |
| 612 this.selectEntry(leafEntry.name); | 627 this.selectEntry(leafEntry.name); |
| 613 if (opt_loadedCallback) | 628 if (opt_loadedCallback) |
| 614 opt_loadedCallback(); | 629 opt_loadedCallback(); |
| 615 }.bind(this), | 630 }.bind(this), |
| 616 false /*HACK*/); | 631 !INITIAL /*HACK*/, |
| 632 EXISTS); |
| 617 // TODO(kaznacheev): Fix history.replaceState for the File Browser and | 633 // TODO(kaznacheev): Fix history.replaceState for the File Browser and |
| 618 // change the last parameter back to |true|. Passing |false| makes things | 634 // change !INITIAL to INITIAL. Passing |false| makes things |
| 619 // less ugly for now. | 635 // less ugly for now. |
| 620 } | 636 } |
| 621 | 637 |
| 622 function onLeafError(baseDirEntry, err) { | 638 function onLeafError(baseDirEntry, err) { |
| 623 resolveCallback(false); | |
| 624 // Usually, leaf does not exist, because it's just a suggested file name. | 639 // Usually, leaf does not exist, because it's just a suggested file name. |
| 625 if (err.code != FileError.NOT_FOUND_ERR) | 640 if (err.code != FileError.NOT_FOUND_ERR) |
| 626 console.log('Unexpected error resolving default leaf: ' + err); | 641 console.log('Unexpected error resolving default leaf: ' + err); |
| 627 this.changeDirectoryEntry_(baseDirEntry, autoSelect, true); | 642 changeDirectoryEntry(baseDirEntry, autoSelect, INITIAL, !EXISTS); |
| 628 } | 643 } |
| 629 | 644 |
| 630 var onBaseError = function(err) { | 645 var onBaseError = function(err) { |
| 631 resolveCallback(false); | |
| 632 console.log('Unexpected error resolving default base "' + | 646 console.log('Unexpected error resolving default base "' + |
| 633 baseName + '": ' + err); | 647 baseName + '": ' + err); |
| 634 if (path != '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { | 648 if (path != '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { |
| 635 // Can't find the provided path, let's go to default one instead. | 649 // Can't find the provided path, let's go to default one instead. |
| 636 this.setupDefaultPath(opt_loadedCallback); | 650 resolveCallback(!EXISTS); |
| 651 if (!overridden) |
| 652 this.setupDefaultPath(opt_loadedCallback); |
| 637 } else { | 653 } else { |
| 638 // Well, we can't find the downloads dir. Let's just show something, | 654 // Well, we can't find the downloads dir. Let's just show something, |
| 639 // or we will get an infinite recursion. | 655 // or we will get an infinite recursion. |
| 640 this.changeDirectoryEntry_(this.root_, opt_loadedCallback, true); | 656 changeDirectoryEntry(this.root_, opt_loadedCallback, INITIAL, !EXISTS); |
| 641 } | 657 } |
| 642 }.bind(this); | 658 }.bind(this); |
| 643 | 659 |
| 644 var onBaseFound = function(baseDirEntry) { | 660 var onBaseFound = function(baseDirEntry) { |
| 645 if (!leafName) { | 661 if (!leafName) { |
| 646 // Default path is just a directory, cd to it and we're done. | 662 // Default path is just a directory, cd to it and we're done. |
| 647 this.changeDirectoryEntry_(baseDirEntry, autoSelect, true); | 663 changeDirectoryEntry(baseDirEntry, autoSelect, INITIAL, !EXISTS); |
| 648 return; | 664 return; |
| 649 } | 665 } |
| 650 | 666 |
| 651 util.resolvePath(this.root_, path, | 667 util.resolvePath(this.root_, path, |
| 652 onLeafFound.bind(this, baseDirEntry), | 668 onLeafFound.bind(this, baseDirEntry), |
| 653 onLeafError.bind(this, baseDirEntry)); | 669 onLeafError.bind(this, baseDirEntry)); |
| 654 }.bind(this); | 670 }.bind(this); |
| 655 | 671 |
| 656 var root = this.root_; | 672 var root = this.root_; |
| 657 if (baseName) { | 673 if (baseName) { |
| 658 root.getDirectory( | 674 root.getDirectory( |
| 659 baseName, {create: false}, onBaseFound, onBaseError); | 675 baseName, {create: false}, onBaseFound, onBaseError); |
| 660 } else { | 676 } else { |
| 661 this.getDefaultDirectory_(function(defaultDir) { | 677 this.getDefaultDirectory_(function(defaultDir) { |
| 662 baseName = defaultDir; | 678 baseName = defaultDir; |
| 663 root.getDirectory( | 679 root.getDirectory( |
| 664 baseName, {create: false}, onBaseFound, onBaseError); | 680 baseName, {create: false}, onBaseFound, onBaseError); |
| 665 }); | 681 }); |
| 666 } | 682 } |
| 667 }, | 683 }, |
| 668 | 684 |
| 669 setupDefaultPath: function(opt_callback) { | 685 setupDefaultPath: function(opt_callback) { |
| 686 var overridden = false; |
| 687 function onExternalDirChange() { overridden = true } |
| 688 this.addEventListener('directory-changed', onExternalDirChange); |
| 689 |
| 670 this.getDefaultDirectory_(function(path) { | 690 this.getDefaultDirectory_(function(path) { |
| 671 this.setupPath(path, opt_callback); | 691 this.removeEventListener('directory-changed', onExternalDirChange); |
| 692 if (!overridden) |
| 693 this.setupPath(path, opt_callback); |
| 672 }.bind(this)); | 694 }.bind(this)); |
| 673 }, | 695 }, |
| 674 | 696 |
| 675 getDefaultDirectory_: function(callback) { | 697 getDefaultDirectory_: function(callback) { |
| 676 function onGetDirectoryComplete(entries, error) { | 698 function onGetDirectoryComplete(entries, error) { |
| 677 if (entries.length > 0) | 699 if (entries.length > 0) |
| 678 callback(entries[0].fullPath); | 700 callback(entries[0].fullPath); |
| 679 else | 701 else |
| 680 callback('/' + DirectoryModel.DOWNLOADS_DIRECTORY); | 702 callback('/' + DirectoryModel.DOWNLOADS_DIRECTORY); |
| 681 } | 703 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 747 function onCacheDone() { | 769 function onCacheDone() { |
| 748 waitCount--; | 770 waitCount--; |
| 749 // If all caching functions finished synchronously or entries.length = 0 | 771 // If all caching functions finished synchronously or entries.length = 0 |
| 750 // call the callback synchronously. | 772 // call the callback synchronously. |
| 751 if (waitCount == 0) | 773 if (waitCount == 0) |
| 752 setTimeout(callback, 0); | 774 setTimeout(callback, 0); |
| 753 } | 775 } |
| 754 }, | 776 }, |
| 755 | 777 |
| 756 /** | 778 /** |
| 757 * Get root entries asynchronously. Invokes callback | 779 * Get root entries asynchronously. |
| 758 * when have finished. | 780 * @param {function(Array.<Entry>} callback Called when roots are resolved. |
| 781 * @param {boolean} resolveGData See comment for updateRoots. |
| 759 */ | 782 */ |
| 760 resolveRoots_: function(callback) { | 783 resolveRoots_: function(callback, resolveGData) { |
| 761 var groups = { | 784 var groups = { |
| 762 downloads: null, | 785 downloads: null, |
| 763 archives: null, | 786 archives: null, |
| 764 removables: null, | 787 removables: null, |
| 765 gdata: null | 788 gdata: null |
| 766 }; | 789 }; |
| 767 | 790 |
| 768 metrics.startInterval('Load.Roots'); | 791 metrics.startInterval('Load.Roots'); |
| 769 function done() { | 792 function done() { |
| 770 for (var i in groups) | 793 for (var i in groups) |
| (...skipping 18 matching lines...) Expand all Loading... |
| 789 } | 812 } |
| 790 | 813 |
| 791 function onDownloadsError(error) { | 814 function onDownloadsError(error) { |
| 792 groups.downloads = []; | 815 groups.downloads = []; |
| 793 done(); | 816 done(); |
| 794 } | 817 } |
| 795 | 818 |
| 796 var self = this; | 819 var self = this; |
| 797 | 820 |
| 798 function onGData(entry) { | 821 function onGData(entry) { |
| 799 console.log('onGData'); | 822 console.log('GData found:', entry); |
| 800 console.log(entry); | |
| 801 self.unmountedGDataEntry_ = null; | 823 self.unmountedGDataEntry_ = null; |
| 802 groups.gdata = [entry]; | 824 groups.gdata = [entry]; |
| 803 done(); | 825 done(); |
| 804 } | 826 } |
| 805 | 827 |
| 806 function onGDataError(error) { | 828 function onGDataError(error) { |
| 807 console.log('onGDataError'); | 829 console.log('GData error: ', error); |
| 808 console.log(error); | |
| 809 self.unmountedGDataEntry_ = { | 830 self.unmountedGDataEntry_ = { |
| 831 unmounted: true, // Clients use this field to distinguish a fake root. |
| 832 toURL: function() { return '' }, |
| 810 fullPath: '/' + DirectoryModel.GDATA_DIRECTORY | 833 fullPath: '/' + DirectoryModel.GDATA_DIRECTORY |
| 811 }; | 834 }; |
| 812 groups.gdata = [self.unmountedGDataEntry_]; | 835 groups.gdata = [self.unmountedGDataEntry_]; |
| 813 done(); | 836 done(); |
| 814 } | 837 } |
| 815 | 838 |
| 816 var root = this.root_; | 839 var root = this.root_; |
| 817 root.getDirectory(DirectoryModel.DOWNLOADS_DIRECTORY, { create: false }, | 840 root.getDirectory(DirectoryModel.DOWNLOADS_DIRECTORY, { create: false }, |
| 818 onDownloads, onDownloadsError); | 841 onDownloads, onDownloadsError); |
| 819 util.readDirectory(root, DirectoryModel.ARCHIVE_DIRECTORY, | 842 util.readDirectory(root, DirectoryModel.ARCHIVE_DIRECTORY, |
| 820 append.bind(this, 'archives')); | 843 append.bind(this, 'archives')); |
| 821 util.readDirectory(root, DirectoryModel.REMOVABLE_DIRECTORY, | 844 util.readDirectory(root, DirectoryModel.REMOVABLE_DIRECTORY, |
| 822 append.bind(this, 'removables')); | 845 append.bind(this, 'removables')); |
| 823 if (this.showGData_) { | 846 if (this.showGData_) { |
| 824 root.getDirectory(DirectoryModel.GDATA_DIRECTORY, { create: false }, | 847 if (resolveGData) { |
| 825 onGData, onGDataError); | 848 root.getDirectory(DirectoryModel.GDATA_DIRECTORY, { create: false }, |
| 849 onGData, onGDataError); |
| 850 } else { |
| 851 onGDataError('lazy mount'); |
| 852 } |
| 826 } else { | 853 } else { |
| 827 groups.gdata = []; | 854 groups.gdata = []; |
| 828 } | 855 } |
| 829 }, | 856 }, |
| 830 | 857 |
| 831 updateRoots: function(opt_callback) { | 858 /** |
| 832 console.log('directoryModel_.updateRoots'); | 859 * @param {function} opt_callback Called when all roots are resolved. |
| 860 * @param {boolean} opt_resolveGData If true GData should be resolved for real, |
| 861 * If false a stub entry should be created. |
| 862 */ |
| 863 updateRoots: function(opt_callback, opt_resolveGData) { |
| 864 console.log('resolving roots'); |
| 833 var self = this; | 865 var self = this; |
| 834 this.resolveRoots_(function(rootEntries) { | 866 this.resolveRoots_(function(rootEntries) { |
| 835 console.log('rootsList_ = '); | 867 console.log('resolved roots:', rootEntries); |
| 836 console.log(self.rootsList_); | |
| 837 var dm = self.rootsList_; | 868 var dm = self.rootsList_; |
| 838 var args = [0, dm.length].concat(rootEntries); | 869 var args = [0, dm.length].concat(rootEntries); |
| 839 dm.splice.apply(dm, args); | 870 dm.splice.apply(dm, args); |
| 840 | 871 |
| 841 self.updateRootsListSelection_(); | 872 self.updateRootsListSelection_(); |
| 842 | 873 |
| 843 if (opt_callback) | 874 if (opt_callback) |
| 844 opt_callback(); | 875 opt_callback(); |
| 845 }); | 876 }, opt_resolveGData); |
| 846 }, | 877 }, |
| 847 | 878 |
| 848 onRootsSelectionChanged_: function(event) { | 879 onRootsSelectionChanged_: function(event) { |
| 849 var root = this.rootsList.item(this.rootsListSelection.selectedIndex); | 880 var root = this.rootsList.item(this.rootsListSelection.selectedIndex); |
| 850 var current = this.currentEntry.fullPath; | |
| 851 if (root && this.rootPath != root.fullPath) | 881 if (root && this.rootPath != root.fullPath) |
| 852 this.changeDirectory(root.fullPath); | 882 this.changeDirectory(root.fullPath); |
| 853 }, | 883 }, |
| 854 | 884 |
| 855 updateRootsListSelection_: function() { | 885 updateRootsListSelection_: function() { |
| 856 var roots = this.rootsList_; | 886 var roots = this.rootsList_; |
| 857 var rootPath = this.rootPath; | 887 var rootPath = this.rootPath; |
| 858 for (var index = 0; index < roots.length; index++) { | 888 for (var index = 0; index < roots.length; index++) { |
| 859 if (roots.item(index).fullPath == rootPath) { | 889 if (roots.item(index).fullPath == rootPath) { |
| 860 this.rootsListSelection.selectedIndex = index; | 890 this.rootsListSelection.selectedIndex = index; |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 990 | 1020 |
| 991 recordMetrics_: function() { | 1021 recordMetrics_: function() { |
| 992 metrics.recordInterval('DirectoryScan'); | 1022 metrics.recordInterval('DirectoryScan'); |
| 993 if (this.dir_.fullPath == | 1023 if (this.dir_.fullPath == |
| 994 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { | 1024 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { |
| 995 metrics.recordMediumCount("DownloadsCount", this.list_.length); | 1025 metrics.recordMediumCount("DownloadsCount", this.list_.length); |
| 996 } | 1026 } |
| 997 } | 1027 } |
| 998 }; | 1028 }; |
| 999 | 1029 |
| OLD | NEW |