blob: c337f51dad2ff9eb87eafb035fbdf813cd72c217 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @unrestricted
*/
export default class IsolatedFileSystemManager extends Common.Object {
constructor() {
super();
/** @type {!Map<string, !Persistence.PlatformFileSystem>} */
this._fileSystems = new Map();
/** @type {!Map<number, function(!Array.<string>)>} */
this._callbacks = new Map();
/** @type {!Map<number, !Common.Progress>} */
this._progresses = new Map();
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.FileSystemRemoved, this._onFileSystemRemoved, this);
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.FileSystemAdded, this._onFileSystemAdded, this);
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved, this._onFileSystemFilesChanged, this);
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, this._onIndexingTotalWorkCalculated, this);
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.IndexingWorked, this._onIndexingWorked, this);
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.IndexingDone, this._onIndexingDone, this);
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.SearchCompleted, this._onSearchCompleted, this);
this._initExcludePatterSetting();
/** @type {?function(?Persistence.IsolatedFileSystem)} */
this._fileSystemRequestResolve = null;
this._fileSystemsLoadedPromise = this._requestFileSystems();
}
/**
* @return {!Promise<!Array<!Persistence.IsolatedFileSystem>>}
*/
_requestFileSystems() {
let fulfill;
const promise = new Promise(f => fulfill = f);
Host.InspectorFrontendHost.events.addEventListener(
Host.InspectorFrontendHostAPI.Events.FileSystemsLoaded, onFileSystemsLoaded, this);
Host.InspectorFrontendHost.requestFileSystems();
return promise;
/**
* @param {!Common.Event} event
* @this {IsolatedFileSystemManager}
*/
function onFileSystemsLoaded(event) {
const fileSystems = /** @type {!Array.<!Persistence.IsolatedFileSystemManager.FileSystem>} */ (event.data);
const promises = [];
for (let i = 0; i < fileSystems.length; ++i) {
promises.push(this._innerAddFileSystem(fileSystems[i], false));
}
Promise.all(promises).then(onFileSystemsAdded);
}
/**
* @param {!Array<?Persistence.IsolatedFileSystem>} fileSystems
*/
function onFileSystemsAdded(fileSystems) {
fulfill(fileSystems.filter(fs => !!fs));
}
}
/**
* @param {string=} type
* @return {!Promise<?Persistence.IsolatedFileSystem>}
*/
addFileSystem(type) {
return new Promise(resolve => {
this._fileSystemRequestResolve = resolve;
Host.InspectorFrontendHost.addFileSystem(type || '');
});
}
/**
* @param {!Persistence.PlatformFileSystem} fileSystem
*/
removeFileSystem(fileSystem) {
Host.InspectorFrontendHost.removeFileSystem(fileSystem.embedderPath());
}
/**
* @return {!Promise<!Array<!Persistence.IsolatedFileSystem>>}
*/
waitForFileSystems() {
return this._fileSystemsLoadedPromise;
}
/**
* @param {!Persistence.IsolatedFileSystemManager.FileSystem} fileSystem
* @param {boolean} dispatchEvent
* @return {!Promise<?Persistence.IsolatedFileSystem>}
*/
_innerAddFileSystem(fileSystem, dispatchEvent) {
const embedderPath = fileSystem.fileSystemPath;
const fileSystemURL = Common.ParsedURL.platformPathToURL(fileSystem.fileSystemPath);
const promise = Persistence.IsolatedFileSystem.create(
this, fileSystemURL, embedderPath, fileSystem.type, fileSystem.fileSystemName, fileSystem.rootURL);
return promise.then(storeFileSystem.bind(this));
/**
* @param {?Persistence.PlatformFileSystem} fileSystem
* @this {IsolatedFileSystemManager}
*/
function storeFileSystem(fileSystem) {
if (!fileSystem) {
return null;
}
this._fileSystems.set(fileSystemURL, fileSystem);
if (dispatchEvent) {
this.dispatchEventToListeners(Events.FileSystemAdded, fileSystem);
}
return fileSystem;
}
}
/**
* @param {string} fileSystemURL
* @param {!Persistence.PlatformFileSystem} fileSystem
*/
addPlatformFileSystem(fileSystemURL, fileSystem) {
this._fileSystems.set(fileSystemURL, fileSystem);
this.dispatchEventToListeners(Events.FileSystemAdded, fileSystem);
}
/**
* @param {!Common.Event} event
*/
async _onFileSystemAdded(event) {
const errorMessage = /** @type {string} */ (event.data['errorMessage']);
let fileSystem = /** @type {?Persistence.IsolatedFileSystemManager.FileSystem} */ (event.data['fileSystem']);
if (errorMessage) {
Common.console.error(Common.UIString('Unable to add filesystem: %s', errorMessage));
if (!this._fileSystemRequestResolve) {
return;
}
this._fileSystemRequestResolve.call(null, null);
this._fileSystemRequestResolve = null;
} else if (fileSystem) {
fileSystem = await this._innerAddFileSystem(fileSystem, true);
if (this._fileSystemRequestResolve) {
this._fileSystemRequestResolve.call(null, fileSystem);
this._fileSystemRequestResolve = null;
}
}
}
/**
* @param {!Common.Event} event
*/
_onFileSystemRemoved(event) {
const embedderPath = /** @type {string} */ (event.data);
const fileSystemPath = Common.ParsedURL.platformPathToURL(embedderPath);
const isolatedFileSystem = this._fileSystems.get(fileSystemPath);
if (!isolatedFileSystem) {
return;
}
this._fileSystems.delete(fileSystemPath);
isolatedFileSystem.fileSystemRemoved();
this.dispatchEventToListeners(Events.FileSystemRemoved, isolatedFileSystem);
}
/**
* @param {!Common.Event} event
*/
_onFileSystemFilesChanged(event) {
const urlPaths = {
changed: groupFilePathsIntoFileSystemPaths.call(this, event.data.changed),
added: groupFilePathsIntoFileSystemPaths.call(this, event.data.added),
removed: groupFilePathsIntoFileSystemPaths.call(this, event.data.removed)
};
this.dispatchEventToListeners(Events.FileSystemFilesChanged, urlPaths);
/**
* @param {!Array<string>} embedderPaths
* @return {!Platform.Multimap<string, string>}
* @this {IsolatedFileSystemManager}
*/
function groupFilePathsIntoFileSystemPaths(embedderPaths) {
const paths = new Platform.Multimap();
for (const embedderPath of embedderPaths) {
const filePath = Common.ParsedURL.platformPathToURL(embedderPath);
for (const fileSystemPath of this._fileSystems.keys()) {
if (this._fileSystems.get(fileSystemPath).isFileExcluded(embedderPath)) {
continue;
}
const pathPrefix = fileSystemPath.endsWith('/') ? fileSystemPath : fileSystemPath + '/';
if (!filePath.startsWith(pathPrefix)) {
continue;
}
paths.set(fileSystemPath, filePath);
}
}
return paths;
}
}
/**
* @return {!Array<!Persistence.IsolatedFileSystem>}
*/
fileSystems() {
return this._fileSystems.valuesArray();
}
/**
* @param {string} fileSystemPath
* @return {?Persistence.PlatformFileSystem}
*/
fileSystem(fileSystemPath) {
return this._fileSystems.get(fileSystemPath) || null;
}
_initExcludePatterSetting() {
const defaultCommonExcludedFolders = [
'/node_modules/', '/bower_components/', '/\\.devtools', '/\\.git/', '/\\.sass-cache/', '/\\.hg/', '/\\.idea/',
'/\\.svn/', '/\\.cache/', '/\\.project/'
];
const defaultWinExcludedFolders = ['/Thumbs.db$', '/ehthumbs.db$', '/Desktop.ini$', '/\\$RECYCLE.BIN/'];
const defaultMacExcludedFolders = [
'/\\.DS_Store$', '/\\.Trashes$', '/\\.Spotlight-V100$', '/\\.AppleDouble$', '/\\.LSOverride$', '/Icon$',
'/\\._.*$'
];
const defaultLinuxExcludedFolders = ['/.*~$'];
let defaultExcludedFolders = defaultCommonExcludedFolders;
if (Host.isWin()) {
defaultExcludedFolders = defaultExcludedFolders.concat(defaultWinExcludedFolders);
} else if (Host.isMac()) {
defaultExcludedFolders = defaultExcludedFolders.concat(defaultMacExcludedFolders);
} else {
defaultExcludedFolders = defaultExcludedFolders.concat(defaultLinuxExcludedFolders);
}
const defaultExcludedFoldersPattern = defaultExcludedFolders.join('|');
this._workspaceFolderExcludePatternSetting = Common.settings.createRegExpSetting(
'workspaceFolderExcludePattern', defaultExcludedFoldersPattern, Host.isWin() ? 'i' : '');
}
/**
* @return {!Common.Setting}
*/
workspaceFolderExcludePatternSetting() {
return this._workspaceFolderExcludePatternSetting;
}
/**
* @param {function(!Array.<string>)} callback
* @return {number}
*/
registerCallback(callback) {
const requestId = ++_lastRequestId;
this._callbacks.set(requestId, callback);
return requestId;
}
/**
* @param {!Common.Progress} progress
* @return {number}
*/
registerProgress(progress) {
const requestId = ++_lastRequestId;
this._progresses.set(requestId, progress);
return requestId;
}
/**
* @param {!Common.Event} event
*/
_onIndexingTotalWorkCalculated(event) {
const requestId = /** @type {number} */ (event.data['requestId']);
const totalWork = /** @type {number} */ (event.data['totalWork']);
const progress = this._progresses.get(requestId);
if (!progress) {
return;
}
progress.setTotalWork(totalWork);
}
/**
* @param {!Common.Event} event
*/
_onIndexingWorked(event) {
const requestId = /** @type {number} */ (event.data['requestId']);
const worked = /** @type {number} */ (event.data['worked']);
const progress = this._progresses.get(requestId);
if (!progress) {
return;
}
progress.worked(worked);
if (progress.isCanceled()) {
Host.InspectorFrontendHost.stopIndexing(requestId);
this._onIndexingDone(event);
}
}
/**
* @param {!Common.Event} event
*/
_onIndexingDone(event) {
const requestId = /** @type {number} */ (event.data['requestId']);
const progress = this._progresses.get(requestId);
if (!progress) {
return;
}
progress.done();
this._progresses.delete(requestId);
}
/**
* @param {!Common.Event} event
*/
_onSearchCompleted(event) {
const requestId = /** @type {number} */ (event.data['requestId']);
const files = /** @type {!Array.<string>} */ (event.data['files']);
const callback = this._callbacks.get(requestId);
if (!callback) {
return;
}
callback.call(null, files);
this._callbacks.delete(requestId);
}
}
/** @enum {symbol} */
export const Events = {
FileSystemAdded: Symbol('FileSystemAdded'),
FileSystemRemoved: Symbol('FileSystemRemoved'),
FileSystemFilesChanged: Symbol('FileSystemFilesChanged'),
ExcludedFolderAdded: Symbol('ExcludedFolderAdded'),
ExcludedFolderRemoved: Symbol('ExcludedFolderRemoved')
};
let _lastRequestId = 0;
/* Legacy exported object */
self.Persistence = self.Persistence || {};
/* Legacy exported object */
Persistence = Persistence || {};
/** @constructor */
Persistence.IsolatedFileSystemManager = IsolatedFileSystemManager;
/** @enum {symbol} */
Persistence.IsolatedFileSystemManager.Events = Events;
/** @typedef {!{type: string, fileSystemName: string, rootURL: string, fileSystemPath: string}} */
Persistence.IsolatedFileSystemManager.FileSystem;
/** @typedef {!{changed:!Platform.Multimap<string, string>, added:!Platform.Multimap<string, string>, removed:!Platform.Multimap<string, string>}} */
Persistence.IsolatedFileSystemManager.FilesChangedData;
/**
* @type {!IsolatedFileSystemManager}
*/
Persistence.isolatedFileSystemManager;