Visual logging for Application > Manifest

Bug: b/308381403
Change-Id: I9accfde8183eb6090ada240d8dd32aca68b267f3
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5126352
Commit-Queue: Kateryna Prokopenko <[email protected]>
Auto-Submit: Kateryna Prokopenko <[email protected]>
Reviewed-by: Danil Somsikov <[email protected]>
diff --git a/docs/visual_logging.md b/docs/visual_logging.md
index f459d06..404717b 100644
--- a/docs/visual_logging.md
+++ b/docs/visual_logging.md
@@ -1,7 +1,7 @@
 # Visual logging
 
 The goal of this project is to improve the logging of user interactions in
-DevTools. The current UMA logging is unreliable and inconsistent. This can lead
+DevTools. The current UMA logging is unreliable and inconsistent. This can lead to
 incorrect conclusions about how users are interacting with the product.
 
 We want to be able to understand how users are interacting with DevTools so that
@@ -90,7 +90,7 @@
 can be a string or a number. If a string is given, it is be first considered
 to refer to a context provider (see below). If no context provider is registered
 with this name, SHA-1 hash is computed and the first 32 bits
-(little endian) is logged. Number is be logged as is.
+(little endian) is logged. Number will be logged as is.
 
 The `parent()` method sets the custom parent provider for the visual logging element
 (see below). If not invoked, the parent visual element is taken from a DOM tree structure.
diff --git a/front_end/panels/application/AppManifestView.ts b/front_end/panels/application/AppManifestView.ts
index 4a0e4bb..582ba5f 100644
--- a/front_end/panels/application/AppManifestView.ts
+++ b/front_end/panels/application/AppManifestView.ts
@@ -5,16 +5,16 @@
 import * as Common from '../../core/common/common.js';
 import * as Host from '../../core/host/host.js';
 import * as i18n from '../../core/i18n/i18n.js';
-
-import appManifestViewStyles from './appManifestView.css.js';
-
 import type * as Platform from '../../core/platform/platform.js';
 import * as SDK from '../../core/sdk/sdk.js';
+import type * as Protocol from '../../generated/protocol.js';
+import * as IconButton from '../../ui/components/icon_button/icon_button.js';
 import * as InlineEditor from '../../ui/legacy/components/inline_editor/inline_editor.js';
 import * as Components from '../../ui/legacy/components/utils/utils.js';
 import * as UI from '../../ui/legacy/legacy.js';
-import type * as Protocol from '../../generated/protocol.js';
-import * as IconButton from '../../ui/components/icon_button/icon_button.js';
+import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
+
+import appManifestViewStyles from './appManifestView.css.js';
 import * as ApplicationComponents from './components/components.js';
 
 const UIStrings = {
@@ -506,6 +506,7 @@
     super(true);
 
     this.contentElement.classList.add('manifest-container');
+    this.contentElement.setAttribute('jslog', `${VisualLogging.pane().context('manifest')}`);
 
     this.emptyView = emptyView;
     this.emptyView.appendLink('https://web.dev/add-manifest/' as Platform.DevToolsPath.UrlString);
@@ -520,14 +521,23 @@
     this.reportView.hideWidget();
 
     this.errorsSection = this.reportView.appendSection(i18nString(UIStrings.errorsAndWarnings));
+    this.errorsSection.element.setAttribute('jslog', `${VisualLogging.section().context('errors-and-warnings')}`);
     this.installabilitySection = this.reportView.appendSection(i18nString(UIStrings.installability));
+    this.installabilitySection.element.setAttribute('jslog', `${VisualLogging.section().context('installability')}`);
     this.identitySection = this.reportView.appendSection(i18nString(UIStrings.identity));
+    this.identitySection.element.setAttribute('jslog', `${VisualLogging.section().context('identity')}`);
     this.presentationSection = this.reportView.appendSection(i18nString(UIStrings.presentation));
+    this.presentationSection.element.setAttribute('jslog', `${VisualLogging.section().context('presentation')}`);
     this.protocolHandlersSection = this.reportView.appendSection(i18nString(UIStrings.protocolHandlers));
+    this.protocolHandlersSection.element.setAttribute(
+        'jslog', `${VisualLogging.section().context('protocol-handlers')}`);
     this.protocolHandlersView = new ApplicationComponents.ProtocolHandlersView.ProtocolHandlersView();
     this.protocolHandlersSection.appendFieldWithCustomView(this.protocolHandlersView);
     this.iconsSection = this.reportView.appendSection(i18nString(UIStrings.icons), 'report-section-icons');
+    this.iconsSection.element.setAttribute('jslog', `${VisualLogging.section().context('icons')}`);
     this.windowControlsSection = this.reportView.appendSection(UIStrings.windowControlsOverlay);
+    this.windowControlsSection.element.setAttribute(
+        'jslog', `${VisualLogging.section().context('window-controls-overlay')}`);
     this.shortcutSections = [];
     this.screenshotsSections = [];
 
@@ -695,10 +705,13 @@
       helpIcon.classList.add('inline-icon');
       helpIcon.title = i18nString(UIStrings.appIdExplainer);
       helpIcon.tabIndex = 0;
+      helpIcon.setAttribute('jslog', `${VisualLogging.action().track({hover: true}).context('help')}`);
       appIdField.appendChild(helpIcon);
 
-      appIdField.appendChild(
-          UI.XLink.XLink.create('https://developer.chrome.com/blog/pwa-manifest-id/', i18nString(UIStrings.learnMore)));
+      const learnMoreLink =
+          UI.XLink.XLink.create('https://developer.chrome.com/blog/pwa-manifest-id/', i18nString(UIStrings.learnMore));
+      learnMoreLink.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('learn-more')}`);
+      appIdField.appendChild(learnMoreLink);
 
       if (!stringProperty('id')) {
         const suggestedIdNote = appIdField.createChild('div', 'multiline-value');
@@ -714,6 +727,7 @@
         suggestedIdSpan.textContent = recommendedId;
 
         const copyButton = new IconButton.IconButton.IconButton();
+        copyButton.setAttribute('jslog', `${VisualLogging.action().track({click: true}).context('copy')}`);
         copyButton.title = i18nString(UIStrings.copyToClipboard);
         copyButton.data = {
           groups: [{
@@ -746,6 +760,7 @@
         const link = Components.Linkifier.Linkifier.linkifyURL(
             completeURL, ({text: startURL} as Components.Linkifier.LinkifyURLOptions));
         link.tabIndex = 0;
+        link.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('start-url')}`);
         this.startURLField.appendChild(link);
       }
     }
@@ -821,12 +836,16 @@
 
     const setIconMaskedCheckbox = UI.UIUtils.CheckboxLabel.create(i18nString(UIStrings.showOnlyTheMinimumSafeAreaFor));
     setIconMaskedCheckbox.classList.add('mask-checkbox');
+    setIconMaskedCheckbox.setAttribute(
+        'jslog',
+        `${VisualLogging.toggle().track({change: true}).context('show-minimal-safe-area-for-maskable-icons')}`);
     setIconMaskedCheckbox.addEventListener('click', () => {
       this.iconsSection.setIconMasked(setIconMaskedCheckbox.checkboxElement.checked);
     });
     this.iconsSection.appendRow().appendChild(setIconMaskedCheckbox);
     const documentationLink =
         UI.XLink.XLink.create('https://web.dev/maskable-icon/', i18nString(UIStrings.documentationOnMaskableIcons));
+    documentationLink.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('learn-more')}`);
     this.iconsSection.appendRow().appendChild(
         i18n.i18n.getFormatLocalizedString(str_, UIStrings.needHelpReadOurS, {PH1: documentationLink}));
 
@@ -847,6 +866,7 @@
     let shortcutIndex = 1;
     for (const shortcut of shortcuts) {
       const shortcutSection = this.reportView.appendSection(i18nString(UIStrings.shortcutS, {PH1: shortcutIndex}));
+      shortcutSection.element.setAttribute('jslog', `${VisualLogging.section().context('shortcuts')}`);
       this.shortcutSections.push(shortcutSection);
 
       shortcutSection.appendFlexedField(i18nString(UIStrings.name), shortcut.name);
@@ -860,6 +880,7 @@
       const shortcutUrl = Common.ParsedURL.ParsedURL.completeURL(url, shortcut.url) as Platform.DevToolsPath.UrlString;
       const link = Components.Linkifier.Linkifier.linkifyURL(
           shortcutUrl, ({text: shortcut.url} as Components.Linkifier.LinkifyURLOptions));
+      link.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('shortcut')}`);
       link.tabIndex = 0;
       urlField.appendChild(link);
 
@@ -971,6 +992,8 @@
 
     const displayOverrideLink = UI.XLink.XLink.create(
         'https://developer.mozilla.org/en-US/docs/Web/Manifest/display_override', 'display-override');
+    displayOverrideLink.setAttribute(
+        'jslog', `${VisualLogging.link().track({click: true}).context('display-override')}`);
     const displayOverrideText = document.createElement('code');
     displayOverrideText.appendChild(displayOverrideLink);
 
@@ -1008,6 +1031,8 @@
     const wcoDocumentationLink = UI.XLink.XLink.create(
         'https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay',
         i18nString(UIStrings.customizePwaTitleBar));
+    wcoDocumentationLink.setAttribute(
+        'jslog', `${VisualLogging.link().track({click: true}).context('customize-pwa-tittle-bar')}`);
     this.windowControlsSection.appendRow().appendChild(
         i18n.i18n.getFormatLocalizedString(str_, UIStrings.wcoNeedHelpReadMore, {PH1: wcoDocumentationLink}));
 
diff --git a/front_end/panels/application/components/ProtocolHandlersView.ts b/front_end/panels/application/components/ProtocolHandlersView.ts
index 3f396c3..6fdea74 100644
--- a/front_end/panels/application/components/ProtocolHandlersView.ts
+++ b/front_end/panels/application/components/ProtocolHandlersView.ts
@@ -14,6 +14,7 @@
 import inspectorCommonStyles from '../../../ui/legacy/inspectorCommon.css.js';
 import * as UI from '../../../ui/legacy/legacy.js';
 import * as LitHtml from '../../../ui/lit-html/lit-html.js';
+import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
 
 import protocolHandlersViewStyles from './protocolHandlersView.css.js';
 
@@ -108,6 +109,7 @@
 
   #renderStatusMessage(): LitHtml.TemplateResult {
     const manifestInTextLink = UI.XLink.XLink.create(this.#manifestLink, i18nString(UIStrings.manifest));
+    manifestInTextLink.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('manifest')}`);
     const statusString = this.#protocolHandlers.length > 0 ? UIStrings.protocolDetected : UIStrings.protocolNotDetected;
     const iconData: IconButton.Icon.IconData = this.#protocolHandlers.length > 0 ?
         {iconName: 'check-circle', color: 'var(--icon-checkmark-green)', width: '16px', height: '16px'} :
@@ -172,6 +174,7 @@
   #render(): void {
     const protocolDocLink =
         UI.XLink.XLink.create(PROTOCOL_DOCUMENT_URL, i18nString(UIStrings.protocolHandlerRegistrations));
+    protocolDocLink.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('learn-more')}`);
     // clang-format off
     LitHtml.render(LitHtml.html`
       ${this.#renderStatusMessage()}
diff --git a/front_end/ui/legacy/EmptyWidget.ts b/front_end/ui/legacy/EmptyWidget.ts
index 3b2b3d6..ed0350e 100644
--- a/front_end/ui/legacy/EmptyWidget.ts
+++ b/front_end/ui/legacy/EmptyWidget.ts
@@ -31,6 +31,7 @@
 import * as Host from '../../core/host/host.js';
 import * as i18n from '../../core/i18n/i18n.js';
 import type * as Platform from '../../core/platform/platform.js';
+import * as VisualLogging from '../visual_logging/visual_logging.js';
 
 import emptyWidgetStyles from './emptyWidget.css.legacy.js';
 import {Infobar, Type} from './Infobar.js';
@@ -54,6 +55,7 @@
     this.registerRequiredCSS(emptyWidgetStyles);
     this.element.classList.add('empty-view-scroller');
     this.contentElement = this.element.createChild('div', 'empty-view') as HTMLDivElement;
+    this.contentElement.setAttribute('jslog', `${VisualLogging.section().context('empty-view')}`);
     this.textElement = this.contentElement.createChild('div', 'empty-bold-text');
     this.textElement.textContent = text;
   }
@@ -63,7 +65,9 @@
   }
 
   appendLink(link: Platform.DevToolsPath.UrlString): HTMLElement {
-    return this.contentElement.appendChild(XLink.create(link, i18nString(UIStrings.learnMore))) as HTMLElement;
+    const learnMoreLink = XLink.create(link, i18nString(UIStrings.learnMore));
+    learnMoreLink.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('learn-more')}`);
+    return this.contentElement.appendChild(learnMoreLink) as HTMLElement;
   }
 
   appendWarning(message: string, learnMoreLink: Platform.DevToolsPath.UrlString): Infobar {
diff --git a/front_end/ui/legacy/ReportView.ts b/front_end/ui/legacy/ReportView.ts
index 566988f..21250e4 100644
--- a/front_end/ui/legacy/ReportView.ts
+++ b/front_end/ui/legacy/ReportView.ts
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import * as VisualLogging from '../visual_logging/visual_logging.js';
+
 import * as ARIAUtils from './ARIAUtils.js';
 import reportViewStyles from './reportView.css.legacy.js';
 import {Toolbar} from './Toolbar.js';
@@ -66,6 +68,7 @@
     if (link) {
       this.urlElement.appendChild(link);
     }
+    this.urlElement.setAttribute('jslog', `${VisualLogging.link().track({click: true}).context('source-location')}`);
   }
 
   createToolbar(): Toolbar {