Merge "Use Suggests CC rest API"
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 5bbc9b8..6fb9220 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -57,8 +57,8 @@
 
 [[details]]
 --
-* `DETAILS`: Includes full name, preferred email, username, avatars and
-status for each account.
+* `DETAILS`: Includes full name, preferred email, username, avatars,
+status and state for each account.
 --
 
 [[all-emails]]
@@ -2200,7 +2200,7 @@
 account.
 
 `AccountDetailInfo` has the same fields as link:#account-info[
-AccountInfo]. In addition `AccountDetailInfo` has the following fields:
+AccountInfo]. In addition `AccountDetailInfo` has the following field:
 
 [options="header",cols="1,^1,5"]
 |=================================
@@ -2208,8 +2208,6 @@
 |`registered_on`     ||
 The link:rest-api.html#timestamp[timestamp] of when the account was
 registered.
-|`inactive`          |not set if `false`|
-Whether the account is inactive.
 |=================================
 
 [[account-external-id-info]]
@@ -2266,6 +2264,8 @@
 Whether the query would deliver more results if not limited. +
 Only set on the last account that is returned.
 |`status`          |optional|Status message of the account.
+|`inactive`        |not set if `false`|
+Whether the account is inactive.
 |===============================
 
 [[account-input]]
diff --git a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
index a498ab0..3ffa97a 100644
--- a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
@@ -18,7 +18,6 @@
 
 public class AccountDetailInfo extends AccountInfo {
   public Timestamp registeredOn;
-  public Boolean inactive;
 
   public AccountDetailInfo(Integer id) {
     super(id);
diff --git a/java/com/google/gerrit/extensions/common/AccountInfo.java b/java/com/google/gerrit/extensions/common/AccountInfo.java
index f20509b..e949f07 100644
--- a/java/com/google/gerrit/extensions/common/AccountInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountInfo.java
@@ -26,6 +26,7 @@
   public List<AvatarInfo> avatars;
   public Boolean _moreAccounts;
   public String status;
+  public Boolean inactive;
 
   public AccountInfo(Integer id) {
     this._accountId = id;
diff --git a/java/com/google/gerrit/extensions/restapi/Response.java b/java/com/google/gerrit/extensions/restapi/Response.java
index 6a1020a..f5b9145 100644
--- a/java/com/google/gerrit/extensions/restapi/Response.java
+++ b/java/com/google/gerrit/extensions/restapi/Response.java
@@ -14,6 +14,10 @@
 
 package com.google.gerrit.extensions.restapi;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.gerrit.common.Nullable;
+import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
 /** Special return value to mean specific HTTP status codes in a REST API. */
@@ -51,6 +55,19 @@
     return new Redirect(location);
   }
 
+  /**
+   * HTTP 500 Internal Server Error: failure due to an unexpected exception.
+   *
+   * <p>Can be returned from REST endpoints, instead of throwing the exception, if additional
+   * properties (e.g. a traceId) should be set on the response.
+   *
+   * @param cause the exception that caused the request to fail, must not be a {@link
+   *     RestApiException} because such an exception would result in a 4XX response code
+   */
+  public static <T> InternalServerError<T> internalServerError(Exception cause) {
+    return new InternalServerError<>(cause);
+  }
+
   /** Arbitrary status code with wrapped result. */
   public static <T> Response<T> withStatusCode(int statusCode, T value) {
     return new Impl<>(statusCode, value);
@@ -64,6 +81,17 @@
     return obj;
   }
 
+  private String traceId;
+
+  public Response<T> traceId(@Nullable String traceId) {
+    this.traceId = traceId;
+    return this;
+  }
+
+  public Optional<String> traceId() {
+    return Optional.ofNullable(traceId);
+  }
+
   public abstract boolean isNone();
 
   public abstract int statusCode();
@@ -258,4 +286,57 @@
       return String.format("[202 Accepted] %s", location);
     }
   }
+
+  public static final class InternalServerError<T> extends Response<T> {
+    private final Exception cause;
+
+    private InternalServerError(Exception cause) {
+      checkArgument(!(cause instanceof RestApiException), "cause must not be a RestApiException");
+      this.cause = cause;
+    }
+
+    @Override
+    public boolean isNone() {
+      return false;
+    }
+
+    @Override
+    public int statusCode() {
+      return 500;
+    }
+
+    @Override
+    public T value() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CacheControl caching() {
+      return CacheControl.NONE;
+    }
+
+    @Override
+    public Response<T> caching(CacheControl c) {
+      throw new UnsupportedOperationException();
+    }
+
+    public Exception cause() {
+      return cause;
+    }
+
+    @Override
+    public int hashCode() {
+      return cause.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof InternalServerError && ((InternalServerError<?>) o).cause.equals(cause);
+    }
+
+    @Override
+    public String toString() {
+      return String.format("[500 Internal Server Error] %s", cause.getClass());
+    }
+  }
 }
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 66768ec..250b665 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -552,6 +552,8 @@
             throw new ResourceNotFoundException();
           }
 
+          response.traceId().ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId));
+
           if (response instanceof Response.Redirect) {
             CacheHeaders.setNotCacheable(res);
             String location = ((Response.Redirect) response).location();
@@ -564,6 +566,12 @@
             res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted) response).location());
             logger.atFinest().log("REST call succeeded: %d", response.statusCode());
             return;
+          } else if (response instanceof Response.InternalServerError) {
+            // Rethrow the exception to have exactly the same error handling as if the REST endpoint
+            // would have thrown the exception directly, instead of returning
+            // Response.InternalServerError.
+            Exception cause = ((Response.InternalServerError<?>) response).cause();
+            throw cause;
           }
 
           status = response.statusCode();
diff --git a/java/com/google/gerrit/server/account/AccountDirectory.java b/java/com/google/gerrit/server/account/AccountDirectory.java
index ee9265f..60c1678 100644
--- a/java/com/google/gerrit/server/account/AccountDirectory.java
+++ b/java/com/google/gerrit/server/account/AccountDirectory.java
@@ -45,7 +45,10 @@
     ID,
 
     /** The user-settable status of this account (e.g. busy, OOO, available) */
-    STATUS
+    STATUS,
+
+    /** The state of the account (e.g. active or inactive) */
+    STATE
   }
 
   public abstract void fillAccountInfo(Iterable<? extends AccountInfo> in, Set<FillOptions> options)
diff --git a/java/com/google/gerrit/server/account/AccountLoader.java b/java/com/google/gerrit/server/account/AccountLoader.java
index 09b9ac3..5d0059e 100644
--- a/java/com/google/gerrit/server/account/AccountLoader.java
+++ b/java/com/google/gerrit/server/account/AccountLoader.java
@@ -42,6 +42,7 @@
               FillOptions.EMAIL,
               FillOptions.USERNAME,
               FillOptions.STATUS,
+              FillOptions.STATE,
               FillOptions.AVATARS));
 
   public interface Factory {
diff --git a/java/com/google/gerrit/server/account/InternalAccountDirectory.java b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
index 8062eaf..dde8f25 100644
--- a/java/com/google/gerrit/server/account/InternalAccountDirectory.java
+++ b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
@@ -147,6 +147,10 @@
       info.status = account.status();
     }
 
+    if (options.contains(FillOptions.STATE)) {
+      info.inactive = account.inactive() ? true : null;
+    }
+
     if (options.contains(FillOptions.AVATARS)) {
       AvatarProvider ap = avatar.get();
       if (ap != null) {
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index d24832b..b120379 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -102,6 +102,8 @@
 
     abstract Optional<Predicate<Throwable>> retryWithTrace();
 
+    abstract Optional<Consumer<String>> onAutoTrace();
+
     @AutoValue.Builder
     public abstract static class Builder {
       public abstract Builder listener(RetryListener listener);
@@ -112,6 +114,8 @@
 
       public abstract Builder retryWithTrace(Predicate<Throwable> exceptionPredicate);
 
+      public abstract Builder onAutoTrace(Consumer<String> traceIdConsumer);
+
       public abstract Options build();
     }
   }
@@ -311,9 +315,9 @@
                     && opts.retryWithTrace().get().test(t)) {
                   String caller = opts.caller().map(Class::getSimpleName).orElse("N/A");
                   if (!traceContext.isTracing()) {
-                    traceContext
-                        .addTag(RequestId.Type.TRACE_ID, "retry-on-failure-" + new RequestId())
-                        .forceLogging();
+                    String traceId = "retry-on-failure-" + new RequestId();
+                    traceContext.addTag(RequestId.Type.TRACE_ID, traceId).forceLogging();
+                    opts.onAutoTrace().ifPresent(c -> c.accept(traceId));
                     logger.atFine().withCause(t).log(
                         "AutoRetry: %s failed, retry with tracing enabled", caller);
                     metrics.autoRetryCount.increment(actionType, caller);
diff --git a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
index 9ed5a67..96c2ed3 100644
--- a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.update;
 
+import com.google.common.base.Throwables;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -21,6 +22,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
 import com.google.gerrit.extensions.restapi.RestResource;
+import java.util.concurrent.atomic.AtomicReference;
 
 public abstract class RetryingRestCollectionModifyView<
         P extends RestResource, C extends RestResource, I, O>
@@ -34,13 +36,21 @@
   @Override
   public final Response<O> apply(P parentResource, I input)
       throws AuthException, BadRequestException, ResourceConflictException, Exception {
-    RetryHelper.Options retryOptions =
-        RetryHelper.options()
-            .caller(getClass())
-            .retryWithTrace(t -> !(t instanceof RestApiException))
-            .build();
-    return retryHelper.execute(
-        (updateFactory) -> applyImpl(updateFactory, parentResource, input), retryOptions);
+    AtomicReference<String> traceId = new AtomicReference<>(null);
+    try {
+      RetryHelper.Options retryOptions =
+          RetryHelper.options()
+              .caller(getClass())
+              .retryWithTrace(t -> !(t instanceof RestApiException))
+              .onAutoTrace(traceId::set)
+              .build();
+      return retryHelper
+          .execute((updateFactory) -> applyImpl(updateFactory, parentResource, input), retryOptions)
+          .traceId(traceId.get());
+    } catch (Exception e) {
+      Throwables.throwIfInstanceOf(e, RestApiException.class);
+      return Response.<O>internalServerError(e).traceId(traceId.get());
+    }
   }
 
   protected abstract Response<O> applyImpl(
diff --git a/java/com/google/gerrit/server/update/RetryingRestModifyView.java b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
index 20cf0c9..275dc55 100644
--- a/java/com/google/gerrit/server/update/RetryingRestModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
@@ -14,10 +14,12 @@
 
 package com.google.gerrit.server.update;
 
+import com.google.common.base.Throwables;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestResource;
+import java.util.concurrent.atomic.AtomicReference;
 
 public abstract class RetryingRestModifyView<R extends RestResource, I, O>
     implements RestModifyView<R, I> {
@@ -28,14 +30,22 @@
   }
 
   @Override
-  public final Response<O> apply(R resource, I input) throws Exception {
-    RetryHelper.Options retryOptions =
-        RetryHelper.options()
-            .caller(getClass())
-            .retryWithTrace(t -> !(t instanceof RestApiException))
-            .build();
-    return retryHelper.execute(
-        (updateFactory) -> applyImpl(updateFactory, resource, input), retryOptions);
+  public final Response<O> apply(R resource, I input) throws RestApiException {
+    AtomicReference<String> traceId = new AtomicReference<>(null);
+    try {
+      RetryHelper.Options retryOptions =
+          RetryHelper.options()
+              .caller(getClass())
+              .retryWithTrace(t -> !(t instanceof RestApiException))
+              .onAutoTrace(traceId::set)
+              .build();
+      return retryHelper
+          .execute((updateFactory) -> applyImpl(updateFactory, resource, input), retryOptions)
+          .traceId(traceId.get());
+    } catch (Exception e) {
+      Throwables.throwIfInstanceOf(e, RestApiException.class);
+      return Response.<O>internalServerError(e).traceId(traceId.get());
+    }
   }
 
   protected abstract Response<O> applyImpl(BatchUpdate.Factory updateFactory, R resource, I input)
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index ac37530..af4a22e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -596,7 +596,7 @@
     try {
       RestResponse response = adminRestSession.post("/changes/" + changeId + "/submit");
       assertThat(response.getStatusCode()).isEqualTo(SC_OK);
-      assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+      assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).startsWith("retry-on-failure-");
       assertThat(traceSubmitRule.traceId).startsWith("retry-on-failure-");
       assertThat(traceSubmitRule.isLoggingForced).isTrue();
     } finally {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
index c4418c0..c4e8c31 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
@@ -23,13 +23,25 @@
 import java.util.List;
 
 public class AccountAssert {
-
-  public static void assertAccountInfo(TestAccount a, AccountInfo ai) {
-    assertThat(ai._accountId).isEqualTo(a.id().get());
-    assertThat(ai.name).isEqualTo(a.fullName());
-    assertThat(ai.email).isEqualTo(a.email());
+  /**
+   * Asserts an AccountInfo for an active account.
+   *
+   * @param testAccount the TestAccount which the provided AccountInfo is expected to match
+   * @param accountInfo the AccountInfo that should be asserted
+   */
+  public static void assertAccountInfo(TestAccount testAccount, AccountInfo accountInfo) {
+    assertThat(accountInfo._accountId).isEqualTo(testAccount.id().get());
+    assertThat(accountInfo.name).isEqualTo(testAccount.fullName());
+    assertThat(accountInfo.email).isEqualTo(testAccount.email());
+    assertThat(accountInfo.inactive).isNull();
   }
 
+  /**
+   * Asserts an AccountInfos for active accounts.
+   *
+   * @param expected the TestAccounts which the provided AccountInfos are expected to match
+   * @param actual the AccountInfos that should be asserted
+   */
   public static void assertAccountInfos(List<TestAccount> expected, List<AccountInfo> actual) {
     Iterable<Account.Id> expectedIds = TestAccount.ids(expected);
     Iterable<Account.Id> actualIds = Iterables.transform(actual, a -> Account.id(a._accountId));
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
index 931dace..782638a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
@@ -14,17 +14,23 @@
 
 package com.google.gerrit.acceptance.rest.account;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfo;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.inject.Inject;
 import org.junit.Test;
 
 @NoHttpd
 public class GetAccountIT extends AbstractDaemonTest {
+  @Inject private AccountOperations accountOperations;
+
   @Test
   public void getNonExistingAccount_NotFound() throws Exception {
     assertThrows(ResourceNotFoundException.class, () -> gApi.accounts().id("non-existing").get());
@@ -51,6 +57,16 @@
     testGetAccount("self", admin);
   }
 
+  @Test
+  public void getInactiveAccount() throws Exception {
+    accountOperations.account(user.id()).forUpdate().inactive().update();
+    AccountInfo accountInfo = gApi.accounts().id(user.id().get()).get();
+    assertThat(accountInfo._accountId).isEqualTo(user.id().get());
+    assertThat(accountInfo.name).isEqualTo(user.fullName());
+    assertThat(accountInfo.email).isEqualTo(user.email());
+    assertThat(accountInfo.inactive).isTrue();
+  }
+
   private void testGetAccount(String id, TestAccount expectedAccount) throws Exception {
     assertAccountInfo(expectedAccount, gApi.accounts().id(id).get());
   }
diff --git a/plugins/replication b/plugins/replication
index 485027c..72489ee 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 485027c2d63dda7b588fa5c07f91d2a65db4d787
+Subproject commit 72489ee33f2e30711a8911c3bd979f37ea0078c2
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 30a0807..ee1ae11 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -139,9 +139,9 @@
       }
       .editCommitMessage {
         margin-top: 1em;
+
         --gr-button: {
-          padding-left: 0;
-          padding-right: 0;
+          padding: 5px 0px;
         }
       }
       .changeStatuses,
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
index 6e2686e..c1031301 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
@@ -51,8 +51,7 @@
       }
       gr-button {
         --gr-button: {
-          padding-left: 0;
-          padding-right: 0;
+          padding: 5px 0px;
         }
       }
       @media screen and (max-width: 50em), screen and (min-width: 75em) {
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index 08bb700..92c0fb8 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -40,7 +40,7 @@
       }
       .bigTitle {
         color: var(--header-text-color);
-        font-size: 1.75rem;
+        font-size: var(--header-title-font-size);
         text-decoration: none;
       }
       .bigTitle:hover {
diff --git a/polygerrit-ui/app/elements/gr-app-element.html b/polygerrit-ui/app/elements/gr-app-element.html
index a42622e..5c297e7 100644
--- a/polygerrit-ui/app/elements/gr-app-element.html
+++ b/polygerrit-ui/app/elements/gr-app-element.html
@@ -68,13 +68,17 @@
         color: var(--primary-text-color);
       }
       gr-main-header {
-        background-color: var(--header-background-color);
+        background: var(--header-background, var(--header-background-color, #eee));
         padding: 0 var(--default-horizontal-margin);
-        border-bottom: 1px solid var(--border-color);
+        border-bottom: var(--header-border-bottom);
+        border-image: var(--header-border-image);
+        border-right: 0;
+        border-left: 0;
+        border-top: 0;
       }
       footer {
-        background-color: var(--footer-background-color);
-        border-top: 1px solid var(--border-color);
+        background: var(--footer-background, var(--footer-background-color, #eee));
+        border-top: var(--footer-border-top);
         display: flex;
         justify-content: space-between;
         padding: .5rem var(--default-horizontal-margin);
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index 340297f..76ff442 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -122,6 +122,8 @@
       }
       :host([disabled][link]) {
         --background-color: transparent;
+        --text-color: var(--deemphasized-text-color);
+        cursor: default;
       }
 
       /* Styles for the optional down arrow */
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
index e32f145..f21605d 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
@@ -95,15 +95,15 @@
     _showDropdown() {
       if (this.readOnly || this.editing) { return; }
       return this._open().then(() => {
-        this.$.input.$.input.focus();
+        this._nativeInput.focus();
         if (!this.$.input.value) { return; }
-        this.$.input.$.input.setSelectionRange(0, this.$.input.value.length);
+        this._nativeInput.setSelectionRange(0, this.$.input.value.length);
       });
     },
 
     open() {
       return this._open().then(() => {
-        this.$.input.$.input.focus();
+        this._nativeInput.focus();
       });
     },
 
@@ -155,6 +155,12 @@
       this._inputText = this.value;
     },
 
+    get _nativeInput() {
+      // In Polymer 2, the namespace of nativeInput
+      // changed from input to nativeInput
+      return this.$.input.$.nativeInput || this.$.input.$.input;
+    },
+
     /**
      * @suppress {checkTypes}
      * Closure doesn't think 'e' is an Event.
@@ -163,7 +169,7 @@
     _handleEnter(e) {
       e = this.getKeyboardEvent(e);
       const target = Polymer.dom(e).rootTarget;
-      if (target === this.$.input.$.input) {
+      if (target === this._nativeInput) {
         e.preventDefault();
         this._save();
       }
@@ -177,7 +183,7 @@
     _handleEsc(e) {
       e = this.getKeyboardEvent(e);
       const target = Polymer.dom(e).rootTarget;
-      if (target === this.$.input.$.input) {
+      if (target === this._nativeInput) {
         e.preventDefault();
         this._cancel();
       }
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
index f494325..984b870 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
@@ -50,18 +50,6 @@
     return getRestApi().getAccountCapabilities(capabilities);
   };
 
-  GrPluginRestApi.prototype.fetchJSON = function(req) {
-    // TODO(dhruvsri): find better implementation for fetchJSON
-    const api = getRestApi();
-    let fetchJSON;
-    if (api._fetchJSON) {
-      fetchJSON = api._fetchJSON.bind(api);
-    } else {
-      fetchJSON = api._restApiHelper.fetchJSON.bind(api._restApiHelper);
-    }
-    return fetchJSON(req);
-  };
-
   GrPluginRestApi.prototype.getRepos =
     function(filter, reposPerPage, opt_offset) {
       return getRestApi().getRepos(filter, reposPerPage, opt_offset);
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
index 6146e0f..0690950 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -76,14 +76,6 @@
       this._storage.removeItem(this._getEditableContentKey(key));
     },
 
-    getPreferences() {
-      return this._getObject('localPrefs');
-    },
-
-    savePreferences(localPrefs) {
-      this._setObject('localPrefs', localPrefs || null);
-    },
-
     _getDraftKey(location) {
       const range = location.range ?
           `${location.range.start_line}-${location.range.start_character}` +
diff --git a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js
index 238cf15..6e503c7 100644
--- a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js
+++ b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js
@@ -1,3 +1,19 @@
+/**
+ * @license
+ * Copyright (C) 2019 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.
+ */
 (function(window) {
   'use strict';
 
diff --git a/polygerrit-ui/app/styles/themes/app-theme.html b/polygerrit-ui/app/styles/themes/app-theme.html
index 142dd82..a0cb3d8 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.html
+++ b/polygerrit-ui/app/styles/themes/app-theme.html
@@ -18,13 +18,19 @@
 html {
   /* Following vars have LTS for plugin API. */
   --primary-text-color: #000;
+  /* For backwords compatibility we keep this as --header-background-color. */
   --header-background-color: #eee;
   --header-title-content: 'Gerrit';
   --header-icon: none;
   --header-icon-size: 0em;
   --header-text-color: #000;
-  --footer-background-color: var(--header-background-color);
+  --header-title-font-size: 1.75rem;
+  --footer-background-color: #eee;
   --border-color: #ddd;
+  /* This allows to add a border in custom themes */
+  --header-border-bottom: 1px solid var(--border-color);
+  --header-border-image: '';
+  --footer-border-top: 1px solid var(--border-color);
 
   /* Following are not part of plugin API. */
   --selection-background-color: rgba(161, 194, 250, 0.1);
@@ -32,6 +38,7 @@
   --expanded-background-color: #eee;
   --view-background-color: #fff;
   --default-horizontal-margin: 1rem;
+
   --deemphasized-text-color: #757575;
   /* Used on text color for change list that doesn't need user's attention. */
   --reviewed-text-color: var(--primary-text-color);
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index 8cf1b13..2324fd5 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -20,6 +20,9 @@
       --primary-text-color: #e8eaed;
       --view-background-color: #131416;
       --border-color: #5f6368;
+      --header-border-bottom: 1px solid var(--border-color);
+      --header-border-image: '';
+      --footer-border-bottom: 1px solid var(--border-color);
       --table-header-background-color: #131416;
       --table-subheader-background-color: #3c4043;
       --header-background-color: #3c4043;
@@ -41,6 +44,7 @@
       --dropdown-background-color: var(--table-header-background-color);
       --dialog-background-color: var(--view-background-color);
       --chip-background-color: var(--table-header-background-color);
+      --header-title-font-size: 1.75rem;
 
       --select-background-color: var(--table-subheader-background-color);
 
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 4754cd8..6cdac42 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -183,7 +183,8 @@
     'shared/gr-editable-content/gr-editable-content_test.html',
     'shared/gr-editable-label/gr-editable-label_test.html',
     'shared/gr-formatted-text/gr-formatted-text_test.html',
-    'shared/gr-hovercard/gr-hovercard_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'shared/gr-hovercard/gr-hovercard_test.html',
     'shared/gr-js-api-interface/gr-annotation-actions-context_test.html',
     'shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html',
     'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',