Merge "Add download commands to project pages"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index aca1586..185af15 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -970,10 +970,10 @@
 [[capability.administrateServer]]capability.administrateServer::
 +
 Names of groups of users that are allowed to exercise the
-administrateServer capability, in addition to those listed in
+`administrateServer` capability, in addition to those listed in
 All-Projects. Configuring this option can be a useful fail-safe
 to recover a server in the event an administrator removed all
-groups from the administrateServer capability, or to ensure that
+groups from the `administrateServer` capability, or to ensure that
 specific groups always have administration capabilities.
 +
 ----
@@ -987,7 +987,16 @@
 is logged and the server will continue normal startup.
 +
 If not specified (default), only the groups listed by All-Projects
-may use the administrateServer capability.
+may use the `administrateServer` capability.
+
+[[capability.makeFirstUserAdmin]]capability.makeFirstUserAdmin::
++
+Whether the first user that logs in to the Gerrit server should
+automatically be added to the administrator group and hence get the
+`administrateServer` capability assigned. This is useful to bootstrap
+the authentication database.
++
+Default is true.
 
 
 [[change]]
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index be6d025..e4a7218 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -344,6 +344,18 @@
 doubt, do not hesitate to ask on the developer
 link:https://groups.google.com/forum/#!forum/repo-discuss[mailing list].
 
+=== Upgrading Libraries
+
+Gerrit's library dependencies should only be upgraded if the new version contains
+something we need in Gerrit. This includes new features, API changes as well as bug
+or security fixes.
+An exception to this rule is that right after a new Gerrit release was branched
+off, all libraries should be upgraded to the latest version to prevent Gerrit
+from falling behind. Doing those upgrades should conclude at the latest two
+months after the branch was cut. This should happen on the master branch to ensure
+that they are vetted long enough before they go into a release and we can be sure
+that the update doesn't introduce a regression.
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/WORKSPACE b/WORKSPACE
index 8cb061e..4c89dc7 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1107,6 +1107,13 @@
     version = "1.0.0",
 )
 
+bower_archive(
+    name = "polymer-resin",
+    package = "polymer/polymer-resin",
+    sha1 = "4a60925f44d004b593e93aca828918b15b29526b",
+    version = "1.2.1-beta",
+)
+
 # bower test stuff
 
 bower_archive(
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index cdb8fbb..877a42a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -323,11 +323,7 @@
     String topic = "my/topic";
     PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%cc=" + user.email);
     r.assertOkStatus();
-    r.assertChange(
-        Change.Status.NEW,
-        topic,
-        ImmutableList.of(),
-        ImmutableList.of(user));
+    r.assertChange(Change.Status.NEW, topic, ImmutableList.of(), ImmutableList.of(user));
 
     // cc several users
     r =
@@ -343,10 +339,7 @@
     r.assertOkStatus();
     // Check that admin isn't CC'd as they own the change
     r.assertChange(
-        Change.Status.NEW,
-        topic,
-        ImmutableList.of(),
-        ImmutableList.of(user, accounts.user2()));
+        Change.Status.NEW, topic, ImmutableList.of(), ImmutableList.of(user, accounts.user2()));
 
     // cc non-existing user
     String nonExistingEmail = "non.existing@example.com";
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
index 4c38d83..d6ad269 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
@@ -132,9 +132,9 @@
         + "> \n";
   }
 
-  protected static String textFooterForChange(String changeId, String timestamp) {
-    return "Gerrit-Change-Id: "
-        + changeId
+  protected static String textFooterForChange(int changeNumber, String timestamp) {
+    return "Gerrit-Change-Number: "
+        + changeNumber
         + "\n"
         + "Gerrit-PatchSet: 1\n"
         + "Gerrit-MessageType: comment\n"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/ListMailFilterIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/ListMailFilterIT.java
index 1dcdd97..a96c6ec 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/ListMailFilterIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/ListMailFilterIT.java
@@ -113,7 +113,7 @@
             null,
             null,
             null);
-    b.textContent(txt + textFooterForChange(changeId, ts));
+    b.textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     mailProcessor.process(b.build());
     return changeInfo;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
index ada222a..7cef8e7 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -67,7 +67,8 @@
 
     Map<String, Object> expectedHeaders = new HashMap<>();
     expectedHeaders.put("Gerrit-PatchSet", "1");
-    expectedHeaders.put("Gerrit-Change-Id", newChange.getChangeId());
+    expectedHeaders.put(
+        "Gerrit-Change-Number", String.valueOf(newChange.getChange().getId().get()));
     expectedHeaders.put("Gerrit-MessageType", "newchange");
     expectedHeaders.put("Gerrit-Commit", newChange.getCommit().getId().name());
     expectedHeaders.put("Gerrit-ChangeURL", changeURL);
@@ -102,7 +103,8 @@
     String changeURL = "<" + canonicalWebUrl.get() + newChange.getChange().getId().get() + ">";
     Map<String, Object> expectedHeaders = new HashMap<>();
     expectedHeaders.put("Gerrit-PatchSet", "1");
-    expectedHeaders.put("Gerrit-Change-Id", newChange.getChangeId());
+    expectedHeaders.put(
+        "Gerrit-Change-Number", String.valueOf(newChange.getChange().getId().get()));
     expectedHeaders.put("Gerrit-MessageType", "comment");
     expectedHeaders.put("Gerrit-Commit", newChange.getCommit().getId().name());
     expectedHeaders.put("Gerrit-ChangeURL", changeURL);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
index e7a0cda..9de4797 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
@@ -51,7 +51,7 @@
             null,
             null,
             null);
-    b.textContent(txt + textFooterForChange(changeId, ts));
+    b.textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     mailProcessor.process(b.build());
 
@@ -79,7 +79,7 @@
             "Some Inline Comment",
             null,
             null);
-    b.textContent(txt + textFooterForChange(changeId, ts));
+    b.textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     mailProcessor.process(b.build());
 
@@ -115,7 +115,7 @@
             null,
             "Some Comment on File 1",
             null);
-    b.textContent(txt + textFooterForChange(changeId, ts));
+    b.textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     mailProcessor.process(b.build());
 
@@ -152,7 +152,7 @@
             "Some Inline Comment",
             null,
             null);
-    b.textContent(txt + textFooterForChange(changeId, ts));
+    b.textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     mailProcessor.process(b.build());
     comments = gApi.changes().id(changeId).current().commentsAsList();
@@ -183,7 +183,7 @@
             "Some Inline Comment",
             null,
             null);
-    b.textContent(txt + textFooterForChange(changeId, ts));
+    b.textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     // Set account state to inactive
     gApi.accounts().id("user").setActive(false);
@@ -219,7 +219,7 @@
     MailMessage.Builder b =
         messageBuilderWithDefaultFields()
             .from(user.emailAddress)
-            .textContent(txt + textFooterForChange(changeId, ts));
+            .textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     sender.clear();
     mailProcessor.process(b.build());
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index eed6ccc..7fd3d81 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -19,6 +19,7 @@
 /**
  * @param canonicalPath
  * @param staticResourcePath
+ * @param? versionInfo
  */
 {template .Index autoescape="strict" kind="html"}
   <!DOCTYPE html>{\n}
@@ -27,8 +28,11 @@
   <meta name="description" content="Gerrit Code Review">{\n}
   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">{\n}
 
-  {if $canonicalPath != ''}
-    <script>window.CANONICAL_PATH = '{$canonicalPath}';</script>{\n}
+  {if $canonicalPath != '' or $versionInfo}
+    <script>
+      {if $canonicalPath != ''}window.CANONICAL_PATH = '{$canonicalPath}';{/if}
+      {if $versionInfo}window.VERSION_INFO = '{$versionInfo}';{/if}
+    </script>{\n}
   {/if}
 
   <link rel="icon" type="image/x-icon" href="{$canonicalPath}/favicon.ico">{\n}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
index 9f2b869..3ff35b3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
@@ -43,6 +44,7 @@
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,6 +71,7 @@
   @Inject
   AccountManager(
       SchemaFactory<ReviewDb> schema,
+      @GerritServerConfig Config cfg,
       Accounts accounts,
       AccountsUpdate.Server accountsUpdateFactory,
       AccountCache byIdCache,
@@ -90,7 +93,8 @@
     this.userFactory = userFactory;
     this.changeUserNameFactory = changeUserNameFactory;
     this.projectCache = projectCache;
-    this.awaitsFirstAccountCheck = new AtomicBoolean(true);
+    this.awaitsFirstAccountCheck =
+        new AtomicBoolean(cfg.getBoolean("capability", "makeFirstUserAdmin", true));
     this.auditService = auditService;
     this.accountQueryProvider = accountQueryProvider;
     this.externalIds = externalIds;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java
index 069380f..54e06de 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -17,6 +17,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -42,7 +43,12 @@
 import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.lib.Repository;
 
-/** Updates accounts. */
+/**
+ * Updates accounts.
+ *
+ * <p>On updating accounts this class takes care to evict them from the account cache and thus
+ * triggers reindex for them.
+ */
 @Singleton
 public class AccountsUpdate {
   /**
@@ -50,9 +56,6 @@
    *
    * <p>The Gerrit server identity will be used as author and committer for all commits that update
    * the accounts.
-   *
-   * <p>On updating accounts this class takes care to evict them from the account cache and thus
-   * triggers reindex for them.
    */
   @Singleton
   public static class Server {
@@ -80,6 +83,37 @@
   }
 
   /**
+   * Factory to create an AccountsUpdate instance for updating accounts by the Gerrit server.
+   *
+   * <p>Using this class will not perform reindexing for the updated accounts and they will also not
+   * be evicted from the account cache.
+   *
+   * <p>The Gerrit server identity will be used as author and committer for all commits that update
+   * the accounts.
+   */
+  @Singleton
+  public static class ServerNoReindex {
+    private final GitRepositoryManager repoManager;
+    private final AllUsersName allUsersName;
+    private final Provider<PersonIdent> serverIdent;
+
+    @Inject
+    public ServerNoReindex(
+        GitRepositoryManager repoManager,
+        AllUsersName allUsersName,
+        @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+      this.repoManager = repoManager;
+      this.allUsersName = allUsersName;
+      this.serverIdent = serverIdent;
+    }
+
+    public AccountsUpdate create() {
+      PersonIdent i = serverIdent.get();
+      return new AccountsUpdate(repoManager, null, allUsersName, i, i);
+    }
+  }
+
+  /**
    * Factory to create an AccountsUpdate instance for updating accounts by the current user.
    *
    * <p>The identity of the current user will be used as author for all commits that update the
@@ -119,19 +153,19 @@
   }
 
   private final GitRepositoryManager repoManager;
-  private final AccountCache accountCache;
+  @Nullable private final AccountCache accountCache;
   private final AllUsersName allUsersName;
   private final PersonIdent committerIdent;
   private final PersonIdent authorIdent;
 
   private AccountsUpdate(
       GitRepositoryManager repoManager,
-      AccountCache accountCache,
+      @Nullable AccountCache accountCache,
       AllUsersName allUsersName,
       PersonIdent committerIdent,
       PersonIdent authorIdent) {
     this.repoManager = checkNotNull(repoManager, "repoManager");
-    this.accountCache = checkNotNull(accountCache, "accountCache");
+    this.accountCache = accountCache;
     this.allUsersName = checkNotNull(allUsersName, "allUsersName");
     this.committerIdent = checkNotNull(committerIdent, "committerIdent");
     this.authorIdent = checkNotNull(authorIdent, "authorIdent");
@@ -146,7 +180,7 @@
   public void insert(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().insert(ImmutableSet.of(account));
     createUserBranch(account);
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /**
@@ -157,13 +191,13 @@
   public void upsert(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().upsert(ImmutableSet.of(account));
     createUserBranchIfNeeded(account);
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /** Updates the account. */
   public void update(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().update(ImmutableSet.of(account));
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /**
@@ -185,7 +219,7 @@
                   consumer.accept(a);
                   return a;
                 });
-    accountCache.evict(accountId);
+    evictAccount(accountId);
     return account;
   }
 
@@ -193,14 +227,14 @@
   public void delete(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().delete(ImmutableSet.of(account));
     deleteUserBranch(account.getId());
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /** Deletes the account. */
   public void deleteByKey(ReviewDb db, Account.Id accountId) throws OrmException, IOException {
     db.accounts().deleteKeys(ImmutableSet.of(accountId));
     deleteUserBranch(accountId);
-    accountCache.evict(accountId);
+    evictAccount(accountId);
   }
 
   private void createUserBranch(Account account) throws IOException {
@@ -294,4 +328,10 @@
       throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
     }
   }
+
+  private void evictAccount(Account.Id accountId) throws IOException {
+    if (accountCache != null) {
+      accountCache.evict(accountId);
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
index 9beae0e..2985504 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
@@ -136,6 +136,47 @@
   }
 
   /**
+   * Factory to create an ExternalIdsUpdate instance for updating external IDs by the Gerrit server.
+   *
+   * <p>Using this class no reindex will be performed for the affected accounts and they will also
+   * not be evicted from the account cache.
+   *
+   * <p>The Gerrit server identity will be used as author and committer for all commits that update
+   * the external IDs.
+   */
+  @Singleton
+  public static class ServerNoReindex {
+    private final GitRepositoryManager repoManager;
+    private final AllUsersName allUsersName;
+    private final MetricMaker metricMaker;
+    private final ExternalIds externalIds;
+    private final ExternalIdCache externalIdCache;
+    private final Provider<PersonIdent> serverIdent;
+
+    @Inject
+    public ServerNoReindex(
+        GitRepositoryManager repoManager,
+        AllUsersName allUsersName,
+        MetricMaker metricMaker,
+        ExternalIds externalIds,
+        ExternalIdCache externalIdCache,
+        @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+      this.repoManager = repoManager;
+      this.allUsersName = allUsersName;
+      this.metricMaker = metricMaker;
+      this.externalIds = externalIds;
+      this.externalIdCache = externalIdCache;
+      this.serverIdent = serverIdent;
+    }
+
+    public ExternalIdsUpdate create() {
+      PersonIdent i = serverIdent.get();
+      return new ExternalIdsUpdate(
+          repoManager, null, allUsersName, metricMaker, externalIds, externalIdCache, i, i);
+    }
+  }
+
+  /**
    * Factory to create an ExternalIdsUpdate instance for updating external IDs by the current user.
    *
    * <p>The identity of the current user will be used as author for all commits that update the
@@ -204,7 +245,7 @@
   private static final Retryer<RefsMetaExternalIdsUpdate> RETRYER = retryerBuilder().build();
 
   private final GitRepositoryManager repoManager;
-  private final AccountCache accountCache;
+  @Nullable private final AccountCache accountCache;
   private final AllUsersName allUsersName;
   private final ExternalIds externalIds;
   private final ExternalIdCache externalIdCache;
@@ -216,7 +257,7 @@
 
   private ExternalIdsUpdate(
       GitRepositoryManager repoManager,
-      AccountCache accountCache,
+      @Nullable AccountCache accountCache,
       AllUsersName allUsersName,
       MetricMaker metricMaker,
       ExternalIds externalIds,
@@ -239,7 +280,7 @@
   @VisibleForTesting
   public ExternalIdsUpdate(
       GitRepositoryManager repoManager,
-      AccountCache accountCache,
+      @Nullable AccountCache accountCache,
       AllUsersName allUsersName,
       MetricMaker metricMaker,
       ExternalIds externalIds,
@@ -375,7 +416,7 @@
               }
             });
     externalIdCache.onRemoveByKeys(u.oldRev(), u.newRev(), accountId, extIdKeys);
-    accountCache.evict(accountId);
+    evictAccount(accountId);
   }
 
   /**
@@ -434,7 +475,7 @@
               }
             });
     externalIdCache.onReplaceByKeys(u.oldRev(), u.newRev(), accountId, toDelete, toAdd);
-    accountCache.evict(accountId);
+    evictAccount(accountId);
   }
 
   /**
@@ -726,7 +767,17 @@
     return ins.insert(OBJ_TREE, new byte[] {});
   }
 
+  private void evictAccount(Account.Id accountId) throws IOException {
+    if (accountCache != null) {
+      accountCache.evict(accountId);
+    }
+  }
+
   private void evictAccounts(Collection<ExternalId> extIds) throws IOException {
+    if (accountCache == null) {
+      return;
+    }
+
     for (Account.Id id : extIds.stream().map(ExternalId::accountId).collect(toSet())) {
       accountCache.evict(id);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MetadataName.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MetadataName.java
index 3db55c0..3080e4f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MetadataName.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MetadataName.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.mail;
 
 public final class MetadataName {
-  public static final String CHANGE_ID = "Gerrit-Change-Id";
+  public static final String CHANGE_NUMBER = "Gerrit-Change-Number";
   public static final String PATCH_SET = "Gerrit-PatchSet";
   public static final String MESSAGE_TYPE = "Gerrit-MessageType";
   public static final String TIMESTAMP = "Gerrit-Comment-Date";
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMetadata.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMetadata.java
index f85291c..04c2add 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMetadata.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMetadata.java
@@ -19,14 +19,14 @@
 
 /** MailMetadata represents metadata parsed from inbound email. */
 public class MailMetadata {
-  public String changeId;
+  public Integer changeNumber;
   public Integer patchSet;
   public String author; // Author of the email
   public Timestamp timestamp;
   public String messageType; // we expect comment here
 
   public boolean hasRequiredFields() {
-    return changeId != null
+    return changeNumber != null
         && patchSet != null
         && author != null
         && timestamp != null
@@ -36,7 +36,7 @@
   @Override
   public String toString() {
     return MoreObjects.toStringHelper(this)
-        .add("Change-Id", changeId)
+        .add("Change-Number", changeNumber)
         .add("Patch-Set", patchSet)
         .add("Author", author)
         .add("Timestamp", timestamp)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 0c2cf04..7ad282b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -169,12 +169,12 @@
 
     try (ManualRequestContext ctx = oneOffRequestContext.openAs(account)) {
       List<ChangeData> changeDataList =
-          queryProvider.get().byKey(Change.Key.parse(metadata.changeId));
+          queryProvider.get().byLegacyChangeId(new Change.Id(metadata.changeNumber));
       if (changeDataList.size() != 1) {
         log.error(
             String.format(
                 "Message %s references unique change %s, but there are %d matching changes in the index. Will delete message.",
-                message.id(), metadata.changeId, changeDataList.size()));
+                message.id(), metadata.changeNumber, changeDataList.size()));
         return;
       }
       ChangeData cd = changeDataList.get(0);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MetadataParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MetadataParser.java
index a18da68..7085051 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MetadataParser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MetadataParser.java
@@ -38,9 +38,9 @@
 
     // Check email headers for X-Gerrit-<Name>
     for (String header : m.additionalHeaders()) {
-      if (header.startsWith(toHeaderWithDelimiter(MetadataName.CHANGE_ID))) {
-        metadata.changeId =
-            header.substring(toHeaderWithDelimiter(MetadataName.CHANGE_ID).length());
+      if (header.startsWith(toHeaderWithDelimiter(MetadataName.CHANGE_NUMBER))) {
+        String num = header.substring(toHeaderWithDelimiter(MetadataName.CHANGE_NUMBER).length());
+        metadata.changeNumber = Ints.tryParse(num);
       } else if (header.startsWith(toHeaderWithDelimiter(MetadataName.PATCH_SET))) {
         String ps = header.substring(toHeaderWithDelimiter(MetadataName.PATCH_SET).length());
         metadata.patchSet = Ints.tryParse(ps);
@@ -84,8 +84,9 @@
 
   private static void extractFooters(String[] lines, MailMetadata metadata, MailMessage m) {
     for (String line : lines) {
-      if (metadata.changeId == null && line.contains(MetadataName.CHANGE_ID)) {
-        metadata.changeId = extractFooter(toFooterWithDelimiter(MetadataName.CHANGE_ID), line);
+      if (metadata.changeNumber == null && line.contains(MetadataName.CHANGE_NUMBER)) {
+        metadata.changeNumber =
+            Ints.tryParse(extractFooter(toFooterWithDelimiter(MetadataName.CHANGE_NUMBER), line));
       } else if (metadata.patchSet == null && line.contains(MetadataName.PATCH_SET)) {
         metadata.patchSet =
             Ints.tryParse(extractFooter(toFooterWithDelimiter(MetadataName.PATCH_SET), line));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java
index 0c4507e..84bae96 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java
@@ -34,7 +34,7 @@
     b.dateReceived(new DateTime());
     b.subject("");
 
-    b.addAdditionalHeader(toHeaderWithDelimiter(MetadataName.CHANGE_ID) + "cid");
+    b.addAdditionalHeader(toHeaderWithDelimiter(MetadataName.CHANGE_NUMBER) + "123");
     b.addAdditionalHeader(toHeaderWithDelimiter(MetadataName.PATCH_SET) + "1");
     b.addAdditionalHeader(toHeaderWithDelimiter(MetadataName.MESSAGE_TYPE) + "comment");
     b.addAdditionalHeader(
@@ -45,7 +45,7 @@
 
     MailMetadata meta = MetadataParser.parse(b.build());
     assertThat(meta.author).isEqualTo(author.getEmail());
-    assertThat(meta.changeId).isEqualTo("cid");
+    assertThat(meta.changeNumber).isEqualTo(123);
     assertThat(meta.patchSet).isEqualTo(1);
     assertThat(meta.messageType).isEqualTo("comment");
     assertThat(meta.timestamp.getTime())
@@ -62,7 +62,7 @@
     b.subject("");
 
     StringBuilder stringBuilder = new StringBuilder();
-    stringBuilder.append(toFooterWithDelimiter(MetadataName.CHANGE_ID) + "cid\r\n");
+    stringBuilder.append(toFooterWithDelimiter(MetadataName.CHANGE_NUMBER) + "123\r\n");
     stringBuilder.append("> " + toFooterWithDelimiter(MetadataName.PATCH_SET) + "1\n");
     stringBuilder.append(toFooterWithDelimiter(MetadataName.MESSAGE_TYPE) + "comment\n");
     stringBuilder.append(
@@ -74,7 +74,7 @@
 
     MailMetadata meta = MetadataParser.parse(b.build());
     assertThat(meta.author).isEqualTo(author.getEmail());
-    assertThat(meta.changeId).isEqualTo("cid");
+    assertThat(meta.changeNumber).isEqualTo(123);
     assertThat(meta.patchSet).isEqualTo(1);
     assertThat(meta.messageType).isEqualTo("comment");
     assertThat(meta.timestamp.getTime())
@@ -92,7 +92,7 @@
 
     StringBuilder stringBuilder = new StringBuilder();
     stringBuilder.append(
-        "<div id\"someid\">" + toFooterWithDelimiter(MetadataName.CHANGE_ID) + "cid</div>");
+        "<div id\"someid\">" + toFooterWithDelimiter(MetadataName.CHANGE_NUMBER) + "123</div>");
     stringBuilder.append("<div>" + toFooterWithDelimiter(MetadataName.PATCH_SET) + "1</div>");
     stringBuilder.append(
         "<div>" + toFooterWithDelimiter(MetadataName.MESSAGE_TYPE) + "comment</div>");
@@ -108,7 +108,7 @@
 
     MailMetadata meta = MetadataParser.parse(b.build());
     assertThat(meta.author).isEqualTo(author.getEmail());
-    assertThat(meta.changeId).isEqualTo("cid");
+    assertThat(meta.changeNumber).isEqualTo(123);
     assertThat(meta.patchSet).isEqualTo(1);
     assertThat(meta.messageType).isEqualTo("comment");
     assertThat(meta.timestamp.getTime())
diff --git a/lib/js/bower_components.bzl b/lib/js/bower_components.bzl
index bb047bd70..9c3c63b 100644
--- a/lib/js/bower_components.bzl
+++ b/lib/js/bower_components.bzl
@@ -168,6 +168,15 @@
     seed = True,
   )
   bower_component(
+    name = "polymer-resin",
+    license = "//lib:LICENSE-polymer",
+    deps = [
+      ":polymer",
+      ":webcomponentsjs",
+    ],
+    seed = True,
+  )
+  bower_component(
     name = "polymer",
     license = "//lib:LICENSE-polymer",
     deps = [ ":webcomponentsjs" ],
diff --git a/polygerrit-ui/BUILD b/polygerrit-ui/BUILD
index 1f11cde..1b58a90 100644
--- a/polygerrit-ui/BUILD
+++ b/polygerrit-ui/BUILD
@@ -21,6 +21,7 @@
         "//lib/js:moment",
         "//lib/js:page",
         "//lib/js:polymer",
+        "//lib/js:polymer-resin",
         "//lib/js:promise-polyfill",
     ],
 )
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html
index 8b969f7..fad9baf 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html
@@ -22,7 +22,7 @@
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 
 <link rel="import" href="gr-admin-group-list.html">
 
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index 81135ae..c0371ee 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -246,7 +246,7 @@
           as="file"
           initial-count="[[fileListIncrement]]"
           target-framerate="1">
-        <div class="file-row row" data-path$="[[file.__path]]">
+        <div class="file-row row" data-path$="[[file.__path]]" tabindex="-1">
           <div class="reviewed" hidden$="[[!_loggedIn]]" hidden>
             <input type="checkbox" checked="[[file.isReviewed]]"
                 class="reviewed" aria-label="Reviewed checkbox">
@@ -401,6 +401,7 @@
     <gr-cursor-manager
         id="fileCursor"
         scroll-behavior="keep-visible"
+        focus-on-move
         cursor-target-class="selected"></gr-cursor-manager>
     <gr-reporting id="reporting"></gr-reporting>
   </template>
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 7621a9d..0bcd9fd 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
@@ -135,7 +135,7 @@
       'c': '_handleCKey',
       '[': '_handleLeftBracketKey',
       ']': '_handleRightBracketKey',
-      'o enter': '_handleEnterKey',
+      'o': '_handleOKey',
       'n': '_handleNKey',
       'p': '_handlePKey',
       'shift+a': '_handleCapitalAKey',
@@ -537,14 +537,10 @@
       this._openSelectedFile(0);
     },
 
-    _handleEnterKey(e) {
+    _handleOKey(e) {
       if (this.shouldSuppressKeyboardShortcut(e) ||
           this.modifierPressed(e)) { return; }
 
-      // Use native handling if an anchor is selected. @see Issue 5754
-      if (e.detail && e.detail.keyboardEvent && e.detail.keyboardEvent.target &&
-          e.detail.keyboardEvent.target.tagName === 'A') { return; }
-
       e.preventDefault();
       if (this._showInlineDiffs) {
         this._openCursorFile();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index f0f5403..4577a05 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -331,9 +331,7 @@
         const showStub = sandbox.stub(page, 'show');
         assert.equal(element.$.fileCursor.index, 2);
         assert.equal(element.selectedIndex, 2);
-        MockInteractions.pressAndReleaseKeyOn(element, 13, null, 'enter');
-        assert(showStub.lastCall.calledWith('/c/42/2/myfile.txt'),
-            'Should navigate to /c/42/2/myfile.txt');
+
         // k with a modifier should not move the cursor.
         MockInteractions.pressAndReleaseKeyOn(element, 75, 'shift', 'k');
         assert.equal(element.$.fileCursor.index, 2);
@@ -384,7 +382,7 @@
         }
       });
 
-      suite('_handleEnterKey', () => {
+      suite('_handleOKey', () => {
         let interact;
 
         setup(() => {
@@ -402,7 +400,7 @@
 
             const e = new CustomEvent('fake-keyboard-event', opt_payload);
             sinon.stub(e, 'preventDefault');
-            element._handleEnterKey(e);
+            element._handleOKey(e);
             assert.isTrue(e.preventDefault.called);
             const result = {};
             if (openCursorStub.called) {
@@ -440,14 +438,6 @@
           element._userPrefs.expand_inline_diffs = true;
           assert.deepEqual(interact(), {expanded: true});
         });
-
-        test('noop when anchor focused', () => {
-          const e = new CustomEvent('fake-keyboard-event',
-              {detail: {keyboardEvent: {target: document.createElement('a')}}});
-          sinon.stub(e, 'preventDefault');
-          element._handleEnterKey(e);
-          assert.isFalse(e.preventDefault.called);
-        });
       });
     });
 
@@ -1242,4 +1232,6 @@
       assert.isFalse(element._displayLine);
     });
   });
+
+  a11ySuite('basic');
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html
index b96f684..4245cf5 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html
@@ -21,7 +21,7 @@
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-label-score-row.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
index f4e26d3..e76a9e5 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
@@ -22,7 +22,6 @@
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
 
 <link rel="import" href="../../../test/common-test-setup.html"/>
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="../../plugins/gr-plugin-host/gr-plugin-host.html">
 <link rel="import" href="gr-reply-dialog.html">
 
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index eadf667..6aea989 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -96,6 +96,12 @@
         min-height: 6em;
         position: relative;
       }
+      .textareaContainer,
+      #textarea,
+      gr-endpoint-decorator {
+        display: flex;
+        width: 100%;
+      }
       .previewContainer gr-formatted-text {
         background: #f6f6f6;
         max-height: 20vh;
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
index 1396a2b..fef0f87 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
@@ -231,7 +231,7 @@
             <td>Select previous file</td>
           </tr>
           <tr>
-            <td><span class="key">Enter</span> or <span class="key">o</span></td>
+            <td><span class="key">o</span></td>
             <td>Show selected file</td>
           </tr>
           <tr>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index a61b9ca..84d9783 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -110,6 +110,7 @@
       }
       .contextLineNum:before,
       .lineNum:before {
+        box-sizing: border-box;
         display: inline-block;
         color: #666;
         content: attr(data-value);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
index 1c0f05e..e7b81be 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
@@ -992,4 +992,6 @@
       });
     });
   });
+
+  a11ySuite('basic');
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html
index 10ef4e1..d8e2ee0 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html
@@ -21,7 +21,7 @@
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-list-view.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/test/common-test-setup.html b/polygerrit-ui/app/test/common-test-setup.html
index 00bd1e7..9e277ab 100644
--- a/polygerrit-ui/app/test/common-test-setup.html
+++ b/polygerrit-ui/app/test/common-test-setup.html
@@ -16,4 +16,10 @@
 -->
 
 <link rel="import"
+    href="../bower_components/polymer-resin/standalone/polymer-resin-debug.html"
+    />
+<script>
+security.polymer_resin.install({allowedIdentifierPrefixes: ['']});
+</script>
+<link rel="import"
     href="../bower_components/iron-test-helpers/iron-test-helpers.html" />