| Index: chrome/browser/resources/file_manager/js/directory_contents.js
|
| diff --git a/chrome/browser/resources/file_manager/js/directory_contents.js b/chrome/browser/resources/file_manager/js/directory_contents.js
|
| index be916c1e51fc38a9e89076d78b8fefe0b1cf7b94..fece6f207fd0335fe38c46a933683be436821ae8 100644
|
| --- a/chrome/browser/resources/file_manager/js/directory_contents.js
|
| +++ b/chrome/browser/resources/file_manager/js/directory_contents.js
|
| @@ -5,6 +5,341 @@
|
| 'use strict';
|
|
|
| /**
|
| + * Scanner of the entries.
|
| + * @constructor
|
| + */
|
| +function ContentScanner() {
|
| + this.cancelled_ = false;
|
| +}
|
| +
|
| +/**
|
| + * Starts to scan the entries. For example, starts to read the entries in a
|
| + * directory, or starts to search with some query on a file system.
|
| + * Derived classes must override this method.
|
| + *
|
| + * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of
|
| + * entries are read. This can be called a couple of times until the
|
| + * completion.
|
| + * @param {function()} successCallback Called when the scan is completed
|
| + * successfully.
|
| + * @param {function(FileError)} errorCallback Called an error occurs.
|
| + */
|
| +ContentScanner.prototype.scan = function(
|
| + entriesCallback, successCallback, errorCallback) {
|
| +};
|
| +
|
| +/**
|
| + * Request cancelling of the running scan. When the cancelling is done,
|
| + * an error will be reported from errorCallback passed to scan().
|
| + */
|
| +ContentScanner.prototype.cancel = function() {
|
| + this.cancelled_ = true;
|
| +};
|
| +
|
| +/**
|
| + * Scanner of the entries in a directory.
|
| + * @param {DirectoryEntry} entry The directory to be read.
|
| + * @constructor
|
| + * @extends {ContentScanner}
|
| + */
|
| +function DirectoryContentScanner(entry) {
|
| + ContentScanner.call(this);
|
| + this.entry_ = entry;
|
| +}
|
| +
|
| +/**
|
| + * Extends ContentScanner.
|
| + */
|
| +DirectoryContentScanner.prototype.__proto__ = ContentScanner.prototype;
|
| +
|
| +/**
|
| + * Starts to read the entries in the directory.
|
| + * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of
|
| + * entries are read. This can be called a couple of times until the
|
| + * completion.
|
| + * @param {function()} successCallback Called when the scan is completed
|
| + * successfully.
|
| + * @param {function(FileError)} errorCallback Called an error occurs.
|
| + * @override
|
| + */
|
| +DirectoryContentScanner.prototype.scan = function(
|
| + entriesCallback, successCallback, errorCallback) {
|
| + if (!this.entry_ || this.entry_ === DirectoryModel.fakeDriveEntry_) {
|
| + // If entry is not specified or a fake, we cannot read it.
|
| + errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR));
|
| + return;
|
| + }
|
| +
|
| + metrics.startInterval('DirectoryScan');
|
| + var reader = this.entry_.createReader();
|
| + var readEntries = function() {
|
| + reader.readEntries(
|
| + function(entries) {
|
| + if (this.cancelled_) {
|
| + errorCallback(util.createFileError(FileError.ABORT_ERR));
|
| + return;
|
| + }
|
| +
|
| + if (entries.length === 0) {
|
| + // All entries are read.
|
| + metrics.recordInterval('DirectoryScan');
|
| + successCallback();
|
| + return;
|
| + }
|
| +
|
| + entriesCallback(entries);
|
| + readEntries();
|
| + }.bind(this),
|
| + errorCallback);
|
| + }.bind(this);
|
| + readEntries();
|
| +};
|
| +
|
| +/**
|
| + * Scanner of the entries for the search results on Drive File System.
|
| + * @param {string} query The query string.
|
| + * @constructor
|
| + * @extends {ContentScanner}
|
| + */
|
| +function DriveSearchContentScanner(query) {
|
| + ContentScanner.call(this);
|
| + this.query_ = query;
|
| +}
|
| +
|
| +/**
|
| + * Extends ContentScanner.
|
| + */
|
| +DriveSearchContentScanner.prototype.__proto__ = ContentScanner.prototype;
|
| +
|
| +/**
|
| + * Delay in milliseconds to be used for drive search scan, in order to reduce
|
| + * the number of server requests while user is typing the query.
|
| + * @type {number}
|
| + * @private
|
| + * @const
|
| + */
|
| +DriveSearchContentScanner.SCAN_DELAY_ = 200;
|
| +
|
| +/**
|
| + * Maximum number of results which is shown on the search.
|
| + * @type {number}
|
| + * @private
|
| + * @const
|
| + */
|
| +DriveSearchContentScanner.MAX_RESULTS_ = 100;
|
| +
|
| +/**
|
| + * Starts to search on Drive File System.
|
| + * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of
|
| + * entries are read. This can be called a couple of times until the
|
| + * completion.
|
| + * @param {function()} successCallback Called when the scan is completed
|
| + * successfully.
|
| + * @param {function(FileError)} errorCallback Called an error occurs.
|
| + * @override
|
| + */
|
| +DriveSearchContentScanner.prototype.scan = function(
|
| + entriesCallback, successCallback, errorCallback) {
|
| + var numReadEntries = 0;
|
| + var readEntries = function(nextFeed) {
|
| + chrome.fileBrowserPrivate.searchDrive(
|
| + {query: this.query_, nextFeed: nextFeed},
|
| + function(entries, nextFeed) {
|
| + if (this.cancelled_) {
|
| + errorCallback(util.createFileError(FileError.ABORT_ERR));
|
| + return;
|
| + }
|
| +
|
| + // TODO(tbarzic): Improve error handling.
|
| + if (!entries) {
|
| + console.error('Drive search encountered an error.');
|
| + errorCallback(util.createFileError(
|
| + FileError.INVALID_MODIFICATION_ERR));
|
| + return;
|
| + }
|
| +
|
| + var numRemainingEntries =
|
| + DriveSearchContentScanner.MAX_RESULTS_ - numReadEntries;
|
| + if (entries.length >= numRemainingEntries) {
|
| + // The limit is hit, so quit the scan here.
|
| + entries = entries.slice(0, numRemainingEntries);
|
| + nextFeed = '';
|
| + }
|
| +
|
| + numReadEntries += entries.length;
|
| + if (entries.length > 0)
|
| + entriesCallback(entries);
|
| +
|
| + if (nextFeed === '')
|
| + successCallback();
|
| + else
|
| + readEntries(nextFeed);
|
| + }.bind(this));
|
| + }.bind(this);
|
| +
|
| + // Let's give another search a chance to cancel us before we begin.
|
| + setTimeout(
|
| + function() {
|
| + // Check cancelled state before read the entries.
|
| + if (this.cancelled_) {
|
| + errorCallback(util.createFileError(FileError.ABORT_ERR));
|
| + return;
|
| + }
|
| + readEntries('');
|
| + }.bind(this),
|
| + DriveSearchContentScanner.SCAN_DELAY_);
|
| +};
|
| +
|
| +/**
|
| + * Scanner of the entries of the file name search on the directory tree, whose
|
| + * root is entry.
|
| + * @param {DirectoryEntry} entry The root of the search target directory tree.
|
| + * @param {string} query The query of the search.
|
| + * @constructor
|
| + * @extends {ContentScanner}
|
| + */
|
| +function LocalSearchContentScanner(entry, query) {
|
| + ContentScanner.call(this);
|
| + this.entry_ = entry;
|
| + this.query_ = query.toLowerCase();
|
| +}
|
| +
|
| +/**
|
| + * Extedns ContentScanner.
|
| + */
|
| +LocalSearchContentScanner.prototype.__proto__ = ContentScanner.prototype;
|
| +
|
| +/**
|
| + * Starts the file name search.
|
| + * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of
|
| + * entries are read. This can be called a couple of times until the
|
| + * completion.
|
| + * @param {function()} successCallback Called when the scan is completed
|
| + * successfully.
|
| + * @param {function(FileError)} errorCallback Called an error occurs.
|
| + * @override
|
| + */
|
| +LocalSearchContentScanner.prototype.scan = function(
|
| + entriesCallback, successCallback, errorCallback) {
|
| + var numRunningTasks = 0;
|
| + var error = null;
|
| + var maybeRunCallback = function() {
|
| + if (numRunningTasks === 0) {
|
| + if (this.cancelled_)
|
| + errorCallback(util.createFileError(FileError.ABORT_ERR));
|
| + else if (error)
|
| + errorCallback(error);
|
| + else
|
| + successCallback();
|
| + }
|
| + }.bind(this);
|
| +
|
| + var processEntry = function(entry) {
|
| + numRunningTasks++;
|
| + var onError = function(fileError) {
|
| + if (!error)
|
| + error = fileError;
|
| + numRunningTasks--;
|
| + maybeRunCallback();
|
| + };
|
| +
|
| + var onSuccess = function(entries) {
|
| + if (this.cancelled_ || error || entries.length === 0) {
|
| + numRunningTasks--;
|
| + maybeRunCallback();
|
| + return;
|
| + }
|
| +
|
| + // Filters by the query, and if found, run entriesCallback.
|
| + var foundEntries = entries.filter(function(entry) {
|
| + return entry.name.toLowerCase().indexOf(this.query_) >= 0;
|
| + }.bind(this));
|
| + if (foundEntries.length > 0)
|
| + entriesCallback(foundEntries);
|
| +
|
| + // Start to process sub directories.
|
| + for (var i = 0; i < entries.length; i++) {
|
| + if (entries[i].isDirectory)
|
| + processEntry(entries[i]);
|
| + }
|
| +
|
| + // Read remaining entries.
|
| + reader.readEntries(onSuccess, onError);
|
| + }.bind(this);
|
| +
|
| + var reader = entry.createReader();
|
| + reader.readEntries(onSuccess, onError);
|
| + }.bind(this);
|
| +
|
| + processEntry(this.entry_);
|
| +};
|
| +
|
| +/**
|
| + * Scanner of the entries for the metadata seaerch on Drive File System.
|
| + * @param {string} query The query of the search.
|
| + * @param {DriveMetadataSearchContentScanner.SearchType} searchType The option
|
| + * of the search.
|
| + * @constructor
|
| + * @extends {ContentScanner}
|
| + */
|
| +function DriveMetadataSearchContentScanner(query, searchType) {
|
| + ContentScanner.call(this);
|
| + this.query_ = query;
|
| + this.searchType_ = searchType;
|
| +}
|
| +
|
| +/**
|
| + * Extends ContentScanner.
|
| + */
|
| +DriveMetadataSearchContentScanner.prototype.__proto__ =
|
| + ContentScanner.prototype;
|
| +
|
| +/**
|
| + * The search types on the Drive File System.
|
| + * @enum {string}
|
| + */
|
| +DriveMetadataSearchContentScanner.SearchType = Object.freeze({
|
| + SEARCH_ALL: 'ALL',
|
| + SEARCH_SHARED_WITH_ME: 'SHARED_WITH_ME',
|
| + SEARCH_RECENT_FILES: 'EXCLUDE_DIRECTORIES',
|
| + SEARCH_OFFLINE: 'OFFLINE'
|
| +});
|
| +
|
| +/**
|
| + * Starts to metadata-search on Drive File System.
|
| + * @param {function(Array.<Entry>)} entriesCallback Called when some chunk of
|
| + * entries are read. This can be called a couple of times until the
|
| + * completion.
|
| + * @param {function()} successCallback Called when the scan is completed
|
| + * successfully.
|
| + * @param {function(FileError)} errorCallback Called an error occurs.
|
| + * @override
|
| + */
|
| +DriveMetadataSearchContentScanner.prototype.scan = function(
|
| + entriesCallback, successCallback, errorCallback) {
|
| + chrome.fileBrowserPrivate.searchDriveMetadata(
|
| + {query: this.query_, types: this.searchType_, maxResults: 500},
|
| + function(results) {
|
| + if (this.cancelled_) {
|
| + errorCallback(util.createFileError(FileError.ABORT_ERR));
|
| + return;
|
| + }
|
| +
|
| + if (!results) {
|
| + console.error('Drive search encountered an error.');
|
| + errorCallback(util.createFileError(
|
| + FileError.INVALID_MODIFICATION_ERR));
|
| + return;
|
| + }
|
| +
|
| + var entries = results.map(function(result) { return result.entry; });
|
| + if (entries.length > 0)
|
| + entriesCallback(entries);
|
| + successCallback();
|
| + }.bind(this));
|
| +};
|
| +
|
| +/**
|
| * This class manages filters and determines a file should be shown or not.
|
| * When filters are changed, a 'changed' event is fired.
|
| *
|
| @@ -19,6 +354,7 @@ function FileFilter(metadataCache, showHidden) {
|
| * @private
|
| */
|
| this.metadataCache_ = metadataCache;
|
| +
|
| /**
|
| * @type Object.<string, Function>
|
| * @private
|
| @@ -102,6 +438,7 @@ function FileListContext(fileFilter, metadataCache) {
|
| * @type {cr.ui.ArrayDataModel}
|
| */
|
| this.fileList = new cr.ui.ArrayDataModel([]);
|
| +
|
| /**
|
| * @type {MetadataCache}
|
| */
|
| @@ -125,11 +462,8 @@ function FileListContext(fileFilter, metadataCache) {
|
| function DirectoryContents(context) {
|
| this.context_ = context;
|
| this.fileList_ = context.fileList;
|
| - this.scanCompletedCallback_ = null;
|
| - this.scanFailedCallback_ = null;
|
| + this.prefetchMetadataQueue_ = new AsyncUtil.Queue();
|
| this.scanCancelled_ = false;
|
| - this.allChunksFetched_ = false;
|
| - this.pendingMetadataRequests_ = 0;
|
| this.fileList_.prepareSort = this.prepareSort_.bind(this);
|
| }
|
|
|
| @@ -173,8 +507,7 @@ DirectoryContents.prototype.replaceContextFileList = function() {
|
| * @return {boolean} If the scan is active.
|
| */
|
| DirectoryContents.prototype.isScanning = function() {
|
| - return !this.scanCancelled_ &&
|
| - (!this.allChunksFetched_ || this.pendingMetadataRequests_ > 0);
|
| + return this.scanner_ || this.prefetchMetadataQueue_.isRunning();
|
| };
|
|
|
| /**
|
| @@ -208,40 +541,84 @@ DirectoryContents.prototype.scan = function() {
|
| };
|
|
|
| /**
|
| - * Read next chunk of results from DirectoryReader.
|
| - * @protected
|
| - */
|
| -DirectoryContents.prototype.readNextChunk = function() {
|
| - throw 'Not implemented.';
|
| -};
|
| -
|
| -/**
|
| - * Cancel the running scan.
|
| + * Cancels the running scan.
|
| */
|
| DirectoryContents.prototype.cancelScan = function() {
|
| if (this.scanCancelled_)
|
| return;
|
| this.scanCancelled_ = true;
|
| + if (this.scanner_)
|
| + this.scanner_.cancel();
|
| +
|
| + this.prefetchMetadataQueue_.cancel();
|
| cr.dispatchSimpleEvent(this, 'scan-cancelled');
|
| };
|
|
|
| +/**
|
| + * Called when the scanning by scanner_ is done.
|
| + * @protected
|
| + */
|
| +DirectoryContents.prototype.onScanCompleted = function() {
|
| + this.scanner_ = null;
|
| + if (this.scanCancelled_)
|
| + return;
|
| +
|
| + this.prefetchMetadataQueue_.run(function(callback) {
|
| + cr.dispatchSimpleEvent(this, 'scan-completed');
|
| + if (!this.isSearch() &&
|
| + this.getDirectoryEntry().fullPath === RootDirectory.DOWNLOADS)
|
| + metrics.recordMediumCount('DownloadsCount', this.fileList_.length);
|
| + callback();
|
| + }.bind(this));
|
| +};
|
|
|
| /**
|
| * Called in case scan has failed. Should send the event.
|
| * @protected
|
| */
|
| -DirectoryContents.prototype.onError = function() {
|
| - cr.dispatchSimpleEvent(this, 'scan-failed');
|
| +DirectoryContents.prototype.onScanError = function() {
|
| + this.scanner_ = null;
|
| + if (this.scanCancelled_)
|
| + return;
|
| +
|
| + this.prefetchMetadataQueue_.run(function(callback) {
|
| + cr.dispatchSimpleEvent(this, 'scan-failed');
|
| + callback();
|
| + }.bind(this));
|
| };
|
|
|
| /**
|
| - * Called in case scan has completed succesfully. Should send the event.
|
| + * Called when some chunk of entries are read by scanner.
|
| + * @param {Array.<Entry>} entries The list of the scanned entries.
|
| * @protected
|
| */
|
| -DirectoryContents.prototype.lastChunkReceived = function() {
|
| - this.allChunksFetched_ = true;
|
| - if (!this.scanCancelled_ && this.pendingMetadataRequests_ === 0)
|
| - cr.dispatchSimpleEvent(this, 'scan-completed');
|
| +DirectoryContents.prototype.onNewEntries = function(entries) {
|
| + if (this.scanCancelled_)
|
| + return;
|
| +
|
| + var entriesFiltered = [].filter.call(
|
| + entries, this.context_.fileFilter.filter.bind(this.context_.fileFilter));
|
| +
|
| + // Because the prefetchMetadata can be slow, throttling by splitting entries
|
| + // into smaller chunks to reduce UI latency.
|
| + // TODO(hidehiko,mtomasz): This should be handled in MetadataCache.
|
| + var MAX_CHUNK_SIZE = 50;
|
| + for (var i = 0; i < entriesFiltered.length; i += MAX_CHUNK_SIZE) {
|
| + var chunk = entriesFiltered.slice(i, i + MAX_CHUNK_SIZE);
|
| + this.prefetchMetadataQueue_.run(function(chunk, callback) {
|
| + this.prefetchMetadata(chunk, function() {
|
| + if (this.scanCancelled_) {
|
| + // Do nothing if the scanning is cancelled.
|
| + callback();
|
| + return;
|
| + }
|
| +
|
| + this.fileList_.push.apply(this.fileList_, chunk);
|
| + cr.dispatchSimpleEvent(this, 'scan-updated');
|
| + callback();
|
| + }.bind(this));
|
| + }.bind(this, chunk));
|
| + }
|
| };
|
|
|
| /**
|
| @@ -275,36 +652,6 @@ DirectoryContents.prototype.reloadMetadata = function(entries, callback) {
|
| };
|
|
|
| /**
|
| - * @protected
|
| - * @param {Array.<Entry>} entries File list.
|
| - */
|
| -DirectoryContents.prototype.onNewEntries = function(entries) {
|
| - if (this.scanCancelled_)
|
| - return;
|
| -
|
| - var entriesFiltered = [].filter.call(
|
| - entries, this.context_.fileFilter.filter.bind(this.context_.fileFilter));
|
| -
|
| - var onPrefetched = function() {
|
| - this.pendingMetadataRequests_--;
|
| - if (this.scanCancelled_)
|
| - return;
|
| - this.fileList_.push.apply(this.fileList_, entriesFiltered);
|
| -
|
| - if (this.pendingMetadataRequests_ === 0 && this.allChunksFetched_)
|
| - cr.dispatchSimpleEvent(this, 'scan-completed');
|
| - else
|
| - cr.dispatchSimpleEvent(this, 'scan-updated');
|
| -
|
| - if (!this.allChunksFetched_)
|
| - this.readNextChunk();
|
| - };
|
| -
|
| - this.pendingMetadataRequests_++;
|
| - this.prefetchMetadata(entriesFiltered, onPrefetched.bind(this));
|
| -};
|
| -
|
| -/**
|
| * @param {string} name Directory name.
|
| * @param {function(DirectoryEntry)} successCallback Called on success.
|
| * @param {function(FileError)} errorCallback On error.
|
| @@ -357,49 +704,12 @@ DirectoryContentsBasic.prototype.getLastNonSearchDirectoryEntry = function() {
|
| * Start directory scan.
|
| */
|
| DirectoryContentsBasic.prototype.scan = function() {
|
| - if (!this.entry_ || this.entry_ === DirectoryModel.fakeDriveEntry_) {
|
| - this.lastChunkReceived();
|
| - return;
|
| - }
|
| -
|
| - metrics.startInterval('DirectoryScan');
|
| - this.reader_ = this.entry_.createReader();
|
| - this.readNextChunk();
|
| -};
|
| -
|
| -/**
|
| - * Read next chunk of results from DirectoryReader.
|
| - * @protected
|
| - */
|
| -DirectoryContentsBasic.prototype.readNextChunk = function() {
|
| - this.reader_.readEntries(this.onChunkComplete_.bind(this),
|
| - this.onError.bind(this));
|
| -};
|
| -
|
| -/**
|
| - * @param {Array.<Entry>} entries File list.
|
| - * @private
|
| - */
|
| -DirectoryContentsBasic.prototype.onChunkComplete_ = function(entries) {
|
| - if (this.scanCancelled_)
|
| - return;
|
| -
|
| - if (entries.length == 0) {
|
| - this.lastChunkReceived();
|
| - this.recordMetrics_();
|
| - return;
|
| - }
|
| -
|
| - this.onNewEntries(entries);
|
| -};
|
| -
|
| -/**
|
| - * @private
|
| - */
|
| -DirectoryContentsBasic.prototype.recordMetrics_ = function() {
|
| - metrics.recordInterval('DirectoryScan');
|
| - if (this.entry_.fullPath === RootDirectory.DOWNLOADS)
|
| - metrics.recordMediumCount('DownloadsCount', this.fileList_.length);
|
| + // TODO(hidehiko,mtomasz): this scan method must be called at most once.
|
| + // Remove such a limitation.
|
| + this.scanner_ = new DirectoryContentScanner(this.entry_);
|
| + this.scanner_.scan(this.onNewEntries.bind(this),
|
| + this.onScanCompleted.bind(this),
|
| + this.onScanError.bind(this));
|
| };
|
|
|
| /**
|
| @@ -409,6 +719,8 @@ DirectoryContentsBasic.prototype.recordMetrics_ = function() {
|
| */
|
| DirectoryContentsBasic.prototype.createDirectory = function(
|
| name, successCallback, errorCallback) {
|
| + // TODO(hidehiko): createDirectory should not be the part of
|
| + // DirectoryContent.
|
| if (!this.entry_) {
|
| errorCallback(util.createFileError(FileError.INVALID_MODIFICATION_ERR));
|
| return;
|
| @@ -425,24 +737,6 @@ DirectoryContentsBasic.prototype.createDirectory = function(
|
| };
|
|
|
| /**
|
| - * Delay to be used for drive search scan.
|
| - * The goal is to reduce the number of server requests when user is typing the
|
| - * query.
|
| - *
|
| - * @type {number}
|
| - * @const
|
| - */
|
| -DirectoryContentsDriveSearch.SCAN_DELAY = 200;
|
| -
|
| -/**
|
| - * Maximum number of results which is shown on the search.
|
| - *
|
| - * @type {number}
|
| - * @const
|
| - */
|
| -DirectoryContentsDriveSearch.MAX_RESULTS = 100;
|
| -
|
| -/**
|
| * @param {FileListContext} context File list context.
|
| * @param {DirectoryEntry} dirEntry Current directory.
|
| * @param {DirectoryEntry} previousDirEntry DirectoryEntry that was current
|
| @@ -459,9 +753,6 @@ function DirectoryContentsDriveSearch(context,
|
| this.directoryEntry_ = dirEntry;
|
| this.previousDirectoryEntry_ = previousDirEntry;
|
| this.query_ = query;
|
| - this.nextFeed_ = '';
|
| - this.done_ = false;
|
| - this.fetchedResultsNum_ = 0;
|
| }
|
|
|
| /**
|
| @@ -507,53 +798,12 @@ DirectoryContentsDriveSearch.prototype.getLastNonSearchDirectoryEntry =
|
| * Start directory scan.
|
| */
|
| DirectoryContentsDriveSearch.prototype.scan = function() {
|
| - // Let's give another search a chance to cancel us before we begin.
|
| - setTimeout(this.readNextChunk.bind(this),
|
| - DirectoryContentsDriveSearch.SCAN_DELAY);
|
| -};
|
| -
|
| -/**
|
| - * All the results are read in one chunk, so when we try to read second chunk,
|
| - * it means we're done.
|
| - */
|
| -DirectoryContentsDriveSearch.prototype.readNextChunk = function() {
|
| - if (this.scanCancelled_)
|
| - return;
|
| -
|
| - if (this.done_) {
|
| - this.lastChunkReceived();
|
| - return;
|
| - }
|
| -
|
| - var searchCallback = (function(entries, nextFeed) {
|
| - // TODO(tbarzic): Improve error handling.
|
| - if (!entries) {
|
| - console.error('Drive search encountered an error.');
|
| - this.lastChunkReceived();
|
| - return;
|
| - }
|
| - this.nextFeed_ = nextFeed;
|
| - var remaining =
|
| - DirectoryContentsDriveSearch.MAX_RESULTS - this.fetchedResultsNum_;
|
| - if (entries.length >= remaining) {
|
| - entries = entries.slice(0, remaining);
|
| - this.nextFeed_ = '';
|
| - }
|
| - this.fetchedResultsNum_ += entries.length;
|
| -
|
| - this.done_ = (this.nextFeed_ == '');
|
| -
|
| - this.onNewEntries(entries);
|
| - }).bind(this);
|
| -
|
| - var searchParams = {
|
| - 'query': this.query_,
|
| - 'nextFeed': this.nextFeed_
|
| - };
|
| - chrome.fileBrowserPrivate.searchDrive(searchParams, searchCallback);
|
| + this.scanner_ = new DriveSearchContentScanner(this.query_);
|
| + this.scanner_.scan(this.onNewEntries.bind(this),
|
| + this.onScanCompleted.bind(this),
|
| + this.onScanError.bind(this));
|
| };
|
|
|
| -
|
| /**
|
| * @param {FileListContext} context File list context.
|
| * @param {DirectoryEntry} dirEntry Current directory.
|
| @@ -564,7 +814,7 @@ DirectoryContentsDriveSearch.prototype.readNextChunk = function() {
|
| function DirectoryContentsLocalSearch(context, dirEntry, query) {
|
| DirectoryContents.call(this, context);
|
| this.directoryEntry_ = dirEntry;
|
| - this.query_ = query.toLowerCase();
|
| + this.query_ = query;
|
| }
|
|
|
| /**
|
| @@ -610,72 +860,11 @@ DirectoryContentsLocalSearch.prototype.getLastNonSearchDirectoryEntry =
|
| * 'scan-failed' event will be fired upon completion.
|
| */
|
| DirectoryContentsLocalSearch.prototype.scan = function() {
|
| - this.pendingScans_ = 0;
|
| - this.scanDirectory_(this.directoryEntry_);
|
| -};
|
| -
|
| -/**
|
| - * Scan a directory.
|
| - * @param {DirectoryEntry} entry A directory to scan.
|
| - * @private
|
| - */
|
| -DirectoryContentsLocalSearch.prototype.scanDirectory_ = function(entry) {
|
| - this.pendingScans_++;
|
| - var reader = entry.createReader();
|
| - var found = [];
|
| -
|
| - var onChunkComplete = function(entries) {
|
| - if (this.scanCancelled_)
|
| - return;
|
| -
|
| - if (entries.length === 0) {
|
| - if (found.length > 0)
|
| - this.onNewEntries(found);
|
| - this.pendingScans_--;
|
| - if (this.pendingScans_ === 0)
|
| - this.lastChunkReceived();
|
| - return;
|
| - }
|
| -
|
| - for (var i = 0; i < entries.length; i++) {
|
| - if (entries[i].name.toLowerCase().indexOf(this.query_) != -1) {
|
| - found.push(entries[i]);
|
| - }
|
| -
|
| - if (entries[i].isDirectory)
|
| - this.scanDirectory_(entries[i]);
|
| - }
|
| -
|
| - getNextChunk();
|
| - }.bind(this);
|
| -
|
| - var getNextChunk = function() {
|
| - reader.readEntries(onChunkComplete, this.onError.bind(this));
|
| - }.bind(this);
|
| -
|
| - getNextChunk();
|
| -};
|
| -
|
| -/**
|
| - * We get results for each directory in one go in scanDirectory_.
|
| - */
|
| -DirectoryContentsLocalSearch.prototype.readNextChunk = function() {
|
| -};
|
| -
|
| -/**
|
| - * List of search types for DirectoryContentsDriveSearch.
|
| - * TODO(haruki): SHARED_WITH_ME support for searchDriveMetadata is not yet
|
| - * implemented. Update this when it's done.
|
| - * SEARCH_ALL uses no filtering.
|
| - * SEARCH_SHARED_WITH_ME searches for the shared-with-me entries.
|
| - * SEARCH_RECENT_FILES searches for recently accessed file entries.
|
| - * @enum {number}
|
| - */
|
| -DirectoryContentsDriveSearchMetadata.SearchType = {
|
| - SEARCH_ALL: 0,
|
| - SEARCH_SHARED_WITH_ME: 1,
|
| - SEARCH_RECENT_FILES: 2,
|
| - SEARCH_OFFLINE: 3
|
| + this.scanner_ =
|
| + new LocalSearchContentScanner(this.directoryEntry_, this.query_);
|
| + this.scanner_.scan(this.onNewEntries.bind(this),
|
| + this.onScanCompleted.bind(this),
|
| + this.onScanError.bind(this));
|
| };
|
|
|
| /**
|
| @@ -686,7 +875,7 @@ DirectoryContentsDriveSearchMetadata.SearchType = {
|
| * @param {DirectoryEntry} fakeDirEntry Fake directory representing the set of
|
| * result files. This serves as a top directory for this search.
|
| * @param {string} query Search query to filter the files.
|
| - * @param {DirectoryContentsDriveSearchMetadata.SearchType} searchType
|
| + * @param {DriveMetadataSearchContentScanner.SearchType} searchType
|
| * Type of search. searchDriveMetadata will restricts the entries based on
|
| * the given search type.
|
| * @constructor
|
| @@ -749,56 +938,9 @@ DirectoryContentsDriveSearchMetadata.prototype.getLastNonSearchDirectoryEntry =
|
| * 'scan-failed' event will be fired upon completion.
|
| */
|
| DirectoryContentsDriveSearchMetadata.prototype.scan = function() {
|
| - this.readNextChunk();
|
| -};
|
| -
|
| -/**
|
| - * All the results are read in one chunk, so when we try to read second chunk,
|
| - * it means we're done.
|
| - */
|
| -DirectoryContentsDriveSearchMetadata.prototype.readNextChunk = function() {
|
| - if (this.scanCancelled_)
|
| - return;
|
| -
|
| - if (this.done_) {
|
| - this.lastChunkReceived();
|
| - return;
|
| - }
|
| -
|
| - var searchCallback = (function(results, nextFeed) {
|
| - if (!results) {
|
| - console.error('Drive search encountered an error.');
|
| - this.lastChunkReceived();
|
| - return;
|
| - }
|
| - this.done_ = true;
|
| -
|
| - var entries = results.map(function(r) { return r.entry; });
|
| - this.onNewEntries(entries);
|
| - this.lastChunkReceived();
|
| - }).bind(this);
|
| -
|
| - var type;
|
| - switch (this.searchType_) {
|
| - case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_ALL:
|
| - type = 'ALL';
|
| - break;
|
| - case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_SHARED_WITH_ME:
|
| - type = 'SHARED_WITH_ME';
|
| - break;
|
| - case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_RECENT_FILES:
|
| - type = 'EXCLUDE_DIRECTORIES';
|
| - break;
|
| - case DirectoryContentsDriveSearchMetadata.SearchType.SEARCH_OFFLINE:
|
| - type = 'OFFLINE';
|
| - break;
|
| - default:
|
| - throw Error('Unknown search type: ' + this.searchType_);
|
| - }
|
| - var searchParams = {
|
| - 'query': this.query_,
|
| - 'types': type,
|
| - 'maxResults': 500
|
| - };
|
| - chrome.fileBrowserPrivate.searchDriveMetadata(searchParams, searchCallback);
|
| + this.scanner_ =
|
| + new DriveMetadataSearchContentScanner(this.query_, this.searchType_);
|
| + this.scanner_.scan(this.onNewEntries.bind(this),
|
| + this.onScanCompleted.bind(this),
|
| + this.onScanError.bind(this));
|
| };
|
|
|