Display `Copy owner's email` button for owner

When owners list is returned from the plugin's REST API it contains
only the account id. Email is available in account details therefore
it can be obtained from the Gerrit's REST API. But that:
* would mean that accout details are obtained two times - once for the
  change screen purposes and another time for plugin
* `gr-account-label` or similar would have to be implemented from
  scratch

In order to circumvent that the alternative approach was chosen. The
`gr-account-label` component (which is a part of `gr-owner` element)
contains all the necessary means to obtain account details therefore:
* when the initial `gr-owner` element updsate is performed
  `gr-account-label` instance is obtained (as defined by `GrAccountLabel`
  interface)
* that instance is used to fill account details - note that it leverages
  the caching abilities of the `AccountState` therefore given account
  details are obtained only once

Bug: Issue 373151160
Change-Id: I8dcffa067e07ecc7e63df6d12bca35d1791bd730
diff --git a/owners/web/gerrit-model.ts b/owners/web/gerrit-model.ts
new file mode 100644
index 0000000..f2b88bf
--- /dev/null
+++ b/owners/web/gerrit-model.ts
@@ -0,0 +1,37 @@
+/**
+ * @license
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {AccountInfo} from '@gerritcodereview/typescript-api/rest-api';
+
+/**
+ * A provider for a value. Copied from Gerrit core.
+ */
+export type Provider<T> = () => T;
+
+/**
+ * Partial Gerrit's `AccountsModel` interface that defines functions needed in the owners plugin
+ */
+export interface AccountsModel {
+  fillDetails(account: AccountInfo): AccountInfo;
+}
+
+/**
+ * Parital Gerrit's `GrAccountLabel` interface that provides access to the `AccountsModel`
+ */
+export interface GrAccountLabel extends Element {
+  getAccountsModel: Provider<AccountsModel>;
+}
diff --git a/owners/web/gr-owners.ts b/owners/web/gr-owners.ts
index 4730586..e60e5d4 100644
--- a/owners/web/gr-owners.ts
+++ b/owners/web/gr-owners.ts
@@ -25,6 +25,7 @@
   ChangeStatus,
   LabelInfo,
   isDetailedLabelInfo,
+  EmailAddress,
 } from '@gerritcodereview/typescript-api/rest-api';
 import {
   FilesOwners,
@@ -42,6 +43,8 @@
   PatchRange,
   UserRole,
 } from './owners-model';
+import {query} from './utils';
+import {GrAccountLabel} from './gerrit-model';
 
 const STATUS_CODE = {
   MISSING: 'missing',
@@ -215,6 +218,9 @@
   @property({type: Object})
   info?: LabelInfo;
 
+  @property({type: String})
+  email?: EmailAddress;
+
   static override get styles() {
     return [
       css`
@@ -226,6 +232,9 @@
           --gr-vote-chip-width: 14px;
           --gr-vote-chip-height: 14px;
         }
+        gr-tooltip-content {
+          display: inline-block;
+        }
       `,
     ];
   }
@@ -244,13 +253,40 @@
         ></gr-vote-chip>`
       : nothing;
 
+    const copyEmail = this.email
+      ? html` <gr-copy-clipboard
+          .text=${this.email}
+          hasTooltip
+          hideinput
+          buttonTitle=${"Copy owner's email to clipboard"}
+        ></gr-copy-clipboard>`
+      : nothing;
+
     return html`
       <div class="container">
         <gr-account-label .account=${this.owner}></gr-account-label>
-        ${voteChip}
+        ${voteChip} ${copyEmail}
       </div>
     `;
   }
+
+  override async updated(changedProperties: PropertyValues) {
+    super.updated(changedProperties);
+
+    if (changedProperties.has('owner')) {
+      if (this.owner && !this.email) {
+        const accountLabel = query<GrAccountLabel>(this, 'gr-account-label');
+        if (!accountLabel) {
+          return;
+        }
+
+        const updateOwner = await accountLabel
+          ?.getAccountsModel()
+          ?.fillDetails(this.owner);
+        this.email = updateOwner?.email;
+      }
+    }
+  }
 }
 
 export const FILE_OWNERS_COLUMN_CONTENT = 'file-owners-column-content';
diff --git a/owners/web/utils.ts b/owners/web/utils.ts
index 349f5fb..d3197f1 100644
--- a/owners/web/utils.ts
+++ b/owners/web/utils.ts
@@ -54,3 +54,18 @@
 
   return false;
 }
+
+/**
+ * Queries for child element specified with a selector. Copied from Gerrit's common-util.ts.
+ */
+export function query<E extends Element = Element>(
+  el: Element | null | undefined,
+  selector: string
+): E | undefined {
+  if (!el) return undefined;
+  if (el.shadowRoot) {
+    const r = el.shadowRoot.querySelector<E>(selector);
+    if (r) return r;
+  }
+  return el.querySelector<E>(selector) ?? undefined;
+}