Merge changes Ieb5535eb,I80bcf81b

* changes:
  Fix `annotateText()` for nodes of length 0
  Refactor `annotateText()` tests to check `innerHTML`
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
index af4e87e..1be5fa3 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
@@ -129,6 +129,7 @@
 
   constructor() {
     super();
+    this.addEventListener('reload', () => window.location.reload());
     subscribe(
       this,
       () => this.getAdminViewModel().state$,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts
index 889a859..558d571 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts
@@ -6,8 +6,6 @@
 import '@polymer/iron-input/iron-input';
 import '../../shared/gr-button/gr-button';
 import '../../shared/gr-select/gr-select';
-import {encodeURL, getBaseUrl} from '../../../utils/url-util';
-import {page} from '../../../utils/page-wrapper-utils';
 import {BranchName, RepoName} from '../../../types/common';
 import {getAppContext} from '../../../services/app-context';
 import {formStyles} from '../../../styles/gr-form-styles';
@@ -15,7 +13,7 @@
 import {LitElement, PropertyValues, css, html} from 'lit';
 import {customElement, property, state} from 'lit/decorators.js';
 import {BindValueChangeEvent} from '../../../types/events';
-import {fireEvent} from '../../../utils/event-util';
+import {fireAlert, fireEvent, fireReload} from '../../../utils/event-util';
 import {RepoDetailView} from '../../../models/views/repo';
 
 declare global {
@@ -126,13 +124,13 @@
       throw new Error('itemName name is not set');
     }
     const USE_HEAD = this.itemRevision ? this.itemRevision : 'HEAD';
-    const url = `${getBaseUrl()}/admin/repos/${encodeURL(this.repoName, true)}`;
     if (this.itemDetail === RepoDetailView.BRANCHES) {
       return this.restApiService
         .createRepoBranch(this.repoName, this.itemName, {revision: USE_HEAD})
         .then(itemRegistered => {
           if (itemRegistered.status === 201) {
-            page.show(`${url},branches`);
+            fireAlert(this, 'Branch created successfully. Reloading...');
+            fireReload(this);
           }
         });
     } else if (this.itemDetail === RepoDetailView.TAGS) {
@@ -143,7 +141,8 @@
         })
         .then(itemRegistered => {
           if (itemRegistered.status === 201) {
-            page.show(`${url},tags`);
+            fireAlert(this, 'Tag created successfully. Reloading...');
+            fireReload(this);
           }
         });
     }
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
index 64bfe91..2b7d1ff 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.ts
@@ -528,6 +528,9 @@
     this.reporting.reportErrorDialog(message);
     this.errorDialog.text = message;
     this.errorDialog.showSignInButton = !!options && !!options.showSignInButton;
+    if (this.errorModal.hasAttribute('open')) {
+      this.errorModal.close();
+    }
     this.errorModal.showModal();
   }
 }
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts
index 090d125..5270603 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts
@@ -347,7 +347,8 @@
   createTextEl(
     lineNumberEl: HTMLElement | null,
     line: GrDiffLine,
-    side?: Side
+    side?: Side,
+    twoSlots?: boolean
   ) {
     const td = createElementDiff('td');
     if (line.type !== GrDiffLineType.BLANK) {
@@ -403,9 +404,19 @@
       const threadGroupEl = document.createElement('div');
       threadGroupEl.className = 'thread-group';
       threadGroupEl.setAttribute('data-side', side);
+
       const slot = document.createElement('slot');
       slot.name = `${side}-${lineNumber}`;
       threadGroupEl.appendChild(slot);
+
+      // For line.type === BOTH in unified diff we want two slots.
+      if (twoSlots) {
+        const slot = document.createElement('slot');
+        const otherSide = side === Side.LEFT ? Side.RIGHT : Side.LEFT;
+        slot.name = `${otherSide}-${line.lineNumber(otherSide)}`;
+        threadGroupEl.appendChild(slot);
+      }
+
       td.appendChild(threadGroupEl);
     }
 
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-unified.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-unified.ts
index f760232..ffc2dcd 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-unified.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-unified.ts
@@ -142,7 +142,8 @@
         .join(' ')
         .trim()
     );
-    row.appendChild(this.createTextEl(lineNumberEl, line, side));
+    const twoSlots = line.type === GrDiffLineType.BOTH;
+    row.appendChild(this.createTextEl(lineNumberEl, line, side, twoSlots));
     return row;
   }
 
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
index e496cca..14b8280 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
@@ -348,7 +348,7 @@
           if (lineNumber)
             fire(this, 'line-mouse-leave', {lineNum: lineNumber, side});
         }}
-      >${this.renderText(side)}${this.renderThreadGroup(side, lineNumber)}</td>
+      >${this.renderText(side)}${this.renderThreadGroup(side)}</td>
     `;
   }
 
@@ -369,11 +369,21 @@
     return html`<td class=${diffClasses(...extras)}>${sign}</td>`;
   }
 
-  private renderThreadGroup(side: Side, lineNumber?: LineNumber) {
-    if (!lineNumber) return;
-    // .content has `white-space: pre`, so prettier must not add spaces.
-    // prettier-ignore
-    return html`<div class="thread-group" data-side=${side}><slot name="${side}-${lineNumber}"></slot></div>`;
+  private renderThreadGroup(side: Side) {
+    const lineNumber = this.lineNumber(side);
+    if (!lineNumber) return nothing;
+    return html`<div class="thread-group" data-side=${side}>
+      <slot name="${side}-${lineNumber}"></slot>
+      ${this.renderSecondSlot()}
+    </div>`;
+  }
+
+  private renderSecondSlot() {
+    if (!this.unifiedDiff) return nothing;
+    if (this.line(Side.LEFT)?.type !== GrDiffLineType.BOTH) return nothing;
+    return html`<slot
+      name="${Side.LEFT}-${this.lineNumber(Side.LEFT)}"
+    ></slot>`;
   }
 
   private contentRef(side: Side) {
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts
index 27fc920..1c7b311 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts
@@ -93,6 +93,66 @@
     );
   });
 
+  test('both unified', async () => {
+    const line = new GrDiffLine(GrDiffLineType.BOTH, 1, 1);
+    line.text = 'lorem ipsum';
+    element.left = line;
+    element.right = line;
+    element.unifiedDiff = true;
+    await element.updateComplete;
+    assert.lightDom.equal(
+      element,
+      /* HTML */ `
+        <table>
+          <tbody>
+            <tr
+              aria-labelledby="left-button-1 right-button-1 right-content-1"
+              class="both diff-row gr-diff unified"
+              tabindex="-1"
+            >
+              <td class="blame gr-diff" data-line-number="1"></td>
+              <td class="gr-diff left lineNum" data-value="1">
+                <button
+                  aria-label="1 unmodified"
+                  class="gr-diff left lineNumButton"
+                  data-value="1"
+                  id="left-button-1"
+                  tabindex="-1"
+                >
+                  1
+                </button>
+              </td>
+              <td class="gr-diff lineNum right" data-value="1">
+                <button
+                  aria-label="1 unmodified"
+                  class="gr-diff lineNumButton right"
+                  data-value="1"
+                  id="right-button-1"
+                  tabindex="-1"
+                >
+                  1
+                </button>
+              </td>
+              <td class="both content gr-diff no-intraline-info right">
+                <div
+                  class="contentText gr-diff"
+                  data-side="right"
+                  id="right-content-1"
+                >
+                  <gr-diff-text> lorem ipsum </gr-diff-text>
+                </div>
+                <div class="thread-group" data-side="right">
+                  <slot name="right-1"> </slot>
+                  <slot name="left-1"> </slot>
+                </div>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      `
+    );
+  });
+
   test('add', async () => {
     const line = new GrDiffLine(GrDiffLineType.ADD, 0, 1);
     line.text = 'lorem ipsum';
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
index bbe091f..ce1393d 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
@@ -3218,6 +3218,7 @@
                       </div>
                       <div class="thread-group" data-side="right">
                         <slot name="right-FILE"> </slot>
+                        <slot name="left-FILE"> </slot>
                       </div>
                     </td>
                   </tr>
diff --git a/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.ts b/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.ts
index 58f3f75..38eecfa 100644
--- a/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.ts
+++ b/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.ts
@@ -144,12 +144,13 @@
       side,
       range,
       operation: (forLine, startChar, endChar) => {
-        forLine.push({
-          start: startChar,
-          end: endChar,
-          id: id(commentRange),
-          longRange,
-        });
+        if (startChar !== endChar)
+          forLine.push({
+            start: startChar,
+            end: endChar,
+            id: id(commentRange),
+            longRange,
+          });
       },
     });
   }
@@ -202,7 +203,7 @@
       // Normalize invalid ranges where the start is after the end but the
       // start still makes sense. Set the end to the end of the line.
       // @see Issue 5744
-      if (range.start >= range.end && range.start < line.text.length) {
+      if (range.start > range.end && range.start < line.text.length) {
         range.end = line.text.length;
       }
 
diff --git a/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.ts b/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.ts
index 33515b25..7feda47 100644
--- a/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.ts
@@ -69,6 +69,16 @@
   },
 };
 
+const rangeF: CommentRangeLayer = {
+  side: Side.RIGHT,
+  range: {
+    end_character: 0,
+    end_line: 24,
+    start_character: 0,
+    start_line: 23,
+  },
+};
+
 suite('gr-ranged-comment-layer', () => {
   let element: GrRangedCommentLayer;
 
@@ -79,6 +89,7 @@
       rangeC,
       rangeD,
       rangeE,
+      rangeF,
     ];
 
     element = new GrRangedCommentLayer();
@@ -219,6 +230,16 @@
       );
     });
 
+    test('do not annotate lines with end_character 0', () => {
+      line = new GrDiffLine(GrDiffLineType.BOTH);
+      line.afterNumber = 24;
+      el.setAttribute('data-side', Side.RIGHT);
+
+      element.annotate(el, lineNumberEl, line);
+
+      assert.isFalse(annotateElementStub.called);
+    });
+
     test('updateRanges remove all', () => {
       assertHasRange(rangeA, true);
       assertHasRange(rangeB, true);