blob: c1d5d746575d0824452e658fa1c8151799dd258d [file] [log] [blame]
/*
* Copyright (C) 2011 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:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS 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 GOOGLE INC.
* OR ITS 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
*/
Sources.NetworkNavigatorView = class extends Sources.NavigatorView {
constructor() {
super();
SDK.targetManager.addEventListener(SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this);
// Record the sources tool load time after the file navigator has loaded.
Host.userMetrics.panelLoaded('sources', 'DevTools.Launch.Sources');
}
/**
* @override
* @param {!Workspace.Project} project
* @return {boolean}
*/
acceptProject(project) {
return project.type() === Workspace.projectTypes.Network;
}
/**
* @param {!Common.Event} event
*/
_inspectedURLChanged(event) {
const mainTarget = SDK.targetManager.mainTarget();
if (event.data !== mainTarget) {
return;
}
const inspectedURL = mainTarget && mainTarget.inspectedURL();
if (!inspectedURL) {
return;
}
for (const uiSourceCode of this.workspace().uiSourceCodes()) {
if (this.acceptProject(uiSourceCode.project()) && uiSourceCode.url() === inspectedURL) {
this.revealUISourceCode(uiSourceCode, true);
}
}
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
*/
uiSourceCodeAdded(uiSourceCode) {
const mainTarget = SDK.targetManager.mainTarget();
const inspectedURL = mainTarget && mainTarget.inspectedURL();
if (!inspectedURL) {
return;
}
if (uiSourceCode.url() === inspectedURL) {
this.revealUISourceCode(uiSourceCode, true);
}
}
};
/**
* @unrestricted
*/
Sources.FilesNavigatorView = class extends Sources.NavigatorView {
constructor() {
super();
const placeholder = new UI.EmptyWidget('');
this.setPlaceholder(placeholder);
placeholder.appendParagraph().appendChild(UI.html`
<div>${ls`Sync changes in DevTools with the local filesystem`}</div><br />
${UI.XLink.create('https://developers.google.com/web/tools/chrome-devtools/workspaces/', ls`Learn more`)}
`);
const toolbar = new UI.Toolbar('navigator-toolbar');
toolbar.appendItemsAtLocation('files-navigator-toolbar').then(() => {
if (!toolbar.empty()) {
this.contentElement.insertBefore(toolbar.element, this.contentElement.firstChild);
}
});
}
/**
* @override
* @param {!Workspace.Project} project
* @return {boolean}
*/
acceptProject(project) {
return project.type() === Workspace.projectTypes.FileSystem &&
Persistence.FileSystemWorkspaceBinding.fileSystemType(project) !== 'overrides' &&
!Snippets.isSnippetsProject(project);
}
/**
* @override
* @param {!Event} event
*/
handleContextMenu(event) {
const contextMenu = new UI.ContextMenu(event);
contextMenu.defaultSection().appendAction('sources.add-folder-to-workspace', undefined, true);
contextMenu.show();
}
};
Sources.OverridesNavigatorView = class extends Sources.NavigatorView {
constructor() {
super();
const placeholder = new UI.EmptyWidget('');
this.setPlaceholder(placeholder);
placeholder.appendParagraph().appendChild(UI.html`
<div>${ls`Override page assets with files from a local folder`}</div><br />
${UI.XLink.create('https://developers.google.com/web/updates/2018/01/devtools#overrides', ls`Learn more`)}
`);
this._toolbar = new UI.Toolbar('navigator-toolbar');
this.contentElement.insertBefore(this._toolbar.element, this.contentElement.firstChild);
Persistence.networkPersistenceManager.addEventListener(
Persistence.NetworkPersistenceManager.Events.ProjectChanged, this._updateProjectAndUI, this);
this.workspace().addEventListener(Workspace.Workspace.Events.ProjectAdded, this._onProjectAddOrRemoved, this);
this.workspace().addEventListener(Workspace.Workspace.Events.ProjectRemoved, this._onProjectAddOrRemoved, this);
this._updateProjectAndUI();
}
/**
* @param {!Common.Event} event
*/
_onProjectAddOrRemoved(event) {
const project = /** @type {!Workspace.Project} */ (event.data);
if (project && project.type() === Workspace.projectTypes.FileSystem &&
Persistence.FileSystemWorkspaceBinding.fileSystemType(project) !== 'overrides') {
return;
}
this._updateUI();
}
_updateProjectAndUI() {
this.reset();
const project = Persistence.networkPersistenceManager.project();
if (project) {
this.tryAddProject(project);
}
this._updateUI();
}
_updateUI() {
this._toolbar.removeToolbarItems();
const project = Persistence.networkPersistenceManager.project();
if (project) {
const enableCheckbox =
new UI.ToolbarSettingCheckbox(Common.settings.moduleSetting('persistenceNetworkOverridesEnabled'));
this._toolbar.appendToolbarItem(enableCheckbox);
this._toolbar.appendToolbarItem(new UI.ToolbarSeparator(true));
const clearButton = new UI.ToolbarButton(Common.UIString('Clear configuration'), 'largeicon-clear');
clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => {
project.remove();
});
this._toolbar.appendToolbarItem(clearButton);
return;
}
const title = Common.UIString('Select folder for overrides');
const setupButton = new UI.ToolbarButton(title, 'largeicon-add', title);
setupButton.addEventListener(UI.ToolbarButton.Events.Click, this._setupNewWorkspace, this);
this._toolbar.appendToolbarItem(setupButton);
}
async _setupNewWorkspace() {
const fileSystem = await Persistence.isolatedFileSystemManager.addFileSystem('overrides');
if (!fileSystem) {
return;
}
Common.settings.moduleSetting('persistenceNetworkOverridesEnabled').set(true);
}
/**
* @override
* @param {!Workspace.Project} project
* @return {boolean}
*/
acceptProject(project) {
return project === Persistence.networkPersistenceManager.project();
}
};
/**
* @unrestricted
*/
Sources.ContentScriptsNavigatorView = class extends Sources.NavigatorView {
constructor() {
super();
const placeholder = new UI.EmptyWidget('');
this.setPlaceholder(placeholder);
placeholder.appendParagraph().appendChild(UI.html`
<div>${ls`Content scripts served by extensions appear here`}</div><br />
${UI.XLink.create('https://developer.chrome.com/extensions/content_scripts', ls`Learn more`)}
`);
}
/**
* @override
* @param {!Workspace.Project} project
* @return {boolean}
*/
acceptProject(project) {
return project.type() === Workspace.projectTypes.ContentScripts;
}
};
/**
* @unrestricted
*/
Sources.SnippetsNavigatorView = class extends Sources.NavigatorView {
constructor() {
super();
const placeholder = new UI.EmptyWidget('');
this.setPlaceholder(placeholder);
placeholder.appendParagraph().appendChild(UI.html`
<div>${ls`Create and save code snippets for later reuse`}</div><br />
${UI.XLink.create('https://developers.google.com/web/tools/chrome-devtools/javascript/snippets', ls`Learn more`)}
`);
const toolbar = new UI.Toolbar('navigator-toolbar');
const newButton = new UI.ToolbarButton('', 'largeicon-add', Common.UIString('New snippet'));
newButton.addEventListener(UI.ToolbarButton.Events.Click, () => this.create(Snippets.project, ''));
toolbar.appendToolbarItem(newButton);
this.contentElement.insertBefore(toolbar.element, this.contentElement.firstChild);
}
/**
* @override
* @param {!Workspace.Project} project
* @return {boolean}
*/
acceptProject(project) {
return Snippets.isSnippetsProject(project);
}
/**
* @override
* @param {!Event} event
*/
handleContextMenu(event) {
const contextMenu = new UI.ContextMenu(event);
contextMenu.headerSection().appendItem(ls`Create new snippet`, () => this.create(Snippets.project, ''));
contextMenu.show();
}
/**
* @override
* @param {!Event} event
* @param {!Sources.NavigatorUISourceCodeTreeNode} node
*/
handleFileContextMenu(event, node) {
const uiSourceCode = node.uiSourceCode();
const contextMenu = new UI.ContextMenu(event);
contextMenu.headerSection().appendItem(Common.UIString('Run'), () => Snippets.evaluateScriptSnippet(uiSourceCode));
contextMenu.editSection().appendItem(Common.UIString('Rename\u2026'), () => this.rename(node, false));
contextMenu.editSection().appendItem(
Common.UIString('Remove'), () => uiSourceCode.project().deleteFile(uiSourceCode));
contextMenu.saveSection().appendItem(Common.UIString('Save as...'), this._handleSaveAs.bind(this, uiSourceCode));
contextMenu.show();
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
async _handleSaveAs(uiSourceCode) {
uiSourceCode.commitWorkingCopy();
const {content} = await uiSourceCode.requestContent();
Workspace.fileManager.save(uiSourceCode.url(), content || '', true);
Workspace.fileManager.close(uiSourceCode.url());
}
};
/**
* @implements {UI.ActionDelegate}
*/
Sources.ActionDelegate = class {
/**
* @override
* @param {!UI.Context} context
* @param {string} actionId
* @return {boolean}
*/
handleAction(context, actionId) {
switch (actionId) {
case 'sources.create-snippet':
Snippets.project.createFile('', null, '').then(uiSourceCode => Common.Revealer.reveal(uiSourceCode));
return true;
case 'sources.add-folder-to-workspace':
Persistence.isolatedFileSystemManager.addFileSystem();
return true;
}
return false;
}
};