Merge changes I632f940f,I86b22613,Ie3c56cb3,I02980c11,Ib59d55e7, ...

* changes:
  Improve readability of copyWithListOfFilesUnchangedButAddedMergeList test
  Add missing license headers
  Fix bad formatting of license headers
  Approval Copying: Improve wording of uploaderin description
  StickyApprovalsIT: Add test for uploaderin condition
  StickyApprovalsIT: Fix copyWithListOfFilesUnchangedButAddedMergeList test
  StickyApprovalsIT: Add test for approverin condition
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
index c3bb1bd..7a80396 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
@@ -119,11 +119,14 @@
             ? html`<a href=${this.computeGroupUrl(audit.member)}
                 >${this.getNameForGroup(audit.member)}</a
               >`
-            : html`<gr-account-label .account=${audit.member}></gr-account-label
+            : html`<gr-account-label
+                  .account=${audit.member}
+                  clickable
+                ></gr-account-label
                 >${this.getIdForUser(audit.member)}`}
         </td>
         <td class="by-user">
-          <gr-account-label .account=${audit.user}></gr-account-label>
+          <gr-account-label clickable .account=${audit.user}></gr-account-label>
           ${this.getIdForUser(audit.user)}
         </td>
       </tr>
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
index 70637b7..ad90759 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
@@ -269,7 +269,7 @@
     return html`
       <tr>
         <td class="nameColumn">
-          <gr-account-label .account=${member}></gr-account-label>
+          <gr-account-label .account=${member} clickable></gr-account-label>
         </td>
         <td>${member.email}</td>
         <td class="deleteColumn">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
index 9fe3de9..d72f916 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
@@ -342,7 +342,7 @@
 
     return html`
       <div class="tagger">
-        <gr-account-label .account=${tagger}> </gr-account-label>
+        <gr-account-label .account=${tagger} clickable> </gr-account-label>
         (<gr-date-formatter withTooltip .dateStr=${tagger.date}>
         </gr-date-formatter
         >)
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts
index bd50407..342280c 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts
@@ -11,7 +11,6 @@
 import {pluralize} from '../../../utils/string-util';
 import {subscribe} from '../../lit/subscription-controller';
 import '../../shared/gr-button/gr-button';
-import '../gr-change-list-mark-active-flow/gr-change-list-mark-active-flow';
 import '../gr-change-list-bulk-abandon-flow/gr-change-list-bulk-abandon-flow';
 
 /**
@@ -108,7 +107,6 @@
               : nothing}
           </div>
           <div class="actionButtons">
-            <gr-change-list-mark-active-flow></gr-change-list-mark-active-flow>
             <gr-change-list-bulk-abandon-flow>
             </gr-change-list-bulk-abandon-flow>
           </div>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts
index c4cca02..ac648f5 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts
@@ -64,7 +64,6 @@
             <span>1 change selected</span>
           </div>
           <div class="actionButtons">
-            <gr-change-list-mark-active-flow></gr-change-list-mark-active-flow>
             <gr-change-list-bulk-abandon-flow>
             </gr-change-list-bulk-abandon-flow>
           </div>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
index cb41851..77602d0 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
@@ -394,6 +394,7 @@
       <td class="cell owner">
         <gr-account-label
           highlightAttention
+          clickable
           .change=${this.change}
           .account=${this.change?.owner}
         ></gr-account-label>
@@ -424,6 +425,7 @@
   private renderChangeReviewers(reviewer: AccountInfo, index: number) {
     return html`
       <gr-account-label
+        clickable
         hideAvatar
         firstName
         highlightAttention
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
index 1ddc604..b345513 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
@@ -588,7 +588,11 @@
         </div>
       </a>
       <span class="placeholder"> -- </span>
-      <gr-account-label deselected="" highlightattention=""></gr-account-label>
+      <gr-account-label
+        deselected=""
+        clickable=""
+        highlightattention=""
+      ></gr-account-label>
       <div></div>
       <span></span>
       <a class="fullRepo" href=""> test-project </a>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow.ts
deleted file mode 100644
index 83c5935..0000000
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {html, LitElement} from 'lit';
-import {customElement, query, state} from 'lit/decorators';
-import {bulkActionsModelToken} from '../../../models/bulk-actions/bulk-actions-model';
-import {resolve} from '../../../models/dependency';
-import {ChangeInfo} from '../../../types/common';
-import {subscribe} from '../../lit/subscription-controller';
-import '../../shared/gr-overlay/gr-overlay';
-import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
-
-@customElement('gr-change-list-mark-active-flow')
-export class GrChangeListMarkActiveFlow extends LitElement {
-  @query('gr-overlay')
-  private overlay!: GrOverlay;
-
-  private readonly getBulkActionsModel = resolve(this, bulkActionsModelToken);
-
-  @state() selectedChanges: ChangeInfo[] = [];
-
-  override connectedCallback(): void {
-    super.connectedCallback();
-    subscribe(
-      this,
-      this.getBulkActionsModel().selectedChanges$,
-      selectedChanges => {
-        this.selectedChanges = selectedChanges;
-      }
-    );
-  }
-
-  override render() {
-    const changesStr = this.selectedChanges.map(c => c._number).join(', ');
-    return html`
-      <gr-button
-        ?disabled=${!this.isEnabled()}
-        flatten
-        @click=${() => this.overlay.open()}
-        >mark as active</gr-button
-      >
-      <gr-overlay>
-        <gr-dialog @cancel=${() => this.overlay.close()}>
-          <div slot="header">Mark Changes as Active</div>
-          <div slot="main">
-            <div>Selected changes: ${changesStr}</div>
-          </div>
-        </gr-dialog>
-      </gr-overlay>
-    `;
-  }
-
-  private isEnabled(): boolean {
-    // TODO: This is sample enable logic. Normally we would also use permissions
-    // from the model data somehow.
-    if (this.selectedChanges.length === 0) return false;
-    return true;
-  }
-}
-declare global {
-  interface HTMLElementTagNameMap {
-    'gr-change-list-mark-active-flow': GrChangeListMarkActiveFlow;
-  }
-}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow_test.ts
deleted file mode 100644
index ef914d8..0000000
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow_test.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {fixture, html} from '@open-wc/testing-helpers';
-import {
-  BulkActionsModel,
-  bulkActionsModelToken,
-} from '../../../models/bulk-actions/bulk-actions-model';
-import {wrapInProvider} from '../../../models/di-provider-element';
-import {getAppContext} from '../../../services/app-context';
-import '../../../test/common-test-setup-karma';
-import {createChange} from '../../../test/test-data-generators';
-import {queryAndAssert, waitUntilObserved} from '../../../test/test-utils';
-import {ChangeInfo, NumericChangeId} from '../../../types/common';
-import {GrButton} from '../../shared/gr-button/gr-button';
-import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
-import './gr-change-list-mark-active-flow';
-import type {GrChangeListMarkActiveFlow} from './gr-change-list-mark-active-flow';
-
-const change1 = {...createChange(), _number: 1 as NumericChangeId};
-const change2 = {...createChange(), _number: 2 as NumericChangeId};
-
-suite('gr-change-list-mark-active-flow tests', () => {
-  let element: GrChangeListMarkActiveFlow;
-  let model: BulkActionsModel;
-
-  async function selectChange(change: ChangeInfo) {
-    model.addSelectedChangeNum(change._number);
-    await waitUntilObserved(model.selectedChangeNums$, selectedChangeNums =>
-      selectedChangeNums.includes(change._number)
-    );
-    await element.updateComplete;
-  }
-
-  setup(async () => {
-    model = new BulkActionsModel(getAppContext().restApiService);
-    model.sync([change1, change2]);
-
-    element = (
-      await fixture(
-        wrapInProvider(
-          html`<gr-change-list-mark-active-flow></gr-change-list-mark-active-flow>`,
-          bulkActionsModelToken,
-          model
-        )
-      )
-    ).querySelector('gr-change-list-mark-active-flow')!;
-    await selectChange(change1);
-    await selectChange(change2);
-    await element.updateComplete;
-  });
-
-  test('renders flow', async () => {
-    expect(element).shadowDom.to.equal(/* HTML */ `
-      <gr-button flatten="" aria-disabled="false" role="button" tabindex="0"
-        >mark as active</gr-button
-      >
-      <gr-overlay aria-hidden="true" style="outline: none; display: none;">
-        <gr-dialog role="dialog">
-          <div slot="header">Mark Changes as Active</div>
-          <div slot="main">
-            <div>Selected changes: 1, 2</div>
-          </div>
-        </gr-dialog>
-      </gr-overlay>
-    `);
-  });
-
-  test('button enabled when changes selected', async () => {
-    const button = queryAndAssert<GrButton>(element, 'gr-button');
-    assert.isFalse(button.disabled);
-  });
-
-  test('button disabled when no changes selected', async () => {
-    model.clearSelectedChangeNums();
-    await waitUntilObserved(model.selectedChangeNums$, s => s.length === 0);
-    await element.updateComplete;
-
-    const button = queryAndAssert<GrButton>(element, 'gr-button');
-    assert.isTrue(button.disabled);
-  });
-
-  test('overlay hidden before button clicked', async () => {
-    const overlay = queryAndAssert<GrOverlay>(element, 'gr-overlay');
-    assert.isFalse(overlay.opened);
-  });
-
-  test('button click shows overlay', async () => {
-    const button = queryAndAssert<GrButton>(element, 'gr-button');
-
-    button.click();
-    await element.updateComplete;
-
-    const overlay = queryAndAssert<GrOverlay>(element, 'gr-overlay');
-    assert.isTrue(overlay.opened);
-  });
-});
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 2456170..7625756 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -581,14 +581,6 @@
     }, createDefaultPatchChange());
   }
 
-  _getDiffPreferences() {
-    return this.restApiService.getDiffPreferences();
-  }
-
-  _getPreferences() {
-    return this.restApiService.getPreferences();
-  }
-
   // private but used in test
   _toggleFileExpanded(file: PatchSetFile) {
     // Is the path in the list of expanded diffs? If so, remove it, otherwise
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
index 6035b6f..993f811 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
@@ -24,7 +24,6 @@
   ChangeInfo,
   AccountInfo,
   DetailedLabelInfo,
-  LabelNameToInfoMap,
   LabelNameToValuesMap,
 } from '../../../types/common';
 import {
@@ -35,8 +34,8 @@
 import {getAppContext} from '../../../services/app-context';
 import {
   getTriggerVotes,
-  labelCompare,
   showNewSubmitRequirements,
+  computeLabels,
 } from '../../../utils/label-util';
 import {ChangeStatus} from '../../../constants/constants';
 import {fontStyles} from '../../../styles/gr-font-styles';
@@ -106,7 +105,7 @@
   }
 
   private renderOldSubmitRequirements() {
-    const labels = this._computeLabels();
+    const labels = computeLabels(this.account, this.change);
     return html`${this.renderLabels(labels)}${this.renderErrorMessages()}`;
   }
 
@@ -117,7 +116,7 @@
 
   private renderSubmitReqsLabels() {
     const triggerVotes = getTriggerVotes(this.change);
-    const labels = this._computeLabels().filter(
+    const labels = computeLabels(this.account, this.change).filter(
       label => !triggerVotes.includes(label.name)
     );
     if (!labels.length) return;
@@ -135,7 +134,7 @@
 
   private renderTriggerVotes() {
     const triggerVotes = getTriggerVotes(this.change);
-    const labels = this._computeLabels().filter(label =>
+    const labels = computeLabels(this.account, this.change).filter(label =>
       triggerVotes.includes(label.name)
     );
     if (!labels.length) return;
@@ -214,27 +213,6 @@
     return labels;
   }
 
-  private getStringLabelValue(
-    labels: LabelNameToInfoMap,
-    labelName: string,
-    numberValue?: number
-  ): string {
-    const detailedInfo = labels[labelName] as DetailedLabelInfo;
-    if (detailedInfo.values) {
-      for (const labelValue of Object.keys(detailedInfo.values)) {
-        if (Number(labelValue) === numberValue) {
-          return labelValue;
-        }
-      }
-    }
-    // TODO: This code is sometimes executed with numberValue taking the
-    // values 0 and undefined.
-    // For now it is unclear how this is happening, ideally this code should
-    // never be executed.
-    const stringVal = `${numberValue}`;
-    return stringVal;
-  }
-
   private getDefaultValue(labelName?: string) {
     const labels = this.change?.labels;
     if (!labelName || !labels?.[labelName]) return undefined;
@@ -242,43 +220,6 @@
     return labelInfo.default_value;
   }
 
-  _getVoteForAccount(labelName: string): string | null {
-    const labels = this.change?.labels;
-    if (!labels) return null;
-    const votes = labels[labelName] as DetailedLabelInfo;
-    if (votes.all && votes.all.length > 0) {
-      for (let i = 0; i < votes.all.length; i++) {
-        if (
-          this.account &&
-          // TODO(TS): Replace == with === and check code can assign string to _account_id instead of number
-          // eslint-disable-next-line eqeqeq
-          votes.all[i]._account_id == this.account._account_id
-        ) {
-          return this.getStringLabelValue(
-            labels,
-            labelName,
-            votes.all[i].value
-          );
-        }
-      }
-    }
-    return null;
-  }
-
-  _computeLabels(): Label[] {
-    if (!this.account) return [];
-    const labelsObj = this.change?.labels;
-    if (!labelsObj) return [];
-    return Object.keys(labelsObj)
-      .sort(labelCompare)
-      .map(key => {
-        return {
-          name: key,
-          value: this._getVoteForAccount(key),
-        };
-      });
-  }
-
   _computeColumns() {
     if (!this.permittedLabels) return;
     const labels = Object.keys(this.permittedLabels);
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
index 0310424..5b743e2 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
@@ -26,6 +26,7 @@
   createChange,
 } from '../../../test/test-data-generators';
 import {ChangeStatus} from '../../../constants/constants';
+import {getVoteForAccount} from '../../../utils/label-util';
 
 const basicFixture = fixtureFromElement('gr-label-scores');
 
@@ -118,7 +119,10 @@
 
   test('_getVoteForAccount', () => {
     const labelName = 'Code-Review';
-    assert.strictEqual(element._getVoteForAccount(labelName), '+1');
+    assert.strictEqual(
+      getVoteForAccount(labelName, element.account, element.change),
+      '+1'
+    );
   });
 
   test('_computeColumns', () => {
@@ -132,61 +136,6 @@
     });
   });
 
-  test('changes in label score are reflected in _labels', async () => {
-    const change = {
-      ...createChange(),
-      labels: {
-        'Code-Review': {
-          values: {
-            '0': 'No score',
-            '+1': 'good',
-            '+2': 'excellent',
-            '-1': 'bad',
-            '-2': 'terrible',
-          },
-          default_value: 0,
-        },
-        Verified: {
-          values: {
-            '0': 'No score',
-            '+1': 'good',
-            '+2': 'excellent',
-            '-1': 'bad',
-            '-2': 'terrible',
-          },
-          default_value: 0,
-        },
-      },
-    };
-    element.change = change;
-    await flush();
-    let labels = element._computeLabels();
-    assert.deepEqual(labels, [
-      {name: 'Code-Review', value: null},
-      {name: 'Verified', value: null},
-    ]);
-    element.change = {
-      ...change,
-      labels: {
-        ...change.labels,
-        Verified: {
-          ...change.labels.Verified,
-          all: [
-            {
-              _account_id: accountId,
-              value: 1,
-            },
-          ],
-        },
-      },
-    };
-    await flush();
-    labels = element._computeLabels();
-    assert.deepEqual(labels, [
-      {name: 'Code-Review', value: null},
-      {name: 'Verified', value: '+1'},
-    ]);
-  });
   suite('message', () => {
     test('shown when change is abandoned', async () => {
       element.change = {
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts
index b6b3a61..28de23c 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts
@@ -197,14 +197,17 @@
           closeShown: this.removable,
         })}"
       >
-        <gr-account-label
-          .account="${this.account}"
-          .change="${this.change}"
-          ?forceAttention=${this.forceAttention}
-          ?highlightAttention=${this.highlightAttention}
-          .voteableText=${this.voteableText}
-        >
-        </gr-account-label>
+        <div>
+          <gr-account-label
+            .account="${this.account}"
+            .change="${this.change}"
+            ?forceAttention=${this.forceAttention}
+            ?highlightAttention=${this.highlightAttention}
+            .voteableText=${this.voteableText}
+            clickable
+          >
+          </gr-account-label>
+        </div>
         <slot name="vote-chip"></slot>
         <gr-button
           id="remove"
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts
index af0ea9b..0379c8a 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts
@@ -39,7 +39,9 @@
   test('renders', () => {
     expect(element).shadowDom.to.equal(/* HTML */ `
       <div class="container">
-        <gr-account-label deselected=""></gr-account-label>
+        <div>
+          <gr-account-label clickable="" deselected=""></gr-account-label>
+        </div>
         <slot name="vote-chip"></slot>
         <gr-button
           aria-disabled="false"
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
index 0c85c20..d9a919e 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
@@ -27,11 +27,12 @@
 import {fireEvent} from '../../../utils/event-util';
 import {isInvolved} from '../../../utils/change-util';
 import {ShowAlertEventDetail} from '../../../types/events';
-import {LitElement, css, html} from 'lit';
+import {LitElement, css, html, TemplateResult} from 'lit';
 import {customElement, property, state} from 'lit/decorators';
 import {classMap} from 'lit/directives/class-map';
 import {getRemovedByIconClickReason} from '../../../utils/attention-set-util';
 import {ifDefined} from 'lit/directives/if-defined';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 
 @customElement('gr-account-label')
 export class GrAccountLabel extends LitElement {
@@ -98,6 +99,9 @@
   deselected = false;
 
   @property({type: Boolean, reflect: true})
+  clickable = false;
+
+  @property({type: Boolean, reflect: true})
   attentionIconShown = false;
 
   @property({type: Boolean, reflect: true})
@@ -174,7 +178,6 @@
         }
         .name {
           display: inline-block;
-          text-decoration: inherit;
           vertical-align: top;
           overflow: hidden;
           text-overflow: ellipsis;
@@ -183,6 +186,13 @@
         .hasAttention .name {
           font-weight: var(--font-weight-bold);
         }
+        a.ownerLink {
+          text-decoration: none;
+          color: var(--primary-text-color);
+        }
+        :host([clickable]) a.ownerLink:hover .name {
+          text-decoration: underline;
+        }
       `,
     ];
   }
@@ -245,26 +255,31 @@
               </gr-button>
             </gr-tooltip-content>`
           : ''}
-        <span
-          class="${classMap({
-            hovercardTargetWrapper: true,
-            hasAttention: this.attentionIconShown,
-          })}"
-        >
-          ${this.avatarShown
-            ? html`<gr-avatar .account="${account}" imageSize="32"></gr-avatar>`
-            : ''}
+        ${this.maybeRenderLink(html`
           <span
-            tabindex=${this.hideHovercard ? '-1' : '0'}
-            role=${ifDefined(this.hideHovercard ? undefined : 'button')}
-            id="hovercardTarget"
-            class="name"
-            part="gr-account-label-text"
+            class="${classMap({
+              hovercardTargetWrapper: true,
+              hasAttention: this.attentionIconShown,
+            })}"
           >
-            ${this._computeName(account, this.firstName, this._config)}
+            ${this.avatarShown
+              ? html`<gr-avatar
+                  .account="${account}"
+                  imageSize="32"
+                ></gr-avatar>`
+              : ''}
+            <span
+              tabindex=${this.hideHovercard ? '-1' : '0'}
+              role=${ifDefined(this.hideHovercard ? undefined : 'button')}
+              id="hovercardTarget"
+              class="name"
+              part="gr-account-label-text"
+            >
+              ${this._computeName(account, this.firstName, this._config)}
+            </span>
+            ${this.renderAccountStatusPlugins()}
           </span>
-          ${this.renderAccountStatusPlugins()}
-        </span>
+        `)}
       </div>
     `;
   }
@@ -283,6 +298,18 @@
     });
   }
 
+  private maybeRenderLink(span: TemplateResult) {
+    if (!this.clickable || !this.account) return span;
+    const url = GerritNav.getUrlForOwner(
+      this.account.email ||
+        this.account.username ||
+        this.account.name ||
+        `${this.account._account_id}`
+    );
+    if (!url) return span;
+    return html`<a class="ownerLink" href="${url}" tabindex="-1">${span}</a>`;
+  }
+
   private renderAccountStatusPlugins() {
     if (!this.account?._account_id || this.noStatusIcons) {
       return;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts
index 5d8ef6e..cad814cd 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts
@@ -24,9 +24,10 @@
   stubRestApi,
 } from '../../../test/test-utils';
 import {GrAccountLabel} from './gr-account-label';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {AccountDetailInfo, ServerInfo} from '../../../types/common';
 import {
-  createAccountDetailWithId,
+  createAccountDetailWithIdNameAndEmail,
   createChange,
   createPluginConfig,
   createServerInfo,
@@ -38,11 +39,12 @@
 suite('gr-account-label tests', () => {
   let element: GrAccountLabel;
   const kermit: AccountDetailInfo = {
-    ...createAccountDetailWithId(31),
+    ...createAccountDetailWithIdNameAndEmail(31),
     name: 'kermit',
   };
 
   setup(async () => {
+    sinon.stub(GerritNav, 'getUrlForOwner').callsFake(() => 'test');
     stubRestApi('getAccount').resolves(kermit);
     stubRestApi('getLoggedIn').resolves(false);
     stubRestApi('getConfig').resolves({
@@ -88,6 +90,38 @@
     `);
   });
 
+  test('renders clickable', async () => {
+    element.account = kermit;
+    element.clickable = true;
+    await element.updateComplete;
+    expect(element).shadowDom.to.equal(/* HTML */ `
+      <div class="container">
+        <gr-hovercard-account for="hovercardTarget"></gr-hovercard-account>
+        <a class="ownerLink" href="test" tabindex="-1">
+          <span class="hovercardTargetWrapper">
+            <gr-avatar hidden="" imagesize="32"> </gr-avatar>
+            <span
+              class="name"
+              id="hovercardTarget"
+              part="gr-account-label-text"
+              role="button"
+              tabindex="0"
+            >
+              kermit
+            </span>
+            <gr-endpoint-decorator
+              class="accountStatusDecorator"
+              name="account-status-icon"
+            >
+              <gr-endpoint-param name="accountId"></gr-endpoint-param>
+              <span class="rightSidePadding"></span>
+            </gr-endpoint-decorator>
+          </span>
+        </a>
+      </div>
+    `);
+  });
+
   suite('_computeName', () => {
     test('not showing anonymous', () => {
       const account = {name: 'Wyatt'};
@@ -137,14 +171,14 @@
       };
       element._selfAccount = kermit;
       element.account = {
-        ...createAccountDetailWithId(42),
+        ...createAccountDetailWithIdNameAndEmail(42),
         name: 'ernie',
       };
       element.change = {
         ...createChange(),
         attention_set: {
           42: {
-            account: createAccountDetailWithId(42),
+            account: createAccountDetailWithIdNameAndEmail(42),
           },
         },
         owner: kermit,
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
index cdbdcab..36a7354 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
@@ -300,6 +300,7 @@
       </td>
       <td>
         <gr-account-label
+          clickable
           .account="${mappedLabel.account}"
           .change="${change}"
         ></gr-account-label>
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts
index cb1145c..2a80fdf 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts
@@ -198,13 +198,17 @@
     startLine: LineNumber,
     endLine: LineNumber,
     side: Side
-  ) {
+  ): GrDiffGroup[] {
     const startIndex = this.groups.findIndex(group =>
       group.containsLine(side, startLine)
     );
-    const endIndex = this.groups.findIndex(group =>
+    if (startIndex === -1) return [];
+    let endIndex = this.groups.findIndex(group =>
       group.containsLine(side, endLine)
     );
+    // Not all groups may have rendered yet. In that case let's just render
+    // *all* groups after `startIndex`.
+    if (endIndex === -1) endIndex = this.groups.length - 1;
     // The filter preserves the legacy behavior to only return non-context
     // groups
     return this.groups
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
index aa2ad07..3c6a498 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
@@ -122,8 +122,10 @@
   'template-tag',
   'template-variable',
   'title',
+  'title function_',
   'type',
   'variable',
+  'variable language_',
 ]);
 
 export class GrSyntaxLayerWorker implements DiffLayer {
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts b/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts
index 267aaaf..b74cbb3 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts
@@ -52,6 +52,9 @@
   .gr-syntax-variable {
     color: var(--syntax-variable-color);
   }
+  .gr-syntax-variable.language_ {
+    color: var(--syntax-variable-language-color);
+  }
   .gr-syntax-template-variable {
     color: var(--syntax-template-variable-color);
   }
@@ -82,6 +85,9 @@
   .gr-syntax-title {
     color: var(--syntax-title-color);
   }
+  .gr-syntax-title.function_ {
+    color: var(--syntax-title-function-color);
+  }
   .gr-syntax-attr {
     color: var(--syntax-attr-color);
   }
diff --git a/polygerrit-ui/app/styles/themes/app-theme.ts b/polygerrit-ui/app/styles/themes/app-theme.ts
index 4e719b7..f31af21 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.ts
+++ b/polygerrit-ui/app/styles/themes/app-theme.ts
@@ -409,19 +409,21 @@
     --syntax-meta-keyword-color: #219;
     --syntax-number-color: #164;
     --syntax-params-color: var(--primary-text-color);
+    --syntax-property-color: var(--primary-text-color);
     --syntax-regexp-color: #fa8602;
     --syntax-selector-attr-color: #fa8602;
     --syntax-selector-class-color: #164;
     --syntax-selector-id-color: #2a00ff;
-    --syntax-property-color: #fa8602;
     --syntax-selector-pseudo-color: #fa8602;
     --syntax-string-color: #2a00ff;
     --syntax-tag-color: #170;
     --syntax-template-tag-color: #fa8602;
     --syntax-template-variable-color: #0000c0;
     --syntax-title-color: #0000c0;
+    --syntax-title-function-color: var(--syntax-title-color);
     --syntax-type-color: var(--blue-700);
     --syntax-variable-color: var(--primary-text-color);
+    --syntax-variable-language-color: var(--syntax-built_in-color);
 
     /* elevation */
     --elevation-level-1: 0px 1px 2px 0px rgba(60, 64, 67, 0.3),
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.ts b/polygerrit-ui/app/styles/themes/dark-theme.ts
index c8f0d47..6856df8 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.ts
+++ b/polygerrit-ui/app/styles/themes/dark-theme.ts
@@ -246,19 +246,21 @@
     --syntax-meta-keyword-color: #eefff7;
     --syntax-number-color: #00998a;
     --syntax-params-color: var(--primary-text-color);
+    --syntax-property-color: #c792ea;
     --syntax-regexp-color: #f77669;
     --syntax-selector-attr-color: #80cbbf;
     --syntax-selector-class-color: #ffcb68;
     --syntax-selector-id-color: #f77669;
     --syntax-selector-pseudo-color: #c792ea;
-    --syntax-property-color: #c792ea;
     --syntax-string-color: #c3e88d;
     --syntax-tag-color: #f77669;
     --syntax-template-tag-color: #c792ea;
     --syntax-template-variable-color: #f77669;
     --syntax-title-color: #75a5ff;
+    --syntax-title-function-color: var(--syntax-title-color);
     --syntax-type-color: #dd5f5f;
     --syntax-variable-color: #f77669;
+    --syntax-variable-language-color: var(--syntax-built_in-color);
 
     /* misc */
     --line-length-indicator-color: #d7aefb;
diff --git a/polygerrit-ui/app/test/test-data-generators.ts b/polygerrit-ui/app/test/test-data-generators.ts
index 329ea11..9666387 100644
--- a/polygerrit-ui/app/test/test-data-generators.ts
+++ b/polygerrit-ui/app/test/test-data-generators.ts
@@ -201,6 +201,17 @@
   };
 }
 
+export function createAccountDetailWithIdNameAndEmail(
+  id = 5
+): AccountDetailInfo {
+  return {
+    _account_id: id as AccountId,
+    email: `user-${id}@` as EmailAddress,
+    name: `User-${id}`,
+    registered_on: dateToTimestamp(new Date(2020, 10, 15, 14, 5, 8)),
+  };
+}
+
 export function createReviewers(): Reviewers {
   return {};
 }
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index 563e864..aa55ea2 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -32,6 +32,7 @@
 } from '../types/common';
 import {ParsedChangeInfo} from '../types/types';
 import {assertNever, unique} from './common-util';
+import {Label} from '../elements/change/gr-label-score-row/gr-label-score-row';
 
 // Name of the standard Code-Review label.
 export enum StandardLabels {
@@ -294,6 +295,60 @@
   return priorityRequirementList.concat(nonPriorityRequirements);
 }
 
+function getStringLabelValue(
+  labels: LabelNameToInfoMap,
+  labelName: string,
+  numberValue?: number
+): string {
+  const detailedInfo = labels[labelName] as DetailedLabelInfo;
+  if (detailedInfo.values) {
+    for (const labelValue of Object.keys(detailedInfo.values)) {
+      if (Number(labelValue) === numberValue) {
+        return labelValue;
+      }
+    }
+  }
+  // TODO: This code is sometimes executed with numberValue taking the
+  // values 0 and undefined.
+  // For now it is unclear how this is happening, ideally this code should
+  // never be executed.
+  return `${numberValue}`;
+}
+
+export function getVoteForAccount(
+  labelName: string,
+  account?: AccountInfo,
+  change?: ChangeInfo
+): string | null {
+  const labels = change?.labels;
+  if (!account || !labels) return null;
+  const votes = labels[labelName] as DetailedLabelInfo;
+  if (!votes.all?.length) return null;
+  for (let i = 0; i < votes.all.length; i++) {
+    if (votes.all[i]._account_id === account._account_id) {
+      return getStringLabelValue(labels, labelName, votes.all[i].value);
+    }
+  }
+  return null;
+}
+
+export function computeLabels(
+  account?: AccountInfo,
+  change?: ChangeInfo
+): Label[] {
+  if (!account) return [];
+  const labelsObj = change?.labels;
+  if (!labelsObj) return [];
+  return Object.keys(labelsObj)
+    .sort(labelCompare)
+    .map(key => {
+      return {
+        name: key,
+        value: getVoteForAccount(key, account, change),
+      };
+    });
+}
+
 export function getTriggerVotes(change?: ParsedChangeInfo | ChangeInfo) {
   const allLabels = Object.keys(change?.labels ?? {});
   // Normally there is utility method getRequirements, which filter out
diff --git a/polygerrit-ui/app/utils/label-util_test.ts b/polygerrit-ui/app/utils/label-util_test.ts
index f4e1da9..56fb58c 100644
--- a/polygerrit-ui/app/utils/label-util_test.ts
+++ b/polygerrit-ui/app/utils/label-util_test.ts
@@ -29,6 +29,7 @@
   hasNeutralStatus,
   labelCompare,
   LabelStatus,
+  computeLabels,
 } from './label-util';
 import {
   AccountId,
@@ -45,10 +46,12 @@
   createSubmitRequirementResultInfo,
   createNonApplicableSubmitRequirementResultInfo,
   createDetailedLabelInfo,
+  createAccountWithId,
 } from '../test/test-data-generators';
 import {
   SubmitRequirementResultInfo,
   SubmitRequirementStatus,
+  LabelNameToInfoMap,
 } from '../api/rest-api';
 
 const VALUES_0 = {
@@ -237,6 +240,58 @@
     assert.equal(getRepresentativeValue(labelInfo), -2);
   });
 
+  test('computeLabels', async () => {
+    const accountId = 123 as AccountId;
+    const account = createAccountWithId(accountId);
+    const change = {
+      ...createChange(),
+      labels: {
+        'Code-Review': {
+          values: {
+            '0': 'No score',
+            '+1': 'good',
+            '+2': 'excellent',
+            '-1': 'bad',
+            '-2': 'terrible',
+          },
+          default_value: 0,
+        } as DetailedLabelInfo,
+        Verified: {
+          values: {
+            '0': 'No score',
+            '+1': 'good',
+            '+2': 'excellent',
+            '-1': 'bad',
+            '-2': 'terrible',
+          },
+          default_value: 0,
+        } as DetailedLabelInfo,
+      } as LabelNameToInfoMap,
+    };
+    let labels = computeLabels(account, change);
+    assert.deepEqual(labels, [
+      {name: 'Code-Review', value: null},
+      {name: 'Verified', value: null},
+    ]);
+    change.labels = {
+      ...change.labels,
+      Verified: {
+        ...change.labels.Verified,
+        all: [
+          {
+            _account_id: accountId,
+            value: 1,
+          },
+        ],
+      } as DetailedLabelInfo,
+    } as LabelNameToInfoMap;
+    labels = computeLabels(account, change);
+    assert.deepEqual(labels, [
+      {name: 'Code-Review', value: null},
+      {name: 'Verified', value: '+1'},
+    ]);
+  });
+
   suite('extractAssociatedLabels()', () => {
     function createSubmitRequirementExpressionInfoWith(expression: string) {
       return {