diff --git a/BUILD.gn b/BUILD.gn index 04c3645c95..e4036fd24e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -121,11 +121,7 @@ if (external_devtools_frontend) { "front_end/cookie_table/CookiesTable.js", "front_end/cookie_table/module.json", "front_end/coverage/coverageListView.css", - "front_end/coverage/CoverageDecorationManager.js", - "front_end/coverage/CoverageListView.js", - "front_end/coverage/CoverageModel.js", "front_end/coverage/coverageView.css", - "front_end/coverage/CoverageView.js", "front_end/coverage/module.json", "front_end/coverage_test_runner/CoverageTestRunner.js", "front_end/coverage_test_runner/module.json", @@ -711,6 +707,11 @@ if (external_devtools_frontend) { all_devtools_files += lighthouse_locale_files all_devtools_modules = [ + "front_end/coverage/coverage.js", + "front_end/coverage/CoverageView.js", + "front_end/coverage/CoverageModel.js", + "front_end/coverage/CoverageListView.js", + "front_end/coverage/CoverageDecorationManager.js", "front_end/console/console.js", "front_end/console/ConsoleContextSelector.js", "front_end/console/ConsoleFilter.js", @@ -1218,6 +1219,11 @@ if (external_devtools_frontend) { ] copied_devtools_modules = [ + "$resources_out_dir/coverage/coverage.js", + "$resources_out_dir/coverage/CoverageView.js", + "$resources_out_dir/coverage/CoverageModel.js", + "$resources_out_dir/coverage/CoverageListView.js", + "$resources_out_dir/coverage/CoverageDecorationManager.js", "$resources_out_dir/console/console.js", "$resources_out_dir/console/ConsoleContextSelector.js", "$resources_out_dir/console/ConsoleFilter.js", @@ -1881,7 +1887,9 @@ if (external_devtools_frontend) { script = "scripts/build/build_debug_applications.py" inputs = all_devtools_files + application_templates stamp = "$resources_out_debug_dir/build.stamp" - outputs = [ stamp ] + outputs = [ + stamp, + ] args = [ "--input_path", @@ -1889,7 +1897,7 @@ if (external_devtools_frontend) { "--output_path", rebase_path(resources_out_debug_dir, root_build_dir), "--build_stamp", - rebase_path(stamp, root_build_dir) + rebase_path(stamp, root_build_dir), ] } diff --git a/front_end/coverage/CoverageDecorationManager.js b/front_end/coverage/CoverageDecorationManager.js index b3a34f81d2..c5c98e4da1 100644 --- a/front_end/coverage/CoverageDecorationManager.js +++ b/front_end/coverage/CoverageDecorationManager.js @@ -2,17 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** - * @typedef {!{ - * id: string, - * contentProvider: !Common.ContentProvider, - * line: number, - * column: number - * }} - */ -Coverage.RawLocation; +export const _decoratorType = 'coverage'; -Coverage.CoverageDecorationManager = class { +export default class CoverageDecorationManager { /** * @param {!Coverage.CoverageModel} coverageModel */ @@ -24,14 +16,14 @@ Coverage.CoverageDecorationManager = class { this._uiSourceCodeByContentProvider = new Platform.Multimap(); for (const uiSourceCode of Workspace.workspace.uiSourceCodes()) { - uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this); + uiSourceCode.addLineDecoration(0, _decoratorType, this); } Workspace.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this._onUISourceCodeAdded, this); } reset() { for (const uiSourceCode of Workspace.workspace.uiSourceCodes()) { - uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._decoratorType); + uiSourceCode.removeDecorationsForType(_decoratorType); } } @@ -47,8 +39,8 @@ Coverage.CoverageDecorationManager = class { update(updatedEntries) { for (const entry of updatedEntries) { for (const uiSourceCode of this._uiSourceCodeByContentProvider.get(entry.contentProvider())) { - uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._decoratorType); - uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this); + uiSourceCode.removeDecorationsForType(_decoratorType); + uiSourceCode.addLineDecoration(0, _decoratorType, this); } } } @@ -205,106 +197,30 @@ Coverage.CoverageDecorationManager = class { */ _onUISourceCodeAdded(event) { const uiSourceCode = /** @type !Workspace.UISourceCode */ (event.data); - uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this); + uiSourceCode.addLineDecoration(0, _decoratorType, this); } -}; +} -Coverage.CoverageDecorationManager._decoratorType = 'coverage'; +/* Legacy exported object */ +self.Coverage = self.Coverage || {}; + +/* Legacy exported object */ +Coverage = Coverage || {}; /** - * @implements {SourceFrame.LineDecorator} + * @typedef {!{ + * id: string, + * contentProvider: !Common.ContentProvider, + * line: number, + * column: number + * }} */ -Coverage.CoverageView.LineDecorator = class { - constructor() { - /** @type {!WeakMap} */ - this._listeners = new WeakMap(); - } +Coverage.RawLocation; - /** - * @override - * @param {!Workspace.UISourceCode} uiSourceCode - * @param {!TextEditor.CodeMirrorTextEditor} textEditor - */ - decorate(uiSourceCode, textEditor) { - const decorations = uiSourceCode.decorationsForType(Coverage.CoverageDecorationManager._decoratorType); - if (!decorations || !decorations.size) { - this._uninstallGutter(textEditor); - return; - } - const decorationManager = - /** @type {!Coverage.CoverageDecorationManager} */ (decorations.values().next().value.data()); - decorationManager.usageByLine(uiSourceCode).then(lineUsage => { - textEditor.operation(() => this._innerDecorate(uiSourceCode, textEditor, lineUsage)); - }); - } +/** + * @constructor + */ +Coverage.CoverageDecorationManager = CoverageDecorationManager; - /** - * @param {!Workspace.UISourceCode} uiSourceCode - * @param {!TextEditor.CodeMirrorTextEditor} textEditor - * @param {!Array} lineUsage - */ - _innerDecorate(uiSourceCode, textEditor, lineUsage) { - const gutterType = Coverage.CoverageView.LineDecorator._gutterType; - this._uninstallGutter(textEditor); - if (lineUsage.length) { - this._installGutter(textEditor, uiSourceCode.url()); - } - for (let line = 0; line < lineUsage.length; ++line) { - // Do not decorate the line if we don't have data. - if (typeof lineUsage[line] !== 'boolean') { - continue; - } - const className = lineUsage[line] ? 'text-editor-coverage-used-marker' : 'text-editor-coverage-unused-marker'; - const gutterElement = createElementWithClass('div', className); - textEditor.setGutterDecoration(line, gutterType, gutterElement); - } - } - - /** - * @param {string} url - the url of the file this click handler will select in the coverage drawer - * @return {function(!Common.Event)} - */ - makeGutterClickHandler(url) { - function handleGutterClick(event) { - const eventData = /** @type {!SourceFrame.SourcesTextEditor.GutterClickEventData} */ (event.data); - if (eventData.gutterType !== Coverage.CoverageView.LineDecorator._gutterType) { - return; - } - const coverageViewId = 'coverage'; - UI.viewManager.showView(coverageViewId).then(() => UI.viewManager.view(coverageViewId).widget()).then(widget => { - const matchFormattedSuffix = url.match(/(.*):formatted$/); - const urlWithoutFormattedSuffix = (matchFormattedSuffix && matchFormattedSuffix[1]) || url; - widget.selectCoverageItemByUrl(urlWithoutFormattedSuffix); - }); - } - return handleGutterClick; - } - - /** - * @param {!TextEditor.CodeMirrorTextEditor} textEditor - the text editor to install the gutter on - * @param {string} url - the url of the file in the text editor - */ - _installGutter(textEditor, url) { - let listener = this._listeners.get(textEditor); - if (!listener) { - listener = this.makeGutterClickHandler(url); - this._listeners.set(textEditor, listener); - } - textEditor.installGutter(Coverage.CoverageView.LineDecorator._gutterType, false); - textEditor.addEventListener(SourceFrame.SourcesTextEditor.Events.GutterClick, listener, this); - } - - /** - * @param {!TextEditor.CodeMirrorTextEditor} textEditor - the text editor to uninstall the gutter from - */ - _uninstallGutter(textEditor) { - textEditor.uninstallGutter(Coverage.CoverageView.LineDecorator._gutterType); - const listener = this._listeners.get(textEditor); - if (listener) { - textEditor.removeEventListener(SourceFrame.SourcesTextEditor.Events.GutterClick, listener, this); - this._listeners.delete(textEditor); - } - } -}; - -Coverage.CoverageView.LineDecorator._gutterType = 'CodeMirror-gutter-coverage'; +/** @public */ +Coverage.CoverageDecorationManager.decoratorType = _decoratorType; diff --git a/front_end/coverage/CoverageListView.js b/front_end/coverage/CoverageListView.js index edf5737687..2384d4a07c 100644 --- a/front_end/coverage/CoverageListView.js +++ b/front_end/coverage/CoverageListView.js @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Coverage.CoverageListView = class extends UI.VBox { +export default class CoverageListView extends UI.VBox { /** * @param {function(!Coverage.URLCoverageInfo):boolean} filterCallback */ @@ -61,7 +61,7 @@ Coverage.CoverageListView = class extends UI.VBox { } continue; } - node = new Coverage.CoverageListView.GridNode(entry, maxSize); + node = new GridNode(entry, maxSize); this._nodeForCoverageInfo.set(entry, node); if (this._filterCallback(node._coverageInfo)) { rootNode.appendChild(node); @@ -231,9 +231,9 @@ Coverage.CoverageListView = class extends UI.VBox { } return types.join('+'); } -}; +} -Coverage.CoverageListView.GridNode = class extends DataGrid.SortableDataGridNode { +export class GridNode extends DataGrid.SortableDataGridNode { /** * @param {!Coverage.URLCoverageInfo} coverageInfo * @param {number} maxSize @@ -379,4 +379,20 @@ Coverage.CoverageListView.GridNode = class extends DataGrid.SortableDataGridNode const range = new TextUtils.SourceRange(matches.index, matches[0].length); UI.highlightRangesWithStyleClass(element, [range], 'filter-highlight'); } -}; +} + +/* Legacy exported object */ +self.Coverage = self.Coverage || {}; + +/* Legacy exported object */ +Coverage = Coverage || {}; + +/** + * @constructor + */ +Coverage.CoverageListView = CoverageListView; + +/** + * @constructor + */ +Coverage.CoverageListView.GridNode = GridNode; diff --git a/front_end/coverage/CoverageModel.js b/front_end/coverage/CoverageModel.js index ebea492e25..6b3d308ed2 100644 --- a/front_end/coverage/CoverageModel.js +++ b/front_end/coverage/CoverageModel.js @@ -11,20 +11,28 @@ Coverage.CoverageSegment; /** * @enum {number} */ -Coverage.CoverageType = { +export const CoverageType = { CSS: (1 << 0), JavaScript: (1 << 1), JavaScriptPerFunction: (1 << 2), }; /** @enum {symbol} */ -Coverage.SuspensionState = { +export const SuspensionState = { Active: Symbol('Active'), Suspending: Symbol('Suspending'), Suspended: Symbol('Suspended') }; -Coverage.CoverageModel = class extends SDK.SDKModel { +/** @enum {symbol} */ +export const Events = { + CoverageUpdated: Symbol('CoverageUpdated') +}; + +/** @type {number} */ +export const _coveragePollingPeriodMs = 200; + +export default class CoverageModel extends SDK.SDKModel { /** * @param {!SDK.Target} target */ @@ -40,7 +48,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { this._coverageByContentProvider = new Map(); /** @type {!Coverage.SuspensionState} */ - this._suspensionState = Coverage.SuspensionState.Active; + this._suspensionState = SuspensionState.Active; /** @type {?number} */ this._pollTimer = null; /** @type {?Promise} */ @@ -60,7 +68,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { * @return {!Promise} */ async start(jsCoveragePerBlock) { - if (this._suspensionState !== Coverage.SuspensionState.Active) { + if (this._suspensionState !== SuspensionState.Active) { throw Error('Cannot start CoverageModel while it is not active.'); } const promises = []; @@ -105,7 +113,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { * @return {!Promise} */ async startPolling() { - if (this._currentPollPromise || this._suspensionState !== Coverage.SuspensionState.Active) { + if (this._currentPollPromise || this._suspensionState !== SuspensionState.Active) { return; } await this._pollLoop(); @@ -118,8 +126,8 @@ Coverage.CoverageModel = class extends SDK.SDKModel { this._clearTimer(); this._currentPollPromise = this._pollAndCallback(); await this._currentPollPromise; - if (this._suspensionState === Coverage.SuspensionState.Active || this._performanceTraceRecording) { - this._pollTimer = setTimeout(() => this._pollLoop(), Coverage.CoverageModel._coveragePollingPeriodMs); + if (this._suspensionState === SuspensionState.Active || this._performanceTraceRecording) { + this._pollTimer = setTimeout(() => this._pollLoop(), _coveragePollingPeriodMs); } } @@ -135,17 +143,17 @@ Coverage.CoverageModel = class extends SDK.SDKModel { * @return {!Promise} */ async _pollAndCallback() { - if (this._suspensionState === Coverage.SuspensionState.Suspended && !this._performanceTraceRecording) { + if (this._suspensionState === SuspensionState.Suspended && !this._performanceTraceRecording) { return; } const updates = await this._takeAllCoverage(); // This conditional should never trigger, as all intended ways to stop // polling are awaiting the `_currentPollPromise` before suspending. console.assert( - this._suspensionState !== Coverage.SuspensionState.Suspended || this._performanceTraceRecording, + this._suspensionState !== SuspensionState.Suspended || this._performanceTraceRecording, 'CoverageModel was suspended while polling.'); if (updates.length) { - this.dispatchEventToListeners(Coverage.CoverageModel.Events.CoverageUpdated, updates); + this.dispatchEventToListeners(Events.CoverageUpdated, updates); } } @@ -164,10 +172,10 @@ Coverage.CoverageModel = class extends SDK.SDKModel { * @return {!Promise} */ async preSuspendModel(reason) { - if (this._suspensionState !== Coverage.SuspensionState.Active) { + if (this._suspensionState !== SuspensionState.Active) { return; } - this._suspensionState = Coverage.SuspensionState.Suspending; + this._suspensionState = SuspensionState.Suspending; if (reason === 'performance-timeline') { this._performanceTraceRecording = true; // Keep polling to the backlog if a performance trace is recorded. @@ -185,7 +193,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { * @return {!Promise} */ async suspendModel(reason) { - this._suspensionState = Coverage.SuspensionState.Suspended; + this._suspensionState = SuspensionState.Suspended; } /** @@ -202,7 +210,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { * @return {!Promise} */ async postResumeModel() { - this._suspensionState = Coverage.SuspensionState.Active; + this._suspensionState = SuspensionState.Active; this._performanceTraceRecording = false; if (this._shouldResumePollingOnResume) { this._shouldResumePollingOnResume = false; @@ -230,7 +238,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { _clearCSS() { for (const entry of this._coverageByContentProvider.values()) { - if (entry.type() !== Coverage.CoverageType.CSS) { + if (entry.type() !== CoverageType.CSS) { continue; } const contentProvider = /** @type {!SDK.CSSStyleSheetHeader} */ (entry.contentProvider()); @@ -268,7 +276,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { } const now = Date.now(); const freshRawCoverageData = await this._cpuProfilerModel.takePreciseCoverage(); - if (this._suspensionState !== Coverage.SuspensionState.Active) { + if (this._suspensionState !== SuspensionState.Active) { if (freshRawCoverageData.length > 0) { this._jsBacklog.push({rawCoverageData: freshRawCoverageData, stamp: now}); } @@ -300,14 +308,14 @@ Coverage.CoverageModel = class extends SDK.SDKModel { } const ranges = []; - let type = Coverage.CoverageType.JavaScript; + let type = CoverageType.JavaScript; for (const func of entry.functions) { // Do not coerce undefined to false, i.e. only consider blockLevel to be false // if back-end explicitly provides blockLevel field, otherwise presume blockLevel // coverage is not available. Also, ignore non-block level functions that weren't // ever called. if (func.isBlockCoverage === false && !(func.ranges.length === 1 && !func.ranges[0].count)) { - type |= Coverage.CoverageType.JavaScriptPerFunction; + type |= CoverageType.JavaScriptPerFunction; } for (const range of func.ranges) { ranges.push(range); @@ -338,7 +346,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { } const now = Date.now(); const freshRawCoverageData = await this._cssModel.takeCoverageDelta(); - if (this._suspensionState !== Coverage.SuspensionState.Active) { + if (this._suspensionState !== SuspensionState.Active) { if (freshRawCoverageData.length > 0) { this._cssBacklog.push({rawCoverageData: freshRawCoverageData, stamp: now}); } @@ -382,7 +390,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { const ranges = /** @type {!Array} */ (entry[1]); const subentry = this._addCoverage( styleSheetHeader, styleSheetHeader.contentLength, styleSheetHeader.startLine, styleSheetHeader.startColumn, - ranges, Coverage.CoverageType.CSS, stamp); + ranges, CoverageType.CSS, stamp); if (subentry) { updatedEntries.push(subentry); } @@ -442,7 +450,7 @@ Coverage.CoverageModel = class extends SDK.SDKModel { _addStyleSheetToCSSCoverage(styleSheetHeader) { this._addCoverage( styleSheetHeader, styleSheetHeader.contentLength, styleSheetHeader.startLine, styleSheetHeader.startColumn, [], - Coverage.CoverageType.CSS, Date.now()); + CoverageType.CSS, Date.now()); } /** @@ -555,19 +563,14 @@ Coverage.CoverageModel = class extends SDK.SDKModel { await fos.write(JSON.stringify(result, undefined, 2)); fos.close(); } -}; +} -/** @enum {symbol} */ -Coverage.CoverageModel.Events = { - CoverageUpdated: Symbol('CoverageUpdated') -}; +SDK.SDKModel.register(CoverageModel, SDK.Target.Capability.None, false); -/** @type {number} */ -Coverage.CoverageModel._coveragePollingPeriodMs = 200; - -SDK.SDKModel.register(Coverage.CoverageModel, SDK.Target.Capability.None, false); - -Coverage.URLCoverageInfo = class { +/** + * @unrestricted + */ +export class URLCoverageInfo { /** * @param {string} url */ @@ -645,7 +648,7 @@ Coverage.URLCoverageInfo = class { const key = `${lineOffset}:${columnOffset}`; let entry = this._coverageInfoByLocation.get(key); - if ((type & Coverage.CoverageType.JavaScript) && !this._coverageInfoByLocation.size) { + if ((type & CoverageType.JavaScript) && !this._coverageInfoByLocation.size) { this._isContentScript = /** @type {!SDK.Script} */ (contentProvider).isContentScript(); } this._type |= type; @@ -655,7 +658,7 @@ Coverage.URLCoverageInfo = class { return entry; } - if ((type & Coverage.CoverageType.JavaScript) && !this._coverageInfoByLocation.size) { + if ((type & CoverageType.JavaScript) && !this._coverageInfoByLocation.size) { this._isContentScript = /** @type {!SDK.Script} */ (contentProvider).isContentScript(); } @@ -665,9 +668,12 @@ Coverage.URLCoverageInfo = class { return entry; } -}; +} -Coverage.CoverageInfo = class { +/** + * @unrestricted + */ +export class CoverageInfo { /** * @param {!Common.ContentProvider} contentProvider * @param {number} size @@ -797,4 +803,39 @@ Coverage.CoverageInfo = class { last = segment.end; } } -}; +} + +/* Legacy exported object */ +self.Coverage = self.Coverage || {}; + +/* Legacy exported object */ +Coverage = Coverage || {}; + +/** + * @constructor + */ +Coverage.CoverageModel = CoverageModel; + +/** @enum {symbol} */ +Coverage.CoverageModel.Events = Events; + +/** @type {number} */ +Coverage.CoverageModel._coveragePollingPeriodMs = _coveragePollingPeriodMs; + +/** + * @enum {number} + */ +Coverage.CoverageType = CoverageType; + +/** @enum {symbol} */ +Coverage.SuspensionState = SuspensionState; + +/** + * @constructor + */ +Coverage.URLCoverageInfo = URLCoverageInfo; + +/** + * @constructor + */ +Coverage.CoverageInfo = CoverageInfo; diff --git a/front_end/coverage/CoverageView.js b/front_end/coverage/CoverageView.js index 8c3cf8dce5..dfa256dc5a 100644 --- a/front_end/coverage/CoverageView.js +++ b/front_end/coverage/CoverageView.js @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -Coverage.CoverageView = class extends UI.VBox { +export class CoverageView extends UI.VBox { constructor() { super(true); @@ -362,14 +362,14 @@ Coverage.CoverageView = class extends UI.VBox { selectCoverageItemByUrl(url) { this._listView.selectByUrl(url); } -}; +} -Coverage.CoverageView._extensionBindingsURLPrefix = 'extensions::'; +CoverageView._extensionBindingsURLPrefix = 'extensions::'; /** * @implements {UI.ActionDelegate} */ -Coverage.CoverageView.ActionDelegate = class { +export class ActionDelegate { /** * @override * @param {!UI.Context} context @@ -401,4 +401,123 @@ Coverage.CoverageView.ActionDelegate = class { console.assert(false, `Unknown action: ${actionId}`); } } -}; +} + +/** + * @implements {SourceFrame.LineDecorator} + */ +export class LineDecorator { + constructor() { + /** @type {!WeakMap} */ + this._listeners = new WeakMap(); + } + + /** + * @override + * @param {!Workspace.UISourceCode} uiSourceCode + * @param {!TextEditor.CodeMirrorTextEditor} textEditor + */ + decorate(uiSourceCode, textEditor) { + const decorations = uiSourceCode.decorationsForType(Coverage.CoverageDecorationManager.decoratorType); + if (!decorations || !decorations.size) { + this._uninstallGutter(textEditor); + return; + } + const decorationManager = + /** @type {!Coverage.CoverageDecorationManager} */ (decorations.values().next().value.data()); + decorationManager.usageByLine(uiSourceCode).then(lineUsage => { + textEditor.operation(() => this._innerDecorate(uiSourceCode, textEditor, lineUsage)); + }); + } + + /** + * @param {!Workspace.UISourceCode} uiSourceCode + * @param {!TextEditor.CodeMirrorTextEditor} textEditor + * @param {!Array} lineUsage + */ + _innerDecorate(uiSourceCode, textEditor, lineUsage) { + const gutterType = Coverage.CoverageView.LineDecorator._gutterType; + this._uninstallGutter(textEditor); + if (lineUsage.length) { + this._installGutter(textEditor, uiSourceCode.url()); + } + for (let line = 0; line < lineUsage.length; ++line) { + // Do not decorate the line if we don't have data. + if (typeof lineUsage[line] !== 'boolean') { + continue; + } + const className = lineUsage[line] ? 'text-editor-coverage-used-marker' : 'text-editor-coverage-unused-marker'; + const gutterElement = createElementWithClass('div', className); + textEditor.setGutterDecoration(line, gutterType, gutterElement); + } + } + + /** + * @param {string} url - the url of the file this click handler will select in the coverage drawer + * @return {function(!Common.Event)} + */ + makeGutterClickHandler(url) { + function handleGutterClick(event) { + const eventData = /** @type {!SourceFrame.SourcesTextEditor.GutterClickEventData} */ (event.data); + if (eventData.gutterType !== Coverage.CoverageView.LineDecorator._gutterType) { + return; + } + const coverageViewId = 'coverage'; + UI.viewManager.showView(coverageViewId).then(() => UI.viewManager.view(coverageViewId).widget()).then(widget => { + const matchFormattedSuffix = url.match(/(.*):formatted$/); + const urlWithoutFormattedSuffix = (matchFormattedSuffix && matchFormattedSuffix[1]) || url; + widget.selectCoverageItemByUrl(urlWithoutFormattedSuffix); + }); + } + return handleGutterClick; + } + + /** + * @param {!TextEditor.CodeMirrorTextEditor} textEditor - the text editor to install the gutter on + * @param {string} url - the url of the file in the text editor + */ + _installGutter(textEditor, url) { + let listener = this._listeners.get(textEditor); + if (!listener) { + listener = this.makeGutterClickHandler(url); + this._listeners.set(textEditor, listener); + } + textEditor.installGutter(Coverage.CoverageView.LineDecorator._gutterType, false); + textEditor.addEventListener(SourceFrame.SourcesTextEditor.Events.GutterClick, listener, this); + } + + /** + * @param {!TextEditor.CodeMirrorTextEditor} textEditor - the text editor to uninstall the gutter from + */ + _uninstallGutter(textEditor) { + textEditor.uninstallGutter(Coverage.CoverageView.LineDecorator._gutterType); + const listener = this._listeners.get(textEditor); + if (listener) { + textEditor.removeEventListener(SourceFrame.SourcesTextEditor.Events.GutterClick, listener, this); + this._listeners.delete(textEditor); + } + } +} + +LineDecorator._gutterType = 'CodeMirror-gutter-coverage'; + +/* Legacy exported object */ +self.Coverage = self.Coverage || {}; + +/* Legacy exported object */ +Coverage = Coverage || {}; + +/** + * @constructor + */ +Coverage.CoverageView = CoverageView; + +/** + * @constructor + */ +Coverage.CoverageView.LineDecorator = LineDecorator; + +/** + * @constructor + */ +Coverage.CoverageView.ActionDelegate = ActionDelegate; diff --git a/front_end/coverage/coverage.js b/front_end/coverage/coverage.js new file mode 100644 index 0000000000..1b73c1c811 --- /dev/null +++ b/front_end/coverage/coverage.js @@ -0,0 +1,20 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import './CoverageModel.js'; +import './CoverageListView.js'; +import './CoverageView.js'; +import './CoverageDecorationManager.js'; + +import * as CoverageDecorationManager from './CoverageDecorationManager.js'; +import * as CoverageListView from './CoverageListView.js'; +import * as CoverageModel from './CoverageModel.js'; +import * as CoverageView from './CoverageView.js'; + +export { + CoverageDecorationManager, + CoverageListView, + CoverageModel, + CoverageView, +}; diff --git a/front_end/coverage/module.json b/front_end/coverage/module.json index cbbb2fc911..05393ca308 100644 --- a/front_end/coverage/module.json +++ b/front_end/coverage/module.json @@ -50,7 +50,8 @@ "sources", "data_grid" ], - "scripts": [ + "scripts": [], + "modules": [ "CoverageModel.js", "CoverageListView.js", "CoverageView.js",