Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(842)

Unified Diff: third_party/WebKit/Source/devtools/front_end/coverage/CoverageDecorationManager.js

Issue 2865573003: DevTools: support live coverage (Closed)
Patch Set: minor polish on corner cases Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/devtools/front_end/coverage/CoverageDecorationManager.js
diff --git a/third_party/WebKit/Source/devtools/front_end/coverage/CoverageDecorationManager.js b/third_party/WebKit/Source/devtools/front_end/coverage/CoverageDecorationManager.js
new file mode 100644
index 0000000000000000000000000000000000000000..351cfa15e0579b86a71a2880889ae0f59f49fbba
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/coverage/CoverageDecorationManager.js
@@ -0,0 +1,218 @@
+// Copyright 2017 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.
+
+/**
+ * @typedef {!{
+ * id: string,
+ * contentProvider: !Common.ContentProvider,
+ * line: number,
+ * column: number
+ * }}
+ */
+Coverage.RawLocation;
+
+Coverage.CoverageDecorationManager = class {
+ /**
+ * @param {!Coverage.CoverageModel} coverageModel
+ */
+ constructor(coverageModel) {
+ this._coverageModel = coverageModel;
+ /** @type {!Map<!Common.ContentProvider, ?TextUtils.Text>} */
+ this._textByProvider = new Map();
+ /** @type {!Multimap<!Common.ContentProvider, !Workspace.UISourceCode>} */
+ this._uiSourceCodeByContentProvider = new Multimap();
+
+ for (var uiSourceCode of Workspace.workspace.uiSourceCodes())
+ uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this);
+ Workspace.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this._onUISourceCodeAdded, this);
+ }
+
+ dispose() {
+ for (var uiSourceCode of Workspace.workspace.uiSourceCodes())
+ uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._decoratorType);
+ Workspace.workspace.removeEventListener(
+ Workspace.Workspace.Events.UISourceCodeAdded, this._onUISourceCodeAdded, this);
+ }
+
+ /**
+ * @param {!Array<!Coverage.CoverageInfo>} updatedEntries
+ */
+ update(updatedEntries) {
+ for (var entry of updatedEntries) {
+ for (var uiSourceCode of this._uiSourceCodeByContentProvider.get(entry.contentProvider())) {
+ uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._decoratorType);
+ uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this);
+ }
+ }
+ }
+
+ /**
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ * @return {!Promise<!Array<boolean>>}
+ */
+ async usageByLine(uiSourceCode) {
+ var result = [];
+ var sourceText = new TextUtils.Text(uiSourceCode.content() || '');
+ await this._updateTexts(uiSourceCode, sourceText);
+ var lineEndings = sourceText.lineEndings();
+ for (var line = 0; line < sourceText.lineCount(); ++line) {
+ var lineLength = lineEndings[line] - (line ? lineEndings[line - 1] : 0) - 1;
+ if (!lineLength) {
+ result.push(false);
+ continue;
+ }
+ var startLocations = this._rawLocationsForSourceLocation(uiSourceCode, line, 0);
+ var endLocations = this._rawLocationsForSourceLocation(uiSourceCode, line, lineLength);
+ var used = false;
+ for (var startIndex = 0, endIndex = 0; startIndex < startLocations.length; ++startIndex) {
+ var start = startLocations[startIndex];
+ while (endIndex < endLocations.length &&
+ Coverage.CoverageDecorationManager._compareLocations(start, endLocations[endIndex]) >= 0)
+ ++endIndex;
+ if (endIndex >= endLocations.length || endLocations[endIndex].id !== start.id)
+ continue;
+ var end = endLocations[endIndex++];
+ var text = this._textByProvider.get(end.contentProvider);
+ if (!text)
+ continue;
+ var textValue = text.value();
+ var startOffset = text.offsetFromPosition(start.line, start.column);
+ var endOffset = text.offsetFromPosition(end.line, end.column);
+ while (startOffset <= endOffset && /\s/.test(textValue[startOffset]))
+ ++startOffset;
+ while (startOffset <= endOffset && /\s/.test(textValue[endOffset]))
+ --endOffset;
+ used =
+ startOffset <= endOffset && this._coverageModel.usageForRange(end.contentProvider, startOffset, endOffset);
+ if (used)
+ break;
+ }
+ result.push(used);
+ }
+ return result;
+ }
+
+ /**
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ * @param {!TextUtils.Text} text
+ * @return {!Promise}
+ */
+ _updateTexts(uiSourceCode, text) {
+ var promises = [];
+ for (var line = 0; line < text.lineCount(); ++line) {
+ for (var entry of this._rawLocationsForSourceLocation(uiSourceCode, line, 0)) {
+ if (this._textByProvider.has(entry.contentProvider))
+ continue;
+ this._textByProvider.set(entry.contentProvider, null);
+ this._uiSourceCodeByContentProvider.set(entry.contentProvider, uiSourceCode);
+ promises.push(this._updateTextForProvider(entry.contentProvider));
+ }
+ }
+ return Promise.all(promises);
+ }
+
+ /**
+ * @param {!Common.ContentProvider} contentProvider
+ * @return {!Promise}
+ */
+ async _updateTextForProvider(contentProvider) {
+ var content = await contentProvider.requestContent();
+ this._textByProvider.set(contentProvider, new TextUtils.Text(content));
+ }
+
+ /**
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ * @param {number} line
+ * @param {number} column
+ * @return {!Array<!Coverage.RawLocation>}
+ */
+ _rawLocationsForSourceLocation(uiSourceCode, line, column) {
+ var result = [];
+ var contentType = uiSourceCode.contentType();
+ if (contentType.hasScripts()) {
+ var location = Bindings.debuggerWorkspaceBinding.uiLocationToRawLocation(uiSourceCode, line, column);
+ if (location && location.script()) {
+ result.push({
+ id: `js:${location.scriptId}`,
+ contentProvider: location.script(),
+ line: location.lineNumber,
+ column: location.columnNumber
+ });
+ }
+ }
+ if (contentType.isStyleSheet() || contentType.isDocument()) {
+ var rawStyleLocations =
+ Bindings.cssWorkspaceBinding.uiLocationToRawLocations(new Workspace.UILocation(uiSourceCode, line, column));
+ for (var location of rawStyleLocations) {
+ if (!location.header())
+ continue;
+ result.push({
+ id: `css:${location.styleSheetId}`,
+ contentProvider: location.header(),
+ line: location.lineNumber,
+ column: location.columnNumber
+ });
+ }
+ }
+ result.sort(Coverage.CoverageDecorationManager._compareLocations);
+ return result;
+ }
+
+ /**
+ * @param {!Coverage.RawLocation} a
+ * @param {!Coverage.RawLocation} b
+ */
+ static _compareLocations(a, b) {
+ return a.id.localeCompare(b.id) || a.line - b.line || a.column - b.column;
+ }
+
+ /**
+ * @param {!Common.Event} event
+ */
+ _onUISourceCodeAdded(event) {
+ var uiSourceCode = /** @type !Workspace.UISourceCode */ (event.data);
+ uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this);
+ }
+};
+
+Coverage.CoverageDecorationManager._decoratorType = 'coverage';
+
+/**
+ * @implements {SourceFrame.UISourceCodeFrame.LineDecorator}
+ */
+Coverage.CoverageView.LineDecorator = class {
+ /**
+ * @override
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ * @param {!TextEditor.CodeMirrorTextEditor} textEditor
+ */
+ decorate(uiSourceCode, textEditor) {
+ var decorations = uiSourceCode.decorationsForType(Coverage.CoverageDecorationManager._decoratorType);
+ if (!decorations.size) {
+ textEditor.uninstallGutter(Coverage.CoverageView.LineDecorator._gutterType);
+ return;
+ }
+ var decorationManager =
+ /** @type {!Coverage.CoverageDecorationManager} */ (decorations.values().next().value.data());
+ decorationManager.usageByLine(uiSourceCode).then(lineUsage => {
+ textEditor.operation(() => this._innerDecorate(textEditor, lineUsage));
+ });
+ }
+
+ /**
+ * @param {!TextEditor.CodeMirrorTextEditor} textEditor
+ * @param {!Array<boolean>} lineUsage
+ */
+ _innerDecorate(textEditor, lineUsage) {
+ var gutterType = Coverage.CoverageView.LineDecorator._gutterType;
+ textEditor.uninstallGutter(gutterType);
+ textEditor.installGutter(gutterType, false);
+ for (var line = 0; line < lineUsage.length; ++line) {
+ var className = lineUsage[line] ? 'text-editor-coverage-used-marker' : 'text-editor-coverage-unused-marker';
+ textEditor.setGutterDecoration(line, gutterType, createElementWithClass('div', className));
+ }
+ }
+};
+
+Coverage.CoverageView.LineDecorator._gutterType = 'CodeMirror-gutter-coverage';
« no previous file with comments | « third_party/WebKit/Source/devtools/BUILD.gn ('k') | third_party/WebKit/Source/devtools/front_end/coverage/CoverageListView.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698