Merge "Fix select in comment for firefox for polymer 2"
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 7975575..f7bdf47 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -2873,35 +2873,72 @@
 
 The process of writing a new plugin goes through different phases:
 
-- Ideation and discussion
-  The idea of creating a new plugin is posted and discussed on the
-  link:https://groups.google.com/d/forum/repo-discuss[repo-discuss] mailing list.
-- Prototyping (optional)
-  The author of the plugin creates a working prototype on a public repository
-  accessible to the community.
-- Proposal and Hosting
-  The author proposes to release the plugin under the
-  link:https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 OpenSource license]
-  and request to be hosted on
-  link:https://gerrit-review.googlesource.com[the Gerrit project site]. The proposal must be
-  accepted by at least one Gerrit maintainer. In case of disagreement between maintainers, the
-  issue can be escalated to the Engineering Steering Committee. If the plugin is accepted,
-  the Gerrit maintainer creates the project under the plugins path on
-  link:https://gerrit-review.googlesource.com[the Gerrit project site].
-- Development and contribution
-  The author develops a production-ready code base of the plugin, with contributions, reviews,
-  and help from the Gerrit community.
-- Release
-  The author releases the plugin by tagging and announcing on the
-  link:https://groups.google.com/d/forum/repo-discuss[repo-discuss] mailing list.
-- Maintenance
-  The author maintains their plugins as new Gerrit versions are released, updates them when necessary,
-  develops further existing or new features and reviews incoming new contributions.
-  A plugin should declare and build on
-  link:https://gerrit-ci.gerritforge.com[the GerritForge CI] for the Gerrit versions it supports.
-- Deprecation
-  The author declares that the plugin is not maintained anymore or is deprecated and should
-  not be used anymore.
+- Ideation and discussion:
++
+The idea of creating a new plugin is posted and discussed on the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss] mailing list.
++
+Also see section link#ideation_discussion[Ideation and discussion] below.
+
+- Prototyping (optional):
++
+The author of the plugin creates a working prototype on a public repository
+accessible to the community.
++
+Also see section link#plugin_prototyping[Plugin Prototyping] below.
+
+- Proposal and Hosting:
++
+The author proposes to release the plugin under the
+link:https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 OpenSource
+license] and requests the plugin to be hosted on
+link:https://gerrit-review.googlesource.com[the Gerrit project site]. The
+proposal must be   accepted by at least one Gerrit maintainer. In case of
+disagreement between maintainers, the issue can be escalated to the
+link:dev-processes.html#steering-committee[Engineering Steering Committee]. If
+the plugin is accepted, the Gerrit maintainer creates the project under the
+plugins path on link:https://gerrit-review.googlesource.com[the Gerrit project
+site].
++
+Also see section link#plugin_proposal[Plugin Proposal] below.
+
+- Build:
++
+To make the consumption of the plugin easy and to notice plugin breakages early
+the plugin author should setup build jobs on
+link:https://gerrit-ci.gerritforge.com[the GerritForge CI] that build the
+plugin for each Gerrit version that it supports.
++
+Also see section link#build[Build] below.
+
+- Development and contribution:
++
+The author develops a production-ready code base of the plugin, with
+contributions, reviews, and help from the Gerrit community.
++
+Also see section link#development_contribution[Development and contribution]
+below.
+
+- Release:
++
+The author releases the plugin by creating a Git tag and announcing the plugin
+on the link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+mailing list.
++
+Also see section link#plugin_release[Plugin release] below.
+
+- Maintenance:
++
+The author maintains their plugins as new Gerrit versions are released, updates
+them when necessary, develops further existing or new features and reviews
+incoming contributions.
+
+- Deprecation:
++
+The author declares that the plugin is not maintained anymore or is deprecated
+and should not be used anymore.
++
+Also see section link#plugin_deprecation[Plugin deprecation] below.
 
 [[ideation_discussion]]
 === Ideation and discussion
@@ -2916,6 +2953,7 @@
 
 Example of a post:
 
+----
   [RFC] Code-Formatter plugin
 
   Hello, community,
@@ -2933,6 +2971,7 @@
   to the patchset, highlighting the part of the code to be changed.
 
   What do you think? Did anyone have the same idea or need?
+----
 
 The idea is discussed on the mailing list and can evolve based on the needs and inputs from
 the entire community.
@@ -2955,7 +2994,7 @@
 so that other members of the community can see the code and try the plugin themselves.
 
 [[plugin_proposal]]
-==== Plugin Proposal
+=== Plugin Proposal
 
 The author decides that the plugin prototype makes sense as a general purpose plugin and decides
 to release the code with the same
@@ -2998,12 +3037,13 @@
   and would not make sense outside of it. The plugin remains in a public repository, widely
   accessible and OpenSource, but not hosted on link:https://gerrit-review.googlesource.com[the Gerrit project site].
 
-[[development_contribution]]
-== Development and contribution
+[[build]]
+=== Build
 
-The plugin's maintainer creates a job on the link:https://gerrit-ci.gerritforge.com[GerritForge CI]
-by creating a new YAML definition in the
-link:https://gerrit.googlesource.com/gerrit-ci-scripts[Gerrit CI Scripts] repository.
+The plugin's maintainer creates a job on the
+link:https://gerrit-ci.gerritforge.com[GerritForge CI] by creating a new YAML
+definition in the link:https://gerrit.googlesource.com/gerrit-ci-scripts[Gerrit
+CI Scripts] repository.
 
 Example of a YAML CI job for plugins:
 
@@ -3016,6 +3056,9 @@
             - master
 ----
 
+[[development_contribution]]
+=== Development and contribution
+
 The plugin follows the same lifecycle as Gerrit Code Review and needs to be kept up-to-date with
 the current active branches, according to the
 link:https://www.gerritcodereview.com/#support[current support policy].
@@ -3023,7 +3066,7 @@
 involved and making them maintainers of his plugin, adding them to the list of the project owners.
 
 [[plugin_release]]
-== Plugin release
+=== Plugin release
 
 The plugin's maintainer is the only person responsible for making and announcing the official
 releases, typically, but not limited to, in conjunction with the major releases of Gerrit Code Review.
diff --git a/WORKSPACE b/WORKSPACE
index 530a4a7..ae22829 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -784,28 +784,36 @@
     sha1 = "89bb3aa5b98b48e584eee2a7401b7682a46779b4",
 )
 
+SSHD_VERS = "2.3.0"
+
 maven_jar(
     name = "sshd",
-    artifact = "org.apache.sshd:sshd-core:2.0.0",
-    sha1 = "f4275079a2463cfd2bf1548a80e1683288a8e86b",
+    artifact = "org.apache.sshd:sshd-core:" + SSHD_VERS,
+    sha1 = "21aeea9deba96c9b81ea0935fa4fac61aa3cf646",
 )
 
 maven_jar(
-    name = "eddsa",
-    artifact = "net.i2p.crypto:eddsa:0.2.0",
-    sha1 = "0856a92559c4daf744cb27c93cd8b7eb1f8c4780",
-)
-
-maven_jar(
-    name = "mina-core",
-    artifact = "org.apache.mina:mina-core:2.0.17",
-    sha1 = "7e10ec974760436d931f3e58be507d1957bcc8db",
+    name = "sshd-common",
+    artifact = "org.apache.sshd:sshd-common:" + SSHD_VERS,
+    sha1 = "8b6e3baaa0d35b547696965eef3e62477f5e74c9",
 )
 
 maven_jar(
     name = "sshd-mina",
-    artifact = "org.apache.sshd:sshd-mina:2.0.0",
-    sha1 = "50f2669312494f6c1996d8bd0d266c1fca7be6f6",
+    artifact = "org.apache.sshd:sshd-mina:" + SSHD_VERS,
+    sha1 = "55dc0830dfcbceba01f9460812ee454978a15fe8",
+)
+
+maven_jar(
+    name = "eddsa",
+    artifact = "net.i2p.crypto:eddsa:0.3.0",
+    sha1 = "1901c8d4d8bffb7d79027686cfb91e704217c3e1",
+)
+
+maven_jar(
+    name = "mina-core",
+    artifact = "org.apache.mina:mina-core:2.0.21",
+    sha1 = "e1a317689ecd438f54e863747e832f741ef8e092",
 )
 
 maven_jar(
@@ -903,65 +911,59 @@
 )
 
 maven_jar(
-    name = "easymock",
-    artifact = "org.easymock:easymock:3.1",
-    sha1 = "3e127311a86fc2e8f550ef8ee4abe094bbcf7e7e",
-)
-
-maven_jar(
     name = "cglib-3_2",
     artifact = "cglib:cglib-nodep:3.2.6",
     sha1 = "92bf48723d277d6efd1150b2f7e9e1e92cb56caf",
 )
 
-JETTY_VERS = "9.4.18.v20190429"
+JETTY_VERS = "9.4.20.v20190813"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VERS,
-    sha1 = "290f7a88f351950d51ebc9fb4a794752c62d7de5",
+    sha1 = "d5d6610321bd173aead473e994f170989d633b25",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VERS,
-    sha1 = "01aceff3608ca1b223bfd275a497797cfe675ef4",
+    sha1 = "300c2e6dd62291c512bdaf3ecfefc1e305e26088",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VERS,
-    sha1 = "b76ef50e04635f11d4d43bc6ccb7c4482a8384f0",
+    sha1 = "d4ee11134bca83db85919a1710fce022c67df3b7",
 )
 
 maven_jar(
     name = "jetty-jmx",
     artifact = "org.eclipse.jetty:jetty-jmx:" + JETTY_VERS,
-    sha1 = "f4c2654db1a55f0780acdfcee8bb98550f56ca70",
+    sha1 = "81cfa612ae7dfed90784aba4f08a22729a15cb5b",
 )
 
 maven_jar(
     name = "jetty-continuation",
     artifact = "org.eclipse.jetty:jetty-continuation:" + JETTY_VERS,
-    sha1 = "3c421a3be5be5805e32b1a7f9c6046526524181d",
+    sha1 = "e024388db5ace49c7459e68b5c0e542ef55fd49b",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VERS,
-    sha1 = "c2e73db2db5c369326b717da71b6587b3da11e0e",
+    sha1 = "edda2fd904a881114aba79e2f881c6f4004e8148",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VERS,
-    sha1 = "844af5efe58ab23fd0166a796efef123f4cb06b0",
+    sha1 = "b246c5e350d0aa1b310c07ec362755c34a1cc8cb",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VERS,
-    sha1 = "13e6148bfda7ae511f69ae7e5e3ea898bc9b0e33",
+    sha1 = "8ee753d673a124ba2880361871ab13f6863b2d40",
 )
 
 maven_jar(
diff --git a/java/com/google/gerrit/acceptance/InProcessProtocol.java b/java/com/google/gerrit/acceptance/InProcessProtocol.java
index d0ed673..2b6cb00 100644
--- a/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.server.RequestCleanup;
 import com.google.gerrit.server.config.GerritRequestModule;
 import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
 import com.google.gerrit.server.git.ReceivePackInitializer;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.git.UploadPackInitializer;
@@ -247,12 +248,15 @@
       if (projectState == null) {
         throw new RuntimeException("can't load project state for " + req.project.get());
       }
-      UploadPack up = new UploadPack(repo);
+      Repository permissionAwareRepository = PermissionAwareRepositoryManager.wrap(repo, perm);
+      UploadPack up = new UploadPack(permissionAwareRepository);
       up.setPackConfig(transferConfig.getPackConfig());
       up.setTimeout(transferConfig.getTimeout());
       up.setAdvertiseRefsHook(new DefaultAdvertiseRefsHook(perm, RefFilterOptions.defaults()));
       List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks);
-      hooks.add(uploadValidatorsFactory.create(projectState.getProject(), repo, "localhost-test"));
+      hooks.add(
+          uploadValidatorsFactory.create(
+              projectState.getProject(), permissionAwareRepository, "localhost-test"));
       up.setPreUploadHook(PreUploadHookChain.newChain(hooks));
       uploadPackInitializers.runEach(initializer -> initializer.init(req.project, up));
       return up;
diff --git a/java/com/google/gerrit/acceptance/SshdModule.java b/java/com/google/gerrit/acceptance/SshdModule.java
index 185d6e2..873ba177 100644
--- a/java/com/google/gerrit/acceptance/SshdModule.java
+++ b/java/com/google/gerrit/acceptance/SshdModule.java
@@ -34,7 +34,7 @@
     if (keys == null) {
       keys = new SimpleGeneratorHostKeyProvider();
       keys.setAlgorithm("RSA");
-      keys.loadKeys();
+      keys.loadKeys(null);
     }
     return keys;
   }
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index d66e8ac..755bf7a 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.server.cache.CacheModule;
 import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.git.UploadPackInitializer;
 import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
@@ -310,27 +311,33 @@
     private final DynamicSet<PreUploadHook> preUploadHooks;
     private final DynamicSet<PostUploadHook> postUploadHooks;
     private final PluginSetContext<UploadPackInitializer> uploadPackInitializers;
+    private final PermissionBackend permissionBackend;
 
     @Inject
     UploadFactory(
         TransferConfig tc,
         DynamicSet<PreUploadHook> preUploadHooks,
         DynamicSet<PostUploadHook> postUploadHooks,
-        PluginSetContext<UploadPackInitializer> uploadPackInitializers) {
+        PluginSetContext<UploadPackInitializer> uploadPackInitializers,
+        PermissionBackend permissionBackend) {
       this.config = tc;
       this.preUploadHooks = preUploadHooks;
       this.postUploadHooks = postUploadHooks;
       this.uploadPackInitializers = uploadPackInitializers;
+      this.permissionBackend = permissionBackend;
     }
 
     @Override
     public UploadPack create(HttpServletRequest req, Repository repo) {
-      UploadPack up = new UploadPack(repo);
+      ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
+      UploadPack up =
+          new UploadPack(
+              PermissionAwareRepositoryManager.wrap(
+                  repo, permissionBackend.currentUser().project(state.getNameKey())));
       up.setPackConfig(config.getPackConfig());
       up.setTimeout(config.getTimeout());
       up.setPreUploadHook(PreUploadHookChain.newChain(Lists.newArrayList(preUploadHooks)));
       up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
-      ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
       uploadPackInitializers.runEach(initializer -> initializer.init(state.getNameKey(), up));
       return up;
     }
diff --git a/java/com/google/gerrit/index/IndexConfig.java b/java/com/google/gerrit/index/IndexConfig.java
index d8a1a89..807c78c 100644
--- a/java/com/google/gerrit/index/IndexConfig.java
+++ b/java/com/google/gerrit/index/IndexConfig.java
@@ -91,7 +91,6 @@
       checkLimit(cfg.maxLimit(), "maxLimit");
       checkLimit(cfg.maxPages(), "maxPages");
       checkLimit(cfg.maxTerms(), "maxTerms");
-      checkTypeLimit(cfg);
       return cfg;
     }
   }
@@ -100,12 +99,6 @@
     checkArgument(limit > 0, "%s must be positive: %s", name, limit);
   }
 
-  private static void checkTypeLimit(IndexConfig cfg) {
-    String limit = cfg.type();
-    boolean known = IndexType.getKnownTypes().asList().contains(limit);
-    checkArgument(known, "type must be known: %s", limit);
-  }
-
   /**
    * @return maximum limit supported by the underlying index, or limited for performance reasons.
    */
@@ -123,7 +116,7 @@
    */
   public abstract int maxTerms();
 
-  /** @return index type, limited to be either one of the known types. */
+  /** @return index type. */
   public abstract String type();
 
   /**
diff --git a/java/com/google/gerrit/server/ApprovalCopier.java b/java/com/google/gerrit/server/ApprovalCopier.java
deleted file mode 100644
index 85a6079..0000000
--- a/java/com/google/gerrit/server/ApprovalCopier.java
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright (C) 2014 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.
-
-package com.google.gerrit.server;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Table;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.extensions.client.ChangeKind;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.change.ChangeKindCache;
-import com.google.gerrit.server.change.LabelNormalizer;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.TreeMap;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-/**
- * Copies approvals between patch sets.
- *
- * <p>The result of a copy may either be stored, as when stamping approvals in the database at
- * submit time, or refreshed on demand, as when reading approvals from the NoteDb.
- */
-@Singleton
-public class ApprovalCopier {
-  private final ProjectCache projectCache;
-  private final ChangeKindCache changeKindCache;
-  private final LabelNormalizer labelNormalizer;
-  private final ChangeData.Factory changeDataFactory;
-  private final PatchSetUtil psUtil;
-
-  @Inject
-  ApprovalCopier(
-      ProjectCache projectCache,
-      ChangeKindCache changeKindCache,
-      LabelNormalizer labelNormalizer,
-      ChangeData.Factory changeDataFactory,
-      PatchSetUtil psUtil) {
-    this.projectCache = projectCache;
-    this.changeKindCache = changeKindCache;
-    this.labelNormalizer = labelNormalizer;
-    this.changeDataFactory = changeDataFactory;
-    this.psUtil = psUtil;
-  }
-
-  Iterable<PatchSetApproval> getForPatchSet(
-      ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
-
-    PatchSet ps = psUtil.get(notes, psId);
-    if (ps == null) {
-      return Collections.emptyList();
-    }
-
-    ChangeData cd = changeDataFactory.create(notes);
-    try {
-      ProjectState project = projectCache.checkedGet(cd.change().getDest().project());
-      ListMultimap<PatchSet.Id, PatchSetApproval> all = cd.approvals();
-      requireNonNull(all, "all should not be null");
-
-      Table<String, Account.Id, PatchSetApproval> byUser = HashBasedTable.create();
-      for (PatchSetApproval psa : all.get(ps.id())) {
-        byUser.put(psa.label(), psa.accountId(), psa);
-      }
-
-      TreeMap<Integer, PatchSet> patchSets = getPatchSets(cd);
-
-      Table<String, Account.Id, PatchSetApproval> wontCopy = HashBasedTable.create();
-
-      // Walk patch sets strictly less than current in descending order.
-      Collection<PatchSet> allPrior =
-          patchSets.descendingMap().tailMap(ps.id().get(), false).values();
-      for (PatchSet priorPs : allPrior) {
-        List<PatchSetApproval> priorApprovals = all.get(priorPs.id());
-        if (priorApprovals.isEmpty()) {
-          continue;
-        }
-
-        ChangeKind kind =
-            changeKindCache.getChangeKind(
-                project.getNameKey(), rw, repoConfig, priorPs.commitId(), ps.commitId());
-
-        for (PatchSetApproval psa : priorApprovals) {
-          if (wontCopy.contains(psa.label(), psa.accountId())) {
-            continue;
-          }
-          if (byUser.contains(psa.label(), psa.accountId())) {
-            continue;
-          }
-          if (!canCopy(project, psa, ps.id(), kind)) {
-            wontCopy.put(psa.label(), psa.accountId(), psa);
-            continue;
-          }
-          byUser.put(psa.label(), psa.accountId(), psa.copyWithPatchSet(ps.id()));
-        }
-      }
-      return labelNormalizer.normalize(notes, byUser.values()).getNormalized();
-    } catch (IOException e) {
-      throw new StorageException(e);
-    }
-  }
-
-  private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd) {
-    Collection<PatchSet> patchSets = cd.patchSets();
-    TreeMap<Integer, PatchSet> result = new TreeMap<>();
-    for (PatchSet ps : patchSets) {
-      result.put(ps.id().get(), ps);
-    }
-    return result;
-  }
-
-  private static boolean canCopy(
-      ProjectState project, PatchSetApproval psa, PatchSet.Id psId, ChangeKind kind) {
-    int n = psa.key().patchSetId().get();
-    checkArgument(n != psId.get());
-    LabelType type = project.getLabelTypes().byLabel(psa.labelId());
-    if (type == null) {
-      return false;
-    } else if ((type.isCopyMinScore() && type.isMaxNegative(psa))
-        || (type.isCopyMaxScore() && type.isMaxPositive(psa))) {
-      return true;
-    }
-    switch (kind) {
-      case MERGE_FIRST_PARENT_UPDATE:
-        return type.isCopyAllScoresOnMergeFirstParentUpdate();
-      case NO_CODE_CHANGE:
-        return type.isCopyAllScoresIfNoCodeChange();
-      case TRIVIAL_REBASE:
-        return type.isCopyAllScoresOnTrivialRebase();
-      case NO_CHANGE:
-        return type.isCopyAllScoresIfNoChange()
-            || type.isCopyAllScoresOnTrivialRebase()
-            || type.isCopyAllScoresOnMergeFirstParentUpdate()
-            || type.isCopyAllScoresIfNoCodeChange();
-      case REWORK:
-      default:
-        return false;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/ApprovalInference.java b/java/com/google/gerrit/server/ApprovalInference.java
new file mode 100644
index 0000000..4cdb7d9
--- /dev/null
+++ b/java/com/google/gerrit/server/ApprovalInference.java
@@ -0,0 +1,181 @@
+// Copyright (C) 2014 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.
+
+package com.google.gerrit.server;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Table;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.extensions.client.ChangeKind;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.server.change.ChangeKindCache;
+import com.google.gerrit.server.change.LabelNormalizer;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/**
+ * Computes approvals for a given patch set by looking at approvals applied to the given patch set
+ * and by additionally inferring approvals from the patch set's parents. The latter is done by
+ * asserting a change's kind and checking the project config for allowed forward-inference.
+ *
+ * <p>The result of a copy may either be stored, as when stamping approvals in the database at
+ * submit time, or refreshed on demand, as when reading approvals from the NoteDb.
+ */
+@Singleton
+public class ApprovalInference {
+  private final ProjectCache projectCache;
+  private final ChangeKindCache changeKindCache;
+  private final LabelNormalizer labelNormalizer;
+  private final ChangeData.Factory changeDataFactory;
+  private final PatchSetUtil psUtil;
+
+  @Inject
+  ApprovalInference(
+      ProjectCache projectCache,
+      ChangeKindCache changeKindCache,
+      LabelNormalizer labelNormalizer,
+      ChangeData.Factory changeDataFactory,
+      PatchSetUtil psUtil) {
+    this.projectCache = projectCache;
+    this.changeKindCache = changeKindCache;
+    this.labelNormalizer = labelNormalizer;
+    this.changeDataFactory = changeDataFactory;
+    this.psUtil = psUtil;
+  }
+
+  /**
+   * Returns all approvals that apply to the given patch set. Honors direct and indirect (approval
+   * on parents) approvals.
+   */
+  Iterable<PatchSetApproval> forPatchSet(
+      ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
+    Collection<PatchSetApproval> approvals =
+        getForPatchSetWithoutNormalization(notes, psId, rw, repoConfig);
+    try {
+      return labelNormalizer.normalize(notes, approvals).getNormalized();
+    } catch (IOException e) {
+      throw new StorageException(e);
+    }
+  }
+
+  private static boolean canCopy(
+      ProjectState project, PatchSetApproval psa, PatchSet.Id psId, ChangeKind kind) {
+    int n = psa.key().patchSetId().get();
+    checkArgument(n != psId.get());
+    LabelType type = project.getLabelTypes().byLabel(psa.labelId());
+    if (type == null) {
+      return false;
+    } else if ((type.isCopyMinScore() && type.isMaxNegative(psa))
+        || (type.isCopyMaxScore() && type.isMaxPositive(psa))) {
+      return true;
+    }
+    switch (kind) {
+      case MERGE_FIRST_PARENT_UPDATE:
+        return type.isCopyAllScoresOnMergeFirstParentUpdate();
+      case NO_CODE_CHANGE:
+        return type.isCopyAllScoresIfNoCodeChange();
+      case TRIVIAL_REBASE:
+        return type.isCopyAllScoresOnTrivialRebase();
+      case NO_CHANGE:
+        return type.isCopyAllScoresIfNoChange()
+            || type.isCopyAllScoresOnTrivialRebase()
+            || type.isCopyAllScoresOnMergeFirstParentUpdate()
+            || type.isCopyAllScoresIfNoCodeChange();
+      case REWORK:
+      default:
+        return false;
+    }
+  }
+
+  private Collection<PatchSetApproval> getForPatchSetWithoutNormalization(
+      ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
+    PatchSet ps = psUtil.get(notes, psId);
+    if (ps == null) {
+      return Collections.emptyList();
+    }
+
+    ChangeData cd = changeDataFactory.create(notes);
+    ProjectState project;
+    try {
+      project = projectCache.checkedGet(cd.change().getDest().project());
+    } catch (IOException e) {
+      throw new StorageException(e);
+    }
+
+    // Start by collecting all current approvals
+    Table<String, Account.Id, PatchSetApproval> byUser = HashBasedTable.create();
+    ListMultimap<PatchSet.Id, PatchSetApproval> all = cd.approvals();
+    requireNonNull(all, "all should not be null");
+    all.get(ps.id()).forEach(psa -> byUser.put(psa.label(), psa.accountId(), psa));
+
+    // Bail out immediately if this is the first patch set
+    if (psId.get() == 1) {
+      return byUser.values();
+    }
+
+    // Call this algorithm recursively to check if the prior patch set had approvals. This has the
+    // advantage that all caches - most importantly ChangeKindCache - have values cached for what we
+    // need for this computation.
+    // The way this algorithm is written is that any approval will be copied forward by one patch
+    // set at a time if configs and change kind allow so. Once an approval is held back - for
+    // example because the patch set is a REWORK - it will not be picked up again in a future
+    // patch set.
+    PatchSet priorPatchSet = notes.load().getPatchSets().lowerEntry(psId).getValue();
+    if (priorPatchSet == null) {
+      return byUser.values();
+    }
+
+    Iterable<PatchSetApproval> priorApprovals =
+        getForPatchSetWithoutNormalization(notes, priorPatchSet.id(), rw, repoConfig);
+    if (!priorApprovals.iterator().hasNext()) {
+      return byUser.values();
+    }
+
+    Table<String, Account.Id, PatchSetApproval> wontCopy = HashBasedTable.create();
+    ChangeKind kind =
+        changeKindCache.getChangeKind(
+            project.getNameKey(), rw, repoConfig, priorPatchSet.commitId(), ps.commitId());
+    for (PatchSetApproval psa : priorApprovals) {
+      if (wontCopy.contains(psa.label(), psa.accountId())) {
+        continue;
+      }
+      if (byUser.contains(psa.label(), psa.accountId())) {
+        continue;
+      }
+      if (!canCopy(project, psa, ps.id(), kind)) {
+        wontCopy.put(psa.label(), psa.accountId(), psa);
+        continue;
+      }
+      byUser.put(psa.label(), psa.accountId(), psa.copyWithPatchSet(ps.id()));
+    }
+    return byUser.values();
+  }
+}
diff --git a/java/com/google/gerrit/server/ApprovalsUtil.java b/java/com/google/gerrit/server/ApprovalsUtil.java
index c4f13f1..1374e74 100644
--- a/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -91,14 +91,14 @@
     return Iterables.filter(psas, a -> Objects.equals(a.accountId(), accountId));
   }
 
-  private final ApprovalCopier copier;
+  private final ApprovalInference copier;
   private final PermissionBackend permissionBackend;
   private final ProjectCache projectCache;
 
   @VisibleForTesting
   @Inject
   public ApprovalsUtil(
-      ApprovalCopier copier, PermissionBackend permissionBackend, ProjectCache projectCache) {
+      ApprovalInference copier, PermissionBackend permissionBackend, ProjectCache projectCache) {
     this.copier = copier;
     this.permissionBackend = permissionBackend;
     this.projectCache = projectCache;
@@ -340,7 +340,7 @@
 
   public Iterable<PatchSetApproval> byPatchSet(
       ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
-    return copier.getForPatchSet(notes, psId, rw, repoConfig);
+    return copier.forPatchSet(notes, psId, rw, repoConfig);
   }
 
   public Iterable<PatchSetApproval> byPatchSetUser(
diff --git a/java/com/google/gerrit/server/git/DelegateRefDatabase.java b/java/com/google/gerrit/server/git/DelegateRefDatabase.java
new file mode 100644
index 0000000..34dd6a9
--- /dev/null
+++ b/java/com/google/gerrit/server/git/DelegateRefDatabase.java
@@ -0,0 +1,86 @@
+// 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.
+
+package com.google.gerrit.server.git;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wrapper around {@link RefDatabase} that delegates all calls to the wrapped {@link RefDatabase}.
+ */
+public class DelegateRefDatabase extends RefDatabase {
+
+  private Repository delegate;
+
+  DelegateRefDatabase(Repository delegate) {
+    this.delegate = delegate;
+  }
+
+  @Override
+  public void create() throws IOException {
+    delegate.getRefDatabase().create();
+  }
+
+  @Override
+  public void close() {
+    delegate.close();
+  }
+
+  @Override
+  public boolean isNameConflicting(String name) throws IOException {
+    return delegate.getRefDatabase().isNameConflicting(name);
+  }
+
+  @Override
+  public RefUpdate newUpdate(String name, boolean detach) throws IOException {
+    return delegate.getRefDatabase().newUpdate(name, detach);
+  }
+
+  @Override
+  public RefRename newRename(String fromName, String toName) throws IOException {
+    return delegate.getRefDatabase().newRename(fromName, toName);
+  }
+
+  @Override
+  public Ref exactRef(String name) throws IOException {
+    return delegate.getRefDatabase().exactRef(name);
+  }
+
+  @SuppressWarnings("deprecation")
+  @Override
+  public Map<String, Ref> getRefs(String prefix) throws IOException {
+    return delegate.getRefDatabase().getRefs(prefix);
+  }
+
+  @Override
+  public List<Ref> getAdditionalRefs() throws IOException {
+    return delegate.getRefDatabase().getAdditionalRefs();
+  }
+
+  @Override
+  public Ref peel(Ref ref) throws IOException {
+    return delegate.getRefDatabase().peel(ref);
+  }
+
+  Repository getDelegate() {
+    return delegate;
+  }
+}
diff --git a/java/com/google/gerrit/server/git/DelegateRepository.java b/java/com/google/gerrit/server/git/DelegateRepository.java
new file mode 100644
index 0000000..800490d
--- /dev/null
+++ b/java/com/google/gerrit/server/git/DelegateRepository.java
@@ -0,0 +1,89 @@
+// 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.
+
+package com.google.gerrit.server.git;
+
+import java.io.IOException;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.lib.BaseRepositoryBuilder;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.ReflogReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+
+/** Wrapper around {@link Repository} that delegates all calls to the wrapped {@link Repository}. */
+class DelegateRepository extends Repository {
+
+  private final Repository delegate;
+
+  DelegateRepository(Repository delegate) {
+    super(toBuilder(delegate));
+    this.delegate = delegate;
+  }
+
+  @Override
+  public void create(boolean bare) throws IOException {
+    delegate.create(bare);
+  }
+
+  @Override
+  public String getIdentifier() {
+    return delegate.getIdentifier();
+  }
+
+  @Override
+  public ObjectDatabase getObjectDatabase() {
+    return delegate.getObjectDatabase();
+  }
+
+  @Override
+  public RefDatabase getRefDatabase() {
+    return delegate.getRefDatabase();
+  }
+
+  @Override
+  public StoredConfig getConfig() {
+    return delegate.getConfig();
+  }
+
+  @Override
+  public AttributesNodeProvider createAttributesNodeProvider() {
+    return delegate.createAttributesNodeProvider();
+  }
+
+  @Override
+  public void scanForRepoChanges() throws IOException {
+    delegate.scanForRepoChanges();
+  }
+
+  @Override
+  public void notifyIndexChanged(boolean internal) {
+    delegate.notifyIndexChanged(internal);
+  }
+
+  @Override
+  public ReflogReader getReflogReader(String refName) throws IOException {
+    return delegate.getReflogReader(refName);
+  }
+
+  @SuppressWarnings("rawtypes")
+  private static BaseRepositoryBuilder toBuilder(Repository repo) {
+    if (!repo.isBare()) {
+      throw new IllegalArgumentException("non-bare repository is not supported");
+    }
+
+    return new BaseRepositoryBuilder<>().setFS(repo.getFS()).setGitDir(repo.getDirectory());
+  }
+}
diff --git a/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java b/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java
new file mode 100644
index 0000000..8f7e684
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java
@@ -0,0 +1,159 @@
+// 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.
+
+package com.google.gerrit.server.git;
+
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wrapper around {@link DelegateRefDatabase} that filters all refs using {@link
+ * com.google.gerrit.server.permissions.PermissionBackend}.
+ */
+public class PermissionAwareReadOnlyRefDatabase extends DelegateRefDatabase {
+
+  private final PermissionBackend.ForProject forProject;
+
+  @Inject
+  PermissionAwareReadOnlyRefDatabase(
+      Repository delegateRepository, PermissionBackend.ForProject forProject) {
+    super(delegateRepository);
+    this.forProject = forProject;
+  }
+
+  @Override
+  public boolean isNameConflicting(String name) {
+    throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only");
+  }
+
+  @Override
+  public RefUpdate newUpdate(String name, boolean detach) {
+    throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only");
+  }
+
+  @Override
+  public RefRename newRename(String fromName, String toName) {
+    throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only");
+  }
+
+  @Override
+  public Ref exactRef(String name) throws IOException {
+    Ref ref = getDelegate().getRefDatabase().exactRef(name);
+    if (ref == null) {
+      return null;
+    }
+
+    Map<String, Ref> result;
+    try {
+      result =
+          forProject.filter(ImmutableMap.of(name, ref), getDelegate(), RefFilterOptions.defaults());
+    } catch (PermissionBackendException e) {
+      if (e.getCause() instanceof IOException) {
+        throw (IOException) e.getCause();
+      }
+      throw new IOException(e);
+    }
+    if (result.isEmpty()) {
+      return null;
+    }
+
+    Preconditions.checkState(
+        result.size() == 1, "Only one element expected, but was: " + result.size());
+    return Iterables.getOnlyElement(result.values());
+  }
+
+  @SuppressWarnings("deprecation")
+  @Override
+  public Map<String, Ref> getRefs(String prefix) throws IOException {
+    Map<String, Ref> refs = getDelegate().getRefDatabase().getRefs(prefix);
+    if (refs.isEmpty()) {
+      return refs;
+    }
+
+    Map<String, Ref> result;
+    try {
+      result = forProject.filter(refs, getDelegate(), RefFilterOptions.defaults());
+    } catch (PermissionBackendException e) {
+      throw new IOException("");
+    }
+    return result;
+  }
+
+  @Override
+  public List<Ref> getRefsByPrefix(String prefix) throws IOException {
+    Map<String, Ref> coarseRefs;
+    int lastSlash = prefix.lastIndexOf('/');
+    if (lastSlash == -1) {
+      coarseRefs = getRefs(ALL);
+    } else {
+      coarseRefs = getRefs(prefix.substring(0, lastSlash + 1));
+    }
+
+    List<Ref> result;
+    if (lastSlash + 1 == prefix.length()) {
+      result = coarseRefs.values().stream().collect(toList());
+    } else {
+      String p = prefix.substring(lastSlash + 1);
+      result =
+          coarseRefs.entrySet().stream()
+              .filter(e -> e.getKey().startsWith(p))
+              .map(e -> e.getValue())
+              .collect(toList());
+    }
+    return Collections.unmodifiableList(result);
+  }
+
+  @Override
+  @NonNull
+  public Map<String, Ref> exactRef(String... refs) throws IOException {
+    Map<String, Ref> result = new HashMap<>(refs.length);
+    for (String name : refs) {
+      Ref ref = exactRef(name);
+      if (ref != null) {
+        result.put(name, ref);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  @Nullable
+  public Ref firstExactRef(String... refs) throws IOException {
+    for (String name : refs) {
+      Ref ref = exactRef(name);
+      if (ref != null) {
+        return ref;
+      }
+    }
+    return null;
+  }
+}
diff --git a/java/com/google/gerrit/server/git/PermissionAwareRepository.java b/java/com/google/gerrit/server/git/PermissionAwareRepository.java
new file mode 100644
index 0000000..bb80cb5
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PermissionAwareRepository.java
@@ -0,0 +1,39 @@
+// 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.
+
+package com.google.gerrit.server.git;
+
+import com.google.gerrit.server.permissions.PermissionBackend;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wrapper around {@link DelegateRepository} that overwrites {@link #getRefDatabase()} to return a
+ * {@link PermissionAwareReadOnlyRefDatabase}.
+ */
+public class PermissionAwareRepository extends DelegateRepository {
+
+  private final PermissionAwareReadOnlyRefDatabase permissionAwareReadOnlyRefDatabase;
+
+  public PermissionAwareRepository(Repository delegate, PermissionBackend.ForProject forProject) {
+    super(delegate);
+    this.permissionAwareReadOnlyRefDatabase =
+        new PermissionAwareReadOnlyRefDatabase(delegate, forProject);
+  }
+
+  @Override
+  public RefDatabase getRefDatabase() {
+    return permissionAwareReadOnlyRefDatabase;
+  }
+}
diff --git a/java/com/google/gerrit/server/git/PermissionAwareRepositoryManager.java b/java/com/google/gerrit/server/git/PermissionAwareRepositoryManager.java
new file mode 100644
index 0000000..b11aa49
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PermissionAwareRepositoryManager.java
@@ -0,0 +1,32 @@
+// 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.
+
+package com.google.gerrit.server.git;
+
+import com.google.common.base.Preconditions;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wraps and unwraps existing repositories and makes them permission-aware by returning a {@link
+ * PermissionAwareReadOnlyRefDatabase}.
+ */
+public class PermissionAwareRepositoryManager {
+  public static Repository wrap(Repository delegate, PermissionBackend.ForProject forProject) {
+    Preconditions.checkState(
+        !(delegate instanceof PermissionAwareRepository),
+        "Cannot wrap PermissionAwareRepository instance");
+    return new PermissionAwareRepository(delegate, forProject);
+  }
+}
diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
index b8a2aed..a644b52 100644
--- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.ReceiveCommitsExecutor;
 import com.google.gerrit.server.git.MultiProgressMonitor;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
 import com.google.gerrit.server.git.ProjectRunnable;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.logging.Metadata;
@@ -281,9 +282,11 @@
     this.user = user;
     this.repo = repo;
     this.metrics = metrics;
-
+    // If the user lacks READ permission, some references may be filtered and hidden from view.
+    // Check objects mentioned inside the incoming pack file are reachable from visible refs.
     Project.NameKey projectName = projectState.getNameKey();
-    receivePack = new ReceivePack(repo);
+    this.perm = permissionBackend.user(user).project(projectName);
+    receivePack = new ReceivePack(PermissionAwareRepositoryManager.wrap(repo, perm));
     receivePack.setAllowCreates(true);
     receivePack.setAllowDeletes(true);
     receivePack.setAllowNonFastForwards(true);
@@ -296,9 +299,6 @@
     receivePack.setPreReceiveHook(this);
     receivePack.setPostReceiveHook(lazyPostReceive.create(user, projectName));
 
-    // If the user lacks READ permission, some references may be filtered and hidden from view.
-    // Check objects mentioned inside the incoming pack file are reachable from visible refs.
-    this.perm = permissionBackend.user(user).project(projectName);
     try {
       projectState.checkStatePermitsRead();
       this.perm.check(ProjectPermission.READ);
@@ -314,7 +314,7 @@
     resultChangeIds = new ResultChangeIds();
     receiveCommits =
         factory.create(
-            projectState, user, receivePack, allRefsWatcher, messageSender, resultChangeIds);
+            projectState, user, receivePack, repo, allRefsWatcher, messageSender, resultChangeIds);
     receiveCommits.init();
     QuotaResponse.Aggregated availableTokens =
         quotaBackend.user(user).project(projectName).availableTokens(REPOSITORY_SIZE_GROUP);
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 6859999..c05ef0c 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -248,6 +248,7 @@
         ProjectState projectState,
         IdentifiedUser user,
         ReceivePack receivePack,
+        Repository repository,
         AllRefsWatcher allRefsWatcher,
         MessageSender messageSender,
         ResultChangeIds resultChangeIds);
@@ -422,6 +423,7 @@
       @Assisted ProjectState projectState,
       @Assisted IdentifiedUser user,
       @Assisted ReceivePack rp,
+      @Assisted Repository repository,
       @Assisted AllRefsWatcher allRefsWatcher,
       @Nullable @Assisted MessageSender messageSender,
       @Assisted ResultChangeIds resultChangeIds)
@@ -471,13 +473,15 @@
     this.projectState = projectState;
     this.user = user;
     this.receivePack = rp;
+    // This repository instance in unwrapped, while the repository instance in
+    // receivePack.getRepo() is wrapped in PermissionAwareRepository instance.
+    this.repo = repository;
 
     // Immutable fields derived from constructor arguments.
-    repo = rp.getRepository();
     project = projectState.getProject();
     labelTypes = projectState.getLabelTypes();
     permissions = permissionBackend.user(user).project(project.getNameKey());
-    rejectCommits = BanCommit.loadRejectCommitsMap(rp.getRepository(), rp.getRevWalk());
+    rejectCommits = BanCommit.loadRejectCommitsMap(repo, rp.getRevWalk());
 
     // Collections populated during processing.
     errors = MultimapBuilder.linkedHashKeys().arrayListValues().build();
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 3e32628..95083d9 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -19,6 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
@@ -319,7 +320,7 @@
   }
 
   /** Lookup a human readable name for an account, usually the "full name". */
-  protected String getNameFor(Account.Id accountId) {
+  protected String getNameFor(@Nullable Account.Id accountId) {
     if (accountId == null) {
       return args.gerritPersonIdent.getName();
     }
@@ -345,7 +346,14 @@
    * @param accountId user to fetch.
    * @return name/email of account, or Anonymous Coward if unset.
    */
-  protected String getNameEmailFor(Account.Id accountId) {
+  protected String getNameEmailFor(@Nullable Account.Id accountId) {
+    if (accountId == null) {
+      return args.gerritPersonIdent.getName()
+          + " <"
+          + args.gerritPersonIdent.getEmailAddress()
+          + ">";
+    }
+
     Optional<Account> account = args.accountCache.get(accountId).map(AccountState::account);
     if (account.isPresent()) {
       String name = account.get().fullName();
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index b8054cd..65237ac 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -399,6 +399,10 @@
   private Map<String, Ref> addUsersSelfSymref(Repository repo, Map<String, Ref> refs)
       throws PermissionBackendException {
     if (user.isIdentifiedUser()) {
+      // User self symref is already there
+      if (refs.containsKey(REFS_USERS_SELF)) {
+        return refs;
+      }
       String refName = RefNames.refsUsers(user.getAccountId());
       try {
         Ref r = repo.exactRef(refName);
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index bf920e1..11ca6bf 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -134,6 +134,24 @@
     this.notifyResolver = notifyResolver;
   }
 
+  /**
+   * This function is used for cherry picking a change.
+   *
+   * @param batchUpdateFactory Used for applying changes to the database.
+   * @param change Change to cherry pick.
+   * @param patch The patch of that change.
+   * @param input Input object for different configurations of cherry pick.
+   * @param dest Destination branch for the cherry pick.
+   * @return Result object that describes the cherry pick.
+   * @throws IOException Unable to open repository or read from the database.
+   * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
+   *     key exist in the branch.
+   * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
+   * @throws UpdateException Problem updating the database using batchUpdateFactory.
+   * @throws RestApiException Error such as invalid SHA1
+   * @throws ConfigInvalidException Can't find account to notify.
+   * @throws NoSuchProjectException Can't find project state.
+   */
   public Result cherryPick(
       BatchUpdate.Factory batchUpdateFactory,
       Change change,
@@ -146,6 +164,27 @@
         batchUpdateFactory, change, change.getProject(), patch.commitId(), input, dest);
   }
 
+  /**
+   * This function is called directly to cherry pick a commit. Also, it is used to cherry pick a
+   * change as well as long as sourceChange is not null.
+   *
+   * @param batchUpdateFactory Used for applying changes to the database.
+   * @param sourceChange Change to cherry pick. Can be null, and then the function will only cherry
+   *     pick a commit.
+   * @param project Project name
+   * @param sourceCommit Id of the commit to be cherry picked.
+   * @param input Input object for different configurations of cherry pick.
+   * @param dest Destination branch for the cherry pick.
+   * @return Result object that describes the cherry pick.
+   * @throws IOException Unable to open repository or read from the database.
+   * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
+   *     key exist in the branch.
+   * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
+   * @throws UpdateException Problem updating the database using batchUpdateFactory.
+   * @throws RestApiException Error such as invalid SHA1
+   * @throws ConfigInvalidException Can't find account to notify.
+   * @throws NoSuchProjectException Can't find project state.
+   */
   public Result cherryPick(
       BatchUpdate.Factory batchUpdateFactory,
       @Nullable Change sourceChange,
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
index 469894a..980ca28 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -21,14 +21,14 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.extensions.client.ReviewerState;
-import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.FanOutExecutor;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.change.ReviewerSuggestion;
 import com.google.gerrit.server.change.SuggestedReviewer;
 import com.google.gerrit.server.config.GerritServerConfig;
@@ -45,11 +45,11 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -79,6 +79,7 @@
   private final Provider<InternalChangeQuery> queryProvider;
   private final ExecutorService executor;
   private final ApprovalsUtil approvalsUtil;
+  private final AccountCache accountCache;
 
   @Inject
   ReviewerRecommender(
@@ -87,13 +88,15 @@
       Provider<InternalChangeQuery> queryProvider,
       @FanOutExecutor ExecutorService executor,
       ApprovalsUtil approvalsUtil,
-      @GerritServerConfig Config config) {
+      @GerritServerConfig Config config,
+      AccountCache accountCache) {
     this.changeQueryBuilder = changeQueryBuilder;
     this.config = config;
     this.queryProvider = queryProvider;
     this.reviewerSuggestionPluginMap = reviewerSuggestionPluginMap;
     this.executor = executor;
     this.approvalsUtil = approvalsUtil;
+    this.accountCache = accountCache;
   }
 
   public List<Account.Id> suggestReviewers(
@@ -111,12 +114,7 @@
     double baseWeight = config.getInt("addReviewer", "baseWeight", 1);
     logger.atFine().log("base weight: %s", baseWeight);
 
-    Map<Account.Id, MutableDouble> reviewerScores;
-    if (Strings.isNullOrEmpty(query)) {
-      reviewerScores = baseRankingForEmptyQuery(baseWeight);
-    } else {
-      reviewerScores = baseRankingForCandidateList(candidateList, projectState, baseWeight);
-    }
+    Map<Account.Id, MutableDouble> reviewerScores = baseRanking(baseWeight, query, candidateList);
     logger.atFine().log("Base reviewer scores: %s", reviewerScores);
 
     // Send the query along with a candidate list to all plugins and merge the
@@ -198,7 +196,18 @@
     return sortedSuggestions;
   }
 
-  private Map<Account.Id, MutableDouble> baseRankingForEmptyQuery(double baseWeight)
+  /**
+   * @param baseWeight The weight applied to the ordering of the reviewers.
+   * @param query Query to match. For example, it can try to match all users that start with "Ab".
+   * @param candidateList The list of candidates based on the query. If query is empty, this list is
+   *     also empty.
+   * @return Map of account ids that match the query and their appropriate ranking (the better the
+   *     ranking, the better it is to suggest them as reviewers).
+   * @throws IOException Can't find owner="self" account.
+   * @throws ConfigInvalidException Can't find owner="self" account.
+   */
+  private Map<Account.Id, MutableDouble> baseRanking(
+      double baseWeight, String query, List<Account.Id> candidateList)
       throws IOException, ConfigInvalidException {
     // Get the user's last 25 changes, check approvals
     try {
@@ -208,14 +217,15 @@
               .setLimit(25)
               .setRequestedFields(ChangeField.APPROVAL)
               .query(changeQueryBuilder.owner("self"));
-      Map<Account.Id, MutableDouble> suggestions = new HashMap<>();
+      Map<Account.Id, MutableDouble> suggestions = new LinkedHashMap<>();
+      // Put those candidates at the bottom of the list
+      candidateList.stream().forEach(id -> suggestions.put(id, new MutableDouble(0)));
+
       for (ChangeData cd : result) {
         for (PatchSetApproval approval : cd.currentApprovals()) {
           Account.Id id = approval.accountId();
-          if (suggestions.containsKey(id)) {
-            suggestions.get(id).add(baseWeight);
-          } else {
-            suggestions.put(id, new MutableDouble(baseWeight));
+          if (Strings.isNullOrEmpty(query) || accountMatchesQuery(id, query)) {
+            suggestions.computeIfAbsent(id, (ignored) -> new MutableDouble(0)).add(baseWeight);
           }
         }
       }
@@ -227,63 +237,15 @@
     }
   }
 
-  private Map<Account.Id, MutableDouble> baseRankingForCandidateList(
-      List<Account.Id> candidates, ProjectState projectState, double baseWeight)
-      throws IOException, ConfigInvalidException {
-    // Get each reviewer's activity based on number of applied labels
-    // (weighted 10d), number of comments (weighted 0.5d) and number of owned
-    // changes (weighted 1d).
-    Map<Account.Id, MutableDouble> reviewers = new LinkedHashMap<>();
-    if (candidates.size() == 0) {
-      return reviewers;
-    }
-    List<Predicate<ChangeData>> predicates = new ArrayList<>();
-    for (Account.Id id : candidates) {
-      try {
-        Predicate<ChangeData> projectQuery = changeQueryBuilder.project(projectState.getName());
-
-        // Get all labels for this project and create a compound OR query to
-        // fetch all changes where users have applied one of these labels
-        List<LabelType> labelTypes = projectState.getLabelTypes().getLabelTypes();
-        List<Predicate<ChangeData>> labelPredicates = new ArrayList<>(labelTypes.size());
-        for (LabelType type : labelTypes) {
-          labelPredicates.add(changeQueryBuilder.label(type.getName() + ",user=" + id));
-        }
-        Predicate<ChangeData> reviewerQuery =
-            Predicate.and(projectQuery, Predicate.or(labelPredicates));
-
-        Predicate<ChangeData> ownerQuery =
-            Predicate.and(projectQuery, changeQueryBuilder.owner(id.toString()));
-        Predicate<ChangeData> commentedByQuery =
-            Predicate.and(projectQuery, changeQueryBuilder.commentby(id.toString()));
-
-        predicates.add(reviewerQuery);
-        predicates.add(ownerQuery);
-        predicates.add(commentedByQuery);
-        reviewers.put(id, new MutableDouble());
-      } catch (QueryParseException e) {
-        // Unhandled: If an exception is thrown, we won't increase the
-        // candidates's score
-        logger.atSevere().withCause(e).log("Exception while suggesting reviewers");
+  private boolean accountMatchesQuery(Account.Id id, String query) {
+    Optional<Account> account = accountCache.get(id).map(AccountState::account);
+    if (account.isPresent() && account.get().isActive()) {
+      if ((account.get().fullName() != null && account.get().fullName().startsWith(query))
+          || (account.get().preferredEmail() != null
+              && account.get().preferredEmail().startsWith(query))) {
+        return true;
       }
     }
-
-    List<List<ChangeData>> result = queryProvider.get().setLimit(25).noFields().query(predicates);
-
-    Iterator<List<ChangeData>> queryResultIterator = result.iterator();
-    Iterator<Account.Id> reviewersIterator = reviewers.keySet().iterator();
-
-    int i = 0;
-    Account.Id currentId = null;
-    while (queryResultIterator.hasNext()) {
-      List<ChangeData> currentResult = queryResultIterator.next();
-      if (i % WEIGHTS.length == 0) {
-        currentId = reviewersIterator.next();
-      }
-
-      reviewers.get(currentId).add(WEIGHTS[i % WEIGHTS.length] * baseWeight * currentResult.size());
-      i++;
-    }
-    return reviewers;
+    return false;
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index 1aeb1a7..122f3eb 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -251,7 +251,7 @@
                   QueryOptions.create(
                       indexConfig,
                       0,
-                      suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER,
+                      suggestReviewers.getLimit(),
                       ImmutableSet.of(AccountField.ID.getName())))
               .readRaw();
       List<Account.Id> matches =
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index c49ae82..f3ba99b 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -24,6 +24,7 @@
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.Repository;
 import org.kohsuke.args4j.Argument;
@@ -47,7 +48,7 @@
   protected Project project;
 
   @Override
-  public void start(Environment env) {
+  public void start(ChannelSession channel, Environment env) {
     Context ctx = context.subContext(newSession(), context.getCommandLine());
     final Context old = sshScope.set(ctx);
     try {
diff --git a/java/com/google/gerrit/sshd/AliasCommand.java b/java/com/google/gerrit/sshd/AliasCommand.java
index 567cf00..bf0dd91 100644
--- a/java/com/google/gerrit/sshd/AliasCommand.java
+++ b/java/com/google/gerrit/sshd/AliasCommand.java
@@ -27,6 +27,7 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 
 /** Command that executes some other command. */
@@ -47,9 +48,9 @@
   }
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     try {
-      begin(env);
+      begin(channel, env);
     } catch (Failure e) {
       String msg = e.getMessage();
       if (!msg.endsWith("\n")) {
@@ -61,7 +62,7 @@
     }
   }
 
-  private void begin(Environment env) throws IOException, Failure {
+  private void begin(ChannelSession channel, Environment env) throws IOException, Failure {
     Map<String, CommandProvider> map = root.getMap();
     for (String name : chain(command)) {
       CommandProvider p = map.get(name);
@@ -90,15 +91,15 @@
     }
     provideStateTo(cmd);
     atomicCmd.set(cmd);
-    cmd.start(env);
+    cmd.start(channel, env);
   }
 
   @Override
-  public void destroy() {
+  public void destroy(ChannelSession channel) {
     Command cmd = atomicCmd.getAndSet(null);
     if (cmd != null) {
       try {
-        cmd.destroy();
+        cmd.destroy(channel);
       } catch (Exception e) {
         Throwables.throwIfUnchecked(e);
         throw new RuntimeException(e);
diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java
index 2081967..d326237 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -57,6 +57,7 @@
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.server.Environment;
 import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.CmdLineException;
@@ -182,7 +183,7 @@
   }
 
   @Override
-  public void destroy() {
+  public void destroy(ChannelSession channel) {
     Future<?> future = task.getAndSet(null);
     if (future != null && !future.isDone()) {
       future.cancel(true);
@@ -264,7 +265,8 @@
   /**
    * Spawn a function into its own thread.
    *
-   * <p>Typically this should be invoked within {@link Command#start(Environment)}, such as:
+   * <p>Typically this should be invoked within {@link Command#start(ChannelSession, Environment)},
+   * such as:
    *
    * <pre>
    * startThread(new CommandRunnable() {
diff --git a/java/com/google/gerrit/sshd/ChannelIdTrackingUnknownChannelReferenceHandler.java b/java/com/google/gerrit/sshd/ChannelIdTrackingUnknownChannelReferenceHandler.java
new file mode 100644
index 0000000..f8ab90e
--- /dev/null
+++ b/java/com/google/gerrit/sshd/ChannelIdTrackingUnknownChannelReferenceHandler.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * This file is based on sshd-contrib Apache SSHD Mina project. Original commit:
+ * https://github.com/apache/mina-sshd/commit/11b33dee37b5b9c71a40a8a98a42007e3687131e
+ */
+package com.google.gerrit.sshd;
+
+import com.google.common.flogger.FluentLogger;
+import java.io.IOException;
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.channel.ChannelListener;
+import org.apache.sshd.common.channel.exception.SshChannelNotFoundException;
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.helpers.DefaultUnknownChannelReferenceHandler;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Makes sure that the referenced &quot;unknown&quot; channel identifier is one that was assigned in
+ * the past. <B>Note:</B> it relies on the fact that the default {@code ConnectionService}
+ * implementation assigns channels identifiers in ascending order.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ChannelIdTrackingUnknownChannelReferenceHandler
+    extends DefaultUnknownChannelReferenceHandler implements ChannelListener {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+  public static final AttributeKey<Integer> LAST_CHANNEL_ID_KEY = new AttributeKey<>();
+
+  public static final ChannelIdTrackingUnknownChannelReferenceHandler TRACKER =
+      new ChannelIdTrackingUnknownChannelReferenceHandler();
+
+  public ChannelIdTrackingUnknownChannelReferenceHandler() {
+    super();
+  }
+
+  @Override
+  public void channelInitialized(Channel channel) {
+    int channelId = channel.getId();
+    Session session = channel.getSession();
+    Integer lastTracked = session.setAttribute(LAST_CHANNEL_ID_KEY, channelId);
+    logger.atFine().log(
+        "channelInitialized(%s) updated last tracked channel ID %s => %s",
+        channel, lastTracked, channelId);
+  }
+
+  @Override
+  public Channel handleUnknownChannelCommand(
+      ConnectionService service, byte cmd, int channelId, Buffer buffer) throws IOException {
+    Session session = service.getSession();
+    Integer lastTracked = session.getAttribute(LAST_CHANNEL_ID_KEY);
+    if ((lastTracked != null) && (channelId <= lastTracked.intValue())) {
+      // Use TRACE level in order to avoid messages flooding
+      logger.atFinest().log(
+          "handleUnknownChannelCommand(%s) apply default handling for %s on channel=%s (lastTracked=%s)",
+          session, SshConstants.getCommandMessageName(cmd), channelId, lastTracked);
+      return super.handleUnknownChannelCommand(service, cmd, channelId, buffer);
+    }
+
+    throw new SshChannelNotFoundException(
+        channelId,
+        "Received "
+            + SshConstants.getCommandMessageName(cmd)
+            + " on unassigned channel "
+            + channelId
+            + " (last assigned="
+            + lastTracked
+            + ")");
+  }
+}
diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index 245dd60..19f3386 100644
--- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -40,6 +40,7 @@
 import org.apache.sshd.server.Environment;
 import org.apache.sshd.server.ExitCallback;
 import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.command.CommandFactory;
 import org.apache.sshd.server.session.ServerSession;
@@ -91,13 +92,13 @@
 
   @Override
   public CommandFactory get() {
-    return requestCommand -> {
-      String c = requestCommand;
+    return (channelSession, requestCommand) -> {
+      String command = requestCommand;
       SshCreateCommandInterceptor interceptor = createCommandInterceptor.get();
       if (interceptor != null) {
-        c = interceptor.intercept(c);
+        command = interceptor.intercept(command);
       }
-      return new Trampoline(c);
+      return new Trampoline(command);
     };
   }
 
@@ -148,7 +149,7 @@
     }
 
     @Override
-    public void start(Environment env) throws IOException {
+    public void start(ChannelSession channel, Environment env) throws IOException {
       this.env = env;
       final Context ctx = this.ctx;
       task.set(
@@ -157,7 +158,7 @@
                 @Override
                 public void run() {
                   try {
-                    onStart();
+                    onStart(channel);
                   } catch (Exception e) {
                     logger.atWarning().withCause(e).log(
                         "Cannot start command \"%s\" for user %s",
@@ -172,7 +173,7 @@
               }));
     }
 
-    private void onStart() throws IOException {
+    private void onStart(ChannelSession channel) throws IOException {
       synchronized (this) {
         final Context old = sshScope.set(ctx);
         try {
@@ -195,7 +196,7 @@
                   log(rc);
                 }
               });
-          cmd.start(env);
+          cmd.start(channel, env);
         } finally {
           sshScope.set(old);
         }
@@ -225,20 +226,20 @@
     }
 
     @Override
-    public void destroy() {
+    public void destroy(ChannelSession channel) {
       Future<?> future = task.getAndSet(null);
       if (future != null) {
         future.cancel(true);
-        destroyExecutor.execute(this::onDestroy);
+        destroyExecutor.execute(() -> onDestroy(channel));
       }
     }
 
-    private void onDestroy() {
+    private void onDestroy(ChannelSession channel) {
       synchronized (this) {
         if (cmd != null) {
           final Context old = sshScope.set(ctx);
           try {
-            cmd.destroy();
+            cmd.destroy(channel);
             log(BaseCommand.STATUS_CANCEL);
           } finally {
             ctx = null;
diff --git a/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java b/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
index 1e32e1b..6c0f3af 100644
--- a/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
+++ b/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
@@ -31,6 +31,7 @@
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.PublicKey;
 import java.util.Collection;
@@ -80,19 +81,24 @@
   }
 
   private static Set<PublicKey> myHostKeys(KeyPairProvider p) {
-    final Set<PublicKey> keys = new HashSet<>(6);
-    addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
-    addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
-    addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
-    addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
-    addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
-    addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+    Set<PublicKey> keys = new HashSet<>(6);
+    try {
+      addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
+      addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
+      addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
+      addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
+      addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
+      addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+    } catch (IOException | GeneralSecurityException e) {
+      throw new IllegalStateException("Cannot load SSHD host key", e);
+    }
+
     return keys;
   }
 
-  private static void addPublicKey(
-      final Collection<PublicKey> out, KeyPairProvider p, String type) {
-    final KeyPair pair = p.loadKey(type);
+  private static void addPublicKey(Collection<PublicKey> out, KeyPairProvider p, String type)
+      throws IOException, GeneralSecurityException {
+    KeyPair pair = p.loadKey(null, type);
     if (pair != null && pair.getPublic() != null) {
       out.add(pair.getPublic());
     }
diff --git a/java/com/google/gerrit/sshd/DispatchCommand.java b/java/com/google/gerrit/sshd/DispatchCommand.java
index 68962db..7db65bd 100644
--- a/java/com/google/gerrit/sshd/DispatchCommand.java
+++ b/java/com/google/gerrit/sshd/DispatchCommand.java
@@ -33,6 +33,7 @@
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 import org.kohsuke.args4j.Argument;
 
@@ -69,7 +70,7 @@
   }
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     try {
       parseCommandLine();
       if (Strings.isNullOrEmpty(commandName)) {
@@ -115,7 +116,7 @@
 
       provideStateTo(cmd);
       atomicCmd.set(cmd);
-      cmd.start(env);
+      cmd.start(channel, env);
 
     } catch (UnloggedFailure e) {
       String msg = e.getMessage();
@@ -145,11 +146,11 @@
   }
 
   @Override
-  public void destroy() {
+  public void destroy(ChannelSession channel) {
     Command cmd = atomicCmd.getAndSet(null);
     if (cmd != null) {
       try {
-        cmd.destroy();
+        cmd.destroy(channel);
       } catch (Exception e) {
         Throwables.throwIfUnchecked(e);
         throw new RuntimeException(e);
diff --git a/java/com/google/gerrit/sshd/HostKeyProvider.java b/java/com/google/gerrit/sshd/HostKeyProvider.java
index bffcfcd..3578fb9 100644
--- a/java/com/google/gerrit/sshd/HostKeyProvider.java
+++ b/java/com/google/gerrit/sshd/HostKeyProvider.java
@@ -18,7 +18,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
-import java.io.File;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -44,21 +43,21 @@
     Path ecdsaKey_521 = site.ssh_ecdsa_521;
     Path ed25519Key = site.ssh_ed25519;
 
-    final List<File> stdKeys = new ArrayList<>(6);
+    final List<Path> stdKeys = new ArrayList<>(6);
     if (Files.exists(rsaKey)) {
-      stdKeys.add(rsaKey.toAbsolutePath().toFile());
+      stdKeys.add(rsaKey);
     }
     if (Files.exists(ecdsaKey_256)) {
-      stdKeys.add(ecdsaKey_256.toAbsolutePath().toFile());
+      stdKeys.add(ecdsaKey_256);
     }
     if (Files.exists(ecdsaKey_384)) {
-      stdKeys.add(ecdsaKey_384.toAbsolutePath().toFile());
+      stdKeys.add(ecdsaKey_384);
     }
     if (Files.exists(ecdsaKey_521)) {
-      stdKeys.add(ecdsaKey_521.toAbsolutePath().toFile());
+      stdKeys.add(ecdsaKey_521);
     }
     if (Files.exists(ed25519Key)) {
-      stdKeys.add(ed25519Key.toAbsolutePath().toFile());
+      stdKeys.add(ed25519Key);
     }
 
     if (Files.exists(objKey)) {
@@ -70,14 +69,14 @@
       // Both formats of host key exist, we don't know which format
       // should be authoritative. Complain and abort.
       //
-      stdKeys.add(objKey.toAbsolutePath().toFile());
+      stdKeys.add(objKey);
       throw new ProvisionException("Multiple host keys exist: " + stdKeys);
     }
     if (stdKeys.isEmpty()) {
       throw new ProvisionException("No SSH keys under " + site.etc_dir);
     }
     FileKeyPairProvider kp = new FileKeyPairProvider();
-    kp.setFiles(stdKeys);
+    kp.setPaths(stdKeys);
     return kp;
   }
 }
diff --git a/java/com/google/gerrit/sshd/NoShell.java b/java/com/google/gerrit/sshd/NoShell.java
index d61d6f7..680dc34 100644
--- a/java/com/google/gerrit/sshd/NoShell.java
+++ b/java/com/google/gerrit/sshd/NoShell.java
@@ -27,12 +27,13 @@
 import java.io.OutputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
-import org.apache.sshd.common.Factory;
 import org.apache.sshd.server.Environment;
 import org.apache.sshd.server.ExitCallback;
 import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.shell.ShellFactory;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.util.SystemReader;
 
@@ -42,7 +43,7 @@
  * <p>This implementation is used to ensure clients who try to SSH directly to this server without
  * supplying a command will get a reasonable error message, but cannot continue further.
  */
-class NoShell implements Factory<Command> {
+class NoShell implements ShellFactory {
   private final Provider<SendMessage> shell;
 
   @Inject
@@ -51,7 +52,7 @@
   }
 
   @Override
-  public Command create() {
+  public Command createShell(ChannelSession channel) {
     return shell.get();
   }
 
@@ -98,7 +99,7 @@
     }
 
     @Override
-    public void start(Environment env) throws IOException {
+    public void start(ChannelSession channel, Environment env) throws IOException {
       Context old = sshScope.set(context);
       String message;
       try {
@@ -116,7 +117,7 @@
     }
 
     @Override
-    public void destroy() {}
+    public void destroy(ChannelSession channel) {}
   }
 
   static class MessageFactory {
diff --git a/java/com/google/gerrit/sshd/SshCommand.java b/java/com/google/gerrit/sshd/SshCommand.java
index 2590188..e60ba6d 100644
--- a/java/com/google/gerrit/sshd/SshCommand.java
+++ b/java/com/google/gerrit/sshd/SshCommand.java
@@ -27,6 +27,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.eclipse.jgit.lib.Config;
 import org.kohsuke.args4j.Option;
 
@@ -45,7 +46,7 @@
   protected PrintWriter stderr;
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     startThread(
         () -> {
           parseCommandLine();
diff --git a/java/com/google/gerrit/sshd/SshDaemon.java b/java/com/google/gerrit/sshd/SshDaemon.java
index 69176a2..7512b3e 100644
--- a/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/java/com/google/gerrit/sshd/SshDaemon.java
@@ -51,6 +51,7 @@
 import java.nio.file.WatchService;
 import java.nio.file.attribute.UserPrincipalLookupService;
 import java.nio.file.spi.FileSystemProvider;
+import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
 import java.security.KeyPair;
 import java.security.PublicKey;
@@ -208,6 +209,7 @@
     final boolean enableCompression = cfg.getBoolean("sshd", "enableCompression", false);
 
     SshSessionBackend backend = cfg.getEnum("sshd", null, "backend", SshSessionBackend.NIO2);
+    boolean channelIdTracking = cfg.getBoolean("sshd", "enableChannelIdTracking", true);
 
     System.setProperty(
         IoServiceFactoryFactory.class.getName(),
@@ -221,7 +223,7 @@
     initMacs(cfg);
     initSignatures();
     initChannels();
-    initUnknownChannelReferenceHandler();
+    initUnknownChannelReferenceHandler(channelIdTracking);
     initForwarding();
     initFileSystemFactory();
     initSubsystems();
@@ -381,12 +383,12 @@
       return Collections.emptyList();
     }
 
-    final List<PublicKey> keys = myHostKeys();
-    final List<HostKey> r = new ArrayList<>();
+    List<HostKey> r = new ArrayList<>();
+    List<PublicKey> keys = myHostKeys();
     for (PublicKey pub : keys) {
-      final Buffer buf = new ByteArrayBuffer();
+      Buffer buf = new ByteArrayBuffer();
       buf.putRawPublicKey(pub);
-      final byte[] keyBin = buf.getCompactData();
+      byte[] keyBin = buf.getCompactData();
 
       for (String addr : advertised) {
         try {
@@ -397,24 +399,29 @@
         }
       }
     }
+
     return Collections.unmodifiableList(r);
   }
 
   private List<PublicKey> myHostKeys() {
-    final KeyPairProvider p = getKeyPairProvider();
-    final List<PublicKey> keys = new ArrayList<>(6);
-    addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
-    addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
-    addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
-    addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
-    addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
-    addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+    KeyPairProvider p = getKeyPairProvider();
+    List<PublicKey> keys = new ArrayList<>(6);
+    try {
+      addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
+      addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
+      addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
+      addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
+      addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
+      addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+    } catch (IOException | GeneralSecurityException e) {
+      throw new IllegalStateException("Cannot load SSHD host key", e);
+    }
     return keys;
   }
 
-  private static void addPublicKey(
-      final Collection<PublicKey> out, KeyPairProvider p, String type) {
-    final KeyPair pair = p.loadKey(type);
+  private static void addPublicKey(final Collection<PublicKey> out, KeyPairProvider p, String type)
+      throws IOException, GeneralSecurityException {
+    final KeyPair pair = p.loadKey(null, type);
     if (pair != null && pair.getPublic() != null) {
       out.add(pair.getPublic());
     }
@@ -514,14 +521,14 @@
 
   @SuppressWarnings("unchecked")
   private void initCiphers(Config cfg) {
-    final List<NamedFactory<Cipher>> a = BaseBuilder.setUpDefaultCiphers(true);
+    List<NamedFactory<Cipher>> a = BaseBuilder.setUpDefaultCiphers(true);
 
     for (Iterator<NamedFactory<Cipher>> i = a.iterator(); i.hasNext(); ) {
-      final NamedFactory<Cipher> f = i.next();
+      NamedFactory<Cipher> f = i.next();
       try {
-        final Cipher c = f.create();
-        final byte[] key = new byte[c.getBlockSize()];
-        final byte[] iv = new byte[c.getIVSize()];
+        Cipher c = f.create();
+        byte[] key = new byte[c.getKdfSize()];
+        byte[] iv = new byte[c.getIVSize()];
         c.init(Cipher.Mode.Encrypt, key, iv);
       } catch (InvalidKeyException e) {
         logger.atWarning().log(
@@ -614,7 +621,8 @@
   }
 
   private void initSignatures() {
-    setSignatureFactories(BaseBuilder.setUpDefaultSignatures(true));
+    setSignatureFactories(
+        NamedFactory.setUpBuiltinFactories(false, ServerBuilder.DEFAULT_SIGNATURE_PREFERENCE));
   }
 
   private void initCompression(boolean enableCompression) {
@@ -646,8 +654,11 @@
     setChannelFactories(ServerBuilder.DEFAULT_CHANNEL_FACTORIES);
   }
 
-  private void initUnknownChannelReferenceHandler() {
-    setUnknownChannelReferenceHandler(DefaultUnknownChannelReferenceHandler.INSTANCE);
+  private void initUnknownChannelReferenceHandler(boolean enableChannelIdTracking) {
+    setUnknownChannelReferenceHandler(
+        enableChannelIdTracking
+            ? ChannelIdTrackingUnknownChannelReferenceHandler.TRACKER
+            : DefaultUnknownChannelReferenceHandler.INSTANCE);
   }
 
   private void initSubsystems() {
diff --git a/java/com/google/gerrit/sshd/SshSession.java b/java/com/google/gerrit/sshd/SshSession.java
index 1a60a20..d6ecc73 100644
--- a/java/com/google/gerrit/sshd/SshSession.java
+++ b/java/com/google/gerrit/sshd/SshSession.java
@@ -19,7 +19,7 @@
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
-import org.apache.sshd.common.AttributeStore.AttributeKey;
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
 
 /** Global data related to an active SSH connection. */
 public class SshSession {
diff --git a/java/com/google/gerrit/sshd/SuExec.java b/java/com/google/gerrit/sshd/SuExec.java
index 7053a0d..a126250 100644
--- a/java/com/google/gerrit/sshd/SuExec.java
+++ b/java/com/google/gerrit/sshd/SuExec.java
@@ -35,6 +35,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
@@ -90,7 +91,7 @@
   }
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     try {
       checkCanRunAs();
       parseCommandLine();
@@ -102,7 +103,7 @@
         cmd.setArguments(args.toArray(new String[args.size()]));
         provideStateTo(cmd);
         atomicCmd.set(cmd);
-        cmd.start(env);
+        cmd.start(channel, env);
       } finally {
         sshScope.set(old);
       }
@@ -158,11 +159,11 @@
   }
 
   @Override
-  public void destroy() {
+  public void destroy(ChannelSession channel) {
     Command cmd = atomicCmd.getAndSet(null);
     if (cmd != null) {
       try {
-        cmd.destroy();
+        cmd.destroy(channel);
       } catch (Exception e) {
         Throwables.throwIfUnchecked(e);
         throw new RuntimeException(e);
diff --git a/java/com/google/gerrit/sshd/commands/ScpCommand.java b/java/com/google/gerrit/sshd/commands/ScpCommand.java
index 5122b35..6912795 100644
--- a/java/com/google/gerrit/sshd/commands/ScpCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ScpCommand.java
@@ -34,6 +34,7 @@
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 
 final class ScpCommand extends BaseCommand {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -81,7 +82,7 @@
   }
 
   @Override
-  public void start(Environment env) {
+  public void start(ChannelSession channel, Environment env) {
     startThread(this::runImp, AccessPath.SSH_COMMAND);
   }
 
diff --git a/java/com/google/gerrit/sshd/commands/ShowCaches.java b/java/com/google/gerrit/sshd/commands/ShowCaches.java
index cee06e1..db0a481 100644
--- a/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -51,6 +51,7 @@
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.mina.MinaSession;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.kohsuke.args4j.Option;
 
 /** Show the current cache states. */
@@ -97,7 +98,7 @@
   private int nw;
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     String s = env.getEnv().get(Environment.ENV_COLUMNS);
     if (s != null && !s.isEmpty()) {
       try {
@@ -106,7 +107,7 @@
         columns = 80;
       }
     }
-    super.start(env);
+    super.start(channel, env);
   }
 
   @Override
diff --git a/java/com/google/gerrit/sshd/commands/ShowConnections.java b/java/com/google/gerrit/sshd/commands/ShowConnections.java
index 231bcf6..decf5d5 100644
--- a/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -44,6 +44,7 @@
 import org.apache.sshd.common.io.nio2.Nio2Acceptor;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.kohsuke.args4j.Option;
 
 /** Show the current SSH connections. */
@@ -71,7 +72,7 @@
   private int columns = 80;
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     String s = env.getEnv().get(Environment.ENV_COLUMNS);
     if (s != null && !s.isEmpty()) {
       try {
@@ -80,7 +81,7 @@
         columns = 80;
       }
     }
-    super.start(env);
+    super.start(channel, env);
   }
 
   @Override
diff --git a/java/com/google/gerrit/sshd/commands/ShowQueue.java b/java/com/google/gerrit/sshd/commands/ShowQueue.java
index a6ed629..2ec9e2d 100644
--- a/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -40,6 +40,7 @@
 import java.util.List;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.kohsuke.args4j.Option;
 
 /** Display the current work queue. */
@@ -70,7 +71,7 @@
   private int maxCommandWidth;
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     String s = env.getEnv().get(Environment.ENV_COLUMNS);
     if (s != null && !s.isEmpty()) {
       try {
@@ -79,7 +80,7 @@
         columns = 80;
       }
     }
-    super.start(env);
+    super.start(channel, env);
   }
 
   @Override
diff --git a/java/com/google/gerrit/sshd/commands/StreamEvents.java b/java/com/google/gerrit/sshd/commands/StreamEvents.java
index c680d30..45540a0 100644
--- a/java/com/google/gerrit/sshd/commands/StreamEvents.java
+++ b/java/com/google/gerrit/sshd/commands/StreamEvents.java
@@ -41,6 +41,7 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.kohsuke.args4j.Option;
 
 @RequiresCapability(GlobalCapability.STREAM_EVENTS)
@@ -105,7 +106,7 @@
   private Future<?> task;
 
   @Override
-  public void start(Environment env) throws IOException {
+  public void start(ChannelSession channel, Environment env) throws IOException {
     try {
       parseCommandLine();
     } catch (UnloggedFailure e) {
@@ -179,7 +180,7 @@
   }
 
   @Override
-  public void destroy() {
+  public void destroy(ChannelSession channel) {
     removeEventListenerRegistration();
 
     final boolean exit;
diff --git a/java/com/google/gerrit/sshd/commands/Upload.java b/java/com/google/gerrit/sshd/commands/Upload.java
index a22cdaf..5a3c745 100644
--- a/java/com/google/gerrit/sshd/commands/Upload.java
+++ b/java/com/google/gerrit/sshd/commands/Upload.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.server.RequestInfo;
 import com.google.gerrit.server.RequestListener;
 import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.git.UploadPackInitializer;
 import com.google.gerrit.server.git.validators.UploadValidationException;
@@ -35,6 +36,7 @@
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.List;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.PostUploadHook;
 import org.eclipse.jgit.transport.PostUploadHookChain;
 import org.eclipse.jgit.transport.PreUploadHook;
@@ -65,7 +67,8 @@
       throw new Failure(1, "fatal: unable to check permissions " + e);
     }
 
-    final UploadPack up = new UploadPack(repo);
+    Repository permissionAwareRepository = PermissionAwareRepositoryManager.wrap(repo, perm);
+    final UploadPack up = new UploadPack(permissionAwareRepository);
     up.setAdvertiseRefsHook(new DefaultAdvertiseRefsHook(perm, RefFilterOptions.defaults()));
     up.setPackConfig(config.getPackConfig());
     up.setTimeout(config.getTimeout());
@@ -73,7 +76,8 @@
 
     List<PreUploadHook> allPreUploadHooks = Lists.newArrayList(preUploadHooks);
     allPreUploadHooks.add(
-        uploadValidatorsFactory.create(project, repo, session.getRemoteAddressAsString()));
+        uploadValidatorsFactory.create(
+            project, permissionAwareRepository, session.getRemoteAddressAsString()));
     up.setPreUploadHook(PreUploadHookChain.newChain(allPreUploadHooks));
     for (UploadPackInitializer initializer : uploadPackInitializers) {
       initializer.init(projectState.getNameKey(), up);
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index 2ab91ae..c6c86b3 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -10,7 +10,6 @@
     visibility = ["//visibility:public"],
     exports = [
         "//lib:junit",
-        "//lib/easymock",
     ],
     deps = [
         "//java/com/google/gerrit/acceptance/testsuite/project",
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index a4157da..894e980 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -30,6 +30,7 @@
 import static com.google.gerrit.server.project.testing.TestLabels.value;
 import static org.eclipse.jgit.lib.Constants.HEAD;
 
+import com.google.common.cache.Cache;
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.GitUtil;
@@ -47,10 +48,14 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.change.ChangeKindCacheImpl;
 import com.google.gerrit.server.project.testing.TestLabels;
 import com.google.inject.Inject;
+import com.google.inject.name.Named;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
@@ -64,6 +69,10 @@
   @Inject private ProjectOperations projectOperations;
   @Inject private RequestScopeOperations requestScopeOperations;
 
+  @Inject
+  @Named("change_kind")
+  private Cache<ChangeKindCacheImpl.Key, ChangeKind> changeKindCache;
+
   @Before
   public void setup() throws Exception {
     try (ProjectConfigUpdate u = updateProject(project)) {
@@ -320,6 +329,42 @@
   }
 
   @Test
+  public void stickyAcrossMultiplePatchSetsDoNotRegressPerformance() throws Exception {
+    // The purpose of this test is to make sure that we compute change kind only against the parent
+    // patch set. Change kind is a heavy operation. In prior version of Gerrit, we computed the
+    // change kind against all prior patch sets. This is a regression that made Gerrit do expensive
+    // work in O(num-patch-sets). This test ensures that we aren't regressing.
+    try (ProjectConfigUpdate u = updateProject(project)) {
+      u.getConfig().getLabelSections().get("Code-Review").setCopyMaxScore(true);
+      u.getConfig().getLabelSections().get("Verified").setCopyAllScoresIfNoCodeChange(true);
+      u.save();
+    }
+
+    String changeId = createChange(REWORK);
+    vote(admin, changeId, 2, 1);
+    updateChange(changeId, NO_CODE_CHANGE);
+    updateChange(changeId, NO_CODE_CHANGE);
+    updateChange(changeId, NO_CODE_CHANGE);
+
+    Map<Integer, ObjectId> revisions = new HashMap<>();
+    gApi.changes()
+        .id(changeId)
+        .get()
+        .revisions
+        .forEach(
+            (revId, revisionInfo) ->
+                revisions.put(revisionInfo._number, ObjectId.fromString(revId)));
+    assertThat(revisions.size()).isEqualTo(4);
+    assertChangeKindCacheContains(revisions.get(3), revisions.get(4));
+    assertChangeKindCacheContains(revisions.get(2), revisions.get(3));
+    assertChangeKindCacheContains(revisions.get(1), revisions.get(2));
+
+    assertChangeKindCacheDoesNotContain(revisions.get(1), revisions.get(4));
+    assertChangeKindCacheDoesNotContain(revisions.get(2), revisions.get(4));
+    assertChangeKindCacheDoesNotContain(revisions.get(1), revisions.get(3));
+  }
+
+  @Test
   public void copyMinMaxAcrossMultiplePatchSets() throws Exception {
     try (ProjectConfigUpdate u = updateProject(project)) {
       u.getConfig().getLabelSections().get("Code-Review").setCopyMaxScore(true);
@@ -379,6 +424,18 @@
     assertVotes(detailedChange(changeId), admin, label, 0, REWORK);
   }
 
+  private void assertChangeKindCacheContains(ObjectId prior, ObjectId next) {
+    ChangeKind kind =
+        changeKindCache.getIfPresent(ChangeKindCacheImpl.Key.create(prior, next, "recursive"));
+    assertThat(kind).isNotNull();
+  }
+
+  private void assertChangeKindCacheDoesNotContain(ObjectId prior, ObjectId next) {
+    ChangeKind kind =
+        changeKindCache.getIfPresent(ChangeKindCacheImpl.Key.create(prior, next, "recursive"));
+    assertThat(kind).isNull();
+  }
+
   private ChangeInfo detailedChange(String changeId) throws Exception {
     return gApi.changes().id(changeId).get(DETAILED_LABELS, CURRENT_REVISION, CURRENT_COMMIT);
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index 42b82c5..3b7a294 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -433,106 +433,26 @@
   }
 
   @Test
-  @GerritConfig(name = "suggest.maxSuggestedReviewers", value = "10")
-  public void reviewerRanking() throws Exception {
-    // Assert that user are ranked by the number of times they have applied a
-    // a label to a change (highest), added comments (medium) or owned a
-    // change (low).
-    String fullName = "Primum Finalis";
-    TestAccount userWhoOwns = user("customuser1", fullName);
-    TestAccount reviewer1 = user("customuser2", fullName);
-    TestAccount reviewer2 = user("customuser3", fullName);
-    TestAccount userWhoComments = user("customuser4", fullName);
-    TestAccount userWhoLooksForSuggestions = user("customuser5", fullName);
-
-    // Create a change as userWhoOwns and add some reviews
-    requestScopeOperations.setApiUser(userWhoOwns.id());
-    String changeId1 = createChangeFromApi();
-
-    requestScopeOperations.setApiUser(reviewer1.id());
-    reviewChange(changeId1);
-
-    requestScopeOperations.setApiUser(user1.id());
-    String changeId2 = createChangeFromApi();
-
-    requestScopeOperations.setApiUser(reviewer1.id());
-    reviewChange(changeId2);
-
-    requestScopeOperations.setApiUser(reviewer2.id());
-    reviewChange(changeId2);
-
-    // Create a comment as a different user
-    requestScopeOperations.setApiUser(userWhoComments.id());
-    ReviewInput ri = new ReviewInput();
-    ri.message = "Test";
-    gApi.changes().id(changeId1).revision(1).review(ri);
-
-    // Create a change as a new user to assert that we receive the correct
-    // ranking
-
-    requestScopeOperations.setApiUser(userWhoLooksForSuggestions.id());
-    List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "Pri", 4);
-    assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
-        .containsExactly(
-            reviewer1.id().get(),
-            reviewer2.id().get(),
-            userWhoOwns.id().get(),
-            userWhoComments.id().get())
-        .inOrder();
-  }
-
-  @Test
-  public void reviewerRankingProjectIsolation() throws Exception {
-    // Create new project
-    Project.NameKey newProject = projectOperations.newProject().create();
-
-    // Create users who review changes in both the default and the new project
-    String fullName = "Primum Finalis";
-    TestAccount userWhoOwns = user("customuser1", fullName);
-    TestAccount reviewer1 = user("customuser2", fullName);
-    TestAccount reviewer2 = user("customuser3", fullName);
-
-    requestScopeOperations.setApiUser(userWhoOwns.id());
-    String changeId1 = createChangeFromApi();
-
-    requestScopeOperations.setApiUser(reviewer1.id());
-    reviewChange(changeId1);
-
-    requestScopeOperations.setApiUser(userWhoOwns.id());
-    String changeId2 = createChangeFromApi(newProject);
-
-    requestScopeOperations.setApiUser(reviewer2.id());
-    reviewChange(changeId2);
-
-    requestScopeOperations.setApiUser(userWhoOwns.id());
-    String changeId3 = createChangeFromApi(newProject);
-
-    requestScopeOperations.setApiUser(reviewer2.id());
-    reviewChange(changeId3);
-
-    requestScopeOperations.setApiUser(userWhoOwns.id());
-    List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "Prim", 4);
-
-    // Assert that reviewer1 is on top, even though reviewer2 has more reviews
-    // in other projects
-    assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
-        .containsExactly(reviewer1.id().get(), reviewer2.id().get())
-        .inOrder();
-  }
-
-  @Test
   public void suggestNoInactiveAccounts() throws Exception {
+    requestScopeOperations.setApiUser(user.id());
+    String changeIdReviewed = createChangeFromApi();
+    String changeId = createChangeFromApi();
+
     String name = name("foo");
     TestAccount foo1 = accountCreator.create(name + "-1");
+    requestScopeOperations.setApiUser(foo1.id());
+    reviewChange(changeIdReviewed);
     assertThat(gApi.accounts().id(foo1.username()).getActive()).isTrue();
 
     TestAccount foo2 = accountCreator.create(name + "-2");
+    requestScopeOperations.setApiUser(foo2.id());
+    reviewChange(changeIdReviewed);
     assertThat(gApi.accounts().id(foo2.username()).getActive()).isTrue();
 
-    String changeId = createChange().getChangeId();
     assertReviewers(
         suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
 
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().id(foo2.username()).setActive(false);
     assertThat(gApi.accounts().id(foo2.id().get()).getActive()).isFalse();
     assertReviewers(suggestReviewers(changeId, name), ImmutableList.of(foo1), ImmutableList.of());
@@ -540,11 +460,19 @@
 
   @Test
   public void suggestNoExistingReviewers() throws Exception {
+    requestScopeOperations.setApiUser(user.id());
+    String changeId = createChangeFromApi();
+    String changeIdReviewed = createChangeFromApi();
+
     String name = name("foo");
     TestAccount foo1 = accountCreator.create(name + "-1");
-    TestAccount foo2 = accountCreator.create(name + "-2");
+    requestScopeOperations.setApiUser(foo1.id());
+    reviewChange(changeIdReviewed);
 
-    String changeId = createChange().getChangeId();
+    TestAccount foo2 = accountCreator.create(name + "-2");
+    requestScopeOperations.setApiUser(foo2.id());
+    reviewChange(changeIdReviewed);
+
     assertReviewers(
         suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
 
@@ -554,11 +482,19 @@
 
   @Test
   public void suggestCcAsReviewer() throws Exception {
+    requestScopeOperations.setApiUser(user.id());
+    String changeId = createChangeFromApi();
+    String changeIdReviewed = createChangeFromApi();
+
     String name = name("foo");
     TestAccount foo1 = accountCreator.create(name + "-1");
-    TestAccount foo2 = accountCreator.create(name + "-2");
+    requestScopeOperations.setApiUser(foo1.id());
+    reviewChange(changeIdReviewed);
 
-    String changeId = createChange().getChangeId();
+    TestAccount foo2 = accountCreator.create(name + "-2");
+    requestScopeOperations.setApiUser(foo2.id());
+    reviewChange(changeIdReviewed);
+
     assertReviewers(
         suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
 
@@ -572,11 +508,19 @@
 
   @Test
   public void suggestReviewerAsCc() throws Exception {
+    requestScopeOperations.setApiUser(user.id());
+    String changeId = createChangeFromApi();
+    String changeIdReviewed = createChangeFromApi();
+
     String name = name("foo");
     TestAccount foo1 = accountCreator.create(name + "-1");
-    TestAccount foo2 = accountCreator.create(name + "-2");
+    requestScopeOperations.setApiUser(foo1.id());
+    reviewChange(changeIdReviewed);
 
-    String changeId = createChange().getChangeId();
+    TestAccount foo2 = accountCreator.create(name + "-2");
+    requestScopeOperations.setApiUser(foo2.id());
+    reviewChange(changeIdReviewed);
+
     assertReviewers(suggestCcs(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
 
     AddReviewerInput reviewerInput = new AddReviewerInput();
@@ -591,25 +535,17 @@
     String secondaryEmail = "foo.secondary@example.com";
     TestAccount foo = createAccountWithSecondaryEmail("foo", secondaryEmail);
 
-    List<SuggestedReviewerInfo> reviewers =
-        suggestReviewers(createChange().getChangeId(), secondaryEmail, 4);
-    assertReviewers(reviewers, ImmutableList.of(foo), ImmutableList.of());
-
-    reviewers = suggestReviewers(createChange().getChangeId(), "secondary", 4);
+    List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "secondary", 4);
     assertReviewers(reviewers, ImmutableList.of(foo), ImmutableList.of());
   }
 
   @Test
   public void cannotSuggestBySecondaryEmailWithoutModifyAccount() throws Exception {
     String secondaryEmail = "foo.secondary@example.com";
-    createAccountWithSecondaryEmail("foo", secondaryEmail);
+    TestAccount foo = createAccountWithSecondaryEmail("foo", secondaryEmail);
 
     requestScopeOperations.setApiUser(user.id());
-    List<SuggestedReviewerInfo> reviewers =
-        suggestReviewers(createChange().getChangeId(), secondaryEmail, 4);
-    assertThat(reviewers).isEmpty();
-
-    reviewers = suggestReviewers(createChange().getChangeId(), "secondary2", 4);
+    List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "secondary", 4);
     assertThat(reviewers).isEmpty();
   }
 
@@ -630,6 +566,24 @@
     assertThat(Iterables.getOnlyElement(reviewers).account.secondaryEmails).isNull();
   }
 
+  @Test
+  public void suggestsPeopleWithNoReviewsWhenExplicitlyQueried() throws Exception {
+    TestAccount newTeamMember = accountCreator.create("newTeamMember");
+
+    requestScopeOperations.setApiUser(user.id());
+    String changeId = createChangeFromApi();
+    String changeIdReviewed = createChangeFromApi();
+
+    TestAccount reviewer = accountCreator.create("newReviewer");
+    requestScopeOperations.setApiUser(reviewer.id());
+    reviewChange(changeIdReviewed);
+
+    List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, "new", 4);
+    assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
+        .containsExactly(reviewer.id().get(), newTeamMember.id().get())
+        .inOrder();
+  }
+
   private TestAccount createAccountWithSecondaryEmail(String name, String secondaryEmail)
       throws Exception {
     TestAccount foo = accountCreator.create(name(name), "foo.primary@example.com", "Foo");
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
index f9011c7..b18db81 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
@@ -43,11 +43,7 @@
   @Before
   public void setUp() throws Exception {
     repo = GitUtil.newTestRepository(repoManager.openRepository(project));
-    projectOperations
-        .project(project)
-        .forUpdate()
-        .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
-        .update();
+    blockRead();
   }
 
   @After
@@ -117,8 +113,17 @@
 
   @Test
   public void getOpenChange_NotFound() throws Exception {
+    // Need to unblock read to allow the push operation to succeed if not, when retrieving the
+    // advertised refs during
+    // the push, the client won't be sent the initial commit and will send it again as part of the
+    // change.
+    unblockRead();
+
     PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
     r.assertOkStatus();
+
+    // Re-blocking the read
+    blockRead();
     assertNotFound(r.getCommit());
   }
 
@@ -129,6 +134,14 @@
     }
   }
 
+  private void blockRead() {
+    projectOperations
+        .project(project)
+        .forUpdate()
+        .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+        .update();
+  }
+
   private void assertNotFound(ObjectId id) throws Exception {
     userRestSession.get("/projects/" + project.get() + "/commits/" + id.name()).assertNotFound();
   }
diff --git a/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java b/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
index 1c6559b0..4932248 100644
--- a/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
+++ b/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
@@ -14,9 +14,12 @@
 
 package com.google.gerrit.httpd;
 
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
@@ -29,11 +32,10 @@
 import javax.servlet.FilterConfig;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.easymock.Capture;
-import org.easymock.EasyMockSupport;
-import org.easymock.IMocksControl;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 
 public class AllRequestFilterFilterProxyTest {
   /**
@@ -81,16 +83,11 @@
 
   @Test
   public void noFilters() throws Exception {
-    EasyMockSupport ems = new EasyMockSupport();
-
-    FilterConfig config = ems.createMock(FilterConfig.class);
+    FilterConfig config = mock(FilterConfig.class);
     HttpServletRequest req = new FakeHttpServletRequest();
     HttpServletResponse res = new FakeHttpServletResponse();
 
-    FilterChain chain = ems.createMock(FilterChain.class);
-    chain.doFilter(req, res);
-
-    ems.replayAll();
+    FilterChain chain = mock(FilterChain.class);
 
     AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
 
@@ -98,25 +95,18 @@
     filterProxy.doFilter(req, res, chain);
     filterProxy.destroy();
 
-    ems.verifyAll();
+    verify(chain).doFilter(req, res);
   }
 
   @Test
   public void singleFilterNoBubbling() throws Exception {
-    EasyMockSupport ems = new EasyMockSupport();
-
-    FilterConfig config = ems.createMock("config", FilterConfig.class);
+    FilterConfig config = mock(FilterConfig.class);
     HttpServletRequest req = new FakeHttpServletRequest();
     HttpServletResponse res = new FakeHttpServletResponse();
 
-    FilterChain chain = ems.createMock("chain", FilterChain.class);
+    FilterChain chain = mock(FilterChain.class);
 
-    AllRequestFilter filter = ems.createStrictMock("filter", AllRequestFilter.class);
-    filter.init(config);
-    filter.doFilter(eq(req), eq(res), anyObject(FilterChain.class));
-    filter.destroy();
-
-    ems.replayAll();
+    AllRequestFilter filter = mock(AllRequestFilter.class);
 
     AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
     addFilter(filter);
@@ -125,63 +115,52 @@
     filterProxy.doFilter(req, res, chain);
     filterProxy.destroy();
 
-    ems.verifyAll();
+    InOrder inorder = inOrder(filter);
+    inorder.verify(filter).init(config);
+    inorder.verify(filter).doFilter(eq(req), eq(res), any(FilterChain.class));
+    inorder.verify(filter).destroy();
   }
 
   @Test
   public void singleFilterBubbling() throws Exception {
-    EasyMockSupport ems = new EasyMockSupport();
-
-    FilterConfig config = ems.createMock(FilterConfig.class);
+    FilterConfig config = mock(FilterConfig.class);
     HttpServletRequest req = new FakeHttpServletRequest();
     HttpServletResponse res = new FakeHttpServletResponse();
 
-    IMocksControl mockControl = ems.createStrictControl();
-    FilterChain chain = mockControl.createMock(FilterChain.class);
+    FilterChain chain = mock(FilterChain.class);
 
-    Capture<FilterChain> capturedChain = new Capture<>();
+    ArgumentCaptor<FilterChain> capturedChain = ArgumentCaptor.forClass(FilterChain.class);
 
-    AllRequestFilter filter = mockControl.createMock(AllRequestFilter.class);
-    filter.init(config);
-    filter.doFilter(eq(req), eq(res), capture(capturedChain));
-    chain.doFilter(req, res);
-    filter.destroy();
-
-    ems.replayAll();
+    AllRequestFilter filter = mock(AllRequestFilter.class);
 
     AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
     addFilter(filter);
 
+    InOrder inorder = inOrder(filter, chain);
+
     filterProxy.init(config);
     filterProxy.doFilter(req, res, chain);
-    capturedChain.getValue().doFilter(req, res);
-    filterProxy.destroy();
 
-    ems.verifyAll();
+    inorder.verify(filter).init(config);
+    inorder.verify(filter).doFilter(eq(req), eq(res), capturedChain.capture());
+    capturedChain.getValue().doFilter(req, res);
+    inorder.verify(chain).doFilter(req, res);
+
+    filterProxy.destroy();
+    inorder.verify(filter).destroy();
   }
 
   @Test
   public void twoFiltersNoBubbling() throws Exception {
-    EasyMockSupport ems = new EasyMockSupport();
-
-    FilterConfig config = ems.createMock(FilterConfig.class);
+    FilterConfig config = mock(FilterConfig.class);
     HttpServletRequest req = new FakeHttpServletRequest();
     HttpServletResponse res = new FakeHttpServletResponse();
 
-    IMocksControl mockControl = ems.createStrictControl();
-    FilterChain chain = mockControl.createMock(FilterChain.class);
+    FilterChain chain = mock(FilterChain.class);
 
-    AllRequestFilter filterA = mockControl.createMock(AllRequestFilter.class);
+    AllRequestFilter filterA = mock(AllRequestFilter.class);
 
-    AllRequestFilter filterB = mockControl.createMock(AllRequestFilter.class);
-    filterA.init(config);
-    filterB.init(config);
-    filterA.doFilter(eq(req), eq(res), anyObject(FilterChain.class));
-    filterA.destroy();
-    filterB.destroy();
-
-    ems.replayAll();
-
+    AllRequestFilter filterB = mock(AllRequestFilter.class);
     AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
     addFilter(filterA);
     addFilter(filterB);
@@ -190,35 +169,27 @@
     filterProxy.doFilter(req, res, chain);
     filterProxy.destroy();
 
-    ems.verifyAll();
+    InOrder inorder = inOrder(filterA, filterB);
+    inorder.verify(filterA).init(config);
+    inorder.verify(filterB).init(config);
+    inorder.verify(filterA).doFilter(eq(req), eq(res), any(FilterChain.class));
+    inorder.verify(filterA).destroy();
+    inorder.verify(filterB).destroy();
   }
 
   @Test
   public void twoFiltersBubbling() throws Exception {
-    EasyMockSupport ems = new EasyMockSupport();
-
-    FilterConfig config = ems.createMock(FilterConfig.class);
+    FilterConfig config = mock(FilterConfig.class);
     HttpServletRequest req = new FakeHttpServletRequest();
     HttpServletResponse res = new FakeHttpServletResponse();
 
-    IMocksControl mockControl = ems.createStrictControl();
-    FilterChain chain = mockControl.createMock(FilterChain.class);
+    FilterChain chain = mock(FilterChain.class);
 
-    Capture<FilterChain> capturedChainA = new Capture<>();
-    Capture<FilterChain> capturedChainB = new Capture<>();
+    ArgumentCaptor<FilterChain> capturedChainA = ArgumentCaptor.forClass(FilterChain.class);
+    ArgumentCaptor<FilterChain> capturedChainB = ArgumentCaptor.forClass(FilterChain.class);
 
-    AllRequestFilter filterA = mockControl.createMock(AllRequestFilter.class);
-    AllRequestFilter filterB = mockControl.createMock(AllRequestFilter.class);
-
-    filterA.init(config);
-    filterB.init(config);
-    filterA.doFilter(eq(req), eq(res), capture(capturedChainA));
-    filterB.doFilter(eq(req), eq(res), capture(capturedChainB));
-    chain.doFilter(req, res);
-    filterA.destroy();
-    filterB.destroy();
-
-    ems.replayAll();
+    AllRequestFilter filterA = mock(AllRequestFilter.class);
+    AllRequestFilter filterB = mock(AllRequestFilter.class);
 
     AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
     addFilter(filterA);
@@ -226,70 +197,69 @@
 
     filterProxy.init(config);
     filterProxy.doFilter(req, res, chain);
-    capturedChainA.getValue().doFilter(req, res);
-    capturedChainB.getValue().doFilter(req, res);
-    filterProxy.destroy();
 
-    ems.verifyAll();
+    InOrder inorder = inOrder(filterA, filterB, chain);
+
+    inorder.verify(filterA).init(config);
+    inorder.verify(filterB).init(config);
+    inorder.verify(filterA).doFilter(eq(req), eq(res), capturedChainA.capture());
+    capturedChainA.getValue().doFilter(req, res);
+    inorder.verify(filterB).doFilter(eq(req), eq(res), capturedChainB.capture());
+    capturedChainB.getValue().doFilter(req, res);
+    inorder.verify(chain).doFilter(req, res);
+
+    filterProxy.destroy();
+    inorder.verify(filterA).destroy();
+    inorder.verify(filterB).destroy();
   }
 
   @Test
   public void postponedLoading() throws Exception {
-    EasyMockSupport ems = new EasyMockSupport();
-
-    FilterConfig config = ems.createMock(FilterConfig.class);
+    FilterConfig config = mock(FilterConfig.class);
     HttpServletRequest req1 = new FakeHttpServletRequest();
     HttpServletRequest req2 = new FakeHttpServletRequest();
     HttpServletResponse res1 = new FakeHttpServletResponse();
     HttpServletResponse res2 = new FakeHttpServletResponse();
 
-    IMocksControl mockControl = ems.createStrictControl();
-    FilterChain chain = mockControl.createMock("chain", FilterChain.class);
+    FilterChain chain = mock(FilterChain.class);
 
-    Capture<FilterChain> capturedChainA1 = new Capture<>();
-    Capture<FilterChain> capturedChainA2 = new Capture<>();
-    Capture<FilterChain> capturedChainB = new Capture<>();
+    ArgumentCaptor<FilterChain> capturedChainA1 = ArgumentCaptor.forClass(FilterChain.class);
+    ArgumentCaptor<FilterChain> capturedChainA2 = ArgumentCaptor.forClass(FilterChain.class);
+    ArgumentCaptor<FilterChain> capturedChainB = ArgumentCaptor.forClass(FilterChain.class);
 
-    AllRequestFilter filterA = mockControl.createMock("filterA", AllRequestFilter.class);
-    AllRequestFilter filterB = mockControl.createMock("filterB", AllRequestFilter.class);
+    AllRequestFilter filterA = mock(AllRequestFilter.class);
+    AllRequestFilter filterB = mock(AllRequestFilter.class);
 
-    filterA.init(config);
-    filterA.doFilter(eq(req1), eq(res1), capture(capturedChainA1));
-    chain.doFilter(req1, res1);
-
-    filterA.doFilter(eq(req2), eq(res2), capture(capturedChainA2));
-    filterB.init(config); // <-- This is crucial part. filterB got loaded
-    // after filterProxy's init finished. Nonetheless filterB gets initialized.
-    filterB.doFilter(eq(req2), eq(res2), capture(capturedChainB));
-    chain.doFilter(req2, res2);
-
-    filterA.destroy();
-    filterB.destroy();
-
-    ems.replayAll();
+    InOrder inorder = inOrder(filterA, filterB, chain);
 
     AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
     addFilter(filterA);
 
     filterProxy.init(config);
     filterProxy.doFilter(req1, res1, chain);
+    inorder.verify(filterA).init(config);
+    inorder.verify(filterA).doFilter(eq(req1), eq(res1), capturedChainA1.capture());
     capturedChainA1.getValue().doFilter(req1, res1);
+    inorder.verify(chain).doFilter(req1, res1);
 
     addFilter(filterB); // <-- Adds filter after filterProxy's init got called.
     filterProxy.doFilter(req2, res2, chain);
+    // after filterProxy's init finished. Nonetheless filterB gets initialized.
+    inorder.verify(filterA).doFilter(eq(req2), eq(res2), capturedChainA2.capture());
     capturedChainA2.getValue().doFilter(req2, res2);
+    inorder.verify(filterB).init(config); // <-- This is crucial part. filterB got loaded
+    inorder.verify(filterB).doFilter(eq(req2), eq(res2), capturedChainB.capture());
     capturedChainB.getValue().doFilter(req2, res2);
+    inorder.verify(chain).doFilter(req2, res2);
 
     filterProxy.destroy();
-
-    ems.verifyAll();
+    inorder.verify(filterA).destroy();
+    inorder.verify(filterB).destroy();
   }
 
   @Test
   public void dynamicUnloading() throws Exception {
-    EasyMockSupport ems = new EasyMockSupport();
-
-    FilterConfig config = ems.createMock(FilterConfig.class);
+    FilterConfig config = mock(FilterConfig.class);
     HttpServletRequest req1 = new FakeHttpServletRequest();
     HttpServletRequest req2 = new FakeHttpServletRequest();
     HttpServletRequest req3 = new FakeHttpServletRequest();
@@ -297,64 +267,62 @@
     HttpServletResponse res2 = new FakeHttpServletResponse();
     HttpServletResponse res3 = new FakeHttpServletResponse();
 
-    Plugin plugin = ems.createMock(Plugin.class);
+    Plugin plugin = mock(Plugin.class);
 
-    IMocksControl mockControl = ems.createStrictControl();
-    FilterChain chain = mockControl.createMock("chain", FilterChain.class);
+    FilterChain chain = mock(FilterChain.class);
 
-    Capture<FilterChain> capturedChainA1 = new Capture<>();
-    Capture<FilterChain> capturedChainB1 = new Capture<>();
-    Capture<FilterChain> capturedChainB2 = new Capture<>();
+    ArgumentCaptor<FilterChain> capturedChainA1 = ArgumentCaptor.forClass(FilterChain.class);
+    ArgumentCaptor<FilterChain> capturedChainB1 = ArgumentCaptor.forClass(FilterChain.class);
+    ArgumentCaptor<FilterChain> capturedChainB2 = ArgumentCaptor.forClass(FilterChain.class);
 
-    AllRequestFilter filterA = mockControl.createMock("filterA", AllRequestFilter.class);
-    AllRequestFilter filterB = mockControl.createMock("filterB", AllRequestFilter.class);
-
-    filterA.init(config);
-    filterB.init(config);
-
-    filterA.doFilter(eq(req1), eq(res1), capture(capturedChainA1));
-    filterB.doFilter(eq(req1), eq(res1), capture(capturedChainB1));
-    chain.doFilter(req1, res1);
-
-    filterA.destroy(); // Cleaning up of filterA after it got unloaded
-
-    filterB.doFilter(eq(req2), eq(res2), capture(capturedChainB2));
-    chain.doFilter(req2, res2);
-
-    filterB.destroy(); // Cleaning up of filterA after it got unloaded
-
-    chain.doFilter(req3, res3);
-
-    ems.replayAll();
+    AllRequestFilter filterA = mock(AllRequestFilter.class);
+    AllRequestFilter filterB = mock(AllRequestFilter.class);
 
     AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
     ReloadableRegistrationHandle<AllRequestFilter> handleFilterA = addFilter(filterA);
     ReloadableRegistrationHandle<AllRequestFilter> handleFilterB = addFilter(filterB);
 
+    InOrder inorder = inOrder(filterA, filterB, chain);
+
     filterProxy.init(config);
 
+    inorder.verify(filterA).init(config);
+    inorder.verify(filterB).init(config);
+
     // Request #1 with filterA and filterB
     filterProxy.doFilter(req1, res1, chain);
+    inorder.verify(filterA).doFilter(eq(req1), eq(res1), capturedChainA1.capture());
     capturedChainA1.getValue().doFilter(req1, res1);
+    inorder.verify(filterB).doFilter(eq(req1), eq(res1), capturedChainB1.capture());
     capturedChainB1.getValue().doFilter(req1, res1);
+    inorder.verify(chain).doFilter(req1, res1);
 
     // Unloading filterA
     handleFilterA.remove();
     filterProxy.onStopPlugin(plugin);
 
-    // Request #1 only with filterB
+    inorder.verify(filterA).destroy(); // Cleaning up of filterA after it got unloaded
+
+    // Request #2 only with filterB
     filterProxy.doFilter(req2, res2, chain);
-    capturedChainA1.getValue().doFilter(req2, res2);
+
+    inorder.verify(filterB).doFilter(eq(req2), eq(res2), capturedChainB2.capture());
+    inorder.verify(filterA, never()).doFilter(eq(req2), eq(res2), any(FilterChain.class));
+    capturedChainB2.getValue().doFilter(req2, res2);
+    inorder.verify(chain).doFilter(req2, res2);
 
     // Unloading filterB
     handleFilterB.remove();
     filterProxy.onStopPlugin(plugin);
 
-    // Request #1 with no additional filters
+    inorder.verify(filterB).destroy(); // Cleaning up of filterA after it got unloaded
+
+    // Request #3 with no additional filters
     filterProxy.doFilter(req3, res3, chain);
+    inorder.verify(chain).doFilter(req3, res3);
+    inorder.verify(filterA, never()).doFilter(eq(req2), eq(res2), any(FilterChain.class));
+    inorder.verify(filterB, never()).doFilter(eq(req2), eq(res2), any(FilterChain.class));
 
     filterProxy.destroy();
-
-    ems.verifyAll();
   }
 }
diff --git a/lib/easymock/BUILD b/lib/easymock/BUILD
deleted file mode 100644
index 90c9673..0000000
--- a/lib/easymock/BUILD
+++ /dev/null
@@ -1,26 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-java_library(
-    name = "easymock",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = ["@easymock//jar"],
-    runtime_deps = [
-        ":cglib-3_2",
-        ":objenesis",
-    ],
-)
-
-java_library(
-    name = "cglib-3_2",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = ["@cglib-3_2//jar"],
-)
-
-java_library(
-    name = "objenesis",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = ["@objenesis//jar"],
-)
diff --git a/lib/mina/BUILD b/lib/mina/BUILD
index 5ad47cd..2f98ee3 100644
--- a/lib/mina/BUILD
+++ b/lib/mina/BUILD
@@ -6,6 +6,7 @@
     visibility = ["//visibility:public"],
     exports = [
         ":eddsa",
+        "@sshd-common//jar",
         "@sshd-mina//jar",
         "@sshd//jar",
     ],
diff --git a/polygerrit-ui/Polymer2.md b/polygerrit-ui/Polymer2.md
new file mode 100644
index 0000000..78112de
--- /dev/null
+++ b/polygerrit-ui/Polymer2.md
@@ -0,0 +1,15 @@
+## Polymer 2 upgrade
+
+Gerrit is updating to use polymer 2 from polymer 1 by following the [Polymer 2.0 upgrade guide](https://polymer-library.polymer-project.org/2.0/docs/upgrade).
+
+Polymer 2 contains several breaking changes that may affect some of the UI features and plugins. One of the biggest change is to have the shadow DOM enabled. This will affect how you query elements inside of your component, how css style works within and across components, and several other usages.
+
+If you are owner of any plugins, please start following the [Polymer 2.0 upgrade guide](https://polymer-library.polymer-project.org/2.0/docs/upgrade) to migrate your plugins to be polymer 2 ready.
+
+If you notice any issues or need help with anything, don't hesitate to report to us [here](https://bugs.chromium.org/p/gerrit/issues/list).
+
+
+### Related resources
+
+- [Polymer 2.0 upgrade guide](https://polymer-library.polymer-project.org/2.0/docs/upgrade)
+- [Polymner Shadow DOM](https://polymer-library.polymer-project.org/2.0/docs/devguide/shadow-dom)
\ No newline at end of file
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
index 8a76d9d..b61b142 100644
--- a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
@@ -54,7 +54,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.BaseUrlBehavior,
         ],
diff --git a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
index 4f16e79..2c513f3 100644
--- a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
@@ -40,7 +40,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'docs-url-behavior-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.DocsUrlBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html b/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
index 15affee..8323ac6 100644
--- a/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
@@ -48,7 +48,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.DomUtilBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
index 817d26e..0d1ee57 100644
--- a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
@@ -40,7 +40,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.AccessBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
index f1e28d1..0285e35 100644
--- a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
@@ -43,7 +43,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.AdminNavBehavior,
         ],
diff --git a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
index 9b7339d..791e2af 100644
--- a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
@@ -50,7 +50,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.ChangeTableBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
index 4c5c899..3d4eca1 100644
--- a/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
@@ -46,7 +46,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element-anon',
-        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.DisplayNameBehavior,
         ],
diff --git a/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html b/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
index 9973ae8..b0b29e1 100644
--- a/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
@@ -42,7 +42,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.ListViewBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
index 2c4b376..173c8d4 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
@@ -53,7 +53,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'tooltip-behavior-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.TooltipBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
index 8601397..73e51d3 100644
--- a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
@@ -42,7 +42,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.URLEncodingBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
index 9d5481d..5a563ab 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
@@ -52,7 +52,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.KeyboardShortcutBehavior],
         keyBindings: {
           k: '_handleKey',
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
index 013ec2e..a77a01f 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
@@ -56,7 +56,6 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
-        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.BaseUrlBehavior,
           Gerrit.RESTClientBehavior,
diff --git a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
index 5d949a5..ab446f1 100644
--- a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
@@ -41,7 +41,6 @@
     suiteSetup(() => {
       Polymer({
         is: 'safe-types-element',
-        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.SafeTypes],
       });
     });
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
index fbde87b..b6f1b74 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
@@ -38,7 +38,6 @@
 
   Polymer({
     is: 'gr-access-section',
-    _legacyUndefinedCheck: true,
 
     properties: {
       capabilities: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
index edf5845..4c099d6 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-admin-group-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
index 5b728eb..d3f020d 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-admin-view',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
index a99c9b1..acc76de 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
@@ -25,7 +25,6 @@
 
   Polymer({
     is: 'gr-confirm-delete-item-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
index 128eb85..5e4d102 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-create-change-dialog',
-    _legacyUndefinedCheck: true,
 
     properties: {
       repoName: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
index ec667ee..01aeb43 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-create-group-dialog',
-    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
index 65bb46d..4e9da90 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
@@ -24,7 +24,6 @@
 
   Polymer({
     is: 'gr-create-pointer-dialog',
-    _legacyUndefinedCheck: true,
 
     properties: {
       detailType: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
index ef7edd4..bb2b5f2 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-create-repo-dialog',
-    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
index 3b41811..8901d4a 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-group-audit-log',
-    _legacyUndefinedCheck: true,
 
     properties: {
       groupId: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
index 915f748..958c0ac 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
@@ -25,7 +25,6 @@
 
   Polymer({
     is: 'gr-group-members',
-    _legacyUndefinedCheck: true,
 
     properties: {
       groupId: Number,
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
index deccab6..68228b4 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
@@ -32,7 +32,6 @@
 
   Polymer({
     is: 'gr-group',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the group name changes.
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
index 9656eee..b77f078 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-plugin-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
index 5459749..94bef54 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
@@ -70,7 +70,6 @@
 
   Polymer({
     is: 'gr-repo-access',
-    _legacyUndefinedCheck: true,
 
     properties: {
       repo: {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
index 04d9781..026c990 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-repo-command',
-    _legacyUndefinedCheck: true,
 
     properties: {
       title: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
index b6f7039..92f15b0 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
@@ -28,7 +28,6 @@
 
   Polymer({
     is: 'gr-repo-commands',
-    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
index 6b20289..6e38566 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-repo-dashboards',
-    _legacyUndefinedCheck: true,
 
     properties: {
       repo: {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
index e50c498..da8ef52 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
@@ -26,7 +26,6 @@
 
   Polymer({
     is: 'gr-repo-detail-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
index 6b46cef..e0b054b 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-repo-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
index 37eff2c..918efba 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
@@ -53,7 +53,6 @@
 
   Polymer({
     is: 'gr-repo',
-    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
index 9cf2ad6..8eb39891 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
@@ -26,7 +26,6 @@
 
   Polymer({
     is: 'gr-change-list-item',
-    _legacyUndefinedCheck: true,
 
     properties: {
       visibleChangeTableColumns: Array,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
index 455019f..02e9a04 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
@@ -31,7 +31,6 @@
 
   Polymer({
     is: 'gr-change-list-view',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index 8500e3b..a02005e 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -24,7 +24,6 @@
 
   Polymer({
     is: 'gr-change-list',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when next page key shortcut was pressed.
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
index b9df583b..19e7a25 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-create-change-help',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the "Create change" button is tapped.
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
index e4958c5..0e71f1c 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
@@ -25,7 +25,6 @@
 
   Polymer({
     is: 'gr-create-commands-dialog',
-    _legacyUndefinedCheck: true,
     properties: {
       branch: String,
       _createNewCommitCommand: {
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
index ed87e9e..4d2802e 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
@@ -26,7 +26,6 @@
 
   Polymer({
     is: 'gr-create-destination-dialog',
-    _legacyUndefinedCheck: true,
     properties: {
       _repo: String,
       _branch: String,
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index 6616dab..df51d49 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-dashboard-view',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
@@ -145,12 +144,6 @@
         return Promise.resolve();
       }
 
-      const user = params.user || 'self';
-
-      // NOTE: This method may be called before attachment. Fire title-change
-      // in an async so that attachment to the DOM can take place first.
-      const title = params.title || this._computeTitle(user);
-      this.async(() => this.fire('title-change', {title}));
       return this._reload();
     },
 
@@ -171,11 +164,19 @@
 
       const checkForNewUser = !project && user === 'self';
       return dashboardPromise
-          .then(res => this._fetchDashboardChanges(res, checkForNewUser))
+          .then(res => {
+            if (res && res.title) {
+              this.fire('title-change', {title: res.title});
+            }
+            return this._fetchDashboardChanges(res, checkForNewUser);
+          })
           .then(() => {
             this._maybeShowDraftsBanner();
             this.$.reporting.dashboardDisplayed();
           }).catch(err => {
+            this.fire('title-change', {
+              title: title || this._computeTitle(user),
+            });
             console.warn(err);
           }).then(() => { this._loading = false; });
     },
@@ -197,7 +198,7 @@
               section.query);
 
       if (checkForNewUser) {
-        queries.push('owner:self');
+        queries.push('owner:self limit:1');
       }
 
       return this.$.restAPI.getChanges(null, queries, null, this.options)
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
index 0f0a7ec..9f3e675 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
@@ -184,7 +184,7 @@
         return paramsChangedPromise.then(() => {
           assert.isTrue(
               getChangesStub.calledWith(
-                  null, ['1', '2', 'owner:self'], null, element.options));
+                  null, ['1', '2', 'owner:self limit:1'], null, element.options));
         });
       });
 
diff --git a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
index 14d0cb0..e31f9ad 100644
--- a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
+++ b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-embed-dashboard',
-    _legacyUndefinedCheck: true,
     properties: {
       account: Object,
       sections: Array,
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
index 67fbd97..cd6eb77 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-repo-header',
-    _legacyUndefinedCheck: true,
     properties: {
       /** @type {?String} */
       repo: {
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
index 93464b7..758b4de 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-user-header',
-    _legacyUndefinedCheck: true,
     properties: {
       /** @type {?String} */
       userId: {
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 54e2edd..5a812ba 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -193,7 +193,6 @@
 
   Polymer({
     is: 'gr-change-actions',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the change should be reloaded.
@@ -269,6 +268,7 @@
       /** @type {?} */
       revisionActions: {
         type: Object,
+        notify: true,
         value() { return {}; },
       },
       // If property binds directly to [[revisionActions.submit]] it is not
@@ -461,7 +461,7 @@
       return this._getRevisionActions().then(revisionActions => {
         if (!revisionActions) { return; }
 
-        this.revisionActions = revisionActions;
+        this.revisionActions = this._updateRebaseAction(revisionActions);
         this._handleLoadingComplete();
       }).catch(err => {
         this.fire('show-alert', {message: ERR_REVISION_ACTIONS});
@@ -474,6 +474,18 @@
       Gerrit.awaitPluginsLoaded().then(() => this._loading = false);
     },
 
+    _updateRebaseAction(revisionActions) {
+      if (revisionActions && revisionActions.rebase) {
+        revisionActions.rebase.rebaseOnCurrent =
+            !!revisionActions.rebase.enabled;
+        this._parentIsCurrent = !revisionActions.rebase.enabled;
+        revisionActions.rebase.enabled = true;
+      } else {
+        this._parentIsCurrent = true;
+      }
+      return revisionActions;
+    },
+
     _changeChanged() {
       this.reload();
     },
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index b88e06b..37201ac 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -376,6 +376,7 @@
         };
         assert.isTrue(fetchChangesStub.called);
         element._handleRebaseConfirm({detail: {base: '1234'}});
+        rebaseAction.rebaseOnCurrent = true;
         assert.deepEqual(fireActionStub.lastCall.args,
           ['/rebase', rebaseAction, true, {base: '1234'}]);
         done();
@@ -1558,5 +1559,58 @@
       assert.strictEqual(element.$.confirmSubmitDialog.action, null);
       assert.strictEqual(element.$.confirmRebase.rebaseOnCurrent, null);
     });
+
+    test('_updateRebaseAction sets _parentIsCurrent on no rebase', () => {
+      const currentRevisionActions = {
+        cherrypick: {
+          enabled: true,
+          label: 'Cherry Pick',
+          method: 'POST',
+          title: 'cherrypick',
+        },
+      };
+      element._parentIsCurrent = undefined;
+      element._updateRebaseAction(currentRevisionActions);
+      assert.isTrue(element._parentIsCurrent);
+    });
+
+    test('_updateRebaseAction', () => {
+      const currentRevisionActions = {
+        cherrypick: {
+          enabled: true,
+          label: 'Cherry Pick',
+          method: 'POST',
+          title: 'cherrypick',
+        },
+        rebase: {
+          enabled: true,
+          label: 'Rebase',
+          method: 'POST',
+          title: 'Rebase onto tip of branch or parent change',
+        },
+      };
+      element._parentIsCurrent = undefined;
+
+      // Rebase enabled should always end up true.
+      // When rebase is enabled initially, rebaseOnCurrent should be set to
+      // true.
+      assert.equal(element._updateRebaseAction(currentRevisionActions),
+          currentRevisionActions);
+
+      assert.isTrue(currentRevisionActions.rebase.enabled);
+      assert.isTrue(currentRevisionActions.rebase.rebaseOnCurrent);
+      assert.isFalse(element._parentIsCurrent);
+
+      delete currentRevisionActions.rebase.enabled;
+
+      // When rebase is not enabled initially, rebaseOnCurrent should be set to
+      // false.
+      assert.equal(element._updateRebaseAction(currentRevisionActions),
+          currentRevisionActions);
+
+      assert.isTrue(currentRevisionActions.rebase.enabled);
+      assert.isFalse(currentRevisionActions.rebase.rebaseOnCurrent);
+      assert.isTrue(element._parentIsCurrent);
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index dbb41f6..b837c62 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -61,7 +61,6 @@
 
   Polymer({
     is: 'gr-change-metadata',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the change topic is changed.
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
index 09efa8d..8ec00dd 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-change-requirements',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
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 cb72b32..e8297af 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
@@ -408,7 +408,7 @@
               disable-edit="[[disableEdit]]"
               has-parent="[[hasParent]]"
               actions="[[_change.actions]]"
-              revision-actions="[[_currentRevisionActions]]"
+              revision-actions="{{_currentRevisionActions}}"
               change-num="[[_changeNum]]"
               change-status="[[_change.status]]"
               commit-num="[[_commitInfo.commit]]"
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 301bdbd..535dc05 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -65,7 +65,6 @@
 
   Polymer({
     is: 'gr-change-view',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
@@ -227,7 +226,8 @@
       },
       _changeStatuses: {
         type: String,
-        computed: '_computeChangeStatusChips(_change, _mergeable)',
+        computed:
+          '_computeChangeStatusChips(_change, _mergeable, _submitEnabled)',
       },
       _commitCollapsed: {
         type: Boolean,
@@ -249,7 +249,10 @@
         observer: '_updateToggleContainerClass',
       },
       _parentIsCurrent: Boolean,
-      _submitEnabled: Boolean,
+      _submitEnabled: {
+        type: Boolean,
+        computed: '_isSubmitEnabled(_currentRevisionActions)',
+      },
 
       /** @type {?} */
       _mergeable: {
@@ -464,7 +467,7 @@
       this._editingCommitMessage = false;
     },
 
-    _computeChangeStatusChips(change, mergeable) {
+    _computeChangeStatusChips(change, mergeable, submitEnabled) {
       // Polymer 2: check for undefined
       if ([
         change,
@@ -483,7 +486,7 @@
       const options = {
         includeDerived: true,
         mergeable: !!mergeable,
-        submitEnabled: this._submitEnabled,
+        submitEnabled: !!submitEnabled,
       };
       return this.changeStatuses(change, options);
     },
@@ -1208,18 +1211,6 @@
       return this.$.restAPI.getPreferences();
     },
 
-    _updateRebaseAction(revisionActions) {
-      if (revisionActions && revisionActions.rebase) {
-        revisionActions.rebase.rebaseOnCurrent =
-            !!revisionActions.rebase.enabled;
-        this._parentIsCurrent = !revisionActions.rebase.enabled;
-        revisionActions.rebase.enabled = true;
-      } else {
-        this._parentIsCurrent = true;
-      }
-      return revisionActions;
-    },
-
     _prepareCommitMsgForLinkify(msg) {
       // TODO(wyatta) switch linkify sequence, see issue 5526.
       // This is a zero-with space. It is added to prevent the linkify library
@@ -1285,8 +1276,6 @@
               this._latestCommitMessage = null;
             }
 
-            // Update the submit enabled based on current revision.
-            this._submitEnabled = this._isSubmitEnabled(currentRevision);
 
             const lineHeight = getComputedStyle(this).lineHeight;
 
@@ -1303,8 +1292,6 @@
                 currentRevision.commit.commit = latestRevisionSha;
               }
               this._commitInfo = currentRevision.commit;
-              this._currentRevisionActions =
-                      this._updateRebaseAction(currentRevision.actions);
               this._selectedRevision = currentRevision;
               // TODO: Fetch and process files.
             } else {
@@ -1316,9 +1303,9 @@
           });
     },
 
-    _isSubmitEnabled(currentRevision) {
-      return !!(currentRevision.actions && currentRevision.actions.submit &&
-          currentRevision.actions.submit.enabled);
+    _isSubmitEnabled(revisionActions) {
+      return !!(revisionActions && revisionActions.submit &&
+        revisionActions.submit.enabled);
     },
 
     _getEdit() {
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index eef4dbf..2267d27 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -530,65 +530,11 @@
       assert.equal(result, 'CC=\u200Btest@google.com');
     }),
 
-    test('_updateRebaseAction', () => {
-      const currentRevisionActions = {
-        cherrypick: {
-          enabled: true,
-          label: 'Cherry Pick',
-          method: 'POST',
-          title: 'cherrypick',
-        },
-        rebase: {
-          enabled: true,
-          label: 'Rebase',
-          method: 'POST',
-          title: 'Rebase onto tip of branch or parent change',
-        },
-      };
-      element._parentIsCurrent = undefined;
-
-      // Rebase enabled should always end up true.
-      // When rebase is enabled initially, rebaseOnCurrent should be set to
-      // true.
-      assert.equal(element._updateRebaseAction(currentRevisionActions),
-          currentRevisionActions);
-
-      assert.isTrue(currentRevisionActions.rebase.enabled);
-      assert.isTrue(currentRevisionActions.rebase.rebaseOnCurrent);
-      assert.isFalse(element._parentIsCurrent);
-
-      delete currentRevisionActions.rebase.enabled;
-
-      // When rebase is not enabled initially, rebaseOnCurrent should be set to
-      // false.
-      assert.equal(element._updateRebaseAction(currentRevisionActions),
-          currentRevisionActions);
-
-      assert.isTrue(currentRevisionActions.rebase.enabled);
-      assert.isFalse(currentRevisionActions.rebase.rebaseOnCurrent);
-      assert.isTrue(element._parentIsCurrent);
-    });
-
     test('_isSubmitEnabled', () => {
       assert.isFalse(element._isSubmitEnabled({}));
-      assert.isFalse(element._isSubmitEnabled({actions: {}}));
-      assert.isFalse(element._isSubmitEnabled({actions: {submit: {}}}));
+      assert.isFalse(element._isSubmitEnabled({submit: {}}));
       assert.isTrue(element._isSubmitEnabled(
-          {actions: {submit: {enabled: true}}}));
-    });
-
-    test('_updateRebaseAction sets _parentIsCurrent on no rebase', () => {
-      const currentRevisionActions = {
-        cherrypick: {
-          enabled: true,
-          label: 'Cherry Pick',
-          method: 'POST',
-          title: 'cherrypick',
-        },
-      };
-      element._parentIsCurrent = undefined;
-      element._updateRebaseAction(currentRevisionActions);
-      assert.isTrue(element._parentIsCurrent);
+          {submit: {enabled: true}}));
     });
 
     test('_reload is called when an approved label is removed', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
index 660dfd9..cbc7e42 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
@@ -18,7 +18,6 @@
   'use strict';
   Polymer({
     is: 'gr-comment-list',
-    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
index 7e25180..e2fcdff 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-commit-info',
-    _legacyUndefinedCheck: true,
 
     properties: {
       change: Object,
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
index f83ea8f..524876e 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-confirm-abandon-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
index 195027a..a0da331 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-confirm-cherrypick-conflict-dialog',
-    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.FireBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
index 6159742..bf84003 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-confirm-cherrypick-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
index 3636c9e..23123c3 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-confirm-move-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
index a2fef69..414182c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-confirm-rebase-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
index c064d14..662b64c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-confirm-revert-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
index 6b4a886..e86e21c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-confirm-submit-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
index ddac3be..bec1b01 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-download-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
index 870be8b..2126433 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
@@ -23,7 +23,6 @@
 
   Polymer({
     is: 'gr-file-list-header',
-    _legacyUndefinedCheck: true,
 
     /**
      * @event expand-diffs
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 9e6997c..00ad793 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -63,7 +63,6 @@
 
   Polymer({
     is: 'gr-file-list',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a draft refresh should get triggered
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
index 2b7dcb6..4b8ce22 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-included-in-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
index 9c3d69a..d3ab28c 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-label-score-row',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when any label is changed.
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
index 548798f..bfff9a0 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-label-scores',
-    _legacyUndefinedCheck: true,
     properties: {
       _labels: {
         type: Array,
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index 4072508..e3411e6 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-message',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when this message's reply link is tapped.
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
index 80115c6..95d8261 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -27,7 +27,6 @@
 
   Polymer({
     is: 'gr-messages-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       changeNum: Number,
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
index aa56550..037f3e6 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-related-changes-list',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a new section is loaded so that the change view can determine
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index 73b12f7..a78e68f 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -48,17 +48,12 @@
     SEND: 'Send reply',
   };
 
-  // TODO(logan): Remove once the fix for issue 6841 is stable on
-  // googlesource.com.
-  const START_REVIEW_MESSAGE = 'This change is ready for review.';
-
   const EMPTY_REPLY_MESSAGE = 'Cannot send an empty reply.';
 
   const SEND_REPLY_TIMING_LABEL = 'SendReply';
 
   Polymer({
     is: 'gr-reply-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a reply is successfully sent.
@@ -220,10 +215,6 @@
 
     FocusTarget,
 
-    // TODO(logan): Remove once the fix for issue 6841 is stable on
-    // googlesource.com.
-    START_REVIEW_MESSAGE,
-
     behaviors: [
       Gerrit.BaseUrlBehavior,
       Gerrit.FireBehavior,
@@ -468,13 +459,6 @@
 
       this.disabled = true;
 
-      if (obj.ready && !obj.message) {
-        // TODO(logan): The server currently doesn't send email in this case.
-        // Insert a dummy message to force an email to be sent. Remove this
-        // once the fix for issue 6841 is stable on googlesource.com.
-        obj.message = START_REVIEW_MESSAGE;
-      }
-
       const errFn = this._handle400Error.bind(this);
       return this._saveReview(obj, errFn).then(response => {
         if (!response) {
@@ -487,18 +471,10 @@
           return {};
         }
 
-        // TODO(logan): Remove once the required API changes are live and stable
-        // on googlesource.com.
-        return this._maybeSetReady(startReview, response).catch(err => {
-          // We catch error here because we still want to treat this as a
-          // successful review.
-          console.error('error setting ready:', err);
-        }).then(() => {
-          this.draft = '';
-          this._includeComments = true;
-          this.fire('send', null, {bubbles: false});
-          return accountAdditions;
-        });
+        this.draft = '';
+        this._includeComments = true;
+        this.fire('send', null, {bubbles: false});
+        return accountAdditions;
       }).then(result => {
         this.disabled = false;
         return result;
@@ -508,32 +484,6 @@
       });
     },
 
-    /**
-     * Returns a promise resolving to true if review was successfully posted,
-     * false otherwise.
-     *
-     * TODO(logan): Remove this once the required API changes are live and
-     * stable on googlesource.com.
-     */
-    _maybeSetReady(startReview, response) {
-      return this.$.restAPI.getResponseObject(response).then(result => {
-        if (!startReview || result.ready) {
-          return Promise.resolve();
-        }
-        // We don't have confirmation that review was started, so attempt to
-        // start review explicitly.
-        return this.$.restAPI.startReview(
-            this.change._number, null, response => {
-              // If we see a 409 response code, then that means the server
-              // *does* support moving from WIP->ready when posting a
-              // review. Only alert user for non-409 failures.
-              if (response.status !== 409) {
-                this.fire('server-error', {response});
-              }
-            });
-      });
-    },
-
     _focusOn(section) {
       // Safeguard- always want to focus on something.
       if (!section || section === FocusTarget.ANY) {
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index 6b738d8..99b91b7 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -989,21 +989,6 @@
           assert.isFalse(startReviewStub.called);
         });
       });
-
-      test('fall back to start review against old backend', () => {
-        stubSaveReview(review => {
-          return {}; // old backend won't set ready: true
-        });
-
-        return element.send(true, true).then(() => {
-          assert.isTrue(startReviewStub.called);
-        }).then(() => {
-          startReviewStub.reset();
-          return element.send(true, false);
-        }).then(() => {
-          assert.isFalse(startReviewStub.called);
-        });
-      });
     });
 
     suite('start review and save buttons', () => {
@@ -1029,28 +1014,9 @@
       });
     });
 
-    test('dummy message to force email on start review', () => {
-      stubSaveReview(review => {
-        assert.equal(review.message, element.START_REVIEW_MESSAGE);
-        return {ready: true};
-      });
-      return element.send(true, true);
-    });
-
     test('buttons disabled until all API calls are resolved', () => {
       stubSaveReview(review => {
-        return {}; // old backend won't set ready: true
-      });
-      // Check that element is disabled asynchronously after the setReady
-      // promise is returned. The element should not be reenabled until
-      // that promise is resolved.
-      sandbox.stub(element, '_maybeSetReady', (startReview, response) => {
-        return new Promise(resolve => {
-          Polymer.Base.async(() => {
-            assert.isTrue(element.disabled);
-            resolve();
-          });
-        });
+        return {ready: true};
       });
       return element.send(true, true).then(() => {
         assert.isFalse(element.disabled);
@@ -1070,11 +1036,6 @@
         assert.isFalse(element.disabled);
       }
 
-      function assertDialogClosed() {
-        assert.strictEqual('', element.draft);
-        assert.isFalse(element.disabled);
-      }
-
       test('error occurs in _saveReview', () => {
         stubSaveReview(review => {
           throw expectedError;
@@ -1085,46 +1046,6 @@
         });
       });
 
-      test('error occurs during startReview', () => {
-        stubSaveReview(review => {
-          return {}; // old backend won't set ready: true
-        });
-        const errorStub = sandbox.stub(
-            console, 'error', (msg, err) => undefined);
-        sandbox.stub(element.$.restAPI, 'startReview', () => {
-          throw expectedError;
-        });
-        return element.send(true, true).then(() => {
-          assertDialogClosed();
-          assert.isTrue(
-              errorStub.calledWith('error setting ready:', expectedError));
-        });
-      });
-
-      test('non-ok response received by startReview', () => {
-        stubSaveReview(review => {
-          return {}; // old backend won't set ready: true
-        });
-        sandbox.stub(element.$.restAPI, 'startReview', (c, b, f) => {
-          f({status: 500});
-        });
-        return element.send(true, true).then(() => {
-          assertDialogClosed();
-        });
-      });
-
-      test('409 response received by startReview', () => {
-        stubSaveReview(review => {
-          return {}; // old backend won't set ready: true
-        });
-        sandbox.stub(element.$.restAPI, 'startReview', (c, b, f) => {
-          f({status: 409});
-        });
-        return element.send(true, true).then(() => {
-          assertDialogClosed();
-        });
-      });
-
       suite('pending diff drafts?', () => {
         test('yes', () => {
           const promise = mockPromise();
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
index 42bcd09..86e76b4 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-reviewer-list',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the "Add reviewer..." button is tapped.
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
index 407ef0f..0cbec84 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
@@ -25,7 +25,6 @@
 
   Polymer({
     is: 'gr-thread-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
index 59eb805..092204a 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
@@ -29,7 +29,6 @@
 
   Polymer({
     is: 'gr-upload-help-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
index f8288c6..7cbe988 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-account-dropdown',
-    _legacyUndefinedCheck: true,
 
     properties: {
       account: Object,
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
index 5679408..8d3b58e 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-error-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the dismiss button is pressed.
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index 54cfe19..5865e3c 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -27,7 +27,6 @@
 
   Polymer({
     is: 'gr-error-manager',
-    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
index e8c6479..89d1091 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-key-binding-display',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {Array<string>} */
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
index 391ec92..4bc6e11 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-keyboard-shortcuts-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index 661cd21..773ad68 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -71,7 +71,6 @@
 
   Polymer({
     is: 'gr-main-header',
-    _legacyUndefinedCheck: true,
 
     hostAttributes: {
       role: 'banner',
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 9ff425c..7c5bacf 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -146,7 +146,6 @@
   // eslint-disable-next-line prefer-const
   let GrReporting = Polymer({
     is: 'gr-reporting',
-    _legacyUndefinedCheck: true,
 
     properties: {
       category: String,
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index 8c57748..1b7103f 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -209,7 +209,6 @@
 
   Polymer({
     is: 'gr-router',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _app: {
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index 5b9776b..6b2a191 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -104,7 +104,6 @@
 
   Polymer({
     is: 'gr-search-bar',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a search is committed
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
index 017310d..94de00c 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
@@ -23,7 +23,6 @@
 
   Polymer({
     is: 'gr-smart-search',
-    _legacyUndefinedCheck: true,
 
     properties: {
       searchQuery: String,
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
index 7bf71f5..b7994e6 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'comment-api-mock',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _changeComments: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
index b5ff9a3..4b64f7b 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
@@ -482,7 +482,6 @@
 
   Polymer({
     is: 'gr-comment-api',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _changeComments: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index f29a5da..c5a622a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -75,7 +75,6 @@
 
       Polymer({
         is: 'gr-diff-builder',
-        _legacyUndefinedCheck: true,
 
         /**
          * Fired when the diff begins rendering.
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
index e3a4821..5ea6066 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
@@ -37,7 +37,6 @@
 
   Polymer({
     is: 'gr-diff-cursor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
index 1f6b372..5472489 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-diff-highlight',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {!Array<!Gerrit.HoveredRange>} */
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
index 79135a7..5faf77e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
@@ -66,7 +66,6 @@
    */
   Polymer({
     is: 'gr-diff-host',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user selects a line.
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
index e2d6a28..88dd91a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-diff-mode-selector',
-    _legacyUndefinedCheck: true,
 
     properties: {
       mode: {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
index f6f48b3..6ecd4d6 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-diff-preferences-dialog',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
index d4b4e2b..87ae7f2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
@@ -104,7 +104,6 @@
    */
   Polymer({
     is: 'gr-diff-processor',
-    _legacyUndefinedCheck: true,
 
     properties: {
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
index 9cd0527..4a0a443 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
@@ -32,7 +32,6 @@
 
   Polymer({
     is: 'gr-diff-selection',
-    _legacyUndefinedCheck: true,
 
     properties: {
       diff: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index eb38ba7..3a66991 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -35,7 +35,6 @@
 
   Polymer({
     is: 'gr-diff-view',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 16c92b1..fb68b7b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -109,7 +109,6 @@
 
   Polymer({
     is: 'gr-diff',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user selects a line.
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
index a9fde2b..4008b7e 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
@@ -31,7 +31,6 @@
 
   Polymer({
     is: 'gr-patch-range-select',
-    _legacyUndefinedCheck: true,
 
     properties: {
       availablePatches: Array,
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
index 0769104..1e952a3 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
@@ -28,7 +28,6 @@
 
   Polymer({
     is: 'gr-ranged-comment-layer',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the range in a range comment was malformed and had to be
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
index 44bf6fb..b1b3e0f 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-selection-action-box',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the comment creation action was taken (hotkey, click).
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
index 627a279..7fb0a7c 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
@@ -129,7 +129,6 @@
 
   Polymer({
     is: 'gr-syntax-layer',
-    _legacyUndefinedCheck: true,
 
     properties: {
       diff: {
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
index 995326f..f850b9d 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-documentation-search',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
index bae838e..ed96bb2 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-default-editor',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the content of the editor changes.
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
index 35b78ca..9265a5d 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-edit-controls',
-    _legacyUndefinedCheck: true,
     properties: {
       change: Object,
       patchNum: String,
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
index 9c59a9a..250816b 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-edit-file-controls',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when an action in the overflow menu is tapped.
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
index 08dcf51..95cfaf6 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
@@ -26,7 +26,6 @@
 
   Polymer({
     is: 'gr-editor-view',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
index 5692969..6e423cd 100644
--- a/polygerrit-ui/app/elements/gr-app-element.js
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-app-element',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the URL location changes.
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
index efd6104..0c4149c 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
@@ -32,7 +32,6 @@
   <script>
     Polymer({
       is: 'some-element',
-      _legacyUndefinedCheck: true,
       properties: {
         fooBar: {
           type: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
index e0fa61e..230be0e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
+++ b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
@@ -54,7 +54,6 @@
   GrDomHook.prototype._createPlaceholder = function(hookName) {
     Polymer({
       is: hookName,
-      _legacyUndefinedCheck: true,
       properties: {
         plugin: Object,
         content: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
index f953b1d..c609e20 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-endpoint-decorator',
-    _legacyUndefinedCheck: true,
 
     properties: {
       name: String,
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
index feef57d..89e98b3 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-endpoint-param',
-    _legacyUndefinedCheck: true,
     properties: {
       name: String,
       value: {
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
index 896238f..f6e4628 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
@@ -32,8 +32,6 @@
   <script>
     Polymer({
       is: 'some-element',
-      _legacyUndefinedCheck: true,
-
       properties: {
         fooBar: {
           type: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
index f865e77..e90ff30 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-external-style',
-    _legacyUndefinedCheck: true,
 
     properties: {
       name: String,
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
index a99f23c..bdc7652 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-plugin-host',
-    _legacyUndefinedCheck: true,
 
     properties: {
       config: {
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
index 3ef93e4..dd37f84 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
@@ -18,7 +18,6 @@
   'use strict';
   Polymer({
     is: 'gr-plugin-popup',
-    _legacyUndefinedCheck: true,
     get opened() {
       return this.$.overlay.opened;
     },
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
index 2fd4534..593c1e0 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
@@ -25,7 +25,6 @@
   <script>
     Polymer({
       is: 'gr-plugin-repo-command',
-      _legacyUndefinedCheck: true,
       properties: {
         title: String,
         repoName: String,
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
index 97966ed..75aeb64 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
@@ -37,7 +37,6 @@
   <script>
     Polymer({
       is: 'gr-custom-plugin-header',
-      _legacyUndefinedCheck: true,
       properties: {
         logoUrl: String,
         title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
index 246662d..b0c7661 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-account-info',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when account details are changed.
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
index fe36a86..41595a9 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-agreements-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _agreements: Array,
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
index 3909c99..43afdf8 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-change-table-editor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       displayedColumns: {
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
index 11a0f1a..3eb1a22 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-cla-view',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _groups: Object,
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
index 37bce08..86350f9 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-edit-preferences',
-    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
index 71d75cc..d08cc90 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-email-editor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
index 7348067..78025d1 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-gpg-editor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
index 9531690..d62a241 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-group-list',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _groups: Array,
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
index 99f4504..003e471 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-http-password',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _username: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
index 4560a2e..c927f1e 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
@@ -24,7 +24,6 @@
 
   Polymer({
     is: 'gr-identities',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _identities: Object,
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
index 8587338..aa83bf7 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-menu-editor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       menuItems: Array,
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
index 3ce226f..0633416 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-registration-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when account details are changed.
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
index dc1aa93..0ee1b28 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-settings-item',
-    _legacyUndefinedCheck: true,
     properties: {
       anchor: String,
       title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
index 2a56b09..38147cd 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-settings-menu-item',
-    _legacyUndefinedCheck: true,
     properties: {
       href: String,
       title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
index 7f0a754..6c8762d 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
@@ -47,7 +47,6 @@
 
   Polymer({
     is: 'gr-settings-view',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
index 4c423e8..874173a 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-ssh-editor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
index bd18456..e751ea3 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
@@ -27,7 +27,6 @@
 
   Polymer({
     is: 'gr-watched-projects-editor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
index 0e85599..10c876d 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
@@ -20,7 +20,6 @@
 
   Polymer({
     is: 'gr-account-chip',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired to indicate a key was pressed while this chip was focused.
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js
index 63bd252..92836a8 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js
@@ -23,7 +23,6 @@
    */
   Polymer({
     is: 'gr-account-entry',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when an account is entered.
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
index a778e8b..418d2ea 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-account-label',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
index 03967f1..faaf9c3 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-account-link',
-    _legacyUndefinedCheck: true,
 
     properties: {
       additionalText: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
index 1be55d2..6d364c9 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
@@ -39,7 +39,6 @@
 
   Polymer({
     is: 'gr-account-list',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when user inputs an invalid email address.
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
index ec7b6eb..e7c8b2c 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-alert',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the action button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
index af0b637..17e1b1c 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-autocomplete-dropdown',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the dropdown is closed.
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index 745cff1..57e8df0 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-autocomplete',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a value is chosen.
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
index 2435e58..bf56382 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-avatar',
-    _legacyUndefinedCheck: true,
 
     properties: {
       account: {
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
index 5988cde..ca6705e 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-button',
-    _legacyUndefinedCheck: true,
 
     properties: {
       tooltip: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
index 98fd94a..a83bc2b 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-change-star',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when star state is toggled.
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
index 70c5d72..3afbe54 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
@@ -34,7 +34,6 @@
 
   Polymer({
     is: 'gr-change-status',
-    _legacyUndefinedCheck: true,
 
     properties: {
       flat: {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
index 615ce3b..093667c 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-comment-thread',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the thread should be discarded.
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
index cf2f57d..2090345 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -33,7 +33,6 @@
 
   Polymer({
     is: 'gr-comment',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the create fix comment action is triggered.
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
index b441ba8..c2075f0 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-confirm-delete-comment-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
index 550f1df..cabee36 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-copy-clipboard',
-    _legacyUndefinedCheck: true,
 
     properties: {
       text: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
index 2c38c2a..be40ce6 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
@@ -24,7 +24,6 @@
 
   Polymer({
     is: 'gr-cursor-manager',
-    _legacyUndefinedCheck: true,
 
     properties: {
       stops: {
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
index a39e170..0360145 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
@@ -33,7 +33,6 @@
 
   Polymer({
     is: 'gr-date-formatter',
-    _legacyUndefinedCheck: true,
 
     properties: {
       dateStr: {
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
index e0a1947..68dc537 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-dialog',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
index 89c3d74..36fdf5b 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-diff-preferences',
-    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
index ed7c2cc..ca77a30 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-download-commands',
-    _legacyUndefinedCheck: true,
     properties: {
       commands: Array,
       _loggedIn: {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
index d87afed..89a0725 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
@@ -47,7 +47,6 @@
 
   Polymer({
     is: 'gr-dropdown-list',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the selected value changes
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
index 6eb3108..969272f 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-dropdown',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a non-link dropdown item with the given ID is tapped.
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
index 29c2f6e..1e339dc 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-editable-content',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the save button is pressed.
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 f21605d..1a5356f 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
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-editable-label',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the value is changed.
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
index 87cd4b4..2c32709 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-fixed-panel',
-    _legacyUndefinedCheck: true,
 
     properties: {
       floatingDisabled: Boolean,
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
index 8836574..4e68d42 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-formatted-text',
-    _legacyUndefinedCheck: true,
 
     properties: {
       content: {
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
index 498c590..f9c1da1 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
@@ -27,7 +27,6 @@
 
   Polymer({
     is: 'gr-hovercard',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
index f907ad6..1bbb832 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
@@ -38,7 +38,6 @@
 
   Polymer({
     is: 'gr-js-api-interface',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _elements: {
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
index 2036f2a..40edc28 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-label-info',
-    _legacyUndefinedCheck: true,
 
     properties: {
       labelInfo: Object,
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
index c437885..0de0881 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-label',
-    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.TooltipBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
index a892522..54b38eb 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-labeled-autocomplete',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a value is chosen.
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
index adc8619..08e668b 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-lib-loader',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _hljsState: {
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
index db13942..048e4f5 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
@@ -26,7 +26,6 @@
 
   Polymer({
     is: 'gr-limited-text',
-    _legacyUndefinedCheck: true,
 
     properties: {
       /** The un-truncated text to display. */
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
index ee8653f..33a9c25 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-linked-chip',
-    _legacyUndefinedCheck: true,
 
     properties: {
       href: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
index b1bd5cd..530da02 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-linked-text',
-    _legacyUndefinedCheck: true,
 
     properties: {
       removeZeroWidthSpace: Boolean,
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
index d70d753..8d81030 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
@@ -21,7 +21,6 @@
 
   Polymer({
     is: 'gr-list-view',
-    _legacyUndefinedCheck: true,
 
     properties: {
       createNew: Boolean,
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
index bba5a46..8623458 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
@@ -23,7 +23,6 @@
 
   Polymer({
     is: 'gr-overlay',
-    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a fullscreen overlay is closed
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
index 0962540..9ccff600 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-page-nav',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _headerHeight: Number,
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
index 2fccc8d..e2298c3 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
@@ -22,7 +22,6 @@
 
   Polymer({
     is: 'gr-repo-branch-picker',
-    _legacyUndefinedCheck: true,
 
     properties: {
       repo: {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index b63acda..a8fec80 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -91,7 +91,8 @@
   };
   const JSON_PREFIX = ')]}\'';
   const MAX_PROJECT_RESULTS = 25;
-  const MAX_UNIFIED_DEFAULT_WINDOW_WIDTH_PX = 900;
+  // This value is somewhat arbitrary and not based on research or calculations.
+  const MAX_UNIFIED_DEFAULT_WINDOW_WIDTH_PX = 850;
   const PARENT_PATCH_NUM = 'PARENT';
 
   const Requests = {
@@ -108,7 +109,6 @@
 
   Polymer({
     is: 'gr-rest-api-interface',
-    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.FireBehavior,
@@ -942,6 +942,8 @@
           const req = {url: '/accounts/self/preferences', reportUrlAsIs: true};
           return this._fetchSharedCacheURL(req).then(res => {
             if (this._isNarrowScreen()) {
+              // Note that this can be problematic, because the diff will stay
+              // unified even after increasing the window width.
               res.default_diff_view = DiffViewMode.UNIFIED;
             } else {
               res.default_diff_view = res.diff_view;
@@ -1092,7 +1094,6 @@
         this.ListChangesOption.ALL_COMMITS,
         this.ListChangesOption.ALL_REVISIONS,
         this.ListChangesOption.CHANGE_ACTIONS,
-        this.ListChangesOption.CURRENT_ACTIONS,
         this.ListChangesOption.DETAILED_LABELS,
         this.ListChangesOption.DOWNLOAD_COMMANDS,
         this.ListChangesOption.MESSAGES,
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
index 9a24ddc..54b1bfb 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
@@ -153,7 +153,6 @@
 
       Polymer({
         is: 'mock-diff-response',
-        _legacyUndefinedCheck: true,
         properties: {
           diffResponse: {
             type: Object,
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
index 333bede..2e35a2a 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
@@ -19,8 +19,6 @@
 
   Polymer({
     is: 'gr-select',
-    _legacyUndefinedCheck: true,
-
     properties: {
       bindValue: {
         type: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
index 901b8ce..2c546cc 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-shell-command',
-    _legacyUndefinedCheck: true,
 
     properties: {
       command: String,
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 0690950..9ae77d9 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -30,7 +30,6 @@
 
   Polymer({
     is: 'gr-storage',
-    _legacyUndefinedCheck: true,
 
     properties: {
       _lastCleanup: Number,
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
index 1f90211..c4d31ac 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -54,7 +54,6 @@
 
   Polymer({
     is: 'gr-textarea',
-    _legacyUndefinedCheck: true,
 
     /**
      * @event bind-value-changed
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
index b46cafb..c5de8f4 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-tooltip-content',
-    _legacyUndefinedCheck: true,
 
     properties: {
       title: {
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
index 3e16beb..fb87b558 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
@@ -19,7 +19,6 @@
 
   Polymer({
     is: 'gr-tooltip',
-    _legacyUndefinedCheck: true,
 
     properties: {
       text: String,
diff --git a/polygerrit-ui/app/samples/bind-parameters.html b/polygerrit-ui/app/samples/bind-parameters.html
index a7eb39a..dc7a87a 100644
--- a/polygerrit-ui/app/samples/bind-parameters.html
+++ b/polygerrit-ui/app/samples/bind-parameters.html
@@ -15,7 +15,6 @@
   <script>
     Polymer({
       is: 'my-bind-sample',
-      _legacyUndefinedCheck: true,
       properties: {
         computedExample: {
           type: String,
diff --git a/polygerrit-ui/app/samples/repo-command.html b/polygerrit-ui/app/samples/repo-command.html
index 37aca04..67e528a 100644
--- a/polygerrit-ui/app/samples/repo-command.html
+++ b/polygerrit-ui/app/samples/repo-command.html
@@ -29,7 +29,6 @@
   <script>
     Polymer({
       is: 'repo-command-low',
-      _legacyUndefinedCheck: true,
       attached() {
         console.log(this.repoName);
         console.log(this.config);
diff --git a/polygerrit-ui/app/samples/some-screen.html b/polygerrit-ui/app/samples/some-screen.html
index 527ebce..de29315 100644
--- a/polygerrit-ui/app/samples/some-screen.html
+++ b/polygerrit-ui/app/samples/some-screen.html
@@ -38,7 +38,6 @@
   <script>
     Polymer({
       is: 'some-screen-main',
-      _legacyUndefinedCheck: true,
       properties: {
         rootUrl: String,
       },