Merge "Update the dev-bazel docs"
diff --git a/.gitignore b/.gitignore
index 93c1339..3a5d28b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 # Keep following lines sorted according to `LC_COLLATE=C sort`
 *.eml
 *.iml
+*.log
 *.pyc
 *.sublime-*
 *.swp
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index a75a859..8ccd150 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -26,6 +26,7 @@
 link:https://github.com/bazelbuild/bazelisk[Bazelisk,role=external,window=_blank]
 * Maven
 * zip, unzip
+* curl
 * gcc
 
 [[bazel]]
@@ -388,7 +389,7 @@
 `GERRIT_LOG_LEVEL=debug` environment variable:
 
 ----
-  bazel test --test_filter=com.gerrit.server.notedb.ChangeNotesTest \
+  bazel test --test_filter=com.google.gerrit.server.notedb.ChangeNotesTest \
   --test_env=GERRIT_LOG_LEVEL=debug \
   javatests/com/google/gerrit/server:server_tests
 ----
diff --git a/Documentation/dev-crafting-changes.txt b/Documentation/dev-crafting-changes.txt
index bc66ff0..8d0cee2 100644
--- a/Documentation/dev-crafting-changes.txt
+++ b/Documentation/dev-crafting-changes.txt
@@ -116,7 +116,7 @@
 link:https://github.com/google/google-java-format[`google-java-format`,role=external,window=_blank]
 tool (version 1.7), and to format Bazel BUILD, WORKSPACE and .bzl files the
 link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`,role=external,window=_blank]
-tool (version 2.0.0). Unused dependencies are found and removed using the
+tool (version 3.0.0). Unused dependencies are found and removed using the
 link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`,role=external,window=_blank]
 build tool, a sibling of `buildifier`.
 
diff --git a/Documentation/dev-e2e-tests.txt b/Documentation/dev-e2e-tests.txt
index fa3eb26..8fe8f4e 100644
--- a/Documentation/dev-e2e-tests.txt
+++ b/Documentation/dev-e2e-tests.txt
@@ -146,9 +146,9 @@
 === Environment properties
 
 The `JAVA_OPTS` environment variable
-link:https://gatling.io/docs/current/cookbook/passing_parameters[can optionally be used] to define
-non-default values for keys found in scenario `json` data files. That variable can currently be set
-with either one or many of these supported properties, from the core framework:
+link:https://gatling.io/docs/current/cookbook/passing_parameters[can optionally be used,role=external,window=_blank]
+to define non-default values for keys found in scenario `json` data files. That variable can
+currently be set with either one or many of these supported properties, from the core framework:
 
 * `-Dcom.google.gerrit.scenarios.hostname=localhost`
 * `-Dcom.google.gerrit.scenarios.ssh_port=29418`
@@ -170,6 +170,17 @@
 gets automatically generated by the scenario. Any property setting for it is therefore not
 applicable. Its usage differs from the non-prefixed `PROJECT` keyword, in that sense.
 
+The following core property can be optionally set depending on the runtime environment. The test
+environments used as reference for scenarios development assume its default value, `1.0`. For
+slower or more complex execution environments, the value can be increased this way for example:
+
+* `-Dcom.google.gerrit.scenarios.power_factor=1.5`
+
+This will make the scenario steps take half more time to expect proper completion. A value smaller
+than the default, say `0.8`, will make scenarios wait somewhat less than how they were developed.
+Scenario development is often done using locally running Gerrit systems under test, which are
+sometimes dockerized.
+
 == How to run tests
 
 Run all tests:
@@ -189,9 +200,9 @@
 
 The `src/test/resources/logback.xml` file
 link:http://logback.qos.ch/manual/configuration.html[configures,role=external,window=_blank]
-Gatling's logging level. To quickly
-enable link:https://gatling.io/docs/current/general/debugging#logback[detailed logging] of `http`
-requests and responses, the `root level` can be set to `trace` in that file.
+Gatling's logging level. To quickly enable
+link:https://gatling.io/docs/current/general/debugging#logback[detailed logging,role=external,window=_blank]
+of `http` requests and responses, the `root level` can be set to `trace` in that file.
 
 === How to run using Docker
 
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index bf6bc77..75ad9c2 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -121,7 +121,7 @@
 A change ref has the format `refs/changes/X/Y/Z` where `X` is the last
 two digits of the change number, `Y` is the entire change number, and `Z`
 is the patch set. For example, if the change number is
-link:https://gerrit-review.googlesource.com/c/gerrit/+/263270[263270],
+link:https://gerrit-review.googlesource.com/c/gerrit/+/263270[263270,role=external,window=_blank],
 the ref would be `refs/changes/70/263270/2` for the second patch set.
 
 [[fetch-change]]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
index c3f772a..ab91185 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
@@ -23,6 +23,7 @@
 class CloneUsingBothProtocols extends GitSimulation {
   private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).queue
   private val default: String = name
+  private val duration: Int = 2
 
   override def replaceOverride(in: String): String = {
     replaceKeyWith("_project", default, in)
@@ -37,14 +38,15 @@
 
   setUp(
     createProject.test.inject(
+      nothingFor(stepWaitTime(createProject) seconds),
       atOnceUsers(1)
     ),
     test.inject(
-      nothingFor(2 seconds),
-      constantUsersPerSec(1) during (2 seconds)
+      nothingFor(stepWaitTime(this) seconds),
+      constantUsersPerSec(1) during (duration seconds)
     ),
     deleteProject.test.inject(
-      nothingFor(6 seconds),
+      nothingFor(stepWaitTime(deleteProject) + duration seconds),
       atOnceUsers(1)
     ),
   ).protocols(gitProtocol, httpProtocol)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
index 75fb0b7..39b6d42 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
@@ -26,6 +26,8 @@
   private val default: String = name
   private val numberKey = "_number"
 
+  override def relativeRuntimeWeight = 2
+
   val test: ScenarioBuilder = scenario(unique)
       .feed(data)
       .exec(httpRequest
@@ -42,18 +44,19 @@
 
   setUp(
     createProject.test.inject(
+      nothingFor(stepWaitTime(createProject) seconds),
       atOnceUsers(1)
     ),
     test.inject(
-      nothingFor(2 seconds),
+      nothingFor(stepWaitTime(this) seconds),
       atOnceUsers(1)
     ),
     deleteChange.test.inject(
-      nothingFor(6 seconds),
+      nothingFor(stepWaitTime(deleteChange) seconds),
       atOnceUsers(1)
     ),
     deleteProject.test.inject(
-      nothingFor(8 seconds),
+      nothingFor(stepWaitTime(deleteProject) seconds),
       atOnceUsers(1)
     ),
   ).protocols(httpProtocol)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
index 0466ced..f3a2d14 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
@@ -23,6 +23,8 @@
   private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).queue
   var number: Option[Int] = None
 
+  override def relativeRuntimeWeight = 2
+
   val test: ScenarioBuilder = scenario(unique)
       .feed(data)
       .exec(session => {
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
index 06f0bdf..36df627 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
@@ -31,6 +31,26 @@
   protected val body: String = s"$pathName-body.json"
   protected val unique: String = name + "-" + this.hashCode()
 
+  private val powerFactor: Double = replaceProperty("power_factor", 1.0).toDouble
+  private val SecondsPerWeightUnit: Int = 2
+  val maxExecutionTime: Int = (SecondsPerWeightUnit * relativeRuntimeWeight * powerFactor).toInt
+  private var cumulativeWaitTime: Int = 0
+
+  /**
+   * How long a scenario step should wait before starting to execute.
+   * This is also registering that step's resulting wait time, so that time
+   * can be reused cumulatively by a potentially following scenario step.
+   * (Otherwise, the Gatling set-up scenario steps execute all at once.)
+   *
+   * @param scenario for which to return a wait time.
+   * @return that step's wait time as an Int.
+   */
+  protected def stepWaitTime(scenario: GerritSimulation): Int = {
+    val currentWaitTime = cumulativeWaitTime
+    cumulativeWaitTime += scenario.maxExecutionTime
+    currentWaitTime
+  }
+
   protected val httpRequest: HttpRequestBuilder = http(unique).post("${url}")
   protected val httpProtocol: HttpProtocolBuilder = http.basicAuth(
     conf.httpConfiguration.userName,
@@ -54,11 +74,15 @@
     replaceProperty(term, term, in)
   }
 
+  private def replaceProperty(term: String, default: Any): String = {
+    replaceProperty(term, default, term.toUpperCase)
+  }
+
   protected def replaceProperty(term: String, default: Any, in: String): String = {
     val property = pack + "." + term
     var value = default
     default match {
-      case _: String =>
+      case _: String | _: Double =>
         val propertyValue = Option(System.getProperty(property))
         if (propertyValue.nonEmpty) {
           value = propertyValue.get
@@ -84,8 +108,26 @@
    * override def replaceOverride(in: String): String = {
    * // Simple e.g., replaceProperty("EXTENSION_JSON_KEY", "default", in)
    * </pre>
+   *
+   * @param in which string to perform the replacements.
+   * @return the resulting String.
    */
   def replaceOverride(in: String): String = {
     in
   }
+
+  /**
+   * Meant to be optionally overridden by (heavier) scenarios.
+   * This is the relative runtime weight of the scenario class or type,
+   * compared to other scenarios' own runtime weights.
+   *
+   * The default weight or unit of weight is the pre-assigned value below.
+   * This default applies to any scenario class that is not overriding it
+   * with a greater, relative runtime weight value. Overriding scenarios
+   * happen to relatively require more run time than siblings, prior to
+   * being expected as completed.
+   *
+   * @return the relative runtime weight of this scenario as an Int.
+   */
+  def relativeRuntimeWeight = 1
 }
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
index e0b3206..74dc052 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
@@ -24,6 +24,8 @@
   private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(keys).circular
   private val default: String = name
 
+  override def relativeRuntimeWeight = 30
+
   override def replaceOverride(in: String): String = {
     replaceKeyWith("_project", default, in)
   }
@@ -36,22 +38,24 @@
 
   private val createProject = new CreateProject(default)
   private val deleteProject = new DeleteProject(default)
+  private val maxBeforeDelete: Int = maxExecutionTime - deleteProject.maxExecutionTime
 
   setUp(
     createProject.test.inject(
+      nothingFor(stepWaitTime(createProject) seconds),
       atOnceUsers(1)
     ),
     test.inject(
-      nothingFor(4 seconds),
+      nothingFor(stepWaitTime(this) seconds),
       atOnceUsers(10),
       rampUsers(10) during (5 seconds),
       constantUsersPerSec(20) during (15 seconds),
       constantUsersPerSec(20) during (15 seconds) randomized
     ),
     deleteProject.test.inject(
-      nothingFor(59 seconds),
+      nothingFor(maxBeforeDelete seconds),
       atOnceUsers(1)
     ),
   ).protocols(gitProtocol, httpProtocol)
-      .maxDuration(61 seconds)
+      .maxDuration(maxExecutionTime seconds)
 }
diff --git a/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java b/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
index dcfb614..be975c5 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
@@ -284,7 +284,7 @@
         // right now. Instead of blocking all of them log the error and
         // let the authentication complete anyway.
         //
-        logger.atSevere().log("Invalid PAPE response %s: %s", openidIdentifier, err);
+        logger.atSevere().withCause(err).log("Invalid PAPE response from %s", openidIdentifier);
         unsupported = true;
         ext = null;
       }
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index ef3f415..1c46ed6 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -107,6 +107,7 @@
         "//lib/auto:auto-value-annotations",
         "//lib/bouncycastle:bcpkix-neverlink",
         "//lib/bouncycastle:bcprov-neverlink",
+        "//lib/commons:codec",
         "//lib/commons:compress",
         "//lib/commons:dbcp",
         "//lib/commons:lang",
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index e9ba72d..450cbe0 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -242,7 +242,9 @@
    * @param changeMessages list of change messages
    */
   public static void linkCommentsToChangeMessages(
-      List<? extends CommentInfo> comments, List<ChangeMessage> changeMessages) {
+      List<? extends CommentInfo> comments,
+      List<ChangeMessage> changeMessages,
+      boolean skipAutoGeneratedMessages) {
     ArrayList<ChangeMessage> sortedChangeMessages =
         changeMessages.stream()
             .sorted(comparing(ChangeMessage::getWrittenOn))
@@ -257,7 +259,7 @@
       // message in timestamp
       while (cmItr < sortedChangeMessages.size()) {
         ChangeMessage cm = sortedChangeMessages.get(cmItr);
-        if (isAfter(comment, cm) || skipChangeMessage(cm)) {
+        if (isAfter(comment, cm) || (skipAutoGeneratedMessages && isAutoGenerated(cm))) {
           cmItr += 1;
         } else {
           break;
@@ -269,7 +271,7 @@
     }
   }
 
-  private static boolean skipChangeMessage(ChangeMessage cm) {
+  private static boolean isAutoGenerated(ChangeMessage cm) {
     return ChangeMessagesUtil.isAutogenerated(cm.getTag());
   }
 
diff --git a/java/com/google/gerrit/server/account/StoredPreferences.java b/java/com/google/gerrit/server/account/StoredPreferences.java
index 1b3ff40..573c619 100644
--- a/java/com/google/gerrit/server/account/StoredPreferences.java
+++ b/java/com/google/gerrit/server/account/StoredPreferences.java
@@ -15,19 +15,13 @@
 package com.google.gerrit.server.account;
 
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.config.ConfigUtil.loadSection;
-import static com.google.gerrit.server.config.ConfigUtil.skipField;
 import static com.google.gerrit.server.config.ConfigUtil.storeSection;
-import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
 import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
 import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
 import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
 import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
@@ -36,13 +30,12 @@
 import com.google.gerrit.extensions.client.MenuItem;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.PreferencesParserUtil;
 import com.google.gerrit.server.config.VersionedDefaultPreferences;
 import com.google.gerrit.server.git.UserConfigSections;
 import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -76,8 +69,6 @@
  * <p>The preferences are lazily parsed.
  */
 public class StoredPreferences {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
   public static final String PREFERENCES_CONFIG = "preferences.config";
 
   private final Account.Id accountId;
@@ -141,7 +132,7 @@
           UserConfigSections.GENERAL,
           null,
           mergedGeneralPreferencesInput,
-          parseDefaultGeneralPreferences(defaultCfg, null));
+          PreferencesParserUtil.parseDefaultGeneralPreferences(defaultCfg, null));
       setChangeTable(cfg, mergedGeneralPreferencesInput.changeTable);
       setMy(cfg, mergedGeneralPreferencesInput.my);
 
@@ -158,7 +149,7 @@
           UserConfigSections.DIFF,
           null,
           mergedDiffPreferencesInput,
-          parseDefaultDiffPreferences(defaultCfg, null));
+          PreferencesParserUtil.parseDefaultDiffPreferences(defaultCfg, null));
 
       // evict the cached diff preferences
       this.diffPreferences = null;
@@ -173,7 +164,7 @@
           UserConfigSections.EDIT,
           null,
           mergedEditPreferencesInput,
-          parseDefaultEditPreferences(defaultCfg, null));
+          PreferencesParserUtil.parseDefaultEditPreferences(defaultCfg, null));
 
       // evict the cached edit preferences
       this.editPreferences = null;
@@ -189,7 +180,7 @@
 
   private GeneralPreferencesInfo parseGeneralPreferences(@Nullable GeneralPreferencesInfo input) {
     try {
-      return parseGeneralPreferences(cfg, defaultCfg, input);
+      return PreferencesParserUtil.parseGeneralPreferences(cfg, defaultCfg, input);
     } catch (ConfigInvalidException e) {
       validationErrorSink.error(
           new ValidationError(
@@ -203,7 +194,7 @@
 
   private DiffPreferencesInfo parseDiffPreferences(@Nullable DiffPreferencesInfo input) {
     try {
-      return parseDiffPreferences(cfg, defaultCfg, input);
+      return PreferencesParserUtil.parseDiffPreferences(cfg, defaultCfg, input);
     } catch (ConfigInvalidException e) {
       validationErrorSink.error(
           new ValidationError(
@@ -216,7 +207,7 @@
 
   private EditPreferencesInfo parseEditPreferences(@Nullable EditPreferencesInfo input) {
     try {
-      return parseEditPreferences(cfg, defaultCfg, input);
+      return PreferencesParserUtil.parseEditPreferences(cfg, defaultCfg, input);
     } catch (ConfigInvalidException e) {
       validationErrorSink.error(
           new ValidationError(
@@ -227,218 +218,6 @@
     }
   }
 
-  /**
-   * Returns a {@link GeneralPreferencesInfo} that is the result of parsing {@code defaultCfg} for
-   * the server's default configs and {@code cfg} for the user's config. These configs are then
-   * overlaid to inherit values (default -> user -> input (if provided).
-   */
-  public static GeneralPreferencesInfo parseGeneralPreferences(
-      Config cfg, @Nullable Config defaultCfg, @Nullable GeneralPreferencesInfo input)
-      throws ConfigInvalidException {
-    GeneralPreferencesInfo r =
-        loadSection(
-            cfg,
-            UserConfigSections.GENERAL,
-            null,
-            new GeneralPreferencesInfo(),
-            defaultCfg != null
-                ? parseDefaultGeneralPreferences(defaultCfg, input)
-                : GeneralPreferencesInfo.defaults(),
-            input);
-    if (input != null) {
-      r.changeTable = input.changeTable;
-      r.my = input.my;
-    } else {
-      r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
-      r.my = parseMyMenus(cfg, defaultCfg);
-    }
-    return r;
-  }
-
-  /**
-   * Returns a {@link DiffPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
-   * server's default configs and {@code cfg} for the user's config. These configs are then overlaid
-   * to inherit values (default -> user -> input (if provided).
-   */
-  public static DiffPreferencesInfo parseDiffPreferences(
-      Config cfg, @Nullable Config defaultCfg, @Nullable DiffPreferencesInfo input)
-      throws ConfigInvalidException {
-    return loadSection(
-        cfg,
-        UserConfigSections.DIFF,
-        null,
-        new DiffPreferencesInfo(),
-        defaultCfg != null
-            ? parseDefaultDiffPreferences(defaultCfg, input)
-            : DiffPreferencesInfo.defaults(),
-        input);
-  }
-
-  /**
-   * Returns a {@link EditPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
-   * server's default configs and {@code cfg} for the user's config. These configs are then overlaid
-   * to inherit values (default -> user -> input (if provided).
-   */
-  public static EditPreferencesInfo parseEditPreferences(
-      Config cfg, @Nullable Config defaultCfg, @Nullable EditPreferencesInfo input)
-      throws ConfigInvalidException {
-    return loadSection(
-        cfg,
-        UserConfigSections.EDIT,
-        null,
-        new EditPreferencesInfo(),
-        defaultCfg != null
-            ? parseDefaultEditPreferences(defaultCfg, input)
-            : EditPreferencesInfo.defaults(),
-        input);
-  }
-
-  private static GeneralPreferencesInfo parseDefaultGeneralPreferences(
-      Config defaultCfg, GeneralPreferencesInfo input) throws ConfigInvalidException {
-    GeneralPreferencesInfo allUserPrefs = new GeneralPreferencesInfo();
-    loadSection(
-        defaultCfg,
-        UserConfigSections.GENERAL,
-        null,
-        allUserPrefs,
-        GeneralPreferencesInfo.defaults(),
-        input);
-    return updateGeneralPreferencesDefaults(allUserPrefs);
-  }
-
-  private static DiffPreferencesInfo parseDefaultDiffPreferences(
-      Config defaultCfg, DiffPreferencesInfo input) throws ConfigInvalidException {
-    DiffPreferencesInfo allUserPrefs = new DiffPreferencesInfo();
-    loadSection(
-        defaultCfg,
-        UserConfigSections.DIFF,
-        null,
-        allUserPrefs,
-        DiffPreferencesInfo.defaults(),
-        input);
-    return updateDiffPreferencesDefaults(allUserPrefs);
-  }
-
-  private static EditPreferencesInfo parseDefaultEditPreferences(
-      Config defaultCfg, EditPreferencesInfo input) throws ConfigInvalidException {
-    EditPreferencesInfo allUserPrefs = new EditPreferencesInfo();
-    loadSection(
-        defaultCfg,
-        UserConfigSections.EDIT,
-        null,
-        allUserPrefs,
-        EditPreferencesInfo.defaults(),
-        input);
-    return updateEditPreferencesDefaults(allUserPrefs);
-  }
-
-  private static GeneralPreferencesInfo updateGeneralPreferencesDefaults(
-      GeneralPreferencesInfo input) {
-    GeneralPreferencesInfo result = GeneralPreferencesInfo.defaults();
-    try {
-      for (Field field : input.getClass().getDeclaredFields()) {
-        if (skipField(field)) {
-          continue;
-        }
-        Object newVal = field.get(input);
-        if (newVal != null) {
-          field.set(result, newVal);
-        }
-      }
-    } catch (IllegalAccessException e) {
-      logger.atSevere().withCause(e).log("Failed to apply default general preferences");
-      return GeneralPreferencesInfo.defaults();
-    }
-    return result;
-  }
-
-  private static DiffPreferencesInfo updateDiffPreferencesDefaults(DiffPreferencesInfo input) {
-    DiffPreferencesInfo result = DiffPreferencesInfo.defaults();
-    try {
-      for (Field field : input.getClass().getDeclaredFields()) {
-        if (skipField(field)) {
-          continue;
-        }
-        Object newVal = field.get(input);
-        if (newVal != null) {
-          field.set(result, newVal);
-        }
-      }
-    } catch (IllegalAccessException e) {
-      logger.atSevere().withCause(e).log("Failed to apply default diff preferences");
-      return DiffPreferencesInfo.defaults();
-    }
-    return result;
-  }
-
-  private static EditPreferencesInfo updateEditPreferencesDefaults(EditPreferencesInfo input) {
-    EditPreferencesInfo result = EditPreferencesInfo.defaults();
-    try {
-      for (Field field : input.getClass().getDeclaredFields()) {
-        if (skipField(field)) {
-          continue;
-        }
-        Object newVal = field.get(input);
-        if (newVal != null) {
-          field.set(result, newVal);
-        }
-      }
-    } catch (IllegalAccessException e) {
-      logger.atSevere().withCause(e).log("Failed to apply default edit preferences");
-      return EditPreferencesInfo.defaults();
-    }
-    return result;
-  }
-
-  private static List<String> parseChangeTableColumns(Config cfg, @Nullable Config defaultCfg) {
-    List<String> changeTable = changeTable(cfg);
-    if (changeTable == null && defaultCfg != null) {
-      changeTable = changeTable(defaultCfg);
-    }
-    return changeTable;
-  }
-
-  private static List<MenuItem> parseMyMenus(Config cfg, @Nullable Config defaultCfg) {
-    List<MenuItem> my = my(cfg);
-    if (my.isEmpty() && defaultCfg != null) {
-      my = my(defaultCfg);
-    }
-    if (my.isEmpty()) {
-      my.add(new MenuItem("Dashboard", "#/dashboard/self", null));
-      my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
-      my.add(new MenuItem("Edits", "#/q/has:edit", null));
-      my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
-      my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
-      my.add(new MenuItem("Groups", "#/settings/#Groups", null));
-    }
-    return my;
-  }
-
-  public static GeneralPreferencesInfo readDefaultGeneralPreferences(
-      AllUsersName allUsersName, Repository allUsersRepo)
-      throws IOException, ConfigInvalidException {
-    return parseGeneralPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
-  }
-
-  public static DiffPreferencesInfo readDefaultDiffPreferences(
-      AllUsersName allUsersName, Repository allUsersRepo)
-      throws IOException, ConfigInvalidException {
-    return parseDiffPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
-  }
-
-  public static EditPreferencesInfo readDefaultEditPreferences(
-      AllUsersName allUsersName, Repository allUsersRepo)
-      throws IOException, ConfigInvalidException {
-    return parseEditPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
-  }
-
-  static Config readDefaultConfig(AllUsersName allUsersName, Repository allUsersRepo)
-      throws IOException, ConfigInvalidException {
-    VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
-    defaultPrefs.load(allUsersName, allUsersRepo);
-    return defaultPrefs.getConfig();
-  }
-
   public static GeneralPreferencesInfo updateDefaultGeneralPreferences(
       MetaDataUpdate md, GeneralPreferencesInfo input) throws IOException, ConfigInvalidException {
     VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
@@ -453,7 +232,7 @@
     setChangeTable(defaultPrefs.getConfig(), input.changeTable);
     defaultPrefs.commit(md);
 
-    return parseGeneralPreferences(defaultPrefs.getConfig(), null, null);
+    return PreferencesParserUtil.parseGeneralPreferences(defaultPrefs.getConfig(), null, null);
   }
 
   public static DiffPreferencesInfo updateDefaultDiffPreferences(
@@ -468,7 +247,7 @@
         DiffPreferencesInfo.defaults());
     defaultPrefs.commit(md);
 
-    return parseDiffPreferences(defaultPrefs.getConfig(), null, null);
+    return PreferencesParserUtil.parseDiffPreferences(defaultPrefs.getConfig(), null, null);
   }
 
   public static EditPreferencesInfo updateDefaultEditPreferences(
@@ -483,11 +262,24 @@
         EditPreferencesInfo.defaults());
     defaultPrefs.commit(md);
 
-    return parseEditPreferences(defaultPrefs.getConfig(), null, null);
+    return PreferencesParserUtil.parseEditPreferences(defaultPrefs.getConfig(), null, null);
   }
 
-  private static List<String> changeTable(Config cfg) {
-    return Lists.newArrayList(cfg.getStringList(CHANGE_TABLE, null, CHANGE_TABLE_COLUMN));
+  public static void validateMy(List<MenuItem> my) throws BadRequestException {
+    if (my == null) {
+      return;
+    }
+    for (MenuItem item : my) {
+      checkRequiredMenuItemField(item.name, "name");
+      checkRequiredMenuItemField(item.url, "URL");
+    }
+  }
+
+  static Config readDefaultConfig(AllUsersName allUsersName, Repository allUsersRepo)
+      throws IOException, ConfigInvalidException {
+    VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
+    defaultPrefs.load(allUsersName, allUsersRepo);
+    return defaultPrefs.getConfig();
   }
 
   private static void setChangeTable(Config cfg, List<String> changeTable) {
@@ -497,21 +289,6 @@
     }
   }
 
-  private static List<MenuItem> my(Config cfg) {
-    List<MenuItem> my = new ArrayList<>();
-    for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
-      String url = my(cfg, subsection, KEY_URL, "#/");
-      String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
-      my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
-    }
-    return my;
-  }
-
-  private static String my(Config cfg, String subsection, String key, String defaultValue) {
-    String val = cfg.getString(UserConfigSections.MY, subsection, key);
-    return !Strings.isNullOrEmpty(val) ? val : defaultValue;
-  }
-
   private static void setMy(Config cfg, List<MenuItem> my) {
     if (my != null) {
       unsetSection(cfg, UserConfigSections.MY);
@@ -526,16 +303,6 @@
     }
   }
 
-  public static void validateMy(List<MenuItem> my) throws BadRequestException {
-    if (my == null) {
-      return;
-    }
-    for (MenuItem item : my) {
-      checkRequiredMenuItemField(item.name, "name");
-      checkRequiredMenuItemField(item.url, "URL");
-    }
-  }
-
   private static void checkRequiredMenuItemField(String value, String name)
       throws BadRequestException {
     if (isNullOrEmpty(value)) {
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 8c4f275..8f97b68 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -140,7 +140,6 @@
           COMMIT_FOOTERS,
           CURRENT_ACTIONS,
           CURRENT_COMMIT,
-          DETAILED_LABELS, // may need to load ChangeNotes to check remove reviewer permissions
           MESSAGES);
 
   @Singleton
@@ -722,7 +721,10 @@
     // testRemoveReviewer check for a specific reviewer in the loop saving potentially many
     // permission checks.
     boolean canRemoveAnyReviewer =
-        permissionBackendForChange(userProvider.get(), cd).test(ChangePermission.REMOVE_REVIEWER);
+        permissionBackend
+            .user(userProvider.get())
+            .change(cd)
+            .test(ChangePermission.REMOVE_REVIEWER);
     for (LabelInfo label : labels) {
       if (label.all == null) {
         continue;
@@ -817,16 +819,4 @@
     }
     return map;
   }
-
-  /**
-   * @return {@link com.google.gerrit.server.permissions.PermissionBackend.ForChange} constructed
-   *     from either an index-backed or a database-backed {@link ChangeData} depending on {@code
-   *     lazyload}.
-   */
-  private PermissionBackend.ForChange permissionBackendForChange(CurrentUser user, ChangeData cd) {
-    PermissionBackend.WithUser withUser = permissionBackend.user(user);
-    return lazyLoad
-        ? withUser.change(cd)
-        : withUser.indexedChange(cd, notesFactory.createFromIndexedChange(cd.change()));
-  }
 }
diff --git a/java/com/google/gerrit/server/change/LabelsJson.java b/java/com/google/gerrit/server/change/LabelsJson.java
index c6f4969..2db17d6 100644
--- a/java/com/google/gerrit/server/change/LabelsJson.java
+++ b/java/com/google/gerrit/server/change/LabelsJson.java
@@ -131,7 +131,7 @@
 
     Map<String, Short> labels = null;
     Set<LabelPermission.WithValue> can =
-        permissionBackendForChange(filterApprovalsBy, cd).testLabels(toCheck.values());
+        permissionBackend.absentUser(filterApprovalsBy).change(cd).testLabels(toCheck.values());
     SetMultimap<String, String> permitted = LinkedHashMultimap.create();
     for (SubmitRecord rec : submitRecords(cd)) {
       if (rec.labels == null) {
@@ -452,7 +452,7 @@
 
     LabelTypes labelTypes = cd.getLabelTypes();
     for (Account.Id accountId : allUsers) {
-      PermissionBackend.ForChange perm = permissionBackendForChange(accountId, cd);
+      PermissionBackend.ForChange perm = permissionBackend.absentUser(accountId).change(cd);
       Map<String, VotingRangeInfo> pvr = getPermittedVotingRanges(permittedLabels(accountId, cd));
       for (Map.Entry<String, LabelWithStatus> e : labels.entrySet()) {
         LabelType lt = labelTypes.byLabel(e.getKey());
@@ -492,18 +492,6 @@
     }
   }
 
-  /**
-   * @return {@link com.google.gerrit.server.permissions.PermissionBackend.ForChange} constructed
-   *     from either an index-backed or a database-backed {@link ChangeData} depending on {@code
-   *     lazyload}.
-   */
-  private PermissionBackend.ForChange permissionBackendForChange(Account.Id user, ChangeData cd) {
-    PermissionBackend.WithUser withUser = permissionBackend.absentUser(user);
-    return lazyLoad
-        ? withUser.change(cd)
-        : withUser.indexedChange(cd, notesFactory.createFromIndexedChange(cd.change()));
-  }
-
   private List<SubmitRecord> submitRecords(ChangeData cd) {
     return cd.submitRecords(ChangeJson.SUBMIT_RULE_OPTIONS_LENIENT);
   }
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index 001a532..414107f 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -31,7 +31,6 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Change;
@@ -60,7 +59,6 @@
 import com.google.gerrit.server.account.GpgApiAdapter;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MergeUtil;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
@@ -107,8 +105,6 @@
   private final AnonymousUser anonymous;
   private final GitRepositoryManager repoManager;
   private final PermissionBackend permissionBackend;
-  private final ChangeNotes.Factory notesFactory;
-  private final boolean lazyLoad;
 
   @Inject
   RevisionJson(
@@ -128,7 +124,6 @@
       ChangeKindCache changeKindCache,
       GitRepositoryManager repoManager,
       PermissionBackend permissionBackend,
-      ChangeNotes.Factory notesFactory,
       @Assisted Iterable<ListChangesOption> options) {
     this.userProvider = userProvider;
     this.anonymous = anonymous;
@@ -145,10 +140,8 @@
     this.changeResourceFactory = changeResourceFactory;
     this.changeKindCache = changeKindCache;
     this.permissionBackend = permissionBackend;
-    this.notesFactory = notesFactory;
     this.repoManager = repoManager;
     this.options = ImmutableSet.copyOf(options);
-    this.lazyLoad = containsAnyOf(this.options, ChangeJson.REQUIRE_LAZY_LOAD);
   }
 
   /**
@@ -346,22 +339,9 @@
     return options.contains(option);
   }
 
-  /**
-   * @return {@link com.google.gerrit.server.permissions.PermissionBackend.ForChange} constructed
-   *     from either an index-backed or a database-backed {@link ChangeData} depending on {@code
-   *     lazyload}.
-   */
-  private PermissionBackend.ForChange permissionBackendForChange(
-      PermissionBackend.WithUser withUser, ChangeData cd) {
-    return lazyLoad
-        ? withUser.change(cd)
-        : withUser.indexedChange(cd, notesFactory.createFromIndexedChange(cd.change()));
-  }
-
   private boolean isWorldReadable(ChangeData cd) throws PermissionBackendException {
     try {
-      permissionBackendForChange(permissionBackend.user(anonymous), cd)
-          .check(ChangePermission.READ);
+      permissionBackend.user(anonymous).change(cd).check(ChangePermission.READ);
     } catch (AuthException ae) {
       return false;
     }
@@ -382,9 +362,4 @@
   private RevWalk newRevWalk(@Nullable Repository repo) {
     return repo != null ? new RevWalk(repo) : null;
   }
-
-  private static boolean containsAnyOf(
-      ImmutableSet<ListChangesOption> set, ImmutableSet<ListChangesOption> toFind) {
-    return !Sets.intersection(toFind, set).isEmpty();
-  }
 }
diff --git a/java/com/google/gerrit/server/config/CachedPreferences.java b/java/com/google/gerrit/server/config/CachedPreferences.java
index f4dcd10..388f58a 100644
--- a/java/com/google/gerrit/server/config/CachedPreferences.java
+++ b/java/com/google/gerrit/server/config/CachedPreferences.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.server.account.StoredPreferences;
 import java.util.Optional;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
@@ -52,7 +51,7 @@
   public static GeneralPreferencesInfo general(
       Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
     try {
-      return StoredPreferences.parseGeneralPreferences(
+      return PreferencesParserUtil.parseGeneralPreferences(
           userPreferences.asConfig(), configOrNull(defaultPreferences), null);
     } catch (ConfigInvalidException e) {
       return GeneralPreferencesInfo.defaults();
@@ -62,7 +61,7 @@
   public static EditPreferencesInfo edit(
       Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
     try {
-      return StoredPreferences.parseEditPreferences(
+      return PreferencesParserUtil.parseEditPreferences(
           userPreferences.asConfig(), configOrNull(defaultPreferences), null);
     } catch (ConfigInvalidException e) {
       return EditPreferencesInfo.defaults();
@@ -72,7 +71,7 @@
   public static DiffPreferencesInfo diff(
       Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
     try {
-      return StoredPreferences.parseDiffPreferences(
+      return PreferencesParserUtil.parseDiffPreferences(
           userPreferences.asConfig(), configOrNull(defaultPreferences), null);
     } catch (ConfigInvalidException e) {
       return DiffPreferencesInfo.defaults();
diff --git a/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java b/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java
deleted file mode 100644
index 4c9e5f0..0000000
--- a/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2012 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.config;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.inject.BindingAnnotation;
-import java.lang.annotation.Retention;
-
-/**
- * Marker on the global {@link ListeningExecutorService} used by asynchronous {@link BatchUpdate}s.
- */
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface ChangeUpdateExecutor {}
diff --git a/java/com/google/gerrit/server/config/PluginConfigFactory.java b/java/com/google/gerrit/server/config/PluginConfigFactory.java
index 842c79c..483fc0a 100644
--- a/java/com/google/gerrit/server/config/PluginConfigFactory.java
+++ b/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -230,7 +230,8 @@
       cfg.load();
     } catch (ConfigInvalidException e) {
       // This is an error in user input, don't spam logs with a stack trace.
-      logger.atWarning().log("Failed to load %s: %s", pluginConfigFile.toAbsolutePath(), e);
+      logger.atWarning().log(
+          "Failed to load %s: %s", pluginConfigFile.toAbsolutePath(), e.getMessage());
     } catch (IOException e) {
       logger.atWarning().withCause(e).log("Failed to load %s", pluginConfigFile.toAbsolutePath());
     }
diff --git a/java/com/google/gerrit/server/config/PreferencesParserUtil.java b/java/com/google/gerrit/server/config/PreferencesParserUtil.java
new file mode 100644
index 0000000..69d75be
--- /dev/null
+++ b/java/com/google/gerrit/server/config/PreferencesParserUtil.java
@@ -0,0 +1,266 @@
+// Copyright (C) 2020 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.config;
+
+import static com.google.gerrit.server.config.ConfigUtil.loadSection;
+import static com.google.gerrit.server.config.ConfigUtil.skipField;
+import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
+import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
+import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
+import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
+import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.EditPreferencesInfo;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.client.MenuItem;
+import com.google.gerrit.server.git.UserConfigSections;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+
+/** Helper to read default or user preferences from Git-style config files. */
+public class PreferencesParserUtil {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  private PreferencesParserUtil() {}
+
+  /**
+   * Returns a {@link GeneralPreferencesInfo} that is the result of parsing {@code defaultCfg} for
+   * the server's default configs and {@code cfg} for the user's config. These configs are then
+   * overlaid to inherit values (default -> user -> input (if provided).
+   */
+  public static GeneralPreferencesInfo parseGeneralPreferences(
+      Config cfg, @Nullable Config defaultCfg, @Nullable GeneralPreferencesInfo input)
+      throws ConfigInvalidException {
+    GeneralPreferencesInfo r =
+        loadSection(
+            cfg,
+            UserConfigSections.GENERAL,
+            null,
+            new GeneralPreferencesInfo(),
+            defaultCfg != null
+                ? parseDefaultGeneralPreferences(defaultCfg, input)
+                : GeneralPreferencesInfo.defaults(),
+            input);
+    if (input != null) {
+      r.changeTable = input.changeTable;
+      r.my = input.my;
+    } else {
+      r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
+      r.my = parseMyMenus(cfg, defaultCfg);
+    }
+    return r;
+  }
+
+  /**
+   * Returns a {@link GeneralPreferencesInfo} that is the result of parsing {@code defaultCfg} for
+   * the server's default configs. These configs are then overlaid to inherit values (default ->
+   * input (if provided).
+   */
+  public static GeneralPreferencesInfo parseDefaultGeneralPreferences(
+      Config defaultCfg, GeneralPreferencesInfo input) throws ConfigInvalidException {
+    GeneralPreferencesInfo allUserPrefs = new GeneralPreferencesInfo();
+    loadSection(
+        defaultCfg,
+        UserConfigSections.GENERAL,
+        null,
+        allUserPrefs,
+        GeneralPreferencesInfo.defaults(),
+        input);
+    return updateGeneralPreferencesDefaults(allUserPrefs);
+  }
+
+  /**
+   * Returns a {@link DiffPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
+   * server's default configs and {@code cfg} for the user's config. These configs are then overlaid
+   * to inherit values (default -> user -> input (if provided).
+   */
+  public static DiffPreferencesInfo parseDiffPreferences(
+      Config cfg, @Nullable Config defaultCfg, @Nullable DiffPreferencesInfo input)
+      throws ConfigInvalidException {
+    return loadSection(
+        cfg,
+        UserConfigSections.DIFF,
+        null,
+        new DiffPreferencesInfo(),
+        defaultCfg != null
+            ? parseDefaultDiffPreferences(defaultCfg, input)
+            : DiffPreferencesInfo.defaults(),
+        input);
+  }
+
+  /**
+   * Returns a {@link DiffPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
+   * server's default configs. These configs are then overlaid to inherit values (default -> input
+   * (if provided).
+   */
+  public static DiffPreferencesInfo parseDefaultDiffPreferences(
+      Config defaultCfg, DiffPreferencesInfo input) throws ConfigInvalidException {
+    DiffPreferencesInfo allUserPrefs = new DiffPreferencesInfo();
+    loadSection(
+        defaultCfg,
+        UserConfigSections.DIFF,
+        null,
+        allUserPrefs,
+        DiffPreferencesInfo.defaults(),
+        input);
+    return updateDiffPreferencesDefaults(allUserPrefs);
+  }
+
+  /**
+   * Returns a {@link EditPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
+   * server's default configs and {@code cfg} for the user's config. These configs are then overlaid
+   * to inherit values (default -> user -> input (if provided).
+   */
+  public static EditPreferencesInfo parseEditPreferences(
+      Config cfg, @Nullable Config defaultCfg, @Nullable EditPreferencesInfo input)
+      throws ConfigInvalidException {
+    return loadSection(
+        cfg,
+        UserConfigSections.EDIT,
+        null,
+        new EditPreferencesInfo(),
+        defaultCfg != null
+            ? parseDefaultEditPreferences(defaultCfg, input)
+            : EditPreferencesInfo.defaults(),
+        input);
+  }
+
+  /**
+   * Returns a {@link EditPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
+   * server's default configs. These configs are then overlaid to inherit values (default -> input
+   * (if provided).
+   */
+  public static EditPreferencesInfo parseDefaultEditPreferences(
+      Config defaultCfg, EditPreferencesInfo input) throws ConfigInvalidException {
+    EditPreferencesInfo allUserPrefs = new EditPreferencesInfo();
+    loadSection(
+        defaultCfg,
+        UserConfigSections.EDIT,
+        null,
+        allUserPrefs,
+        EditPreferencesInfo.defaults(),
+        input);
+    return updateEditPreferencesDefaults(allUserPrefs);
+  }
+
+  private static List<String> parseChangeTableColumns(Config cfg, @Nullable Config defaultCfg) {
+    List<String> changeTable = changeTable(cfg);
+    if (changeTable == null && defaultCfg != null) {
+      changeTable = changeTable(defaultCfg);
+    }
+    return changeTable;
+  }
+
+  private static List<MenuItem> parseMyMenus(Config cfg, @Nullable Config defaultCfg) {
+    List<MenuItem> my = my(cfg);
+    if (my.isEmpty() && defaultCfg != null) {
+      my = my(defaultCfg);
+    }
+    if (my.isEmpty()) {
+      my.add(new MenuItem("Dashboard", "#/dashboard/self", null));
+      my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
+      my.add(new MenuItem("Edits", "#/q/has:edit", null));
+      my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
+      my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
+      my.add(new MenuItem("Groups", "#/settings/#Groups", null));
+    }
+    return my;
+  }
+
+  private static GeneralPreferencesInfo updateGeneralPreferencesDefaults(
+      GeneralPreferencesInfo input) {
+    GeneralPreferencesInfo result = GeneralPreferencesInfo.defaults();
+    try {
+      for (Field field : input.getClass().getDeclaredFields()) {
+        if (skipField(field)) {
+          continue;
+        }
+        Object newVal = field.get(input);
+        if (newVal != null) {
+          field.set(result, newVal);
+        }
+      }
+    } catch (IllegalAccessException e) {
+      logger.atSevere().withCause(e).log("Failed to apply default general preferences");
+      return GeneralPreferencesInfo.defaults();
+    }
+    return result;
+  }
+
+  private static DiffPreferencesInfo updateDiffPreferencesDefaults(DiffPreferencesInfo input) {
+    DiffPreferencesInfo result = DiffPreferencesInfo.defaults();
+    try {
+      for (Field field : input.getClass().getDeclaredFields()) {
+        if (skipField(field)) {
+          continue;
+        }
+        Object newVal = field.get(input);
+        if (newVal != null) {
+          field.set(result, newVal);
+        }
+      }
+    } catch (IllegalAccessException e) {
+      logger.atSevere().withCause(e).log("Failed to apply default diff preferences");
+      return DiffPreferencesInfo.defaults();
+    }
+    return result;
+  }
+
+  private static EditPreferencesInfo updateEditPreferencesDefaults(EditPreferencesInfo input) {
+    EditPreferencesInfo result = EditPreferencesInfo.defaults();
+    try {
+      for (Field field : input.getClass().getDeclaredFields()) {
+        if (skipField(field)) {
+          continue;
+        }
+        Object newVal = field.get(input);
+        if (newVal != null) {
+          field.set(result, newVal);
+        }
+      }
+    } catch (IllegalAccessException e) {
+      logger.atSevere().withCause(e).log("Failed to apply default edit preferences");
+      return EditPreferencesInfo.defaults();
+    }
+    return result;
+  }
+
+  private static List<String> changeTable(Config cfg) {
+    return Lists.newArrayList(cfg.getStringList(CHANGE_TABLE, null, CHANGE_TABLE_COLUMN));
+  }
+
+  private static List<MenuItem> my(Config cfg) {
+    List<MenuItem> my = new ArrayList<>();
+    for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
+      String url = my(cfg, subsection, KEY_URL, "#/");
+      String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
+      my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
+    }
+    return my;
+  }
+
+  private static String my(Config cfg, String subsection, String key, String defaultValue) {
+    String val = cfg.getString(UserConfigSections.MY, subsection, key);
+    return !Strings.isNullOrEmpty(val) ? val : defaultValue;
+  }
+}
diff --git a/java/com/google/gerrit/server/config/SysExecutorModule.java b/java/com/google/gerrit/server/config/SysExecutorModule.java
index 6c729d9..e7f4540 100644
--- a/java/com/google/gerrit/server/config/SysExecutorModule.java
+++ b/java/com/google/gerrit/server/config/SysExecutorModule.java
@@ -14,19 +14,13 @@
 
 package com.google.gerrit.server.config;
 
-import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.google.gerrit.server.FanOutExecutor;
 import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 import com.google.inject.Singleton;
-import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 import org.eclipse.jgit.lib.Config;
 
 /**
@@ -73,28 +67,4 @@
     }
     return queues.createQueue(poolSize, "FanOut");
   }
-
-  @Provides
-  @Singleton
-  @ChangeUpdateExecutor
-  public ListeningExecutorService createChangeUpdateExecutor(@GerritServerConfig Config config) {
-    int poolSize = config.getInt("receive", null, "changeUpdateThreads", 1);
-    if (poolSize <= 1) {
-      return MoreExecutors.newDirectExecutorService();
-    }
-    return MoreExecutors.listeningDecorator(
-        new LoggingContextAwareExecutorService(
-            MoreExecutors.getExitingExecutorService(
-                new ThreadPoolExecutor(
-                    1,
-                    poolSize,
-                    10,
-                    TimeUnit.MINUTES,
-                    new ArrayBlockingQueue<>(poolSize),
-                    new ThreadFactoryBuilder()
-                        .setNameFormat("ChangeUpdate-%d")
-                        .setDaemon(true)
-                        .build(),
-                    new ThreadPoolExecutor.CallerRunsPolicy()))));
-  }
 }
diff --git a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
index 196fc61..fed6541 100644
--- a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
+++ b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
@@ -165,7 +165,7 @@
         List<CachedChange> result = new ArrayList<>(cds.size());
         for (ChangeData cd : cds) {
           result.add(
-              new AutoValue_SearchingChangeCacheImpl_CachedChange(cd.change(), cd.getReviewers()));
+              new AutoValue_SearchingChangeCacheImpl_CachedChange(cd.change(), cd.reviewers()));
         }
         return Collections.unmodifiableList(result);
       }
diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
index 85dc034..8bd3e7e 100644
--- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -417,6 +417,16 @@
     metrics.latencyPerPush.record(pushType, deltaNanos, NANOSECONDS);
   }
 
+  /**
+   * Sends all messages which have been collected while processing the push to the client.
+   *
+   * @see ReceiveCommits#sendMessages()
+   */
+  @UsedAt(UsedAt.Project.GOOGLE)
+  public void sendMessages() {
+    receiveCommits.sendMessages();
+  }
+
   public ReceivePack getReceivePack() {
     return receivePack;
   }
diff --git a/java/com/google/gerrit/server/mail/SignedToken.java b/java/com/google/gerrit/server/mail/SignedToken.java
index a010e30..7dcac1a 100644
--- a/java/com/google/gerrit/server/mail/SignedToken.java
+++ b/java/com/google/gerrit/server/mail/SignedToken.java
@@ -22,6 +22,7 @@
 import javax.crypto.Mac;
 import javax.crypto.ShortBufferException;
 import javax.crypto.spec.SecretKeySpec;
+import org.apache.commons.codec.binary.Base64;
 
 /**
  * Utility function to compute and verify XSRF tokens.
@@ -46,7 +47,7 @@
   public static String generateRandomKey() {
     final byte[] r = new byte[26];
     new SecureRandom().nextBytes(r);
-    return encodeBase64(r);
+    return encodeBase64PrivateKey(r);
   }
 
   private final int maxAge;
@@ -63,7 +64,7 @@
    */
   public SignedToken(final int age, final String keyBase64) throws XsrfException {
     maxAge = age > 5 ? age / 5 : age;
-    key = new SecretKeySpec(decodeBase64(keyBase64), MAC_ALG);
+    key = new SecretKeySpec(decodeBase64PrivateKey(keyBase64), MAC_ALG);
     rng = new SecureRandom();
     tokenLength = 2 * INT_SZ + newMac().getMacLength();
   }
@@ -169,6 +170,14 @@
     return (int) (System.currentTimeMillis() / 5000L);
   }
 
+  private static byte[] decodeBase64PrivateKey(final String privateKeyBase64String) {
+    return Base64.decodeBase64(toBytes(privateKeyBase64String));
+  }
+
+  private static String encodeBase64PrivateKey(final byte[] buf) {
+    return toString(Base64.encodeBase64(buf));
+  }
+
   private static byte[] decodeBase64(final String s) {
     return BaseEncoding.base64Url().decode(s);
   }
@@ -208,4 +217,12 @@
     }
     return r;
   }
+
+  private static String toString(final byte[] b) {
+    final StringBuilder r = new StringBuilder(b.length);
+    for (int i = 0; i < b.length; i++) {
+      r.append((char) b[i]);
+    }
+    return r.toString();
+  }
 }
diff --git a/java/com/google/gerrit/server/patch/Text.java b/java/com/google/gerrit/server/patch/Text.java
index f127f44..cc0a5e4 100644
--- a/java/com/google/gerrit/server/patch/Text.java
+++ b/java/com/google/gerrit/server/patch/Text.java
@@ -162,11 +162,11 @@
       return Charset.forName(encoding);
 
     } catch (IllegalCharsetNameException err) {
-      logger.atSevere().log("Invalid detected charset name '%s': %s", encoding, err);
+      logger.atSevere().log("Invalid detected charset name '%s': %s", encoding, err.getMessage());
       return ISO_8859_1;
 
     } catch (UnsupportedCharsetException err) {
-      logger.atSevere().log("Detected charset '%s' not supported: %s", encoding, err);
+      logger.atSevere().log("Detected charset '%s' not supported: %s", encoding, err.getMessage());
       return ISO_8859_1;
     }
   }
diff --git a/java/com/google/gerrit/server/permissions/ChangeControl.java b/java/com/google/gerrit/server/permissions/ChangeControl.java
index 253f50c..f56c3c0 100644
--- a/java/com/google/gerrit/server/permissions/ChangeControl.java
+++ b/java/com/google/gerrit/server/permissions/ChangeControl.java
@@ -19,21 +19,16 @@
 
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRange;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.conditions.BooleanCondition;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Map;
@@ -41,39 +36,16 @@
 
 /** Access control management for a user accessing a single change. */
 class ChangeControl {
-  @Singleton
-  static class Factory {
-    private final ChangeData.Factory changeDataFactory;
-    private final ChangeNotes.Factory notesFactory;
-
-    @Inject
-    Factory(ChangeData.Factory changeDataFactory, ChangeNotes.Factory notesFactory) {
-      this.changeDataFactory = changeDataFactory;
-      this.notesFactory = notesFactory;
-    }
-
-    ChangeControl create(RefControl refControl, Project.NameKey project, Change.Id changeId) {
-      return create(refControl, notesFactory.create(project, changeId));
-    }
-
-    ChangeControl create(RefControl refControl, ChangeNotes notes) {
-      return new ChangeControl(changeDataFactory, refControl, notes);
-    }
-  }
-
-  private final ChangeData.Factory changeDataFactory;
   private final RefControl refControl;
-  private final ChangeNotes notes;
+  private final ChangeData changeData;
 
-  private ChangeControl(
-      ChangeData.Factory changeDataFactory, RefControl refControl, ChangeNotes notes) {
-    this.changeDataFactory = changeDataFactory;
+  ChangeControl(RefControl refControl, ChangeData changeData) {
     this.refControl = refControl;
-    this.notes = notes;
+    this.changeData = changeData;
   }
 
-  ForChange asForChange(@Nullable ChangeData cd) {
-    return new ForChangeImpl(cd);
+  ForChange asForChange() {
+    return new ForChangeImpl();
   }
 
   private CurrentUser getUser() {
@@ -85,7 +57,7 @@
   }
 
   private Change getChange() {
-    return notes.getChange();
+    return changeData.change();
   }
 
   /** Can this user see this change? */
@@ -218,19 +190,13 @@
   }
 
   private class ForChangeImpl extends ForChange {
-    private ChangeData cd;
     private Map<String, PermissionRange> labels;
     private String resourcePath;
 
-    ForChangeImpl(@Nullable ChangeData cd) {
-      this.cd = cd;
-    }
+    private ForChangeImpl() {}
 
     private ChangeData changeData() {
-      if (cd == null) {
-        cd = changeDataFactory.create(notes);
-      }
-      return cd;
+      return changeData;
     }
 
     @Override
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackendModule.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackendModule.java
index f3a3c78..3f84dff 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackendModule.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackendModule.java
@@ -31,7 +31,6 @@
       // TODO(hiesel) Hide ProjectControl, RefControl, ChangeControl related bindings.
       factory(ProjectControl.Factory.class);
       factory(DefaultRefFilter.Factory.class);
-      bind(ChangeControl.Factory.class);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index 90d9d88..4e1a30c 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -448,12 +448,11 @@
     try {
       Map<Change.Id, BranchNameKey> visibleChanges = new HashMap<>();
       for (ChangeData cd : changeCache.getChangeData(project)) {
-        ChangeNotes notes = changeNotesFactory.createFromIndexedChange(cd.change());
         if (!projectState.statePermitsRead()) {
           continue;
         }
         try {
-          permissionBackendForProject.indexedChange(cd, notes).check(ChangePermission.READ);
+          permissionBackendForProject.change(cd).check(ChangePermission.READ);
           visibleChanges.put(cd.getId(), cd.change().getDest());
         } catch (AuthException e) {
           // Do nothing.
diff --git a/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java b/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
index 2344781..749ca6b 100644
--- a/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
@@ -173,11 +173,6 @@
     }
 
     @Override
-    public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
-      return new FailedChange(message, cause);
-    }
-
-    @Override
     public void check(RefPermission perm) throws PermissionBackendException {
       throw new PermissionBackendException(message, cause);
     }
diff --git a/java/com/google/gerrit/server/permissions/PermissionBackend.java b/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 653c3b5..23145ba 100644
--- a/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -173,15 +173,6 @@
       return ref(notes.getChange().getDest()).change(notes);
     }
 
-    /**
-     * Returns an instance scoped for the change loaded from index, and its destination ref and
-     * project. This method should only be used when database access is harmful and potentially
-     * stale data from the index is acceptable.
-     */
-    public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
-      return ref(notes.getChange().getDest()).indexedChange(cd, notes);
-    }
-
     /** Verify scoped user can {@code perm}, throwing if denied. */
     public abstract void check(GlobalOrPluginPermission perm)
         throws AuthException, PermissionBackendException;
@@ -289,15 +280,6 @@
       return ref(notes.getChange().getDest().branch()).change(notes);
     }
 
-    /**
-     * Returns an instance scoped for the change loaded from index, and its destination ref and
-     * project. This method should only be used when database access is harmful and potentially
-     * stale data from the index is acceptable.
-     */
-    public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
-      return ref(notes.getChange().getDest().branch()).indexedChange(cd, notes);
-    }
-
     /** Verify scoped user can {@code perm}, throwing if denied. */
     public abstract void check(CoreOrPluginProjectPermission perm)
         throws AuthException, PermissionBackendException;
@@ -386,12 +368,6 @@
     /** Returns an instance scoped to change. */
     public abstract ForChange change(ChangeNotes notes);
 
-    /**
-     * @return instance scoped to change loaded from index. This method should only be used when
-     *     database access is harmful and potentially stale data from the index is acceptable.
-     */
-    public abstract ForChange indexedChange(ChangeData cd, ChangeNotes notes);
-
     /** Verify scoped user can {@code perm}, throwing if denied. */
     public abstract void check(RefPermission perm) throws AuthException, PermissionBackendException;
 
diff --git a/java/com/google/gerrit/server/permissions/ProjectControl.java b/java/com/google/gerrit/server/permissions/ProjectControl.java
index 145e0b6..e6d66ee 100644
--- a/java/com/google/gerrit/server/permissions/ProjectControl.java
+++ b/java/com/google/gerrit/server/permissions/ProjectControl.java
@@ -70,9 +70,9 @@
   private final PermissionBackend permissionBackend;
   private final CurrentUser user;
   private final ProjectState state;
-  private final ChangeControl.Factory changeControlFactory;
   private final PermissionCollection.Factory permissionFilter;
   private final DefaultRefFilter.Factory refFilterFactory;
+  private final ChangeData.Factory changeDataFactory;
 
   private List<SectionMatcher> allSections;
   private Map<String, RefControl> refControls;
@@ -83,17 +83,17 @@
       @GitUploadPackGroups Set<AccountGroup.UUID> uploadGroups,
       @GitReceivePackGroups Set<AccountGroup.UUID> receiveGroups,
       PermissionCollection.Factory permissionFilter,
-      ChangeControl.Factory changeControlFactory,
       PermissionBackend permissionBackend,
       DefaultRefFilter.Factory refFilterFactory,
+      ChangeData.Factory changeDataFactory,
       @Assisted CurrentUser who,
       @Assisted ProjectState ps) {
-    this.changeControlFactory = changeControlFactory;
     this.uploadGroups = uploadGroups;
     this.receiveGroups = receiveGroups;
     this.permissionFilter = permissionFilter;
     this.permissionBackend = permissionBackend;
     this.refFilterFactory = refFilterFactory;
+    this.changeDataFactory = changeDataFactory;
     user = who;
     state = ps;
   }
@@ -102,13 +102,8 @@
     return new ForProjectImpl();
   }
 
-  ChangeControl controlFor(Change change) {
-    return changeControlFactory.create(
-        controlForRef(change.getDest()), change.getProject(), change.getId());
-  }
-
-  ChangeControl controlFor(ChangeNotes notes) {
-    return changeControlFactory.create(controlForRef(notes.getChange().getDest()), notes);
+  ChangeControl controlFor(ChangeData cd) {
+    return new ChangeControl(controlForRef(cd.change().getDest()), cd);
   }
 
   RefControl controlForRef(BranchNameKey ref) {
@@ -122,7 +117,7 @@
     RefControl ctl = refControls.get(refName);
     if (ctl == null) {
       PermissionCollection relevant = permissionFilter.filter(access(), refName, user);
-      ctl = new RefControl(this, refName, relevant);
+      ctl = new RefControl(changeDataFactory, this, refName, relevant);
       refControls.put(refName, ctl);
     }
     return ctl;
diff --git a/java/com/google/gerrit/server/permissions/RefControl.java b/java/com/google/gerrit/server/permissions/RefControl.java
index 2c633ba..395b8cf 100644
--- a/java/com/google/gerrit/server/permissions/RefControl.java
+++ b/java/com/google/gerrit/server/permissions/RefControl.java
@@ -43,6 +43,7 @@
 class RefControl {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
+  private final ChangeData.Factory changeDataFactory;
   private final ProjectControl projectControl;
   private final String refName;
 
@@ -58,7 +59,12 @@
   private Boolean canForgeCommitter;
   private Boolean isVisible;
 
-  RefControl(ProjectControl projectControl, String ref, PermissionCollection relevant) {
+  RefControl(
+      ChangeData.Factory changeDataFactory,
+      ProjectControl projectControl,
+      String ref,
+      PermissionCollection relevant) {
+    this.changeDataFactory = changeDataFactory;
     this.projectControl = projectControl;
     this.refName = ref;
     this.relevant = relevant;
@@ -440,7 +446,7 @@
     @Override
     public ForChange change(ChangeData cd) {
       try {
-        return getProjectControl().controlFor(cd.notes()).asForChange(cd);
+        return getProjectControl().controlFor(cd).asForChange();
       } catch (StorageException e) {
         return FailedPermissionBackend.change("unavailable", e);
       }
@@ -455,12 +461,9 @@
           "expected change in project %s, not %s",
           project,
           change.getProject());
-      return getProjectControl().controlFor(notes).asForChange(null);
-    }
-
-    @Override
-    public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
-      return getProjectControl().controlFor(notes).asForChange(cd);
+      // Having ChangeNotes means it's OK to load values from NoteDb if needed.
+      // ChangeData.Factory will allow lazyLoading
+      return getProjectControl().controlFor(changeDataFactory.create(notes)).asForChange();
     }
 
     @Override
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 6e55bf2..ae23821 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -674,7 +674,9 @@
   public ReviewerSet reviewers() {
     if (reviewers == null) {
       if (!lazyLoad) {
-        return ReviewerSet.empty();
+        // We are not allowed to load values from NoteDb. Reviewers were not populated with values
+        // from the index. However, we need these values for permission checks.
+        throw new IllegalStateException("reviewers not populated");
       }
       reviewers = approvalsUtil.getReviewers(notes(), approvals().values());
     }
@@ -685,10 +687,6 @@
     this.reviewers = reviewers;
   }
 
-  public ReviewerSet getReviewers() {
-    return reviewers;
-  }
-
   public ReviewerByEmailSet reviewersByEmail() {
     if (reviewersByEmail == null) {
       if (!lazyLoad) {
diff --git a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
index 077131b..bf96180 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
@@ -72,7 +72,6 @@
       return false;
     }
 
-    ChangeNotes notes = notesFactory.createFromIndexedChange(change);
     Optional<ProjectState> projectState = projectCache.get(cd.project());
     if (!projectState.isPresent()) {
       logger.atFine().log("Filter out change %s of non-existing project %s", cd, cd.project());
@@ -86,9 +85,12 @@
     PermissionBackend.WithUser withUser =
         user.isIdentifiedUser()
             ? permissionBackend.absentUser(user.getAccountId())
-            : permissionBackend.user(anonymousUserProvider.get());
+            : permissionBackend.user(
+                Optional.of(user)
+                    .filter(u -> u instanceof SingleGroupUser)
+                    .orElseGet(anonymousUserProvider::get));
     try {
-      withUser.indexedChange(cd, notes).check(ChangePermission.READ);
+      withUser.change(cd).check(ChangePermission.READ);
     } catch (PermissionBackendException e) {
       Throwable cause = e.getCause();
       if (cause instanceof RepositoryNotFoundException) {
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
index e544509..409b0d5 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
@@ -73,7 +73,7 @@
       throws PermissionBackendException {
     ImmutableList<CommentInfo> commentInfos = getCommentFormatter().formatAsList(comments);
     List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
-    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
+    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages, true);
     return commentInfos;
   }
 
@@ -83,7 +83,7 @@
     List<CommentInfo> commentInfos =
         commentInfosMap.values().stream().flatMap(List::stream).collect(toList());
     List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
-    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
+    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages, true);
     return commentInfosMap;
   }
 
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
index 0ed7d60..d841183 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
@@ -63,7 +63,7 @@
     List<RobotCommentInfo> commentInfos =
         robotCommentsMap.values().stream().flatMap(List::stream).collect(toList());
     List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
-    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
+    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages, false);
     return Response.ok(robotCommentsMap);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index 742eaca..25f4005 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -64,7 +64,7 @@
       Iterable<RobotComment> comments, RevisionResource rsrc) throws PermissionBackendException {
     ImmutableList<RobotCommentInfo> commentInfos = getCommentFormatter().formatAsList(comments);
     List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
-    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
+    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages, false);
     return commentInfos;
   }
 
@@ -74,7 +74,7 @@
     List<RobotCommentInfo> commentInfos =
         commentInfosMap.values().stream().flatMap(List::stream).collect(toList());
     List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
-    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
+    CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages, false);
     return commentInfosMap;
   }
 
diff --git a/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
index 83b0262..8185281 100644
--- a/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
@@ -19,9 +19,9 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.account.StoredPreferences;
 import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.config.DefaultPreferencesCache;
+import com.google.gerrit.server.config.PreferencesParserUtil;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -41,7 +41,7 @@
   public Response<DiffPreferencesInfo> apply(ConfigResource configResource)
       throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
     return Response.ok(
-        StoredPreferences.parseDiffPreferences(
+        PreferencesParserUtil.parseDiffPreferences(
             defaultPreferenceCache.get().asConfig(), null, null));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
index 95fc10e..bb9e483 100644
--- a/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
@@ -19,9 +19,9 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.account.StoredPreferences;
 import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.config.DefaultPreferencesCache;
+import com.google.gerrit.server.config.PreferencesParserUtil;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -40,7 +40,7 @@
   public Response<EditPreferencesInfo> apply(ConfigResource configResource)
       throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
     return Response.ok(
-        StoredPreferences.parseEditPreferences(
+        PreferencesParserUtil.parseEditPreferences(
             defaultPreferenceCache.get().asConfig(), null, null));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/config/GetPreferences.java b/java/com/google/gerrit/server/restapi/config/GetPreferences.java
index 8a28d55..288055b 100644
--- a/java/com/google/gerrit/server/restapi/config/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetPreferences.java
@@ -17,9 +17,9 @@
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.account.StoredPreferences;
 import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.config.DefaultPreferencesCache;
+import com.google.gerrit.server.config.PreferencesParserUtil;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -38,7 +38,7 @@
   public Response<GeneralPreferencesInfo> apply(ConfigResource rsrc)
       throws IOException, ConfigInvalidException {
     return Response.ok(
-        StoredPreferences.parseGeneralPreferences(
+        PreferencesParserUtil.parseGeneralPreferences(
             defaultPreferenceCache.get().asConfig(), null, null));
   }
 }
diff --git a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
index 33c3584..edc3725 100644
--- a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
@@ -17,6 +17,7 @@
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.gerrit.server.submit.CommitMergeStatus.EMPTY_COMMIT;
 import static com.google.gerrit.server.submit.CommitMergeStatus.SKIPPED_IDENTICAL_TREE;
+import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.BooleanProjectConfig;
@@ -62,20 +63,42 @@
     } catch (IOException | StorageException e) {
       throw new StorageException("Commit sorting failed", e);
     }
-    List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
-    boolean first = true;
 
+    // We cannot rebase merge commits. This is why we integrate merge changes into the target branch
+    // the same way as if MERGE_IF_NECESSARY was the submit strategy. This means if needed we create
+    // a merge commit that integrates the merge change into the target branch.
+    // If we integrate a change series that consists out of a normal change and a merge change,
+    // where the merge change depends on the normal change, we must skip rebasing the normal change,
+    // because it already gets integrated by merging the merge change. If the rebasing of the normal
+    // change is not skipped, it would appear twice in the history after the submit is done (once
+    // through its rebased commit, and once through its original commit which is a parent of the
+    // merge change that was merged into the target branch. To skip the rebasing of the normal
+    // change, we call MergeUtil#reduceToMinimalMerge, as it excludes commits which will be
+    // implicitly integrated by merging the series. Then we use the MergeIfNecessaryOp to integrate
+    // the whole series.
+    // If on the other hand, we integrate a change series that consists out of a merge change and a
+    // normal change, where the normal change depends on the merge change, we can first integrate
+    // the merge change by a merge and then integrate the normal change by a rebase. In this case we
+    // do not want to call MergeUtil#reduceToMinimalMerge as we are not intending to integrate the
+    // whole series by a merge, but rather do the integration of the commits one by one.
+    boolean foundNonMerge = false;
     for (CodeReviewCommit c : sorted) {
       if (c.getParentCount() > 1) {
-        // Since there is a merge commit, sort and prune again using
-        // MERGE_IF_NECESSARY semantics to avoid creating duplicate
-        // commits.
-        //
+        if (!foundNonMerge) {
+          // found a merge change, but it doesn't depend on a normal change, this means we are not
+          // required to merge the whole series at once
+          continue;
+        }
+        // found a merge commit that depends on a normal change, this means we are required to merge
+        // the whole series at once
         sorted = args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, sorted);
-        break;
+        return sorted.stream().map(n -> new MergeIfNecessaryOp(n)).collect(toList());
       }
+      foundNonMerge = true;
     }
 
+    List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
+    boolean first = true;
     while (!sorted.isEmpty()) {
       CodeReviewCommit n = sorted.remove(0);
       if (first && args.mergeTip.getInitialTip() == null) {
@@ -87,7 +110,7 @@
       } else if (n.getParentCount() == 1) {
         ops.add(new RebaseOneOp(n));
       } else {
-        ops.add(new RebaseMultipleParentsOp(n));
+        ops.add(new MergeIfNecessaryOp(n));
       }
       first = false;
     }
@@ -254,8 +277,8 @@
     }
   }
 
-  private class RebaseMultipleParentsOp extends SubmitStrategyOp {
-    private RebaseMultipleParentsOp(CodeReviewCommit toMerge) {
+  private class MergeIfNecessaryOp extends SubmitStrategyOp {
+    private MergeIfNecessaryOp(CodeReviewCommit toMerge) {
       super(RebaseSubmitStrategy.this.args, toMerge);
     }
 
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index af9d8c3..baccbf9 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -18,7 +18,6 @@
 import static com.google.inject.Scopes.SINGLETON;
 
 import com.google.common.base.Strings;
-import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperationsImpl;
@@ -48,7 +47,6 @@
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
 import com.google.gerrit.server.config.CanonicalWebUrlModule;
 import com.google.gerrit.server.config.CanonicalWebUrlProvider;
-import com.google.gerrit.server.config.ChangeUpdateExecutor;
 import com.google.gerrit.server.config.DefaultUrlFormatter;
 import com.google.gerrit.server.config.GerritGlobalModule;
 import com.google.gerrit.server.config.GerritInstanceNameModule;
@@ -186,9 +184,6 @@
     bind(GitRepositoryManager.class).to(InMemoryRepositoryManager.class);
     bind(InMemoryRepositoryManager.class).in(SINGLETON);
     bind(TrackingFooters.class).toProvider(TrackingFootersProvider.class).in(SINGLETON);
-    bind(ListeningExecutorService.class)
-        .annotatedWith(ChangeUpdateExecutor.class)
-        .toInstance(MoreExecutors.newDirectExecutorService());
     bind(SecureStore.class).to(DefaultSecureStore.class);
 
     install(new InMemorySchemaModule());
diff --git a/java/com/google/gerrit/testing/TestCommentHelper.java b/java/com/google/gerrit/testing/TestCommentHelper.java
index 5ce6d13..dfa4601 100644
--- a/java/com/google/gerrit/testing/TestCommentHelper.java
+++ b/java/com/google/gerrit/testing/TestCommentHelper.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.extensions.client.Side;
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.common.FixSuggestionInfo;
+import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.inject.Inject;
 import java.util.Arrays;
 import java.util.Collection;
@@ -144,6 +145,7 @@
     reviewInput.robotComments =
         Collections.singletonMap(robotCommentInput.path, ImmutableList.of(robotCommentInput));
     reviewInput.message = message;
+    reviewInput.tag = ChangeMessagesUtil.AUTOGENERATED_TAG_PREFIX;
     gApi.changes().id(targetChangeId).current().review(reviewInput);
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 5c786a5..82a19d4 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -4382,6 +4382,7 @@
           .message("Modify rules.pl")
           .create();
     }
+    projectCache.evict(project);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
index 50f20c8..647a44e 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
@@ -1248,6 +1248,7 @@
           .message("Modify rules.pl")
           .create();
     }
+    projectCache.evict(project);
   }
 
   private List<ChangeApi> getChangeApis(RevertSubmissionInfo revertSubmissionInfo)
diff --git a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
index dab2d00..a9afcbc 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
@@ -106,6 +106,7 @@
       r.rule = rule;
       r.commit(md);
     }
+    projectCache.evict(project);
   }
 
   private static final String SUBMIT_TYPE_FROM_SUBJECT =
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
index 0b8f441..986c8c4 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
@@ -151,7 +151,7 @@
     TestTimeUtil.resetWithClockStep(0, TimeUnit.SECONDS);
     createChange();
     /* Advancing the time after creating the change so that the first robot comment is not in the same timestamp as with the change creation */
-    TestTimeUtil.incrementClock(5, TimeUnit.SECONDS);
+    TestTimeUtil.incrementClock(10, TimeUnit.SECONDS);
 
     RobotCommentInput c1 = TestCommentHelper.createRobotCommentInput(FILE_NAME);
     RobotCommentInput c2 = TestCommentHelper.createRobotCommentInput(FILE_NAME);
@@ -220,9 +220,9 @@
             .changeMessageId;
 
     /**
-     * Upload PS message, robot message 1 & robot comment 1 all have the same timestamp. The robot
-     * comment is matched to robot message 1 because the PS upload message is auto-generated and is
-     * ignored in matching
+     * All change messages have the auto-generated tag. Robot comments can be linked to
+     * auto-generated messages where each comment is linked to the next nearest change message in
+     * timestamp
      */
     assertThat(message1ChangeId).isEqualTo(comment1MessageId);
     assertThat(message2ChangeId).isEqualTo(comment2MessageId);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 72db9b3..2779284 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -299,6 +299,89 @@
     assertTrees(project, actual);
   }
 
+  /**
+   * Tests the following situation:
+   *
+   * <ul>
+   *   <li>1. create a change series, consisting out of a merge commit and a normal commit
+   *   <li>2. before submitting the change series, another non-conflicting change gets submitted
+   *   <li>3. when the change series gets submitted, Gerrit must perform a merge/rebase/cherry-pick
+   * </ul>
+   */
+  @Test
+  public void submitChangeSeriesWithMergeCommitThatIsBasedOnOldTip() throws Throwable {
+    RevCommit initialHead = projectOperations.project(project).getHead("master");
+
+    // create a commit which will become the first parent of a merge commit
+    PushOneCommit.Result parent1 =
+        pushFactory
+            .create(
+                admin.newIdent(),
+                testRepo,
+                "parent 2",
+                ImmutableMap.of("foo", "foo-2", "bar", "bar-2"))
+            .to("refs/heads/master");
+
+    // reset the testRepo in order to create a sibling of parent1
+    testRepo.reset(initialHead);
+
+    // create a stable branch that we can merge back into master later
+    BranchInput in = new BranchInput();
+    in.revision = initialHead.getName();
+    gApi.projects().name(project.get()).branch("refs/heads/stable").create(in);
+
+    // create one commit in the stable branch, which will become the second parent of the merge
+    // commit
+    PushOneCommit.Result parent2 =
+        pushFactory
+            .create(
+                admin.newIdent(),
+                testRepo,
+                "parent 1",
+                ImmutableMap.of("foo", "foo-1", "bar", "bar-1"))
+            .to("refs/heads/stable");
+
+    // create a merge change that merges the stable branch back into master
+    testRepo.reset(parent1.getCommit());
+    PushOneCommit m =
+        pushFactory.create(
+            admin.newIdent(), testRepo, "merge", ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
+    m.setParents(ImmutableList.of(parent1.getCommit(), parent2.getCommit()));
+    PushOneCommit.Result mergeChange = m.to("refs/for/master");
+    mergeChange.assertOkStatus();
+
+    // approve the merge change so that it becomes submittable
+    approve(mergeChange.getChangeId());
+
+    // create a successor change that depends on the merge change
+    PushOneCommit.Result successorChange = createChange("refs/for/master");
+
+    // simulate another developer submitting a change in the meantime (non-conflicting sibling
+    // commit of the merge commit), this means when the change series gets submitted Gerrit must
+    // perform a merge/rebase/cherry-pick now
+    testRepo.reset(parent1.getCommit());
+    submit(createChange("Other Change", "x.txt", "x content").getChangeId());
+
+    // submit the change series
+    if (getSubmitType() != SubmitType.FAST_FORWARD_ONLY) {
+      submit(successorChange.getChangeId());
+    } else {
+      submitWithConflict(
+          successorChange.getChangeId(),
+          "Failed to submit 2 changes due to the following problems:\n"
+              + "Change "
+              + mergeChange.getChange().getId()
+              + ": Project policy "
+              + "requires all submissions to be a fast-forward. Please "
+              + "rebase the change locally and upload again for review.\n"
+              + "Change "
+              + successorChange.getChange().getId()
+              + ": Project policy "
+              + "requires all submissions to be a fast-forward. Please "
+              + "rebase the change locally and upload again for review.");
+    }
+  }
+
   @Test
   public void submitNoPermission() throws Throwable {
     // create project where submit is blocked
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendIT.java
new file mode 100644
index 0000000..2aab159
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendIT.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2020 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.acceptance.server.permissions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.permissions.ChangePermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.inject.Inject;
+import org.junit.Test;
+
+/** Asserts behavior on {@link PermissionBackend} using a fully-started Gerrit. */
+public class PermissionBackendIT extends AbstractDaemonTest {
+  @Inject PermissionBackend pb;
+  @Inject ChangeNotes.Factory changeNotesFactory;
+
+  @Test
+  public void changeDataFromIndex_canCheckReviewerState() throws Exception {
+    Change.Id changeId = createChange().getChange().getId();
+    gApi.changes().id(changeId.get()).setPrivate(true);
+    gApi.changes().id(changeId.get()).addReviewer(user.email());
+
+    ChangeData changeData =
+        Iterables.getOnlyElement(queryProvider.get().byLegacyChangeId(changeId));
+    boolean reviewerCanSee =
+        pb.absentUser(user.id()).change(changeData).test(ChangePermission.READ);
+    assertThat(reviewerCanSee).isTrue();
+  }
+
+  @Test
+  public void changeDataFromNoteDb_canCheckReviewerState() throws Exception {
+    Change.Id changeId = createChange().getChange().getId();
+    gApi.changes().id(changeId.get()).setPrivate(true);
+    gApi.changes().id(changeId.get()).addReviewer(user.email());
+
+    ChangeNotes notes = changeNotesFactory.create(project, changeId);
+    ChangeData changeData = changeDataFactory.create(notes);
+    boolean reviewerCanSee =
+        pb.absentUser(user.id()).change(changeData).test(ChangePermission.READ);
+    assertThat(reviewerCanSee).isTrue();
+  }
+
+  @Test
+  public void changeNotes_canCheckReviewerState() throws Exception {
+    Change.Id changeId = createChange().getChange().getId();
+    gApi.changes().id(changeId.get()).setPrivate(true);
+    gApi.changes().id(changeId.get()).addReviewer(user.email());
+
+    ChangeNotes notes = changeNotesFactory.create(project, changeId);
+    boolean reviewerCanSee = pb.absentUser(user.id()).change(notes).test(ChangePermission.READ);
+    assertThat(reviewerCanSee).isTrue();
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
index e5432d1..18ae6c4 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
@@ -29,7 +29,6 @@
 import java.util.Collection;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Repository;
-import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -44,14 +43,6 @@
   @Inject private ProjectOperations projectOperations;
   @Inject private SubmitRuleEvaluator.Factory evaluatorFactory;
 
-  @Before
-  public void setUp() {
-    // We don't want caches to interfere with our tests. If we didn't, the cache would take
-    // precedence over the index, which would never be called.
-    baseConfig.setString("cache", "changes", "memoryLimit", "0");
-    baseConfig.setString("cache", "projects", "memoryLimit", "0");
-  }
-
   @Test
   public void testUnresolvedCommentsCountPredicate() throws Exception {
     modifySubmitRules("gerrit:unresolved_comments_count(0)");
@@ -119,5 +110,6 @@
           .message("Modify rules.pl")
           .create();
     }
+    projectCache.evict(project);
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java b/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
index 5a31bfd..58c2517 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
@@ -65,7 +65,7 @@
     session.exec(
         String.format("gerrit set-reviewers -%s %s %s", add ? "a" : "r", user.email(), id));
     session.assertSuccess();
-    ImmutableSet<Account.Id> reviewers = change.getChange().getReviewers().all();
+    ImmutableSet<Account.Id> reviewers = change.getChange().reviewers().all();
     if (add) {
       assertThat(reviewers).contains(user.id());
     } else {
diff --git a/javatests/com/google/gerrit/server/mail/SignedTokenTest.java b/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
index cfb551f..41d8d69 100644
--- a/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
+++ b/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
@@ -18,20 +18,14 @@
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
 import java.util.Random;
+import java.util.regex.Pattern;
 import org.junit.Before;
 import org.junit.Test;
 
 public class SignedTokenTest {
 
-  private static final String REGISTER_EMAIL_PRIVATE_KEY =
-      "R2Vycml0JTIwcmVnaXN0ZXJFbWFpbFByaXZhdGVLZXk=";
-  private static final String URL_SAFE_REGISTER_EMAIL_PRIVATE_KEY =
-      REGISTER_EMAIL_PRIVATE_KEY.replaceFirst("R2", "_-");
-  private static final String URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_PLUS =
-      REGISTER_EMAIL_PRIVATE_KEY.replaceFirst("R", "+");
-  private static final String URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_SLASH =
-      REGISTER_EMAIL_PRIVATE_KEY.replaceFirst("R", "/");
-
+  private static final Pattern URL_UNSAFE_CHARS = Pattern.compile("(\\+|/)");
+  private static final String REGISTER_EMAIL_PRIVATE_KEY = "TGMv3/bTC42jUKQndTQrXyHhHYMP0t69i/4=";
   private static final int maxAge = 5;
   private static final String TEXT = "This is a text";
   private static final String FORGED_TEXT = "This is a forged text";
@@ -44,29 +38,23 @@
     signedToken = new SignedToken(maxAge, REGISTER_EMAIL_PRIVATE_KEY);
   }
 
-  /**
-   * Test new token: the key is a normal BASE64 string without index of '62'(+ or _) or '63'(/ or -)
-   */
+  /** Test new token: the key is a normal BASE64 string that can be used for URL safely */
   @Test
   public void newTokenKeyDoesNotContainUnsafeChar() throws Exception {
-    new SignedToken(maxAge, REGISTER_EMAIL_PRIVATE_KEY);
-  }
-
-  /** Test new token: the key is an URL safe BASE64 string with indexes of '62'(_) and '63'(-) */
-  @Test
-  public void newTokenWithUrlSafeBase64() throws Exception {
-    new SignedToken(maxAge, URL_SAFE_REGISTER_EMAIL_PRIVATE_KEY);
+    assertThat(signedToken.newToken(TEXT)).doesNotContainMatch(URL_UNSAFE_CHARS);
   }
 
   /** Test new token: the key is an URL unsafe BASE64 string with index of '62'(+) */
   @Test
   public void newTokenWithUrlUnsafeBase64Plus() throws Exception {
-    IllegalArgumentException thrown =
-        assertThrows(
-            IllegalArgumentException.class,
-            () -> new SignedToken(maxAge, URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_PLUS));
+    String token = "+" + signedToken.newToken(TEXT);
+    CheckTokenException thrown =
+        assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
+
+    assertThat(thrown).hasMessageThat().contains("decoding failed");
 
     assertThat(thrown)
+        .hasCauseThat()
         .hasMessageThat()
         .isEqualTo(
             "com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: +");
@@ -75,12 +63,14 @@
   /** Test new token: the key is an URL unsafe BASE64 string with '63'(/) */
   @Test
   public void newTokenWithUrlUnsafeBase64Slash() throws Exception {
-    IllegalArgumentException thrown =
-        assertThrows(
-            IllegalArgumentException.class,
-            () -> new SignedToken(maxAge, URL_UNSAFE_REGISTER_EMAIL_PRIVATE_KEY_WITH_SLASH));
+    String token = "/" + signedToken.newToken(TEXT);
+    CheckTokenException thrown =
+        assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
+
+    assertThat(thrown).hasMessageThat().contains("decoding failed");
 
     assertThat(thrown)
+        .hasCauseThat()
         .hasMessageThat()
         .isEqualTo(
             "com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: /");
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 29b9f4f..208eb96 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -44,8 +44,13 @@
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Patch;
@@ -123,6 +128,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
+import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -133,6 +139,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
@@ -1791,6 +1799,18 @@
 
     String g1 = createGroup("group1", "Administrators");
     gApi.groups().id(g1).addMembers("user2");
+
+    // By default when a group is created without any permission granted,
+    // nothing is visible to it; having members or not has nothing to do with it
+    assertQuery(q + " visibleto:" + g1);
+
+    // change is visible to group ONLY when access is granted
+    grant(
+        Project.nameKey("repo"),
+        "refs/*",
+        Permission.READ,
+        false,
+        AccountGroup.uuid(gApi.groups().id(g1).get().id));
     assertQuery(q + " visibleto:" + g1, change1);
 
     requestContext.setContext(newRequestContext(user2));
@@ -1824,6 +1844,26 @@
         q + " visibleto:\"Another User\"", "\"Another User\" resolves to multiple accounts");
   }
 
+  protected void grant(
+      Project.NameKey project,
+      String ref,
+      String permission,
+      boolean force,
+      AccountGroup.UUID groupUUID)
+      throws RepositoryNotFoundException, IOException, ConfigInvalidException {
+    try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
+      md.setMessage(String.format("Grant %s on %s", permission, ref));
+      ProjectConfig config = projectConfigFactory.read(md);
+      AccessSection s = config.getAccessSection(ref, true);
+      Permission p = s.getPermission(permission, true);
+      PermissionRule rule = new PermissionRule(new GroupReference(groupUUID, groupUUID.get()));
+      rule.setForce(force);
+      p.add(rule);
+      config.commit(md);
+      projectCache.evict(config.getProject());
+    }
+  }
+
   @Test
   public void visibleToSelf() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
diff --git a/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java b/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java
index 9f34377..2616bec 100644
--- a/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java
+++ b/javatests/com/google/gerrit/server/restapi/change/ListChangeCommentsTest.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.ChangeMessage;
 import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.CommentsUtil;
 import java.sql.Timestamp;
 import org.junit.Test;
@@ -33,27 +34,38 @@
 
   @Test
   public void commentsLinkedToChangeMessages() {
+    /**
+     * Human comments should not be linked to auto-generated messages. A comment is linked to the
+     * nearest next change message in timestamp
+     */
     CommentInfo c1 = getNewCommentInfo("c1", Timestamp.valueOf("2018-01-01 09:01:00"));
     CommentInfo c2 = getNewCommentInfo("c2", Timestamp.valueOf("2018-01-01 09:01:15"));
     CommentInfo c3 = getNewCommentInfo("c3", Timestamp.valueOf("2018-01-01 09:01:25"));
 
     ChangeMessage cm1 =
-        getNewChangeMessage("cm1key", "cm1", Timestamp.valueOf("2018-01-01 00:00:00"));
+        getNewChangeMessage("cm1key", "cm1", Timestamp.valueOf("2018-01-01 00:00:00"), null);
+    ChangeMessage cmIgnore =
+        getNewChangeMessage(
+            "cm2key",
+            "cm2",
+            Timestamp.valueOf("2018-01-01 09:01:15"),
+            ChangeMessagesUtil.AUTOGENERATED_TAG_PREFIX);
     ChangeMessage cm2 =
-        getNewChangeMessage("cm2key", "cm2", Timestamp.valueOf("2018-01-01 09:01:15"));
+        getNewChangeMessage("cm2key", "cm2", Timestamp.valueOf("2018-01-01 09:01:16"), null);
     ChangeMessage cm3 =
-        getNewChangeMessage("cm3key", "cm3", Timestamp.valueOf("2018-01-01 09:01:27"));
+        getNewChangeMessage("cm3key", "cm3", Timestamp.valueOf("2018-01-01 09:01:27"), null);
 
     assertThat(c1.changeMessageId).isNull();
     assertThat(c2.changeMessageId).isNull();
     assertThat(c3.changeMessageId).isNull();
 
     ImmutableList<CommentInfo> comments = ImmutableList.of(c1, c2, c3);
-    ImmutableList<ChangeMessage> changeMessages = ImmutableList.of(cm1, cm2, cm3);
+    ImmutableList<ChangeMessage> changeMessages = ImmutableList.of(cm1, cmIgnore, cm2, cm3);
 
-    CommentsUtil.linkCommentsToChangeMessages(comments, changeMessages);
+    CommentsUtil.linkCommentsToChangeMessages(comments, changeMessages, true);
 
     assertThat(c1.changeMessageId).isEqualTo(changeMessageKey(cm2));
+    /** comment 2 ignored the auto-generated change message */
     assertThat(c2.changeMessageId).isEqualTo(changeMessageKey(cm2));
     assertThat(c3.changeMessageId).isEqualTo(changeMessageKey(cm3));
   }
@@ -65,10 +77,12 @@
     return c;
   }
 
-  private static ChangeMessage getNewChangeMessage(String id, String message, Timestamp ts) {
+  private static ChangeMessage getNewChangeMessage(
+      String id, String message, Timestamp ts, String tag) {
     ChangeMessage.Key key = ChangeMessage.key(Change.id(1), id);
     ChangeMessage cm = new ChangeMessage(key, null, ts, null);
     cm.setMessage(message);
+    cm.setTag(tag);
     return cm;
   }
 
diff --git a/modules/jgit b/modules/jgit
index 9b2f8ce..e624e13 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit 9b2f8ced9018dad519fefdc19d3940104b95434a
+Subproject commit e624e1310836747c15afeb411d9fd7eaee41ddae
diff --git a/package.json b/package.json
index 18e76bf..526d201 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,10 @@
     "eslint-plugin-html": "^6.0.0",
     "eslint-plugin-import": "^2.20.1",
     "eslint-plugin-jsdoc": "^19.2.0",
+    "eslint-plugin-prettier": "^3.1.3",
     "fried-twinkie": "^0.2.2",
     "polymer-cli": "^1.9.11",
+    "prettier": "2.0.5",
     "typescript": "^3.7.4",
     "web-component-tester": "^6.5.1"
   },
diff --git a/plugins/delete-project b/plugins/delete-project
index 128d50b..34ea95f 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 128d50bf6f877b3d803475aa03e7d8a25abd79f5
+Subproject commit 34ea95f9c7997aaa0b2275fadbbd00acc2fcbde8
diff --git a/plugins/gitiles b/plugins/gitiles
index df5d2e5..641476e 160000
--- a/plugins/gitiles
+++ b/plugins/gitiles
@@ -1 +1 @@
-Subproject commit df5d2e55b1126c7bc7c589a9971f758b0dae5ce7
+Subproject commit 641476e153143c2b67e334b35626beb9b2534956
diff --git a/plugins/replication b/plugins/replication
index 841ed8f..ab6af46 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 841ed8fe0417a8aef985d1dd4bfd368d9a00b355
+Subproject commit ab6af46f5d191c10a163f4d8a77b75e920bde372
diff --git a/plugins/webhooks b/plugins/webhooks
index 11510b8..b539049 160000
--- a/plugins/webhooks
+++ b/plugins/webhooks
@@ -1 +1 @@
-Subproject commit 11510b804a5491a2a4368a5b0681d11cb1e77240
+Subproject commit b539049a9e2073b7e9af8b151d8ea9c1087e12da
diff --git a/polygerrit-ui/app/.eslintrc.js b/polygerrit-ui/app/.eslintrc.js
index fc24278..51c77ba 100644
--- a/polygerrit-ui/app/.eslintrc.js
+++ b/polygerrit-ui/app/.eslintrc.js
@@ -156,7 +156,7 @@
     "import/no-cycle": 0,
     "import/no-useless-path-segments": 2,
     "import/no-unused-modules": 2,
-    "import/no-default-export": 2
+    "import/no-default-export": 2,
   },
 
   // List of allowed globals in all files
@@ -165,11 +165,9 @@
     // You must not add anything new in this list!
     // Instead export variables from modules
     // TODO(dmfilippov): Remove global variables from polygerrit
-    "GrReporting": "readonly",
     // Global variables from 3rd party libraries.
     // You should not add anything in this list, always try to import
     // If import is not possible - you can extend this list
-    "Polymer": "readonly",
     "ShadyCSS": "readonly",
     "linkify": "readonly",
     "security": "readonly",
@@ -216,6 +214,7 @@
       "globals": {
         // Settings for samples. You can add globals here if you want to use it
         "Gerrit": "readonly",
+        "Polymer": "readonly",
       }
     },
     {
@@ -242,12 +241,22 @@
       "rules": {
         "max-len": "off"
       }
+    },
+    {
+      "files": ["*_html.js"],
+      "rules": {
+        "prettier/prettier": ["error", {
+          "bracketSpacing": false,
+          "singleQuote": true,
+        }]
+      }
     }
   ],
   "plugins": [
     "html",
     "jsdoc",
-    "import"
+    "import",
+    "prettier"
   ],
   "settings": {
     "html/report-bad-indent": "error"
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index f382e42..ca021db 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -89,6 +89,7 @@
         "@npm//eslint-plugin-html",
         "@npm//eslint-plugin-import",
         "@npm//eslint-plugin-jsdoc",
+        "@npm//eslint-plugin-prettier",
     ],
 )
 
diff --git a/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html b/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html
index 1adeb7c..1d50cc4 100644
--- a/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>async-foreach-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 28f9ffd..61d7bac 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>base-url-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 7f3f2cb..1e842c1 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>dom-util-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 aa16261..c5f3f94 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>keyboard-shortcut-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 a1b6cbb..3f58499 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>keyboard-shortcut-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 39a4ea5..3c5aedd 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>keyboard-shortcut-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 fe78220..fa72c40 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-display-name-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 2f2bae6..80013bf 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>keyboard-shortcut-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
index 2a592f0..cc8b55a 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../scripts/bundled-polymer.js';
-
 import '../../elements/shared/gr-tooltip/gr-tooltip.js';
 import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {getRootElement} from '../../scripts/rootElement.js';
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js
index cb21a9f..09e3b72 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js
@@ -95,12 +95,6 @@
 NOTE: doc-only shortcuts will not be customizable in the same way that other
 shortcuts are.
 */
-/*
-  FIXME(polymer-modulizer): the above comments were extracted
-  from HTML and may be out of place here. Review them and
-  then delete this comment!
-*/
-import '../../scripts/bundled-polymer.js';
 
 import {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
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 7372983..fcb7b4f 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>keyboard-shortcut-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.js b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.js
index 919a763..29464bf 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.js
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.js
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../scripts/bundled-polymer.js';
 import {BaseUrlBehavior} from '../base-url-behavior/base-url-behavior.js';
 
 /** @polymerBehavior Gerrit.RESTClientBehavior */
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 04b154f..980bc8f 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>keyboard-shortcut-behavior</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/constants/constants.js b/polygerrit-ui/app/constants/constants.js
index cab50f6..084bb0f 100644
--- a/polygerrit-ui/app/constants/constants.js
+++ b/polygerrit-ui/app/constants/constants.js
@@ -33,3 +33,13 @@
   COMMENT_THREADS: '_commentThreads',
 };
 
+/**
+ * @enum
+ * @desc Tag names of change log messages.
+ */
+export const MessageTags = {
+  TAG_DELETE_REVIEWER: 'autogenerated:gerrit:deleteReviewer',
+  TAG_NEW_PATCHSET: 'autogenerated:gerrit:newPatchSet',
+  TAG_NEW_WIP_PATCHSET: 'autogenerated:gerrit:newWipPatchSet',
+  TAG_REVIEWER_UPDATE: 'autogenerated:gerrit:reviewerUpdate',
+};
\ No newline at end of file
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 e07a64e..632387e 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
@@ -51,7 +50,7 @@
 const LABEL = 'Label';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccessSection extends mixinBehaviors( [
   AccessBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.js b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.js
index 5f35f55..c46cf30 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.js
@@ -17,101 +17,141 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        margin-bottom: var(--spacing-l);
-      }
-      fieldset {
-        border: 1px solid var(--border-color);
-      }
-      .name {
-        align-items: center;
-        display: flex;
-      }
-      .header,
-      #deletedContainer {
-        align-items: center;
-        background: var(--table-header-background-color);
-        border-bottom: 1px dotted var(--border-color);
-        display: flex;
-        justify-content: space-between;
-        min-height: 3em;
-        padding: 0 var(--spacing-m);
-      }
-      #deletedContainer {
-        border-bottom: 0;
-      }
-      .sectionContent {
-        padding: var(--spacing-m);
-      }
-      #editBtn,
-      .editing #editBtn.global,
-      #deletedContainer,
-      .deleted #mainContainer,
-      #addPermission,
-      #deleteBtn,
-      .editingRef .name,
-      .editRefInput {
-        display: none;
-      }
-      .editing #editBtn,
-      .editingRef .editRefInput {
-        display: flex;
-      }
-      .deleted #deletedContainer {
-        display: flex;
-      }
-      .editing #addPermission,
-      #mainContainer,
-      .editing #deleteBtn  {
-        display: block;
-      }
-      .editing #deleteBtn,
-      #undoRemoveBtn {
-        padding-right: var(--spacing-m);
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <fieldset id="section" class\$="gr-form-styles [[_computeSectionClass(editing, canUpload, ownerOf, _editingRef, _deleted)]]">
-      <div id="mainContainer">
-        <div class="header">
-          <div class="name">
-            <h3>[[_computeSectionName(section.id)]]</h3>
-            <gr-button id="editBtn" link="" class\$="[[_computeEditBtnClass(section.id)]]" on-click="editReference">
-              <iron-icon id="icon" icon="gr-icons:create"></iron-icon>
-            </gr-button>
-          </div>
-          <iron-input class="editRefInput" bind-value="{{section.id}}" type="text" on-input="_handleValueChange">
-            <input class="editRefInput" bind-value="{{section.id}}" is="iron-input" type="text" on-input="_handleValueChange">
-          </iron-input>
-          <gr-button link="" id="deleteBtn" on-click="_handleRemoveReference">Remove</gr-button>
-        </div><!-- end header -->
-        <div class="sectionContent">
-          <template is="dom-repeat" items="{{_permissions}}" as="permission">
-            <gr-permission name="[[_computePermissionName(section.id, permission, permissionValues, capabilities)]]" permission="{{permission}}" labels="[[labels]]" section="[[section.id]]" editing="[[editing]]" groups="[[groups]]" on-added-permission-removed="_handleAddedPermissionRemoved">
-            </gr-permission>
-          </template>
-          <div id="addPermission">
-            Add permission:
-            <select id="permissionSelect">
-              <!-- called with a third parameter so that permissions update
+  <style include="shared-styles">
+    :host {
+      display: block;
+      margin-bottom: var(--spacing-l);
+    }
+    fieldset {
+      border: 1px solid var(--border-color);
+    }
+    .name {
+      align-items: center;
+      display: flex;
+    }
+    .header,
+    #deletedContainer {
+      align-items: center;
+      background: var(--table-header-background-color);
+      border-bottom: 1px dotted var(--border-color);
+      display: flex;
+      justify-content: space-between;
+      min-height: 3em;
+      padding: 0 var(--spacing-m);
+    }
+    #deletedContainer {
+      border-bottom: 0;
+    }
+    .sectionContent {
+      padding: var(--spacing-m);
+    }
+    #editBtn,
+    .editing #editBtn.global,
+    #deletedContainer,
+    .deleted #mainContainer,
+    #addPermission,
+    #deleteBtn,
+    .editingRef .name,
+    .editRefInput {
+      display: none;
+    }
+    .editing #editBtn,
+    .editingRef .editRefInput {
+      display: flex;
+    }
+    .deleted #deletedContainer {
+      display: flex;
+    }
+    .editing #addPermission,
+    #mainContainer,
+    .editing #deleteBtn {
+      display: block;
+    }
+    .editing #deleteBtn,
+    #undoRemoveBtn {
+      padding-right: var(--spacing-m);
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <fieldset
+    id="section"
+    class$="gr-form-styles [[_computeSectionClass(editing, canUpload, ownerOf, _editingRef, _deleted)]]"
+  >
+    <div id="mainContainer">
+      <div class="header">
+        <div class="name">
+          <h3>[[_computeSectionName(section.id)]]</h3>
+          <gr-button
+            id="editBtn"
+            link=""
+            class$="[[_computeEditBtnClass(section.id)]]"
+            on-click="editReference"
+          >
+            <iron-icon id="icon" icon="gr-icons:create"></iron-icon>
+          </gr-button>
+        </div>
+        <iron-input
+          class="editRefInput"
+          bind-value="{{section.id}}"
+          type="text"
+          on-input="_handleValueChange"
+        >
+          <input
+            class="editRefInput"
+            bind-value="{{section.id}}"
+            is="iron-input"
+            type="text"
+            on-input="_handleValueChange"
+          />
+        </iron-input>
+        <gr-button link="" id="deleteBtn" on-click="_handleRemoveReference"
+          >Remove</gr-button
+        >
+      </div>
+      <!-- end header -->
+      <div class="sectionContent">
+        <template is="dom-repeat" items="{{_permissions}}" as="permission">
+          <gr-permission
+            name="[[_computePermissionName(section.id, permission, permissionValues, capabilities)]]"
+            permission="{{permission}}"
+            labels="[[labels]]"
+            section="[[section.id]]"
+            editing="[[editing]]"
+            groups="[[groups]]"
+            on-added-permission-removed="_handleAddedPermissionRemoved"
+          >
+          </gr-permission>
+        </template>
+        <div id="addPermission">
+          Add permission:
+          <select id="permissionSelect">
+            <!-- called with a third parameter so that permissions update
                   after a new section is added. -->
-              <template is="dom-repeat" items="[[_computePermissions(section.id, capabilities, labels, section.value.permissions.*)]]">
-                <option value="[[item.value.id]]">[[item.value.name]]</option>
-              </template>
-            </select>
-            <gr-button link="" id="addBtn" on-click="_handleAddPermission">Add</gr-button>
-          </div>
-          <!-- end addPermission -->
-        </div><!-- end sectionContent -->
-      </div><!-- end mainContainer -->
-      <div id="deletedContainer">
-        <span>[[_computeSectionName(section.id)]] was deleted</span>
-        <gr-button link="" id="undoRemoveBtn" on-click="_handleUndoRemove">Undo</gr-button>
-      </div><!-- end deletedContainer -->
-    </fieldset>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+            <template
+              is="dom-repeat"
+              items="[[_computePermissions(section.id, capabilities, labels, section.value.permissions.*)]]"
+            >
+              <option value="[[item.value.id]]">[[item.value.name]]</option>
+            </template>
+          </select>
+          <gr-button link="" id="addBtn" on-click="_handleAddPermission"
+            >Add</gr-button
+          >
+        </div>
+        <!-- end addPermission -->
+      </div>
+      <!-- end sectionContent -->
+    </div>
+    <!-- end mainContainer -->
+    <div id="deletedContainer">
+      <span>[[_computeSectionName(section.id)]] was deleted</span>
+      <gr-button link="" id="undoRemoveBtn" on-click="_handleUndoRemove"
+        >Undo</gr-button
+      >
+    </div>
+    <!-- end deletedContainer -->
+  </fieldset>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html
index ff19d4b..95345fe 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-access-section</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 d2fde51c..d606bac 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../../styles/gr-table-styles.js';
 import '../../../styles/shared-styles.js';
@@ -33,7 +32,7 @@
 
 /**
  * @appliesMixin ListViewMixin
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAdminGroupList extends mixinBehaviors( [
   ListViewBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.js b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.js
index ffc10d7..136f6b6 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_html.js
@@ -17,44 +17,67 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <gr-list-view create-new="[[_createNewCapability]]" filter="[[_filter]]" items="[[_groups]]" items-per-page="[[_groupsPerPage]]" loading="[[_loading]]" offset="[[_offset]]" on-create-clicked="_handleCreateClicked" path="[[_path]]">
-      <table id="list" class="genericList">
-        <tbody><tr class="headerRow">
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <gr-list-view
+    create-new="[[_createNewCapability]]"
+    filter="[[_filter]]"
+    items="[[_groups]]"
+    items-per-page="[[_groupsPerPage]]"
+    loading="[[_loading]]"
+    offset="[[_offset]]"
+    on-create-clicked="_handleCreateClicked"
+    path="[[_path]]"
+  >
+    <table id="list" class="genericList">
+      <tbody>
+        <tr class="headerRow">
           <th class="name topHeader">Group Name</th>
           <th class="description topHeader">Group Description</th>
           <th class="visibleToAll topHeader">Visible To All</th>
         </tr>
-        <tr id="loading" class\$="loadingMsg [[computeLoadingClass(_loading)]]">
+        <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
           <td>Loading...</td>
         </tr>
-        </tbody><tbody class\$="[[computeLoadingClass(_loading)]]">
-          <template is="dom-repeat" items="[[_shownGroups]]">
-            <tr class="table">
-              <td class="name">
-                <a href\$="[[_computeGroupUrl(item.group_id)]]">[[item.name]]</a>
-              </td>
-              <td class="description">[[item.description]]</td>
-              <td class="visibleToAll">[[_visibleToAll(item)]]</td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-    </gr-list-view>
-    <gr-overlay id="createOverlay" with-backdrop="">
-      <gr-dialog id="createDialog" class="confirmDialog" disabled="[[!_hasNewGroupName]]" confirm-label="Create" confirm-on-enter="" on-confirm="_handleCreateGroup" on-cancel="_handleCloseCreate">
-        <div class="header" slot="header">
-          Create Group
-        </div>
-        <div class="main" slot="main">
-          <gr-create-group-dialog has-new-group-name="{{_hasNewGroupName}}" params="[[params]]" id="createNewModal"></gr-create-group-dialog>
-        </div>
-      </gr-dialog>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </tbody>
+      <tbody class$="[[computeLoadingClass(_loading)]]">
+        <template is="dom-repeat" items="[[_shownGroups]]">
+          <tr class="table">
+            <td class="name">
+              <a href$="[[_computeGroupUrl(item.group_id)]]">[[item.name]]</a>
+            </td>
+            <td class="description">[[item.description]]</td>
+            <td class="visibleToAll">[[_visibleToAll(item)]]</td>
+          </tr>
+        </template>
+      </tbody>
+    </table>
+  </gr-list-view>
+  <gr-overlay id="createOverlay" with-backdrop="">
+    <gr-dialog
+      id="createDialog"
+      class="confirmDialog"
+      disabled="[[!_hasNewGroupName]]"
+      confirm-label="Create"
+      confirm-on-enter=""
+      on-confirm="_handleCreateGroup"
+      on-cancel="_handleCloseCreate"
+    >
+      <div class="header" slot="header">
+        Create Group
+      </div>
+      <div class="main" slot="main">
+        <gr-create-group-dialog
+          has-new-group-name="{{_hasNewGroupName}}"
+          params="[[params]]"
+          id="createNewModal"
+        ></gr-create-group-dialog>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
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 cf3ff5d..c7ac2cb 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-admin-group-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b318ee6..25ba83e 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-menu-page-styles.js';
 import '../../../styles/gr-page-nav-styles.js';
 import '../../../styles/shared-styles.js';
@@ -48,7 +47,7 @@
 const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAdminView extends mixinBehaviors( [
   AdminNavBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.js
index 0bc9431..b62a41b 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_html.js
@@ -17,137 +17,167 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-menu-page-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-page-nav-styles">
-      gr-dropdown-list {
-        --trigger-style: {
-          text-transform: none;
-        }
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-menu-page-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-page-nav-styles">
+    gr-dropdown-list {
+      --trigger-style: {
+        text-transform: none;
       }
-      .breadcrumbText {
-        /* Same as dropdown trigger so chevron spacing is consistent. */
-        padding: 5px 4px;
-      }
-      iron-icon {
-        margin: 0 var(--spacing-xs);
-      }
-      .breadcrumb {
-        align-items: center;
-        display: flex;
-      }
-      .mainHeader {
-        align-items: baseline;
-        border-bottom: 1px solid var(--border-color);
-        display: flex;
-      }
-      .selectText {
-        display: none;
-      }
-      .selectText.show {
-        display: inline-block;
-      }
-      main.breadcrumbs:not(.table) {
-        margin-top: var(--spacing-l);
-      }
-    </style>
-    <gr-page-nav class="navStyles">
-      <ul class="sectionContent">
-        <template id="adminNav" is="dom-repeat" items="[[_filteredLinks]]">
-          <li class\$="sectionTitle [[_computeSelectedClass(item.view, params)]]">
-            <a class="title" href="[[_computeLinkURL(item)]]" rel="noopener">[[item.name]]</a>
+    }
+    .breadcrumbText {
+      /* Same as dropdown trigger so chevron spacing is consistent. */
+      padding: 5px 4px;
+    }
+    iron-icon {
+      margin: 0 var(--spacing-xs);
+    }
+    .breadcrumb {
+      align-items: center;
+      display: flex;
+    }
+    .mainHeader {
+      align-items: baseline;
+      border-bottom: 1px solid var(--border-color);
+      display: flex;
+    }
+    .selectText {
+      display: none;
+    }
+    .selectText.show {
+      display: inline-block;
+    }
+    main.breadcrumbs:not(.table) {
+      margin-top: var(--spacing-l);
+    }
+  </style>
+  <gr-page-nav class="navStyles">
+    <ul class="sectionContent">
+      <template id="adminNav" is="dom-repeat" items="[[_filteredLinks]]">
+        <li class$="sectionTitle [[_computeSelectedClass(item.view, params)]]">
+          <a class="title" href="[[_computeLinkURL(item)]]" rel="noopener"
+            >[[item.name]]</a
+          >
+        </li>
+        <template is="dom-repeat" items="[[item.children]]" as="child">
+          <li class$="[[_computeSelectedClass(child.view, params)]]">
+            <a href$="[[_computeLinkURL(child)]]" rel="noopener"
+              >[[child.name]]</a
+            >
           </li>
-          <template is="dom-repeat" items="[[item.children]]" as="child">
-            <li class\$="[[_computeSelectedClass(child.view, params)]]">
-              <a href\$="[[_computeLinkURL(child)]]" rel="noopener">[[child.name]]</a>
+        </template>
+        <template is="dom-if" if="[[item.subsection]]">
+          <!--If a section has a subsection, render that.-->
+          <li class$="[[_computeSelectedClass(item.subsection.view, params)]]">
+            <a
+              class="title"
+              href$="[[_computeLinkURL(item.subsection)]]"
+              rel="noopener"
+            >
+              [[item.subsection.name]]</a
+            >
+          </li>
+          <!--Loop through the links in the sub-section.-->
+          <template
+            is="dom-repeat"
+            items="[[item.subsection.children]]"
+            as="child"
+          >
+            <li
+              class$="subsectionItem [[_computeSelectedClass(child.view, params, child.detailType)]]"
+            >
+              <a href$="[[_computeLinkURL(child)]]">[[child.name]]</a>
             </li>
           </template>
-          <template is="dom-if" if="[[item.subsection]]">
-            <!--If a section has a subsection, render that.-->
-            <li class\$="[[_computeSelectedClass(item.subsection.view, params)]]">
-              <a class="title" href\$="[[_computeLinkURL(item.subsection)]]" rel="noopener">
-                [[item.subsection.name]]</a>
-            </li>
-            <!--Loop through the links in the sub-section.-->
-            <template is="dom-repeat" items="[[item.subsection.children]]" as="child">
-              <li class\$="subsectionItem [[_computeSelectedClass(child.view, params, child.detailType)]]">
-                <a href\$="[[_computeLinkURL(child)]]">[[child.name]]</a>
-              </li>
-            </template>
-          </template>
         </template>
-      </ul>
-    </gr-page-nav>
-    <template is="dom-if" if="[[_subsectionLinks.length]]">
-      <section class="mainHeader">
-        <span class="breadcrumb">
-          <span class="breadcrumbText">[[_breadcrumbParentName]]</span>
-          <iron-icon icon="gr-icons:chevron-right"></iron-icon>
-        </span>
-        <gr-dropdown-list lowercase="" id="pageSelect" value="[[_computeSelectValue(params)]]" items="[[_subsectionLinks]]" on-value-change="_handleSubsectionChange">
-        </gr-dropdown-list>
-      </section>
-    </template>
-    <template is="dom-if" if="[[_showRepoList]]" restamp="true">
-      <main class="table">
-        <gr-repo-list class="table" params="[[params]]"></gr-repo-list>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showGroupList]]" restamp="true">
-      <main class="table">
-        <gr-admin-group-list class="table" params="[[params]]">
-        </gr-admin-group-list>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showPluginList]]" restamp="true">
-      <main class="table">
-        <gr-plugin-list class="table" params="[[params]]"></gr-plugin-list>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showRepoMain]]" restamp="true">
-      <main class="breadcrumbs">
-        <gr-repo repo="[[params.repo]]"></gr-repo>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showGroup]]" restamp="true">
-      <main class="breadcrumbs">
-        <gr-group group-id="[[params.groupId]]" on-name-changed="_updateGroupName"></gr-group>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showGroupMembers]]" restamp="true">
-      <main class="breadcrumbs">
-        <gr-group-members group-id="[[params.groupId]]"></gr-group-members>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showRepoDetailList]]" restamp="true">
-      <main class="table breadcrumbs">
-        <gr-repo-detail-list params="[[params]]" class="table"></gr-repo-detail-list>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showGroupAuditLog]]" restamp="true">
-      <main class="table breadcrumbs">
-        <gr-group-audit-log group-id="[[params.groupId]]" class="table"></gr-group-audit-log>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showRepoCommands]]" restamp="true">
-      <main class="breadcrumbs">
-        <gr-repo-commands repo="[[params.repo]]"></gr-repo-commands>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showRepoAccess]]" restamp="true">
-      <main class="breadcrumbs">
-        <gr-repo-access path="[[path]]" repo="[[params.repo]]"></gr-repo-access>
-      </main>
-    </template>
-    <template is="dom-if" if="[[_showRepoDashboards]]" restamp="true">
-      <main class="table breadcrumbs">
-        <gr-repo-dashboards repo="[[params.repo]]"></gr-repo-dashboards>
-      </main>
-    </template>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+      </template>
+    </ul>
+  </gr-page-nav>
+  <template is="dom-if" if="[[_subsectionLinks.length]]">
+    <section class="mainHeader">
+      <span class="breadcrumb">
+        <span class="breadcrumbText">[[_breadcrumbParentName]]</span>
+        <iron-icon icon="gr-icons:chevron-right"></iron-icon>
+      </span>
+      <gr-dropdown-list
+        lowercase=""
+        id="pageSelect"
+        value="[[_computeSelectValue(params)]]"
+        items="[[_subsectionLinks]]"
+        on-value-change="_handleSubsectionChange"
+      >
+      </gr-dropdown-list>
+    </section>
+  </template>
+  <template is="dom-if" if="[[_showRepoList]]" restamp="true">
+    <main class="table">
+      <gr-repo-list class="table" params="[[params]]"></gr-repo-list>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showGroupList]]" restamp="true">
+    <main class="table">
+      <gr-admin-group-list class="table" params="[[params]]">
+      </gr-admin-group-list>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showPluginList]]" restamp="true">
+    <main class="table">
+      <gr-plugin-list class="table" params="[[params]]"></gr-plugin-list>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showRepoMain]]" restamp="true">
+    <main class="breadcrumbs">
+      <gr-repo repo="[[params.repo]]"></gr-repo>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showGroup]]" restamp="true">
+    <main class="breadcrumbs">
+      <gr-group
+        group-id="[[params.groupId]]"
+        on-name-changed="_updateGroupName"
+      ></gr-group>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showGroupMembers]]" restamp="true">
+    <main class="breadcrumbs">
+      <gr-group-members group-id="[[params.groupId]]"></gr-group-members>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showRepoDetailList]]" restamp="true">
+    <main class="table breadcrumbs">
+      <gr-repo-detail-list
+        params="[[params]]"
+        class="table"
+      ></gr-repo-detail-list>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showGroupAuditLog]]" restamp="true">
+    <main class="table breadcrumbs">
+      <gr-group-audit-log
+        group-id="[[params.groupId]]"
+        class="table"
+      ></gr-group-audit-log>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showRepoCommands]]" restamp="true">
+    <main class="breadcrumbs">
+      <gr-repo-commands repo="[[params.repo]]"></gr-repo-commands>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showRepoAccess]]" restamp="true">
+    <main class="breadcrumbs">
+      <gr-repo-access path="[[path]]" repo="[[params.repo]]"></gr-repo-access>
+    </main>
+  </template>
+  <template is="dom-if" if="[[_showRepoDashboards]]" restamp="true">
+    <main class="table breadcrumbs">
+      <gr-repo-dashboards repo="[[params.repo]]"></gr-repo-dashboards>
+    </main>
+  </template>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
index 3387982..ec72bfd 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-admin-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b6b21bd..288af4c 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -30,7 +28,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmDeleteItemDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.js
index 12dc29c..3810d32 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_html.js
@@ -17,21 +17,29 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        width: 30em;
-      }
-    </style>
-    <gr-dialog confirm-label="Delete [[_computeItemName(itemType)]]" confirm-on-enter="" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">[[_computeItemName(itemType)]] Deletion</div>
-      <div class="main" slot="main">
-        <label for="branchInput">
-          Do you really want to delete the following [[_computeItemName(itemType)]]?
-        </label>
-        <div>
-          [[item]]
-        </div>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      width: 30em;
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Delete [[_computeItemName(itemType)]]"
+    confirm-on-enter=""
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">
+      [[_computeItemName(itemType)]] Deletion
+    </div>
+    <div class="main" slot="main">
+      <label for="branchInput">
+        Do you really want to delete the following
+        [[_computeItemName(itemType)]]?
+      </label>
+      <div>
+        [[item]]
       </div>
-    </gr-dialog>
+    </div>
+  </gr-dialog>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
index 02d18a3..003edfb 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-delete-item-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 3347655..90b148b 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
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-
 import '@polymer/iron-input/iron-input.js';
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-autocomplete/gr-autocomplete.js';
@@ -37,7 +34,7 @@
 const REF_PREFIX = 'refs/heads/';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCreateChangeDialog extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.js
index 8f11af3..f18da81 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_html.js
@@ -17,64 +17,101 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      input:not([type="checkbox"]),
-      gr-autocomplete,
-      iron-autogrow-textarea {
-        width: 100%;
-      }
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    input:not([type='checkbox']),
+    gr-autocomplete,
+    iron-autogrow-textarea {
+      width: 100%;
+    }
+    .value {
+      width: 32em;
+    }
+    .hide {
+      display: none;
+    }
+    @media only screen and (max-width: 40em) {
       .value {
-        width: 32em;
+        width: 29em;
       }
-      .hide {
-        display: none;
-      }
-      @media only screen and (max-width: 40em) {
-        .value {
-          width: 29em;
-        }
-      }
-    </style>
-    <div class="gr-form-styles">
-      <section class\$="[[_computeBranchClass(baseChange)]]">
-        <span class="title">Select branch for new change</span>
-        <span class="value">
-          <gr-autocomplete id="branchInput" text="{{branch}}" query="[[_query]]" placeholder="Destination branch">
-          </gr-autocomplete>
-        </span>
-      </section>
-      <section class\$="[[_computeBranchClass(baseChange)]]">
-        <span class="title">Provide base commit sha1 for change</span>
-        <span class="value">
-          <iron-input maxlength="40" placeholder="(optional)" bind-value="{{baseCommit}}">
-            <input is="iron-input" id="baseCommitInput" maxlength="40" placeholder="(optional)" bind-value="{{baseCommit}}">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Enter topic for new change</span>
-        <span class="value">
-          <iron-input maxlength="1024" placeholder="(optional)" bind-value="{{topic}}">
-            <input is="iron-input" id="tagNameInput" maxlength="1024" placeholder="(optional)" bind-value="{{topic}}">
-          </iron-input>
-        </span>
-      </section>
-      <section id="description">
-        <span class="title">Description</span>
-        <span class="value">
-          <iron-autogrow-textarea id="messageInput" class="message" autocomplete="on" rows="4" max-rows="15" bind-value="{{subject}}" placeholder="Insert the description of the change.">
-          </iron-autogrow-textarea>
-        </span>
-      </section>
-      <section class\$="[[_computePrivateSectionClass(_privateChangesEnabled)]]">
-        <label class="title" for="privateChangeCheckBox">Private change</label>
-        <span class="value">
-          <input type="checkbox" id="privateChangeCheckBox" checked\$="[[_formatBooleanString(privateByDefault)]]">
-        </span>
-      </section>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    }
+  </style>
+  <div class="gr-form-styles">
+    <section class$="[[_computeBranchClass(baseChange)]]">
+      <span class="title">Select branch for new change</span>
+      <span class="value">
+        <gr-autocomplete
+          id="branchInput"
+          text="{{branch}}"
+          query="[[_query]]"
+          placeholder="Destination branch"
+        >
+        </gr-autocomplete>
+      </span>
+    </section>
+    <section class$="[[_computeBranchClass(baseChange)]]">
+      <span class="title">Provide base commit sha1 for change</span>
+      <span class="value">
+        <iron-input
+          maxlength="40"
+          placeholder="(optional)"
+          bind-value="{{baseCommit}}"
+        >
+          <input
+            is="iron-input"
+            id="baseCommitInput"
+            maxlength="40"
+            placeholder="(optional)"
+            bind-value="{{baseCommit}}"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Enter topic for new change</span>
+      <span class="value">
+        <iron-input
+          maxlength="1024"
+          placeholder="(optional)"
+          bind-value="{{topic}}"
+        >
+          <input
+            is="iron-input"
+            id="tagNameInput"
+            maxlength="1024"
+            placeholder="(optional)"
+            bind-value="{{topic}}"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section id="description">
+      <span class="title">Description</span>
+      <span class="value">
+        <iron-autogrow-textarea
+          id="messageInput"
+          class="message"
+          autocomplete="on"
+          rows="4"
+          max-rows="15"
+          bind-value="{{subject}}"
+          placeholder="Insert the description of the change."
+        >
+        </iron-autogrow-textarea>
+      </span>
+    </section>
+    <section class$="[[_computePrivateSectionClass(_privateChangesEnabled)]]">
+      <label class="title" for="privateChangeCheckBox">Private change</label>
+      <span class="value">
+        <input
+          type="checkbox"
+          id="privateChangeCheckBox"
+          checked$="[[_formatBooleanString(privateByDefault)]]"
+        />
+      </span>
+    </section>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
index 4ce1855..87105a7 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-create-change-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b21bdde..7f33663 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
@@ -30,7 +28,7 @@
 import page from 'page/page.mjs';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCreateGroupDialog extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.js
index 2cdde81..bc1f24c 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_html.js
@@ -17,26 +17,26 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      :host {
-        display: inline-block;
-      }
-      input {
-        width: 20em;
-      }
-    </style>
-    <div class="gr-form-styles">
-      <div id="form">
-        <section>
-          <span class="title">Group name</span>
-          <iron-input bind-value="{{_name}}">
-            <input is="iron-input" bind-value="{{_name}}">
-          </iron-input>
-        </section>
-      </div>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    :host {
+      display: inline-block;
+    }
+    input {
+      width: 20em;
+    }
+  </style>
+  <div class="gr-form-styles">
+    <div id="form">
+      <section>
+        <span class="title">Group name</span>
+        <iron-input bind-value="{{_name}}">
+          <input is="iron-input" bind-value="{{_name}}" />
+        </iron-input>
+      </section>
     </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html
index 1ad13ea..164db53 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-create-group-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 72a6b99..eb131f5 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
@@ -37,7 +35,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCreatePointerDialog extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.js
index 3a6df2f..62a2e0f 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_html.js
@@ -17,44 +17,68 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      :host {
-        display: inline-block;
-      }
-      input {
-        width: 20em;
-      }
-      /* Add css selector with #id to increase priority
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    :host {
+      display: inline-block;
+    }
+    input {
+      width: 20em;
+    }
+    /* Add css selector with #id to increase priority
       (otherwise ".gr-form-styles section" rule wins) */
-      .hideItem,
-      #itemAnnotationSection.hideItem {
-        display: none;
-      }
-    </style>
-    <div class="gr-form-styles">
-      <div id="form">
-        <section id="itemNameSection">
-          <span class="title">[[detailType]] name</span>
-          <iron-input placeholder="[[detailType]] Name" bind-value="{{_itemName}}">
-            <input is="iron-input" placeholder="[[detailType]] Name" bind-value="{{_itemName}}">
-          </iron-input>
-        </section>
-        <section id="itemRevisionSection">
-          <span class="title">Initial Revision</span>
-          <iron-input placeholder="Revision (Branch or SHA-1)" bind-value="{{_itemRevision}}">
-            <input is="iron-input" placeholder="Revision (Branch or SHA-1)" bind-value="{{_itemRevision}}">
-          </iron-input>
-        </section>
-        <section id="itemAnnotationSection" class\$="[[_computeHideItemClass(itemDetail)]]">
-          <span class="title">Annotation</span>
-          <iron-input placeholder="Annotation (Optional)" bind-value="{{_itemAnnotation}}">
-            <input is="iron-input" placeholder="Annotation (Optional)" bind-value="{{_itemAnnotation}}">
-          </iron-input>
-        </section>
-      </div>
+    .hideItem,
+    #itemAnnotationSection.hideItem {
+      display: none;
+    }
+  </style>
+  <div class="gr-form-styles">
+    <div id="form">
+      <section id="itemNameSection">
+        <span class="title">[[detailType]] name</span>
+        <iron-input
+          placeholder="[[detailType]] Name"
+          bind-value="{{_itemName}}"
+        >
+          <input
+            is="iron-input"
+            placeholder="[[detailType]] Name"
+            bind-value="{{_itemName}}"
+          />
+        </iron-input>
+      </section>
+      <section id="itemRevisionSection">
+        <span class="title">Initial Revision</span>
+        <iron-input
+          placeholder="Revision (Branch or SHA-1)"
+          bind-value="{{_itemRevision}}"
+        >
+          <input
+            is="iron-input"
+            placeholder="Revision (Branch or SHA-1)"
+            bind-value="{{_itemRevision}}"
+          />
+        </iron-input>
+      </section>
+      <section
+        id="itemAnnotationSection"
+        class$="[[_computeHideItemClass(itemDetail)]]"
+      >
+        <span class="title">Annotation</span>
+        <iron-input
+          placeholder="Annotation (Optional)"
+          bind-value="{{_itemAnnotation}}"
+        >
+          <input
+            is="iron-input"
+            placeholder="Annotation (Optional)"
+            bind-value="{{_itemAnnotation}}"
+          />
+        </iron-input>
+      </section>
     </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
index 394b1d8..2778d40 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-create-pointer-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 040f41b..0410f14 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
@@ -33,7 +31,7 @@
 import page from 'page/page.mjs';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCreateRepoDialog extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.js b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.js
index 65666ea..680986c 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_html.js
@@ -17,66 +17,87 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      :host {
-        display: inline-block;
-      }
-      input {
-        width: 20em;
-      }
-      gr-autocomplete {
-        width: 20em;
-      }
-    </style>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    :host {
+      display: inline-block;
+    }
+    input {
+      width: 20em;
+    }
+    gr-autocomplete {
+      width: 20em;
+    }
+  </style>
 
-    <div class="gr-form-styles">
-      <div id="form">
-        <section>
-          <span class="title">Repository name</span>
-          <iron-input autocomplete="on" bind-value="{{_repoConfig.name}}">
-            <input is="iron-input" id="repoNameInput" autocomplete="on" bind-value="{{_repoConfig.name}}">
-          </iron-input>
-        </section>
-        <section>
-          <span class="title">Rights inherit from</span>
-          <span class="value">
-            <gr-autocomplete id="rightsInheritFromInput" text="{{_repoConfig.parent}}" query="[[_query]]" placeholder="Optional, defaults to 'All-Projects'">
-            </gr-autocomplete>
-          </span>
-        </section>
-        <section>
-          <span class="title">Owner</span>
-          <span class="value">
-            <gr-autocomplete id="ownerInput" text="{{_repoOwner}}" value="{{_repoOwnerId}}" query="[[_queryGroups]]">
-            </gr-autocomplete>
-          </span>
-        </section>
-        <section>
-          <span class="title">Create initial empty commit</span>
-          <span class="value">
-            <gr-select id="initialCommit" bind-value="{{_repoConfig.create_empty_commit}}">
-              <select>
-                <option value="false">False</option>
-                <option value="true">True</option>
-              </select>
-            </gr-select>
-          </span>
-        </section>
-        <section>
-          <span class="title">Only serve as parent for other repositories</span>
-          <span class="value">
-            <gr-select id="parentRepo" bind-value="{{_repoConfig.permissions_only}}">
-              <select>
-                <option value="false">False</option>
-                <option value="true">True</option>
-              </select>
-            </gr-select>
-          </span>
-        </section>
-      </div>
+  <div class="gr-form-styles">
+    <div id="form">
+      <section>
+        <span class="title">Repository name</span>
+        <iron-input autocomplete="on" bind-value="{{_repoConfig.name}}">
+          <input
+            is="iron-input"
+            id="repoNameInput"
+            autocomplete="on"
+            bind-value="{{_repoConfig.name}}"
+          />
+        </iron-input>
+      </section>
+      <section>
+        <span class="title">Rights inherit from</span>
+        <span class="value">
+          <gr-autocomplete
+            id="rightsInheritFromInput"
+            text="{{_repoConfig.parent}}"
+            query="[[_query]]"
+            placeholder="Optional, defaults to 'All-Projects'"
+          >
+          </gr-autocomplete>
+        </span>
+      </section>
+      <section>
+        <span class="title">Owner</span>
+        <span class="value">
+          <gr-autocomplete
+            id="ownerInput"
+            text="{{_repoOwner}}"
+            value="{{_repoOwnerId}}"
+            query="[[_queryGroups]]"
+          >
+          </gr-autocomplete>
+        </span>
+      </section>
+      <section>
+        <span class="title">Create initial empty commit</span>
+        <span class="value">
+          <gr-select
+            id="initialCommit"
+            bind-value="{{_repoConfig.create_empty_commit}}"
+          >
+            <select>
+              <option value="false">False</option>
+              <option value="true">True</option>
+            </select>
+          </gr-select>
+        </span>
+      </section>
+      <section>
+        <span class="title">Only serve as parent for other repositories</span>
+        <span class="value">
+          <gr-select
+            id="parentRepo"
+            bind-value="{{_repoConfig.permissions_only}}"
+          >
+            <select>
+              <option value="false">False</option>
+              <option value="true">True</option>
+            </select>
+          </gr-select>
+        </span>
+      </section>
     </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
index a14a9c9..dfab4ac 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-create-repo-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 a3c05cb..2c0c130 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
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-table-styles.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-date-formatter/gr-date-formatter.js';
@@ -32,7 +31,7 @@
 const GROUP_EVENTS = ['ADD_GROUP', 'REMOVE_GROUP'];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrGroupAuditLog extends mixinBehaviors( [
   ListViewBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.js
index 0958e7c..130efbb 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.js
@@ -17,52 +17,54 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-table-styles">
-      /* GenericList style centers the last column, but we don't want that here. */
-      .genericList tr th:last-of-type,
-      .genericList tr td:last-of-type {
-        text-align: left;
-      }
-    </style>
-    <table id="list" class="genericList">
-      <tbody><tr class="headerRow">
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-table-styles">
+    /* GenericList style centers the last column, but we don't want that here. */
+    .genericList tr th:last-of-type,
+    .genericList tr td:last-of-type {
+      text-align: left;
+    }
+  </style>
+  <table id="list" class="genericList">
+    <tbody>
+      <tr class="headerRow">
         <th class="date topHeader">Date</th>
         <th class="type topHeader">Type</th>
         <th class="member topHeader">Member</th>
         <th class="by-user topHeader">By User</th>
       </tr>
-      <tr id="loading" class\$="loadingMsg [[computeLoadingClass(_loading)]]">
+      <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
         <td>Loading...</td>
       </tr>
-      </tbody><tbody class\$="[[computeLoadingClass(_loading)]]">
-        <template is="dom-repeat" items="[[_auditLog]]">
-          <tr class="table">
-            <td class="date">
-              <gr-date-formatter has-tooltip="" date-str="[[item.date]]">
-              </gr-date-formatter>
-            </td>
-            <td class="type">[[itemType(item.type)]]</td>
-            <td class="member">
-              <template is="dom-if" if="[[_isGroupEvent(item.type)]]">
-                <a href\$="[[_computeGroupUrl(item.member)]]">
-                  [[_getNameForGroup(item.member)]]
-                </a>
-              </template>
-              <template is="dom-if" if="[[!_isGroupEvent(item.type)]]">
-                <gr-account-link account="[[item.member]]"></gr-account-link>
-                [[_getIdForUser(item.member)]]
-              </template>
-            </td>
-            <td class="by-user">
-              <gr-account-link account="[[item.user]]"></gr-account-link>
-              [[_getIdForUser(item.user)]]
-            </td>
-          </tr>
-        </template>
-      </tbody>
-    </table>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </tbody>
+    <tbody class$="[[computeLoadingClass(_loading)]]">
+      <template is="dom-repeat" items="[[_auditLog]]">
+        <tr class="table">
+          <td class="date">
+            <gr-date-formatter has-tooltip="" date-str="[[item.date]]">
+            </gr-date-formatter>
+          </td>
+          <td class="type">[[itemType(item.type)]]</td>
+          <td class="member">
+            <template is="dom-if" if="[[_isGroupEvent(item.type)]]">
+              <a href$="[[_computeGroupUrl(item.member)]]">
+                [[_getNameForGroup(item.member)]]
+              </a>
+            </template>
+            <template is="dom-if" if="[[!_isGroupEvent(item.type)]]">
+              <gr-account-link account="[[item.member]]"></gr-account-link>
+              [[_getIdForUser(item.member)]]
+            </template>
+          </td>
+          <td class="by-user">
+            <gr-account-link account="[[item.user]]"></gr-account-link>
+            [[_getIdForUser(item.user)]]
+          </td>
+        </tr>
+      </template>
+    </tbody>
+  </table>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
index c7a41bf..4590220 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-group-audit-log</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 45f7612..9e5793f 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/gr-subpage-styles.js';
@@ -41,7 +40,7 @@
 const URL_REGEX = '^(?:[a-z]+:)?//';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrGroupMembers extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.js
index 79a88fd..c5577c2 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_html.js
@@ -17,130 +17,168 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-subpage-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="shared-styles">
-      .input {
-        width: 15em;
-      }
-      gr-autocomplete {
-        width: 20em;
-      }
-      a {
-        color: var(--primary-text-color);
-        text-decoration: none;
-      }
-      a:hover {
-        text-decoration: underline;
-      }
-      th {
-        border-bottom: 1px solid var(--border-color);
-        font-weight: var(--font-weight-bold);
-        text-align: left;
-      }
-      .canModify #groupMemberSearchInput,
-      .canModify #saveGroupMember,
-      .canModify .deleteHeader,
-      .canModify .deleteColumn,
-      .canModify #includedGroupSearchInput,
-      .canModify #saveIncludedGroups,
-      .canModify .deleteIncludedHeader,
-      .canModify #saveIncludedGroups {
-        display: none;
-      }
-    </style>
-    <main class\$="gr-form-styles [[_computeHideItemClass(_groupOwner, _isAdmin)]]">
-      <div id="loading" class\$="[[_computeLoadingClass(_loading)]]">
-        Loading...
-      </div>
-      <div id="loadedContent" class\$="[[_computeLoadingClass(_loading)]]">
-        <h1 id="Title">[[_groupName]]</h1>
-        <div id="form">
-          <h3 id="members">Members</h3>
-          <fieldset>
-            <span class="value">
-              <gr-autocomplete id="groupMemberSearchInput" text="{{_groupMemberSearchName}}" value="{{_groupMemberSearchId}}" query="[[_queryMembers]]" placeholder="Name Or Email">
-              </gr-autocomplete>
-            </span>
-            <gr-button id="saveGroupMember" on-click="_handleSavingGroupMember" disabled="[[!_groupMemberSearchId]]">
-              Add
-            </gr-button>
-            <table id="groupMembers">
-              <tbody><tr class="headerRow">
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-subpage-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="shared-styles">
+    .input {
+      width: 15em;
+    }
+    gr-autocomplete {
+      width: 20em;
+    }
+    a {
+      color: var(--primary-text-color);
+      text-decoration: none;
+    }
+    a:hover {
+      text-decoration: underline;
+    }
+    th {
+      border-bottom: 1px solid var(--border-color);
+      font-weight: var(--font-weight-bold);
+      text-align: left;
+    }
+    .canModify #groupMemberSearchInput,
+    .canModify #saveGroupMember,
+    .canModify .deleteHeader,
+    .canModify .deleteColumn,
+    .canModify #includedGroupSearchInput,
+    .canModify #saveIncludedGroups,
+    .canModify .deleteIncludedHeader,
+    .canModify #saveIncludedGroups {
+      display: none;
+    }
+  </style>
+  <main
+    class$="gr-form-styles [[_computeHideItemClass(_groupOwner, _isAdmin)]]"
+  >
+    <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
+      Loading...
+    </div>
+    <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
+      <h1 id="Title">[[_groupName]]</h1>
+      <div id="form">
+        <h3 id="members">Members</h3>
+        <fieldset>
+          <span class="value">
+            <gr-autocomplete
+              id="groupMemberSearchInput"
+              text="{{_groupMemberSearchName}}"
+              value="{{_groupMemberSearchId}}"
+              query="[[_queryMembers]]"
+              placeholder="Name Or Email"
+            >
+            </gr-autocomplete>
+          </span>
+          <gr-button
+            id="saveGroupMember"
+            on-click="_handleSavingGroupMember"
+            disabled="[[!_groupMemberSearchId]]"
+          >
+            Add
+          </gr-button>
+          <table id="groupMembers">
+            <tbody>
+              <tr class="headerRow">
                 <th class="nameHeader">Name</th>
                 <th class="emailAddressHeader">Email Address</th>
                 <th class="deleteHeader">Delete Member</th>
               </tr>
-              </tbody><tbody>
-                <template is="dom-repeat" items="[[_groupMembers]]">
-                  <tr>
-                    <td class="nameColumn">
-                      <gr-account-link account="[[item]]"></gr-account-link>
-                    </td>
-                    <td>[[item.email]]</td>
-                    <td class="deleteColumn">
-                      <gr-button class="deleteMembersButton" on-click="_handleDeleteMember">
-                        Delete
-                      </gr-button>
-                    </td>
-                  </tr>
-                </template>
-              </tbody>
-            </table>
-          </fieldset>
-          <h3 id="includedGroups">Included Groups</h3>
-          <fieldset>
-            <span class="value">
-              <gr-autocomplete id="includedGroupSearchInput" text="{{_includedGroupSearchName}}" value="{{_includedGroupSearchId}}" query="[[_queryIncludedGroup]]" placeholder="Group Name">
-              </gr-autocomplete>
-            </span>
-            <gr-button id="saveIncludedGroups" on-click="_handleSavingIncludedGroups" disabled="[[!_includedGroupSearchId]]">
-              Add
-            </gr-button>
-            <table id="includedGroups">
-              <tbody><tr class="headerRow">
+            </tbody>
+            <tbody>
+              <template is="dom-repeat" items="[[_groupMembers]]">
+                <tr>
+                  <td class="nameColumn">
+                    <gr-account-link account="[[item]]"></gr-account-link>
+                  </td>
+                  <td>[[item.email]]</td>
+                  <td class="deleteColumn">
+                    <gr-button
+                      class="deleteMembersButton"
+                      on-click="_handleDeleteMember"
+                    >
+                      Delete
+                    </gr-button>
+                  </td>
+                </tr>
+              </template>
+            </tbody>
+          </table>
+        </fieldset>
+        <h3 id="includedGroups">Included Groups</h3>
+        <fieldset>
+          <span class="value">
+            <gr-autocomplete
+              id="includedGroupSearchInput"
+              text="{{_includedGroupSearchName}}"
+              value="{{_includedGroupSearchId}}"
+              query="[[_queryIncludedGroup]]"
+              placeholder="Group Name"
+            >
+            </gr-autocomplete>
+          </span>
+          <gr-button
+            id="saveIncludedGroups"
+            on-click="_handleSavingIncludedGroups"
+            disabled="[[!_includedGroupSearchId]]"
+          >
+            Add
+          </gr-button>
+          <table id="includedGroups">
+            <tbody>
+              <tr class="headerRow">
                 <th class="groupNameHeader">Group Name</th>
                 <th class="descriptionHeader">Description</th>
                 <th class="deleteIncludedHeader">
                   Delete Group
                 </th>
               </tr>
-              </tbody><tbody>
-                <template is="dom-repeat" items="[[_includedGroups]]">
-                  <tr>
-                    <td class="nameColumn">
-                      <template is="dom-if" if="[[item.url]]">
-                        <a href\$="[[_computeGroupUrl(item.url)]]" rel="noopener">
-                          [[item.name]]
-                        </a>
-                      </template>
-                      <template is="dom-if" if="[[!item.url]]">
+            </tbody>
+            <tbody>
+              <template is="dom-repeat" items="[[_includedGroups]]">
+                <tr>
+                  <td class="nameColumn">
+                    <template is="dom-if" if="[[item.url]]">
+                      <a href$="[[_computeGroupUrl(item.url)]]" rel="noopener">
                         [[item.name]]
-                      </template>
-                    </td>
-                    <td>[[item.description]]</td>
-                    <td class="deleteColumn">
-                      <gr-button class="deleteIncludedGroupButton" on-click="_handleDeleteIncludedGroup">
-                        Delete
-                      </gr-button>
-                    </td>
-                  </tr>
-                </template>
-              </tbody>
-            </table>
-          </fieldset>
-        </div>
+                      </a>
+                    </template>
+                    <template is="dom-if" if="[[!item.url]]">
+                      [[item.name]]
+                    </template>
+                  </td>
+                  <td>[[item.description]]</td>
+                  <td class="deleteColumn">
+                    <gr-button
+                      class="deleteIncludedGroupButton"
+                      on-click="_handleDeleteIncludedGroup"
+                    >
+                      Delete
+                    </gr-button>
+                  </td>
+                </tr>
+              </template>
+            </tbody>
+          </table>
+        </fieldset>
       </div>
-    </main>
-    <gr-overlay id="overlay" with-backdrop="">
-      <gr-confirm-delete-item-dialog class="confirmDialog" on-confirm="_handleDeleteConfirm" on-cancel="_handleConfirmDialogCancel" item="[[_itemName]]" item-type="[[_itemType]]"></gr-confirm-delete-item-dialog>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </div>
+  </main>
+  <gr-overlay id="overlay" with-backdrop="">
+    <gr-confirm-delete-item-dialog
+      class="confirmDialog"
+      on-confirm="_handleDeleteConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      item="[[_itemName]]"
+      item-type="[[_itemType]]"
+    ></gr-confirm-delete-item-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
index e7c4afd..4dd9a7b 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-group-members</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 fc38c12..3dbc37f 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import '../../../scripts/bundled-polymer.js';
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/gr-subpage-styles.js';
@@ -43,7 +42,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrGroup extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.js
index 37f964e..0097d6d 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_html.js
@@ -17,99 +17,144 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-subpage-styles">
-      h3.edited:after {
-        color: var(--deemphasized-text-color);
-        content: ' *';
-      }
-      .inputUpdateBtn {
-        margin-top: var(--spacing-s);
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <main class="gr-form-styles read-only">
-      <div id="loading" class\$="[[_computeLoadingClass(_loading)]]">
-        Loading...
-      </div>
-      <div id="loadedContent" class\$="[[_computeLoadingClass(_loading)]]">
-        <h1 id="Title">[[_groupName]]</h1>
-        <h2 id="configurations">General</h2>
-        <div id="form">
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-subpage-styles">
+    h3.edited:after {
+      color: var(--deemphasized-text-color);
+      content: ' *';
+    }
+    .inputUpdateBtn {
+      margin-top: var(--spacing-s);
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <main class="gr-form-styles read-only">
+    <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
+      Loading...
+    </div>
+    <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
+      <h1 id="Title">[[_groupName]]</h1>
+      <h2 id="configurations">General</h2>
+      <div id="form">
+        <fieldset>
+          <h3 id="groupUUID">Group UUID</h3>
           <fieldset>
-            <h3 id="groupUUID">Group UUID</h3>
-            <fieldset>
-              <gr-copy-clipboard text="[[groupId]]"></gr-copy-clipboard>
-            </fieldset>
-            <h3 id="groupName" class\$="[[_computeHeaderClass(_rename)]]">
-              Group Name
-            </h3>
-            <fieldset>
-              <span class="value">
-                <gr-autocomplete id="groupNameInput" text="{{_groupConfig.name}}" disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"></gr-autocomplete>
-              </span>
-              <span class="value" disabled\$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
-                <gr-button id="inputUpdateNameBtn" on-click="_handleSaveName" disabled="[[!_rename]]">
-                  Rename Group</gr-button>
-              </span>
-            </fieldset>
-            <h3 id="groupOwner" class\$="[[_computeHeaderClass(_owner)]]">
-              Owners
-            </h3>
-            <fieldset>
-              <span class="value">
-                <gr-autocomplete id="groupOwnerInput" text="{{_groupConfig.owner}}" value="{{_groupConfigOwner}}" query="[[_query]]" disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
-                </gr-autocomplete>
-              </span>
-              <span class="value" disabled\$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
-                <gr-button id="inputUpdateOwnerBtn" on-click="_handleSaveOwner" disabled="[[!_owner]]">
-                  Change Owners</gr-button>
-              </span>
-            </fieldset>
-            <h3 class\$="[[_computeHeaderClass(_description)]]">
-              Description
-            </h3>
-            <fieldset>
-              <div>
-                <iron-autogrow-textarea class="description" autocomplete="on" bind-value="{{_groupConfig.description}}" disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"></iron-autogrow-textarea>
-              </div>
-              <span class="value" disabled\$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
-                <gr-button on-click="_handleSaveDescription" disabled="[[!_description]]">
-                  Save Description
-                </gr-button>
-              </span>
-            </fieldset>
-            <h3 id="options" class\$="[[_computeHeaderClass(_options)]]">
-              Group Options
-            </h3>
-            <fieldset id="visableToAll">
-              <section>
-                <span class="title">
-                  Make group visible to all registered users
-                </span>
-                <span class="value">
-                  <gr-select id="visibleToAll" bind-value="{{_groupConfig.options.visible_to_all}}">
-                    <select disabled\$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
-                      <template is="dom-repeat" items="[[_submitTypes]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <span class="value" disabled\$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
-                <gr-button on-click="_handleSaveOptions" disabled="[[!_options]]">
-                  Save Group Options
-                </gr-button>
-              </span>
-            </fieldset>
+            <gr-copy-clipboard text="[[groupId]]"></gr-copy-clipboard>
           </fieldset>
-        </div>
+          <h3 id="groupName" class$="[[_computeHeaderClass(_rename)]]">
+            Group Name
+          </h3>
+          <fieldset>
+            <span class="value">
+              <gr-autocomplete
+                id="groupNameInput"
+                text="{{_groupConfig.name}}"
+                disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+              ></gr-autocomplete>
+            </span>
+            <span
+              class="value"
+              disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+            >
+              <gr-button
+                id="inputUpdateNameBtn"
+                on-click="_handleSaveName"
+                disabled="[[!_rename]]"
+              >
+                Rename Group</gr-button
+              >
+            </span>
+          </fieldset>
+          <h3 id="groupOwner" class$="[[_computeHeaderClass(_owner)]]">
+            Owners
+          </h3>
+          <fieldset>
+            <span class="value">
+              <gr-autocomplete
+                id="groupOwnerInput"
+                text="{{_groupConfig.owner}}"
+                value="{{_groupConfigOwner}}"
+                query="[[_query]]"
+                disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+              >
+              </gr-autocomplete>
+            </span>
+            <span
+              class="value"
+              disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+            >
+              <gr-button
+                id="inputUpdateOwnerBtn"
+                on-click="_handleSaveOwner"
+                disabled="[[!_owner]]"
+              >
+                Change Owners</gr-button
+              >
+            </span>
+          </fieldset>
+          <h3 class$="[[_computeHeaderClass(_description)]]">
+            Description
+          </h3>
+          <fieldset>
+            <div>
+              <iron-autogrow-textarea
+                class="description"
+                autocomplete="on"
+                bind-value="{{_groupConfig.description}}"
+                disabled="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+              ></iron-autogrow-textarea>
+            </div>
+            <span
+              class="value"
+              disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+            >
+              <gr-button
+                on-click="_handleSaveDescription"
+                disabled="[[!_description]]"
+              >
+                Save Description
+              </gr-button>
+            </span>
+          </fieldset>
+          <h3 id="options" class$="[[_computeHeaderClass(_options)]]">
+            Group Options
+          </h3>
+          <fieldset id="visableToAll">
+            <section>
+              <span class="title">
+                Make group visible to all registered users
+              </span>
+              <span class="value">
+                <gr-select
+                  id="visibleToAll"
+                  bind-value="{{_groupConfig.options.visible_to_all}}"
+                >
+                  <select
+                    disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+                  >
+                    <template is="dom-repeat" items="[[_submitTypes]]">
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <span
+              class="value"
+              disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]"
+            >
+              <gr-button on-click="_handleSaveOptions" disabled="[[!_options]]">
+                Save Group Options
+              </gr-button>
+            </span>
+          </fieldset>
+        </fieldset>
       </div>
-    </main>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </div>
+  </main>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html
index f06ffde..2a278da 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-group</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
index 9fac11e..6edc9b0 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '@polymer/paper-toggle-button/paper-toggle-button.js';
 import '../../../styles/gr-form-styles.js';
@@ -48,7 +47,7 @@
  * Fired when a permission that was previously added was removed.
  *
  * @event added-permission-removed
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrPermission extends mixinBehaviors( [
   AccessBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.js b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.js
index 1b57336..ef4f1da 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_html.js
@@ -17,91 +17,127 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        margin-bottom: var(--spacing-m);
-      }
-      .header {
-        align-items: baseline;
-        display: flex;
-        justify-content: space-between;
-        margin: var(--spacing-s) var(--spacing-m);
-      }
-      .rules {
-        background: var(--table-header-background-color);
-        border: 1px solid var(--border-color);
-        border-bottom: 0;
-      }
-      .editing .rules {
-        border-bottom: 1px solid var(--border-color);
-      }
-      .title {
-        margin-bottom: var(--spacing-s);
-      }
-      #addRule,
-      #removeBtn {
-        display: none;
-      }
-      .right {
-        display: flex;
-        align-items: center;
-      }
-      .editing #removeBtn {
-        display: block;
-        margin-left: var(--spacing-xl);
-      }
-      .editing #addRule {
-        display: block;
-        padding: var(--spacing-m);
-      }
-      #deletedContainer,
-      .deleted #mainContainer {
-        display: none;
-      }
-      .deleted #deletedContainer {
-        align-items: baseline;
-        border: 1px solid var(--border-color);
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-m);
-      }
-      #mainContainer {
-        display: block;
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-menu-page-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <section id="permission" class\$="gr-form-styles [[_computeSectionClass(editing, _deleted)]]">
-      <div id="mainContainer">
-        <div class="header">
-          <span class="title">[[name]]</span>
-          <div class="right">
-            <template is="dom-if" if="[[!_permissionIsOwnerOrGlobal(permission.id, section)]]">
-              <paper-toggle-button id="exclusiveToggle" checked="{{permission.value.exclusive}}" on-change="_handleValueChange" disabled\$="[[!editing]]"></paper-toggle-button>Exclusive
-            </template>
-            <gr-button link="" id="removeBtn" on-click="_handleRemovePermission">Remove</gr-button>
-          </div>
-        </div><!-- end header -->
-        <div class="rules">
-          <template is="dom-repeat" items="{{_rules}}" as="rule">
-            <gr-rule-editor has-range="[[_computeHasRange(name)]]" label="[[_label]]" editing="[[editing]]" group-id="[[rule.id]]" group-name="[[_computeGroupName(groups, rule.id)]]" permission="[[permission.id]]" rule="{{rule}}" section="[[section]]" on-added-rule-removed="_handleAddedRuleRemoved"></gr-rule-editor>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      margin-bottom: var(--spacing-m);
+    }
+    .header {
+      align-items: baseline;
+      display: flex;
+      justify-content: space-between;
+      margin: var(--spacing-s) var(--spacing-m);
+    }
+    .rules {
+      background: var(--table-header-background-color);
+      border: 1px solid var(--border-color);
+      border-bottom: 0;
+    }
+    .editing .rules {
+      border-bottom: 1px solid var(--border-color);
+    }
+    .title {
+      margin-bottom: var(--spacing-s);
+    }
+    #addRule,
+    #removeBtn {
+      display: none;
+    }
+    .right {
+      display: flex;
+      align-items: center;
+    }
+    .editing #removeBtn {
+      display: block;
+      margin-left: var(--spacing-xl);
+    }
+    .editing #addRule {
+      display: block;
+      padding: var(--spacing-m);
+    }
+    #deletedContainer,
+    .deleted #mainContainer {
+      display: none;
+    }
+    .deleted #deletedContainer {
+      align-items: baseline;
+      border: 1px solid var(--border-color);
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-m);
+    }
+    #mainContainer {
+      display: block;
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-menu-page-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <section
+    id="permission"
+    class$="gr-form-styles [[_computeSectionClass(editing, _deleted)]]"
+  >
+    <div id="mainContainer">
+      <div class="header">
+        <span class="title">[[name]]</span>
+        <div class="right">
+          <template
+            is="dom-if"
+            if="[[!_permissionIsOwnerOrGlobal(permission.id, section)]]"
+          >
+            <paper-toggle-button
+              id="exclusiveToggle"
+              checked="{{permission.value.exclusive}}"
+              on-change="_handleValueChange"
+              disabled$="[[!editing]]"
+            ></paper-toggle-button
+            >Exclusive
           </template>
-          <div id="addRule">
-            <gr-autocomplete id="groupAutocomplete" text="{{_groupFilter}}" query="[[_query]]" placeholder="Add group" on-commit="_handleAddRuleItem">
-            </gr-autocomplete>
-          </div>
-          <!-- end addRule -->
-        </div> <!-- end rules -->
-      </div><!-- end mainContainer -->
-      <div id="deletedContainer">
-        <span>[[name]] was deleted</span>
-        <gr-button link="" id="undoRemoveBtn" on-click="_handleUndoRemove">Undo</gr-button>
-      </div><!-- end deletedContainer -->
-    </section>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+          <gr-button link="" id="removeBtn" on-click="_handleRemovePermission"
+            >Remove</gr-button
+          >
+        </div>
+      </div>
+      <!-- end header -->
+      <div class="rules">
+        <template is="dom-repeat" items="{{_rules}}" as="rule">
+          <gr-rule-editor
+            has-range="[[_computeHasRange(name)]]"
+            label="[[_label]]"
+            editing="[[editing]]"
+            group-id="[[rule.id]]"
+            group-name="[[_computeGroupName(groups, rule.id)]]"
+            permission="[[permission.id]]"
+            rule="{{rule}}"
+            section="[[section]]"
+            on-added-rule-removed="_handleAddedRuleRemoved"
+          ></gr-rule-editor>
+        </template>
+        <div id="addRule">
+          <gr-autocomplete
+            id="groupAutocomplete"
+            text="{{_groupFilter}}"
+            query="[[_query]]"
+            placeholder="Add group"
+            on-commit="_handleAddRuleItem"
+          >
+          </gr-autocomplete>
+        </div>
+        <!-- end addRule -->
+      </div>
+      <!-- end rules -->
+    </div>
+    <!-- end mainContainer -->
+    <div id="deletedContainer">
+      <span>[[name]] was deleted</span>
+      <gr-button link="" id="undoRemoveBtn" on-click="_handleUndoRemove"
+        >Undo</gr-button
+      >
+    </div>
+    <!-- end deletedContainer -->
+  </section>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
index 90c299c..1ce492e 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-permission</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js
index 318c2c3..d9d37f6 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '@polymer/paper-toggle-button/paper-toggle-button.js';
 import '../../../styles/gr-form-styles.js';
@@ -27,7 +25,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-plugin-config-array-editor_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrPluginConfigArrayEditor extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.js b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.js
index d97e2b37..be35035 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_html.js
@@ -17,66 +17,83 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      .wrapper {
-        width: 30em;
-      }
-      .existingItems {
-        background: var(--table-header-background-color);
-        border: 1px solid var(--border-color);
-        border-radius: var(--border-radius);
-      }
-      gr-button {
-        float: right;
-        margin-left: var(--spacing-m);
-        width: 4.5em;
-      }
-      .row {
-        align-items: center;
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-m) 0;
-        width: 100%;
-      }
-      .existingItems .row {
-        padding: var(--spacing-m);
-      }
-      .existingItems .row:not(:first-of-type) {
-        border-top: 1px solid var(--border-color);
-      }
-      input {
-        flex-grow: 1;
-      }
-      .hide {
-        display: none;
-      }
-      .placeholder {
-        color: var(--deemphasized-text-color);
-        padding-top: var(--spacing-m);
-      }
-    </style>
-    <div class="wrapper gr-form-styles">
-      <template is="dom-if" if="[[pluginOption.info.values.length]]">
-        <div class="existingItems">
-          <template is="dom-repeat" items="[[pluginOption.info.values]]">
-            <div class="row">
-              <span>[[item]]</span>
-              <gr-button link="" disabled\$="[[disabled]]" data-item\$="[[item]]" on-click="_handleDelete">Delete</gr-button>
-            </div>
-          </template>
-        </div>
-      </template>
-      <template is="dom-if" if="[[!pluginOption.info.values.length]]">
-        <div class="row placeholder">None configured.</div>
-      </template>
-      <div class\$="row [[_computeShowInputRow(disabled)]]">
-        <iron-input on-keydown="_handleInputKeydown" bind-value="{{_newValue}}">
-          <input is="iron-input" id="input" on-keydown="_handleInputKeydown" bind-value="{{_newValue}}">
-        </iron-input>
-        <gr-button id="addButton" disabled\$="[[!_newValue.length]]" link="" on-click="_handleAddTap">Add</gr-button>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    .wrapper {
+      width: 30em;
+    }
+    .existingItems {
+      background: var(--table-header-background-color);
+      border: 1px solid var(--border-color);
+      border-radius: var(--border-radius);
+    }
+    gr-button {
+      float: right;
+      margin-left: var(--spacing-m);
+      width: 4.5em;
+    }
+    .row {
+      align-items: center;
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-m) 0;
+      width: 100%;
+    }
+    .existingItems .row {
+      padding: var(--spacing-m);
+    }
+    .existingItems .row:not(:first-of-type) {
+      border-top: 1px solid var(--border-color);
+    }
+    input {
+      flex-grow: 1;
+    }
+    .hide {
+      display: none;
+    }
+    .placeholder {
+      color: var(--deemphasized-text-color);
+      padding-top: var(--spacing-m);
+    }
+  </style>
+  <div class="wrapper gr-form-styles">
+    <template is="dom-if" if="[[pluginOption.info.values.length]]">
+      <div class="existingItems">
+        <template is="dom-repeat" items="[[pluginOption.info.values]]">
+          <div class="row">
+            <span>[[item]]</span>
+            <gr-button
+              link=""
+              disabled$="[[disabled]]"
+              data-item$="[[item]]"
+              on-click="_handleDelete"
+              >Delete</gr-button
+            >
+          </div>
+        </template>
       </div>
+    </template>
+    <template is="dom-if" if="[[!pluginOption.info.values.length]]">
+      <div class="row placeholder">None configured.</div>
+    </template>
+    <div class$="row [[_computeShowInputRow(disabled)]]">
+      <iron-input on-keydown="_handleInputKeydown" bind-value="{{_newValue}}">
+        <input
+          is="iron-input"
+          id="input"
+          on-keydown="_handleInputKeydown"
+          bind-value="{{_newValue}}"
+        />
+      </iron-input>
+      <gr-button
+        id="addButton"
+        disabled$="[[!_newValue.length]]"
+        link=""
+        on-click="_handleAddTap"
+        >Add</gr-button
+      >
     </div>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html
index ecb2ec3..5eff42d 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-plugin-config-array-editor</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 154af6e..868a7cd 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/gr-table-styles.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-list-view/gr-list-view.js';
@@ -29,7 +27,7 @@
 
 /**
  * @appliesMixin ListViewMixin
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrPluginList extends mixinBehaviors( [
   ListViewBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.js b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.js
index 90192c4..042364f 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_html.js
@@ -17,39 +17,48 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <gr-list-view filter="[[_filter]]" items-per-page="[[_pluginsPerPage]]" items="[[_plugins]]" loading="[[_loading]]" offset="[[_offset]]" path="[[_path]]">
-      <table id="list" class="genericList">
-        <tbody><tr class="headerRow">
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <gr-list-view
+    filter="[[_filter]]"
+    items-per-page="[[_pluginsPerPage]]"
+    items="[[_plugins]]"
+    loading="[[_loading]]"
+    offset="[[_offset]]"
+    path="[[_path]]"
+  >
+    <table id="list" class="genericList">
+      <tbody>
+        <tr class="headerRow">
           <th class="name topHeader">Plugin Name</th>
           <th class="version topHeader">Version</th>
           <th class="status topHeader">Status</th>
         </tr>
-        <tr id="loading" class\$="loadingMsg [[computeLoadingClass(_loading)]]">
+        <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
           <td>Loading...</td>
         </tr>
-        </tbody><tbody class\$="[[computeLoadingClass(_loading)]]">
-          <template is="dom-repeat" items="[[_shownPlugins]]">
-            <tr class="table">
-              <td class="name">
-                <template is="dom-if" if="[[item.index_url]]">
-                  <a href\$="[[_computePluginUrl(item.index_url)]]">[[item.id]]</a>
-                </template>
-                <template is="dom-if" if="[[!item.index_url]]">
-                  [[item.id]]
-                </template>
-              </td>
-              <td class="version">[[item.version]]</td>
-              <td class="status">[[_status(item)]]</td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-    </gr-list-view>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </tbody>
+      <tbody class$="[[computeLoadingClass(_loading)]]">
+        <template is="dom-repeat" items="[[_shownPlugins]]">
+          <tr class="table">
+            <td class="name">
+              <template is="dom-if" if="[[item.index_url]]">
+                <a href$="[[_computePluginUrl(item.index_url)]]">[[item.id]]</a>
+              </template>
+              <template is="dom-if" if="[[!item.index_url]]">
+                [[item.id]]
+              </template>
+            </td>
+            <td class="version">[[item.version]]</td>
+            <td class="status">[[_status(item)]]</td>
+          </tr>
+        </template>
+      </tbody>
+    </table>
+  </gr-list-view>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
index da626e9..e2c88a2 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-plugin-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 9aa81f8..f6a1c10 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/gr-menu-page-styles.js';
 import '../../../styles/gr-subpage-styles.js';
 import '../../../styles/shared-styles.js';
@@ -83,7 +81,7 @@
 Defs.projectAccessInput;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepoAccess extends mixinBehaviors( [
   AccessBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.js
index 9a27371..5f0739a 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.js
@@ -17,75 +17,123 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-subpage-styles">
-      gr-button,
-      #inheritsFrom,
-      #editInheritFromInput,
-      .editing #inheritFromName,
-      .weblinks,
-      .editing .invisible{
-        display: none;
-      }
-      #inheritsFrom.show {
-        display: flex;
-        min-height: 2em;
-        align-items: center;
-      }
-      .weblink {
-        margin-right: var(--spacing-xs);
-      }
-      .weblinks.show,
-      .referenceContainer {
-        display: block;
-      }
-      .rightsText {
-        margin-right: var(--spacing-s);
-      }
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-subpage-styles">
+    gr-button,
+    #inheritsFrom,
+    #editInheritFromInput,
+    .editing #inheritFromName,
+    .weblinks,
+    .editing .invisible {
+      display: none;
+    }
+    #inheritsFrom.show {
+      display: flex;
+      min-height: 2em;
+      align-items: center;
+    }
+    .weblink {
+      margin-right: var(--spacing-xs);
+    }
+    .weblinks.show,
+    .referenceContainer {
+      display: block;
+    }
+    .rightsText {
+      margin-right: var(--spacing-s);
+    }
 
-      .editing gr-button,
-      .admin #editBtn {
-        display: inline-block;
-        margin: var(--spacing-l) 0;
-      }
-      .editing #editInheritFromInput {
-        display: inline-block;
-      }
-    </style>
-    <style include="gr-menu-page-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <main class\$="[[_computeMainClass(_ownerOf, _canUpload, _editing)]]">
-      <div id="loading" class\$="[[_computeLoadingClass(_loading)]]">
-        Loading...
-      </div>
-      <div id="loadedContent" class\$="[[_computeLoadingClass(_loading)]]">
-        <h3 id="inheritsFrom" class\$="[[_computeShowInherit(_inheritsFrom)]]">
-          <span class="rightsText">Rights Inherit From</span>
-          <a href\$="[[_computeParentHref(_inheritsFrom.name)]]" rel="noopener" id="inheritFromName">
-            [[_inheritsFrom.name]]</a>
-          <gr-autocomplete id="editInheritFromInput" text="{{_inheritFromFilter}}" query="[[_query]]" on-commit="_handleUpdateInheritFrom"></gr-autocomplete>
-        </h3>
-        <div class\$="weblinks [[_computeWebLinkClass(_weblinks)]]">
-          History:
-          <template is="dom-repeat" items="[[_weblinks]]" as="link">
-            <a href="[[link.url]]" class="weblink" rel="noopener" target="[[link.target]]">
-              [[link.name]]
-            </a>
-          </template>
-        </div>
-        <gr-button id="editBtn" on-click="_handleEdit">[[_editOrCancel(_editing)]]</gr-button>
-        <gr-button id="saveBtn" primary="" class\$="[[_computeSaveBtnClass(_ownerOf)]]" on-click="_handleSave" disabled="[[!_modified]]">Save</gr-button>
-        <gr-button id="saveReviewBtn" primary="" class\$="[[_computeSaveReviewBtnClass(_canUpload)]]" on-click="_handleSaveForReview" disabled="[[!_modified]]">Save for review</gr-button>
-        <template is="dom-repeat" items="{{_sections}}" initial-count="5" target-framerate="60" as="section">
-          <gr-access-section capabilities="[[_capabilities]]" section="{{section}}" labels="[[_labels]]" can-upload="[[_canUpload]]" editing="[[_editing]]" owner-of="[[_ownerOf]]" groups="[[_groups]]" on-added-section-removed="_handleAddedSectionRemoved"></gr-access-section>
+    .editing gr-button,
+    .admin #editBtn {
+      display: inline-block;
+      margin: var(--spacing-l) 0;
+    }
+    .editing #editInheritFromInput {
+      display: inline-block;
+    }
+  </style>
+  <style include="gr-menu-page-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <main class$="[[_computeMainClass(_ownerOf, _canUpload, _editing)]]">
+    <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
+      Loading...
+    </div>
+    <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
+      <h3 id="inheritsFrom" class$="[[_computeShowInherit(_inheritsFrom)]]">
+        <span class="rightsText">Rights Inherit From</span>
+        <a
+          href$="[[_computeParentHref(_inheritsFrom.name)]]"
+          rel="noopener"
+          id="inheritFromName"
+        >
+          [[_inheritsFrom.name]]</a
+        >
+        <gr-autocomplete
+          id="editInheritFromInput"
+          text="{{_inheritFromFilter}}"
+          query="[[_query]]"
+          on-commit="_handleUpdateInheritFrom"
+        ></gr-autocomplete>
+      </h3>
+      <div class$="weblinks [[_computeWebLinkClass(_weblinks)]]">
+        History:
+        <template is="dom-repeat" items="[[_weblinks]]" as="link">
+          <a
+            href="[[link.url]]"
+            class="weblink"
+            rel="noopener"
+            target="[[link.target]]"
+          >
+            [[link.name]]
+          </a>
         </template>
-        <div class="referenceContainer">
-          <gr-button id="addReferenceBtn" on-click="_handleCreateSection">Add Reference</gr-button>
-        </div>
       </div>
-    </main>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      <gr-button id="editBtn" on-click="_handleEdit"
+        >[[_editOrCancel(_editing)]]</gr-button
+      >
+      <gr-button
+        id="saveBtn"
+        primary=""
+        class$="[[_computeSaveBtnClass(_ownerOf)]]"
+        on-click="_handleSave"
+        disabled="[[!_modified]]"
+        >Save</gr-button
+      >
+      <gr-button
+        id="saveReviewBtn"
+        primary=""
+        class$="[[_computeSaveReviewBtnClass(_canUpload)]]"
+        on-click="_handleSaveForReview"
+        disabled="[[!_modified]]"
+        >Save for review</gr-button
+      >
+      <template
+        is="dom-repeat"
+        items="{{_sections}}"
+        initial-count="5"
+        target-framerate="60"
+        as="section"
+      >
+        <gr-access-section
+          capabilities="[[_capabilities]]"
+          section="{{section}}"
+          labels="[[_labels]]"
+          can-upload="[[_canUpload]]"
+          editing="[[_editing]]"
+          owner-of="[[_ownerOf]]"
+          groups="[[_groups]]"
+          on-added-section-removed="_handleAddedSectionRemoved"
+        ></gr-access-section>
+      </template>
+      <div class="referenceContainer">
+        <gr-button id="addReferenceBtn" on-click="_handleCreateSection"
+          >Add Reference</gr-button
+        >
+      </div>
+    </div>
+  </main>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
index 4835dfe..7d66cb0 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-access</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 53b4989..d161423 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -23,7 +21,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-repo-command_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrRepoCommand extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_html.js b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_html.js
index 10d22fc..cf934b0 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_html.js
@@ -17,14 +17,18 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        margin-bottom: var(--spacing-xxl);
-      }
-    </style>
-    <h3>[[title]]</h3>
-    <gr-button title\$="[[tooltip]]" disabled\$="[[disabled]]" on-click="_onCommandTap">
-      [[title]]
-    </gr-button>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      margin-bottom: var(--spacing-xxl);
+    }
+  </style>
+  <h3>[[title]]</h3>
+  <gr-button
+    title$="[[tooltip]]"
+    disabled$="[[disabled]]"
+    on-click="_onCommandTap"
+  >
+    [[title]]
+  </gr-button>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html
index c260613..a73f071 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-command</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 1f1dc5b..03a2bd3 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/gr-subpage-styles.js';
@@ -43,7 +41,7 @@
 const CREATE_CHANGE_SUCCEEDED_MESSAGE = 'Navigating to change';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepoCommands extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.js b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.js
index ce19555..b27c36b 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_html.js
@@ -17,45 +17,69 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-subpage-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <main class="gr-form-styles read-only">
-      <h1 id="Title">Repository Commands</h1>
-      <div id="loading" class\$="[[_computeLoadingClass(_loading)]]">Loading...</div>
-      <div id="loadedContent" class\$="[[_computeLoadingClass(_loading)]]">
-        <h2 id="options">Command</h2>
-        <div id="form">
-          <gr-repo-command title="Create change" on-command-tap="_createNewChange">
-          </gr-repo-command>
-          <gr-repo-command id="editRepoConfig" title="Edit repo config" on-command-tap="_handleEditRepoConfig">
-          </gr-repo-command>
-          <gr-repo-command title="[[_repoConfig.actions.gc.label]]" tooltip="[[_repoConfig.actions.gc.title]]" hidden\$="[[!_repoConfig.actions.gc.enabled]]" on-command-tap="_handleRunningGC">
-          </gr-repo-command>
-          <gr-endpoint-decorator name="repo-command">
-            <gr-endpoint-param name="config" value="[[_repoConfig]]">
-            </gr-endpoint-param>
-            <gr-endpoint-param name="repoName" value="[[repo]]">
-            </gr-endpoint-param>
-          </gr-endpoint-decorator>
-        </div>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-subpage-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <main class="gr-form-styles read-only">
+    <h1 id="Title">Repository Commands</h1>
+    <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
+      Loading...
+    </div>
+    <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
+      <h2 id="options">Command</h2>
+      <div id="form">
+        <gr-repo-command
+          title="Create change"
+          on-command-tap="_createNewChange"
+        >
+        </gr-repo-command>
+        <gr-repo-command
+          id="editRepoConfig"
+          title="Edit repo config"
+          on-command-tap="_handleEditRepoConfig"
+        >
+        </gr-repo-command>
+        <gr-repo-command
+          title="[[_repoConfig.actions.gc.label]]"
+          tooltip="[[_repoConfig.actions.gc.title]]"
+          hidden$="[[!_repoConfig.actions.gc.enabled]]"
+          on-command-tap="_handleRunningGC"
+        >
+        </gr-repo-command>
+        <gr-endpoint-decorator name="repo-command">
+          <gr-endpoint-param name="config" value="[[_repoConfig]]">
+          </gr-endpoint-param>
+          <gr-endpoint-param name="repoName" value="[[repo]]">
+          </gr-endpoint-param>
+        </gr-endpoint-decorator>
       </div>
-    </main>
-    <gr-overlay id="createChangeOverlay" with-backdrop="">
-      <gr-dialog id="createChangeDialog" confirm-label="Create" disabled="[[!_canCreate]]" on-confirm="_handleCreateChange" on-cancel="_handleCloseCreateChange">
-        <div class="header" slot="header">
-          Create Change
-        </div>
-        <div class="main" slot="main">
-          <gr-create-change-dialog id="createNewChangeModal" can-create="{{_canCreate}}" repo-name="[[repo]]"></gr-create-change-dialog>
-        </div>
-      </gr-dialog>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </div>
+  </main>
+  <gr-overlay id="createChangeOverlay" with-backdrop="">
+    <gr-dialog
+      id="createChangeDialog"
+      confirm-label="Create"
+      disabled="[[!_canCreate]]"
+      on-confirm="_handleCreateChange"
+      on-cancel="_handleCloseCreateChange"
+    >
+      <div class="header" slot="header">
+        Create Change
+      </div>
+      <div class="main" slot="main">
+        <gr-create-change-dialog
+          id="createNewChangeModal"
+          can-create="{{_canCreate}}"
+          repo-name="[[repo]]"
+        ></gr-create-change-dialog>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html
index 948eaa2..db2bfcf 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-commands</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 072fc721..f47ff76 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -26,7 +25,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepoDashboards extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.js b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.js
index 3bac16c..8ce69df 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_html.js
@@ -17,24 +17,25 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        margin-bottom: var(--spacing-xxl);
-      }
-      .loading #dashboards,
-      #loadingContainer {
-        display: none;
-      }
-      .loading #loadingContainer {
-        display: block;
-      }
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <table id="list" class\$="genericList [[_computeLoadingClass(_loading)]]">
-      <tbody><tr class="headerRow">
+  <style include="shared-styles">
+    :host {
+      display: block;
+      margin-bottom: var(--spacing-xxl);
+    }
+    .loading #dashboards,
+    #loadingContainer {
+      display: none;
+    }
+    .loading #loadingContainer {
+      display: block;
+    }
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <table id="list" class$="genericList [[_computeLoadingClass(_loading)]]">
+    <tbody>
+      <tr class="headerRow">
         <th class="topHeader">Dashboard name</th>
         <th class="topHeader">Dashboard title</th>
         <th class="topHeader">Dashboard description</th>
@@ -44,22 +45,27 @@
       <tr id="loadingContainer">
         <td>Loading...</td>
       </tr>
-      </tbody><tbody id="dashboards">
-        <template is="dom-repeat" items="[[_dashboards]]">
-          <tr class="groupHeader">
-            <td colspan="5">[[item.section]]</td>
+    </tbody>
+    <tbody id="dashboards">
+      <template is="dom-repeat" items="[[_dashboards]]">
+        <tr class="groupHeader">
+          <td colspan="5">[[item.section]]</td>
+        </tr>
+        <template is="dom-repeat" items="[[item.dashboards]]">
+          <tr class="table">
+            <td class="name">
+              <a href$="[[_getUrl(item.project, item.id)]]">[[item.path]]</a>
+            </td>
+            <td class="title">[[item.title]]</td>
+            <td class="desc">[[item.description]]</td>
+            <td class="inherited">
+              [[_computeInheritedFrom(item.project, item.defining_project)]]
+            </td>
+            <td class="default">[[_computeIsDefault(item.is_default)]]</td>
           </tr>
-          <template is="dom-repeat" items="[[item.dashboards]]">
-            <tr class="table">
-              <td class="name"><a href\$="[[_getUrl(item.project, item.id)]]">[[item.path]]</a></td>
-              <td class="title">[[item.title]]</td>
-              <td class="desc">[[item.description]]</td>
-              <td class="inherited">[[_computeInheritedFrom(item.project, item.defining_project)]]</td>
-              <td class="default">[[_computeIsDefault(item.is_default)]]</td>
-            </tr>
-          </template>
         </template>
-      </tbody>
-    </table>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </template>
+    </tbody>
+  </table>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
index 8cae592..dc12eff 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-dashboards</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 e8b3d9a..e3a9958 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
@@ -16,7 +16,6 @@
  */
 
 import '@polymer/iron-input/iron-input.js';
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/gr-table-styles.js';
 import '../../../styles/shared-styles.js';
@@ -47,7 +46,7 @@
 
 /**
  * @appliesMixin ListViewMixin
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepoDetailList extends mixinBehaviors( [
   ListViewBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.js
index 0d232f2..21971e7 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.js
@@ -17,141 +17,206 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="shared-styles">
-      .tags td.name {
-        min-width: 25em;
-      }
-      td.name,
-      td.revision,
-      td.message {
-        word-break: break-word;
-      }
-      td.revision.tags {
-        width: 27em;
-      }
-      td.message,
-      td.tagger {
-        max-width: 15em;
-      }
-      .editing .editItem {
-        display: inherit;
-      }
-      .editItem,
-      .editing .editBtn,
-      .canEdit .revisionNoEditing,
-      .editing .revisionWithEditing,
-      .revisionEdit,
-      .hideItem {
-        display: none;
-      }
-      .revisionEdit gr-button {
-        margin-left: var(--spacing-m);
-      }
-      .editBtn {
-        margin-left: var(--spacing-l);
-      }
-      .canEdit .revisionEdit{
-        align-items: center;
-        display: flex;
-      }
-      .deleteButton:not(.show) {
-        display: none;
-      }
-      .tagger.hide {
-        display: none;
-      }
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <gr-list-view create-new="[[_loggedIn]]" filter="[[_filter]]" items-per-page="[[_itemsPerPage]]" items="[[_items]]" loading="[[_loading]]" offset="[[_offset]]" on-create-clicked="_handleCreateClicked" path="[[_getPath(_repo, detailType)]]">
-      <table id="list" class="genericList gr-form-styles">
-        <tbody><tr class="headerRow">
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="shared-styles">
+    .tags td.name {
+      min-width: 25em;
+    }
+    td.name,
+    td.revision,
+    td.message {
+      word-break: break-word;
+    }
+    td.revision.tags {
+      width: 27em;
+    }
+    td.message,
+    td.tagger {
+      max-width: 15em;
+    }
+    .editing .editItem {
+      display: inherit;
+    }
+    .editItem,
+    .editing .editBtn,
+    .canEdit .revisionNoEditing,
+    .editing .revisionWithEditing,
+    .revisionEdit,
+    .hideItem {
+      display: none;
+    }
+    .revisionEdit gr-button {
+      margin-left: var(--spacing-m);
+    }
+    .editBtn {
+      margin-left: var(--spacing-l);
+    }
+    .canEdit .revisionEdit {
+      align-items: center;
+      display: flex;
+    }
+    .deleteButton:not(.show) {
+      display: none;
+    }
+    .tagger.hide {
+      display: none;
+    }
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <gr-list-view
+    create-new="[[_loggedIn]]"
+    filter="[[_filter]]"
+    items-per-page="[[_itemsPerPage]]"
+    items="[[_items]]"
+    loading="[[_loading]]"
+    offset="[[_offset]]"
+    on-create-clicked="_handleCreateClicked"
+    path="[[_getPath(_repo, detailType)]]"
+  >
+    <table id="list" class="genericList gr-form-styles">
+      <tbody>
+        <tr class="headerRow">
           <th class="name topHeader">Name</th>
           <th class="revision topHeader">Revision</th>
-          <th class\$="message topHeader [[_hideIfBranch(detailType)]]">
-            Message</th>
-          <th class\$="tagger topHeader [[_hideIfBranch(detailType)]]">
-            Tagger</th>
+          <th class$="message topHeader [[_hideIfBranch(detailType)]]">
+            Message
+          </th>
+          <th class$="tagger topHeader [[_hideIfBranch(detailType)]]">
+            Tagger
+          </th>
           <th class="repositoryBrowser topHeader">
-            Repository Browser</th>
+            Repository Browser
+          </th>
           <th class="delete topHeader"></th>
         </tr>
-        <tr id="loading" class\$="loadingMsg [[computeLoadingClass(_loading)]]">
+        <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
           <td>Loading...</td>
         </tr>
-        </tbody><tbody class\$="[[computeLoadingClass(_loading)]]">
-          <template is="dom-repeat" items="[[_shownItems]]">
-            <tr class="table">
-              <td class\$="[[detailType]] name">[[_stripRefs(item.ref, detailType)]]</td>
-              <td class\$="[[detailType]] revision [[_computeCanEditClass(item.ref, detailType, _isOwner)]]">
-                <span class="revisionNoEditing">
+      </tbody>
+      <tbody class$="[[computeLoadingClass(_loading)]]">
+        <template is="dom-repeat" items="[[_shownItems]]">
+          <tr class="table">
+            <td class$="[[detailType]] name">
+              [[_stripRefs(item.ref, detailType)]]
+            </td>
+            <td
+              class$="[[detailType]] revision [[_computeCanEditClass(item.ref, detailType, _isOwner)]]"
+            >
+              <span class="revisionNoEditing">
+                [[item.revision]]
+              </span>
+              <span class$="revisionEdit [[_computeEditingClass(_isEditing)]]">
+                <span class="revisionWithEditing">
                   [[item.revision]]
                 </span>
-                <span class\$="revisionEdit [[_computeEditingClass(_isEditing)]]">
-                  <span class="revisionWithEditing">
-                    [[item.revision]]
-                  </span>
-                  <gr-button link="" on-click="_handleEditRevision" class="editBtn">
-                    edit
-                  </gr-button>
-                  <iron-input bind-value="{{_revisedRef}}" class="editItem">
-                    <input is="iron-input" bind-value="{{_revisedRef}}">
-                  </iron-input>
-                  <gr-button link="" on-click="_handleCancelRevision" class="cancelBtn editItem">
-                    Cancel
-                  </gr-button>
-                  <gr-button link="" on-click="_handleSaveRevision" class="saveBtn editItem" disabled="[[!_revisedRef]]">
-                    Save
-                  </gr-button>
-                </span>
-              </td>
-              <td class\$="message [[_hideIfBranch(detailType)]]">
-                [[_computeMessage(item.message)]]
-              </td>
-              <td class\$="tagger [[_hideIfBranch(detailType)]]">
-                <div class\$="tagger [[_computeHideTagger(item.tagger)]]">
-                  <gr-account-link account="[[item.tagger]]">
-                  </gr-account-link>
-                  (<gr-date-formatter has-tooltip="" date-str="[[item.tagger.date]]">
-                  </gr-date-formatter>)
-                </div>
-              </td>
-              <td class="repositoryBrowser">
-                <template is="dom-repeat" items="[[_computeWeblink(item)]]" as="link">
-                  <a href\$="[[link.url]]" class="webLink" rel="noopener" target="_blank">
-                    ([[link.name]])
-                  </a>
-                </template>
-              </td>
-              <td class="delete">
-                <gr-button link="" class\$="deleteButton [[_computeHideDeleteClass(_isOwner, item.can_delete)]]" on-click="_handleDeleteItem">
-                  Delete
+                <gr-button
+                  link=""
+                  on-click="_handleEditRevision"
+                  class="editBtn"
+                >
+                  edit
                 </gr-button>
-              </td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-      <gr-overlay id="overlay" with-backdrop="">
-        <gr-confirm-delete-item-dialog class="confirmDialog" on-confirm="_handleDeleteItemConfirm" on-cancel="_handleConfirmDialogCancel" item="[[_refName]]" item-type="[[detailType]]"></gr-confirm-delete-item-dialog>
-      </gr-overlay>
-    </gr-list-view>
-    <gr-overlay id="createOverlay" with-backdrop="">
-      <gr-dialog id="createDialog" disabled="[[!_hasNewItemName]]" confirm-label="Create" on-confirm="_handleCreateItem" on-cancel="_handleCloseCreate">
-        <div class="header" slot="header">
-          Create [[_computeItemName(detailType)]]
-        </div>
-        <div class="main" slot="main">
-          <gr-create-pointer-dialog id="createNewModal" detail-type="[[_computeItemName(detailType)]]" has-new-item-name="{{_hasNewItemName}}" item-detail="[[detailType]]" repo-name="[[_repo]]"></gr-create-pointer-dialog>
-        </div>
-      </gr-dialog>
+                <iron-input bind-value="{{_revisedRef}}" class="editItem">
+                  <input is="iron-input" bind-value="{{_revisedRef}}" />
+                </iron-input>
+                <gr-button
+                  link=""
+                  on-click="_handleCancelRevision"
+                  class="cancelBtn editItem"
+                >
+                  Cancel
+                </gr-button>
+                <gr-button
+                  link=""
+                  on-click="_handleSaveRevision"
+                  class="saveBtn editItem"
+                  disabled="[[!_revisedRef]]"
+                >
+                  Save
+                </gr-button>
+              </span>
+            </td>
+            <td class$="message [[_hideIfBranch(detailType)]]">
+              [[_computeMessage(item.message)]]
+            </td>
+            <td class$="tagger [[_hideIfBranch(detailType)]]">
+              <div class$="tagger [[_computeHideTagger(item.tagger)]]">
+                <gr-account-link account="[[item.tagger]]"> </gr-account-link>
+                (<gr-date-formatter
+                  has-tooltip=""
+                  date-str="[[item.tagger.date]]"
+                >
+                </gr-date-formatter
+                >)
+              </div>
+            </td>
+            <td class="repositoryBrowser">
+              <template
+                is="dom-repeat"
+                items="[[_computeWeblink(item)]]"
+                as="link"
+              >
+                <a
+                  href$="[[link.url]]"
+                  class="webLink"
+                  rel="noopener"
+                  target="_blank"
+                >
+                  ([[link.name]])
+                </a>
+              </template>
+            </td>
+            <td class="delete">
+              <gr-button
+                link=""
+                class$="deleteButton [[_computeHideDeleteClass(_isOwner, item.can_delete)]]"
+                on-click="_handleDeleteItem"
+              >
+                Delete
+              </gr-button>
+            </td>
+          </tr>
+        </template>
+      </tbody>
+    </table>
+    <gr-overlay id="overlay" with-backdrop="">
+      <gr-confirm-delete-item-dialog
+        class="confirmDialog"
+        on-confirm="_handleDeleteItemConfirm"
+        on-cancel="_handleConfirmDialogCancel"
+        item="[[_refName]]"
+        item-type="[[detailType]]"
+      ></gr-confirm-delete-item-dialog>
     </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </gr-list-view>
+  <gr-overlay id="createOverlay" with-backdrop="">
+    <gr-dialog
+      id="createDialog"
+      disabled="[[!_hasNewItemName]]"
+      confirm-label="Create"
+      on-confirm="_handleCreateItem"
+      on-cancel="_handleCloseCreate"
+    >
+      <div class="header" slot="header">
+        Create [[_computeItemName(detailType)]]
+      </div>
+      <div class="main" slot="main">
+        <gr-create-pointer-dialog
+          id="createNewModal"
+          detail-type="[[_computeItemName(detailType)]]"
+          has-new-item-name="{{_hasNewItemName}}"
+          item-detail="[[detailType]]"
+          repo-name="[[_repo]]"
+        ></gr-create-pointer-dialog>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html
index 0ec12ac..9d7bba4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-detail-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 59abb72..a8119e6 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/gr-table-styles.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-dialog/gr-dialog.js';
@@ -33,7 +31,7 @@
 
 /**
  * @appliesMixin ListViewMixin
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepoList extends mixinBehaviors( [
   ListViewBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.js b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.js
index d498869..3681399 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_html.js
@@ -17,68 +17,104 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style>
-      .genericList tr td:last-of-type {
-        text-align: left;
-      }
-      .genericList tr th:last-of-type {
-        text-align: left;
-      }
-      .readOnly {
-        text-align: center;
-      }
-      .changesLink, .name, .repositoryBrowser, .readOnly {
-        white-space:nowrap;
-      }
-    </style>
-    <gr-list-view create-new="[[_createNewCapability]]" filter="[[_filter]]" items-per-page="[[_reposPerPage]]" items="[[_repos]]" loading="[[_loading]]" offset="[[_offset]]" on-create-clicked="_handleCreateClicked" path="[[_path]]">
-      <table id="list" class="genericList">
-        <tbody><tr class="headerRow">
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style>
+    .genericList tr td:last-of-type {
+      text-align: left;
+    }
+    .genericList tr th:last-of-type {
+      text-align: left;
+    }
+    .readOnly {
+      text-align: center;
+    }
+    .changesLink,
+    .name,
+    .repositoryBrowser,
+    .readOnly {
+      white-space: nowrap;
+    }
+  </style>
+  <gr-list-view
+    create-new="[[_createNewCapability]]"
+    filter="[[_filter]]"
+    items-per-page="[[_reposPerPage]]"
+    items="[[_repos]]"
+    loading="[[_loading]]"
+    offset="[[_offset]]"
+    on-create-clicked="_handleCreateClicked"
+    path="[[_path]]"
+  >
+    <table id="list" class="genericList">
+      <tbody>
+        <tr class="headerRow">
           <th class="name topHeader">Repository Name</th>
           <th class="repositoryBrowser topHeader">Repository Browser</th>
           <th class="changesLink topHeader">Changes</th>
           <th class="topHeader readOnly">Read only</th>
           <th class="description topHeader">Repository Description</th>
         </tr>
-        <tr id="loading" class\$="loadingMsg [[computeLoadingClass(_loading)]]">
+        <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
           <td>Loading...</td>
         </tr>
-        </tbody><tbody class\$="[[computeLoadingClass(_loading)]]">
-          <template is="dom-repeat" items="[[_shownRepos]]">
-            <tr class="table">
-              <td class="name">
-                <a href\$="[[_computeRepoUrl(item.name)]]">[[item.name]]</a>
-              </td>
-              <td class="repositoryBrowser">
-                <template is="dom-repeat" items="[[_computeWeblink(item)]]" as="link">
-                  <a href\$="[[link.url]]" class="webLink" rel="noopener" target="_blank">
-                    [[link.name]]
-                  </a>
-                </template>
-              </td>
-              <td class="changesLink"><a href\$="[[_computeChangesLink(item.name)]]">view all</a></td>
-              <td class="readOnly">[[_readOnly(item)]]</td>
-              <td class="description">[[item.description]]</td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-    </gr-list-view>
-    <gr-overlay id="createOverlay" with-backdrop="">
-      <gr-dialog id="createDialog" class="confirmDialog" disabled="[[!_hasNewRepoName]]" confirm-label="Create" on-confirm="_handleCreateRepo" on-cancel="_handleCloseCreate">
-        <div class="header" slot="header">
-          Create Repository
-        </div>
-        <div class="main" slot="main">
-          <gr-create-repo-dialog has-new-repo-name="{{_hasNewRepoName}}" params="[[params]]" id="createNewModal"></gr-create-repo-dialog>
-        </div>
-      </gr-dialog>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </tbody>
+      <tbody class$="[[computeLoadingClass(_loading)]]">
+        <template is="dom-repeat" items="[[_shownRepos]]">
+          <tr class="table">
+            <td class="name">
+              <a href$="[[_computeRepoUrl(item.name)]]">[[item.name]]</a>
+            </td>
+            <td class="repositoryBrowser">
+              <template
+                is="dom-repeat"
+                items="[[_computeWeblink(item)]]"
+                as="link"
+              >
+                <a
+                  href$="[[link.url]]"
+                  class="webLink"
+                  rel="noopener"
+                  target="_blank"
+                >
+                  [[link.name]]
+                </a>
+              </template>
+            </td>
+            <td class="changesLink">
+              <a href$="[[_computeChangesLink(item.name)]]">view all</a>
+            </td>
+            <td class="readOnly">[[_readOnly(item)]]</td>
+            <td class="description">[[item.description]]</td>
+          </tr>
+        </template>
+      </tbody>
+    </table>
+  </gr-list-view>
+  <gr-overlay id="createOverlay" with-backdrop="">
+    <gr-dialog
+      id="createDialog"
+      class="confirmDialog"
+      disabled="[[!_hasNewRepoName]]"
+      confirm-label="Create"
+      on-confirm="_handleCreateRepo"
+      on-cancel="_handleCloseCreate"
+    >
+      <div class="header" slot="header">
+        Create Repository
+      </div>
+      <div class="main" slot="main">
+        <gr-create-repo-dialog
+          has-new-repo-name="{{_hasNewRepoName}}"
+          params="[[params]]"
+          id="createNewModal"
+        ></gr-create-repo-dialog>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html
index 62652bd..96cb9ff 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
index d01f9ab..a70aa11 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '@polymer/paper-toggle-button/paper-toggle-button.js';
 import '../../../styles/gr-form-styles.js';
@@ -34,7 +32,7 @@
 import {RepoPluginConfig} from '../../../behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepoPluginConfig extends mixinBehaviors( [
   RepoPluginConfig,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.js
index fa4617d..80d77d6 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_html.js
@@ -17,64 +17,97 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-subpage-styles">
-      .inherited {
-        color: var(--deemphasized-text-color);
-        margin-left: var(--spacing-m);
-      }
-      section.section:not(.ARRAY) .title {
-        align-items: center;
-        display: flex;
-      }
-      section.section.ARRAY .title {
-        padding-top: var(--spacing-m);
-      }
-    </style>
-    <div class="gr-form-styles">
-      <fieldset>
-        <h4>[[pluginData.name]]</h4>
-        <template is="dom-repeat" items="[[_pluginConfigOptions]]" as="option">
-          <section class\$="section [[option.info.type]]">
-            <span class="title">
-              <gr-tooltip-content has-tooltip="[[option.info.description]]" show-icon="[[option.info.description]]" title="[[option.info.description]]">
-                <span>[[option.info.display_name]]</span>
-              </gr-tooltip-content>
-            </span>
-            <span class="value">
-              <template is="dom-if" if="[[_isArray(option.info.type)]]">
-                <gr-plugin-config-array-editor on-plugin-config-option-changed="_handleArrayChange" plugin-option="[[option]]"></gr-plugin-config-array-editor>
-              </template>
-              <template is="dom-if" if="[[_isBoolean(option.info.type)]]">
-                <paper-toggle-button checked="[[_computeChecked(option.info.value)]]" on-change="_handleBooleanChange" data-option-key\$="[[option._key]]" disabled\$="[[_computeDisabled(option.info.editable)]]"></paper-toggle-button>
-              </template>
-              <template is="dom-if" if="[[_isList(option.info.type)]]">
-                <gr-select bind-value\$="[[option.info.value]]" on-change="_handleListChange">
-                  <select data-option-key\$="[[option._key]]" disabled\$="[[_computeDisabled(option.info.editable)]]">
-                    <template is="dom-repeat" items="[[option.info.permitted_values]]" as="value">
-                      <option value\$="[[value]]">[[value]]</option>
-                    </template>
-                  </select>
-                </gr-select>
-              </template>
-              <template is="dom-if" if="[[_isString(option.info.type)]]">
-                <iron-input bind-value="[[option.info.value]]" on-input="_handleStringChange" data-option-key\$="[[option._key]]" disabled\$="[[_computeDisabled(option.info.editable)]]">
-                  <input is="iron-input" value="[[option.info.value]]" on-input="_handleStringChange" data-option-key\$="[[option._key]]" disabled\$="[[_computeDisabled(option.info.editable)]]">
-                </iron-input>
-              </template>
-              <template is="dom-if" if="[[option.info.inherited_value]]">
-                <span class="inherited">
-                  (Inherited: [[option.info.inherited_value]])
-                </span>
-              </template>
-            </span>
-          </section>
-        </template>
-      </fieldset>
-    </div>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-subpage-styles">
+    .inherited {
+      color: var(--deemphasized-text-color);
+      margin-left: var(--spacing-m);
+    }
+    section.section:not(.ARRAY) .title {
+      align-items: center;
+      display: flex;
+    }
+    section.section.ARRAY .title {
+      padding-top: var(--spacing-m);
+    }
+  </style>
+  <div class="gr-form-styles">
+    <fieldset>
+      <h4>[[pluginData.name]]</h4>
+      <template is="dom-repeat" items="[[_pluginConfigOptions]]" as="option">
+        <section class$="section [[option.info.type]]">
+          <span class="title">
+            <gr-tooltip-content
+              has-tooltip="[[option.info.description]]"
+              show-icon="[[option.info.description]]"
+              title="[[option.info.description]]"
+            >
+              <span>[[option.info.display_name]]</span>
+            </gr-tooltip-content>
+          </span>
+          <span class="value">
+            <template is="dom-if" if="[[_isArray(option.info.type)]]">
+              <gr-plugin-config-array-editor
+                on-plugin-config-option-changed="_handleArrayChange"
+                plugin-option="[[option]]"
+              ></gr-plugin-config-array-editor>
+            </template>
+            <template is="dom-if" if="[[_isBoolean(option.info.type)]]">
+              <paper-toggle-button
+                checked="[[_computeChecked(option.info.value)]]"
+                on-change="_handleBooleanChange"
+                data-option-key$="[[option._key]]"
+                disabled$="[[_computeDisabled(option.info.editable)]]"
+              ></paper-toggle-button>
+            </template>
+            <template is="dom-if" if="[[_isList(option.info.type)]]">
+              <gr-select
+                bind-value$="[[option.info.value]]"
+                on-change="_handleListChange"
+              >
+                <select
+                  data-option-key$="[[option._key]]"
+                  disabled$="[[_computeDisabled(option.info.editable)]]"
+                >
+                  <template
+                    is="dom-repeat"
+                    items="[[option.info.permitted_values]]"
+                    as="value"
+                  >
+                    <option value$="[[value]]">[[value]]</option>
+                  </template>
+                </select>
+              </gr-select>
+            </template>
+            <template is="dom-if" if="[[_isString(option.info.type)]]">
+              <iron-input
+                bind-value="[[option.info.value]]"
+                on-input="_handleStringChange"
+                data-option-key$="[[option._key]]"
+                disabled$="[[_computeDisabled(option.info.editable)]]"
+              >
+                <input
+                  is="iron-input"
+                  value="[[option.info.value]]"
+                  on-input="_handleStringChange"
+                  data-option-key$="[[option._key]]"
+                  disabled$="[[_computeDisabled(option.info.editable)]]"
+                />
+              </iron-input>
+            </template>
+            <template is="dom-if" if="[[option.info.inherited_value]]">
+              <span class="inherited">
+                (Inherited: [[option.info.inherited_value]])
+              </span>
+            </template>
+          </span>
+        </section>
+      </template>
+    </fieldset>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
index c2abef2..a2370d9 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-plugin-config</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 05ae73d..b3ddaf4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '@polymer/iron-input/iron-input.js';
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
@@ -68,7 +66,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepo extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.js b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.js
index 2c7540f..de36e73 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_html.js
@@ -17,280 +17,421 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-subpage-styles">
+    h2.edited:after {
+      color: var(--deemphasized-text-color);
+      content: ' *';
+    }
+    .loading,
+    .hide {
+      display: none;
+    }
+    #loading.loading {
+      display: block;
+    }
+    #loading:not(.loading) {
+      display: none;
+    }
+    #options .repositorySettings {
+      display: none;
+    }
+    #options .repositorySettings.showConfig {
+      display: block;
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <main class="gr-form-styles read-only">
     <style include="shared-styles">
       /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
     </style>
-    <style include="gr-subpage-styles">
-      h2.edited:after {
-        color: var(--deemphasized-text-color);
-        content: ' *';
-      }
-      .loading,
-      .hide {
-        display: none;
-      }
-      #loading.loading {
-        display: block;
-      }
-      #loading:not(.loading) {
-        display: none;
-      }
-      #options .repositorySettings {
-        display: none;
-      }
-      #options .repositorySettings.showConfig {
-        display: block;
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <main class="gr-form-styles read-only">
-      <style include="shared-styles">
-        /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-      </style>
-      <div class="info">
-        <h1 id="Title" class\$="name">
-          [[repo]]
-          <hr>
-        </h1>
-        <div>
-          <a href\$="[[_computeChangesUrl(repo)]]">(view changes)</a>
-        </div>
+    <div class="info">
+      <h1 id="Title" class$="name">
+        [[repo]]
+        <hr />
+      </h1>
+      <div>
+        <a href$="[[_computeChangesUrl(repo)]]">(view changes)</a>
       </div>
-      <div id="loading" class\$="[[_computeLoadingClass(_loading)]]">Loading...</div>
-      <div id="loadedContent" class\$="[[_computeLoadingClass(_loading)]]">
-        <div id="downloadContent" class\$="[[_computeHideClass(_schemes)]]">
-          <h2 id="download">Download</h2>
+    </div>
+    <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
+      Loading...
+    </div>
+    <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
+      <div id="downloadContent" class$="[[_computeHideClass(_schemes)]]">
+        <h2 id="download">Download</h2>
+        <fieldset>
+          <gr-download-commands
+            id="downloadCommands"
+            commands="[[_computeCommands(repo, _schemesObj, _selectedScheme)]]"
+            schemes="[[_schemes]]"
+            selected-scheme="{{_selectedScheme}}"
+          ></gr-download-commands>
+        </fieldset>
+      </div>
+      <h2 id="configurations" class$="[[_computeHeaderClass(_configChanged)]]">
+        Configurations
+      </h2>
+      <div id="form">
+        <fieldset>
+          <h3 id="Description">Description</h3>
           <fieldset>
-            <gr-download-commands id="downloadCommands" commands="[[_computeCommands(repo, _schemesObj, _selectedScheme)]]" schemes="[[_schemes]]" selected-scheme="{{_selectedScheme}}"></gr-download-commands>
+            <iron-autogrow-textarea
+              id="descriptionInput"
+              class="description"
+              autocomplete="on"
+              placeholder="<Insert repo description here>"
+              bind-value="{{_repoConfig.description}}"
+              disabled$="[[_readOnly]]"
+            ></iron-autogrow-textarea>
           </fieldset>
-        </div>
-        <h2 id="configurations" class\$="[[_computeHeaderClass(_configChanged)]]">Configurations</h2>
-        <div id="form">
-          <fieldset>
-            <h3 id="Description">Description</h3>
-            <fieldset>
-              <iron-autogrow-textarea id="descriptionInput" class="description" autocomplete="on" placeholder="<Insert repo description here>" bind-value="{{_repoConfig.description}}" disabled\$="[[_readOnly]]"></iron-autogrow-textarea>
-            </fieldset>
-            <h3 id="Options">Repository Options</h3>
-            <fieldset id="options">
-              <section>
-                <span class="title">State</span>
-                <span class="value">
-                  <gr-select id="stateSelect" bind-value="{{_repoConfig.state}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_states]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">Submit type</span>
-                <span class="value">
-                  <gr-select id="submitTypeSelect" bind-value="{{_repoConfig.submit_type}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatSubmitTypeSelect(_repoConfig)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">Allow content merges</span>
-                <span class="value">
-                  <gr-select id="contentMergeSelect" bind-value="{{_repoConfig.use_content_merge.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.use_content_merge)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">
-                  Create a new change for every commit not in the target branch
-                </span>
-                <span class="value">
-                  <gr-select id="newChangeSelect" bind-value="{{_repoConfig.create_new_change_for_all_not_in_target.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.create_new_change_for_all_not_in_target)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">Require Change-Id in commit message</span>
-                <span class="value">
-                  <gr-select id="requireChangeIdSelect" bind-value="{{_repoConfig.require_change_id.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.require_change_id)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section id="enableSignedPushSettings" class\$="repositorySettings [[_computeRepositoriesClass(_repoConfig.enable_signed_push)]]">
-                <span class="title">Enable signed push</span>
-                <span class="value">
-                  <gr-select id="enableSignedPush" bind-value="{{_repoConfig.enable_signed_push.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.enable_signed_push)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section id="requireSignedPushSettings" class\$="repositorySettings [[_computeRepositoriesClass(_repoConfig.require_signed_push)]]">
-                <span class="title">Require signed push</span>
-                <span class="value">
-                  <gr-select id="requireSignedPush" bind-value="{{_repoConfig.require_signed_push.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.require_signed_push)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">
-                  Reject implicit merges when changes are pushed for review</span>
-                <span class="value">
-                  <gr-select id="rejectImplicitMergesSelect" bind-value="{{_repoConfig.reject_implicit_merges.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.reject_implicit_merges)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">
-                  Enable adding unregistered users as reviewers and CCs on changes</span>
-                <span class="value">
-                  <gr-select id="unRegisteredCcSelect" bind-value="{{_repoConfig.enable_reviewer_by_email.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.enable_reviewer_by_email)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                  </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">
-                  Set all new changes private by default</span>
-                <span class="value">
-                  <gr-select id="setAllnewChangesPrivateByDefaultSelect" bind-value="{{_repoConfig.private_by_default.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.private_by_default)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">
-                  Set new changes to "work in progress" by default</span>
-                <span class="value">
-                  <gr-select id="setAllNewChangesWorkInProgressByDefaultSelect" bind-value="{{_repoConfig.work_in_progress_by_default.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.work_in_progress_by_default)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">Maximum Git object size limit</span>
-                <span class="value">
-                  <iron-input id="maxGitObjSizeIronInput" bind-value="{{_repoConfig.max_object_size_limit.configured_value}}" type="text" disabled\$="[[_readOnly]]">
-                    <input id="maxGitObjSizeInput" bind-value="{{_repoConfig.max_object_size_limit.configured_value}}" is="iron-input" type="text" disabled\$="[[_readOnly]]">
-                  </iron-input>
-                  <template is="dom-if" if="[[_repoConfig.max_object_size_limit.value]]">
-                    effective: [[_repoConfig.max_object_size_limit.value]] bytes
-                  </template>
-                </span>
-              </section>
-              <section>
-                <span class="title">Match authored date with committer date upon submit</span>
-                <span class="value">
-                  <gr-select id="matchAuthoredDateWithCommitterDateSelect" bind-value="{{_repoConfig.match_author_to_committer_date.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.match_author_to_committer_date)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                  </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">Reject empty commit upon submit</span>
-                <span class="value">
-                  <gr-select id="rejectEmptyCommitSelect" bind-value="{{_repoConfig.reject_empty_commit.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.reject_empty_commit)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                  </select>
-                  </gr-select>
-                </span>
-              </section>
-            </fieldset>
-            <h3 id="Options">Contributor Agreements</h3>
-            <fieldset id="agreements">
-              <section>
-                <span class="title">
-                  Require a valid contributor agreement to upload</span>
-                <span class="value">
-                  <gr-select id="contributorAgreementSelect" bind-value="{{_repoConfig.use_contributor_agreements.configured_value}}">
-                  <select disabled\$="[[_readOnly]]">
-                    <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.use_contributor_agreements)]]">
+          <h3 id="Options">Repository Options</h3>
+          <fieldset id="options">
+            <section>
+              <span class="title">State</span>
+              <span class="value">
+                <gr-select id="stateSelect" bind-value="{{_repoConfig.state}}">
+                  <select disabled$="[[_readOnly]]">
+                    <template is="dom-repeat" items="[[_states]]">
                       <option value="[[item.value]]">[[item.label]]</option>
                     </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-              <section>
-                <span class="title">Require Signed-off-by in commit message</span>
-                <span class="value">
-                  <gr-select id="useSignedOffBySelect" bind-value="{{_repoConfig.use_signed_off_by.configured_value}}">
-                    <select disabled\$="[[_readOnly]]">
-                      <template is="dom-repeat" items="[[_formatBooleanSelect(_repoConfig.use_signed_off_by)]]">
-                        <option value="[[item.value]]">[[item.label]]</option>
-                      </template>
-                    </select>
-                  </gr-select>
-                </span>
-              </section>
-            </fieldset>
-            <div class\$="pluginConfig [[_computeHideClass(_pluginData)]]" on-plugin-config-changed="_handlePluginConfigChanged">
-              <h3>Plugins</h3>
-              <template is="dom-repeat" items="[[_pluginData]]" as="data">
-                <gr-repo-plugin-config plugin-data="[[data]]"></gr-repo-plugin-config>
-              </template>
-            </div>
-            <gr-button on-click="_handleSaveRepoConfig" disabled\$="[[_computeButtonDisabled(_readOnly, _configChanged)]]">Save changes</gr-button>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Submit type</span>
+              <span class="value">
+                <gr-select
+                  id="submitTypeSelect"
+                  bind-value="{{_repoConfig.submit_type}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatSubmitTypeSelect(_repoConfig)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Allow content merges</span>
+              <span class="value">
+                <gr-select
+                  id="contentMergeSelect"
+                  bind-value="{{_repoConfig.use_content_merge.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.use_content_merge)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">
+                Create a new change for every commit not in the target branch
+              </span>
+              <span class="value">
+                <gr-select
+                  id="newChangeSelect"
+                  bind-value="{{_repoConfig.create_new_change_for_all_not_in_target.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.create_new_change_for_all_not_in_target)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Require Change-Id in commit message</span>
+              <span class="value">
+                <gr-select
+                  id="requireChangeIdSelect"
+                  bind-value="{{_repoConfig.require_change_id.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.require_change_id)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section
+              id="enableSignedPushSettings"
+              class$="repositorySettings [[_computeRepositoriesClass(_repoConfig.enable_signed_push)]]"
+            >
+              <span class="title">Enable signed push</span>
+              <span class="value">
+                <gr-select
+                  id="enableSignedPush"
+                  bind-value="{{_repoConfig.enable_signed_push.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.enable_signed_push)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section
+              id="requireSignedPushSettings"
+              class$="repositorySettings [[_computeRepositoriesClass(_repoConfig.require_signed_push)]]"
+            >
+              <span class="title">Require signed push</span>
+              <span class="value">
+                <gr-select
+                  id="requireSignedPush"
+                  bind-value="{{_repoConfig.require_signed_push.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.require_signed_push)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">
+                Reject implicit merges when changes are pushed for review</span
+              >
+              <span class="value">
+                <gr-select
+                  id="rejectImplicitMergesSelect"
+                  bind-value="{{_repoConfig.reject_implicit_merges.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.reject_implicit_merges)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">
+                Enable adding unregistered users as reviewers and CCs on
+                changes</span
+              >
+              <span class="value">
+                <gr-select
+                  id="unRegisteredCcSelect"
+                  bind-value="{{_repoConfig.enable_reviewer_by_email.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.enable_reviewer_by_email)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title"> Set all new changes private by default</span>
+              <span class="value">
+                <gr-select
+                  id="setAllnewChangesPrivateByDefaultSelect"
+                  bind-value="{{_repoConfig.private_by_default.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.private_by_default)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">
+                Set new changes to "work in progress" by default</span
+              >
+              <span class="value">
+                <gr-select
+                  id="setAllNewChangesWorkInProgressByDefaultSelect"
+                  bind-value="{{_repoConfig.work_in_progress_by_default.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.work_in_progress_by_default)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Maximum Git object size limit</span>
+              <span class="value">
+                <iron-input
+                  id="maxGitObjSizeIronInput"
+                  bind-value="{{_repoConfig.max_object_size_limit.configured_value}}"
+                  type="text"
+                  disabled$="[[_readOnly]]"
+                >
+                  <input
+                    id="maxGitObjSizeInput"
+                    bind-value="{{_repoConfig.max_object_size_limit.configured_value}}"
+                    is="iron-input"
+                    type="text"
+                    disabled$="[[_readOnly]]"
+                  />
+                </iron-input>
+                <template
+                  is="dom-if"
+                  if="[[_repoConfig.max_object_size_limit.value]]"
+                >
+                  effective: [[_repoConfig.max_object_size_limit.value]] bytes
+                </template>
+              </span>
+            </section>
+            <section>
+              <span class="title"
+                >Match authored date with committer date upon submit</span
+              >
+              <span class="value">
+                <gr-select
+                  id="matchAuthoredDateWithCommitterDateSelect"
+                  bind-value="{{_repoConfig.match_author_to_committer_date.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.match_author_to_committer_date)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Reject empty commit upon submit</span>
+              <span class="value">
+                <gr-select
+                  id="rejectEmptyCommitSelect"
+                  bind-value="{{_repoConfig.reject_empty_commit.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.reject_empty_commit)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
           </fieldset>
-          <gr-endpoint-decorator name="repo-config">
-            <gr-endpoint-param name="repoName" value="[[repo]]"></gr-endpoint-param>
-            <gr-endpoint-param name="readOnly" value="[[_readOnly]]"></gr-endpoint-param>
-          </gr-endpoint-decorator>
-        </div>
+          <h3 id="Options">Contributor Agreements</h3>
+          <fieldset id="agreements">
+            <section>
+              <span class="title">
+                Require a valid contributor agreement to upload</span
+              >
+              <span class="value">
+                <gr-select
+                  id="contributorAgreementSelect"
+                  bind-value="{{_repoConfig.use_contributor_agreements.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.use_contributor_agreements)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Require Signed-off-by in commit message</span>
+              <span class="value">
+                <gr-select
+                  id="useSignedOffBySelect"
+                  bind-value="{{_repoConfig.use_signed_off_by.configured_value}}"
+                >
+                  <select disabled$="[[_readOnly]]">
+                    <template
+                      is="dom-repeat"
+                      items="[[_formatBooleanSelect(_repoConfig.use_signed_off_by)]]"
+                    >
+                      <option value="[[item.value]]">[[item.label]]</option>
+                    </template>
+                  </select>
+                </gr-select>
+              </span>
+            </section>
+          </fieldset>
+          <div
+            class$="pluginConfig [[_computeHideClass(_pluginData)]]"
+            on-plugin-config-changed="_handlePluginConfigChanged"
+          >
+            <h3>Plugins</h3>
+            <template is="dom-repeat" items="[[_pluginData]]" as="data">
+              <gr-repo-plugin-config
+                plugin-data="[[data]]"
+              ></gr-repo-plugin-config>
+            </template>
+          </div>
+          <gr-button
+            on-click="_handleSaveRepoConfig"
+            disabled$="[[_computeButtonDisabled(_readOnly, _configChanged)]]"
+            >Save changes</gr-button
+          >
+        </fieldset>
+        <gr-endpoint-decorator name="repo-config">
+          <gr-endpoint-param
+            name="repoName"
+            value="[[repo]]"
+          ></gr-endpoint-param>
+          <gr-endpoint-param
+            name="readOnly"
+            value="[[_readOnly]]"
+          ></gr-endpoint-param>
+        </gr-endpoint-decorator>
       </div>
-    </main>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </div>
+  </main>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
index 1dccb7a..58b488a 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
index 234015a..99957ff 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
@@ -79,7 +77,7 @@
 ];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRuleEditor extends mixinBehaviors( [
   AccessBehavior,
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.js
index 4ea13b1..3e4f9d4 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_html.js
@@ -17,100 +17,144 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        border-bottom: 1px solid var(--border-color);
-        padding: var(--spacing-m);
-        display: block;
-      }
-      #removeBtn {
-        display: none;
-      }
-      .editing #removeBtn  {
-        display: flex;
-      }
-      #options {
-        align-items: baseline;
-        display: flex;
-      }
-      #options > * {
-        margin-right: var(--spacing-m);
-      }
-      #mainContainer {
-        align-items: baseline;
-        display: flex;
-        flex-wrap: nowrap;
-        justify-content: space-between;
-      }
-      #deletedContainer.deleted {
-        align-items: baseline;
-        display: flex;
-        justify-content: space-between;
-      }
-      #undoBtn,
-      #force,
-      #deletedContainer,
-      #mainContainer.deleted {
-        display: none;
-      }
-      #undoBtn.modified,
-      #force.force {
-        display: block;
-      }
-      .groupPath {
-        color: var(--deemphasized-text-color);
-      }
-    </style>
-    <style include="gr-form-styles">
-      iron-autogrow-textarea {
-        width: 14em;
-      }
-    </style>
-    <div id="mainContainer" class\$="gr-form-styles [[_computeSectionClass(editing, _deleted)]]">
-      <div id="options">
-        <gr-select id="action" bind-value="{{rule.value.action}}" on-change="_handleValueChange">
-          <select disabled\$="[[!editing]]">
-            <template is="dom-repeat" items="[[_computeOptions(permission)]]">
-              <option value="[[item]]">[[item]]</option>
+  <style include="shared-styles">
+    :host {
+      border-bottom: 1px solid var(--border-color);
+      padding: var(--spacing-m);
+      display: block;
+    }
+    #removeBtn {
+      display: none;
+    }
+    .editing #removeBtn {
+      display: flex;
+    }
+    #options {
+      align-items: baseline;
+      display: flex;
+    }
+    #options > * {
+      margin-right: var(--spacing-m);
+    }
+    #mainContainer {
+      align-items: baseline;
+      display: flex;
+      flex-wrap: nowrap;
+      justify-content: space-between;
+    }
+    #deletedContainer.deleted {
+      align-items: baseline;
+      display: flex;
+      justify-content: space-between;
+    }
+    #undoBtn,
+    #force,
+    #deletedContainer,
+    #mainContainer.deleted {
+      display: none;
+    }
+    #undoBtn.modified,
+    #force.force {
+      display: block;
+    }
+    .groupPath {
+      color: var(--deemphasized-text-color);
+    }
+  </style>
+  <style include="gr-form-styles">
+    iron-autogrow-textarea {
+      width: 14em;
+    }
+  </style>
+  <div
+    id="mainContainer"
+    class$="gr-form-styles [[_computeSectionClass(editing, _deleted)]]"
+  >
+    <div id="options">
+      <gr-select
+        id="action"
+        bind-value="{{rule.value.action}}"
+        on-change="_handleValueChange"
+      >
+        <select disabled$="[[!editing]]">
+          <template is="dom-repeat" items="[[_computeOptions(permission)]]">
+            <option value="[[item]]">[[item]]</option>
+          </template>
+        </select>
+      </gr-select>
+      <template is="dom-if" if="[[label]]">
+        <gr-select
+          id="labelMin"
+          bind-value="{{rule.value.min}}"
+          on-change="_handleValueChange"
+        >
+          <select disabled$="[[!editing]]">
+            <template is="dom-repeat" items="[[label.values]]">
+              <option value="[[item.value]]">[[item.value]]</option>
             </template>
           </select>
         </gr-select>
-        <template is="dom-if" if="[[label]]">
-          <gr-select id="labelMin" bind-value="{{rule.value.min}}" on-change="_handleValueChange">
-            <select disabled\$="[[!editing]]">
-              <template is="dom-repeat" items="[[label.values]]">
-                <option value="[[item.value]]">[[item.value]]</option>
-              </template>
-            </select>
-          </gr-select>
-          <gr-select id="labelMax" bind-value="{{rule.value.max}}" on-change="_handleValueChange">
-            <select disabled\$="[[!editing]]">
-              <template is="dom-repeat" items="[[label.values]]">
-                <option value="[[item.value]]">[[item.value]]</option>
-              </template>
-            </select>
-          </gr-select>
-        </template>
-        <template is="dom-if" if="[[hasRange]]">
-          <iron-autogrow-textarea id="minInput" class="min" autocomplete="on" placeholder="Min value" bind-value="{{rule.value.min}}" disabled\$="[[!editing]]"></iron-autogrow-textarea>
-          <iron-autogrow-textarea id="maxInput" class="max" autocomplete="on" placeholder="Max value" bind-value="{{rule.value.max}}" disabled\$="[[!editing]]"></iron-autogrow-textarea>
-        </template>
-        <a class="groupPath" href\$="[[_computeGroupPath(groupId)]]">
-          [[groupName]]
-        </a>
-        <gr-select id="force" class\$="[[_computeForceClass(permission, rule.value.action)]]" bind-value="{{rule.value.force}}" on-change="_handleValueChange">
-          <select disabled\$="[[!editing]]">
-            <template is="dom-repeat" items="[[_computeForceOptions(permission, rule.value.action)]]">
-              <option value="[[item.value]]">[[item.name]]</option>
+        <gr-select
+          id="labelMax"
+          bind-value="{{rule.value.max}}"
+          on-change="_handleValueChange"
+        >
+          <select disabled$="[[!editing]]">
+            <template is="dom-repeat" items="[[label.values]]">
+              <option value="[[item.value]]">[[item.value]]</option>
             </template>
           </select>
         </gr-select>
-      </div>
-      <gr-button link="" id="removeBtn" on-click="_handleRemoveRule">Remove</gr-button>
+      </template>
+      <template is="dom-if" if="[[hasRange]]">
+        <iron-autogrow-textarea
+          id="minInput"
+          class="min"
+          autocomplete="on"
+          placeholder="Min value"
+          bind-value="{{rule.value.min}}"
+          disabled$="[[!editing]]"
+        ></iron-autogrow-textarea>
+        <iron-autogrow-textarea
+          id="maxInput"
+          class="max"
+          autocomplete="on"
+          placeholder="Max value"
+          bind-value="{{rule.value.max}}"
+          disabled$="[[!editing]]"
+        ></iron-autogrow-textarea>
+      </template>
+      <a class="groupPath" href$="[[_computeGroupPath(groupId)]]">
+        [[groupName]]
+      </a>
+      <gr-select
+        id="force"
+        class$="[[_computeForceClass(permission, rule.value.action)]]"
+        bind-value="{{rule.value.force}}"
+        on-change="_handleValueChange"
+      >
+        <select disabled$="[[!editing]]">
+          <template
+            is="dom-repeat"
+            items="[[_computeForceOptions(permission, rule.value.action)]]"
+          >
+            <option value="[[item.value]]">[[item.name]]</option>
+          </template>
+        </select>
+      </gr-select>
     </div>
-    <div id="deletedContainer" class\$="gr-form-styles [[_computeSectionClass(editing, _deleted)]]">
-      [[groupName]] was deleted
-      <gr-button link="" id="undoRemoveBtn" on-click="_handleUndoRemove">Undo</gr-button>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    <gr-button link="" id="removeBtn" on-click="_handleRemoveRule"
+      >Remove</gr-button
+    >
+  </div>
+  <div
+    id="deletedContainer"
+    class$="gr-form-styles [[_computeSectionClass(editing, _deleted)]]"
+  >
+    [[groupName]] was deleted
+    <gr-button link="" id="undoRemoveBtn" on-click="_handleUndoRemove"
+      >Undo</gr-button
+    >
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
index 052eb96..f096eed 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-rule-editor</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 da13492..a3fe990 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
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-change-list-styles.js';
 import '../../shared/gr-account-link/gr-account-link.js';
 import '../../shared/gr-change-star/gr-change-star.js';
@@ -50,7 +49,7 @@
 
 /**
  * @appliesMixin RESTClientMixin
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeListItem extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
index aed0077..13b3c24 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.js
@@ -17,201 +17,262 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
+  <style include="shared-styles">
+    :host {
+      display: table-row;
+      color: var(--primary-text-color);
+    }
+    :host(:focus) {
+      outline: none;
+    }
+    :host(:hover) {
+      background-color: var(--hover-background-color);
+    }
+    :host([needs-review]) {
+      font-weight: var(--font-weight-bold);
+      color: var(--primary-text-color);
+    }
+    :host([highlight]) {
+      background-color: var(--assignee-highlight-color);
+    }
+    .container {
+      position: relative;
+    }
+    .content {
+      overflow: hidden;
+      position: absolute;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      width: 100%;
+    }
+    .content a {
+      display: block;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      width: 100%;
+    }
+    .comments,
+    .reviewers {
+      white-space: nowrap;
+    }
+    .spacer {
+      height: 0;
+      overflow: hidden;
+    }
+    .status {
+      align-items: center;
+      display: inline-flex;
+    }
+    .status .comma {
+      padding-right: var(--spacing-xs);
+    }
+    /* Used to hide the leading separator comma for statuses. */
+    .status .comma:first-of-type {
+      display: none;
+    }
+    .size gr-tooltip-content {
+      margin: -0.4rem -0.6rem;
+      max-width: 2.5rem;
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    a {
+      color: inherit;
+      cursor: pointer;
+      text-decoration: none;
+    }
+    a:hover {
+      text-decoration: underline;
+    }
+    .u-monospace {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+    }
+    .u-green {
+      color: var(--vote-text-color-recommended);
+    }
+    .u-red {
+      color: var(--vote-text-color-disliked);
+    }
+    .u-gray-background {
+      background-color: var(--table-header-background-color);
+    }
+    .comma,
+    .placeholder {
+      color: var(--deemphasized-text-color);
+    }
+    .cell.label {
+      font-weight: var(--font-weight-normal);
+    }
+    .lastChildHidden:last-of-type {
+      display: none;
+    }
+    @media only screen and (max-width: 50em) {
       :host {
-        display: table-row;
-        color: var(--primary-text-color);
+        display: flex;
       }
-      :host(:focus) {
-        outline: none;
-      }
-      :host(:hover) {
-        background-color: var(--hover-background-color);
-      }
-      :host([needs-review]) {
-        font-weight: var(--font-weight-bold);
-        color: var(--primary-text-color);
-      }
-      :host([highlight]) {
-        background-color: var(--assignee-highlight-color);
-      }
-      .container {
-        position: relative;
-      }
-      .content {
-        overflow: hidden;
-        position: absolute;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        width: 100%;
-      }
-      .content a {
-        display: block;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        width: 100%;
-      }
-      .comments,
-      .reviewers {
-        white-space: nowrap;
-      }
-      .spacer {
-        height: 0;
-        overflow: hidden;
-      }
-      .status {
-        align-items: center;
-        display: inline-flex;
-      }
-      .status .comma {
-        padding-right: var(--spacing-xs);
-      }
-      /* Used to hide the leading separator comma for statuses. */
-      .status .comma:first-of-type {
-        display: none;
-      }
-      .size gr-tooltip-content {
-        margin: -.4rem -.6rem;
-        max-width: 2.5rem;
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      a {
-        color: inherit;
-        cursor: pointer;
-        text-decoration: none;
-      }
-      a:hover {
-        text-decoration: underline;
-      }
-      .u-monospace {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-      }
-      .u-green {
-        color: var(--vote-text-color-recommended);
-      }
-      .u-red {
-        color: var(--vote-text-color-disliked);
-      }
-      .u-gray-background {
-        background-color: var(--table-header-background-color);
-      }
-      .comma,
-      .placeholder {
-        color: var(--deemphasized-text-color);
-      }
-      .cell.label {
-        font-weight: var(--font-weight-normal);
-      }
-      .lastChildHidden:last-of-type {
-        display: none;
-      }
-      @media only screen and (max-width: 50em) {
-        :host {
-          display: flex;
-        }
-      }
-    </style>
-    <style include="gr-change-list-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <td class="cell leftPadding"></td>
-    <td class="cell star" hidden\$="[[!showStar]]" hidden="">
-      <gr-change-star change="{{change}}"></gr-change-star>
-    </td>
-    <td class="cell number" hidden\$="[[!showNumber]]" hidden="">
-      <a href\$="[[changeURL]]">[[change._number]]</a>
-    </td>
-    <td class="cell subject" hidden\$="[[isColumnHidden('Subject', visibleChangeTableColumns)]]">
-      <div class="container">
-        <div class="content">
-          <a title\$="[[change.subject]]" href\$="[[changeURL]]">
-            [[change.subject]]
-          </a>
-        </div>
-        <div class="spacer">
-           [[change.subject]]
-        </div>
-        <span>&nbsp;</span>
+    }
+  </style>
+  <style include="gr-change-list-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <td class="cell leftPadding"></td>
+  <td class="cell star" hidden$="[[!showStar]]" hidden="">
+    <gr-change-star change="{{change}}"></gr-change-star>
+  </td>
+  <td class="cell number" hidden$="[[!showNumber]]" hidden="">
+    <a href$="[[changeURL]]">[[change._number]]</a>
+  </td>
+  <td
+    class="cell subject"
+    hidden$="[[isColumnHidden('Subject', visibleChangeTableColumns)]]"
+  >
+    <div class="container">
+      <div class="content">
+        <a title$="[[change.subject]]" href$="[[changeURL]]">
+          [[change.subject]]
+        </a>
       </div>
-    </td>
-    <td class="cell status" hidden\$="[[isColumnHidden('Status', visibleChangeTableColumns)]]">
-      <template is="dom-repeat" items="[[statuses]]" as="status">
-        <div class="comma">,</div>
-        <gr-change-status flat="" status="[[status]]"></gr-change-status>
-      </template>
-      <template is="dom-if" if="[[!statuses.length]]">
-        <span class="placeholder">--</span>
-      </template>
-    </td>
-    <td class="cell owner" hidden\$="[[isColumnHidden('Owner', visibleChangeTableColumns)]]">
-      <gr-account-link account="[[change.owner]]"></gr-account-link>
-    </td>
-    <td class="cell assignee" hidden\$="[[isColumnHidden('Assignee', visibleChangeTableColumns)]]">
-      <template is="dom-if" if="[[change.assignee]]">
-        <gr-account-link id="assigneeAccountLink" account="[[change.assignee]]"></gr-account-link>
-      </template>
-      <template is="dom-if" if="[[!change.assignee]]">
-        <span class="placeholder">--</span>
-      </template>
-    </td>
-    <td class="cell reviewers" hidden\$="[[isColumnHidden('Reviewers', visibleChangeTableColumns)]]">
-      <div>
-        <template is="dom-repeat" items="[[change.reviewers.REVIEWER]]" as="reviewer">
-          <gr-account-link hide-avatar="" hide-status="" account="[[reviewer]]"></gr-account-link><!--
+      <div class="spacer">
+        [[change.subject]]
+      </div>
+      <span>&nbsp;</span>
+    </div>
+  </td>
+  <td
+    class="cell status"
+    hidden$="[[isColumnHidden('Status', visibleChangeTableColumns)]]"
+  >
+    <template is="dom-repeat" items="[[statuses]]" as="status">
+      <div class="comma">,</div>
+      <gr-change-status flat="" status="[[status]]"></gr-change-status>
+    </template>
+    <template is="dom-if" if="[[!statuses.length]]">
+      <span class="placeholder">--</span>
+    </template>
+  </td>
+  <td
+    class="cell owner"
+    hidden$="[[isColumnHidden('Owner', visibleChangeTableColumns)]]"
+  >
+    <gr-account-link account="[[change.owner]]"></gr-account-link>
+  </td>
+  <td
+    class="cell assignee"
+    hidden$="[[isColumnHidden('Assignee', visibleChangeTableColumns)]]"
+  >
+    <template is="dom-if" if="[[change.assignee]]">
+      <gr-account-link
+        id="assigneeAccountLink"
+        account="[[change.assignee]]"
+      ></gr-account-link>
+    </template>
+    <template is="dom-if" if="[[!change.assignee]]">
+      <span class="placeholder">--</span>
+    </template>
+  </td>
+  <td
+    class="cell reviewers"
+    hidden$="[[isColumnHidden('Reviewers', visibleChangeTableColumns)]]"
+  >
+    <div>
+      <template
+        is="dom-repeat"
+        items="[[change.reviewers.REVIEWER]]"
+        as="reviewer"
+      >
+        <gr-account-link
+          hide-avatar=""
+          hide-status=""
+          account="[[reviewer]]"
+        ></gr-account-link
+        ><!--
        --><span class="lastChildHidden">, </span>
-        </template>
-      </div>
-    </td>
-    <td class="cell comments" hidden\$="[[isColumnHidden('Comments', visibleChangeTableColumns)]]">
-      <iron-icon hidden\$="[[!change.unresolved_comment_count]]" icon="gr-icons:comment"></iron-icon>
-      <span>[[_computeComments(change.unresolved_comment_count)]]</span>
-    </td>
-    <td class="cell repo" hidden\$="[[isColumnHidden('Repo', visibleChangeTableColumns)]]">
-      <a class="fullRepo" href\$="[[_computeRepoUrl(change)]]">
-        [[_computeRepoDisplay(change)]]
-      </a>
-      <a class="truncatedRepo" href\$="[[_computeRepoUrl(change)]]" title\$="[[_computeRepoDisplay(change)]]">
-        [[_computeRepoDisplay(change, 'true')]]
-      </a>
-    </td>
-    <td class="cell branch" hidden\$="[[isColumnHidden('Branch', visibleChangeTableColumns)]]">
-      <a href\$="[[_computeRepoBranchURL(change)]]">
-        [[change.branch]]
-      </a>
-      <template is="dom-if" if="[[change.topic]]">
-        (<a href\$="[[_computeTopicURL(change)]]"><!--
-       --><gr-limited-text limit="50" text="[[change.topic]]">
-          </gr-limited-text><!--
-     --></a>)
       </template>
-    </td>
-    <td class="cell updated" hidden\$="[[isColumnHidden('Updated', visibleChangeTableColumns)]]">
-      <gr-date-formatter has-tooltip="" date-str="[[change.updated]]"></gr-date-formatter>
-    </td>
-    <td class="cell size" hidden\$="[[isColumnHidden('Size', visibleChangeTableColumns)]]">
-      <gr-tooltip-content has-tooltip="" title="[[_computeSizeTooltip(change)]]">
-        <template is="dom-if" if="[[_changeSize]]">
-            <span>[[_changeSize]]</span>
-        </template>
-        <template is="dom-if" if="[[!_changeSize]]">
-            <span class="placeholder">--</span>
-        </template>
-      </gr-tooltip-content>
-    </td>
-    <template is="dom-repeat" items="[[labelNames]]" as="labelName">
-      <td title\$="[[_computeLabelTitle(change, labelName)]]" class\$="[[_computeLabelClass(change, labelName)]]">
-        [[_computeLabelValue(change, labelName)]]
-      </td>
+    </div>
+  </td>
+  <td
+    class="cell comments"
+    hidden$="[[isColumnHidden('Comments', visibleChangeTableColumns)]]"
+  >
+    <iron-icon
+      hidden$="[[!change.unresolved_comment_count]]"
+      icon="gr-icons:comment"
+    ></iron-icon>
+    <span>[[_computeComments(change.unresolved_comment_count)]]</span>
+  </td>
+  <td
+    class="cell repo"
+    hidden$="[[isColumnHidden('Repo', visibleChangeTableColumns)]]"
+  >
+    <a class="fullRepo" href$="[[_computeRepoUrl(change)]]">
+      [[_computeRepoDisplay(change)]]
+    </a>
+    <a
+      class="truncatedRepo"
+      href$="[[_computeRepoUrl(change)]]"
+      title$="[[_computeRepoDisplay(change)]]"
+    >
+      [[_computeRepoDisplay(change, 'true')]]
+    </a>
+  </td>
+  <td
+    class="cell branch"
+    hidden$="[[isColumnHidden('Branch', visibleChangeTableColumns)]]"
+  >
+    <a href$="[[_computeRepoBranchURL(change)]]">
+      [[change.branch]]
+    </a>
+    <template is="dom-if" if="[[change.topic]]">
+      (<a href$="[[_computeTopicURL(change)]]"
+        ><!--
+       --><gr-limited-text limit="50" text="[[change.topic]]"> </gr-limited-text
+        ><!--
+     --></a
+      >)
     </template>
-    <template is="dom-repeat" items="[[_dynamicCellEndpoints]]" as="pluginEndpointName">
-      <td class="cell endpoint">
-        <gr-endpoint-decorator name\$="[[pluginEndpointName]]">
-          <gr-endpoint-param name="change" value="[[change]]">
-          </gr-endpoint-param>
-        </gr-endpoint-decorator>
-      </td>
-    </template>
+  </td>
+  <td
+    class="cell updated"
+    hidden$="[[isColumnHidden('Updated', visibleChangeTableColumns)]]"
+  >
+    <gr-date-formatter
+      has-tooltip=""
+      date-str="[[change.updated]]"
+    ></gr-date-formatter>
+  </td>
+  <td
+    class="cell size"
+    hidden$="[[isColumnHidden('Size', visibleChangeTableColumns)]]"
+  >
+    <gr-tooltip-content has-tooltip="" title="[[_computeSizeTooltip(change)]]">
+      <template is="dom-if" if="[[_changeSize]]">
+        <span>[[_changeSize]]</span>
+      </template>
+      <template is="dom-if" if="[[!_changeSize]]">
+        <span class="placeholder">--</span>
+      </template>
+    </gr-tooltip-content>
+  </td>
+  <template is="dom-repeat" items="[[labelNames]]" as="labelName">
+    <td
+      title$="[[_computeLabelTitle(change, labelName)]]"
+      class$="[[_computeLabelClass(change, labelName)]]"
+    >
+      [[_computeLabelValue(change, labelName)]]
+    </td>
+  </template>
+  <template
+    is="dom-repeat"
+    items="[[_dynamicCellEndpoints]]"
+    as="pluginEndpointName"
+  >
+    <td class="cell endpoint">
+      <gr-endpoint-decorator name$="[[pluginEndpointName]]">
+        <gr-endpoint-param name="change" value="[[change]]">
+        </gr-endpoint-param>
+      </gr-endpoint-decorator>
+    </td>
+  </template>
 `;
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
index f7361d2..6b45618 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-list-item</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 c416b11..361ad83 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
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-icons/gr-icons.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import '../gr-change-list/gr-change-list.js';
@@ -45,7 +44,7 @@
 const LIMIT_OPERATOR_PATTERN = /\blimit:(\d+)/i;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeListView extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.js
index bed3985..4add1da 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_html.js
@@ -17,61 +17,85 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    .loading {
+      color: var(--deemphasized-text-color);
+      padding: var(--spacing-l);
+    }
+    gr-change-list {
+      width: 100%;
+    }
+    gr-user-header,
+    gr-repo-header {
+      border-bottom: 1px solid var(--border-color);
+    }
+    nav {
+      align-items: center;
+      display: flex;
+      height: 3rem;
+      justify-content: flex-end;
+      margin-right: 20px;
+    }
+    nav,
+    iron-icon {
+      color: var(--deemphasized-text-color);
+    }
+    iron-icon {
+      height: 1.85rem;
+      margin-left: 16px;
+      width: 1.85rem;
+    }
+    .hide {
+      display: none;
+    }
+    @media only screen and (max-width: 50em) {
+      .loading,
+      .error {
+        padding: 0 var(--spacing-l);
       }
-      .loading {
-        color: var(--deemphasized-text-color);
-        padding: var(--spacing-l);
-      }
-      gr-change-list {
-        width: 100%;
-      }
-      gr-user-header,
-      gr-repo-header {
-        border-bottom: 1px solid var(--border-color);
-      }
-      nav {
-        align-items: center;
-        display: flex;
-        height: 3rem;
-        justify-content: flex-end;
-        margin-right: 20px;
-      }
-      nav,
-      iron-icon {
-        color: var(--deemphasized-text-color);
-      }
-      iron-icon {
-        height: 1.85rem;
-        margin-left: 16px;
-        width: 1.85rem;
-      }
-      .hide {
-        display: none;
-      }
-      @media only screen and (max-width: 50em) {
-        .loading,
-        .error {
-          padding: 0 var(--spacing-l);
-        }
-      }
-    </style>
-    <div class="loading" hidden\$="[[!_loading]]" hidden="">Loading...</div>
-    <div hidden\$="[[_loading]]" hidden="">
-      <gr-repo-header repo="[[_repo]]" class\$="[[_computeHeaderClass(_repo)]]"></gr-repo-header>
-      <gr-user-header user-id="[[_userId]]" show-dashboard-link="" logged-in="[[_loggedIn]]" class\$="[[_computeHeaderClass(_userId)]]"></gr-user-header>
-      <gr-change-list account="[[account]]" changes="{{_changes}}" preferences="[[preferences]]" selected-index="{{viewState.selectedChangeIndex}}" show-star="[[_loggedIn]]" on-toggle-star="_handleToggleStar" on-toggle-reviewed="_handleToggleReviewed"></gr-change-list>
-      <nav class\$="[[_computeNavClass(_loading)]]">
-          Page [[_computePage(_offset, _changesPerPage)]]
-          <a id="prevArrow" href\$="[[_computeNavLink(_query, _offset, -1, _changesPerPage)]]" class\$="[[_computePrevArrowClass(_offset)]]">
-            <iron-icon icon="gr-icons:chevron-left"></iron-icon>
-          </a>
-          <a id="nextArrow" href\$="[[_computeNavLink(_query, _offset, 1, _changesPerPage)]]" class\$="[[_computeNextArrowClass(_changes)]]">
-            <iron-icon icon="gr-icons:chevron-right"></iron-icon>
-          </a>
-      </nav>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    }
+  </style>
+  <div class="loading" hidden$="[[!_loading]]" hidden="">Loading...</div>
+  <div hidden$="[[_loading]]" hidden="">
+    <gr-repo-header
+      repo="[[_repo]]"
+      class$="[[_computeHeaderClass(_repo)]]"
+    ></gr-repo-header>
+    <gr-user-header
+      user-id="[[_userId]]"
+      show-dashboard-link=""
+      logged-in="[[_loggedIn]]"
+      class$="[[_computeHeaderClass(_userId)]]"
+    ></gr-user-header>
+    <gr-change-list
+      account="[[account]]"
+      changes="{{_changes}}"
+      preferences="[[preferences]]"
+      selected-index="{{viewState.selectedChangeIndex}}"
+      show-star="[[_loggedIn]]"
+      on-toggle-star="_handleToggleStar"
+      on-toggle-reviewed="_handleToggleReviewed"
+    ></gr-change-list>
+    <nav class$="[[_computeNavClass(_loading)]]">
+      Page [[_computePage(_offset, _changesPerPage)]]
+      <a
+        id="prevArrow"
+        href$="[[_computeNavLink(_query, _offset, -1, _changesPerPage)]]"
+        class$="[[_computePrevArrowClass(_offset)]]"
+      >
+        <iron-icon icon="gr-icons:chevron-left"></iron-icon>
+      </a>
+      <a
+        id="nextArrow"
+        href$="[[_computeNavLink(_query, _offset, 1, _changesPerPage)]]"
+        class$="[[_computeNextArrowClass(_changes)]]"
+      >
+        <iron-icon icon="gr-icons:chevron-right"></iron-icon>
+      </a>
+    </nav>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
index 33e70a0..58ec4e1 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-list-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 0a19ae1..bd78ae9 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
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-change-list-styles.js';
 import '../../shared/gr-cursor-manager/gr-cursor-manager.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -45,7 +44,7 @@
 const MAX_SHORTCUT_CHARS = 5;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeList extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
index 3309fb2..17150df 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_html.js
@@ -17,91 +17,129 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-change-list-styles">
-      #changeList {
-        border-collapse: collapse;
-        width: 100%;
-      }
-      .section-count-label {
-        color: var(--deemphasized-text-color);
-        font-family: var(--font-family);
-        font-size: var(--font-size-small);
-        font-weight: var(--font-weight-normal);
-        line-height: var(--line-height-small);
-      }
-      a.section-title:hover {
-        text-decoration: none;
-      }
-      a.section-title:hover .section-count-label {
-        text-decoration: none;
-      }
-      a.section-title:hover .section-name {
-        text-decoration: underline;
-      }
-    </style>
-    <table id="changeList">
-      <template is="dom-repeat" items="[[sections]]" as="changeSection" index-as="sectionIndex">
-        <template is="dom-if" if="[[changeSection.name]]">
-          <tbody>
-            <tr class="groupHeader">
-              <td class="leftPadding"></td>
-              <td class="star" hidden\$="[[!showStar]]" hidden=""></td>
-              <td class="cell" colspan\$="[[_computeColspan(changeTableColumns, labelNames)]]">
-                <a href\$="[[_sectionHref(changeSection.query)]]" class="section-title">
-                  <span class="section-name">[[changeSection.name]]</span>
-                  <span class="section-count-label">[[changeSection.countLabel]]</span>
-                </a>
-              </td>
-            </tr>
-          </tbody>
-        </template>
-        <tbody class="groupContent">
-          <template is="dom-if" if="[[_isEmpty(changeSection)]]">
-            <tr class="noChanges">
-              <td class="leftPadding"></td>
-              <td class="star" hidden\$="[[!showStar]]" hidden=""></td>
-              <td class="cell" colspan\$="[[_computeColspan(changeTableColumns, labelNames)]]">
-                <template is="dom-if" if="[[_isOutgoing(changeSection)]]">
-                  <slot name="empty-outgoing"></slot>
-                </template>
-                <template is="dom-if" if="[[!_isOutgoing(changeSection)]]">
-                  No changes
-                </template>
-              </td>
-            </tr>
-          </template>
-          <template is="dom-if" if="[[!_isEmpty(changeSection)]]">
-            <tr class="groupTitle">
-              <td class="leftPadding"></td>
-              <td class="star" hidden\$="[[!showStar]]" hidden=""></td>
-              <td class="number" hidden\$="[[!showNumber]]" hidden="">#</td>
-              <template is="dom-repeat" items="[[changeTableColumns]]" as="item">
-                <td class\$="[[_lowerCase(item)]]" hidden\$="[[isColumnHidden(item, visibleChangeTableColumns)]]">
-                  [[item]]
-                </td>
-              </template>
-              <template is="dom-repeat" items="[[labelNames]]" as="labelName">
-                <td class="label" title\$="[[labelName]]">
-                  [[_computeLabelShortcut(labelName)]]
-                </td>
-              </template>
-              <template is="dom-repeat" items="[[_dynamicHeaderEndpoints]]" as="pluginHeader">
-                <td class="endpoint">
-                  <gr-endpoint-decorator name\$="[[pluginHeader]]">
-                  </gr-endpoint-decorator>
-                </td>
-              </template>
-            </tr>
-          </template>
-          <template is="dom-repeat" items="[[changeSection.results]]" as="change">
-            <gr-change-list-item selected\$="[[_computeItemSelected(sectionIndex, index, selectedIndex)]]" highlight\$="[[_computeItemHighlight(account, change)]]" needs-review\$="[[_computeItemNeedsReview(account, change, showReviewedState)]]" change="[[change]]" visible-change-table-columns="[[visibleChangeTableColumns]]" show-number="[[showNumber]]" show-star="[[showStar]]" tabindex="0" label-names="[[labelNames]]"></gr-change-list-item>
-          </template>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-change-list-styles">
+    #changeList {
+      border-collapse: collapse;
+      width: 100%;
+    }
+    .section-count-label {
+      color: var(--deemphasized-text-color);
+      font-family: var(--font-family);
+      font-size: var(--font-size-small);
+      font-weight: var(--font-weight-normal);
+      line-height: var(--line-height-small);
+    }
+    a.section-title:hover {
+      text-decoration: none;
+    }
+    a.section-title:hover .section-count-label {
+      text-decoration: none;
+    }
+    a.section-title:hover .section-name {
+      text-decoration: underline;
+    }
+  </style>
+  <table id="changeList">
+    <template
+      is="dom-repeat"
+      items="[[sections]]"
+      as="changeSection"
+      index-as="sectionIndex"
+    >
+      <template is="dom-if" if="[[changeSection.name]]">
+        <tbody>
+          <tr class="groupHeader">
+            <td class="leftPadding"></td>
+            <td class="star" hidden$="[[!showStar]]" hidden=""></td>
+            <td
+              class="cell"
+              colspan$="[[_computeColspan(changeTableColumns, labelNames)]]"
+            >
+              <a
+                href$="[[_sectionHref(changeSection.query)]]"
+                class="section-title"
+              >
+                <span class="section-name">[[changeSection.name]]</span>
+                <span class="section-count-label"
+                  >[[changeSection.countLabel]]</span
+                >
+              </a>
+            </td>
+          </tr>
         </tbody>
       </template>
-    </table>
-    <gr-cursor-manager id="cursor" index="{{selectedIndex}}" scroll-behavior="keep-visible" focus-on-move=""></gr-cursor-manager>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      <tbody class="groupContent">
+        <template is="dom-if" if="[[_isEmpty(changeSection)]]">
+          <tr class="noChanges">
+            <td class="leftPadding"></td>
+            <td class="star" hidden$="[[!showStar]]" hidden=""></td>
+            <td
+              class="cell"
+              colspan$="[[_computeColspan(changeTableColumns, labelNames)]]"
+            >
+              <template is="dom-if" if="[[_isOutgoing(changeSection)]]">
+                <slot name="empty-outgoing"></slot>
+              </template>
+              <template is="dom-if" if="[[!_isOutgoing(changeSection)]]">
+                No changes
+              </template>
+            </td>
+          </tr>
+        </template>
+        <template is="dom-if" if="[[!_isEmpty(changeSection)]]">
+          <tr class="groupTitle">
+            <td class="leftPadding"></td>
+            <td class="star" hidden$="[[!showStar]]" hidden=""></td>
+            <td class="number" hidden$="[[!showNumber]]" hidden="">#</td>
+            <template is="dom-repeat" items="[[changeTableColumns]]" as="item">
+              <td
+                class$="[[_lowerCase(item)]]"
+                hidden$="[[isColumnHidden(item, visibleChangeTableColumns)]]"
+              >
+                [[item]]
+              </td>
+            </template>
+            <template is="dom-repeat" items="[[labelNames]]" as="labelName">
+              <td class="label" title$="[[labelName]]">
+                [[_computeLabelShortcut(labelName)]]
+              </td>
+            </template>
+            <template
+              is="dom-repeat"
+              items="[[_dynamicHeaderEndpoints]]"
+              as="pluginHeader"
+            >
+              <td class="endpoint">
+                <gr-endpoint-decorator name$="[[pluginHeader]]">
+                </gr-endpoint-decorator>
+              </td>
+            </template>
+          </tr>
+        </template>
+        <template is="dom-repeat" items="[[changeSection.results]]" as="change">
+          <gr-change-list-item
+            selected$="[[_computeItemSelected(sectionIndex, index, selectedIndex)]]"
+            highlight$="[[_computeItemHighlight(account, change)]]"
+            needs-review$="[[_computeItemNeedsReview(account, change, showReviewedState)]]"
+            change="[[change]]"
+            visible-change-table-columns="[[visibleChangeTableColumns]]"
+            show-number="[[showNumber]]"
+            show-star="[[showStar]]"
+            tabindex="0"
+            label-names="[[labelNames]]"
+          ></gr-change-list-item>
+        </template>
+      </tbody>
+    </template>
+  </table>
+  <gr-cursor-manager
+    id="cursor"
+    index="{{selectedIndex}}"
+    scroll-behavior="keep-visible"
+    focus-on-move=""
+  ></gr-cursor-manager>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
index c535600..62763d9 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 3758a78..d9fd378 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
@@ -24,7 +23,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-create-change-help_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrCreateChangeHelp extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.js b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.js
index f40bda7..4a357af 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_html.js
@@ -17,68 +17,68 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    #graphic,
+    #help {
+      display: inline-block;
+      margin: var(--spacing-m);
+    }
+    #graphic #circle {
+      align-items: center;
+      background-color: var(--chip-background-color);
+      border-radius: 50%;
+      display: flex;
+      height: 10em;
+      justify-content: center;
+      width: 10em;
+    }
+    #graphic iron-icon {
+      color: #9e9e9e;
+      height: 5em;
+      width: 5em;
+    }
+    #graphic p {
+      color: var(--deemphasized-text-color);
+      text-align: center;
+    }
+    #help {
+      padding-top: var(--spacing-xl);
+      vertical-align: top;
+    }
+    #help h1 {
+      font-family: var(--header-font-family);
+      font-size: var(--font-size-h3);
+      font-weight: var(--font-weight-h3);
+      line-height: var(--line-height-h3);
+    }
+    #help p {
+      margin-bottom: var(--spacing-m);
+      max-width: 35em;
+    }
+    @media only screen and (max-width: 50em) {
+      #graphic {
+        display: none;
       }
-      #graphic,
-      #help {
-        display: inline-block;
-        margin: var(--spacing-m);
-      }
-      #graphic #circle {
-        align-items: center;
-        background-color: var(--chip-background-color);
-        border-radius: 50%;
-        display: flex;
-        height: 10em;
-        justify-content: center;
-        width: 10em;
-      }
-      #graphic iron-icon {
-        color: #9e9e9e;
-        height: 5em;
-        width: 5em;
-      }
-      #graphic p {
-        color: var(--deemphasized-text-color);
-        text-align: center;
-      }
-      #help {
-        padding-top: var(--spacing-xl);
-        vertical-align: top;
-      }
-      #help h1 {
-        font-family: var(--header-font-family);
-        font-size: var(--font-size-h3);
-        font-weight: var(--font-weight-h3);
-        line-height: var(--line-height-h3);
-      }
-      #help p {
-        margin-bottom: var(--spacing-m);
-        max-width: 35em;
-      }
-      @media only screen and (max-width: 50em) {
-        #graphic {
-          display: none;
-        }
-      }
-    </style>
-    <div id="graphic">
-      <div id="circle">
-        <iron-icon id="icon" icon="gr-icons:zeroState"></iron-icon>
-      </div>
-      <p>
-        No outgoing changes yet
-      </p>
+    }
+  </style>
+  <div id="graphic">
+    <div id="circle">
+      <iron-icon id="icon" icon="gr-icons:zeroState"></iron-icon>
     </div>
-    <div id="help">
-      <h1>Push your first change for code review</h1>
-      <p>
-        Pushing a change for review is easy, but a little different from
-        other git code review tools. Click on the \`Create Change' button
-        and follow the step by step instructions.
-      </p>
-      <gr-button on-click="_handleCreateTap">Create Change</gr-button>
-    </div>
+    <p>
+      No outgoing changes yet
+    </p>
+  </div>
+  <div id="help">
+    <h1>Push your first change for code review</h1>
+    <p>
+      Pushing a change for review is easy, but a little different from other git
+      code review tools. Click on the \`Create Change' button and follow the
+      step by step instructions.
+    </p>
+    <gr-button on-click="_handleCreateTap">Create Change</gr-button>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html
index d5b42a9..9b8ed29 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-create-change-help</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 7e5e749..cc53e49 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../shared/gr-overlay/gr-overlay.js';
@@ -30,7 +29,7 @@
   PUSH_PREFIX: 'git push origin HEAD:refs/for/',
 };
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrCreateCommandsDialog extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.js b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.js
index aa13dca..d2a1af9 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_html.js
@@ -17,59 +17,69 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      ol {
-        list-style: decimal;
-        margin-left: var(--spacing-l);
-      }
-      p {
-        margin-bottom: var(--spacing-m);
-      }
-      #commandsDialog {
-        max-width: 40em;
-      }
-    </style>
-    <gr-overlay id="commandsOverlay" with-backdrop="">
-      <gr-dialog id="commandsDialog" confirm-label="Done" cancel-label="" confirm-on-enter="" on-confirm="_handleClose">
-        <div class="header" slot="header">
-          Create change commands
-        </div>
-        <div class="main" slot="main">
-          <ol>
-            <li>
-              <p>
-                Make the changes to the files on your machine
-              </p>
-            </li>
-            <li>
-              <p>
-                If you are making a new commit use
-              </p>
-              <gr-shell-command command="[[_createNewCommitCommand]]"></gr-shell-command>
-              <p>
-                Or to amend an existing commit use
-              </p>
-              <gr-shell-command command="[[_amendExistingCommitCommand]]"></gr-shell-command>
-              <p>
-                Please make sure you add a commit message as it becomes the
-                description for your change.
-              </p>
-            </li>
-            <li>
-              <p>
-                Push the change for code review
-              </p>
-              <gr-shell-command command="[[_pushCommand]]"></gr-shell-command>
-            </li>
-            <li>
-              <p>
-                Close this dialog and you should be able to see your recently
-                created change in the 'Outgoing changes' section on the
-                'Your changes' page.
-              </p>
-            </li>
-          </ol>
-        </div>
-      </gr-dialog>
-    </gr-overlay>
+  <style include="shared-styles">
+    ol {
+      list-style: decimal;
+      margin-left: var(--spacing-l);
+    }
+    p {
+      margin-bottom: var(--spacing-m);
+    }
+    #commandsDialog {
+      max-width: 40em;
+    }
+  </style>
+  <gr-overlay id="commandsOverlay" with-backdrop="">
+    <gr-dialog
+      id="commandsDialog"
+      confirm-label="Done"
+      cancel-label=""
+      confirm-on-enter=""
+      on-confirm="_handleClose"
+    >
+      <div class="header" slot="header">
+        Create change commands
+      </div>
+      <div class="main" slot="main">
+        <ol>
+          <li>
+            <p>
+              Make the changes to the files on your machine
+            </p>
+          </li>
+          <li>
+            <p>
+              If you are making a new commit use
+            </p>
+            <gr-shell-command
+              command="[[_createNewCommitCommand]]"
+            ></gr-shell-command>
+            <p>
+              Or to amend an existing commit use
+            </p>
+            <gr-shell-command
+              command="[[_amendExistingCommitCommand]]"
+            ></gr-shell-command>
+            <p>
+              Please make sure you add a commit message as it becomes the
+              description for your change.
+            </p>
+          </li>
+          <li>
+            <p>
+              Push the change for code review
+            </p>
+            <gr-shell-command command="[[_pushCommand]]"></gr-shell-command>
+          </li>
+          <li>
+            <p>
+              Close this dialog and you should be able to see your recently
+              created change in the 'Outgoing changes' section on the 'Your
+              changes' page.
+            </p>
+          </li>
+        </ol>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
 `;
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html
index 620e6fe..e6cd587 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-create-commands-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 f8757ba..9062a3f 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../shared/gr-overlay/gr-overlay.js';
@@ -29,7 +28,7 @@
  * name and the branch name.
  *
  * @event confirm
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCreateDestinationDialog extends GestureEventListeners(
     LegacyElementMixin(
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.js b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.js
index 73f6ec0..c7cd647 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog_html.js
@@ -17,19 +17,26 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-    </style>
-    <gr-overlay id="createOverlay" with-backdrop="">
-      <gr-dialog confirm-label="View commands" on-confirm="_pickerConfirm" on-cancel="_handleClose" disabled="[[!_repoAndBranchSelected]]">
-        <div class="header" slot="header">
-          Create change
-        </div>
-        <div class="main" slot="main">
-          <gr-repo-branch-picker repo="{{_repo}}" branch="{{_branch}}"></gr-repo-branch-picker>
-          <p>
-            If you haven't done so, you will need to clone the repository.
-          </p>
-        </div>
-      </gr-dialog>
-    </gr-overlay>
+  <style include="shared-styles"></style>
+  <gr-overlay id="createOverlay" with-backdrop="">
+    <gr-dialog
+      confirm-label="View commands"
+      on-confirm="_pickerConfirm"
+      on-cancel="_handleClose"
+      disabled="[[!_repoAndBranchSelected]]"
+    >
+      <div class="header" slot="header">
+        Create change
+      </div>
+      <div class="main" slot="main">
+        <gr-repo-branch-picker
+          repo="{{_repo}}"
+          branch="{{_branch}}"
+        ></gr-repo-branch-picker>
+        <p>
+          If you haven't done so, you will need to clone the repository.
+        </p>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
 `;
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 8b8b981..1cc2316 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
@@ -14,11 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../gr-change-list/gr-change-list.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../shared/gr-overlay/gr-overlay.js';
@@ -34,11 +31,12 @@
 import {htmlTemplate} from './gr-dashboard-view_html.js';
 import {RESTClientBehavior} from '../../../behaviors/rest-client-behavior/rest-client-behavior.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
+import {appContext} from '../../../services/app-context.js';
 
 const PROJECT_PLACEHOLDER_PATTERN = /\$\{project\}/g;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDashboardView extends mixinBehaviors( [
   RESTClientBehavior,
@@ -98,6 +96,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   static get observers() {
     return [
       '_paramsChanged(params.*)',
@@ -197,7 +200,7 @@
         })
         .then(() => {
           this._maybeShowDraftsBanner();
-          this.$.reporting.dashboardDisplayed();
+          this.reporting.dashboardDisplayed();
         })
         .catch(err => {
           this.dispatchEvent(new CustomEvent('title-change', {
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.js
index 07d638c..cf1036f 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_html.js
@@ -17,81 +17,106 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    .loading {
+      color: var(--deemphasized-text-color);
+      padding: var(--spacing-l);
+    }
+    gr-change-list {
+      width: 100%;
+    }
+    gr-user-header {
+      border-bottom: 1px solid var(--border-color);
+    }
+    .banner {
+      align-items: center;
+      background-color: var(--comment-background-color);
+      border-bottom: 1px solid var(--border-color);
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-xs) var(--spacing-l);
+    }
+    .banner gr-button {
+      --gr-button: {
+        color: var(--primary-text-color);
       }
+    }
+    .hide {
+      display: none;
+    }
+    #emptyOutgoing {
+      display: block;
+    }
+    @media only screen and (max-width: 50em) {
       .loading {
-        color: var(--deemphasized-text-color);
-        padding: var(--spacing-l);
+        padding: 0 var(--spacing-l);
       }
-      gr-change-list {
-        width: 100%;
-      }
-      gr-user-header {
-        border-bottom: 1px solid var(--border-color);
-      }
-      .banner {
-        align-items: center;
-        background-color: var(--comment-background-color);
-        border-bottom: 1px solid var(--border-color);
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-xs) var(--spacing-l);
-      }
-      .banner gr-button {
-        --gr-button: {
-          color: var(--primary-text-color);
-        }
-      }
-      .hide {
-        display: none;
-      }
-      #emptyOutgoing {
-        display: block;
-      }
-      @media only screen and (max-width: 50em) {
-        .loading {
-          padding: 0 var(--spacing-l);
-        }
-      }
-    </style>
-    <div class\$="banner [[_computeBannerClass(_showDraftsBanner)]]">
-      <div>
-        You have draft comments on closed changes.
-        <a href\$="[[_computeDraftsLink(_showDraftsBanner)]]" target="_blank">(view all)</a>
-      </div>
-      <div>
-        <gr-button class="delete" link="" on-click="_handleOpenDeleteDialog">Delete All</gr-button>
-      </div>
+    }
+  </style>
+  <div class$="banner [[_computeBannerClass(_showDraftsBanner)]]">
+    <div>
+      You have draft comments on closed changes.
+      <a href$="[[_computeDraftsLink(_showDraftsBanner)]]" target="_blank"
+        >(view all)</a
+      >
     </div>
-    <div class="loading" hidden\$="[[!_loading]]">Loading...</div>
-    <div hidden\$="[[_loading]]" hidden="">
-      <gr-user-header user-id="[[params.user]]" class\$="[[_computeUserHeaderClass(params)]]"></gr-user-header>
-      <gr-change-list show-star="" show-reviewed-state="" account="[[account]]" preferences="[[preferences]]" selected-index="{{viewState.selectedChangeIndex}}" sections="[[_results]]" on-toggle-star="_handleToggleStar" on-toggle-reviewed="_handleToggleReviewed">
-        <div id="emptyOutgoing" slot="empty-outgoing">
-          <template is="dom-if" if="[[_showNewUserHelp]]">
-            <gr-create-change-help on-create-tap="createChangeTap"></gr-create-change-help>
-          </template>
-          <template is="dom-if" if="[[!_showNewUserHelp]]">
-            No changes
-          </template>
-        </div>
-      </gr-change-list>
+    <div>
+      <gr-button class="delete" link="" on-click="_handleOpenDeleteDialog"
+        >Delete All</gr-button
+      >
     </div>
-    <gr-overlay id="confirmDeleteOverlay" with-backdrop="">
-      <gr-dialog id="confirmDeleteDialog" confirm-label="Delete" on-confirm="_handleConfirmDelete" on-cancel="_closeConfirmDeleteOverlay">
-        <div class="header" slot="header">
-          Delete comments
-        </div>
-        <div class="main" slot="main">
-          Are you sure you want to delete all your draft comments in closed changes? This action
-          cannot be undone.
-        </div>
-      </gr-dialog>
-    </gr-overlay>
-    <gr-create-destination-dialog id="destinationDialog" on-confirm="_handleDestinationConfirm"></gr-create-destination-dialog>
-    <gr-create-commands-dialog id="commandsDialog"></gr-create-commands-dialog>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-reporting id="reporting"></gr-reporting>
+  </div>
+  <div class="loading" hidden$="[[!_loading]]">Loading...</div>
+  <div hidden$="[[_loading]]" hidden="">
+    <gr-user-header
+      user-id="[[params.user]]"
+      class$="[[_computeUserHeaderClass(params)]]"
+    ></gr-user-header>
+    <gr-change-list
+      show-star=""
+      show-reviewed-state=""
+      account="[[account]]"
+      preferences="[[preferences]]"
+      selected-index="{{viewState.selectedChangeIndex}}"
+      sections="[[_results]]"
+      on-toggle-star="_handleToggleStar"
+      on-toggle-reviewed="_handleToggleReviewed"
+    >
+      <div id="emptyOutgoing" slot="empty-outgoing">
+        <template is="dom-if" if="[[_showNewUserHelp]]">
+          <gr-create-change-help
+            on-create-tap="createChangeTap"
+          ></gr-create-change-help>
+        </template>
+        <template is="dom-if" if="[[!_showNewUserHelp]]">
+          No changes
+        </template>
+      </div>
+    </gr-change-list>
+  </div>
+  <gr-overlay id="confirmDeleteOverlay" with-backdrop="">
+    <gr-dialog
+      id="confirmDeleteDialog"
+      confirm-label="Delete"
+      on-confirm="_handleConfirmDelete"
+      on-cancel="_closeConfirmDeleteOverlay"
+    >
+      <div class="header" slot="header">
+        Delete comments
+      </div>
+      <div class="main" slot="main">
+        Are you sure you want to delete all your draft comments in closed
+        changes? This action cannot be undone.
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-create-destination-dialog
+    id="destinationDialog"
+    on-confirm="_handleDestinationConfirm"
+  ></gr-create-destination-dialog>
+  <gr-create-commands-dialog id="commandsDialog"></gr-create-commands-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
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 19742a5..2fcf233 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-dashboard-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -356,7 +357,7 @@
         });
     element.addEventListener('page-error', e => {
       assert.strictEqual(e.detail.response, response);
-      done();
+      paramsChangedPromise.then(done);
     });
     element.params = {
       view: GerritNav.View.DASHBOARD,
@@ -366,14 +367,14 @@
   });
 
   test('params change triggers dashboardDisplayed()', () => {
-    sandbox.stub(element.$.reporting, 'dashboardDisplayed');
+    sandbox.stub(element.reporting, 'dashboardDisplayed');
     element.params = {
       view: GerritNav.View.DASHBOARD,
       project: 'project',
       dashboard: 'dashboard',
     };
     return paramsChangedPromise.then(() => {
-      assert.isTrue(element.$.reporting.dashboardDisplayed.calledOnce);
+      assert.isTrue(element.reporting.dashboardDisplayed.calledOnce);
     });
   });
 });
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 2523700..7d1f104 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-change-list/gr-change-list.js';
 import '../gr-create-change-help/gr-create-change-help.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -23,7 +21,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-embed-dashboard_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrEmbedDashboard extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard_html.js b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard_html.js
index 2c122f37..802e365 100644
--- a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard_html.js
@@ -17,14 +17,19 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-change-list show-star="" account="[[account]]" preferences="[[preferences]]" sections="[[sections]]">
-      <div id="emptyOutgoing" slot="empty-outgoing">
-        <template is="dom-if" if="[[showNewUserHelp]]">
-          <gr-create-change-help></gr-create-change-help>
-        </template>
-        <template is="dom-if" if="[[!showNewUserHelp]]">
-          No changes
-        </template>
-      </div>
-    </gr-change-list>
+  <gr-change-list
+    show-star=""
+    account="[[account]]"
+    preferences="[[preferences]]"
+    sections="[[sections]]"
+  >
+    <div id="emptyOutgoing" slot="empty-outgoing">
+      <template is="dom-if" if="[[showNewUserHelp]]">
+        <gr-create-change-help></gr-create-change-help>
+      </template>
+      <template is="dom-if" if="[[!showNewUserHelp]]">
+        No changes
+      </template>
+    </div>
+  </gr-change-list>
 `;
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 5f0021e..303c612 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../../styles/dashboard-header-styles.js';
 import '../../../styles/shared-styles.js';
@@ -26,7 +25,7 @@
 import {htmlTemplate} from './gr-repo-header_html.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrRepoHeader extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.js b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.js
index d1274ee..f6fb1d0 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_html.js
@@ -17,20 +17,18 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="dashboard-header-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div class="info">
-      <h1 class\$="name">
-        [[repo]]
-        <hr>
-      </h1>
-      <div>
-        <span>Detail:</span> <a href\$="[[_repoUrl]]">Repo settings</a>
-      </div>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="dashboard-header-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div class="info">
+    <h1 class$="name">
+      [[repo]]
+      <hr />
+    </h1>
+    <div><span>Detail:</span> <a href$="[[_repoUrl]]">Repo settings</a></div>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html
index b0a8d1c..78c1f09 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-header</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6bb1bf8..7901c53 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../../styles/shared-styles.js';
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
@@ -30,7 +29,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrUserHeader extends GestureEventListeners(
     LegacyElementMixin(
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.js b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.js
index 3de4277..5a5d590 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.js
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_html.js
@@ -17,52 +17,60 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="dashboard-header-styles">
-      .name {
-        display: inline-block;
-      }
-      .name hr {
-        width: 100%;
-      }
-      .status.hide,
-      .name.hide,
-      .dashboardLink.hide {
-        display: none;
-      }
-    </style>
-    <gr-avatar account="[[_accountDetails]]" image-size="100" aria-label="Account avatar"></gr-avatar>
-    <div class="info">
-      <h1 class="name">
-        [[_computeDetail(_accountDetails, 'name')]]
-      </h1>
-      <hr>
-      <div class\$="status [[_computeStatusClass(_accountDetails)]]">
-        <span>Status:</span> [[_status]]
-      </div>
-      <div>
-        <span>Email:</span>
-        <a href="mailto:[[_computeDetail(_accountDetails, 'email')]]"><!--
-          -->[[_computeDetail(_accountDetails, 'email')]]</a>
-      </div>
-      <div>
-        <span>Joined:</span>
-        <gr-date-formatter date-str="[[_computeDetail(_accountDetails, 'registered_on')]]">
-        </gr-date-formatter>
-      </div>
-      <gr-endpoint-decorator name="user-header">
-        <gr-endpoint-param name="accountDetails" value="[[_accountDetails]]">
-        </gr-endpoint-param>
-        <gr-endpoint-param name="loggedIn" value="[[loggedIn]]">
-        </gr-endpoint-param>
-      </gr-endpoint-decorator>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="dashboard-header-styles">
+    .name {
+      display: inline-block;
+    }
+    .name hr {
+      width: 100%;
+    }
+    .status.hide,
+    .name.hide,
+    .dashboardLink.hide {
+      display: none;
+    }
+  </style>
+  <gr-avatar
+    account="[[_accountDetails]]"
+    image-size="100"
+    aria-label="Account avatar"
+  ></gr-avatar>
+  <div class="info">
+    <h1 class="name">
+      [[_computeDetail(_accountDetails, 'name')]]
+    </h1>
+    <hr />
+    <div class$="status [[_computeStatusClass(_accountDetails)]]">
+      <span>Status:</span> [[_status]]
     </div>
-    <div class="info">
-      <div class\$="[[_computeDashboardLinkClass(showDashboardLink, loggedIn)]]">
-        <a href\$="[[_computeDashboardUrl(_accountDetails)]]">View dashboard</a>
-      </div>
+    <div>
+      <span>Email:</span>
+      <a href="mailto:[[_computeDetail(_accountDetails, 'email')]]"
+        ><!--
+          -->[[_computeDetail(_accountDetails, 'email')]]</a
+      >
     </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    <div>
+      <span>Joined:</span>
+      <gr-date-formatter
+        date-str="[[_computeDetail(_accountDetails, 'registered_on')]]"
+      >
+      </gr-date-formatter>
+    </div>
+    <gr-endpoint-decorator name="user-header">
+      <gr-endpoint-param name="accountDetails" value="[[_accountDetails]]">
+      </gr-endpoint-param>
+      <gr-endpoint-param name="loggedIn" value="[[loggedIn]]">
+      </gr-endpoint-param>
+    </gr-endpoint-decorator>
+  </div>
+  <div class="info">
+    <div class$="[[_computeDashboardLinkClass(showDashboardLink, loggedIn)]]">
+      <a href$="[[_computeDashboardUrl(_accountDetails)]]">View dashboard</a>
+    </div>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html
index aa32a94..44eb96c 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-user-header</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 d65757a..d5a10c9 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
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../admin/gr-create-change-dialog/gr-create-change-dialog.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../shared/gr-dropdown/gr-dropdown.js';
@@ -44,6 +41,7 @@
 import {RESTClientBehavior} from '../../../behaviors/rest-client-behavior/rest-client-behavior.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 import {pluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
+import {appContext} from '../../../services/app-context.js';
 
 const ERR_BRANCH_EMPTY = 'The destination branch can’t be empty.';
 const ERR_COMMIT_EMPTY = 'The commit message can’t be empty.';
@@ -232,7 +230,7 @@
 const SKIP_ACTION_KEYS = [ChangeActions.REVERT_SUBMISSION];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeActions extends mixinBehaviors( [
   PatchSetBehavior,
@@ -272,6 +270,7 @@
     this.ActionType = ActionType;
     this.ChangeActions = ChangeActions;
     this.RevisionActions = RevisionActions;
+    this.reporting = appContext.reportingService;
   }
 
   static get properties() {
@@ -1014,7 +1013,7 @@
   }
 
   _handleAction(type, key) {
-    this.$.reporting.reportInteraction(`${type}-${key}`);
+    this.reporting.reportInteraction(`${type}-${key}`);
     switch (type) {
       case ActionType.REVISION:
         this._handleRevisionAction(key);
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.js
index b66beed..e0b7bdd 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.js
@@ -17,124 +17,258 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: flex;
-        font-family: var(--font-family);
+  <style include="shared-styles">
+    :host {
+      display: flex;
+      font-family: var(--font-family);
+    }
+    #actionLoadingMessage,
+    #mainContent,
+    section {
+      display: flex;
+    }
+    #actionLoadingMessage,
+    gr-button,
+    gr-dropdown {
+      /* px because don't have the same font size */
+      margin-left: 8px;
+    }
+    #actionLoadingMessage {
+      align-items: center;
+      color: var(--deemphasized-text-color);
+    }
+    #confirmSubmitDialog .changeSubject {
+      margin: var(--spacing-l);
+      text-align: center;
+    }
+    iron-icon {
+      color: inherit;
+      margin-right: var(--spacing-xs);
+    }
+    #moreActions iron-icon {
+      margin: 0;
+    }
+    #moreMessage,
+    .hidden {
+      display: none;
+    }
+    @media screen and (max-width: 50em) {
+      #mainContent {
+        flex-wrap: wrap;
       }
-      #actionLoadingMessage,
-      #mainContent,
-      section {
-        display: flex;
+      gr-button {
+        --gr-button: {
+          padding: var(--spacing-m);
+          white-space: nowrap;
+        }
       }
-      #actionLoadingMessage,
       gr-button,
       gr-dropdown {
-        /* px because don't have the same font size */
-        margin-left: 8px;
-      }
-      #actionLoadingMessage {
-        align-items: center;
-        color: var(--deemphasized-text-color);
-      }
-      #confirmSubmitDialog .changeSubject {
-        margin: var(--spacing-l);
-        text-align: center;
-      }
-      iron-icon {
-        color: inherit;
-        margin-right: var(--spacing-xs);
-      }
-      #moreActions iron-icon {
         margin: 0;
       }
-      #moreMessage,
-      .hidden {
-        display: none;
+      #actionLoadingMessage {
+        margin: var(--spacing-m);
+        text-align: center;
       }
-      @media screen and (max-width: 50em) {
-        #mainContent {
-          flex-wrap: wrap;
-        }
-        gr-button {
-          --gr-button: {
-            padding: var(--spacing-m);
-            white-space: nowrap;
-          }
-        }
-        gr-button,
-        gr-dropdown {
-          margin: 0;
-        }
-        #actionLoadingMessage {
-          margin: var(--spacing-m);
-          text-align: center;
-        }
-        #moreMessage {
-          display: inline;
-        }
+      #moreMessage {
+        display: inline;
       }
-    </style>
-    <div id="mainContent">
-      <span id="actionLoadingMessage" hidden\$="[[!_actionLoadingMessage]]">
-        [[_actionLoadingMessage]]</span>
-        <section id="primaryActions" hidden\$="[[_shouldHideActions(_topLevelActions.*, _loading)]]">
-          <template is="dom-repeat" items="[[_topLevelPrimaryActions]]" as="action">
-            <gr-button link="" title\$="[[action.title]]" has-tooltip="[[_computeHasTooltip(action.title)]]" position-below="true" data-action-key\$="[[action.__key]]" data-action-type\$="[[action.__type]]" data-label\$="[[action.label]]" disabled\$="[[_calculateDisabled(action, _hasKnownChainState)]]" on-click="_handleActionTap">
-                <iron-icon class\$="[[_computeHasIcon(action)]]" icon\$="gr-icons:[[action.icon]]"></iron-icon>
-              [[action.label]]
-            </gr-button>
-          </template>
-        </section>
-        <section id="secondaryActions" hidden\$="[[_shouldHideActions(_topLevelActions.*, _loading)]]">
-          <template is="dom-repeat" items="[[_topLevelSecondaryActions]]" as="action">
-            <gr-button link="" title\$="[[action.title]]" has-tooltip="[[_computeHasTooltip(action.title)]]" position-below="true" data-action-key\$="[[action.__key]]" data-action-type\$="[[action.__type]]" data-label\$="[[action.label]]" disabled\$="[[_calculateDisabled(action, _hasKnownChainState)]]" on-click="_handleActionTap">
-              <iron-icon class\$="[[_computeHasIcon(action)]]" icon\$="gr-icons:[[action.icon]]"></iron-icon>
-              [[action.label]]
-            </gr-button>
-          </template>
-        </section>
-      <gr-button hidden\$="[[!_loading]]" disabled="">Loading actions...</gr-button>
-      <gr-dropdown id="moreActions" link="" tabindex="0" vertical-offset="32" horizontal-align="right" on-tap-item="_handleOveflowItemTap" hidden\$="[[_shouldHideActions(_menuActions.*, _loading)]]" disabled-ids="[[_disabledMenuActions]]" items="[[_menuActions]]">
-          <iron-icon icon="gr-icons:more-vert"></iron-icon>
-          <span id="moreMessage">More</span>
-        </gr-dropdown>
-    </div>
-    <gr-overlay id="overlay" with-backdrop="">
-      <gr-confirm-rebase-dialog id="confirmRebase" class="confirmDialog" change-number="[[change._number]]" on-confirm="_handleRebaseConfirm" on-cancel="_handleConfirmDialogCancel" branch="[[change.branch]]" has-parent="[[hasParent]]" rebase-on-current="[[_computeRebaseOnCurrent(_revisionRebaseAction)]]" hidden=""></gr-confirm-rebase-dialog>
-      <gr-confirm-cherrypick-dialog id="confirmCherrypick" class="confirmDialog" change-status="[[changeStatus]]" commit-message="[[commitMessage]]" commit-num="[[commitNum]]" on-confirm="_handleCherrypickConfirm" on-cancel="_handleConfirmDialogCancel" project="[[change.project]]" hidden=""></gr-confirm-cherrypick-dialog>
-      <gr-confirm-cherrypick-conflict-dialog id="confirmCherrypickConflict" class="confirmDialog" on-confirm="_handleCherrypickConflictConfirm" on-cancel="_handleConfirmDialogCancel" hidden=""></gr-confirm-cherrypick-conflict-dialog>
-      <gr-confirm-move-dialog id="confirmMove" class="confirmDialog" on-confirm="_handleMoveConfirm" on-cancel="_handleConfirmDialogCancel" project="[[change.project]]" hidden=""></gr-confirm-move-dialog>
-      <gr-confirm-revert-dialog id="confirmRevertDialog" class="confirmDialog" on-confirm="_handleRevertDialogConfirm" on-cancel="_handleConfirmDialogCancel" hidden=""></gr-confirm-revert-dialog>
-      <gr-confirm-revert-submission-dialog id="confirmRevertSubmissionDialog" class="confirmDialog" commit-message="[[commitMessage]]" on-confirm="_handleRevertSubmissionDialogConfirm" on-cancel="_handleConfirmDialogCancel" hidden=""></gr-confirm-revert-submission-dialog>
-      <gr-confirm-abandon-dialog id="confirmAbandonDialog" class="confirmDialog" on-confirm="_handleAbandonDialogConfirm" on-cancel="_handleConfirmDialogCancel" hidden=""></gr-confirm-abandon-dialog>
-      <gr-confirm-submit-dialog id="confirmSubmitDialog" class="confirmDialog" change="[[change]]" action="[[_revisionSubmitAction]]" on-cancel="_handleConfirmDialogCancel" on-confirm="_handleSubmitConfirm" hidden=""></gr-confirm-submit-dialog>
-      <gr-dialog id="createFollowUpDialog" class="confirmDialog" confirm-label="Create" on-confirm="_handleCreateFollowUpChange" on-cancel="_handleCloseCreateFollowUpChange">
-        <div class="header" slot="header">
-          Create Follow-Up Change
-        </div>
-        <div class="main" slot="main">
-          <gr-create-change-dialog id="createFollowUpChange" branch="[[change.branch]]" base-change="[[change.id]]" repo-name="[[change.project]]" private-by-default="[[privateByDefault]]"></gr-create-change-dialog>
-        </div>
-      </gr-dialog>
-      <gr-dialog id="confirmDeleteDialog" class="confirmDialog" confirm-label="Delete" confirm-on-enter="" on-cancel="_handleConfirmDialogCancel" on-confirm="_handleDeleteConfirm">
-        <div class="header" slot="header">
-          Delete Change
-        </div>
-        <div class="main" slot="main">
-          Do you really want to delete the change?
-        </div>
-      </gr-dialog>
-      <gr-dialog id="confirmDeleteEditDialog" class="confirmDialog" confirm-label="Delete" confirm-on-enter="" on-cancel="_handleConfirmDialogCancel" on-confirm="_handleDeleteEditConfirm">
-        <div class="header" slot="header">
-          Delete Change Edit
-        </div>
-        <div class="main" slot="main">
-          Do you really want to delete the edit?
-        </div>
-      </gr-dialog>
-    </gr-overlay>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-reporting id="reporting" category="change-actions"></gr-reporting>
+    }
+  </style>
+  <div id="mainContent">
+    <span id="actionLoadingMessage" hidden$="[[!_actionLoadingMessage]]">
+      [[_actionLoadingMessage]]</span
+    >
+    <section
+      id="primaryActions"
+      hidden$="[[_shouldHideActions(_topLevelActions.*, _loading)]]"
+    >
+      <template is="dom-repeat" items="[[_topLevelPrimaryActions]]" as="action">
+        <gr-button
+          link=""
+          title$="[[action.title]]"
+          has-tooltip="[[_computeHasTooltip(action.title)]]"
+          position-below="true"
+          data-action-key$="[[action.__key]]"
+          data-action-type$="[[action.__type]]"
+          data-label$="[[action.label]]"
+          disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
+          on-click="_handleActionTap"
+        >
+          <iron-icon
+            class$="[[_computeHasIcon(action)]]"
+            icon$="gr-icons:[[action.icon]]"
+          ></iron-icon>
+          [[action.label]]
+        </gr-button>
+      </template>
+    </section>
+    <section
+      id="secondaryActions"
+      hidden$="[[_shouldHideActions(_topLevelActions.*, _loading)]]"
+    >
+      <template
+        is="dom-repeat"
+        items="[[_topLevelSecondaryActions]]"
+        as="action"
+      >
+        <gr-button
+          link=""
+          title$="[[action.title]]"
+          has-tooltip="[[_computeHasTooltip(action.title)]]"
+          position-below="true"
+          data-action-key$="[[action.__key]]"
+          data-action-type$="[[action.__type]]"
+          data-label$="[[action.label]]"
+          disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
+          on-click="_handleActionTap"
+        >
+          <iron-icon
+            class$="[[_computeHasIcon(action)]]"
+            icon$="gr-icons:[[action.icon]]"
+          ></iron-icon>
+          [[action.label]]
+        </gr-button>
+      </template>
+    </section>
+    <gr-button hidden$="[[!_loading]]" disabled=""
+      >Loading actions...</gr-button
+    >
+    <gr-dropdown
+      id="moreActions"
+      link=""
+      tabindex="0"
+      vertical-offset="32"
+      horizontal-align="right"
+      on-tap-item="_handleOveflowItemTap"
+      hidden$="[[_shouldHideActions(_menuActions.*, _loading)]]"
+      disabled-ids="[[_disabledMenuActions]]"
+      items="[[_menuActions]]"
+    >
+      <iron-icon icon="gr-icons:more-vert"></iron-icon>
+      <span id="moreMessage">More</span>
+    </gr-dropdown>
+  </div>
+  <gr-overlay id="overlay" with-backdrop="">
+    <gr-confirm-rebase-dialog
+      id="confirmRebase"
+      class="confirmDialog"
+      change-number="[[change._number]]"
+      on-confirm="_handleRebaseConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      branch="[[change.branch]]"
+      has-parent="[[hasParent]]"
+      rebase-on-current="[[_computeRebaseOnCurrent(_revisionRebaseAction)]]"
+      hidden=""
+    ></gr-confirm-rebase-dialog>
+    <gr-confirm-cherrypick-dialog
+      id="confirmCherrypick"
+      class="confirmDialog"
+      change-status="[[changeStatus]]"
+      commit-message="[[commitMessage]]"
+      commit-num="[[commitNum]]"
+      on-confirm="_handleCherrypickConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      project="[[change.project]]"
+      hidden=""
+    ></gr-confirm-cherrypick-dialog>
+    <gr-confirm-cherrypick-conflict-dialog
+      id="confirmCherrypickConflict"
+      class="confirmDialog"
+      on-confirm="_handleCherrypickConflictConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      hidden=""
+    ></gr-confirm-cherrypick-conflict-dialog>
+    <gr-confirm-move-dialog
+      id="confirmMove"
+      class="confirmDialog"
+      on-confirm="_handleMoveConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      project="[[change.project]]"
+      hidden=""
+    ></gr-confirm-move-dialog>
+    <gr-confirm-revert-dialog
+      id="confirmRevertDialog"
+      class="confirmDialog"
+      on-confirm="_handleRevertDialogConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      hidden=""
+    ></gr-confirm-revert-dialog>
+    <gr-confirm-revert-submission-dialog
+      id="confirmRevertSubmissionDialog"
+      class="confirmDialog"
+      commit-message="[[commitMessage]]"
+      on-confirm="_handleRevertSubmissionDialogConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      hidden=""
+    ></gr-confirm-revert-submission-dialog>
+    <gr-confirm-abandon-dialog
+      id="confirmAbandonDialog"
+      class="confirmDialog"
+      on-confirm="_handleAbandonDialogConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      hidden=""
+    ></gr-confirm-abandon-dialog>
+    <gr-confirm-submit-dialog
+      id="confirmSubmitDialog"
+      class="confirmDialog"
+      change="[[change]]"
+      action="[[_revisionSubmitAction]]"
+      on-cancel="_handleConfirmDialogCancel"
+      on-confirm="_handleSubmitConfirm"
+      hidden=""
+    ></gr-confirm-submit-dialog>
+    <gr-dialog
+      id="createFollowUpDialog"
+      class="confirmDialog"
+      confirm-label="Create"
+      on-confirm="_handleCreateFollowUpChange"
+      on-cancel="_handleCloseCreateFollowUpChange"
+    >
+      <div class="header" slot="header">
+        Create Follow-Up Change
+      </div>
+      <div class="main" slot="main">
+        <gr-create-change-dialog
+          id="createFollowUpChange"
+          branch="[[change.branch]]"
+          base-change="[[change.id]]"
+          repo-name="[[change.project]]"
+          private-by-default="[[privateByDefault]]"
+        ></gr-create-change-dialog>
+      </div>
+    </gr-dialog>
+    <gr-dialog
+      id="confirmDeleteDialog"
+      class="confirmDialog"
+      confirm-label="Delete"
+      confirm-on-enter=""
+      on-cancel="_handleConfirmDialogCancel"
+      on-confirm="_handleDeleteConfirm"
+    >
+      <div class="header" slot="header">
+        Delete Change
+      </div>
+      <div class="main" slot="main">
+        Do you really want to delete the change?
+      </div>
+    </gr-dialog>
+    <gr-dialog
+      id="confirmDeleteEditDialog"
+      class="confirmDialog"
+      confirm-label="Delete"
+      confirm-on-enter=""
+      on-cancel="_handleConfirmDialogCancel"
+      on-confirm="_handleDeleteEditConfirm"
+    >
+      <div class="header" slot="header">
+        Delete Change Edit
+      </div>
+      <div class="main" slot="main">
+        Do you really want to delete the edit?
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
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 30907dd..a763250 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-actions</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -1965,7 +1966,7 @@
 
     test('_handleAction reports', () => {
       sandbox.stub(element, '_fireAction');
-      const reportStub = sandbox.stub(element.$.reporting, 'reportInteraction');
+      const reportStub = sandbox.stub(element.reporting, 'reportInteraction');
       element._handleAction('type', 'key');
       assert.isTrue(reportStub.called);
       assert.equal(reportStub.lastCall.args[0], 'type-key');
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
index 84ac4e0..d08f529 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-metadata</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></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 0bd73f5..b8abb25 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../../styles/gr-change-metadata-shared-styles.js';
 import '../../../styles/gr-change-view-integration-shared-styles.js';
@@ -78,7 +76,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeMetadata extends mixinBehaviors( [
   RESTClientBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.js
index 4de3128..8f6c02b 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.js
@@ -17,239 +17,349 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-change-metadata-shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="shared-styles">
-      :host {
-        display: table;
-        --account-max-length: 20ch;
-      }
-      gr-change-requirements {
-        --requirements-horizontal-padding: var(--metadata-horizontal-padding);
-      }
-      gr-editable-label {
-        max-width: 9em;
-      }
-      .webLink {
-        display: block;
-      }
-      /* CSS Mixins should be applied last. */
-      section.assignee {
-        @apply --change-metadata-assignee;
-      }
-      section.strategy {
-        @apply --change-metadata-strategy;
-      }
-      section.topic {
-        @apply --change-metadata-topic;
-      }
-      gr-account-chip[disabled],
-      gr-linked-chip[disabled] {
-        opacity: 0;
-        pointer-events: none;
-      }
-      .hashtagChip {
-        margin-bottom: var(--spacing-m);
-      }
-      #externalStyle {
-        display: block;
-      }
-      .parentList.merge {
-        list-style-type: decimal;
-        padding-left: var(--spacing-l);
-      }
-      .parentList gr-commit-info {
-        display: inline-block;
-      }
-      .hideDisplay,
-      #parentNotCurrentMessage {
-        display: none;
-      }
-      .icon {
-        margin: -3px 0;
-      }
-      .icon.help,
-      .icon.notTrusted {
-        color: #FFA62F;
-      }
-      .icon.invalid {
-        color: var(--vote-text-color-disliked);
-      }
-      .icon.trusted {
-        color: var(--vote-text-color-recommended);
-      }
-      .parentList.notCurrent.nonMerge #parentNotCurrentMessage {
-        --arrow-color: #ffa62f;
-        display: inline-block;
-      }
-      .separatedSection {
-        margin-top: var(--spacing-l);
-        padding: var(--spacing-m) 0;
-      }
-      .hashtag gr-linked-chip,
-      .topic gr-linked-chip {
-        --linked-chip-text-color: var(--link-color);
-      }
-      gr-reviewer-list {
-        max-width: 200px;
-      }
-    </style>
-    <gr-external-style id="externalStyle" name="change-metadata">
-      <section>
-        <span class="title">Updated</span>
+  <style include="gr-change-metadata-shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="shared-styles">
+    :host {
+      display: table;
+      --account-max-length: 20ch;
+    }
+    gr-change-requirements {
+      --requirements-horizontal-padding: var(--metadata-horizontal-padding);
+    }
+    gr-editable-label {
+      max-width: 9em;
+    }
+    .webLink {
+      display: block;
+    }
+    /* CSS Mixins should be applied last. */
+    section.assignee {
+      @apply --change-metadata-assignee;
+    }
+    section.strategy {
+      @apply --change-metadata-strategy;
+    }
+    section.topic {
+      @apply --change-metadata-topic;
+    }
+    gr-account-chip[disabled],
+    gr-linked-chip[disabled] {
+      opacity: 0;
+      pointer-events: none;
+    }
+    .hashtagChip {
+      margin-bottom: var(--spacing-m);
+    }
+    #externalStyle {
+      display: block;
+    }
+    .parentList.merge {
+      list-style-type: decimal;
+      padding-left: var(--spacing-l);
+    }
+    .parentList gr-commit-info {
+      display: inline-block;
+    }
+    .hideDisplay,
+    #parentNotCurrentMessage {
+      display: none;
+    }
+    .icon {
+      margin: -3px 0;
+    }
+    .icon.help,
+    .icon.notTrusted {
+      color: #ffa62f;
+    }
+    .icon.invalid {
+      color: var(--vote-text-color-disliked);
+    }
+    .icon.trusted {
+      color: var(--vote-text-color-recommended);
+    }
+    .parentList.notCurrent.nonMerge #parentNotCurrentMessage {
+      --arrow-color: #ffa62f;
+      display: inline-block;
+    }
+    .separatedSection {
+      margin-top: var(--spacing-l);
+      padding: var(--spacing-m) 0;
+    }
+    .hashtag gr-linked-chip,
+    .topic gr-linked-chip {
+      --linked-chip-text-color: var(--link-color);
+    }
+    gr-reviewer-list {
+      max-width: 200px;
+    }
+  </style>
+  <gr-external-style id="externalStyle" name="change-metadata">
+    <section>
+      <span class="title">Updated</span>
+      <span class="value">
+        <gr-date-formatter
+          has-tooltip=""
+          date-str="[[change.updated]]"
+        ></gr-date-formatter>
+      </span>
+    </section>
+    <section>
+      <span class="title">Owner</span>
+      <span class="value">
+        <gr-account-link account="[[change.owner]]"></gr-account-link>
+        <template is="dom-if" if="[[_pushCertificateValidation]]">
+          <gr-tooltip-content
+            has-tooltip=""
+            title$="[[_pushCertificateValidation.message]]"
+          >
+            <iron-icon
+              class$="icon [[_pushCertificateValidation.class]]"
+              icon="[[_pushCertificateValidation.icon]]"
+            >
+            </iron-icon>
+          </gr-tooltip-content>
+        </template>
+      </span>
+    </section>
+    <section class$="[[_computeShowRoleClass(change, _CHANGE_ROLE.UPLOADER)]]">
+      <span class="title">Uploader</span>
+      <span class="value">
+        <gr-account-link
+          account="[[_getNonOwnerRole(change, _CHANGE_ROLE.UPLOADER)]]"
+        ></gr-account-link>
+      </span>
+    </section>
+    <section class$="[[_computeShowRoleClass(change, _CHANGE_ROLE.AUTHOR)]]">
+      <span class="title">Author</span>
+      <span class="value">
+        <gr-account-link
+          account="[[_getNonOwnerRole(change, _CHANGE_ROLE.AUTHOR)]]"
+        ></gr-account-link>
+      </span>
+    </section>
+    <section class$="[[_computeShowRoleClass(change, _CHANGE_ROLE.COMMITTER)]]">
+      <span class="title">Committer</span>
+      <span class="value">
+        <gr-account-link
+          account="[[_getNonOwnerRole(change, _CHANGE_ROLE.COMMITTER)]]"
+        ></gr-account-link>
+      </span>
+    </section>
+    <template is="dom-if" if="[[_isAssigneeEnabled(serverConfig)]]">
+      <section class="assignee">
+        <span class="title">Assignee</span>
         <span class="value">
-          <gr-date-formatter has-tooltip="" date-str="[[change.updated]]"></gr-date-formatter>
+          <gr-account-list
+            id="assigneeValue"
+            placeholder="Set assignee..."
+            max-count="1"
+            skip-suggest-on-empty=""
+            accounts="{{_assignee}}"
+            readonly="[[_computeAssigneeReadOnly(_mutable, change)]]"
+            suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]"
+          >
+          </gr-account-list>
+        </span>
+      </section>
+    </template>
+    <section>
+      <span class="title">Reviewers</span>
+      <span class="value">
+        <gr-reviewer-list
+          change="{{change}}"
+          mutable="[[_mutable]]"
+          reviewers-only=""
+          server-config="[[serverConfig]]"
+        ></gr-reviewer-list>
+      </span>
+    </section>
+    <section>
+      <span class="title">CC</span>
+      <span class="value">
+        <gr-reviewer-list
+          change="{{change}}"
+          mutable="[[_mutable]]"
+          ccs-only=""
+          server-config="[[serverConfig]]"
+        ></gr-reviewer-list>
+      </span>
+    </section>
+    <template
+      is="dom-if"
+      if="[[_computeShowRepoBranchTogether(change.project, change.branch)]]"
+    >
+      <section>
+        <span class="title">Repo / Branch</span>
+        <span class="value">
+          <a href$="[[_computeProjectUrl(change.project)]]"
+            >[[change.project]]</a
+          >
+          /
+          <a href$="[[_computeBranchUrl(change.project, change.branch)]]"
+            >[[change.branch]]</a
+          >
+        </span>
+      </section>
+    </template>
+    <template
+      is="dom-if"
+      if="[[!_computeShowRepoBranchTogether(change.project, change.branch)]]"
+    >
+      <section>
+        <span class="title">Repo</span>
+        <span class="value">
+          <a href$="[[_computeProjectUrl(change.project)]]">
+            <gr-limited-text
+              limit="40"
+              text="[[change.project]]"
+            ></gr-limited-text>
+          </a>
         </span>
       </section>
       <section>
-        <span class="title">Owner</span>
+        <span class="title">Branch</span>
         <span class="value">
-          <gr-account-link account="[[change.owner]]"></gr-account-link>
-          <template is="dom-if" if="[[_pushCertificateValidation]]">
-            <gr-tooltip-content has-tooltip="" title\$="[[_pushCertificateValidation.message]]">
-              <iron-icon class\$="icon [[_pushCertificateValidation.class]]" icon="[[_pushCertificateValidation.icon]]">
-              </iron-icon>
-            </gr-tooltip-content>
+          <a href$="[[_computeBranchUrl(change.project, change.branch)]]">
+            <gr-limited-text
+              limit="40"
+              text="[[change.branch]]"
+            ></gr-limited-text>
+          </a>
+        </span>
+      </section>
+    </template>
+    <section>
+      <span class="title">[[_computeParentsLabel(_currentParents)]]</span>
+      <span class="value">
+        <ol
+          class$="[[_computeParentListClass(_currentParents, parentIsCurrent)]]"
+        >
+          <template is="dom-repeat" items="[[_currentParents]]" as="parent">
+            <li>
+              <gr-commit-info
+                change="[[change]]"
+                commit-info="[[parent]]"
+                server-config="[[serverConfig]]"
+              ></gr-commit-info>
+              <gr-tooltip-content
+                id="parentNotCurrentMessage"
+                has-tooltip=""
+                show-icon=""
+                title$="[[_notCurrentMessage]]"
+              ></gr-tooltip-content>
+            </li>
           </template>
-        </span>
-      </section>
-      <section class\$="[[_computeShowRoleClass(change, _CHANGE_ROLE.UPLOADER)]]">
-        <span class="title">Uploader</span>
-        <span class="value">
-          <gr-account-link account="[[_getNonOwnerRole(change, _CHANGE_ROLE.UPLOADER)]]"></gr-account-link>
-        </span>
-      </section>
-      <section class\$="[[_computeShowRoleClass(change, _CHANGE_ROLE.AUTHOR)]]">
-        <span class="title">Author</span>
-        <span class="value">
-          <gr-account-link account="[[_getNonOwnerRole(change, _CHANGE_ROLE.AUTHOR)]]"></gr-account-link>
-        </span>
-      </section>
-      <section class\$="[[_computeShowRoleClass(change, _CHANGE_ROLE.COMMITTER)]]">
-        <span class="title">Committer</span>
-        <span class="value">
-          <gr-account-link account="[[_getNonOwnerRole(change, _CHANGE_ROLE.COMMITTER)]]"></gr-account-link>
-        </span>
-      </section>
-      <template is="dom-if" if="[[_isAssigneeEnabled(serverConfig)]]">
-        <section class="assignee">
-          <span class="title">Assignee</span>
-          <span class="value">
-            <gr-account-list id="assigneeValue" placeholder="Set assignee..." max-count="1" skip-suggest-on-empty="" accounts="{{_assignee}}" readonly="[[_computeAssigneeReadOnly(_mutable, change)]]" suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
-            </gr-account-list>
-          </span>
-        </section>
-      </template>
+        </ol>
+      </span>
+    </section>
+    <section class="topic">
+      <span class="title">Topic</span>
+      <span class="value">
+        <template is="dom-if" if="[[_showTopicChip(change.*, _settingTopic)]]">
+          <gr-linked-chip
+            text="[[change.topic]]"
+            limit="40"
+            href="[[_computeTopicUrl(change.topic)]]"
+            removable="[[!_topicReadOnly]]"
+            on-remove="_handleTopicRemoved"
+          ></gr-linked-chip>
+        </template>
+        <template is="dom-if" if="[[_showAddTopic(change.*, _settingTopic)]]">
+          <gr-editable-label
+            class="topicEditableLabel"
+            label-text="Add a topic"
+            value="[[change.topic]]"
+            max-length="1024"
+            placeholder="[[_computeTopicPlaceholder(_topicReadOnly)]]"
+            read-only="[[_topicReadOnly]]"
+            on-changed="_handleTopicChanged"
+          ></gr-editable-label>
+        </template>
+      </span>
+    </section>
+    <template is="dom-if" if="[[_showCherryPickOf(change.*)]]">
       <section>
-        <span class="title">Reviewers</span>
+        <span class="title">Cherry pick of</span>
         <span class="value">
-          <gr-reviewer-list change="{{change}}" mutable="[[_mutable]]" reviewers-only="" server-config="[[serverConfig]]"></gr-reviewer-list>
+          <a
+            href$="[[_computeCherryPickOfUrl(change.cherry_pick_of_change, change.cherry_pick_of_patch_set, change.project)]]"
+          >
+            <gr-limited-text
+              text="[[change.cherry_pick_of_change]],[[change.cherry_pick_of_patch_set]]"
+              limit="40"
+            >
+            </gr-limited-text>
+          </a>
         </span>
       </section>
-      <section>
-        <span class="title">CC</span>
-        <span class="value">
-          <gr-reviewer-list change="{{change}}" mutable="[[_mutable]]" ccs-only="" server-config="[[serverConfig]]"></gr-reviewer-list>
-        </span>
-      </section>
-      <template is="dom-if" if="[[_computeShowRepoBranchTogether(change.project, change.branch)]]">
-        <section>
-          <span class="title">Repo / Branch</span>
-          <span class="value">
-            <a href\$="[[_computeProjectUrl(change.project)]]">[[change.project]]</a>
-            /
-            <a href\$="[[_computeBranchUrl(change.project, change.branch)]]">[[change.branch]]</a>
-          </span>
-        </section>
-      </template>
-      <template is="dom-if" if="[[!_computeShowRepoBranchTogether(change.project, change.branch)]]">
-        <section>
-          <span class="title">Repo</span>
-          <span class="value">
-            <a href\$="[[_computeProjectUrl(change.project)]]">
-              <gr-limited-text limit="40" text="[[change.project]]"></gr-limited-text>
-            </a>
-          </span>
-        </section>
-        <section>
-          <span class="title">Branch</span>
-          <span class="value">
-            <a href\$="[[_computeBranchUrl(change.project, change.branch)]]">
-              <gr-limited-text limit="40" text="[[change.branch]]"></gr-limited-text>
-            </a>
-          </span>
-        </section>
-      </template>
-      <section>
-        <span class="title">[[_computeParentsLabel(_currentParents)]]</span>
-        <span class="value">
-          <ol class\$="[[_computeParentListClass(_currentParents, parentIsCurrent)]]">
-            <template is="dom-repeat" items="[[_currentParents]]" as="parent">
-              <li>
-                <gr-commit-info change="[[change]]" commit-info="[[parent]]" server-config="[[serverConfig]]"></gr-commit-info>
-                <gr-tooltip-content id="parentNotCurrentMessage" has-tooltip="" show-icon="" title\$="[[_notCurrentMessage]]"></gr-tooltip-content>
-              </li>
-            </template>
-          </ol>
-        </span>
-      </section>
-      <section class="topic">
-        <span class="title">Topic</span>
-        <span class="value">
-          <template is="dom-if" if="[[_showTopicChip(change.*, _settingTopic)]]">
-            <gr-linked-chip text="[[change.topic]]" limit="40" href="[[_computeTopicUrl(change.topic)]]" removable="[[!_topicReadOnly]]" on-remove="_handleTopicRemoved"></gr-linked-chip>
-          </template>
-          <template is="dom-if" if="[[_showAddTopic(change.*, _settingTopic)]]">
-            <gr-editable-label class="topicEditableLabel" label-text="Add a topic" value="[[change.topic]]" max-length="1024" placeholder="[[_computeTopicPlaceholder(_topicReadOnly)]]" read-only="[[_topicReadOnly]]" on-changed="_handleTopicChanged"></gr-editable-label>
-          </template>
-        </span>
-      </section>
-      <template is="dom-if" if="[[_showCherryPickOf(change.*)]]">
-        <section>
-          <span class="title">Cherry pick of</span>
-          <span class="value">
-            <a href\$="[[_computeCherryPickOfUrl(change.cherry_pick_of_change, change.cherry_pick_of_patch_set, change.project)]]">
-              <gr-limited-text text="[[change.cherry_pick_of_change]],[[change.cherry_pick_of_patch_set]]" limit="40">
-              </gr-limited-text>
-            </a>
-          </span>
-        </section>
-      </template>
-      <section class="strategy" hidden\$="[[_computeHideStrategy(change)]]" hidden="">
-        <span class="title">Strategy</span>
-        <span class="value">[[_computeStrategy(change)]]</span>
-      </section>
-      <section class="hashtag">
-        <span class="title">Hashtags</span>
-        <span class="value">
-          <template is="dom-repeat" items="[[change.hashtags]]">
-            <gr-linked-chip class="hashtagChip" text="[[item]]" href="[[_computeHashtagUrl(item)]]" removable="[[!_hashtagReadOnly]]" on-remove="_handleHashtagRemoved">
-            </gr-linked-chip>
-          </template>
-          <template is="dom-if" if="[[!_hashtagReadOnly]]">
-            <gr-editable-label uppercase="" label-text="Add a hashtag" value="{{_newHashtag}}" placeholder="[[_computeHashtagPlaceholder(_hashtagReadOnly)]]" read-only="[[_hashtagReadOnly]]" on-changed="_handleHashtagChanged"></gr-editable-label>
-          </template>
-        </span>
-      </section>
-      <div class="separatedSection">
-        <gr-change-requirements change="{{change}}" account="[[account]]" mutable="[[_mutable]]"></gr-change-requirements>
-      </div>
-      <section id="webLinks" hidden\$="[[!_computeWebLinks(commitInfo, serverConfig)]]">
-        <span class="title">Links</span>
-        <span class="value">
-          <template is="dom-repeat" items="[[_computeWebLinks(commitInfo, serverConfig)]]" as="link">
-            <a href="[[link.url]]" class="webLink" rel="noopener" target="_blank">
-              [[link.name]]
-            </a>
-          </template>
-        </span>
-      </section>
-      <gr-endpoint-decorator name="change-metadata-item">
-        <gr-endpoint-param name="labels" value="[[labels]]"></gr-endpoint-param>
-        <gr-endpoint-param name="change" value="[[change]]"></gr-endpoint-param>
-        <gr-endpoint-param name="revision" value="[[revision]]"></gr-endpoint-param>
-      </gr-endpoint-decorator>
-    </gr-external-style>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </template>
+    <section
+      class="strategy"
+      hidden$="[[_computeHideStrategy(change)]]"
+      hidden=""
+    >
+      <span class="title">Strategy</span>
+      <span class="value">[[_computeStrategy(change)]]</span>
+    </section>
+    <section class="hashtag">
+      <span class="title">Hashtags</span>
+      <span class="value">
+        <template is="dom-repeat" items="[[change.hashtags]]">
+          <gr-linked-chip
+            class="hashtagChip"
+            text="[[item]]"
+            href="[[_computeHashtagUrl(item)]]"
+            removable="[[!_hashtagReadOnly]]"
+            on-remove="_handleHashtagRemoved"
+          >
+          </gr-linked-chip>
+        </template>
+        <template is="dom-if" if="[[!_hashtagReadOnly]]">
+          <gr-editable-label
+            uppercase=""
+            label-text="Add a hashtag"
+            value="{{_newHashtag}}"
+            placeholder="[[_computeHashtagPlaceholder(_hashtagReadOnly)]]"
+            read-only="[[_hashtagReadOnly]]"
+            on-changed="_handleHashtagChanged"
+          ></gr-editable-label>
+        </template>
+      </span>
+    </section>
+    <div class="separatedSection">
+      <gr-change-requirements
+        change="{{change}}"
+        account="[[account]]"
+        mutable="[[_mutable]]"
+      ></gr-change-requirements>
+    </div>
+    <section
+      id="webLinks"
+      hidden$="[[!_computeWebLinks(commitInfo, serverConfig)]]"
+    >
+      <span class="title">Links</span>
+      <span class="value">
+        <template
+          is="dom-repeat"
+          items="[[_computeWebLinks(commitInfo, serverConfig)]]"
+          as="link"
+        >
+          <a href="[[link.url]]" class="webLink" rel="noopener" target="_blank">
+            [[link.name]]
+          </a>
+        </template>
+      </span>
+    </section>
+    <gr-endpoint-decorator name="change-metadata-item">
+      <gr-endpoint-param name="labels" value="[[labels]]"></gr-endpoint-param>
+      <gr-endpoint-param name="change" value="[[change]]"></gr-endpoint-param>
+      <gr-endpoint-param
+        name="revision"
+        value="[[revision]]"
+      ></gr-endpoint-param>
+    </gr-endpoint-decorator>
+  </gr-external-style>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
index 5d9745e..58cc25b 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-metadata</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 d301813..78627a7 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-icons/gr-icons.js';
@@ -30,7 +28,7 @@
 import {RESTClientBehavior} from '../../../behaviors/rest-client-behavior/rest-client-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeRequirements extends mixinBehaviors( [
   RESTClientBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.js b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.js
index 311cfe4..0da31de 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_html.js
@@ -17,121 +17,159 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: table;
-        width: 100%;
-      }
-      .status {
-        color: #FFA62F;
-        display: inline-block;
-        text-align: center;
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-      }
-      .approved.status {
-        color: var(--vote-text-color-recommended);
-      }
-      .rejected.status {
-        color: var(--vote-text-color-disliked);
-      }
-      iron-icon {
-        color: inherit;
-      }
-      .status iron-icon {
-        vertical-align: top;
-      }
-      section {
-        display: table-row;
-      }
-      .show-hide {
-        float: right;
-      }
-      .title {
-        min-width: 10em;
-        padding: var(--spacing-s) var(--spacing-m) 0 var(--requirements-horizontal-padding);
-      }
-      .value {
-        padding: var(--spacing-s) 0 0 0;
-      }
-      .title,
-      .value {
-        display: table-cell;
-        vertical-align: top;
-      }
-      .hidden {
-        display: none;
-      }
-      .showHide {
-        cursor: pointer;
-      }
-      .showHide .title {
-        padding-bottom: var(--spacing-m);
-        padding-top: var(--spacing-l);
-      }
-      .showHide .value {
-        padding-top: 0;
-        vertical-align: middle;
-      }
-      .showHide iron-icon {
-        color: var(--deemphasized-text-color);
-        float: right;
-      }
-      .spacer {
-        height: var(--spacing-m);
-      }
-    </style>
-    <template is="dom-repeat" items="[[_requirements]]">
-      <section>
-        <div class="title requirement">
-          <span class\$="status [[item.style]]">
-            <iron-icon class="icon" icon="[[_computeRequirementIcon(item.satisfied)]]"></iron-icon>
-          </span>
-          <gr-limited-text class="name" limit="40" text="[[item.fallback_text]]"></gr-limited-text>
-        </div>
-      </section>
-    </template>
-    <template is="dom-repeat" items="[[_requiredLabels]]">
-      <section>
-        <div class="title">
-          <span class\$="status [[item.style]]">
-            <iron-icon class="icon" icon="[[item.icon]]"></iron-icon>
-          </span>
-          <gr-limited-text class="name" limit="40" text="[[item.label]]"></gr-limited-text>
-        </div>
-        <div class="value">
-          <gr-label-info change="{{change}}" account="[[account]]" mutable="[[mutable]]" label="[[item.label]]" label-info="[[item.labelInfo]]"></gr-label-info>
-        </div>
-      </section>
-    </template>
-    <section class="spacer"></section>
-    <section class\$="spacer [[_computeShowOptional(_optionalLabels.*)]]"></section>
-    <section show-bottom-border\$="[[_showOptionalLabels]]" on-click="_handleShowHide" class\$="showHide [[_computeShowOptional(_optionalLabels.*)]]">
-      <div class="title">Other labels</div>
-      <div class="value">
-        <iron-icon id="showHide" icon="[[_computeShowHideIcon(_showOptionalLabels)]]">
-        </iron-icon>
-      
+  <style include="shared-styles">
+    :host {
+      display: table;
+      width: 100%;
+    }
+    .status {
+      color: #ffa62f;
+      display: inline-block;
+      text-align: center;
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+    }
+    .approved.status {
+      color: var(--vote-text-color-recommended);
+    }
+    .rejected.status {
+      color: var(--vote-text-color-disliked);
+    }
+    iron-icon {
+      color: inherit;
+    }
+    .status iron-icon {
+      vertical-align: top;
+    }
+    section {
+      display: table-row;
+    }
+    .show-hide {
+      float: right;
+    }
+    .title {
+      min-width: 10em;
+      padding: var(--spacing-s) var(--spacing-m) 0
+        var(--requirements-horizontal-padding);
+    }
+    .value {
+      padding: var(--spacing-s) 0 0 0;
+    }
+    .title,
+    .value {
+      display: table-cell;
+      vertical-align: top;
+    }
+    .hidden {
+      display: none;
+    }
+    .showHide {
+      cursor: pointer;
+    }
+    .showHide .title {
+      padding-bottom: var(--spacing-m);
+      padding-top: var(--spacing-l);
+    }
+    .showHide .value {
+      padding-top: 0;
+      vertical-align: middle;
+    }
+    .showHide iron-icon {
+      color: var(--deemphasized-text-color);
+      float: right;
+    }
+    .spacer {
+      height: var(--spacing-m);
+    }
+  </style>
+  <template is="dom-repeat" items="[[_requirements]]">
+    <section>
+      <div class="title requirement">
+        <span class$="status [[item.style]]">
+          <iron-icon
+            class="icon"
+            icon="[[_computeRequirementIcon(item.satisfied)]]"
+          ></iron-icon>
+        </span>
+        <gr-limited-text
+          class="name"
+          limit="40"
+          text="[[item.fallback_text]]"
+        ></gr-limited-text>
       </div>
     </section>
-    <template is="dom-repeat" items="[[_optionalLabels]]">
-      <section class\$="optional [[_computeSectionClass(_showOptionalLabels)]]">
-        <div class="title">
-          <span class\$="status [[item.style]]">
-            <template is="dom-if" if="[[item.icon]]">
-              <iron-icon class="icon" icon="[[item.icon]]"></iron-icon>
-            </template>
-            <template is="dom-if" if="[[!item.icon]]">
-              <span>[[_computeLabelValue(item.labelInfo.value)]]</span>
-            </template>
-          </span>
-          <gr-limited-text class="name" limit="40" text="[[item.label]]"></gr-limited-text>
-        </div>
-        <div class="value">
-          <gr-label-info change="{{change}}" account="[[account]]" mutable="[[mutable]]" label="[[item.label]]" label-info="[[item.labelInfo]]"></gr-label-info>
-        </div>
-      </section>
-    </template>
-    <section class\$="spacer [[_computeShowOptional(_optionalLabels.*)]] [[_computeSectionClass(_showOptionalLabels)]]"></section>
+  </template>
+  <template is="dom-repeat" items="[[_requiredLabels]]">
+    <section>
+      <div class="title">
+        <span class$="status [[item.style]]">
+          <iron-icon class="icon" icon="[[item.icon]]"></iron-icon>
+        </span>
+        <gr-limited-text
+          class="name"
+          limit="40"
+          text="[[item.label]]"
+        ></gr-limited-text>
+      </div>
+      <div class="value">
+        <gr-label-info
+          change="{{change}}"
+          account="[[account]]"
+          mutable="[[mutable]]"
+          label="[[item.label]]"
+          label-info="[[item.labelInfo]]"
+        ></gr-label-info>
+      </div>
+    </section>
+  </template>
+  <section class="spacer"></section>
+  <section
+    class$="spacer [[_computeShowOptional(_optionalLabels.*)]]"
+  ></section>
+  <section
+    show-bottom-border$="[[_showOptionalLabels]]"
+    on-click="_handleShowHide"
+    class$="showHide [[_computeShowOptional(_optionalLabels.*)]]"
+  >
+    <div class="title">Other labels</div>
+    <div class="value">
+      <iron-icon
+        id="showHide"
+        icon="[[_computeShowHideIcon(_showOptionalLabels)]]"
+      >
+      </iron-icon>
+    </div>
+  </section>
+  <template is="dom-repeat" items="[[_optionalLabels]]">
+    <section class$="optional [[_computeSectionClass(_showOptionalLabels)]]">
+      <div class="title">
+        <span class$="status [[item.style]]">
+          <template is="dom-if" if="[[item.icon]]">
+            <iron-icon class="icon" icon="[[item.icon]]"></iron-icon>
+          </template>
+          <template is="dom-if" if="[[!item.icon]]">
+            <span>[[_computeLabelValue(item.labelInfo.value)]]</span>
+          </template>
+        </span>
+        <gr-limited-text
+          class="name"
+          limit="40"
+          text="[[item.label]]"
+        ></gr-limited-text>
+      </div>
+      <div class="value">
+        <gr-label-info
+          change="{{change}}"
+          account="[[account]]"
+          mutable="[[mutable]]"
+          label="[[item.label]]"
+          label-info="[[item.labelInfo]]"
+        ></gr-label-info>
+      </div>
+    </section>
+  </template>
+  <section
+    class$="spacer [[_computeShowOptional(_optionalLabels.*)]] [[_computeSectionClass(_showOptionalLabels)]]"
+  ></section>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html
index 7c8deb3..e100f91 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-requirements</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 d770ddd..8653bff 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
@@ -14,11 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/paper-tabs/paper-tabs.js';
 import '../../../styles/shared-styles.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../diff/gr-comment-api/gr-comment-api.js';
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
 import '../../plugins/gr-endpoint-param/gr-endpoint-param.js';
@@ -69,6 +66,7 @@
 import {PrimaryTabs, SecondaryTabs} from '../../../constants/constants.js';
 import {NO_ROBOT_COMMENTS_THREADS_MSG} from '../../../constants/messages.js';
 import {appContext} from '../../../services/app-context.js';
+import {ExperimentIds} from '../../../services/flags.js';
 
 const CHANGE_ID_ERROR = {
   MISMATCH: 'mismatch',
@@ -131,7 +129,7 @@
 /**
  * @appliesMixin RESTClientMixin
  * @appliesMixin PatchSetMixin
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeView extends mixinBehaviors( [
   KeyboardShortcutBehavior,
@@ -449,6 +447,7 @@
   constructor() {
     super();
     this.flagsService = appContext.flagsService;
+    this.reporting = appContext.reportingService;
   }
 
   /** @override */
@@ -539,7 +538,7 @@
   }
 
   _isChangeLogExperimentEnabled() {
-    return this.flagsService.isEnabled('UiFeature__cleaner_changelog');
+    return this.flagsService.isEnabled(ExperimentIds.CLEANER_CHANGELOG);
   }
 
   get messagesList() {
@@ -626,7 +625,7 @@
     }
     if (paperTabs.selected !== activeIndex) {
       paperTabs.selected = activeIndex;
-      this.$.reporting.reportInteraction('show-tab', {tabName});
+      this.reporting.reportInteraction('show-tab', {tabName});
     }
     return tabName;
   }
@@ -978,7 +977,7 @@
   _handleReplySent(e) {
     this.addEventListener('change-details-loaded',
         () => {
-          this.$.reporting.timeEnd(SEND_REPLY_TIMING_LABEL);
+          this.reporting.timeEnd(SEND_REPLY_TIMING_LABEL);
         }, {once: true});
     this.$.replyOverlay.close();
     this._reload();
@@ -1745,8 +1744,8 @@
   _reload(opt_isLocationChange) {
     this._loading = true;
     this._relatedChangesCollapsed = true;
-    this.$.reporting.time(CHANGE_RELOAD_TIMING_LABEL);
-    this.$.reporting.time(CHANGE_DATA_TIMING_LABEL);
+    this.reporting.time(CHANGE_RELOAD_TIMING_LABEL);
+    this.reporting.time(CHANGE_DATA_TIMING_LABEL);
 
     // Array to house all promises related to data requests.
     const allDataPromises = [];
@@ -1765,9 +1764,9 @@
               {bubbles: true, composed: true}));
         })
         .then(() => {
-          this.$.reporting.timeEnd(CHANGE_RELOAD_TIMING_LABEL);
+          this.reporting.timeEnd(CHANGE_RELOAD_TIMING_LABEL);
           if (opt_isLocationChange) {
-            this.$.reporting.changeDisplayed();
+            this.reporting.changeDisplayed();
           }
         });
 
@@ -1837,9 +1836,9 @@
     }
 
     Promise.all(allDataPromises).then(() => {
-      this.$.reporting.timeEnd(CHANGE_DATA_TIMING_LABEL);
+      this.reporting.timeEnd(CHANGE_DATA_TIMING_LABEL);
       if (opt_isLocationChange) {
-        this.$.reporting.changeFullyLoaded();
+        this.reporting.changeFullyLoaded();
       }
     });
 
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.js
index 13ffc02..58d4601 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.js
@@ -17,519 +17,780 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .container:not(.loading) {
-        background-color: var(--background-color-tertiary);
+  <style include="shared-styles">
+    .container:not(.loading) {
+      background-color: var(--background-color-tertiary);
+    }
+    .container.loading {
+      color: var(--deemphasized-text-color);
+      padding: var(--spacing-l);
+    }
+    .header {
+      align-items: center;
+      background-color: var(--background-color-primary);
+      border-bottom: 1px solid var(--border-color);
+      display: flex;
+      padding: var(--spacing-s) var(--spacing-l);
+      z-index: 99; /* Less than gr-overlay's backdrop */
+    }
+    .header.editMode {
+      background-color: var(--edit-mode-background-color);
+    }
+    .header .download {
+      margin-right: var(--spacing-l);
+    }
+    gr-change-status {
+      display: initial;
+      margin-left: var(--spacing-s);
+    }
+    gr-change-status:first-child {
+      margin-left: 0;
+    }
+    .headerTitle {
+      align-items: center;
+      display: flex;
+      flex: 1;
+    }
+    .headerSubject {
+      font-family: var(--header-font-family);
+      font-size: var(--font-size-h3);
+      font-weight: var(--font-weight-h3);
+      line-height: var(--line-height-h3);
+      margin-left: var(--spacing-l);
+    }
+    .changeNumberColon {
+      color: transparent;
+    }
+    .changeCopyClipboard {
+      margin-left: var(--spacing-s);
+    }
+    #replyBtn {
+      margin-bottom: var(--spacing-l);
+    }
+    gr-change-star {
+      margin-left: var(--spacing-s);
+      --gr-change-star-size: var(--line-height-normal);
+    }
+    a.changeNumber {
+      margin-left: var(--spacing-xs);
+    }
+    gr-reply-dialog {
+      width: 60em;
+    }
+    .changeStatus {
+      text-transform: capitalize;
+    }
+    /* Strong specificity here is needed due to
+         https://github.com/Polymer/polymer/issues/2531 */
+    .container .changeInfo {
+      display: flex;
+      background-color: var(--background-color-secondary);
+    }
+    section {
+      background-color: var(--view-background-color);
+      box-shadow: var(--elevation-level-1);
+    }
+    .changeId {
+      color: var(--deemphasized-text-color);
+      font-family: var(--font-family);
+      margin-top: var(--spacing-l);
+    }
+    .changeMetadata {
+      /* Limit meta section to half of the screen at max */
+      max-width: 50%;
+    }
+    .commitMessage {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      margin-right: var(--spacing-l);
+      margin-bottom: var(--spacing-l);
+      /* Account for border and padding and rounding errors. */
+      max-width: calc(72ch + 2px + 2 * var(--spacing-m) + 0.4px);
+    }
+    .commitMessage gr-linked-text {
+      word-break: break-word;
+    }
+    #commitMessageEditor {
+      /* Account for border and padding and rounding errors. */
+      min-width: calc(72ch + 2px + 2 * var(--spacing-m) + 0.4px);
+    }
+    .editCommitMessage {
+      margin-top: var(--spacing-l);
+
+      --gr-button: {
+        padding: 5px 0px;
       }
-      .container.loading {
-        color: var(--deemphasized-text-color);
-        padding: var(--spacing-l);
+    }
+    .changeStatuses,
+    .commitActions,
+    .statusText {
+      align-items: center;
+      display: flex;
+    }
+    .changeStatuses {
+      flex-wrap: wrap;
+    }
+    .mainChangeInfo {
+      display: flex;
+      flex: 1;
+      flex-direction: column;
+      min-width: 0;
+    }
+    #commitAndRelated {
+      align-content: flex-start;
+      display: flex;
+      flex: 1;
+      overflow-x: hidden;
+    }
+    .relatedChanges {
+      flex: 1 1 auto;
+      overflow: hidden;
+      padding: var(--spacing-l) 0;
+    }
+    .mobile {
+      display: none;
+    }
+    .warning {
+      color: var(--error-text-color);
+    }
+    hr {
+      border: 0;
+      border-top: 1px solid var(--border-color);
+      height: 0;
+      margin-bottom: var(--spacing-l);
+    }
+    #relatedChanges.collapsed {
+      margin-bottom: var(--spacing-l);
+      max-height: var(--relation-chain-max-height, 2em);
+      overflow: hidden;
+    }
+    .commitContainer {
+      display: flex;
+      flex-direction: column;
+      flex-shrink: 0;
+      margin: var(--spacing-l) 0;
+      padding: 0 var(--spacing-l);
+    }
+    #startReviewBtn {
+      margin-left: var(--spacing-s);
+    }
+    .collapseToggleContainer {
+      display: flex;
+      margin-bottom: 8px;
+    }
+    #relatedChangesToggle {
+      display: none;
+    }
+    #relatedChangesToggle.showToggle {
+      display: flex;
+    }
+    .collapseToggleContainer gr-button {
+      display: block;
+    }
+    #relatedChangesToggle {
+      margin-left: var(--spacing-l);
+      padding-top: var(--related-change-btn-top-padding, 0);
+    }
+    .showOnEdit {
+      display: none;
+    }
+    .scrollable {
+      overflow: auto;
+    }
+    .text {
+      white-space: pre;
+    }
+    gr-commit-info {
+      display: inline-block;
+    }
+    paper-tabs {
+      background-color: var(--background-color-tertiary);
+      margin-top: var(--spacing-m);
+      height: calc(var(--line-height-h3) + var(--spacing-m));
+      --paper-tabs-selection-bar-color: var(--link-color);
+    }
+    paper-tab {
+      box-sizing: border-box;
+      max-width: 12em;
+      --paper-tab-ink: var(--link-color);
+    }
+    gr-thread-list,
+    gr-messages-list,
+    gr-messages-list-experimental {
+      display: block;
+    }
+    gr-thread-list {
+      min-height: 250px;
+    }
+    #includedInOverlay {
+      width: 65em;
+    }
+    #uploadHelpOverlay {
+      width: 50em;
+    }
+    #metadata {
+      --metadata-horizontal-padding: var(--spacing-l);
+      padding-top: var(--spacing-l);
+      width: 100%;
+    }
+    /* NOTE: If you update this breakpoint, also update the
+      BREAKPOINT_RELATED_MED in the JS */
+    @media screen and (max-width: 75em) {
+      .relatedChanges {
+        padding: 0;
+      }
+      #relatedChanges {
+        padding-top: var(--spacing-l);
+      }
+      #commitAndRelated {
+        flex-direction: column;
+        flex-wrap: nowrap;
+      }
+      #commitMessageEditor {
+        min-width: 0;
+      }
+      .commitMessage {
+        margin-right: 0;
+      }
+      .mainChangeInfo {
+        padding-right: 0;
+      }
+    }
+    /* NOTE: If you update this breakpoint, also update the
+      BREAKPOINT_RELATED_SMALL in the JS */
+    @media screen and (max-width: 50em) {
+      .mobile {
+        display: block;
       }
       .header {
-        align-items: center;
-        background-color: var(--background-color-primary);
-        border-bottom: 1px solid var(--border-color);
-        display: flex;
+        align-items: flex-start;
+        flex-direction: column;
+        flex: 1;
         padding: var(--spacing-s) var(--spacing-l);
-        z-index: 99;  /* Less than gr-overlay's backdrop */
       }
-      .header.editMode {
-        background-color: var(--edit-mode-background-color);
-      }
-      .header .download {
-        margin-right: var(--spacing-l);
-      }
-      gr-change-status {
-        display: initial;
-        margin-left: var(--spacing-s);
-      }
-      gr-change-status:first-child {
-        margin-left: 0;
+      gr-change-star {
+        vertical-align: middle;
       }
       .headerTitle {
-        align-items: center;
-        display: flex;
-        flex: 1;
-      }
-      .headerSubject {
+        flex-wrap: wrap;
         font-family: var(--header-font-family);
         font-size: var(--font-size-h3);
         font-weight: var(--font-weight-h3);
         line-height: var(--line-height-h3);
-        margin-left: var(--spacing-l);
       }
-      .changeNumberColon {
-        color: transparent;
-      }
-      .changeCopyClipboard {
-        margin-left: var(--spacing-s);
-      }
-      #replyBtn {
-        margin-bottom: var(--spacing-l);
-      }
-      gr-change-star {
-        margin-left: var(--spacing-s);
-        --gr-change-star-size: var(--line-height-normal);
-      }
-      a.changeNumber {
-        margin-left: var(--spacing-xs);
-      }
-      gr-reply-dialog {
-        width: 60em;
-      }
-      .changeStatus {
-        text-transform: capitalize;
-      }
-      /* Strong specificity here is needed due to
-         https://github.com/Polymer/polymer/issues/2531 */
-      .container .changeInfo {
-        display: flex;
-        background-color: var(--background-color-secondary);
-      }
-      section {
-        background-color: var(--view-background-color);
-        box-shadow: var(--elevation-level-1);
-      }
-      .changeId {
-        color: var(--deemphasized-text-color);
-        font-family: var(--font-family);
-        margin-top: var(--spacing-l);
-      }
-      .changeMetadata {
-        /* Limit meta section to half of the screen at max */
-        max-width: 50%;
-      }
-      .commitMessage {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        margin-right: var(--spacing-l);
-        margin-bottom: var(--spacing-l);
-        /* Account for border and padding and rounding errors. */
-        max-width: calc(72ch + 2px + 2*var(--spacing-m) + 0.4px);
-      }
-      .commitMessage gr-linked-text {
-        word-break: break-word;
-      }
-      #commitMessageEditor {
-        /* Account for border and padding and rounding errors. */
-        min-width: calc(72ch + 2px + 2*var(--spacing-m) + 0.4px);
-      }
-      .editCommitMessage {
-        margin-top: var(--spacing-l);
-
-        --gr-button: {
-          padding: 5px 0px;
-        }
-      }
-      .changeStatuses,
-      .commitActions,
-      .statusText {
-        align-items: center;
-        display: flex;
-      }
-      .changeStatuses {
-        flex-wrap: wrap;
-      }
-      .mainChangeInfo {
-        display: flex;
-        flex: 1;
-        flex-direction: column;
-        min-width: 0;
-      }
-      #commitAndRelated {
-        align-content: flex-start;
-        display: flex;
-        flex: 1;
-        overflow-x: hidden;
-      }
-      .relatedChanges {
-        flex: 1 1 auto;
-        overflow: hidden;
-        padding: var(--spacing-l) 0;
-      }
-      .mobile {
+      .desktop {
         display: none;
       }
-      .warning {
-        color: var(--error-text-color);
+      .reply {
+        display: block;
+        margin-right: 0;
+        /* px because don't have the same font size */
+        margin-bottom: 6px;
       }
-      hr {
-        border: 0;
-        border-top: 1px solid var(--border-color);
-        height: 0;
-        margin-bottom: var(--spacing-l);
+      .changeInfo-column:not(:last-of-type) {
+        margin-right: 0;
+        padding-right: 0;
       }
-      #relatedChanges.collapsed {
-        margin-bottom: var(--spacing-l);
-        max-height: var(--relation-chain-max-height, 2em);
-        overflow: hidden;
+      .changeInfo,
+      #commitAndRelated {
+        flex-direction: column;
+        flex-wrap: nowrap;
       }
       .commitContainer {
-        display: flex;
-        flex-direction: column;
-        flex-shrink: 0;
-        margin: var(--spacing-l) 0;
-        padding: 0 var(--spacing-l);
+        margin: 0;
+        padding: var(--spacing-l);
       }
-      #startReviewBtn {
-        margin-left: var(--spacing-s);
+      .changeMetadata {
+        margin-top: var(--spacing-xs);
+        max-width: none;
       }
-      .collapseToggleContainer {
-        display: flex;
-        margin-bottom: 8px;
+      #metadata,
+      .mainChangeInfo {
+        padding: 0;
       }
-      #relatedChangesToggle {
-        display: none;
-      }
-      #relatedChangesToggle.showToggle {
-        display: flex;
-      }
-      .collapseToggleContainer gr-button {
+      .commitActions {
         display: block;
-      }
-      #relatedChangesToggle {
-        margin-left: var(--spacing-l);
-        padding-top: var(--related-change-btn-top-padding, 0);
-      }
-      .showOnEdit {
-        display: none;
-      }
-      .scrollable {
-        overflow: auto;
-      }
-      .text {
-        white-space: pre;
-      }
-      gr-commit-info {
-        display: inline-block;
-      }
-      paper-tabs {
-        background-color: var(--background-color-tertiary);
-        margin-top: var(--spacing-m);
-        height: calc(var(--line-height-h3) + var(--spacing-m));
-        --paper-tabs-selection-bar-color: var(--link-color);
-      }
-      paper-tab {
-        box-sizing: border-box;
-        max-width: 12em;
-        --paper-tab-ink: var(--link-color);
-      }
-      gr-thread-list,
-      gr-messages-list,
-      gr-messages-list-experimental {
-        display: block;
-      }
-      gr-thread-list {
-        min-height: 250px;
-      }
-      #includedInOverlay {
-        width: 65em;
-      }
-      #uploadHelpOverlay {
-        width: 50em;
-      }
-      #metadata {
-        --metadata-horizontal-padding: var(--spacing-l);
-        padding-top: var(--spacing-l);
+        margin-top: var(--spacing-l);
         width: 100%;
       }
-      /* NOTE: If you update this breakpoint, also update the
-      BREAKPOINT_RELATED_MED in the JS */
-      @media screen and (max-width: 75em) {
-        .relatedChanges {
-          padding: 0;
-        }
-        #relatedChanges {
-          padding-top: var(--spacing-l);
-        }
-        #commitAndRelated {
-          flex-direction: column;
-          flex-wrap: nowrap;
-        }
-        #commitMessageEditor {
-          min-width: 0;
-        }
-        .commitMessage {
-          margin-right: 0;
-        }
-        .mainChangeInfo {
-          padding-right: 0;
-        }
+      .commitMessage {
+        flex: initial;
+        margin: 0;
       }
-      /* NOTE: If you update this breakpoint, also update the
-      BREAKPOINT_RELATED_SMALL in the JS */
-      @media screen and (max-width: 50em) {
-        .mobile {
-          display: block;
-        }
-        .header {
-          align-items: flex-start;
-          flex-direction: column;
-          flex: 1;
-          padding: var(--spacing-s) var(--spacing-l);
-        }
-        gr-change-star {
-          vertical-align: middle;
-        }
-        .headerTitle {
-          flex-wrap: wrap;
-          font-family: var(--header-font-family);
-          font-size: var(--font-size-h3);
-          font-weight: var(--font-weight-h3);
-          line-height: var(--line-height-h3);
-        }
-        .desktop {
-          display: none;
-        }
-        .reply {
-          display: block;
-          margin-right: 0;
-          /* px because don't have the same font size */
-          margin-bottom: 6px;
-        }
-        .changeInfo-column:not(:last-of-type) {
-          margin-right: 0;
-          padding-right: 0;
-        }
-        .changeInfo,
-        #commitAndRelated {
-          flex-direction: column;
-          flex-wrap: nowrap;
-        }
-        .commitContainer {
-          margin: 0;
-          padding: var(--spacing-l);
-        }
-        .changeMetadata {
-          margin-top: var(--spacing-xs);
-          max-width: none;
-        }
-        #metadata,
-        .mainChangeInfo {
-          padding: 0;
-        }
-        .commitActions {
-          display: block;
-          margin-top: var(--spacing-l);
-          width: 100%;
-        }
-        .commitMessage {
-          flex: initial;
-          margin: 0;
-        }
-        /* Change actions are the only thing thant need to remain visible due
+      /* Change actions are the only thing thant need to remain visible due
         to the fact that they may have the currently visible overlay open. */
-        #mainContent.overlayOpen .hideOnMobileOverlay {
-          display: none;
-        }
-        gr-reply-dialog {
-          height: 100vh;
-          min-width: initial;
-          width: 100vw;
-        }
-        #replyOverlay {
-          z-index: var(--reply-overlay-z-index);
-        }
+      #mainContent.overlayOpen .hideOnMobileOverlay {
+        display: none;
       }
-      .patch-set-dropdown {
-        margin: var(--spacing-m) 0 0 var(--spacing-m);
+      gr-reply-dialog {
+        height: 100vh;
+        min-width: initial;
+        width: 100vw;
       }
-      .show-robot-comments {
-        margin: var(--spacing-m);
+      #replyOverlay {
+        z-index: var(--reply-overlay-z-index);
       }
-    </style>
-    <div class="container loading" hidden\$="[[!_loading]]">Loading...</div>
-    <!-- TODO(taoalpha): remove on-show-checks-table,
+    }
+    .patch-set-dropdown {
+      margin: var(--spacing-m) 0 0 var(--spacing-m);
+    }
+    .show-robot-comments {
+      margin: var(--spacing-m);
+    }
+  </style>
+  <div class="container loading" hidden$="[[!_loading]]">Loading...</div>
+  <!-- TODO(taoalpha): remove on-show-checks-table,
     Gerrit should not have any thing too special for a plugin,
     replace with a generic event: show-primary-tab. -->
-    <div
-      id="mainContent"
-      class="container"
-      on-show-checks-table="_setActivePrimaryTab"
-      hidden\$="{{_loading}}">
-      <section class="changeInfoSection">
-        <div class\$="[[_computeHeaderClass(_editMode)]]">
-          <div class="headerTitle">
-            <div class="changeStatuses">
-              <template is="dom-repeat" items="[[_changeStatuses]]" as="status">
-                <gr-change-status max-width="100" status="[[status]]"></gr-change-status>
-              </template>
-            </div>
-            <div class="statusText">
-              <template is="dom-if" if="[[_computeShowCommitInfo(_changeStatus, _change.current_revision)]]">
-                <span class="text"> as </span>
-                <gr-commit-info change="[[_change]]" commit-info="[[_computeMergedCommitInfo(_change.current_revision, _change.revisions)]]" server-config="[[_serverConfig]]"></gr-commit-info>
-              </template>
-            </div>
-            <gr-change-star id="changeStar" change="{{_change}}" on-toggle-star="_handleToggleStar" hidden\$="[[!_loggedIn]]"></gr-change-star>
-
-            <a class="changeNumber" aria-label\$="[[_computeChangePermalinkAriaLabel(_change._number)]]" href\$="[[_computeChangeUrl(_change)]]">[[_change._number]]</a>
-            <span class="changeNumberColon">:&nbsp;</span>
-            <span class="headerSubject">[[_change.subject]]</span>
-            <gr-copy-clipboard class="changeCopyClipboard" hide-input="" text="[[_computeCopyTextForTitle(_change)]]">
-            </gr-copy-clipboard>
-          </div><!-- end headerTitle -->
-          <div class="commitActions" hidden\$="[[!_loggedIn]]">
-            <gr-change-actions id="actions" change="[[_change]]" disable-edit="[[disableEdit]]" has-parent="[[hasParent]]" actions="[[_change.actions]]" revision-actions="{{_currentRevisionActions}}" change-num="[[_changeNum]]" change-status="[[_change.status]]" commit-num="[[_commitInfo.commit]]" latest-patch-num="[[computeLatestPatchNum(_allPatchSets)]]" commit-message="[[_latestCommitMessage]]" edit-patchset-loaded="[[hasEditPatchsetLoaded(_patchRange.*)]]" edit-mode="[[_editMode]]" edit-based-on-current-patch-set="[[hasEditBasedOnCurrentPatchSet(_allPatchSets)]]" private-by-default="[[_projectConfig.private_by_default]]" on-reload-change="_handleReloadChange" on-edit-tap="_handleEditTap" on-stop-edit-tap="_handleStopEditTap" on-download-tap="_handleOpenDownloadDialog"></gr-change-actions>
-          </div><!-- end commit actions -->
-        </div><!-- end header -->
-        <div class="changeInfo">
-          <div class="changeInfo-column changeMetadata hideOnMobileOverlay">
-            <gr-change-metadata id="metadata" change="{{_change}}" account="[[_account]]" revision="[[_selectedRevision]]" commit-info="[[_commitInfo]]" server-config="[[_serverConfig]]" parent-is-current="[[_parentIsCurrent]]" on-show-reply-dialog="_handleShowReplyDialog">
-            </gr-change-metadata>
+  <div
+    id="mainContent"
+    class="container"
+    on-show-checks-table="_setActivePrimaryTab"
+    hidden$="{{_loading}}"
+  >
+    <section class="changeInfoSection">
+      <div class$="[[_computeHeaderClass(_editMode)]]">
+        <div class="headerTitle">
+          <div class="changeStatuses">
+            <template is="dom-repeat" items="[[_changeStatuses]]" as="status">
+              <gr-change-status
+                max-width="100"
+                status="[[status]]"
+              ></gr-change-status>
+            </template>
           </div>
-          <div id="mainChangeInfo" class="changeInfo-column mainChangeInfo">
-            <div id="commitAndRelated" class="hideOnMobileOverlay">
-              <div class="commitContainer">
-                <div>
-                  <gr-button id="replyBtn" class="reply" title="[[createTitle(Shortcut.OPEN_REPLY_DIALOG,
-                        ShortcutSection.ACTIONS)]]" hidden\$="[[!_loggedIn]]" primary="" disabled="[[_replyDisabled]]" on-click="_handleReplyTap">[[_replyButtonLabel]]</gr-button>
-                </div>
-                <div id="commitMessage" class="commitMessage">
-                  <gr-editable-content id="commitMessageEditor" editing="[[_editingCommitMessage]]" content="{{_latestCommitMessage}}" storage-key="[[_computeCommitMessageKey(_change._number, _change.current_revision)]]" remove-zero-width-space="" collapsed\$="[[_computeCommitMessageCollapsed(_commitCollapsed, _commitCollapsible)]]">
-                    <gr-linked-text pre="" content="[[_latestCommitMessage]]" config="[[_projectConfig.commentlinks]]" remove-zero-width-space=""></gr-linked-text>
-                  </gr-editable-content>
-                  <gr-button link="" class="editCommitMessage" on-click="_handleEditCommitMessage" hidden\$="[[_hideEditCommitMessage]]">Edit</gr-button>
-                  <div class="changeId" hidden\$="[[!_changeIdCommitMessageError]]">
-                    <hr>
-                    Change-Id:
-                    <span class\$="[[_computeChangeIdClass(_changeIdCommitMessageError)]]" title\$="[[_computeTitleAttributeWarning(_changeIdCommitMessageError)]]">
-                      [[_change.change_id]]
-                    </span>
-                  </div>
-                </div>
-                <div id="commitCollapseToggle" class="collapseToggleContainer" hidden\$="[[!_commitCollapsible]]">
-                  <gr-button link="" id="commitCollapseToggleButton" class="collapseToggleButton" on-click="_toggleCommitCollapsed">
-                    [[_computeCollapseText(_commitCollapsed)]]
-                  </gr-button>
-                </div>
-                <gr-endpoint-decorator name="commit-container">
-                  <gr-endpoint-param name="change" value="[[_change]]">
-                  </gr-endpoint-param>
-                  <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
-                  </gr-endpoint-param>
-                </gr-endpoint-decorator>
-              </div>
-              <div class="relatedChanges">
-                <gr-related-changes-list id="relatedChanges" class\$="[[_computeRelatedChangesClass(_relatedChangesCollapsed)]]" change="[[_change]]" mergeable="[[_mergeable]]" has-parent="{{hasParent}}" on-update="_updateRelatedChangeMaxHeight" patch-num="[[computeLatestPatchNum(_allPatchSets)]]" on-new-section-loaded="_computeShowRelatedToggle">
-                </gr-related-changes-list>
-                <div id="relatedChangesToggle" class="collapseToggleContainer">
-                  <gr-button link="" id="relatedChangesToggleButton" class="collapseToggleButton" on-click="_toggleRelatedChangesCollapsed">
-                    [[_computeCollapseText(_relatedChangesCollapsed)]]
-                  </gr-button>
-                </div>
-              </div>
-            </div>
+          <div class="statusText">
+            <template
+              is="dom-if"
+              if="[[_computeShowCommitInfo(_changeStatus, _change.current_revision)]]"
+            >
+              <span class="text"> as </span>
+              <gr-commit-info
+                change="[[_change]]"
+                commit-info="[[_computeMergedCommitInfo(_change.current_revision, _change.revisions)]]"
+                server-config="[[_serverConfig]]"
+              ></gr-commit-info>
+            </template>
           </div>
+          <gr-change-star
+            id="changeStar"
+            change="{{_change}}"
+            on-toggle-star="_handleToggleStar"
+            hidden$="[[!_loggedIn]]"
+          ></gr-change-star>
+
+          <a
+            class="changeNumber"
+            aria-label$="[[_computeChangePermalinkAriaLabel(_change._number)]]"
+            href$="[[_computeChangeUrl(_change)]]"
+            >[[_change._number]]</a
+          >
+          <span class="changeNumberColon">:&nbsp;</span>
+          <span class="headerSubject">[[_change.subject]]</span>
+          <gr-copy-clipboard
+            class="changeCopyClipboard"
+            hide-input=""
+            text="[[_computeCopyTextForTitle(_change)]]"
+          >
+          </gr-copy-clipboard>
         </div>
-      </section>
-
-      <paper-tabs id="primaryTabs" on-selected-changed="_setActivePrimaryTab">
-        <paper-tab data-name\$="[[_constants.PrimaryTabs.FILES]]">Files</paper-tab>
-        <template is="dom-repeat" items="[[_dynamicTabHeaderEndpoints]]" as="tabHeader">
-          <paper-tab data-name\$="[[tabHeader]]">
-              <gr-endpoint-decorator name\$="[[tabHeader]]">
-                  <gr-endpoint-param name="change" value="[[_change]]">
-                  </gr-endpoint-param>
-                  <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
-                  </gr-endpoint-param>
-              </gr-endpoint-decorator>
-          </paper-tab>
-        </template>
-        <paper-tab data-name\$="[[_constants.PrimaryTabs.FINDINGS]]">
-          Findings
-        </paper-tab>
-      </paper-tabs>
-
-      <section class="patchInfo">
-        <div hidden\$="[[!_isTabActive(_constants.PrimaryTabs.FILES, _activeTabs)]]">
-          <gr-file-list-header id="fileListHeader" account="[[_account]]" all-patch-sets="[[_allPatchSets]]" change="[[_change]]" change-num="[[_changeNum]]" revision-info="[[_revisionInfo]]" change-comments="[[_changeComments]]" commit-info="[[_commitInfo]]" change-url="[[_computeChangeUrl(_change)]]" edit-mode="[[_editMode]]" logged-in="[[_loggedIn]]" server-config="[[_serverConfig]]" shown-file-count="[[_shownFileCount]]" diff-prefs="[[_diffPrefs]]" diff-view-mode="{{viewState.diffMode}}" patch-num="{{_patchRange.patchNum}}" base-patch-num="{{_patchRange.basePatchNum}}" files-expanded="[[_filesExpanded]]" diff-prefs-disabled="[[_diffPrefsDisabled]]" on-open-diff-prefs="_handleOpenDiffPrefs" on-open-download-dialog="_handleOpenDownloadDialog" on-open-upload-help-dialog="_handleOpenUploadHelpDialog" on-open-included-in-dialog="_handleOpenIncludedInDialog" on-expand-diffs="_expandAllDiffs" on-collapse-diffs="_collapseAllDiffs">
-          </gr-file-list-header>
-          <gr-file-list id="fileList" class="hideOnMobileOverlay" diff-prefs="{{_diffPrefs}}" change="[[_change]]" change-num="[[_changeNum]]" patch-range="{{_patchRange}}" change-comments="[[_changeComments]]" drafts="[[_diffDrafts]]" revisions="[[_change.revisions]]" project-config="[[_projectConfig]]" selected-index="{{viewState.selectedFileIndex}}" diff-view-mode="[[viewState.diffMode]]" edit-mode="[[_editMode]]" num-files-shown="{{_numFilesShown}}" files-expanded="{{_filesExpanded}}" file-list-increment="{{_numFilesShown}}" on-files-shown-changed="_setShownFiles" on-file-action-tap="_handleFileActionTap" on-reload-drafts="_reloadDraftsWithCallback">
-          </gr-file-list>
-        </div>
-
-        <template is="dom-if" if="[[_isTabActive(_constants.PrimaryTabs.FINDINGS, _activeTabs)]]">
-          <gr-dropdown-list class="patch-set-dropdown" items="[[_robotCommentsPatchSetDropdownItems]]" on-value-change="_handleRobotCommentPatchSetChanged" value="[[_currentRobotCommentsPatchSet]]">
-          </gr-dropdown-list>
-          <gr-thread-list
-            threads="[[_robotCommentThreads]]"
+        <!-- end headerTitle -->
+        <div class="commitActions" hidden$="[[!_loggedIn]]">
+          <gr-change-actions
+            id="actions"
             change="[[_change]]"
+            disable-edit="[[disableEdit]]"
+            has-parent="[[hasParent]]"
+            actions="[[_change.actions]]"
+            revision-actions="{{_currentRevisionActions}}"
             change-num="[[_changeNum]]"
-            logged-in="[[_loggedIn]]"
-            hide-toggle-buttons
-            empty-thread-msg="[[_messages.NO_ROBOT_COMMENTS_THREADS_MSG]]"
-            on-thread-list-modified="_handleReloadDiffComments">
-          </gr-thread-list>
-          <template is="dom-if" if="[[_showRobotCommentsButton]]">
-            <gr-button class="show-robot-comments" on-click="_toggleShowRobotComments">
-              [[_computeShowText(_showAllRobotComments)]]
-            </gr-button>
-          </template>
-        </template>
+            change-status="[[_change.status]]"
+            commit-num="[[_commitInfo.commit]]"
+            latest-patch-num="[[computeLatestPatchNum(_allPatchSets)]]"
+            commit-message="[[_latestCommitMessage]]"
+            edit-patchset-loaded="[[hasEditPatchsetLoaded(_patchRange.*)]]"
+            edit-mode="[[_editMode]]"
+            edit-based-on-current-patch-set="[[hasEditBasedOnCurrentPatchSet(_allPatchSets)]]"
+            private-by-default="[[_projectConfig.private_by_default]]"
+            on-reload-change="_handleReloadChange"
+            on-edit-tap="_handleEditTap"
+            on-stop-edit-tap="_handleStopEditTap"
+            on-download-tap="_handleOpenDownloadDialog"
+          ></gr-change-actions>
+        </div>
+        <!-- end commit actions -->
+      </div>
+      <!-- end header -->
+      <div class="changeInfo">
+        <div class="changeInfo-column changeMetadata hideOnMobileOverlay">
+          <gr-change-metadata
+            id="metadata"
+            change="{{_change}}"
+            account="[[_account]]"
+            revision="[[_selectedRevision]]"
+            commit-info="[[_commitInfo]]"
+            server-config="[[_serverConfig]]"
+            parent-is-current="[[_parentIsCurrent]]"
+            on-show-reply-dialog="_handleShowReplyDialog"
+          >
+          </gr-change-metadata>
+        </div>
+        <div id="mainChangeInfo" class="changeInfo-column mainChangeInfo">
+          <div id="commitAndRelated" class="hideOnMobileOverlay">
+            <div class="commitContainer">
+              <div>
+                <gr-button
+                  id="replyBtn"
+                  class="reply"
+                  title="[[createTitle(Shortcut.OPEN_REPLY_DIALOG,
+                        ShortcutSection.ACTIONS)]]"
+                  hidden$="[[!_loggedIn]]"
+                  primary=""
+                  disabled="[[_replyDisabled]]"
+                  on-click="_handleReplyTap"
+                  >[[_replyButtonLabel]]</gr-button
+                >
+              </div>
+              <div id="commitMessage" class="commitMessage">
+                <gr-editable-content
+                  id="commitMessageEditor"
+                  editing="[[_editingCommitMessage]]"
+                  content="{{_latestCommitMessage}}"
+                  storage-key="[[_computeCommitMessageKey(_change._number, _change.current_revision)]]"
+                  remove-zero-width-space=""
+                  collapsed$="[[_computeCommitMessageCollapsed(_commitCollapsed, _commitCollapsible)]]"
+                >
+                  <gr-linked-text
+                    pre=""
+                    content="[[_latestCommitMessage]]"
+                    config="[[_projectConfig.commentlinks]]"
+                    remove-zero-width-space=""
+                  ></gr-linked-text>
+                </gr-editable-content>
+                <gr-button
+                  link=""
+                  class="editCommitMessage"
+                  on-click="_handleEditCommitMessage"
+                  hidden$="[[_hideEditCommitMessage]]"
+                  >Edit</gr-button
+                >
+                <div
+                  class="changeId"
+                  hidden$="[[!_changeIdCommitMessageError]]"
+                >
+                  <hr />
+                  Change-Id:
+                  <span
+                    class$="[[_computeChangeIdClass(_changeIdCommitMessageError)]]"
+                    title$="[[_computeTitleAttributeWarning(_changeIdCommitMessageError)]]"
+                  >
+                    [[_change.change_id]]
+                  </span>
+                </div>
+              </div>
+              <div
+                id="commitCollapseToggle"
+                class="collapseToggleContainer"
+                hidden$="[[!_commitCollapsible]]"
+              >
+                <gr-button
+                  link=""
+                  id="commitCollapseToggleButton"
+                  class="collapseToggleButton"
+                  on-click="_toggleCommitCollapsed"
+                >
+                  [[_computeCollapseText(_commitCollapsed)]]
+                </gr-button>
+              </div>
+              <gr-endpoint-decorator name="commit-container">
+                <gr-endpoint-param name="change" value="[[_change]]">
+                </gr-endpoint-param>
+                <gr-endpoint-param
+                  name="revision"
+                  value="[[_selectedRevision]]"
+                >
+                </gr-endpoint-param>
+              </gr-endpoint-decorator>
+            </div>
+            <div class="relatedChanges">
+              <gr-related-changes-list
+                id="relatedChanges"
+                class$="[[_computeRelatedChangesClass(_relatedChangesCollapsed)]]"
+                change="[[_change]]"
+                mergeable="[[_mergeable]]"
+                has-parent="{{hasParent}}"
+                on-update="_updateRelatedChangeMaxHeight"
+                patch-num="[[computeLatestPatchNum(_allPatchSets)]]"
+                on-new-section-loaded="_computeShowRelatedToggle"
+              >
+              </gr-related-changes-list>
+              <div id="relatedChangesToggle" class="collapseToggleContainer">
+                <gr-button
+                  link=""
+                  id="relatedChangesToggleButton"
+                  class="collapseToggleButton"
+                  on-click="_toggleRelatedChangesCollapsed"
+                >
+                  [[_computeCollapseText(_relatedChangesCollapsed)]]
+                </gr-button>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
 
-        <template is="dom-if" if="[[_isTabActive(_selectedTabPluginHeader, _activeTabs)]]">
-          <gr-endpoint-decorator name\$="[[_selectedTabPluginEndpoint]]">
+    <paper-tabs id="primaryTabs" on-selected-changed="_setActivePrimaryTab">
+      <paper-tab data-name$="[[_constants.PrimaryTabs.FILES]]">Files</paper-tab>
+      <template
+        is="dom-repeat"
+        items="[[_dynamicTabHeaderEndpoints]]"
+        as="tabHeader"
+      >
+        <paper-tab data-name$="[[tabHeader]]">
+          <gr-endpoint-decorator name$="[[tabHeader]]">
             <gr-endpoint-param name="change" value="[[_change]]">
             </gr-endpoint-param>
             <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
             </gr-endpoint-param>
           </gr-endpoint-decorator>
-        </template>
-      </section>
-
-      <gr-endpoint-decorator name="change-view-integration">
-        <gr-endpoint-param name="change" value="[[_change]]">
-        </gr-endpoint-param>
-        <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
-        </gr-endpoint-param>
-      </gr-endpoint-decorator>
-
-      <paper-tabs id="secondaryTabs" on-selected-changed="_setActiveSecondaryTab">
-        <paper-tab
-          data-name\$="[[_constants.SecondaryTabs.CHANGE_LOG]]"
-          class="changeLog">
-          Change Log
         </paper-tab>
-        <paper-tab
-          data-name\$="[[_constants.SecondaryTabs.COMMENT_THREADS]]"
-          class="commentThreads">
-          <gr-tooltip-content has-tooltip="" title\$="[[_computeTotalCommentCounts(_change.unresolved_comment_count, _changeComments)]]">
-            <span>Comment Threads</span></gr-tooltip-content>
-        </paper-tab>
-      </paper-tabs>
-      <section class="changeLog">
-        <template is="dom-if" if="[[_isTabActive(_constants.SecondaryTabs.CHANGE_LOG, _activeTabs)]]">
-          <template is="dom-if" if="[[!_isChangeLogExperimentEnabled()]]">
-            <gr-messages-list class="hideOnMobileOverlay" change-num="[[_changeNum]]" labels="[[_change.labels]]" messages="[[_change.messages]]" reviewer-updates="[[_change.reviewer_updates]]" change-comments="[[_changeComments]]" project-name="[[_change.project]]" show-reply-buttons="[[_loggedIn]]" on-message-anchor-tap="_handleMessageAnchorTap" on-reply="_handleMessageReply"></gr-messages-list>
-          </template>
-          <template is="dom-if" if="[[_isChangeLogExperimentEnabled()]]">
-            <gr-messages-list-experimental class="hideOnMobileOverlay" change-num="[[_changeNum]]" labels="[[_change.labels]]" messages="[[_change.messages]]" reviewer-updates="[[_change.reviewer_updates]]" change-comments="[[_changeComments]]" project-name="[[_change.project]]" show-reply-buttons="[[_loggedIn]]" on-message-anchor-tap="_handleMessageAnchorTap" on-reply="_handleMessageReply"></gr-messages-list-experimental>
-          </template>
-        </template>
-        <template is="dom-if" if="[[_isTabActive(_constants.SecondaryTabs.COMMENT_THREADS, _activeTabs)]]">
-          <gr-thread-list threads="[[_commentThreads]]" change="[[_change]]" change-num="[[_changeNum]]" logged-in="[[_loggedIn]]" only-show-robot-comments-with-human-reply="" on-thread-list-modified="_handleReloadDiffComments"></gr-thread-list>
-        </template>
-      </section>
-    </div>
+      </template>
+      <paper-tab data-name$="[[_constants.PrimaryTabs.FINDINGS]]">
+        Findings
+      </paper-tab>
+    </paper-tabs>
 
-    <gr-apply-fix-dialog id="applyFixDialog" prefs="[[_diffPrefs]]" change="[[_change]]" change-num="[[_changeNum]]"></gr-apply-fix-dialog>
-    <gr-overlay id="downloadOverlay" with-backdrop="">
-      <gr-download-dialog id="downloadDialog" change="[[_change]]" patch-num="[[_patchRange.patchNum]]" config="[[_serverConfig.download]]" on-close="_handleDownloadDialogClose"></gr-download-dialog>
-    </gr-overlay>
-    <gr-overlay id="uploadHelpOverlay" with-backdrop="">
-      <gr-upload-help-dialog revision="[[_currentRevision]]" target-branch="[[_change.branch]]" on-close="_handleCloseUploadHelpDialog"></gr-upload-help-dialog>
-    </gr-overlay>
-    <gr-overlay id="includedInOverlay" with-backdrop="">
-      <gr-included-in-dialog id="includedInDialog" change-num="[[_changeNum]]" on-close="_handleIncludedInDialogClose"></gr-included-in-dialog>
-    </gr-overlay>
-    <gr-overlay id="replyOverlay" class="scrollable" no-cancel-on-outside-click="" no-cancel-on-esc-key="" with-backdrop="">
-      <gr-reply-dialog id="replyDialog" change="{{_change}}" patch-num="[[computeLatestPatchNum(_allPatchSets)]]" permitted-labels="[[_change.permitted_labels]]" draft-comment-threads="[[_draftCommentThreads]]" project-config="[[_projectConfig]]" can-be-started="[[_canStartReview]]" on-send="_handleReplySent" on-cancel="_handleReplyCancel" on-autogrow="_handleReplyAutogrow" on-send-disabled-changed="_resetReplyOverlayFocusStops" hidden\$="[[!_loggedIn]]">
-      </gr-reply-dialog>
-    </gr-overlay>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-comment-api id="commentAPI"></gr-comment-api>
-    <gr-reporting id="reporting"></gr-reporting>
+    <section class="patchInfo">
+      <div
+        hidden$="[[!_isTabActive(_constants.PrimaryTabs.FILES, _activeTabs)]]"
+      >
+        <gr-file-list-header
+          id="fileListHeader"
+          account="[[_account]]"
+          all-patch-sets="[[_allPatchSets]]"
+          change="[[_change]]"
+          change-num="[[_changeNum]]"
+          revision-info="[[_revisionInfo]]"
+          change-comments="[[_changeComments]]"
+          commit-info="[[_commitInfo]]"
+          change-url="[[_computeChangeUrl(_change)]]"
+          edit-mode="[[_editMode]]"
+          logged-in="[[_loggedIn]]"
+          server-config="[[_serverConfig]]"
+          shown-file-count="[[_shownFileCount]]"
+          diff-prefs="[[_diffPrefs]]"
+          diff-view-mode="{{viewState.diffMode}}"
+          patch-num="{{_patchRange.patchNum}}"
+          base-patch-num="{{_patchRange.basePatchNum}}"
+          files-expanded="[[_filesExpanded]]"
+          diff-prefs-disabled="[[_diffPrefsDisabled]]"
+          on-open-diff-prefs="_handleOpenDiffPrefs"
+          on-open-download-dialog="_handleOpenDownloadDialog"
+          on-open-upload-help-dialog="_handleOpenUploadHelpDialog"
+          on-open-included-in-dialog="_handleOpenIncludedInDialog"
+          on-expand-diffs="_expandAllDiffs"
+          on-collapse-diffs="_collapseAllDiffs"
+        >
+        </gr-file-list-header>
+        <gr-file-list
+          id="fileList"
+          class="hideOnMobileOverlay"
+          diff-prefs="{{_diffPrefs}}"
+          change="[[_change]]"
+          change-num="[[_changeNum]]"
+          patch-range="{{_patchRange}}"
+          change-comments="[[_changeComments]]"
+          drafts="[[_diffDrafts]]"
+          revisions="[[_change.revisions]]"
+          project-config="[[_projectConfig]]"
+          selected-index="{{viewState.selectedFileIndex}}"
+          diff-view-mode="[[viewState.diffMode]]"
+          edit-mode="[[_editMode]]"
+          num-files-shown="{{_numFilesShown}}"
+          files-expanded="{{_filesExpanded}}"
+          file-list-increment="{{_numFilesShown}}"
+          on-files-shown-changed="_setShownFiles"
+          on-file-action-tap="_handleFileActionTap"
+          on-reload-drafts="_reloadDraftsWithCallback"
+        >
+        </gr-file-list>
+      </div>
+
+      <template
+        is="dom-if"
+        if="[[_isTabActive(_constants.PrimaryTabs.FINDINGS, _activeTabs)]]"
+      >
+        <gr-dropdown-list
+          class="patch-set-dropdown"
+          items="[[_robotCommentsPatchSetDropdownItems]]"
+          on-value-change="_handleRobotCommentPatchSetChanged"
+          value="[[_currentRobotCommentsPatchSet]]"
+        >
+        </gr-dropdown-list>
+        <gr-thread-list
+          threads="[[_robotCommentThreads]]"
+          change="[[_change]]"
+          change-num="[[_changeNum]]"
+          logged-in="[[_loggedIn]]"
+          hide-toggle-buttons
+          empty-thread-msg="[[_messages.NO_ROBOT_COMMENTS_THREADS_MSG]]"
+          on-thread-list-modified="_handleReloadDiffComments"
+        >
+        </gr-thread-list>
+        <template is="dom-if" if="[[_showRobotCommentsButton]]">
+          <gr-button
+            class="show-robot-comments"
+            on-click="_toggleShowRobotComments"
+          >
+            [[_computeShowText(_showAllRobotComments)]]
+          </gr-button>
+        </template>
+      </template>
+
+      <template
+        is="dom-if"
+        if="[[_isTabActive(_selectedTabPluginHeader, _activeTabs)]]"
+      >
+        <gr-endpoint-decorator name$="[[_selectedTabPluginEndpoint]]">
+          <gr-endpoint-param name="change" value="[[_change]]">
+          </gr-endpoint-param>
+          <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
+          </gr-endpoint-param>
+        </gr-endpoint-decorator>
+      </template>
+    </section>
+
+    <gr-endpoint-decorator name="change-view-integration">
+      <gr-endpoint-param name="change" value="[[_change]]"> </gr-endpoint-param>
+      <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
+      </gr-endpoint-param>
+    </gr-endpoint-decorator>
+
+    <paper-tabs id="secondaryTabs" on-selected-changed="_setActiveSecondaryTab">
+      <paper-tab
+        data-name$="[[_constants.SecondaryTabs.CHANGE_LOG]]"
+        class="changeLog"
+      >
+        Change Log
+      </paper-tab>
+      <paper-tab
+        data-name$="[[_constants.SecondaryTabs.COMMENT_THREADS]]"
+        class="commentThreads"
+      >
+        <gr-tooltip-content
+          has-tooltip=""
+          title$="[[_computeTotalCommentCounts(_change.unresolved_comment_count, _changeComments)]]"
+        >
+          <span>Comment Threads</span></gr-tooltip-content
+        >
+      </paper-tab>
+    </paper-tabs>
+    <section class="changeLog">
+      <template
+        is="dom-if"
+        if="[[_isTabActive(_constants.SecondaryTabs.CHANGE_LOG, _activeTabs)]]"
+      >
+        <template is="dom-if" if="[[!_isChangeLogExperimentEnabled()]]">
+          <gr-messages-list
+            class="hideOnMobileOverlay"
+            change-num="[[_changeNum]]"
+            labels="[[_change.labels]]"
+            messages="[[_change.messages]]"
+            reviewer-updates="[[_change.reviewer_updates]]"
+            change-comments="[[_changeComments]]"
+            project-name="[[_change.project]]"
+            show-reply-buttons="[[_loggedIn]]"
+            on-message-anchor-tap="_handleMessageAnchorTap"
+            on-reply="_handleMessageReply"
+          ></gr-messages-list>
+        </template>
+        <template is="dom-if" if="[[_isChangeLogExperimentEnabled()]]">
+          <gr-messages-list-experimental
+            class="hideOnMobileOverlay"
+            change="[[_change]]"
+            change-num="[[_changeNum]]"
+            labels="[[_change.labels]]"
+            messages="[[_change.messages]]"
+            reviewer-updates="[[_change.reviewer_updates]]"
+            change-comments="[[_changeComments]]"
+            project-name="[[_change.project]]"
+            show-reply-buttons="[[_loggedIn]]"
+            on-message-anchor-tap="_handleMessageAnchorTap"
+            on-reply="_handleMessageReply"
+          ></gr-messages-list-experimental>
+        </template>
+      </template>
+      <template
+        is="dom-if"
+        if="[[_isTabActive(_constants.SecondaryTabs.COMMENT_THREADS, _activeTabs)]]"
+      >
+        <gr-thread-list
+          threads="[[_commentThreads]]"
+          change="[[_change]]"
+          change-num="[[_changeNum]]"
+          logged-in="[[_loggedIn]]"
+          only-show-robot-comments-with-human-reply=""
+          on-thread-list-modified="_handleReloadDiffComments"
+        ></gr-thread-list>
+      </template>
+    </section>
+  </div>
+
+  <gr-apply-fix-dialog
+    id="applyFixDialog"
+    prefs="[[_diffPrefs]]"
+    change="[[_change]]"
+    change-num="[[_changeNum]]"
+  ></gr-apply-fix-dialog>
+  <gr-overlay id="downloadOverlay" with-backdrop="">
+    <gr-download-dialog
+      id="downloadDialog"
+      change="[[_change]]"
+      patch-num="[[_patchRange.patchNum]]"
+      config="[[_serverConfig.download]]"
+      on-close="_handleDownloadDialogClose"
+    ></gr-download-dialog>
+  </gr-overlay>
+  <gr-overlay id="uploadHelpOverlay" with-backdrop="">
+    <gr-upload-help-dialog
+      revision="[[_currentRevision]]"
+      target-branch="[[_change.branch]]"
+      on-close="_handleCloseUploadHelpDialog"
+    ></gr-upload-help-dialog>
+  </gr-overlay>
+  <gr-overlay id="includedInOverlay" with-backdrop="">
+    <gr-included-in-dialog
+      id="includedInDialog"
+      change-num="[[_changeNum]]"
+      on-close="_handleIncludedInDialogClose"
+    ></gr-included-in-dialog>
+  </gr-overlay>
+  <gr-overlay
+    id="replyOverlay"
+    class="scrollable"
+    no-cancel-on-outside-click=""
+    no-cancel-on-esc-key=""
+    with-backdrop=""
+  >
+    <gr-reply-dialog
+      id="replyDialog"
+      change="{{_change}}"
+      patch-num="[[computeLatestPatchNum(_allPatchSets)]]"
+      permitted-labels="[[_change.permitted_labels]]"
+      draft-comment-threads="[[_draftCommentThreads]]"
+      project-config="[[_projectConfig]]"
+      can-be-started="[[_canStartReview]]"
+      on-send="_handleReplySent"
+      on-cancel="_handleReplyCancel"
+      on-autogrow="_handleReplyAutogrow"
+      on-send-disabled-changed="_resetReplyOverlayFocusStops"
+      hidden$="[[!_loggedIn]]"
+    >
+    </gr-reply-dialog>
+  </gr-overlay>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-comment-api id="commentAPI"></gr-comment-api>
 `;
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 b5c8793..2b3bfef 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -2320,9 +2321,9 @@
 
     test('don\'t report changedDisplayed on reply', done => {
       const changeDisplayStub =
-        sandbox.stub(element.$.reporting, 'changeDisplayed');
+        sandbox.stub(element.reporting, 'changeDisplayed');
       const changeFullyLoadedStub =
-        sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+        sandbox.stub(element.reporting, 'changeFullyLoaded');
       element._handleReplySent();
       flush(() => {
         assert.isFalse(changeDisplayStub.called);
@@ -2333,9 +2334,9 @@
 
     test('report changedDisplayed on _paramsChanged', done => {
       const changeDisplayStub =
-        sandbox.stub(element.$.reporting, 'changeDisplayed');
+        sandbox.stub(element.reporting, 'changeDisplayed');
       const changeFullyLoadedStub =
-        sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+        sandbox.stub(element.reporting, 'changeFullyLoaded');
       element._paramsChanged({
         view: GerritNav.View.CHANGE,
         changeNum: 101,
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 7ca9d6b..576b8bd 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
@@ -23,8 +23,6 @@
   from HTML and may be out of place here. Review them and
   then delete this comment!
 */
-
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-formatted-text/gr-formatted-text.js';
 import '../../../styles/shared-styles.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
@@ -38,7 +36,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCommentList extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_html.js b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_html.js
index d50ba6a..60b83ee 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_html.js
@@ -17,53 +17,73 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        word-wrap: break-word;
-      }
-      .file {
-        padding: var(--spacing-s) 0;
-      }
+  <style include="shared-styles">
+    :host {
+      display: block;
+      word-wrap: break-word;
+    }
+    .file {
+      padding: var(--spacing-s) 0;
+    }
+    .container {
+      display: flex;
+      padding: var(--spacing-s) 0;
+    }
+    .lineNum {
+      margin-right: var(--spacing-s);
+      min-width: 135px;
+      text-align: right;
+    }
+    .message {
+      flex: 1;
+      --gr-formatted-text-prose-max-width: 80ch;
+    }
+    @media screen and (max-width: 50em) {
       .container {
-        display: flex;
-        padding: var(--spacing-s) 0;
+        flex-direction: column;
       }
       .lineNum {
-        margin-right: var(--spacing-s);
-        min-width: 135px;
-        text-align: right;
+        margin-right: 0;
+        min-width: initial;
+        text-align: left;
       }
-      .message {
-        flex: 1;
-        --gr-formatted-text-prose-max-width: 80ch;
-      }
-      @media screen and (max-width: 50em) {
-        .container {
-          flex-direction: column;
-        }
-        .lineNum {
-          margin-right: 0;
-          min-width: initial;
-          text-align: left;
-        }
-      }
-    </style>
-    <template is="dom-repeat" items="[[_computeFilesFromComments(comments)]]" as="file">
-      <div class="file"><a class="fileLink" href="[[_computeDiffURL(file, changeNum, comments)]]">[[computeDisplayPath(file)]]</a></div>
-      <template is="dom-repeat" items="[[_computeCommentsForFile(comments, file)]]" as="comment">
-        <div class="container">
-          <a class="lineNum" href\$="[[_computeDiffLineURL(file, changeNum, comment.patch_set, comment)]]">
-             <span hidden\$="[[!comment.line]]">
-               <span>[[_computePatchDisplayName(comment)]]</span>
-               Line <span>[[comment.line]]</span>
-             </span>
-             <span hidden\$="[[comment.line]]">
-               File comment:
-             </span>
-          </a>
-          <gr-formatted-text class="message" no-trailing-margin="" content="[[comment.message]]" config="[[projectConfig.commentlinks]]"></gr-formatted-text>
-        </div>
-      </template>
+    }
+  </style>
+  <template
+    is="dom-repeat"
+    items="[[_computeFilesFromComments(comments)]]"
+    as="file"
+  >
+    <div class="file">
+      <a class="fileLink" href="[[_computeDiffURL(file, changeNum, comments)]]"
+        >[[computeDisplayPath(file)]]</a
+      >
+    </div>
+    <template
+      is="dom-repeat"
+      items="[[_computeCommentsForFile(comments, file)]]"
+      as="comment"
+    >
+      <div class="container">
+        <a
+          class="lineNum"
+          href$="[[_computeDiffLineURL(file, changeNum, comment.patch_set, comment)]]"
+        >
+          <span hidden$="[[!comment.line]]">
+            <span>[[_computePatchDisplayName(comment)]]</span>
+            Line <span>[[comment.line]]</span>
+          </span>
+          <span hidden$="[[comment.line]]">
+            File comment:
+          </span>
+        </a>
+        <gr-formatted-text
+          class="message"
+          no-trailing-margin=""
+          content="[[comment.message]]"
+          config="[[projectConfig.commentlinks]]"
+        ></gr-formatted-text>
+      </div>
     </template>
+  </template>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
index e68f1dc..075b883 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-comment-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 4dba3af..052189e 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-copy-clipboard/gr-copy-clipboard.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -24,7 +22,7 @@
 import {htmlTemplate} from './gr-commit-info_html.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrCommitInfo extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.js b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.js
index ffd36f4..608d12b 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.js
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_html.js
@@ -17,20 +17,27 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .container {
-        align-items: center;
-        display: flex;
-      }
-    </style>
-    <div class="container">
-      <template is="dom-if" if="[[_showWebLink]]">
-        <a target="_blank" rel="noopener" href\$="[[_webLink]]">[[_computeShortHash(commitInfo)]]</a>
-      </template>
-      <template is="dom-if" if="[[!_showWebLink]]">
-        [[_computeShortHash(commitInfo)]]
-      </template>
-      <gr-copy-clipboard has-tooltip="" button-title="Copy full SHA to clipboard" hide-input="" text="[[commitInfo.commit]]">
-      </gr-copy-clipboard>
-    </div>
+  <style include="shared-styles">
+    .container {
+      align-items: center;
+      display: flex;
+    }
+  </style>
+  <div class="container">
+    <template is="dom-if" if="[[_showWebLink]]">
+      <a target="_blank" rel="noopener" href$="[[_webLink]]"
+        >[[_computeShortHash(commitInfo)]]</a
+      >
+    </template>
+    <template is="dom-if" if="[[!_showWebLink]]">
+      [[_computeShortHash(commitInfo)]]
+    </template>
+    <gr-copy-clipboard
+      has-tooltip=""
+      button-title="Copy full SHA to clipboard"
+      hide-input=""
+      text="[[commitInfo.commit]]"
+    >
+    </gr-copy-clipboard>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
index 0da54d1..d9664ec 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-commit-info</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 d28e2b7..ada7dae 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
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../../styles/shared-styles.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
@@ -27,7 +25,7 @@
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmAbandonDialog extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.js
index e8b530b..050df25 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_html.js
@@ -17,36 +17,46 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      .main {
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-      }
-      label {
-        cursor: pointer;
-        display: block;
-        width: 100%;
-      }
-      iron-autogrow-textarea {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        width: 73ch; /* Add a char to account for the border. */
-      }
-    </style>
-    <gr-dialog confirm-label="Abandon" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">Abandon Change</div>
-      <div class="main" slot="main">
-        <label for="messageInput">Abandon Message</label>
-        <iron-autogrow-textarea id="messageInput" class="message" autocomplete="on" placeholder="<Insert reasoning here>" bind-value="{{message}}"></iron-autogrow-textarea>
-      </div>
-    </gr-dialog>
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    .main {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+    }
+    label {
+      cursor: pointer;
+      display: block;
+      width: 100%;
+    }
+    iron-autogrow-textarea {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      width: 73ch; /* Add a char to account for the border. */
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Abandon"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">Abandon Change</div>
+    <div class="main" slot="main">
+      <label for="messageInput">Abandon Message</label>
+      <iron-autogrow-textarea
+        id="messageInput"
+        class="message"
+        autocomplete="on"
+        placeholder="<Insert reasoning here>"
+        bind-value="{{message}}"
+      ></iron-autogrow-textarea>
+    </div>
+  </gr-dialog>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html
index c077f2b6..8010814 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-abandon-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 480e6cf..34a3dcc 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-dialog/gr-dialog.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -24,7 +22,7 @@
 import {htmlTemplate} from './gr-confirm-cherrypick-conflict-dialog_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmCherrypickConflictDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.js
index c03c246..c7fb70c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_html.js
@@ -17,26 +17,33 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      .main {
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-      }
-    </style>
-    <gr-dialog confirm-label="Continue" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">Cherry Pick Conflict!</div>
-      <div class="main" slot="main">
-        <span>Cherry Pick failed! (merge conflicts)</span>
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    .main {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Continue"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">Cherry Pick Conflict!</div>
+    <div class="main" slot="main">
+      <span>Cherry Pick failed! (merge conflicts)</span>
 
-        <span>Please select "Continue" to continue with conflicts or select "cancel" to close the dialog.</span>
-      </div>
-    </gr-dialog>
+      <span
+        >Please select "Continue" to continue with conflicts or select "cancel"
+        to close the dialog.</span
+      >
+    </div>
+  </gr-dialog>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html
index 3c7af47..e0016f0 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-cherrypick-conflict-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 2802046..d2dcbca 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
@@ -14,20 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-autocomplete/gr-autocomplete.js';
 import '../../shared/gr-dialog/gr-dialog.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-confirm-cherrypick-dialog_html.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
+import {appContext} from '../../../services/app-context.js';
 
 const SUGGESTIONS_LIMIT = 15;
 const CHANGE_SUBJECT_LIMIT = 50;
@@ -37,7 +35,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmCherrypickDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
@@ -97,6 +95,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   static get observers() {
     return [
       '_computeMessage(changeStatus, commitNum, commitMessage)',
@@ -260,7 +263,7 @@
     e.preventDefault();
     e.stopPropagation();
     if (this._cherryPickType === CHERRY_PICK_TYPES.TOPIC) {
-      this.$.reporting.reportInteraction('cherry-pick-topic-clicked');
+      this.reporting.reportInteraction('cherry-pick-topic-clicked');
       this._handleCherryPickTopic();
       return;
     }
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.js
index 4edee72..eaf9cc8 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_html.js
@@ -17,142 +17,200 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      label {
-        cursor: pointer;
-      }
-      .main {
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-      }
-      .main label,
-      .main input[type="text"] {
-        display: block;
-        width: 100%;
-      }
-      iron-autogrow-textarea {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        width: 73ch; /* Add a char to account for the border. */
-      }
-      .cherryPickTopicLayout {
-        display: flex;
-      }
-      .cherryPickSingleChange, .cherryPickTopic {
-        margin-left: var(--spacing-m);
-        margin-bottom: var(--spacing-m);
-      }
-      .cherry-pick-topic-message {
-        margin-bottom: var(--spacing-m);
-      }
-      label[for='messageInput'] , label[for='baseInput'] {
-        margin-top: var(--spacing-m);
-      }
-      .title {
-        font-weight: var(--font-weight-bold);
-      }
-      tr > td {
-        padding: var(--spacing-m);
-      }
-      th {
-        color: var(--deemphasized-text-color);
-      }
-      table {
-        border-collapse: collapse;
-      }
-      tr {
-        border-bottom: 1px solid var(--border-color);
-      }
-      .error {
-        color: var(--error-text-color);
-      }
-      .error-message {
-        color: var(--error-text-color);
-        margin: var(--spacing-m) 0 var(--spacing-m) 0;
-      }
-    </style>
-    <gr-dialog confirm-label="Cherry Pick" cancel-label="[[_computeCancelLabel(_statuses)]]" disabled\$="[[_computeDisableCherryPick(_cherryPickType, _duplicateProjectChanges, _statuses)]]" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header title" slot="header">Cherry Pick Change to Another Branch</div>
-      <div class="main" slot="main">
-
-        <template is="dom-if" if="[[_showCherryPickTopic]]">
-          <div class="cherryPickTopicLayout">
-            <input name="cherryPickOptions" type="radio" id="cherryPickSingleChange" on-change="_handlecherryPickSingleChangeClicked" checked="">
-            <label for="cherryPickSingleChange" class="cherryPickSingleChange">
-              Cherry Pick single change
-            </label>
-          </div>
-          <div class="cherryPickTopicLayout">
-            <input name="cherryPickOptions" type="radio" id="cherryPickTopic" on-change="_handlecherryPickTopicClicked">
-            <label for="cherryPickTopic" class="cherryPickTopic">
-              Cherry Pick entire topic ([[_changesCount]] Changes)
-            </label>
-        </div></template>
-
-        <label for="branchInput">
-          Cherry Pick to branch
-        </label>
-        <gr-autocomplete id="branchInput" text="{{branch}}" query="[[_query]]" placeholder="Destination branch">
-        </gr-autocomplete>
-        <template is="dom-if" if="[[_invalidBranch]]">
-          <span class="error"> Branch name cannot contain space or commas. </span>
-        </template>
-        <template is="dom-if" if="[[_computeIfSinglecherryPick(_cherryPickType)]]">
-          <label for="baseInput">
-            Provide base commit sha1 for cherry-pick
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    label {
+      cursor: pointer;
+    }
+    .main {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+    }
+    .main label,
+    .main input[type='text'] {
+      display: block;
+      width: 100%;
+    }
+    iron-autogrow-textarea {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      width: 73ch; /* Add a char to account for the border. */
+    }
+    .cherryPickTopicLayout {
+      display: flex;
+    }
+    .cherryPickSingleChange,
+    .cherryPickTopic {
+      margin-left: var(--spacing-m);
+      margin-bottom: var(--spacing-m);
+    }
+    .cherry-pick-topic-message {
+      margin-bottom: var(--spacing-m);
+    }
+    label[for='messageInput'],
+    label[for='baseInput'] {
+      margin-top: var(--spacing-m);
+    }
+    .title {
+      font-weight: var(--font-weight-bold);
+    }
+    tr > td {
+      padding: var(--spacing-m);
+    }
+    th {
+      color: var(--deemphasized-text-color);
+    }
+    table {
+      border-collapse: collapse;
+    }
+    tr {
+      border-bottom: 1px solid var(--border-color);
+    }
+    .error {
+      color: var(--error-text-color);
+    }
+    .error-message {
+      color: var(--error-text-color);
+      margin: var(--spacing-m) 0 var(--spacing-m) 0;
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Cherry Pick"
+    cancel-label="[[_computeCancelLabel(_statuses)]]"
+    disabled$="[[_computeDisableCherryPick(_cherryPickType, _duplicateProjectChanges, _statuses)]]"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header title" slot="header">
+      Cherry Pick Change to Another Branch
+    </div>
+    <div class="main" slot="main">
+      <template is="dom-if" if="[[_showCherryPickTopic]]">
+        <div class="cherryPickTopicLayout">
+          <input
+            name="cherryPickOptions"
+            type="radio"
+            id="cherryPickSingleChange"
+            on-change="_handlecherryPickSingleChangeClicked"
+            checked=""
+          />
+          <label for="cherryPickSingleChange" class="cherryPickSingleChange">
+            Cherry Pick single change
           </label>
-          <iron-input maxlength="40" placeholder="(optional)" bind-value="{{baseCommit}}">
-            <input is="iron-input" id="baseCommitInput" maxlength="40" placeholder="(optional)" bind-value="{{baseCommit}}">
-          </iron-input>
-          <label for="messageInput">
-            Cherry Pick Commit Message
-          </label> 
-        </template>
-        <template is="dom-if" if="[[_computeIfSinglecherryPick(_cherryPickType)]]">
-          <iron-autogrow-textarea id="messageInput" class="message" autocomplete="on" rows="4" max-rows="15" bind-value="{{message}}"></iron-autogrow-textarea>
-        </template>
-        <template is="dom-if" if="[[_computeIfCherryPickTopic(_cherryPickType)]]">
-          <span class="error-message">[[_computeTopicErrorMessage(_duplicateProjectChanges)]]</span>
-          <span class="cherry-pick-topic-message"> Commit Message will be auto generated </span>
-          <table>
-            <thead>
+        </div>
+        <div class="cherryPickTopicLayout">
+          <input
+            name="cherryPickOptions"
+            type="radio"
+            id="cherryPickTopic"
+            on-change="_handlecherryPickTopicClicked"
+          />
+          <label for="cherryPickTopic" class="cherryPickTopic">
+            Cherry Pick entire topic ([[_changesCount]] Changes)
+          </label>
+        </div></template
+      >
+
+      <label for="branchInput">
+        Cherry Pick to branch
+      </label>
+      <gr-autocomplete
+        id="branchInput"
+        text="{{branch}}"
+        query="[[_query]]"
+        placeholder="Destination branch"
+      >
+      </gr-autocomplete>
+      <template is="dom-if" if="[[_invalidBranch]]">
+        <span class="error"> Branch name cannot contain space or commas. </span>
+      </template>
+      <template
+        is="dom-if"
+        if="[[_computeIfSinglecherryPick(_cherryPickType)]]"
+      >
+        <label for="baseInput">
+          Provide base commit sha1 for cherry-pick
+        </label>
+        <iron-input
+          maxlength="40"
+          placeholder="(optional)"
+          bind-value="{{baseCommit}}"
+        >
+          <input
+            is="iron-input"
+            id="baseCommitInput"
+            maxlength="40"
+            placeholder="(optional)"
+            bind-value="{{baseCommit}}"
+          />
+        </iron-input>
+        <label for="messageInput">
+          Cherry Pick Commit Message
+        </label>
+      </template>
+      <template
+        is="dom-if"
+        if="[[_computeIfSinglecherryPick(_cherryPickType)]]"
+      >
+        <iron-autogrow-textarea
+          id="messageInput"
+          class="message"
+          autocomplete="on"
+          rows="4"
+          max-rows="15"
+          bind-value="{{message}}"
+        ></iron-autogrow-textarea>
+      </template>
+      <template is="dom-if" if="[[_computeIfCherryPickTopic(_cherryPickType)]]">
+        <span class="error-message"
+          >[[_computeTopicErrorMessage(_duplicateProjectChanges)]]</span
+        >
+        <span class="cherry-pick-topic-message">
+          Commit Message will be auto generated
+        </span>
+        <table>
+          <thead>
+            <tr>
+              <th>Change</th>
+              <th>Subject</th>
+              <th>Project</th>
+              <th>Status</th>
+              <!-- Error Message -->
+              <th></th>
+            </tr>
+          </thead>
+          <tbody>
+            <template is="dom-repeat" items="[[changes]]">
               <tr>
-                <th> Change </th>
-                <th> Subject </th>
-                <th> Project </th>
-                <th> Status </th>
-                <!-- Error Message -->
-                <th></th>
+                <td><span> [[_getChangeId(item)]] </span></td>
+                <td>
+                  <span> [[_getTrimmedChangeSubject(item.subject)]] </span>
+                </td>
+                <td><span> [[item.project]] </span></td>
+                <td>
+                  <span class$="[[_computeStatusClass(item, _statuses)]]">
+                    [[_computeStatus(item, _statuses)]]
+                  </span>
+                </td>
+                <td>
+                  <span class="error">
+                    [[_computeError(item, _statuses)]]
+                  </span>
+                </td>
               </tr>
-            </thead>
-            <tbody>
-              <template is="dom-repeat" items="[[changes]]">
-                <tr>
-                  <td> <span> [[_getChangeId(item)]] </span> </td>
-                  <td> <span> [[_getTrimmedChangeSubject(item.subject)]] </span> </td>
-                  <td> <span> [[item.project]] </span> </td>
-                  <td>
-                    <span class\$="[[_computeStatusClass(item, _statuses)]]">
-                      [[_computeStatus(item, _statuses)]]
-                    </span>
-                  </td>
-                  <td> <span class="error"> [[_computeError(item, _statuses)]] </span>  </td>
-                </tr>
-              </template>
-            </tbody>
-          </table>
-        </template>
-      </div>
-    </gr-dialog>
-    <gr-reporting id="reporting" category="confirm-cherry-pick-dialog"></gr-reporting>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+            </template>
+          </tbody>
+        </table>
+      </template>
+    </div>
+  </gr-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
index f1ee7e3..000718b 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-cherrypick-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 25beb2d5..f2ea45d 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
@@ -14,9 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-autocomplete/gr-autocomplete.js';
 import '../../shared/gr-dialog/gr-dialog.js';
@@ -31,7 +28,7 @@
 const SUGGESTIONS_LIMIT = 15;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmMoveDialog extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.js
index b8f3336..f5ddf41 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_html.js
@@ -17,51 +17,67 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        width: 30em;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      label {
-        cursor: pointer;
-      }
-      .main {
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-      }
-      .main label,
-      .main input[type="text"] {
-        display: block;
-        width: 100%;
-      }
-      .main .message {
-        width: 100%;
-      }
-      .warning {
-        color: var(--error-text-color);
-      }
-    </style>
-    <gr-dialog confirm-label="Move Change" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">Move Change to Another Branch</div>
-      <div class="main" slot="main">
-        <p class="warning">
-          Warning: moving a change will not change its parents.
-        </p>
-        <label for="branchInput">
-          Move change to branch
-        </label>
-        <gr-autocomplete id="branchInput" text="{{branch}}" query="[[_query]]" placeholder="Destination branch">
-        </gr-autocomplete>
-        <label for="messageInput">
-          Move Change Message
-        </label>
-        <iron-autogrow-textarea id="messageInput" class="message" autocomplete="on" rows="4" max-rows="15" bind-value="{{message}}"></iron-autogrow-textarea>
-      </div>
-    </gr-dialog>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      width: 30em;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    label {
+      cursor: pointer;
+    }
+    .main {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+    }
+    .main label,
+    .main input[type='text'] {
+      display: block;
+      width: 100%;
+    }
+    .main .message {
+      width: 100%;
+    }
+    .warning {
+      color: var(--error-text-color);
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Move Change"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">Move Change to Another Branch</div>
+    <div class="main" slot="main">
+      <p class="warning">
+        Warning: moving a change will not change its parents.
+      </p>
+      <label for="branchInput">
+        Move change to branch
+      </label>
+      <gr-autocomplete
+        id="branchInput"
+        text="{{branch}}"
+        query="[[_query]]"
+        placeholder="Destination branch"
+      >
+      </gr-autocomplete>
+      <label for="messageInput">
+        Move Change Message
+      </label>
+      <iron-autogrow-textarea
+        id="messageInput"
+        class="message"
+        autocomplete="on"
+        rows="4"
+        max-rows="15"
+        bind-value="{{message}}"
+      ></iron-autogrow-textarea>
+    </div>
+  </gr-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
index 25b110ad..a8392aa 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-move-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 e451034..0a45de2 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-autocomplete/gr-autocomplete.js';
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -25,7 +23,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-confirm-rebase-dialog_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrConfirmRebaseDialog extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.js
index 20872bc..e9a8424 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_html.js
@@ -17,70 +17,115 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        width: 30em;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      label {
-        cursor: pointer;
-      }
-      .message {
-        font-style: italic;
-      }
-      .parentRevisionContainer label,
-      .parentRevisionContainer input[type="text"] {
-        display: block;
-        width: 100%;
-      }
-      .parentRevisionContainer label {
-        margin-bottom: var(--spacing-xs);
-      }
-      .rebaseOption {
-        margin: var(--spacing-m) 0;
-      }
-    </style>
-    <gr-dialog id="confirmDialog" confirm-label="Rebase" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">Confirm rebase</div>
-      <div class="main" slot="main">
-        <div id="rebaseOnParent" class="rebaseOption" hidden\$="[[!_displayParentOption(rebaseOnCurrent, hasParent)]]">
-          <input id="rebaseOnParentInput" name="rebaseOptions" type="radio" on-click="_handleRebaseOnParent">
-          <label id="rebaseOnParentLabel" for="rebaseOnParentInput">
-            Rebase on parent change
-          </label>
-        </div>
-        <div id="parentUpToDateMsg" class="message" hidden\$="[[!_displayParentUpToDateMsg(rebaseOnCurrent, hasParent)]]">
-          This change is up to date with its parent.
-        </div>
-        <div id="rebaseOnTip" class="rebaseOption" hidden\$="[[!_displayTipOption(rebaseOnCurrent, hasParent)]]">
-          <input id="rebaseOnTipInput" name="rebaseOptions" type="radio" disabled\$="[[!_displayTipOption(rebaseOnCurrent, hasParent)]]" on-click="_handleRebaseOnTip">
-          <label id="rebaseOnTipLabel" for="rebaseOnTipInput">
-            Rebase on top of the [[branch]]
-            branch<span hidden\$="[[!hasParent]]">
-              (breaks relation chain)
-            </span>
-          </label>
-        </div>
-        <div id="tipUpToDateMsg" class="message" hidden\$="[[_displayTipOption(rebaseOnCurrent, hasParent)]]">
-          Change is up to date with the target branch already ([[branch]])
-        </div>
-        <div id="rebaseOnOther" class="rebaseOption">
-          <input id="rebaseOnOtherInput" name="rebaseOptions" type="radio" on-click="_handleRebaseOnOther">
-          <label id="rebaseOnOtherLabel" for="rebaseOnOtherInput">
-            Rebase on a specific change, ref, or commit <span hidden\$="[[!hasParent]]">
-              (breaks relation chain)
-            </span>
-          </label>
-        </div>
-        <div class="parentRevisionContainer">
-          <gr-autocomplete id="parentInput" query="[[_query]]" no-debounce="" text="{{_text}}" on-click="_handleEnterChangeNumberClick" allow-non-suggested-values="" placeholder="Change number, ref, or commit hash">
-          </gr-autocomplete>
-        </div>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      width: 30em;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    label {
+      cursor: pointer;
+    }
+    .message {
+      font-style: italic;
+    }
+    .parentRevisionContainer label,
+    .parentRevisionContainer input[type='text'] {
+      display: block;
+      width: 100%;
+    }
+    .parentRevisionContainer label {
+      margin-bottom: var(--spacing-xs);
+    }
+    .rebaseOption {
+      margin: var(--spacing-m) 0;
+    }
+  </style>
+  <gr-dialog
+    id="confirmDialog"
+    confirm-label="Rebase"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">Confirm rebase</div>
+    <div class="main" slot="main">
+      <div
+        id="rebaseOnParent"
+        class="rebaseOption"
+        hidden$="[[!_displayParentOption(rebaseOnCurrent, hasParent)]]"
+      >
+        <input
+          id="rebaseOnParentInput"
+          name="rebaseOptions"
+          type="radio"
+          on-click="_handleRebaseOnParent"
+        />
+        <label id="rebaseOnParentLabel" for="rebaseOnParentInput">
+          Rebase on parent change
+        </label>
       </div>
-    </gr-dialog>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      <div
+        id="parentUpToDateMsg"
+        class="message"
+        hidden$="[[!_displayParentUpToDateMsg(rebaseOnCurrent, hasParent)]]"
+      >
+        This change is up to date with its parent.
+      </div>
+      <div
+        id="rebaseOnTip"
+        class="rebaseOption"
+        hidden$="[[!_displayTipOption(rebaseOnCurrent, hasParent)]]"
+      >
+        <input
+          id="rebaseOnTipInput"
+          name="rebaseOptions"
+          type="radio"
+          disabled$="[[!_displayTipOption(rebaseOnCurrent, hasParent)]]"
+          on-click="_handleRebaseOnTip"
+        />
+        <label id="rebaseOnTipLabel" for="rebaseOnTipInput">
+          Rebase on top of the [[branch]] branch<span hidden$="[[!hasParent]]">
+            (breaks relation chain)
+          </span>
+        </label>
+      </div>
+      <div
+        id="tipUpToDateMsg"
+        class="message"
+        hidden$="[[_displayTipOption(rebaseOnCurrent, hasParent)]]"
+      >
+        Change is up to date with the target branch already ([[branch]])
+      </div>
+      <div id="rebaseOnOther" class="rebaseOption">
+        <input
+          id="rebaseOnOtherInput"
+          name="rebaseOptions"
+          type="radio"
+          on-click="_handleRebaseOnOther"
+        />
+        <label id="rebaseOnOtherLabel" for="rebaseOnOtherInput">
+          Rebase on a specific change, ref, or commit
+          <span hidden$="[[!hasParent]]">
+            (breaks relation chain)
+          </span>
+        </label>
+      </div>
+      <div class="parentRevisionContainer">
+        <gr-autocomplete
+          id="parentInput"
+          query="[[_query]]"
+          no-debounce=""
+          text="{{_text}}"
+          on-click="_handleEnterChangeNumberClick"
+          allow-non-suggested-values=""
+          placeholder="Change number, ref, or commit hash"
+        >
+        </gr-autocomplete>
+      </div>
+    </div>
+  </gr-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
index ffcb8ae..080a7e0 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-rebase-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6eb4c82..9489b94 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
@@ -14,9 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../../styles/shared-styles.js';
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
@@ -37,7 +34,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmRevertDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.js
index 3f293cf..7875fa7 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_html.js
@@ -17,65 +17,88 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      label {
-        cursor: pointer;
-        display: block;
-        width: 100%;
-      }
-      .revertSubmissionLayout {
-        display: flex;
-      }
-      .label {
-        margin-left: var(--spacing-m);
-        margin-bottom: var(--spacing-m);
-      }
-      iron-autogrow-textarea {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        width: 73ch; /* Add a char to account for the border. */
-      }
-      .error {
-        color: var(--error-text-color);
-        margin-bottom: var(--spacing-m);
-      }
-    </style>
-    <gr-dialog confirm-label="Revert" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">
-        Revert Merged Change
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    label {
+      cursor: pointer;
+      display: block;
+      width: 100%;
+    }
+    .revertSubmissionLayout {
+      display: flex;
+    }
+    .label {
+      margin-left: var(--spacing-m);
+      margin-bottom: var(--spacing-m);
+    }
+    iron-autogrow-textarea {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      width: 73ch; /* Add a char to account for the border. */
+    }
+    .error {
+      color: var(--error-text-color);
+      margin-bottom: var(--spacing-m);
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Revert"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">
+      Revert Merged Change
+    </div>
+    <div class="main" slot="main">
+      <div class="error" hidden$="[[!_showErrorMessage]]">
+        <span> A reason is required </span>
       </div>
-      <div class="main" slot="main">
-        <div class="error" hidden\$="[[!_showErrorMessage]]">
-          <span> A reason is required </span>
-        </div>
-        <template is="dom-if" if="[[_showRevertSubmission]]">
-          <div class="revertSubmissionLayout">
-            <input name="revertOptions" type="radio" id="revertSingleChange" on-change="_handleRevertSingleChangeClicked" checked="[[_computeIfSingleRevert(_revertType)]]">
-            <label for="revertSingleChange" class="label revertSingleChange">
-              Revert single change
-            </label>
-          </div>
-          <div class="revertSubmissionLayout">
-            <input name="revertOptions" type="radio" id="revertSubmission" on-change="_handleRevertSubmissionClicked" checked="[[_computeIfRevertSubmission(_revertType)]]">
-            <label for="revertSubmission" class="label revertSubmission">
-              Revert entire submission ([[_changesCount]] Changes)
-            </label>
-        </div></template>
-        <gr-endpoint-decorator name="confirm-revert-change">
-          <label for="messageInput">
-            Revert Commit Message
+      <template is="dom-if" if="[[_showRevertSubmission]]">
+        <div class="revertSubmissionLayout">
+          <input
+            name="revertOptions"
+            type="radio"
+            id="revertSingleChange"
+            on-change="_handleRevertSingleChangeClicked"
+            checked="[[_computeIfSingleRevert(_revertType)]]"
+          />
+          <label for="revertSingleChange" class="label revertSingleChange">
+            Revert single change
           </label>
-          <iron-autogrow-textarea id="messageInput" class="message" autocomplete="on" max-rows="15" bind-value="{{_message}}"></iron-autogrow-textarea>
-        </gr-endpoint-decorator>
-      </div>
-    </gr-dialog>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+        </div>
+        <div class="revertSubmissionLayout">
+          <input
+            name="revertOptions"
+            type="radio"
+            id="revertSubmission"
+            on-change="_handleRevertSubmissionClicked"
+            checked="[[_computeIfRevertSubmission(_revertType)]]"
+          />
+          <label for="revertSubmission" class="label revertSubmission">
+            Revert entire submission ([[_changesCount]] Changes)
+          </label>
+        </div></template
+      >
+      <gr-endpoint-decorator name="confirm-revert-change">
+        <label for="messageInput">
+          Revert Commit Message
+        </label>
+        <iron-autogrow-textarea
+          id="messageInput"
+          class="message"
+          autocomplete="on"
+          max-rows="15"
+          bind-value="{{_message}}"
+        ></iron-autogrow-textarea>
+      </gr-endpoint-decorator>
+    </div>
+  </gr-dialog>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
index fd984ec..3a341c5 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-revert-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.js
index 212f909..c8e14f7 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog.js
@@ -14,9 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-js-api-interface/gr-js-api-interface.js';
@@ -30,7 +27,7 @@
 const CHANGE_SUBJECT_LIMIT = 50;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmRevertSubmissionDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.js
index a68920c..48051a0 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_html.js
@@ -17,35 +17,45 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <!-- TODO(taoalpha): move all shared styles to a style module. -->
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      label {
-        cursor: pointer;
-        display: block;
-        width: 100%;
-      }
-      iron-autogrow-textarea {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        width: 73ch; /* Add a char to account for the border. */
-      }
-    </style>
-    <gr-dialog confirm-label="Revert Submission" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">Revert Submission</div>
-      <div class="main" slot="main">
-        <label for="messageInput">
-          Revert Commit Message
-        </label>
-        <iron-autogrow-textarea id="messageInput" class="message" autocomplete="on" max-rows="15" bind-value="{{message}}"></iron-autogrow-textarea>
-      </div>
-    </gr-dialog>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+  <!-- TODO(taoalpha): move all shared styles to a style module. -->
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    label {
+      cursor: pointer;
+      display: block;
+      width: 100%;
+    }
+    iron-autogrow-textarea {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      width: 73ch; /* Add a char to account for the border. */
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Revert Submission"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">Revert Submission</div>
+    <div class="main" slot="main">
+      <label for="messageInput">
+        Revert Commit Message
+      </label>
+      <iron-autogrow-textarea
+        id="messageInput"
+        class="message"
+        autocomplete="on"
+        max-rows="15"
+        bind-value="{{message}}"
+      ></iron-autogrow-textarea>
+    </div>
+  </gr-dialog>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.html
index cc35d72..c51d635 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-submission-dialog/gr-confirm-revert-submission-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-revert-submission-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 5d599b7..42afcc2 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-icon/iron-icon.js';
 import '../../shared/gr-icons/gr-icons.js';
 import '../../shared/gr-dialog/gr-dialog.js';
@@ -28,7 +26,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-confirm-submit-dialog_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrConfirmSubmitDialog extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.js b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.js
index 83b3611..cf1a332 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.js
@@ -17,54 +17,69 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
+  <style include="shared-styles">
+    #dialog {
+      min-width: 40em;
+    }
+    p {
+      margin-bottom: var(--spacing-l);
+    }
+    .warningBeforeSubmit {
+      color: var(--error-text-color);
+      vertical-align: top;
+      margin-right: var(--spacing-s);
+    }
+    @media screen and (max-width: 50em) {
       #dialog {
-        min-width: 40em;
+        min-width: inherit;
+        width: 100%;
       }
-      p {
-        margin-bottom: var(--spacing-l);
-      }
-      .warningBeforeSubmit {
-        color: var(--error-text-color);
-        vertical-align: top;
-        margin-right: var(--spacing-s);
-      }
-      @media screen and (max-width: 50em) {
-        #dialog {
-          min-width: inherit;
-          width: 100%;
-        }
-      }
-    </style>
-    <gr-dialog id="dialog" confirm-label="Continue" confirm-on-enter="" on-cancel="_handleCancelTap" on-confirm="_handleConfirmTap">
-      <div class="header" slot="header">
-        [[action.label]]
-      </div>
-      <div class="main" slot="main">
-        <gr-endpoint-decorator name="confirm-submit-change">
-          <p>Ready to submit “<strong>[[change.subject]]</strong>”?</p>
-          <template is="dom-if" if="[[change.is_private]]">
-            <p>
-              <iron-icon icon="gr-icons:error" class="warningBeforeSubmit"></iron-icon>
-              <strong>Heads Up!</strong>
-              Submitting this private change will also make it public.
-            </p>
-          </template>
-          <template is="dom-if" if="[[change.unresolved_comment_count]]">
-            <p>
-              <iron-icon icon="gr-icons:error" class="warningBeforeSubmit"></iron-icon>
-              [[_computeUnresolvedCommentsWarning(change)]]
-            </p>
-          </template>
-          <template is="dom-if" if="[[_computeHasChangeEdit(change)]]">
-              <iron-icon icon="gr-icons:error" class="warningBeforeSubmit"></iron-icon>
-              Your unpublished edit will not be submitted. Did you
-              forget to click <b>PUBLISH</b>?
-          </template>
-          <gr-endpoint-param name="change" value="[[change]]"></gr-endpoint-param>
-          <gr-endpoint-param name="action" value="[[action]]"></gr-endpoint-param>
-        </gr-endpoint-decorator>
-      </div>
-    </gr-dialog>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    }
+  </style>
+  <gr-dialog
+    id="dialog"
+    confirm-label="Continue"
+    confirm-on-enter=""
+    on-cancel="_handleCancelTap"
+    on-confirm="_handleConfirmTap"
+  >
+    <div class="header" slot="header">
+      [[action.label]]
+    </div>
+    <div class="main" slot="main">
+      <gr-endpoint-decorator name="confirm-submit-change">
+        <p>Ready to submit “<strong>[[change.subject]]</strong>”?</p>
+        <template is="dom-if" if="[[change.is_private]]">
+          <p>
+            <iron-icon
+              icon="gr-icons:error"
+              class="warningBeforeSubmit"
+            ></iron-icon>
+            <strong>Heads Up!</strong>
+            Submitting this private change will also make it public.
+          </p>
+        </template>
+        <template is="dom-if" if="[[change.unresolved_comment_count]]">
+          <p>
+            <iron-icon
+              icon="gr-icons:error"
+              class="warningBeforeSubmit"
+            ></iron-icon>
+            [[_computeUnresolvedCommentsWarning(change)]]
+          </p>
+        </template>
+        <template is="dom-if" if="[[_computeHasChangeEdit(change)]]">
+          <iron-icon
+            icon="gr-icons:error"
+            class="warningBeforeSubmit"
+          ></iron-icon>
+          Your unpublished edit will not be submitted. Did you forget to click
+          <b>PUBLISH</b>?
+        </template>
+        <gr-endpoint-param name="change" value="[[change]]"></gr-endpoint-param>
+        <gr-endpoint-param name="action" value="[[action]]"></gr-endpoint-param>
+      </gr-endpoint-decorator>
+    </div>
+  </gr-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
index 3a39833..30699d5 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-confirm-submit-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 4c457a1..6e22374 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-download-commands/gr-download-commands.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
@@ -27,7 +25,7 @@
 import {RESTClientBehavior} from '../../../behaviors/rest-client-behavior/rest-client-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDownloadDialog extends mixinBehaviors( [
   PatchSetBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.js
index 324f9f4..9569c03 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_html.js
@@ -17,87 +17,108 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        padding: var(--spacing-m) 0;
-      }
-      section {
-        display: flex;
-        padding: var(--spacing-m) var(--spacing-xl);
-      }
-      .flexContainer {
-        display: flex;
-        justify-content: space-between;
-        padding-top: var(--spacing-m);
-      }
-      .footer {
-        justify-content: flex-end;
-      }
-      .closeButtonContainer {
-        align-items: flex-end;
-        display: flex;
-        flex: 0;
-        justify-content: flex-end;
-      }
-      .patchFiles,
-      .archivesContainer {
-        padding-bottom: var(--spacing-m);
-      }
-      .patchFiles {
-        margin-right: var(--spacing-xxl);
-      }
-      .patchFiles a,
-      .archives a {
-        display: inline-block;
-        margin-right: var(--spacing-l);
-      }
-      .patchFiles a:last-of-type,
-      .archives a:last-of-type {
-        margin-right: 0;
-      }
-      .title {
-        flex: 1;
-        font-weight: var(--font-weight-bold);
-      }
-      .hidden {
-        display: none;
-      }
-    </style>
-    <section>
-      <h3 class="title">
-        Patch set [[patchNum]] of [[_computePatchSetQuantity(change.revisions)]]
-      </h3>
-    </section>
-    <section class\$="[[_computeShowDownloadCommands(_schemes)]]">
-      <gr-download-commands id="downloadCommands" commands="[[_computeDownloadCommands(change, patchNum, _selectedScheme)]]" schemes="[[_schemes]]" selected-scheme="{{_selectedScheme}}"></gr-download-commands>
-    </section>
-    <section class="flexContainer">
-      <div class="patchFiles" hidden="[[_computeHidePatchFile(change, patchNum)]]">
-        <label>Patch file</label>
-        <div>
-          <a id="download" href\$="[[_computeDownloadLink(change, patchNum)]]" download="">
-            [[_computeDownloadFilename(change, patchNum)]]
-          </a>
-          <a href\$="[[_computeZipDownloadLink(change, patchNum)]]" download="">
-            [[_computeZipDownloadFilename(change, patchNum)]]
-          </a>
-        </div>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      padding: var(--spacing-m) 0;
+    }
+    section {
+      display: flex;
+      padding: var(--spacing-m) var(--spacing-xl);
+    }
+    .flexContainer {
+      display: flex;
+      justify-content: space-between;
+      padding-top: var(--spacing-m);
+    }
+    .footer {
+      justify-content: flex-end;
+    }
+    .closeButtonContainer {
+      align-items: flex-end;
+      display: flex;
+      flex: 0;
+      justify-content: flex-end;
+    }
+    .patchFiles,
+    .archivesContainer {
+      padding-bottom: var(--spacing-m);
+    }
+    .patchFiles {
+      margin-right: var(--spacing-xxl);
+    }
+    .patchFiles a,
+    .archives a {
+      display: inline-block;
+      margin-right: var(--spacing-l);
+    }
+    .patchFiles a:last-of-type,
+    .archives a:last-of-type {
+      margin-right: 0;
+    }
+    .title {
+      flex: 1;
+      font-weight: var(--font-weight-bold);
+    }
+    .hidden {
+      display: none;
+    }
+  </style>
+  <section>
+    <h3 class="title">
+      Patch set [[patchNum]] of [[_computePatchSetQuantity(change.revisions)]]
+    </h3>
+  </section>
+  <section class$="[[_computeShowDownloadCommands(_schemes)]]">
+    <gr-download-commands
+      id="downloadCommands"
+      commands="[[_computeDownloadCommands(change, patchNum, _selectedScheme)]]"
+      schemes="[[_schemes]]"
+      selected-scheme="{{_selectedScheme}}"
+    ></gr-download-commands>
+  </section>
+  <section class="flexContainer">
+    <div
+      class="patchFiles"
+      hidden="[[_computeHidePatchFile(change, patchNum)]]"
+    >
+      <label>Patch file</label>
+      <div>
+        <a
+          id="download"
+          href$="[[_computeDownloadLink(change, patchNum)]]"
+          download=""
+        >
+          [[_computeDownloadFilename(change, patchNum)]]
+        </a>
+        <a href$="[[_computeZipDownloadLink(change, patchNum)]]" download="">
+          [[_computeZipDownloadFilename(change, patchNum)]]
+        </a>
       </div>
-      <div class="archivesContainer" hidden\$="[[!config.archives.length]]" hidden="">
-        <label>Archive</label>
-        <div id="archives" class="archives">
-          <template is="dom-repeat" items="[[config.archives]]" as="format">
-            <a href\$="[[_computeArchiveDownloadLink(change, patchNum, format)]]" download="">
-              [[format]]
-            </a>
-          </template>
-        </div>
+    </div>
+    <div
+      class="archivesContainer"
+      hidden$="[[!config.archives.length]]"
+      hidden=""
+    >
+      <label>Archive</label>
+      <div id="archives" class="archives">
+        <template is="dom-repeat" items="[[config.archives]]" as="format">
+          <a
+            href$="[[_computeArchiveDownloadLink(change, patchNum, format)]]"
+            download=""
+          >
+            [[format]]
+          </a>
+        </template>
       </div>
-    </section>
-    <section class="footer">
-      <span class="closeButtonContainer">
-        <gr-button id="closeButton" link="" on-click="_handleCloseTap">Close</gr-button>
-      </span>
-    </section>
+    </div>
+  </section>
+  <section class="footer">
+    <span class="closeButtonContainer">
+      <gr-button id="closeButton" link="" on-click="_handleCloseTap"
+        >Close</gr-button
+      >
+    </span>
+  </section>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
index c2f4963..46c57fe 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-download-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 73c6721..1f9ca20 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../diff/gr-diff-mode-selector/gr-diff-mode-selector.js';
 import '../../diff/gr-patch-range-select/gr-patch-range-select.js';
@@ -43,7 +41,7 @@
 const MERGED_STATUS = 'MERGED';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrFileListHeader extends mixinBehaviors( [
   PatchSetBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.js
index 28cd645..10b8606 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.js
@@ -17,180 +17,254 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .prefsButton {
-        float: right;
+  <style include="shared-styles">
+    .prefsButton {
+      float: right;
+    }
+    .collapseToggleButton {
+      text-decoration: none;
+    }
+    .patchInfoOldPatchSet.patchInfo-header {
+      background-color: var(--emphasis-color);
+    }
+    .patchInfo-header {
+      align-items: center;
+      border-top: 1px solid var(--border-color);
+      display: flex;
+      padding: var(--spacing-s) var(--spacing-l);
+    }
+    .patchInfo-left {
+      align-items: baseline;
+      display: flex;
+    }
+    .patchInfoContent {
+      align-items: center;
+      display: flex;
+      flex-wrap: wrap;
+    }
+    .patchInfo-header .container.latestPatchContainer {
+      display: none;
+    }
+    .patchInfoOldPatchSet .container.latestPatchContainer {
+      display: initial;
+    }
+    .latestPatchContainer a {
+      text-decoration: none;
+    }
+    gr-editable-label.descriptionLabel {
+      max-width: 100%;
+    }
+    .mobile {
+      display: none;
+    }
+    .patchInfo-header .container {
+      align-items: center;
+      display: flex;
+    }
+    .downloadContainer,
+    .uploadContainer,
+    .includedInContainer {
+      margin-right: 16px;
+    }
+    .includedInContainer.hide,
+    .uploadContainer.hide {
+      display: none;
+    }
+    .rightControls {
+      align-self: flex-end;
+      margin: auto 0 auto auto;
+      align-items: center;
+      display: flex;
+      flex-wrap: wrap;
+      font-weight: var(--font-weight-normal);
+      justify-content: flex-end;
+    }
+    #collapseBtn,
+    .expanded #expandBtn,
+    .fileViewActions {
+      display: none;
+    }
+    .expanded #expandBtn {
+      display: none;
+    }
+    gr-linked-chip {
+      --linked-chip-text-color: var(--primary-text-color);
+    }
+    .expanded #collapseBtn,
+    .openFile .fileViewActions {
+      align-items: center;
+      display: flex;
+    }
+    .rightControls gr-button,
+    gr-patch-range-select {
+      margin: 0 -4px;
+    }
+    .fileViewActions gr-button {
+      margin: 0;
+      --gr-button: {
+        padding: 2px 4px;
       }
-      .collapseToggleButton {
-        text-decoration: none;
-      }
-      .patchInfoOldPatchSet.patchInfo-header {
-        background-color: var(--emphasis-color);
-      }
-      .patchInfo-header {
-        align-items: center;
-        border-top: 1px solid var(--border-color);
-        display: flex;
-        padding: var(--spacing-s) var(--spacing-l);
-      }
-      .patchInfo-left {
-        align-items: baseline;
-        display: flex;
-      }
-      .patchInfoContent {
-        align-items: center;
-        display: flex;
-        flex-wrap: wrap;
-      }
-      .patchInfo-header .container.latestPatchContainer {
+    }
+    .editMode .hideOnEdit {
+      display: none;
+    }
+    .showOnEdit {
+      display: none;
+    }
+    .editMode .showOnEdit {
+      display: initial;
+    }
+    .editMode .showOnEdit.flexContainer {
+      align-items: center;
+      display: flex;
+    }
+    .label {
+      font-weight: var(--font-weight-bold);
+      margin-right: 24px;
+    }
+    gr-commit-info,
+    gr-edit-controls {
+      margin-right: -5px;
+    }
+    .fileViewActionsLabel {
+      margin-right: var(--spacing-xs);
+    }
+    @media screen and (max-width: 50em) {
+      .patchInfo-header .desktop {
         display: none;
       }
-      .patchInfoOldPatchSet .container.latestPatchContainer {
-        display: initial;
-      }
-      .latestPatchContainer a {
-        text-decoration: none;
-      }
-      gr-editable-label.descriptionLabel {
-        max-width: 100%;
-      }
-      .mobile {
-        display: none;
-      }
-      .patchInfo-header .container {
-        align-items: center;
-        display: flex;
-      }
-      .downloadContainer,
-      .uploadContainer,
-      .includedInContainer {
-        margin-right: 16px;
-      }
-      .includedInContainer.hide,
-      .uploadContainer.hide {
-        display: none;
-      }
-      .rightControls {
-        align-self: flex-end;
-        margin: auto 0 auto auto;
-        align-items: center;
-        display: flex;
-        flex-wrap: wrap;
-        font-weight: var(--font-weight-normal);
-        justify-content: flex-end;
-      }
-      #collapseBtn,
-      .expanded #expandBtn,
-      .fileViewActions{
-        display: none;
-      }
-      .expanded #expandBtn {
-        display: none;
-      }
-      gr-linked-chip {
-        --linked-chip-text-color: var(--primary-text-color);
-      }
-      .expanded #collapseBtn,
-      .openFile .fileViewActions {
-        align-items: center;
-        display: flex;
-      }
-      .rightControls gr-button,
-      gr-patch-range-select {
-        margin: 0 -4px;
-      }
-      .fileViewActions gr-button {
-        margin: 0;
-        --gr-button: {
-          padding: 2px 4px;
-        }
-      }
-      .editMode .hideOnEdit {
-        display: none;
-      }
-      .showOnEdit {
-        display: none;
-      }
-      .editMode .showOnEdit {
-        display: initial;
-      }
-      .editMode .showOnEdit.flexContainer {
-        align-items: center;
-        display: flex;
-      }
-      .label {
-        font-weight: var(--font-weight-bold);
-        margin-right: 24px;
-      }
-      gr-commit-info,
-      gr-edit-controls {
-        margin-right: -5px;
-      }
-      .fileViewActionsLabel {
-        margin-right: var(--spacing-xs);
-      }
-      @media screen and (max-width: 50em) {
-        .patchInfo-header .desktop {
-          display: none;
-        }
-      }
-    </style>
-    <div class\$="patchInfo-header [[_computeEditModeClass(editMode)]] [[_computePatchInfoClass(patchNum, allPatchSets)]]">
-      <div class="patchInfo-left">
-        <div class="patchInfoContent">
-          <gr-patch-range-select id="rangeSelect" change-comments="[[changeComments]]" change-num="[[changeNum]]" patch-num="[[patchNum]]" base-patch-num="[[basePatchNum]]" available-patches="[[allPatchSets]]" revisions="[[change.revisions]]" revision-info="[[revisionInfo]]" on-patch-range-change="_handlePatchChange">
-          </gr-patch-range-select>
+    }
+  </style>
+  <div
+    class$="patchInfo-header [[_computeEditModeClass(editMode)]] [[_computePatchInfoClass(patchNum, allPatchSets)]]"
+  >
+    <div class="patchInfo-left">
+      <div class="patchInfoContent">
+        <gr-patch-range-select
+          id="rangeSelect"
+          change-comments="[[changeComments]]"
+          change-num="[[changeNum]]"
+          patch-num="[[patchNum]]"
+          base-patch-num="[[basePatchNum]]"
+          available-patches="[[allPatchSets]]"
+          revisions="[[change.revisions]]"
+          revision-info="[[revisionInfo]]"
+          on-patch-range-change="_handlePatchChange"
+        >
+        </gr-patch-range-select>
+        <span class="separator"></span>
+        <gr-commit-info
+          change="[[change]]"
+          server-config="[[serverConfig]]"
+          commit-info="[[commitInfo]]"
+        ></gr-commit-info>
+        <span class="container latestPatchContainer">
           <span class="separator"></span>
-          <gr-commit-info change="[[change]]" server-config="[[serverConfig]]" commit-info="[[commitInfo]]"></gr-commit-info>
-          <span class="container latestPatchContainer">
-            <span class="separator"></span>
-            <a href\$="[[changeUrl]]">Go to latest patch set</a>
-          </span>
-          <span class="container descriptionContainer hideOnEdit">
-            <span class="separator"></span>
-            <template is="dom-if" if="[[_patchsetDescription]]">
-              <gr-linked-chip id="descriptionChip" text="[[_patchsetDescription]]" removable="[[!_descriptionReadOnly]]" on-remove="_handleDescriptionRemoved"></gr-linked-chip>
-            </template>
-            <template is="dom-if" if="[[!_patchsetDescription]]">
-              <gr-editable-label id="descriptionLabel" uppercase="" class="descriptionLabel" label-text="Add patchset description" value="[[_patchsetDescription]]" placeholder="[[_computeDescriptionPlaceholder(_descriptionReadOnly)]]" read-only="[[_descriptionReadOnly]]" on-changed="_handleDescriptionChanged"></gr-editable-label>
-            </template>
-          </span>
-        </div>
-      </div>
-      <div class\$="rightControls [[_computeExpandedClass(filesExpanded)]]">
-        <span class="showOnEdit flexContainer">
-          <gr-edit-controls id="editControls" patch-num="[[patchNum]]" change="[[change]]"></gr-edit-controls>
+          <a href$="[[changeUrl]]">Go to latest patch set</a>
+        </span>
+        <span class="container descriptionContainer hideOnEdit">
           <span class="separator"></span>
+          <template is="dom-if" if="[[_patchsetDescription]]">
+            <gr-linked-chip
+              id="descriptionChip"
+              text="[[_patchsetDescription]]"
+              removable="[[!_descriptionReadOnly]]"
+              on-remove="_handleDescriptionRemoved"
+            ></gr-linked-chip>
+          </template>
+          <template is="dom-if" if="[[!_patchsetDescription]]">
+            <gr-editable-label
+              id="descriptionLabel"
+              uppercase=""
+              class="descriptionLabel"
+              label-text="Add patchset description"
+              value="[[_patchsetDescription]]"
+              placeholder="[[_computeDescriptionPlaceholder(_descriptionReadOnly)]]"
+              read-only="[[_descriptionReadOnly]]"
+              on-changed="_handleDescriptionChanged"
+            ></gr-editable-label>
+          </template>
         </span>
-        <span class\$="[[_computeUploadHelpContainerClass(change, account)]]">
-          <gr-button link="" class="upload" on-click="_handleUploadTap">Update Change</gr-button>
-        </span>
-        <span class="downloadContainer desktop">
-          <gr-button link="" class="download" title="[[createTitle(Shortcut.OPEN_DOWNLOAD_DIALOG,
-                ShortcutSection.ACTIONS)]]" on-click="_handleDownloadTap">Download</gr-button>
-        </span>
-        <span class\$="includedInContainer [[_hideIncludedIn(change)]] desktop">
-          <gr-button link="" class="includedIn" on-click="_handleIncludedInTap">Included In</gr-button>
-        </span>
-        <template is="dom-if" if="[[_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]">
-          <gr-button id="expandBtn" link="" title="[[createTitle(Shortcut.EXPAND_ALL_DIFF_CONTEXT,
-                ShortcutSection.DIFFS)]]" on-click="_expandAllDiffs">Expand All</gr-button>
-          <gr-button id="collapseBtn" link="" on-click="_collapseAllDiffs">Collapse All</gr-button>
-        </template>
-        <template is="dom-if" if="[[!_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]">
-          <div class="warning">
-            Bulk actions disabled because there are too many files.
-          </div>
-        </template>
-        <div class="fileViewActions">
-          <span class="separator"></span>
-          <span class="fileViewActionsLabel">Diff view:</span>
-          <gr-diff-mode-selector id="modeSelect" mode="{{diffViewMode}}" save-on-change="[[!diffPrefsDisabled]]"></gr-diff-mode-selector>
-          <span id="diffPrefsContainer" class="hideOnEdit" hidden\$="[[_computePrefsButtonHidden(diffPrefs, diffPrefsDisabled)]]" hidden="">
-            <gr-button link="" has-tooltip="" title="Diff preferences" class="prefsButton desktop" on-click="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
-          </span>
-        </div>
       </div>
     </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    <div class$="rightControls [[_computeExpandedClass(filesExpanded)]]">
+      <span class="showOnEdit flexContainer">
+        <gr-edit-controls
+          id="editControls"
+          patch-num="[[patchNum]]"
+          change="[[change]]"
+        ></gr-edit-controls>
+        <span class="separator"></span>
+      </span>
+      <span class$="[[_computeUploadHelpContainerClass(change, account)]]">
+        <gr-button link="" class="upload" on-click="_handleUploadTap"
+          >Update Change</gr-button
+        >
+      </span>
+      <span class="downloadContainer desktop">
+        <gr-button
+          link=""
+          class="download"
+          title="[[createTitle(Shortcut.OPEN_DOWNLOAD_DIALOG,
+                ShortcutSection.ACTIONS)]]"
+          on-click="_handleDownloadTap"
+          >Download</gr-button
+        >
+      </span>
+      <span class$="includedInContainer [[_hideIncludedIn(change)]] desktop">
+        <gr-button link="" class="includedIn" on-click="_handleIncludedInTap"
+          >Included In</gr-button
+        >
+      </span>
+      <template
+        is="dom-if"
+        if="[[_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]"
+      >
+        <gr-button
+          id="expandBtn"
+          link=""
+          title="[[createTitle(Shortcut.EXPAND_ALL_DIFF_CONTEXT,
+                ShortcutSection.DIFFS)]]"
+          on-click="_expandAllDiffs"
+          >Expand All</gr-button
+        >
+        <gr-button id="collapseBtn" link="" on-click="_collapseAllDiffs"
+          >Collapse All</gr-button
+        >
+      </template>
+      <template
+        is="dom-if"
+        if="[[!_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]"
+      >
+        <div class="warning">
+          Bulk actions disabled because there are too many files.
+        </div>
+      </template>
+      <div class="fileViewActions">
+        <span class="separator"></span>
+        <span class="fileViewActionsLabel">Diff view:</span>
+        <gr-diff-mode-selector
+          id="modeSelect"
+          mode="{{diffViewMode}}"
+          save-on-change="[[!diffPrefsDisabled]]"
+        ></gr-diff-mode-selector>
+        <span
+          id="diffPrefsContainer"
+          class="hideOnEdit"
+          hidden$="[[_computePrefsButtonHidden(diffPrefs, diffPrefsDisabled)]]"
+          hidden=""
+        >
+          <gr-button
+            link=""
+            has-tooltip=""
+            title="Diff preferences"
+            class="prefsButton desktop"
+            on-click="_handlePrefsTap"
+            ><iron-icon icon="gr-icons:settings"></iron-icon
+          ></gr-button>
+        </span>
+      </div>
+    </div>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html
index ceac41a..19362d5 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-file-list-header</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 eb85cd7..47999cc 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
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../diff/gr-diff-cursor/gr-diff-cursor.js';
 import '../../diff/gr-diff-host/gr-diff-host.js';
 import '../../diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js';
@@ -46,6 +43,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 import {pluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints.js';
 import {pluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
+import {appContext} from '../../../services/app-context.js';
 
 // Maximum length for patch set descriptions.
 const PATCH_DESC_MAX_LENGTH = 500;
@@ -97,7 +95,7 @@
  */
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrFileList extends mixinBehaviors( [
   AsyncForeachBehavior,
@@ -290,6 +288,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   created() {
     super.created();
@@ -375,14 +378,14 @@
     return Promise.all(promises).then(() => {
       this._loading = false;
       this._detectChromiteButler();
-      this.$.reporting.fileListDisplayed();
+      this.reporting.fileListDisplayed();
     });
   }
 
   _detectChromiteButler() {
     const hasButler = !!document.getElementById('butler-suggested-owners');
     if (hasButler) {
-      this.$.reporting.reportExtension('butler');
+      this.reporting.reportExtension('butler');
     }
   }
 
@@ -450,7 +453,7 @@
   _updateDiffPreferences() {
     if (!this.diffs.length) { return; }
     // Re-render all expanded diffs sequentially.
-    this.$.reporting.time(EXPAND_ALL_TIMING_LABEL);
+    this.reporting.time(EXPAND_ALL_TIMING_LABEL);
     this._renderInOrder(this._expandedFiles, this.diffs,
         this._expandedFiles.length);
   }
@@ -1016,7 +1019,7 @@
     // Start the timer for the rendering work hwere because this is where the
     // _shownFiles property is being set, and _shownFiles is used in the
     // dom-repeat binding.
-    this.$.reporting.time(RENDER_TIMING_LABEL);
+    this.reporting.time(RENDER_TIMING_LABEL);
 
     // How many more files are being shown (if it's an increase).
     this._reportinShownFilesIncrement =
@@ -1141,7 +1144,7 @@
     // Required so that the newly created diff view is included in this.diffs.
     flush();
 
-    this.$.reporting.time(EXPAND_ALL_TIMING_LABEL);
+    this.reporting.time(EXPAND_ALL_TIMING_LABEL);
 
     if (newFiles.length) {
       this._renderInOrder(newFiles, this.diffs, newFiles.length);
@@ -1200,7 +1203,7 @@
       this._cancelForEachDiff = null;
       this._nextRenderParams = null;
       console.log('Finished expanding', initialCount, 'diff(s)');
-      this.$.reporting.timeEndWithAverage(EXPAND_ALL_TIMING_LABEL,
+      this.reporting.timeEndWithAverage(EXPAND_ALL_TIMING_LABEL,
           EXPAND_ALL_AVG_TIMING_LABEL, initialCount);
       this.$.diffCursor.handleDiffUpdate();
     }));
@@ -1452,7 +1455,7 @@
   _reportRenderedRow(index) {
     if (index === this._shownFiles.length - 1) {
       this.async(() => {
-        this.$.reporting.timeEndWithAverage(RENDER_TIMING_LABEL,
+        this.reporting.timeEndWithAverage(RENDER_TIMING_LABEL,
             RENDER_AVG_TIMING_LABEL, this._reportinShownFilesIncrement);
       }, 1);
     }
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.js
index 93b8414..e9349c9 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_html.js
@@ -17,428 +17,574 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      .row {
-        align-items: center;
-        border-top: 1px solid var(--border-color);
-        display: flex;
-        min-height: calc(var(--line-height-normal) + 2*var(--spacing-s));
-        padding: var(--spacing-xs) var(--spacing-l) var(--spacing-xs) calc(var(--spacing-l) - .35rem);
-      }
-      :host(.loading) .row {
-        opacity: .5;
-      };
-      :host(.editMode) .hideOnEdit {
-        display: none;
-      }
-      .showOnEdit {
-        display: none;
-      }
-      :host(.editMode) .showOnEdit {
-        display: initial;
-      }
-      .invisible {
-        visibility: hidden;
-      }
-      .header-row {
-        background-color: var(--background-color-secondary);
-      }
-      .controlRow {
-        align-items: center;
-        display: flex;
-        height: 2.25em;
-        justify-content: center;
-      }
-      .controlRow.invisible,
-      .show-hide.invisible {
-        display: none;
-      }
-      .reviewed,
-      .status {
-        align-items: center;
-        display: inline-flex;
-      }
-      .reviewed,
-      .status {
-        display: inline-block;
-        text-align: left;
-        width: 1.5em;
-      }
-      .file-row {
-        cursor: pointer;
-      }
-      .file-row.expanded {
-        border-bottom: 1px solid var(--border-color);
-        position: -webkit-sticky;
-        position: sticky;
-        top: 0;
-        /* Has to visible above the diff view, and by default has a lower
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    .row {
+      align-items: center;
+      border-top: 1px solid var(--border-color);
+      display: flex;
+      min-height: calc(var(--line-height-normal) + 2 * var(--spacing-s));
+      padding: var(--spacing-xs) var(--spacing-l) var(--spacing-xs)
+        calc(var(--spacing-l) - 0.35rem);
+    }
+    :host(.loading) .row {
+      opacity: 0.5;
+    }
+    :host(.editMode) .hideOnEdit {
+      display: none;
+    }
+    .showOnEdit {
+      display: none;
+    }
+    :host(.editMode) .showOnEdit {
+      display: initial;
+    }
+    .invisible {
+      visibility: hidden;
+    }
+    .header-row {
+      background-color: var(--background-color-secondary);
+    }
+    .controlRow {
+      align-items: center;
+      display: flex;
+      height: 2.25em;
+      justify-content: center;
+    }
+    .controlRow.invisible,
+    .show-hide.invisible {
+      display: none;
+    }
+    .reviewed,
+    .status {
+      align-items: center;
+      display: inline-flex;
+    }
+    .reviewed,
+    .status {
+      display: inline-block;
+      text-align: left;
+      width: 1.5em;
+    }
+    .file-row {
+      cursor: pointer;
+    }
+    .file-row.expanded {
+      border-bottom: 1px solid var(--border-color);
+      position: -webkit-sticky;
+      position: sticky;
+      top: 0;
+      /* Has to visible above the diff view, and by default has a lower
          z-index. setting to 1 places it directly above. */
-        z-index: 1;
+      z-index: 1;
+    }
+    .file-row:hover {
+      background-color: var(--hover-background-color);
+    }
+    .file-row.selected {
+      background-color: var(--selection-background-color);
+    }
+    .file-row.expanded,
+    .file-row.expanded:hover {
+      background-color: var(--expanded-background-color);
+    }
+    .path {
+      cursor: pointer;
+      flex: 1;
+      /* Wrap it into multiple lines if too long. */
+      white-space: normal;
+      word-break: break-word;
+    }
+    .oldPath {
+      color: var(--deemphasized-text-color);
+    }
+    .header-stats {
+      text-align: center;
+      min-width: 7.5em;
+    }
+    .stats {
+      text-align: right;
+      min-width: 7.5em;
+    }
+    .comments {
+      padding-left: var(--spacing-l);
+      min-width: 7.5em;
+    }
+    .row:not(.header-row) .stats,
+    .total-stats {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      display: flex;
+    }
+    .sizeBars {
+      margin-left: var(--spacing-m);
+      min-width: 7em;
+      text-align: center;
+    }
+    .sizeBars.hide {
+      display: none;
+    }
+    .added,
+    .removed {
+      display: inline-block;
+      min-width: 3.5em;
+    }
+    .added {
+      color: var(--vote-text-color-recommended);
+    }
+    .removed {
+      color: var(--vote-text-color-disliked);
+      text-align: left;
+      min-width: 4em;
+      padding-left: var(--spacing-s);
+    }
+    .drafts {
+      color: #c62828;
+      font-weight: var(--font-weight-bold);
+    }
+    .show-hide {
+      margin-left: var(--spacing-s);
+      width: 1.9em;
+    }
+    .fileListButton {
+      margin: var(--spacing-m);
+    }
+    .totalChanges {
+      justify-content: flex-end;
+      text-align: right;
+    }
+    .warning {
+      color: var(--deemphasized-text-color);
+    }
+    input.show-hide {
+      display: none;
+    }
+    label.show-hide {
+      cursor: pointer;
+      display: block;
+      min-width: 2em;
+    }
+    gr-diff {
+      display: block;
+      overflow-x: auto;
+    }
+    .truncatedFileName {
+      display: none;
+    }
+    .mobile {
+      display: none;
+    }
+    .reviewed {
+      margin-left: var(--spacing-xxl);
+      width: 15em;
+    }
+    .reviewed label {
+      color: var(--link-color);
+      opacity: 0;
+      justify-content: flex-end;
+      width: 100%;
+    }
+    .reviewed label:hover {
+      cursor: pointer;
+      opacity: 100;
+    }
+    .row:focus {
+      outline: none;
+    }
+    .row:hover .reviewed label,
+    .row:focus .reviewed label,
+    .row.expanded .reviewed label {
+      opacity: 100;
+    }
+    .reviewed input {
+      display: none;
+    }
+    .reviewedLabel {
+      color: var(--deemphasized-text-color);
+      margin-right: var(--spacing-l);
+      opacity: 0;
+    }
+    .reviewedLabel.isReviewed {
+      display: initial;
+      opacity: 100;
+    }
+    .editFileControls {
+      width: 7em;
+    }
+    .markReviewed,
+    .pathLink {
+      display: inline-block;
+      margin: -2px 0;
+      padding: var(--spacing-s) 0;
+      text-decoration: none;
+    }
+    .pathLink:hover {
+      text-decoration: underline;
+    }
+
+    /** copy on file path **/
+    .pathLink gr-copy-clipboard,
+    .oldPath gr-copy-clipboard {
+      display: inline-block;
+      visibility: hidden;
+      vertical-align: bottom;
+      text-decoration: none;
+      --gr-button: {
+        padding: 0px;
       }
-      .file-row:hover {
-        background-color: var(--hover-background-color);
-      }
-      .file-row.selected {
-        background-color: var(--selection-background-color);
-      }
-      .file-row.expanded,
-      .file-row.expanded:hover {
-        background-color: var(--expanded-background-color);
-      }
-      .path {
-        cursor: pointer;
-        flex: 1;
-        /* Wrap it into multiple lines if too long. */
-        white-space: normal;
-        word-break: break-word;
-      }
-      .oldPath {
-        color: var(--deemphasized-text-color);
-      }
-      .header-stats {
-        text-align: center;
-        min-width: 7.5em;
-      }
-      .stats {
-        text-align: right;
-        min-width: 7.5em;
-      }
-      .comments {
-        padding-left: var(--spacing-l);
-        min-width: 7.5em;
-      }
-      .row:not(.header-row) .stats,
-      .total-stats {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        display: flex;
-      }
-      .sizeBars {
-        margin-left: var(--spacing-m);
-        min-width: 7em;
-        text-align: center;
-      }
-      .sizeBars.hide {
-        display: none;
-      }
-      .added,
-      .removed {
-        display: inline-block;
-        min-width: 3.5em;
-      }
-      .added {
-        color: var(--vote-text-color-recommended);
-      }
-      .removed {
-        color: var(--vote-text-color-disliked);
-        text-align: left;
-        min-width: 4em;
-        padding-left: var(--spacing-s);
-      }
-      .drafts {
-        color: #C62828;
-        font-weight: var(--font-weight-bold);
-      }
-      .show-hide {
-        margin-left: var(--spacing-s);
-        width: 1.9em;
-      }
-      .fileListButton {
-        margin: var(--spacing-m);
-      }
-      .totalChanges {
-        justify-content: flex-end;
-        text-align: right;
-      }
-      .warning {
-        color: var(--deemphasized-text-color);
-      }
-      input.show-hide {
-        display: none;
-      }
-      label.show-hide {
-        cursor: pointer;
-        display: block;
-        min-width: 2em;
-      }
-      gr-diff {
-        display: block;
-        overflow-x: auto;
-      }
-      .truncatedFileName {
+    }
+    .pathLink:hover gr-copy-clipboard,
+    .oldPath:hover gr-copy-clipboard {
+      visibility: visible;
+    }
+
+    /** small screen breakpoint: 768px */
+    @media screen and (max-width: 55em) {
+      .desktop {
         display: none;
       }
       .mobile {
+        display: block;
+      }
+      .row.selected {
+        background-color: var(--view-background-color);
+      }
+      .stats {
         display: none;
       }
+      .reviewed,
+      .status {
+        justify-content: flex-start;
+      }
       .reviewed {
-        margin-left: var(--spacing-xxl);
-        width: 15em;
-      }
-      .reviewed label {
-        color: var(--link-color);
-        opacity: 0;
-        justify-content: flex-end;
-        width: 100%;
-      }
-      .reviewed label:hover {
-        cursor: pointer;
-        opacity: 100;
-      }
-      .row:focus {
-        outline: none;
-      }
-      .row:hover .reviewed label,
-      .row:focus .reviewed label,
-      .row.expanded .reviewed label {
-        opacity: 100;
-      }
-      .reviewed input {
         display: none;
       }
-      .reviewedLabel {
-        color: var(--deemphasized-text-color);
-        margin-right: var(--spacing-l);
-        opacity: 0;
+      .comments {
+        min-width: initial;
       }
-      .reviewedLabel.isReviewed {
-        display: initial;
-        opacity: 100;
+      .expanded .fullFileName,
+      .truncatedFileName {
+        display: inline;
       }
-      .editFileControls {
-        width: 7em;
+      .expanded .truncatedFileName,
+      .fullFileName {
+        display: none;
       }
-      .markReviewed,
-      .pathLink {
-        display: inline-block;
-        margin: -2px 0;
-        padding: var(--spacing-s) 0;
-        text-decoration: none;
-      }
-      .pathLink:hover {
-        text-decoration: underline;
-      }
-
-      /** copy on file path **/
-      .pathLink gr-copy-clipboard,
-      .oldPath gr-copy-clipboard {
-        display: inline-block;
-        visibility: hidden;
-        vertical-align: bottom;
-        text-decoration: none;
-        --gr-button: {
-          padding: 0px;
-        }
-      }
-      .pathLink:hover gr-copy-clipboard,
-      .oldPath:hover gr-copy-clipboard {
-        visibility: visible;
-      }
-
-      /** small screen breakpoint: 768px */
-      @media screen and (max-width: 55em) {
-        .desktop {
-          display: none;
-        }
-        .mobile {
-          display: block;
-        }
-        .row.selected {
-          background-color: var(--view-background-color);
-        }
-        .stats {
-          display: none;
-        }
-        .reviewed,
-        .status {
-          justify-content: flex-start;
-        }
-        .reviewed {
-          display: none;
-        }
-        .comments {
-          min-width: initial;
-        }
-        .expanded .fullFileName,
-        .truncatedFileName {
-          display: inline;
-        }
-        .expanded .truncatedFileName,
-        .fullFileName {
-          display: none;
-        }
-      }
-    </style>
-    <div id="container" on-click="_handleFileListClick">
-      <div class="header-row row">
-        <div class="status"></div>
-        <div class="path">File</div>
-        <div class="comments">Comments</div>
-        <div class="sizeBars">Size</div>
-        <div class="header-stats">Delta</div>
-        <template is="dom-if" if="[[_showDynamicColumns]]">
-          <template is="dom-repeat" items="[[_dynamicHeaderEndpoints]]" as="headerEndpoint">
-            <gr-endpoint-decorator name\$="[[headerEndpoint]]">
-            </gr-endpoint-decorator>
-          </template>
-        </template>
-        <!-- Empty div here exists to keep spacing in sync with file rows. -->
-        <div class="reviewed hideOnEdit" hidden\$="[[!_loggedIn]]"></div>
-        <div class="editFileControls showOnEdit"></div>
-        <div class="show-hide"></div>
-      </div>
-
-      <template is="dom-repeat" items="[[_shownFiles]]" id="files" as="file" initial-count="[[fileListIncrement]]" target-framerate="1">
-        [[_reportRenderedRow(index)]]
-        <div class="stickyArea">
-          <div class\$="file-row row [[_computePathClass(file.__path, _expandedFiles.*)]]" data-file\$="[[_computeFileData(file)]]" tabindex="-1">
-              <div class\$="[[_computeClass('status', file.__path)]]" tabindex="0" title\$="[[_computeFileStatusLabel(file.status)]]" aria-label\$="[[_computeFileStatusLabel(file.status)]]">
-              [[_computeFileStatus(file.status)]]
-            </div>
-            <!-- TODO: Remove data-url as it appears its not used -->
-            <span data-url="[[_computeDiffURL(change, patchRange, file.__path, editMode)]]" class="path">
-              <a class="pathLink" href\$="[[_computeDiffURL(change, patchRange, file.__path, editMode)]]">
-                <span title\$="[[computeDisplayPath(file.__path)]]" class="fullFileName">
-                  [[computeDisplayPath(file.__path)]]
-                </span>
-                <span title\$="[[computeDisplayPath(file.__path)]]" class="truncatedFileName">
-                  [[computeTruncatedPath(file.__path)]]
-                </span>
-                <gr-copy-clipboard hide-input="" text="[[file.__path]]"></gr-copy-clipboard>
-              </a>
-              <template is="dom-if" if="[[file.old_path]]">
-                <div class="oldPath" title\$="[[file.old_path]]">
-                  [[file.old_path]]
-                  <gr-copy-clipboard hide-input="" text="[[file.old_path]]"></gr-copy-clipboard>
-                </div>
-              </template>
-            </span>
-            <div class="comments desktop">
-              <span class="drafts">
-                [[_computeDraftsString(changeComments, patchRange, file.__path)]]
-              </span>
-              [[_computeCommentsString(changeComments, patchRange, file.__path)]]
-            </div>
-            <div class="comments mobile">
-              <span class="drafts">
-                [[_computeDraftsStringMobile(changeComments, patchRange,
-                    file.__path)]]
-              </span>
-              [[_computeCommentsStringMobile(changeComments, patchRange,
-                  file.__path)]]
-            </div>
-            <div class\$="[[_computeSizeBarsClass(_showSizeBars, file.__path)]]">
-              <svg width="61" height="8">
-                <rect x\$="[[_computeBarAdditionX(file, _sizeBarLayout)]]" y="0" height="8" fill="#388E3C" width\$="[[_computeBarAdditionWidth(file, _sizeBarLayout)]]"></rect>
-                <rect x\$="[[_computeBarDeletionX(_sizeBarLayout)]]" y="0" height="8" fill="#D32F2F" width\$="[[_computeBarDeletionWidth(file, _sizeBarLayout)]]"></rect>
-              </svg>
-            </div>
-            <div class\$="[[_computeClass('stats', file.__path)]]">
-              <span class="added" tabindex="0" aria-label\$="[[file.lines_inserted]] lines added" hidden\$="[[file.binary]]">
-                +[[file.lines_inserted]]
-              </span>
-              <span class="removed" tabindex="0" aria-label\$="[[file.lines_deleted]] lines removed" hidden\$="[[file.binary]]">
-                -[[file.lines_deleted]]
-              </span>
-              <span class\$="[[_computeBinaryClass(file.size_delta)]]" hidden\$="[[!file.binary]]">
-                [[_formatBytes(file.size_delta)]]
-                [[_formatPercentage(file.size, file.size_delta)]]
-              </span>
-            </div>
-            <template is="dom-if" if="[[_showDynamicColumns]]">
-              <template is="dom-repeat" items="[[_dynamicContentEndpoints]]" as="contentEndpoint">
-                <div class\$="[[_computeClass('', file.__path)]]">
-                  <gr-endpoint-decorator name="[[contentEndpoint]]">
-                    <gr-endpoint-param name="changeNum" value="[[changeNum]]">
-                    </gr-endpoint-param>
-                    <gr-endpoint-param name="patchRange" value="[[patchRange]]">
-                    </gr-endpoint-param>
-                    <gr-endpoint-param name="path" value="[[file.__path]]">
-                    </gr-endpoint-param>
-                  </gr-endpoint-decorator>
-                </div>
-              </template>
-            </template>
-            <div class="reviewed hideOnEdit" hidden\$="[[!_loggedIn]]" hidden="">
-              <span class\$="reviewedLabel [[_computeReviewedClass(file.isReviewed)]]">Reviewed</span>
-              <label>
-                <input class="reviewed" type="checkbox" checked="[[file.isReviewed]]">
-                <span class="markReviewed" title\$="[[_reviewedTitle(file.isReviewed)]]">[[_computeReviewedText(file.isReviewed)]]</span>
-              </label>
-            </div>
-            <div class="editFileControls showOnEdit">
-              <template is="dom-if" if="[[editMode]]">
-                <gr-edit-file-controls class\$="[[_computeClass('', file.__path)]]" file-path="[[file.__path]]"></gr-edit-file-controls>
-              </template>
-            </div>
-            <div class="show-hide">
-              <label class="show-hide" data-path\$="[[file.__path]]" data-expand="true">
-                <input type="checkbox" class="show-hide" checked\$="[[_isFileExpanded(file.__path, _expandedFiles.*)]]" data-path\$="[[file.__path]]" data-expand="true">
-                  <iron-icon id="icon" icon="[[_computeShowHideIcon(file.__path, _expandedFiles.*)]]">
-                  </iron-icon>
-              </label>
-            </div>
-          </div>
-          <template is="dom-if" if="[[_isFileExpanded(file.__path, _expandedFiles.*)]]">
-            <gr-diff-host no-auto-render="" show-load-failure="" display-line="[[_displayLine]]" hidden="[[!_isFileExpanded(file.__path, _expandedFiles.*)]]" change-num="[[changeNum]]" patch-range="[[patchRange]]" path="[[file.__path]]" prefs="[[diffPrefs]]" project-name="[[change.project]]" no-render-on-prefs-change="" view-mode="[[diffViewMode]]"></gr-diff-host>
-          </template>
-        </div>
-      </template>
-    </div>
-    <div class="row totalChanges" hidden\$="[[_hideChangeTotals]]">
-      <div class="total-stats">
-        <span class="added" tabindex="0" aria-label\$="[[_patchChange.inserted]] lines added">
-          +[[_patchChange.inserted]]
-        </span>
-        <span class="removed" tabindex="0" aria-label\$="[[_patchChange.deleted]] lines removed">
-          -[[_patchChange.deleted]]
-        </span>
-      </div>
+    }
+  </style>
+  <div id="container" on-click="_handleFileListClick">
+    <div class="header-row row">
+      <div class="status"></div>
+      <div class="path">File</div>
+      <div class="comments">Comments</div>
+      <div class="sizeBars">Size</div>
+      <div class="header-stats">Delta</div>
       <template is="dom-if" if="[[_showDynamicColumns]]">
-        <template is="dom-repeat" items="[[_dynamicSummaryEndpoints]]" as="summaryEndpoint">
-          <gr-endpoint-decorator name="[[summaryEndpoint]]">
+        <template
+          is="dom-repeat"
+          items="[[_dynamicHeaderEndpoints]]"
+          as="headerEndpoint"
+        >
+          <gr-endpoint-decorator name$="[[headerEndpoint]]">
           </gr-endpoint-decorator>
         </template>
       </template>
       <!-- Empty div here exists to keep spacing in sync with file rows. -->
-      <div class="reviewed hideOnEdit" hidden\$="[[!_loggedIn]]"></div>
+      <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]"></div>
       <div class="editFileControls showOnEdit"></div>
       <div class="show-hide"></div>
     </div>
-    <div class="row totalChanges" hidden\$="[[_hideBinaryChangeTotals]]">
-      <div class="total-stats">
-        <span class="added" aria-label="Total lines added">
-          [[_formatBytes(_patchChange.size_delta_inserted)]]
-          [[_formatPercentage(_patchChange.total_size,
-              _patchChange.size_delta_inserted)]]
-        </span>
-        <span class="removed" aria-label="Total lines removed">
-          [[_formatBytes(_patchChange.size_delta_deleted)]]
-          [[_formatPercentage(_patchChange.total_size,
-              _patchChange.size_delta_deleted)]]
-        </span>
+
+    <template
+      is="dom-repeat"
+      items="[[_shownFiles]]"
+      id="files"
+      as="file"
+      initial-count="[[fileListIncrement]]"
+      target-framerate="1"
+    >
+      [[_reportRenderedRow(index)]]
+      <div class="stickyArea">
+        <div
+          class$="file-row row [[_computePathClass(file.__path, _expandedFiles.*)]]"
+          data-file$="[[_computeFileData(file)]]"
+          tabindex="-1"
+        >
+          <div
+            class$="[[_computeClass('status', file.__path)]]"
+            tabindex="0"
+            title$="[[_computeFileStatusLabel(file.status)]]"
+            aria-label$="[[_computeFileStatusLabel(file.status)]]"
+          >
+            [[_computeFileStatus(file.status)]]
+          </div>
+          <!-- TODO: Remove data-url as it appears its not used -->
+          <span
+            data-url="[[_computeDiffURL(change, patchRange, file.__path, editMode)]]"
+            class="path"
+          >
+            <a
+              class="pathLink"
+              href$="[[_computeDiffURL(change, patchRange, file.__path, editMode)]]"
+            >
+              <span
+                title$="[[computeDisplayPath(file.__path)]]"
+                class="fullFileName"
+              >
+                [[computeDisplayPath(file.__path)]]
+              </span>
+              <span
+                title$="[[computeDisplayPath(file.__path)]]"
+                class="truncatedFileName"
+              >
+                [[computeTruncatedPath(file.__path)]]
+              </span>
+              <gr-copy-clipboard
+                hide-input=""
+                text="[[file.__path]]"
+              ></gr-copy-clipboard>
+            </a>
+            <template is="dom-if" if="[[file.old_path]]">
+              <div class="oldPath" title$="[[file.old_path]]">
+                [[file.old_path]]
+                <gr-copy-clipboard
+                  hide-input=""
+                  text="[[file.old_path]]"
+                ></gr-copy-clipboard>
+              </div>
+            </template>
+          </span>
+          <div class="comments desktop">
+            <span class="drafts">
+              [[_computeDraftsString(changeComments, patchRange, file.__path)]]
+            </span>
+            [[_computeCommentsString(changeComments, patchRange, file.__path)]]
+          </div>
+          <div class="comments mobile">
+            <span class="drafts">
+              [[_computeDraftsStringMobile(changeComments, patchRange,
+              file.__path)]]
+            </span>
+            [[_computeCommentsStringMobile(changeComments, patchRange,
+            file.__path)]]
+          </div>
+          <div class$="[[_computeSizeBarsClass(_showSizeBars, file.__path)]]">
+            <svg width="61" height="8">
+              <rect
+                x$="[[_computeBarAdditionX(file, _sizeBarLayout)]]"
+                y="0"
+                height="8"
+                fill="#388E3C"
+                width$="[[_computeBarAdditionWidth(file, _sizeBarLayout)]]"
+              ></rect>
+              <rect
+                x$="[[_computeBarDeletionX(_sizeBarLayout)]]"
+                y="0"
+                height="8"
+                fill="#D32F2F"
+                width$="[[_computeBarDeletionWidth(file, _sizeBarLayout)]]"
+              ></rect>
+            </svg>
+          </div>
+          <div class$="[[_computeClass('stats', file.__path)]]">
+            <span
+              class="added"
+              tabindex="0"
+              aria-label$="[[file.lines_inserted]] lines added"
+              hidden$="[[file.binary]]"
+            >
+              +[[file.lines_inserted]]
+            </span>
+            <span
+              class="removed"
+              tabindex="0"
+              aria-label$="[[file.lines_deleted]] lines removed"
+              hidden$="[[file.binary]]"
+            >
+              -[[file.lines_deleted]]
+            </span>
+            <span
+              class$="[[_computeBinaryClass(file.size_delta)]]"
+              hidden$="[[!file.binary]]"
+            >
+              [[_formatBytes(file.size_delta)]] [[_formatPercentage(file.size,
+              file.size_delta)]]
+            </span>
+          </div>
+          <template is="dom-if" if="[[_showDynamicColumns]]">
+            <template
+              is="dom-repeat"
+              items="[[_dynamicContentEndpoints]]"
+              as="contentEndpoint"
+            >
+              <div class$="[[_computeClass('', file.__path)]]">
+                <gr-endpoint-decorator name="[[contentEndpoint]]">
+                  <gr-endpoint-param name="changeNum" value="[[changeNum]]">
+                  </gr-endpoint-param>
+                  <gr-endpoint-param name="patchRange" value="[[patchRange]]">
+                  </gr-endpoint-param>
+                  <gr-endpoint-param name="path" value="[[file.__path]]">
+                  </gr-endpoint-param>
+                </gr-endpoint-decorator>
+              </div>
+            </template>
+          </template>
+          <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]" hidden="">
+            <span
+              class$="reviewedLabel [[_computeReviewedClass(file.isReviewed)]]"
+              >Reviewed</span
+            >
+            <label>
+              <input
+                class="reviewed"
+                type="checkbox"
+                checked="[[file.isReviewed]]"
+              />
+              <span
+                class="markReviewed"
+                title$="[[_reviewedTitle(file.isReviewed)]]"
+                >[[_computeReviewedText(file.isReviewed)]]</span
+              >
+            </label>
+          </div>
+          <div class="editFileControls showOnEdit">
+            <template is="dom-if" if="[[editMode]]">
+              <gr-edit-file-controls
+                class$="[[_computeClass('', file.__path)]]"
+                file-path="[[file.__path]]"
+              ></gr-edit-file-controls>
+            </template>
+          </div>
+          <div class="show-hide">
+            <label
+              class="show-hide"
+              data-path$="[[file.__path]]"
+              data-expand="true"
+            >
+              <input
+                type="checkbox"
+                class="show-hide"
+                checked$="[[_isFileExpanded(file.__path, _expandedFiles.*)]]"
+                data-path$="[[file.__path]]"
+                data-expand="true"
+              />
+              <iron-icon
+                id="icon"
+                icon="[[_computeShowHideIcon(file.__path, _expandedFiles.*)]]"
+              >
+              </iron-icon>
+            </label>
+          </div>
+        </div>
+        <template
+          is="dom-if"
+          if="[[_isFileExpanded(file.__path, _expandedFiles.*)]]"
+        >
+          <gr-diff-host
+            no-auto-render=""
+            show-load-failure=""
+            display-line="[[_displayLine]]"
+            hidden="[[!_isFileExpanded(file.__path, _expandedFiles.*)]]"
+            change-num="[[changeNum]]"
+            patch-range="[[patchRange]]"
+            path="[[file.__path]]"
+            prefs="[[diffPrefs]]"
+            project-name="[[change.project]]"
+            no-render-on-prefs-change=""
+            view-mode="[[diffViewMode]]"
+          ></gr-diff-host>
+        </template>
       </div>
+    </template>
+  </div>
+  <div class="row totalChanges" hidden$="[[_hideChangeTotals]]">
+    <div class="total-stats">
+      <span
+        class="added"
+        tabindex="0"
+        aria-label$="[[_patchChange.inserted]] lines added"
+      >
+        +[[_patchChange.inserted]]
+      </span>
+      <span
+        class="removed"
+        tabindex="0"
+        aria-label$="[[_patchChange.deleted]] lines removed"
+      >
+        -[[_patchChange.deleted]]
+      </span>
     </div>
-    <div class\$="row controlRow [[_computeFileListControlClass(numFilesShown, _files)]]">
-      <gr-button class="fileListButton" id="incrementButton" link="" on-click="_incrementNumFilesShown">
-        [[_computeIncrementText(numFilesShown, _files)]]
-      </gr-button>
-      <gr-tooltip-content has-tooltip="[[_computeWarnShowAll(_files)]]" show-icon="[[_computeWarnShowAll(_files)]]" title\$="[[_computeShowAllWarning(_files)]]">
-        <gr-button class="fileListButton" id="showAllButton" link="" on-click="_showAllFiles">
-          [[_computeShowAllText(_files)]]
-        </gr-button><!--
+    <template is="dom-if" if="[[_showDynamicColumns]]">
+      <template
+        is="dom-repeat"
+        items="[[_dynamicSummaryEndpoints]]"
+        as="summaryEndpoint"
+      >
+        <gr-endpoint-decorator name="[[summaryEndpoint]]">
+        </gr-endpoint-decorator>
+      </template>
+    </template>
+    <!-- Empty div here exists to keep spacing in sync with file rows. -->
+    <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]"></div>
+    <div class="editFileControls showOnEdit"></div>
+    <div class="show-hide"></div>
+  </div>
+  <div class="row totalChanges" hidden$="[[_hideBinaryChangeTotals]]">
+    <div class="total-stats">
+      <span class="added" aria-label="Total lines added">
+        [[_formatBytes(_patchChange.size_delta_inserted)]]
+        [[_formatPercentage(_patchChange.total_size,
+        _patchChange.size_delta_inserted)]]
+      </span>
+      <span class="removed" aria-label="Total lines removed">
+        [[_formatBytes(_patchChange.size_delta_deleted)]]
+        [[_formatPercentage(_patchChange.total_size,
+        _patchChange.size_delta_deleted)]]
+      </span>
+    </div>
+  </div>
+  <div
+    class$="row controlRow [[_computeFileListControlClass(numFilesShown, _files)]]"
+  >
+    <gr-button
+      class="fileListButton"
+      id="incrementButton"
+      link=""
+      on-click="_incrementNumFilesShown"
+    >
+      [[_computeIncrementText(numFilesShown, _files)]]
+    </gr-button>
+    <gr-tooltip-content
+      has-tooltip="[[_computeWarnShowAll(_files)]]"
+      show-icon="[[_computeWarnShowAll(_files)]]"
+      title$="[[_computeShowAllWarning(_files)]]"
+    >
+      <gr-button
+        class="fileListButton"
+        id="showAllButton"
+        link=""
+        on-click="_showAllFiles"
+      >
+        [[_computeShowAllText(_files)]] </gr-button
+      ><!--
   --></gr-tooltip-content>
-    </div>
-    <gr-diff-preferences-dialog id="diffPreferencesDialog" diff-prefs="{{diffPrefs}}" on-reload-diff-preference="_handleReloadingDiffPreference">
-    </gr-diff-preferences-dialog>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
-    <gr-diff-cursor id="diffCursor"></gr-diff-cursor>
-    <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>
+  </div>
+  <gr-diff-preferences-dialog
+    id="diffPreferencesDialog"
+    diff-prefs="{{diffPrefs}}"
+    on-reload-diff-preference="_handleReloadingDiffPreference"
+  >
+  </gr-diff-preferences-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-storage id="storage"></gr-storage>
+  <gr-diff-cursor id="diffCursor"></gr-diff-cursor>
+  <gr-cursor-manager
+    id="fileCursor"
+    scroll-behavior="keep-visible"
+    focus-on-move=""
+    cursor-target-class="selected"
+  ></gr-cursor-manager>
 `;
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 e0c70a4..a5bb030 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-file-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -44,9 +45,9 @@
 <script type="module">
 import '../../../test/common-test-setup.js';
 import '../../diff/gr-comment-api/gr-comment-api.js';
-import {getMockDiffResponse} from '../../../test/mock-diff-response.js';
+import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
 import './gr-file-list.js';
-import '../../diff/gr-comment-api/gr-comment-api-mock_test.js';
+import '../../../test/mocks/comment-api.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {KeyboardShortcutBinder} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 import {GrFileListConstants} from '../gr-file-list-constants.js';
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 f79dba3..132cd16 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
@@ -26,7 +24,7 @@
 import {htmlTemplate} from './gr-included-in-dialog_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrIncludedInDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.js b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.js
index b7d455c..f5948e8 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_html.js
@@ -17,74 +17,85 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        background-color: var(--dialog-background-color);
-        display: block;
-        max-height: 80vh;
-        overflow-y: auto;
-        padding: 4.5em var(--spacing-l) var(--spacing-l) var(--spacing-l);
-      }
-      header {
-        background-color: var(--dialog-background-color);
-        border-bottom: 1px solid var(--border-color);
-        left: 0;
-        padding: var(--spacing-l);
-        position: absolute;
-        right: 0;
-        top: 0;
-      }
-      #title {
-        display: inline-block;
-        font-family: var(--header-font-family);
-        font-size: var(--font-size-h3);
-        font-weight: var(--font-weight-h3);
-        line-height: var(--line-height-h3);
-        margin-top: var(--spacing-xs);
-      }
-      #filterInput {
-        display: inline-block;
-        float: right;
-        margin: 0 var(--spacing-l);
-        padding: var(--spacing-xs);
-      }
-      .closeButtonContainer {
-        float: right;
-      }
-      ul {
-        margin-bottom: var(--spacing-l);
-      }
-      ul li {
-        border: 1px solid var(--border-color);
-        border-radius: var(--border-radius);
-        background: var(--chip-background-color);
-        display: inline-block;
-        margin: 0 var(--spacing-xs) var(--spacing-s) var(--spacing-xs);
-        padding: var(--spacing-xs) var(--spacing-s);
-      }
-      .loading.loaded {
-        display: none;
-      }
-    </style>
-    <header>
-      <h1 id="title">Included In:</h1>
-      <span class="closeButtonContainer">
-        <gr-button id="closeButton" link="" on-click="_handleCloseTap">Close</gr-button>
-      </span>
-      <iron-input placeholder="Filter" on-bind-value-changed="_onFilterChanged">
-        <input id="filterInput" is="iron-input" placeholder="Filter" on-bind-value-changed="_onFilterChanged">
-      </iron-input>
-    </header>
-    <div class\$="[[_computeLoadingClass(_loaded)]]">Loading...</div>
-    <template is="dom-repeat" items="[[_computeGroups(_includedIn, _filterText)]]" as="group">
-      <div>
-        <span>[[group.title]]:</span>
-        <ul>
-          <template is="dom-repeat" items="[[group.items]]">
-            <li>[[item]]</li>
-          </template>
-        </ul>
-      </div>
-    </template>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      background-color: var(--dialog-background-color);
+      display: block;
+      max-height: 80vh;
+      overflow-y: auto;
+      padding: 4.5em var(--spacing-l) var(--spacing-l) var(--spacing-l);
+    }
+    header {
+      background-color: var(--dialog-background-color);
+      border-bottom: 1px solid var(--border-color);
+      left: 0;
+      padding: var(--spacing-l);
+      position: absolute;
+      right: 0;
+      top: 0;
+    }
+    #title {
+      display: inline-block;
+      font-family: var(--header-font-family);
+      font-size: var(--font-size-h3);
+      font-weight: var(--font-weight-h3);
+      line-height: var(--line-height-h3);
+      margin-top: var(--spacing-xs);
+    }
+    #filterInput {
+      display: inline-block;
+      float: right;
+      margin: 0 var(--spacing-l);
+      padding: var(--spacing-xs);
+    }
+    .closeButtonContainer {
+      float: right;
+    }
+    ul {
+      margin-bottom: var(--spacing-l);
+    }
+    ul li {
+      border: 1px solid var(--border-color);
+      border-radius: var(--border-radius);
+      background: var(--chip-background-color);
+      display: inline-block;
+      margin: 0 var(--spacing-xs) var(--spacing-s) var(--spacing-xs);
+      padding: var(--spacing-xs) var(--spacing-s);
+    }
+    .loading.loaded {
+      display: none;
+    }
+  </style>
+  <header>
+    <h1 id="title">Included In:</h1>
+    <span class="closeButtonContainer">
+      <gr-button id="closeButton" link="" on-click="_handleCloseTap"
+        >Close</gr-button
+      >
+    </span>
+    <iron-input placeholder="Filter" on-bind-value-changed="_onFilterChanged">
+      <input
+        id="filterInput"
+        is="iron-input"
+        placeholder="Filter"
+        on-bind-value-changed="_onFilterChanged"
+      />
+    </iron-input>
+  </header>
+  <div class$="[[_computeLoadingClass(_loaded)]]">Loading...</div>
+  <template
+    is="dom-repeat"
+    items="[[_computeGroups(_includedIn, _filterText)]]"
+    as="group"
+  >
+    <div>
+      <span>[[group.title]]:</span>
+      <ul>
+        <template is="dom-repeat" items="[[group.items]]">
+          <li>[[item]]</li>
+        </template>
+      </ul>
+    </div>
+  </template>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html
index c87c54f..fe6119e 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-included-in-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 8541840..5b9730b 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-selector/iron-selector.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../../styles/gr-voting-styles.js';
@@ -25,7 +23,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-label-score-row_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrLabelScoreRow extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.js b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.js
index 1b5b425..77148ad 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.js
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.js
@@ -17,99 +17,128 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-voting-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="shared-styles">
-      .labelNameCell,
-      .buttonsCell,
+  <style include="gr-voting-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="shared-styles">
+    .labelNameCell,
+    .buttonsCell,
+    .selectedValueCell {
+      padding: var(--spacing-s) var(--spacing-m);
+      display: table-cell;
+    }
+    /* We want the :hover highlight to extend to the border of the dialog. */
+    .labelNameCell {
+      padding-left: var(--spacing-xl);
+    }
+    .selectedValueCell {
+      padding-right: var(--spacing-xl);
+    }
+    /* This is a trick to let the selectedValueCell take the remaining width. */
+    .labelNameCell,
+    .buttonsCell {
+      white-space: nowrap;
+    }
+    .selectedValueCell {
+      width: 75%;
+    }
+    .labelMessage {
+      color: var(--deemphasized-text-color);
+    }
+    gr-button {
+      min-width: 42px;
+      box-sizing: border-box;
+      --gr-button: {
+        background-color: var(
+          --button-background-color,
+          var(--table-header-background-color)
+        );
+        color: var(--primary-text-color);
+        padding: 0 var(--spacing-m);
+        @apply --vote-chip-styles;
+      }
+    }
+    gr-button.iron-selected[vote='max'] {
+      --button-background-color: var(--vote-color-approved);
+    }
+    gr-button.iron-selected[vote='positive'] {
+      --button-background-color: var(--vote-color-recommended);
+    }
+    gr-button.iron-selected[vote='min'] {
+      --button-background-color: var(--vote-color-rejected);
+    }
+    gr-button.iron-selected[vote='negative'] {
+      --button-background-color: var(--vote-color-disliked);
+    }
+    gr-button.iron-selected[vote='neutral'] {
+      --button-background-color: var(--vote-color-neutral);
+    }
+    .placeholder {
+      display: inline-block;
+      width: 42px;
+      height: 1px;
+    }
+    .placeholder::before {
+      content: ' ';
+    }
+    .selectedValueCell {
+      color: var(--deemphasized-text-color);
+      font-style: italic;
+    }
+    .selectedValueCell.hidden {
+      display: none;
+    }
+    @media only screen and (max-width: 50em) {
       .selectedValueCell {
-        padding: var(--spacing-s) var(--spacing-m);
-        display: table-cell;
-      }
-      /* We want the :hover highlight to extend to the border of the dialog. */
-      .labelNameCell {
-        padding-left: var(--spacing-xl);
-      }
-      .selectedValueCell {
-        padding-right: var(--spacing-xl);
-      }
-      /* This is a trick to let the selectedValueCell take the remaining width. */
-      .labelNameCell,
-      .buttonsCell {
-        white-space: nowrap;
-      }
-      .selectedValueCell {
-        width: 75%;
-      }
-      .labelMessage {
-        color: var(--deemphasized-text-color);
-      }
-      gr-button {
-        min-width: 42px;
-        box-sizing: border-box;
-        --gr-button: {
-          background-color: var(--button-background-color, var(--table-header-background-color));
-          color: var(--primary-text-color);
-          padding: 0 var(--spacing-m);
-          @apply --vote-chip-styles;
-        }
-      }
-      gr-button.iron-selected[vote="max"] {
-        --button-background-color: var(--vote-color-approved);
-      }
-      gr-button.iron-selected[vote="positive"] {
-        --button-background-color: var(--vote-color-recommended);
-      }
-      gr-button.iron-selected[vote="min"] {
-        --button-background-color: var(--vote-color-rejected);
-      }
-      gr-button.iron-selected[vote="negative"] {
-        --button-background-color: var(--vote-color-disliked);
-      }
-      gr-button.iron-selected[vote="neutral"] {
-        --button-background-color: var(--vote-color-neutral);
-      }
-      .placeholder {
-        display: inline-block;
-        width: 42px;
-        height: 1px;
-      }
-      .placeholder::before {
-        content: ' ';
-      }
-      .selectedValueCell {
-        color: var(--deemphasized-text-color);
-        font-style: italic;
-      }
-      .selectedValueCell.hidden {
         display: none;
       }
-      @media only screen and (max-width: 50em) {
-        .selectedValueCell {
-          display: none;
-        }
-      }
-    </style>
-    <span class="labelNameCell">[[label.name]]</span>
-    <div class="buttonsCell">
-      <template is="dom-repeat" items="[[_computeBlankItems(permittedLabels, label.name, 'start')]]" as="value">
-        <span class="placeholder" data-label\$="[[label.name]]"></span>
+    }
+  </style>
+  <span class="labelNameCell">[[label.name]]</span>
+  <div class="buttonsCell">
+    <template
+      is="dom-repeat"
+      items="[[_computeBlankItems(permittedLabels, label.name, 'start')]]"
+      as="value"
+    >
+      <span class="placeholder" data-label$="[[label.name]]"></span>
+    </template>
+    <iron-selector
+      id="labelSelector"
+      attr-for-selected="data-value"
+      selected="[[_computeLabelValue(labels, permittedLabels, label)]]"
+      hidden$="[[!_computeAnyPermittedLabelValues(permittedLabels, label.name)]]"
+      on-selected-item-changed="_setSelectedValueText"
+    >
+      <template is="dom-repeat" items="[[_items]]" as="value">
+        <gr-button
+          vote$="[[_computeVoteAttribute(value, index, _items.length)]]"
+          has-tooltip=""
+          data-name$="[[label.name]]"
+          data-value$="[[value]]"
+          title$="[[_computeLabelValueTitle(labels, label.name, value)]]"
+        >
+          [[value]]</gr-button
+        >
       </template>
-      <iron-selector id="labelSelector" attr-for-selected="data-value" selected="[[_computeLabelValue(labels, permittedLabels, label)]]" hidden\$="[[!_computeAnyPermittedLabelValues(permittedLabels, label.name)]]" on-selected-item-changed="_setSelectedValueText">
-        <template is="dom-repeat" items="[[_items]]" as="value">
-          <gr-button vote\$="[[_computeVoteAttribute(value, index, _items.length)]]" has-tooltip="" data-name\$="[[label.name]]" data-value\$="[[value]]" title\$="[[_computeLabelValueTitle(labels, label.name, value)]]">
-            [[value]]</gr-button>
-        </template>
-      </iron-selector>
-      <template is="dom-repeat" items="[[_computeBlankItems(permittedLabels, label.name, 'end')]]" as="value">
-        <span class="placeholder" data-label\$="[[label.name]]"></span>
-      </template>
-      <span class="labelMessage" hidden\$="[[_computeAnyPermittedLabelValues(permittedLabels, label.name)]]">
-        You don't have permission to edit this label.
-      </span>
-    </div>
-    <div class\$="selectedValueCell [[_computeHiddenClass(permittedLabels, label.name)]]">
-      <span id="selectedValueLabel">[[_selectedValueText]]</span>
-    </div>
+    </iron-selector>
+    <template
+      is="dom-repeat"
+      items="[[_computeBlankItems(permittedLabels, label.name, 'end')]]"
+      as="value"
+    >
+      <span class="placeholder" data-label$="[[label.name]]"></span>
+    </template>
+    <span
+      class="labelMessage"
+      hidden$="[[_computeAnyPermittedLabelValues(permittedLabels, label.name)]]"
+    >
+      You don't have permission to edit this label.
+    </span>
+  </div>
+  <div
+    class$="selectedValueCell [[_computeHiddenClass(permittedLabels, label.name)]]"
+  >
+    <span id="selectedValueLabel">[[_selectedValueText]]</span>
+  </div>
 `;
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 374472f..6e0a90d 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-label-score-row</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 2d6825b..35ccc3c 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import '../gr-label-score-row/gr-label-score-row.js';
 import '../../../styles/shared-styles.js';
@@ -24,7 +22,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-label-scores_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrLabelScores extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.js b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.js
index b9c53c3..1e3e7ec 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.js
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_html.js
@@ -17,32 +17,39 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .scoresTable {
-        display: table;
-        width: 100%;
-      }
-      .mergedMessage {
-        font-style: italic;
-        text-align: center;
-        width: 100%;
-      }
-      gr-label-score-row:hover {
-        background-color: var(--hover-background-color);
-      }
-      gr-label-score-row {
-        display: table-row;
-      }
-      gr-label-score-row.no-access {
-        display: var(--label-no-access-display, table-row);
-      }
-    </style>
-    <div class="scoresTable">
-      <template is="dom-repeat" items="[[_labels]]" as="label">
-        <gr-label-score-row class\$="[[_computeLabelAccessClass(label.name, permittedLabels)]]" label="[[label]]" name="[[label.name]]" labels="[[change.labels]]" permitted-labels="[[permittedLabels]]" label-values="[[_labelValues]]"></gr-label-score-row>
-      </template>
-    </div>
-    <div class="mergedMessage" hidden\$="[[!_changeIsMerged(change.status)]]">
-      Because this change has been merged, votes may not be decreased.
-    </div>
+  <style include="shared-styles">
+    .scoresTable {
+      display: table;
+      width: 100%;
+    }
+    .mergedMessage {
+      font-style: italic;
+      text-align: center;
+      width: 100%;
+    }
+    gr-label-score-row:hover {
+      background-color: var(--hover-background-color);
+    }
+    gr-label-score-row {
+      display: table-row;
+    }
+    gr-label-score-row.no-access {
+      display: var(--label-no-access-display, table-row);
+    }
+  </style>
+  <div class="scoresTable">
+    <template is="dom-repeat" items="[[_labels]]" as="label">
+      <gr-label-score-row
+        class$="[[_computeLabelAccessClass(label.name, permittedLabels)]]"
+        label="[[label]]"
+        name="[[label.name]]"
+        labels="[[change.labels]]"
+        permitted-labels="[[permittedLabels]]"
+        label-values="[[_labelValues]]"
+      ></gr-label-score-row>
+    </template>
+  </div>
+  <div class="mergedMessage" hidden$="[[!_changeIsMerged(change.status)]]">
+    Because this change has been merged, votes may not be decreased.
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
index a01454c0..7ee86d6 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-label-scores</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 101f304..400b122 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '@polymer/iron-icon/iron-icon.js';
 import '../../shared/gr-account-label/gr-account-label.js';
@@ -26,6 +25,8 @@
 import '../../../styles/shared-styles.js';
 import '../../../styles/gr-voting-styles.js';
 import '../gr-comment-list/gr-comment-list.js';
+import {appContext} from '../../../services/app-context.js';
+import {ExperimentIds} from '../../../services/flags.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
@@ -35,7 +36,7 @@
 const LABEL_TITLE_SCORE_PATTERN = /^(-?)([A-Za-z0-9-]+?)([+-]\d+)?$/;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrMessage extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
@@ -62,6 +63,8 @@
 
   static get properties() {
     return {
+      /** @type {?} */
+      change: Object,
       changeNum: Number,
       /** @type {?} */
       message: Object,
@@ -69,6 +72,19 @@
         type: Object,
         computed: '_computeAuthor(message)',
       },
+      /**
+       * Used in cleaner_changelog experiment
+       *
+       * @type {Array} - array of threads attached to the message
+       */
+      commentThreads: {
+        type: Array,
+      },
+      /**
+       * TODO(taoalpha): remove once the change log experiment is launched
+       *
+       * @type {Object} - a map on file and comments on it
+       */
       comments: {
         type: Object,
       },
@@ -126,7 +142,7 @@
       },
       _commentCountText: {
         type: Number,
-        computed: '_computeCommentCountText(comments)',
+        computed: '_computeCommentCountText(comments, commentThreads)',
       },
       _loggedIn: {
         type: Boolean,
@@ -140,6 +156,7 @@
         type: Boolean,
         value: false,
       },
+      _isCleanerLogExperimentEnabled: Boolean,
     };
   }
 
@@ -149,6 +166,11 @@
     ];
   }
 
+  constructor() {
+    super();
+    this.flagsService = appContext.flagsService;
+  }
+
   /** @override */
   created() {
     super.created();
@@ -159,6 +181,8 @@
   /** @override */
   ready() {
     super.ready();
+    this._isCleanerLogExperimentEnabled = this.flagsService
+        .isEnabled(ExperimentIds.CLEANER_CHANGELOG);
     this.$.restAPI.getConfig().then(config => {
       this.config = config;
     });
@@ -178,22 +202,44 @@
     }
   }
 
-  _computeCommentCountText(comments) {
-    if (!comments) return undefined;
-    let count = 0;
-    for (const file in comments) {
-      if (comments.hasOwnProperty(file)) {
-        const commentArray = comments[file] || [];
-        count += commentArray.length;
+  _computeCommentCountText(comments, threads) {
+    // TODO(taoalpha): clean up after cleaner-changelog experiment launched
+    if (this._isCleanerLogExperimentEnabled) {
+      if (!threads) return undefined;
+      const count = threads.length;
+      if (count === 0) {
+        return undefined;
+      } else if (count === 1) {
+        return '1 comment';
+      } else {
+        return `${count} comments`;
+      }
+    } else {
+      if (!comments) return undefined;
+      let count = 0;
+      for (const file in comments) {
+        if (comments.hasOwnProperty(file)) {
+          const commentArray = comments[file] || [];
+          count += commentArray.length;
+        }
+      }
+      if (count === 0) {
+        return undefined;
+      } else if (count === 1) {
+        return '1 comment';
+      } else {
+        return `${count} comments`;
       }
     }
-    if (count === 0) {
-      return undefined;
-    } else if (count === 1) {
-      return '1 comment';
-    } else {
-      return `${count} comments`;
-    }
+  }
+
+  _onThreadListModified() {
+    // TODO(taoalpha): this won't propogate the changes to the files
+    // should consider replacing this with either top level events
+    // or gerrit level events
+
+    // emit the event so change-view can also get updated with latest changes
+    this.fire('comment-refresh');
   }
 
   _computeMessageContentExpanded(content, tag) {
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
index 5754fdc..0c9de60 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.js
@@ -17,237 +17,303 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-voting-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="shared-styles">
-      :host {
-        display: block;
-        position: relative;
-        cursor: pointer;
-        overflow-y: hidden;
-      }
-      :host(.expanded) {
-        cursor: auto;
-      }
-      .collapsed .contentContainer {
-        align-items: center;
-        color: var(--deemphasized-text-color);
-        display: flex;
-        white-space: nowrap;
-      }
-      .contentContainer {
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      .collapsed .contentContainer {
-        /* For expanded state we inherit the alternating background color
+  <style include="gr-voting-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      position: relative;
+      cursor: pointer;
+      overflow-y: hidden;
+    }
+    :host(.expanded) {
+      cursor: auto;
+    }
+    .collapsed .contentContainer {
+      align-items: center;
+      color: var(--deemphasized-text-color);
+      display: flex;
+      white-space: nowrap;
+    }
+    .contentContainer {
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    .collapsed .contentContainer {
+      /* For expanded state we inherit the alternating background color
            that is set in gr-messages-list. */
-        background-color: var(--background-color-primary);
-      }
-      .name {
+      background-color: var(--background-color-primary);
+    }
+    .name {
+      font-weight: var(--font-weight-bold);
+    }
+    .message {
+      --gr-formatted-text-prose-max-width: 80ch;
+    }
+    .collapsed .message {
+      max-width: none;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+    .collapsed .author,
+    .collapsed .content,
+    .collapsed .message,
+    .collapsed .updateCategory,
+    gr-account-chip {
+      display: inline;
+    }
+    gr-button {
+      margin: 0 -4px;
+    }
+    .collapsed gr-comment-list,
+    .collapsed gr-thread-list,
+    .collapsed .replyBtn,
+    .collapsed .deleteBtn,
+    .collapsed .hideOnCollapsed,
+    .hideOnOpen {
+      display: none;
+    }
+    .replyBtn {
+      margin-right: var(--spacing-m);
+    }
+    .collapsed .hideOnOpen {
+      display: block;
+    }
+    .collapsed .content {
+      flex: 1;
+      margin-right: var(--spacing-m);
+      min-width: 0;
+      overflow: hidden;
+    }
+    .collapsed .content.messageContent {
+      text-overflow: ellipsis;
+    }
+    .collapsed .dateContainer {
+      position: static;
+    }
+    .collapsed .author {
+      overflow: hidden;
+      color: var(--primary-text-color);
+      margin-right: var(--spacing-s);
+    }
+    .authorLabel {
+      min-width: 160px;
+      display: inline-block;
+    }
+    .expanded .author {
+      cursor: pointer;
+      margin-bottom: var(--spacing-m);
+    }
+    .expanded .content {
+      padding-left: 40px;
+    }
+    .dateContainer {
+      position: absolute;
+      /* right and top values should match .contentContainer padding */
+      right: var(--spacing-l);
+      top: var(--spacing-m);
+    }
+    .dateContainer .patchset {
+      margin-right: var(--spacing-m);
+      color: var(--deemphasized-text-color);
+    }
+    .dateContainer .patchset:before {
+      content: 'Patchset ';
+    }
+    span.date {
+      color: var(--deemphasized-text-color);
+    }
+    span.date:hover {
+      text-decoration: underline;
+    }
+    .dateContainer iron-icon {
+      cursor: pointer;
+      vertical-align: top;
+    }
+    .score {
+      border-radius: var(--border-radius);
+      color: var(--primary-text-color);
+      display: inline-block;
+      padding: 0 var(--spacing-s);
+      text-align: center;
+    }
+    .score,
+    .commentsSummary {
+      margin-right: var(--spacing-s);
+      min-width: 115px;
+    }
+    .expanded .commentsSummary {
+      display: none;
+    }
+    .commentsIcon {
+      vertical-align: top;
+    }
+    .score.removed {
+      background-color: var(--vote-color-neutral);
+    }
+    .score.negative {
+      background-color: var(--vote-color-disliked);
+    }
+    .score.negative.min {
+      background-color: var(--vote-color-rejected);
+    }
+    .score.positive {
+      background-color: var(--vote-color-recommended);
+    }
+    .score.positive.max {
+      background-color: var(--vote-color-approved);
+    }
+    gr-account-label {
+      --gr-account-label-text-style: {
         font-weight: var(--font-weight-bold);
       }
-      .message {
-        --gr-formatted-text-prose-max-width: 80ch;
-      }
-      .collapsed .message {
-        max-width: none;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-      .collapsed .author,
-      .collapsed .content,
-      .collapsed .message,
-      .collapsed .updateCategory,
-      gr-account-chip {
-        display: inline;
-      }
-      gr-button {
-        margin: 0 -4px;
-      }
-      .collapsed gr-comment-list,
-      .collapsed .replyBtn,
-      .collapsed .deleteBtn,
-      .collapsed .hideOnCollapsed,
-      .hideOnOpen {
-        display: none;
-      }
-      .replyBtn {
-        margin-right: var(--spacing-m);
-      }
-      .collapsed .hideOnOpen {
-        display: block;
-      }
-      .collapsed .content {
-        flex: 1;
-        margin-right: var(--spacing-m);
-        min-width: 0;
-        overflow: hidden;
-      }
-      .collapsed .content.messageContent {
-        text-overflow: ellipsis;
-      }
-      .collapsed .dateContainer {
-        position: static;
-      }
-      .collapsed .author {
-        overflow: hidden;
-        color: var(--primary-text-color);
-        margin-right: var(--spacing-s);
-      }
-      .authorLabel {
-        min-width: 160px;
-        display: inline-block;
-      }
-      .expanded .author {
-        cursor: pointer;
-        margin-bottom: var(--spacing-m);
-      }
+    }
+    @media screen and (max-width: 50em) {
       .expanded .content {
-        padding-left: 40px;
-      }
-      .dateContainer {
-        position: absolute;
-        /* right and top values should match .contentContainer padding */
-        right: var(--spacing-l);
-        top: var(--spacing-m);
-      }
-      .dateContainer .patchset {
-        margin-right: var(--spacing-m);
-        color: var(--deemphasized-text-color);
-      }
-      .dateContainer .patchset:before {
-        content: 'Patchset ';
-      }
-      span.date {
-        color: var(--deemphasized-text-color);
-      }
-      span.date:hover {
-        text-decoration: underline;
-      }
-      .dateContainer iron-icon {
-        cursor: pointer;
-        vertical-align: top;
-      }
-      .score {
-        border-radius: var(--border-radius);
-        color: var(--primary-text-color);
-        display: inline-block;
-        padding: 0 var(--spacing-s);
-        text-align: center;
+        padding-left: 0;
       }
       .score,
-      .commentsSummary {
-        margin-right: var(--spacing-s);
-        min-width: 115px;
+      .commentsSummary,
+      .authorLabel {
+        min-width: 0px;
       }
-      .expanded .commentsSummary {
-        display: none;
+      .dateContainer .patchset:before {
+        content: 'PS ';
       }
-      .commentsIcon {
-        vertical-align: top;
-      }
-      .score.removed {
-        background-color: var(--vote-color-neutral);
-      }
-      .score.negative {
-        background-color: var(--vote-color-disliked);
-      }
-      .score.negative.min {
-        background-color: var(--vote-color-rejected);
-      }
-      .score.positive {
-        background-color: var(--vote-color-recommended);
-      }
-      .score.positive.max {
-        background-color: var(--vote-color-approved);
-      }
-      gr-account-label {
-        --gr-account-label-text-style: {
-          font-weight: var(--font-weight-bold);
-        };
-      }
-      @media screen and (max-width: 50em) {
-        .expanded .content {
-          padding-left: 0;
-        }
-        .score,
-        .commentsSummary,
-        .authorLabel {
-          min-width: 0px;
-        }
-        .dateContainer .patchset:before {
-          content: 'PS ';
-        }
-      }
-    </style>
-    <div class\$="[[_computeClass(_expanded)]]">
-      <div class="contentContainer">
-        <div class="author" on-click="_handleAuthorClick">
-          <span hidden\$="[[!showOnBehalfOf]]">
-            <span class="name">[[message.real_author.name]]</span>
-            on behalf of
+    }
+  </style>
+  <div class$="[[_computeClass(_expanded)]]">
+    <div class="contentContainer">
+      <div class="author" on-click="_handleAuthorClick">
+        <span hidden$="[[!showOnBehalfOf]]">
+          <span class="name">[[message.real_author.name]]</span>
+          on behalf of
+        </span>
+        <gr-account-label
+          account="[[author]]"
+          class="authorLabel"
+        ></gr-account-label>
+        <template
+          is="dom-repeat"
+          items="[[_getScores(message, labelExtremes)]]"
+          as="score"
+        >
+          <span class$="score [[_computeScoreClass(score, labelExtremes)]]">
+            [[score.label]] [[score.value]]
           </span>
-          <gr-account-label account="[[author]]" class="authorLabel"></gr-account-label>
-          <template is="dom-repeat" items="[[_getScores(message, labelExtremes)]]" as="score">
-            <span class\$="score [[_computeScoreClass(score, labelExtremes)]]">
-              [[score.label]] [[score.value]]
-            </span>
+        </template>
+      </div>
+      <template is="dom-if" if="[[_commentCountText]]">
+        <div class="commentsSummary">
+          <iron-icon icon="gr-icons:comment" class="commentsIcon"></iron-icon>
+          <span class="numberOfComments">[[_commentCountText]]</span>
+        </div>
+      </template>
+      <template is="dom-if" if="[[message.message]]">
+        <div class="content messageContent">
+          <div class="message hideOnOpen">[[_messageContentCollapsed]]</div>
+          <gr-formatted-text
+            no-trailing-margin=""
+            class="message hideOnCollapsed"
+            content="[[_messageContentExpanded]]"
+            config="[[_projectConfig.commentlinks]]"
+          ></gr-formatted-text>
+          <template is="dom-if" if="[[!_isMessageContentEmpty()]]">
+            <div
+              class="replyActionContainer"
+              hidden$="[[!showReplyButton]]"
+              hidden=""
+            >
+              <gr-button
+                class="replyBtn"
+                link=""
+                small=""
+                on-click="_handleReplyTap"
+              >
+                Reply
+              </gr-button>
+              <gr-button
+                disabled$="[[_isDeletingChangeMsg]]"
+                class="deleteBtn"
+                hidden$="[[!_isAdmin]]"
+                hidden=""
+                link=""
+                small=""
+                on-click="_handleDeleteMessage"
+              >
+                Delete
+              </gr-button>
+            </div>
+          </template>
+          <template is="dom-if" if="[[!_isCleanerLogExperimentEnabled]]">
+            <gr-comment-list
+              comments="[[comments]]"
+              change-num="[[changeNum]]"
+              patch-num="[[message._revision_number]]"
+              project-name="[[projectName]]"
+              project-config="[[_projectConfig]]"
+            ></gr-comment-list>
+          </template>
+          <template is="dom-if" if="[[_isCleanerLogExperimentEnabled]]">
+            <gr-thread-list
+              change="[[change]]"
+              hidden$="[[!commentThreads.length]]"
+              threads="[[commentThreads]]"
+              change-num="[[changeNum]]"
+              logged-in="[[_loggedIn]]"
+              hide-toggle-buttons
+              on-thread-list-modified="_onThreadListModified"
+            >
+            </gr-thread-list>
           </template>
         </div>
-        <template is="dom-if" if="[[_commentCountText]]">
-          <div class="commentsSummary">
-            <iron-icon icon="gr-icons:comment" class="commentsIcon"></iron-icon>
-            <span class="numberOfComments">[[_commentCountText]]</span>
-          </div>
-        </template>
-        <template is="dom-if" if="[[message.message]]">
-          <div class="content messageContent">
-            <div class="message hideOnOpen">[[_messageContentCollapsed]]</div>
-            <gr-formatted-text no-trailing-margin="" class="message hideOnCollapsed" content="[[_messageContentExpanded]]" config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
-            <template is="dom-if" if="[[!_isMessageContentEmpty()]]">
-              <div class="replyActionContainer" hidden\$="[[!showReplyButton]]" hidden="">
-                  <gr-button class="replyBtn" link="" small="" on-click="_handleReplyTap">
-                    Reply
-                  </gr-button>
-                  <gr-button disabled\$="[[_isDeletingChangeMsg]]" class="deleteBtn" hidden\$="[[!_isAdmin]]" hidden="" link="" small="" on-click="_handleDeleteMessage">
-                    Delete
-                  </gr-button>
-              </div>
-            </template>
-            <gr-comment-list comments="[[comments]]" change-num="[[changeNum]]" patch-num="[[message._revision_number]]" project-name="[[projectName]]" project-config="[[_projectConfig]]"></gr-comment-list>
-          </div>
-        </template>
-        <template is="dom-if" if="[[_computeIsReviewerUpdate(message)]]">
-          <div class="content">
-            <template is="dom-repeat" items="[[message.updates]]" as="update">
-              <div class="updateCategory">
-                [[update.message]]
-                <template is="dom-repeat" items="[[update.reviewers]]" as="reviewer">
-                  <gr-account-chip account="[[reviewer]]">
-                  </gr-account-chip>
-                </template>
-              </div>
-            </template>
-          </div>
-        </template>
-        <span class="dateContainer">
-          <template is="dom-if" if="[[message._revision_number]]">
-            <span class="patchset">[[message._revision_number]]</span>
+      </template>
+      <template is="dom-if" if="[[_computeIsReviewerUpdate(message)]]">
+        <div class="content">
+          <template is="dom-repeat" items="[[message.updates]]" as="update">
+            <div class="updateCategory">
+              [[update.message]]
+              <template
+                is="dom-repeat"
+                items="[[update.reviewers]]"
+                as="reviewer"
+              >
+                <gr-account-chip account="[[reviewer]]"> </gr-account-chip>
+              </template>
+            </div>
           </template>
-          <template is="dom-if" if="[[!message.id]]">
-            <span class="date">
-              <gr-date-formatter has-tooltip="" show-date-and-time="" date-str="[[message.date]]"></gr-date-formatter>
-            </span>
-          </template>
-          <template is="dom-if" if="[[message.id]]">
-            <span class="date" on-click="_handleAnchorClick">
-              <gr-date-formatter has-tooltip="" show-date-and-time="" date-str="[[message.date]]"></gr-date-formatter>
-            </span>
-          </template>
-          <iron-icon id="expandToggle" on-click="_toggleExpanded" title="Toggle expanded state" icon="[[_computeExpandToggleIcon(_expanded)]]"></iron-icon>
-        </span>
-      </div>
+        </div>
+      </template>
+      <span class="dateContainer">
+        <template is="dom-if" if="[[message._revision_number]]">
+          <span class="patchset">[[message._revision_number]]</span>
+        </template>
+        <template is="dom-if" if="[[!message.id]]">
+          <span class="date">
+            <gr-date-formatter
+              has-tooltip=""
+              show-date-and-time=""
+              date-str="[[message.date]]"
+            ></gr-date-formatter>
+          </span>
+        </template>
+        <template is="dom-if" if="[[message.id]]">
+          <span class="date" on-click="_handleAnchorClick">
+            <gr-date-formatter
+              has-tooltip=""
+              show-date-and-time=""
+              date-str="[[message.date]]"
+            ></gr-date-formatter>
+          </span>
+        </template>
+        <iron-icon
+          id="expandToggle"
+          on-click="_toggleExpanded"
+          title="Toggle expanded state"
+          icon="[[_computeExpandToggleIcon(_expanded)]]"
+        ></iron-icon>
+      </span>
     </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index 2e195c1..5e2feb4 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-message</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental.js
index e87baaa..32ee718 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental.js
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/paper-toggle-button/paper-toggle-button.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../shared/gr-button/gr-button.js';
 import '../gr-message/gr-message.js';
 import '../../../styles/shared-styles.js';
@@ -29,6 +26,8 @@
 import {htmlTemplate} from './gr-messages-list-experimental_html.js';
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 import {util} from '../../../scripts/util.js';
+import {MessageTags} from '../../../constants/constants.js';
+import {appContext} from '../../../services/app-context.js';
 
 /**
  * The content of the enum is also used in the UI for the button text.
@@ -40,8 +39,41 @@
   COLLAPSE_ALL: 'Collapse All',
 };
 
+function isNewPatchSet(message) {
+  if (!message || !message.tag) return false;
+  return message.tag.includes(MessageTags.TAG_NEW_PATCHSET)
+      || message.tag.includes(MessageTags.TAG_NEW_WIP_PATCHSET);
+}
+
+function hasHigherRevisionNumber(m, message) {
+  return m._revision_number && m._revision_number > message._revision_number;
+}
+
+function isNewerReviewerUpdate(m, message) {
+  if (!message || !message.tag || !m || !m.tag) return false;
+  if (m.tag != MessageTags.TAG_REVIEWER_UPDATE) return false;
+  return m.date > message.date;
+}
+
+function isImportant(message, allMessages) {
+  // Human messages don't have a tag. They are always important.
+  if (!message.tag) return true;
+
+  // Autogenerated messages are only important, if there is not a newer message
+  // with the same tag.
+  const tag = message.tag;
+  const sameTag = m =>
+    m.tag === tag || (isNewPatchSet(m) && isNewPatchSet(message));
+  return !allMessages.filter(sameTag).some(m =>
+    hasHigherRevisionNumber(m, message) || isNewerReviewerUpdate(m, message));
+}
+
+export const TEST_ONLY = {
+  isImportant,
+};
+
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrMessagesListExperimental extends mixinBehaviors( [
   KeyboardShortcutBehavior,
@@ -54,6 +86,8 @@
 
   static get properties() {
     return {
+      /** @type {?} */
+      change: Object,
       changeNum: Number,
       /**
        * These are just the change messages. They are combined with reviewer
@@ -95,10 +129,10 @@
         computed: '_computeExpandAllTitle(_expandAllState)',
       },
 
-      _hideAutomated: {
+      _showAllActivity: {
         type: Boolean,
         value: false,
-        observer: '_hideAutomatedChanged',
+        observer: '_observeShowAllActivity',
       },
       /**
        * The merged array of change messages and reviewer updates.
@@ -116,16 +150,21 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   scrollToMessage(messageID) {
     const selector = `[data-message-id="${messageID}"]`;
     const el = this.shadowRoot.querySelector(selector);
 
-    if (!el && !this._hideAutomated) {
+    if (!el && this._showAllActivity) {
       console.warn(`Failed to scroll to message: ${messageID}`);
       return;
     }
     if (!el) {
-      this._hideAutomated = false;
+      this._showAllActivity = true;
       setTimeout(() => this.scrollToMessage(messageID));
       return;
     }
@@ -141,15 +180,7 @@
     this._highlightEl(el);
   }
 
-  _isAutomated(message) {
-    const isReviewerUpdate =
-        !!(message.reviewer || message.type === 'REVIEWER_UPDATE');
-    const isAutoGenerated =
-        !!(message.tag && message.tag.startsWith('autogenerated'));
-    return isReviewerUpdate || isAutoGenerated;
-  }
-
-  _hideAutomatedChanged(hideAutomated) {
+  _observeShowAllActivity(showAllActivity) {
     // We have to call render() such that the dom-repeat filter picks up the
     // change.
     this.$.messageRepeat.render();
@@ -159,7 +190,8 @@
    * Filter for the dom-repeat of combinedMessages.
    */
   _isMessageVisible(message) {
-    return !(this._hideAutomated && this._isAutomated(message));
+    const allMessages = this._combinedMessages;
+    return this._showAllActivity || isImportant(message, allMessages);
   }
 
   /**
@@ -258,43 +290,9 @@
     this.scrollToMessage(e.detail.id);
   }
 
-  _hasAutomatedMessages(messages) {
-    if (!messages) { return false; }
-    for (const message of messages) {
-      if (this._isAutomated(message)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Computes message author's file comments for change's message. The backend
-   * sets comment.change_message_id for matching, so this computation is fairly
-   * straightforward.
-   *
-   * @param {!Object} changeComments changeComment object, which includes
-   *     a method to get all published comments (including robot comments),
-   *     which returns a Hash of arrays of comments, filename as key.
-   * @param {!Object} message
-   * @return {!Object} Hash of arrays of comments, filename as key.
-   */
-  _computeCommentsForMessage(changeComments, message) {
-    if ([changeComments, message].some(arg => arg === undefined)) {
-      return [];
-    }
-    const comments = changeComments.getAllPublishedComments();
-    if (message._index === undefined || !comments || !this.messages) {
-      return [];
-    }
-    const idFilter = comment => comment.change_message_id === message.id;
-
-    const msgComments = {};
-    for (const file in comments) {
-      if (!comments.hasOwnProperty(file)) { continue; }
-      msgComments[file] = comments[file].filter(idFilter);
-    }
-    return msgComments;
+  _isVisibleShowAllActivityToggle(messages) {
+    messages = messages || [];
+    return messages.some(m => !isImportant(m, messages));
   }
 
   /**
@@ -310,7 +308,7 @@
         acc[val] = (acc[val] || 0) + 1;
         return acc;
       }, {all: combinedMessages.length});
-      this.$.reporting.reportInteraction('messages-count', tagsCounted);
+      this.reporting.reportInteraction('messages-count', tagsCounted);
     }
   }
 
@@ -332,6 +330,48 @@
     }
     return extremes;
   }
+
+  /**
+   * Computes message author's file comments for change's message. The backend
+   * sets comment.change_message_id for matching, so this computation is fairly
+   * straightforward.
+   *
+   * @param {!Object} changeComments changeComment object, which includes
+   *     a method to get all published comments (including robot comments),
+   *     which returns a Hash of arrays of comments, filename as key.
+   * @param {!Object} message
+   * @return {!Array} Array of comment threads.
+   */
+  _computeThreadsForMessage(changeComments, message) {
+    if ([changeComments, message].some(arg => arg === undefined)) {
+      return [];
+    }
+
+    if (message._index === undefined || !this.messages) {
+      return [];
+    }
+
+    const commentThreads = changeComments.getAllThreadsForChange()
+        .map(c => { return {...c}; });
+
+    return commentThreads.filter(thread => thread.comments
+        .map(comment => {
+          // collapse all by default
+          comment.collapsed = true;
+          return comment;
+        }).some(comment => {
+          const condition = comment.change_message_id === message.id;
+          // Since getAllThreadsForChange() always returns a new copy of
+          // all comments we can modify them here without worrying about
+          // polluting other threads.
+          comment.collapsed = !condition;
+          if (condition) {
+            comment.extraNote = 'From this log entry';
+          }
+          return condition;
+        })
+    );
+  }
 }
 
 customElements.define(GrMessagesListExperimental.is,
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_html.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_html.js
index 83e2815..e9cd272 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_html.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_html.js
@@ -17,58 +17,79 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: flex;
-        justify-content: space-between;
+  <style include="shared-styles">
+    :host {
+      display: flex;
+      justify-content: space-between;
+    }
+    .header {
+      align-items: center;
+      border-top: 1px solid var(--border-color);
+      border-bottom: 1px solid var(--border-color);
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-s) var(--spacing-l);
+    }
+    .highlighted {
+      animation: 3s fadeOut;
+    }
+    @keyframes fadeOut {
+      0% {
+        background-color: var(--emphasis-color);
       }
-      .header {
-        align-items: center;
-        border-top: 1px solid var(--border-color);
-        border-bottom: 1px solid var(--border-color);
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-s) var(--spacing-l);
+      100% {
+        background-color: var(--view-background-color);
       }
-      .highlighted {
-        animation: 3s fadeOut;
-      }
-      @keyframes fadeOut {
-        0% { background-color: var(--emphasis-color); }
-        100% { background-color: var(--view-background-color); }
-      }
-      .container {
-        align-items: center;
-        display: flex;
-      }
-      gr-message:not(:last-of-type) {
-        border-bottom: 1px solid var(--border-color);
-      }
-      gr-message:nth-child(2n) {
-        background-color: var(--background-color-secondary);
-      }
-      gr-message:nth-child(2n+1) {
-        background-color: var(--background-color-tertiary);
-      }
-    </style>
-    <div class="header">
-      <span id="automatedMessageToggleContainer" class="container" hidden\$="[[!_hasAutomatedMessages(messages)]]">
-        <paper-toggle-button id="automatedMessageToggle" checked="{{_hideAutomated}}"></paper-toggle-button>Only comments
-        <span class="transparent separator"></span>
-      </span>
-      <gr-button id="collapse-messages" link="" title="[[_expandAllTitle]]" on-click="_handleExpandCollapseTap">
-        [[_expandAllState]]
-      </gr-button>
-    </div>
-    <template id="messageRepeat" is="dom-repeat" items="[[_combinedMessages]]" as="message" filter="_isMessageVisible">
-      <gr-message change-num="[[changeNum]]"
-                  message="[[message]]"
-                  comments="[[_computeCommentsForMessage(changeComments, message)]]"
-                  project-name="[[projectName]]"
-                  show-reply-button="[[showReplyButtons]]"
-                  on-message-anchor-tap="_handleAnchorClick"
-                  label-extremes="[[_labelExtremes]]"
-                  data-message-id\$="[[message.id]]"></gr-message>
-    </template>
-    <gr-reporting id="reporting" category="message-list"></gr-reporting>
+    }
+    .container {
+      align-items: center;
+      display: flex;
+    }
+    gr-message:not(:last-of-type) {
+      border-bottom: 1px solid var(--border-color);
+    }
+    gr-message {
+      background-color: var(--background-color-secondary);
+    }
+  </style>
+  <div class="header">
+    <span
+      id="showAllActivityToggleContainer"
+      class="container"
+      hidden$="[[!_isVisibleShowAllActivityToggle(_combinedMessages)]]"
+    >
+      <paper-toggle-button
+        id="showAllActivityToggle"
+        checked="{{_showAllActivity}}"
+      ></paper-toggle-button
+      >Show all entries
+      <span class="transparent separator"></span>
+    </span>
+    <gr-button
+      id="collapse-messages"
+      link=""
+      title="[[_expandAllTitle]]"
+      on-click="_handleExpandCollapseTap"
+    >
+      [[_expandAllState]]
+    </gr-button>
+  </div>
+  <template
+    id="messageRepeat"
+    is="dom-repeat"
+    items="[[_combinedMessages]]"
+    as="message"
+    filter="_isMessageVisible"
+  >
+    <gr-message
+      change-num="[[changeNum]]"
+      message="[[message]]"
+      comment-threads="[[_computeThreadsForMessage(changeComments, message)]]"
+      project-name="[[projectName]]"
+      show-reply-button="[[showReplyButtons]]"
+      on-message-anchor-tap="_handleAnchorClick"
+      label-extremes="[[_labelExtremes]]"
+      data-message-id$="[[message.id]]"
+    ></gr-message>
+  </template>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.html
index b5544a5..bb5b173 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list-experimental_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-messages-list-experimental</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -45,8 +46,11 @@
 import '../../../test/common-test-setup.js';
 import '../../diff/gr-comment-api/gr-comment-api.js';
 import './gr-messages-list-experimental.js';
-import '../../diff/gr-comment-api/gr-comment-api-mock_test.js';
+import '../../../test/mocks/comment-api.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
+import {TEST_ONLY} from './gr-messages-list-experimental.js';
+import {MessageTags} from '../../../constants/constants.js';
+
 const randomMessage = function(opt_params) {
   const params = opt_params || {};
   const author1 = {
@@ -60,14 +64,10 @@
     message: params.message || Math.random().toString(),
     _revision_number: params._revision_number || 1,
     author: params.author || author1,
+    tag: params.tag,
   };
 };
 
-const randomAutomated = function(opt_params) {
-  return Object.assign({tag: 'autogenerated:gerrit:replace'},
-      randomMessage(opt_params));
-};
-
 suite('gr-messages-list-experimental tests', () => {
   let element;
   let messages;
@@ -88,61 +88,53 @@
     email: 'marvin@sirius.org',
   };
 
+  const createComment = function() {
+    return {
+      id: '1a2b3c4d',
+      message: 'some random test text',
+      change_message_id: '8a7b6c5d',
+      updated: '2016-01-01 01:02:03.000000000',
+      line: 1,
+      patch_set: 1,
+      author,
+    };
+  };
+
   const comments = {
     file1: [
       {
-        message: 'message text',
+        ...createComment(),
         change_message_id: MESSAGE_ID_0,
-        updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: '6505d749_f0bec0aa',
-        line: 62,
-        id: '6505d749_10ed44b2',
-        patch_set: 2,
         author: {
           email: 'some@email.com',
           _account_id: 123,
         },
       },
       {
-        message: 'message text',
+        ...createComment(),
+        id: '2b3c4d5e',
         change_message_id: MESSAGE_ID_1,
-        updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: 'c5912363_6b820105',
-        line: 42,
-        id: '450a935e_0f1c05db',
-        patch_set: 2,
-        author,
       },
       {
-        message: 'message text',
+        ...createComment(),
+        id: '2b3c4d5e',
         change_message_id: MESSAGE_ID_1,
-        updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: '6505d749_f0bec0aa',
-        line: 62,
-        id: '6505d749_10ed44b2',
-        patch_set: 2,
-        author,
       },
       {
-        message: 'message text',
-        change_message_id: MESSAGE_ID_2,
-        updated: '2016-09-27 00:18:03.000000000',
-        line: 64,
+        ...createComment(),
         id: '34ed05d749_10ed44b2',
-        patch_set: 2,
-        author,
+        change_message_id: MESSAGE_ID_2,
       },
     ],
     file2: [
       {
-        message: 'message text',
+        ...createComment(),
         change_message_id: MESSAGE_ID_1,
-        updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: 'c5912363_4b7d450a',
-        line: 132,
         id: '450a935e_4f260d25',
-        patch_set: 2,
-        author,
       },
     ],
   };
@@ -214,9 +206,9 @@
       assert.isTrue([...getMessages()].filter(m => m._expanded).length == 0);
     });
 
-    test('hide messages does not appear when no automated messages', () => {
+    test('showAllActivity does not appear when all msgs are important', () => {
       assert.isOk(element.shadowRoot
-          .querySelector('#automatedMessageToggleContainer[hidden]'));
+          .querySelector('#showAllActivityToggleContainer[hidden]'));
     });
 
     test('scroll to message', () => {
@@ -264,7 +256,7 @@
               ._expanded);
     });
 
-    test('messages', () => {
+    test('associating messages with comments', () => {
       const messages = [].concat(
           randomMessage(),
           {
@@ -285,24 +277,102 @@
           }
       );
       element.messages = messages;
-      const isAuthor = function(author, comment) {
-        return comment.author._account_id === author._account_id;
-      };
-      const isMarvin = isAuthor.bind(null, author);
       flushAsynchronousOperations();
       const messageElements = getMessages();
       assert.equal(messageElements.length, messages.length);
       assert.deepEqual(messageElements[1].message, messages[1]);
       assert.deepEqual(messageElements[2].message, messages[2]);
-      assert.deepEqual(messageElements[1].comments.file1,
-          comments.file1.filter(isMarvin).filter(
-              c => c.change_message_id === messages[1].id));
-      assert.deepEqual(messageElements[1].comments.file2,
-          comments.file2.filter(isMarvin).filter(
-              c => c.change_message_id === messages[1].id));
-      assert.deepEqual(messageElements[2].comments.file1,
-          comments.file1.filter(isMarvin).filter(
-              c => c.change_message_id === messages[2].id));
+    });
+
+    test('threads', () => {
+      const messages = [
+        {
+          _index: 5,
+          _revision_number: 4,
+          message: 'Uploaded patch set 4.',
+          date: '2016-09-28 13:36:33.000000000',
+          author,
+          id: '8c19ccc949c6d482b061be6a28e10782abf0e7af',
+        },
+      ];
+      element.messages = messages;
+      flushAsynchronousOperations();
+      const messageElements = getMessages();
+      // threads
+      assert.equal(
+          messageElements[0].commentThreads.length,
+          3);
+      // first thread contains 1 comment
+      assert.equal(
+          messageElements[0].commentThreads[0].comments.length,
+          1);
+      assert.equal(
+          messageElements[0].commentThreads[0].comments[0].extraNote,
+          'From this log entry');
+    });
+
+    test('isImportant human message', () => {
+      const m = randomMessage();
+      assert.isTrue(TEST_ONLY.isImportant(m, []));
+      assert.isTrue(TEST_ONLY.isImportant(m, [m]));
+    });
+
+    test('isImportant even with a tag', () => {
+      const m1 = randomMessage();
+      const m2 = randomMessage({tag: 'autogenerated:gerrit1'});
+      const m3 = randomMessage({tag: 'autogenerated:gerrit2'});
+      assert.isTrue(TEST_ONLY.isImportant(m2, []));
+      assert.isTrue(TEST_ONLY.isImportant(m1, [m1, m2, m3]));
+      assert.isTrue(TEST_ONLY.isImportant(m2, [m1, m2, m3]));
+      assert.isTrue(TEST_ONLY.isImportant(m3, [m1, m2, m3]));
+    });
+
+    test('isImportant filters same tag and older revision', () => {
+      const m1 = randomMessage({tag: 'auto', _revision_number: 2});
+      const m2 = randomMessage({tag: 'auto', _revision_number: 1});
+      const m3 = randomMessage({tag: 'auto'});
+      assert.isTrue(TEST_ONLY.isImportant(m1, [m1]));
+      assert.isTrue(TEST_ONLY.isImportant(m2, [m2]));
+      assert.isTrue(TEST_ONLY.isImportant(m1, [m1, m2]));
+      assert.isFalse(TEST_ONLY.isImportant(m2, [m1, m2]));
+      assert.isTrue(TEST_ONLY.isImportant(m1, [m1, m3]));
+      assert.isFalse(TEST_ONLY.isImportant(m3, [m1, m3]));
+      assert.isTrue(TEST_ONLY.isImportant(m1, [m1, m2, m3]));
+      assert.isFalse(TEST_ONLY.isImportant(m2, [m1, m2, m3]));
+      assert.isFalse(TEST_ONLY.isImportant(m3, [m1, m2, m3]));
+    });
+
+    test('isImportant filters newPatchSet also for wip', () => {
+      const m1 = randomMessage(
+          {tag: MessageTags.TAG_NEW_PATCHSET, _revision_number: 3});
+      const m2 = randomMessage(
+          {tag: MessageTags.TAG_NEW_PATCHSET, _revision_number: 2});
+      const m3 = randomMessage(
+          {tag: MessageTags.TAG_NEW_WIP_PATCHSET, _revision_number: 1});
+      assert.isTrue(TEST_ONLY.isImportant(m1, [m1, m2, m3]));
+      assert.isFalse(TEST_ONLY.isImportant(m2, [m1, m2, m3]));
+      assert.isFalse(TEST_ONLY.isImportant(m3, [m1, m2, m3]));
+    });
+
+    test('isImportant filters older reviewer updates', () => {
+      const m1 = randomMessage(
+          {
+            tag: MessageTags.TAG_REVIEWER_UPDATE,
+            date: '2020-04-24 10:11:02.000000000',
+          });
+      const m2 = randomMessage(
+          {
+            tag: MessageTags.TAG_REVIEWER_UPDATE,
+            date: '2020-04-25 10:11:02.000000000',
+          });
+      const m3 = randomMessage(
+          {
+            tag: MessageTags.TAG_REVIEWER_UPDATE,
+            date: '2020-04-25 10:13:02.000000000',
+          });
+      assert.isFalse(TEST_ONLY.isImportant(m1, [m1, m2, m3]));
+      assert.isFalse(TEST_ONLY.isImportant(m2, [m1, m2, m3]));
+      assert.isTrue(TEST_ONLY.isImportant(m3, [m1, m2, m3]));
     });
 
     test('messages without author do not throw', () => {
@@ -327,18 +397,6 @@
     let sandbox;
     let commentApiWrapper;
 
-    const getMessages = function() {
-      return dom(element.root).querySelectorAll('gr-message');
-    };
-    const getHiddenMessages = function() {
-      return dom(element.root).querySelectorAll('gr-message[hidden]');
-    };
-
-    const randomMessageReviewer = {
-      reviewer: {},
-      date: '2016-01-13 20:30:33.038000',
-    };
-
     setup(() => {
       stub('gr-rest-api-interface', {
         getConfig() { return Promise.resolve({}); },
@@ -349,8 +407,11 @@
       });
 
       sandbox = sinon.sandbox.create();
-      messages = _.times(2, randomAutomated);
-      messages.push(randomMessageReviewer);
+      messages = [
+        randomMessage(),
+        randomMessage({tag: 'auto', _revision_number: 2}),
+        randomMessage({tag: 'auto', _revision_number: 3}),
+      ];
 
       // Element must be wrapped in an element with direct access to the
       // comment API.
@@ -370,40 +431,29 @@
 
     test('hide autogenerated button is not hidden', () => {
       assert.isNotOk(element.shadowRoot
-          .querySelector('#automatedMessageToggle[hidden]'));
+          .querySelector('#showAllActivityToggle[hidden]'));
     });
 
-    test('autogenerated messages are not hidden initially', () => {
-      const allHiddenMessageEls = getHiddenMessages();
-
-      // There are no hidden messages.
-      assert.isFalse(!!allHiddenMessageEls.length);
+    test('one unimportant message is hidden initially', () => {
+      const displayedMsgs = dom(element.root).querySelectorAll('gr-message');
+      assert.equal(displayedMsgs.length, 2);
     });
 
-    test('autogenerated messages hidden after comments only toggle', () => {
-      let allHiddenMessageEls = getHiddenMessages();
-
-      element._hideAutomated = false;
-      MockInteractions.tap(element.$.automatedMessageToggle);
+    test('unimportant messages hidden after toggle', () => {
+      element._showAllActivity = true;
+      MockInteractions.tap(element.$.showAllActivityToggle);
       flushAsynchronousOperations();
-      const allMessageEls = getMessages();
-      allHiddenMessageEls = getHiddenMessages();
-
-      // Autogenerated messages are now hidden.
-      assert.equal(allHiddenMessageEls.length, allMessageEls.length);
+      const displayedMsgs = dom(element.root).querySelectorAll('gr-message');
+      assert.equal(displayedMsgs.length, 2);
     });
 
-    test('autogenerated messages not hidden after comments only toggle',
-        () => {
-          let allHiddenMessageEls = getHiddenMessages();
-
-          element._hideAutomated = true;
-          MockInteractions.tap(element.$.automatedMessageToggle);
-          allHiddenMessageEls = getHiddenMessages();
-
-          // Autogenerated messages are now hidden.
-          assert.isFalse(!!allHiddenMessageEls.length);
-        });
+    test('unimportant messages shown after toggle', () => {
+      element._showAllActivity = false;
+      MockInteractions.tap(element.$.showAllActivityToggle);
+      flushAsynchronousOperations();
+      const displayedMsgs = dom(element.root).querySelectorAll('gr-message');
+      assert.equal(displayedMsgs.length, 3);
+    });
 
     test('_computeLabelExtremes', () => {
       const computeSpy = sandbox.spy(element, '_computeLabelExtremes');
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 d6a20f0..a43358b 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
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/paper-toggle-button/paper-toggle-button.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../shared/gr-button/gr-button.js';
 import '../gr-message/gr-message.js';
 import '../../../styles/shared-styles.js';
@@ -29,6 +26,7 @@
 import {htmlTemplate} from './gr-messages-list_html.js';
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 import {util} from '../../../scripts/util.js';
+import {appContext} from '../../../services/app-context.js';
 
 const MAX_INITIAL_SHOWN_MESSAGES = 20;
 const MESSAGES_INCREMENT = 5;
@@ -49,7 +47,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrMessagesList extends mixinBehaviors( [
   KeyboardShortcutBehavior,
@@ -122,6 +120,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   scrollToMessage(messageID) {
     let el = this.shadowRoot
         .querySelector('[data-message-id="' + messageID + '"]');
@@ -279,9 +282,9 @@
   }
 
   /**
-   * Computes message author's file comments for change's message. The backend
-   * sets comment.change_message_id for matching, so this computation is fairly
-   * straightforward.
+   * Computes message author's file comments for change's message.
+   * Method uses this.messages to find next message and relies on messages
+   * to be sorted by date field descending.
    *
    * @param {!Object} changeComments changeComment object, which includes
    *     a method to get all published comments (including robot comments),
@@ -291,18 +294,45 @@
    */
   _computeCommentsForMessage(changeComments, message) {
     if ([changeComments, message].some(arg => arg === undefined)) {
-      return [];
+      return {};
     }
     const comments = changeComments.getAllPublishedComments();
     if (message._index === undefined || !comments || !this.messages) {
-      return [];
+      return {};
     }
-
-    const idFilter = comment => comment.change_message_id === message.id;
+    const messages = this.messages || [];
+    const index = message._index;
+    const authorId = message.author && message.author._account_id;
+    const mDate = util.parseDate(message.date).getTime();
+    // NB: Messages array has oldest messages first.
+    let nextMDate;
+    if (index > 0) {
+      for (let i = index - 1; i >= 0; i--) {
+        if (messages[i] && messages[i].author &&
+            messages[i].author._account_id === authorId) {
+          nextMDate = util.parseDate(messages[i].date).getTime();
+          break;
+        }
+      }
+    }
     const msgComments = {};
     for (const file in comments) {
       if (!comments.hasOwnProperty(file)) { continue; }
-      msgComments[file] = comments[file].filter(idFilter);
+      const fileComments = comments[file];
+      for (let i = 0; i < fileComments.length; i++) {
+        if (fileComments[i].author &&
+            fileComments[i].author._account_id !== authorId) {
+          continue;
+        }
+        const cDate = util.parseDate(fileComments[i].updated).getTime();
+        if (cDate <= mDate) {
+          if (nextMDate && cDate <= nextMDate) {
+            continue;
+          }
+          msgComments[file] = msgComments[file] || [];
+          msgComments[file].push(fileComments[i]);
+        }
+      }
     }
     return msgComments;
   }
@@ -374,7 +404,7 @@
 
   _handleShowAllTap() {
     this._visibleMessages = this._processedMessages;
-    this.$.reporting.reportInteraction(ReportingEvent.SHOW_ALL);
+    this.reporting.reportInteraction(ReportingEvent.SHOW_ALL);
   }
 
   _handleIncrementShownMessages() {
@@ -384,7 +414,7 @@
     const newMessages = this._processedMessages.slice(-(len + delta), -len);
     // Add newMessages to the beginning of _visibleMessages
     this.splice(...['_visibleMessages', 0, 0].concat(newMessages));
-    this.$.reporting.reportInteraction(ReportingEvent.SHOW_MORE);
+    this.reporting.reportInteraction(ReportingEvent.SHOW_MORE);
   }
 
   _processedMessagesChanged(messages) {
@@ -398,7 +428,7 @@
         acc[val] = (acc[val] || 0) + 1;
         return acc;
       }, {all: messages.length});
-      this.$.reporting.reportInteraction('messages-count', tagsCounted);
+      this.reporting.reportInteraction('messages-count', tagsCounted);
     }
   }
 
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.js
index 1a24234..ddae150 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.js
@@ -17,77 +17,115 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host,
-      .messageListControls {
-        display: flex;
-        justify-content: space-between;
+  <style include="shared-styles">
+    :host,
+    .messageListControls {
+      display: flex;
+      justify-content: space-between;
+    }
+    .header {
+      align-items: center;
+      border-top: 1px solid var(--border-color);
+      border-bottom: 1px solid var(--border-color);
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-s) var(--spacing-l);
+    }
+    #messageControlsContainer {
+      padding: 0 var(--spacing-l);
+    }
+    .highlighted {
+      animation: 3s fadeOut;
+    }
+    @keyframes fadeOut {
+      0% {
+        background-color: var(--emphasis-color);
       }
-      .header {
-        align-items: center;
-        border-top: 1px solid var(--border-color);
-        border-bottom: 1px solid var(--border-color);
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-s) var(--spacing-l);
+      100% {
+        background-color: var(--view-background-color);
       }
-      #messageControlsContainer {
-        padding: 0 var(--spacing-l);
-      }
-      .highlighted {
-        animation: 3s fadeOut;
-      }
-      @keyframes fadeOut {
-        0% { background-color: var(--emphasis-color); }
-        100% { background-color: var(--view-background-color); }
-      }
-      #messageControlsContainer {
-        align-items: center;
-        background-color: var(--background-color-secondary);
-        border-bottom: 1px solid var(--border-color);
-        display: flex;
-        height: 2.25em;
-        justify-content: center;
-      }
-      #messageControlsContainer gr-button {
-        padding: var(--spacing-s) 0;
-      }
-      .container {
-        align-items: center;
-        display: flex;
-      }
-      gr-message:not(:last-of-type) {
-        border-bottom: 1px solid var(--border-color);
-      }
-      gr-message:nth-child(2n) {
-        background-color: var(--background-color-secondary);
-      }
-      gr-message:nth-child(2n+1) {
-        background-color: var(--background-color-tertiary);
-      }
-    </style>
-    <div class="header">
-        <span id="automatedMessageToggleContainer" class="container" hidden\$="[[!_hasAutomatedMessages(messages)]]">
-          <paper-toggle-button id="automatedMessageToggle" checked="{{_hideAutomated}}"></paper-toggle-button>Only comments
-          <span class="transparent separator"></span>
-        </span>
-        <gr-button id="collapse-messages" link="" title="[[_expandAllTitle]]" on-click="_handleExpandCollapseTap">
-          [[_expandAllState]]
-        </gr-button>
-      </div>
-    <span id="messageControlsContainer" hidden\$="[[_computeShowHideTextHidden(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]">
-      <gr-button id="oldMessagesBtn" link="" on-click="_handleShowAllTap">
-          [[_computeNumMessagesText(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]
-      </gr-button>
-      <span class="container" hidden\$="[[_computeIncrementHidden(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]">
-        <span class="transparent separator"></span>
-        <gr-button id="incrementMessagesBtn" link="" on-click="_handleIncrementShownMessages">
-          [[_computeIncrementText(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]
-        </gr-button>
-      </span>
+    }
+    #messageControlsContainer {
+      align-items: center;
+      background-color: var(--background-color-secondary);
+      border-bottom: 1px solid var(--border-color);
+      display: flex;
+      height: 2.25em;
+      justify-content: center;
+    }
+    #messageControlsContainer gr-button {
+      padding: var(--spacing-s) 0;
+    }
+    .container {
+      align-items: center;
+      display: flex;
+    }
+    gr-message:not(:last-of-type) {
+      border-bottom: 1px solid var(--border-color);
+    }
+    gr-message:nth-child(2n) {
+      background-color: var(--background-color-secondary);
+    }
+    gr-message:nth-child(2n + 1) {
+      background-color: var(--background-color-tertiary);
+    }
+  </style>
+  <div class="header">
+    <span
+      id="automatedMessageToggleContainer"
+      class="container"
+      hidden$="[[!_hasAutomatedMessages(messages)]]"
+    >
+      <paper-toggle-button
+        id="automatedMessageToggle"
+        checked="{{_hideAutomated}}"
+      ></paper-toggle-button
+      >Only comments
+      <span class="transparent separator"></span>
     </span>
-    <template is="dom-repeat" items="[[_visibleMessages]]" as="message">
-      <gr-message change-num="[[changeNum]]" message="[[message]]" comments="[[_computeCommentsForMessage(changeComments, message)]]" hide-automated="[[_hideAutomated]]" project-name="[[projectName]]" show-reply-button="[[showReplyButtons]]" on-message-anchor-tap="_handleAnchorClick" label-extremes="[[_labelExtremes]]" data-message-id\$="[[message.id]]"></gr-message>
-    </template>
-    <gr-reporting id="reporting" category="message-list"></gr-reporting>
+    <gr-button
+      id="collapse-messages"
+      link=""
+      title="[[_expandAllTitle]]"
+      on-click="_handleExpandCollapseTap"
+    >
+      [[_expandAllState]]
+    </gr-button>
+  </div>
+  <span
+    id="messageControlsContainer"
+    hidden$="[[_computeShowHideTextHidden(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]"
+  >
+    <gr-button id="oldMessagesBtn" link="" on-click="_handleShowAllTap">
+      [[_computeNumMessagesText(_visibleMessages, _processedMessages,
+      _hideAutomated, _visibleMessages.length)]]
+    </gr-button>
+    <span
+      class="container"
+      hidden$="[[_computeIncrementHidden(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]"
+    >
+      <span class="transparent separator"></span>
+      <gr-button
+        id="incrementMessagesBtn"
+        link=""
+        on-click="_handleIncrementShownMessages"
+      >
+        [[_computeIncrementText(_visibleMessages, _processedMessages,
+        _hideAutomated, _visibleMessages.length)]]
+      </gr-button>
+    </span>
+  </span>
+  <template is="dom-repeat" items="[[_visibleMessages]]" as="message">
+    <gr-message
+      change-num="[[changeNum]]"
+      message="[[message]]"
+      comments="[[_computeCommentsForMessage(changeComments, message)]]"
+      hide-automated="[[_hideAutomated]]"
+      project-name="[[projectName]]"
+      show-reply-button="[[showReplyButtons]]"
+      on-message-anchor-tap="_handleAnchorClick"
+      label-extremes="[[_labelExtremes]]"
+      data-message-id$="[[message.id]]"
+    ></gr-message>
+  </template>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
index 79ee96e..4a0f3f1 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-messages-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -45,7 +46,7 @@
 import '../../../test/common-test-setup.js';
 import '../../diff/gr-comment-api/gr-comment-api.js';
 import './gr-messages-list.js';
-import '../../diff/gr-comment-api/gr-comment-api-mock_test.js';
+import '../../../test/mocks/comment-api.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 const randomMessage = function(opt_params) {
   const params = opt_params || {};
@@ -78,10 +79,6 @@
     return dom(element.root).querySelectorAll('gr-message');
   };
 
-  const MESSAGE_ID_0 = '1234ccc949c6d482b061be6a28e10782abf0e7af';
-  const MESSAGE_ID_1 = '8c19ccc949c6d482b061be6a28e10782abf0e7af';
-  const MESSAGE_ID_2 = 'e7bfdbc842f6b6d8064bc68e0f52b673f40c0ca5';
-
   const author = {
     _account_id: 42,
     name: 'Marvin the Paranoid Android',
@@ -92,7 +89,6 @@
     file1: [
       {
         message: 'message text',
-        change_message_id: MESSAGE_ID_0,
         updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: '6505d749_f0bec0aa',
         line: 62,
@@ -105,7 +101,6 @@
       },
       {
         message: 'message text',
-        change_message_id: MESSAGE_ID_1,
         updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: 'c5912363_6b820105',
         line: 42,
@@ -115,7 +110,6 @@
       },
       {
         message: 'message text',
-        change_message_id: MESSAGE_ID_1,
         updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: '6505d749_f0bec0aa',
         line: 62,
@@ -123,20 +117,10 @@
         patch_set: 2,
         author,
       },
-      {
-        message: 'message text',
-        change_message_id: MESSAGE_ID_2,
-        updated: '2016-09-27 00:18:03.000000000',
-        line: 64,
-        id: '34ed05d749_10ed44b2',
-        patch_set: 2,
-        author,
-      },
     ],
     file2: [
       {
         message: 'message text',
-        change_message_id: MESSAGE_ID_1,
         updated: '2016-09-27 00:18:03.000000000',
         in_reply_to: 'c5912363_4b7d450a',
         line: 132,
@@ -406,8 +390,8 @@
           }
       );
       element.messages = messages;
-      const isAuthor = function(author, comment) {
-        return comment.author._account_id === author._account_id;
+      const isAuthor = function(author, message) {
+        return message.author._account_id === author._account_id;
       };
       const isMarvin = isAuthor.bind(null, author);
       flushAsynchronousOperations();
@@ -416,14 +400,16 @@
       assert.deepEqual(messageElements[1].message, messages[1]);
       assert.deepEqual(messageElements[2].message, messages[2]);
       assert.deepEqual(messageElements[1].comments.file1,
-          comments.file1.filter(isMarvin).filter(
-              c => c.change_message_id === messages[1].id));
+          comments.file1.filter(isMarvin).map(c => {
+            return {...c,
+              path: 'file1'};
+          }));
       assert.deepEqual(messageElements[1].comments.file2,
-          comments.file2.filter(isMarvin).filter(
-              c => c.change_message_id === messages[1].id));
-      assert.deepEqual(messageElements[2].comments.file1,
-          comments.file1.filter(isMarvin).filter(
-              c => c.change_message_id === messages[2].id));
+          comments.file2.filter(isMarvin).map(c => {
+            return {...c,
+              path: 'file2'};
+          }));
+      assert.deepEqual(messageElements[2].comments, {});
     });
 
     test('messages without author do not throw', () => {
@@ -570,7 +556,7 @@
     });
 
     test('initially show only 20 messages', () => {
-      sandbox.stub(element.$.reporting, 'reportInteraction',
+      sandbox.stub(element.reporting, 'reportInteraction',
           (eventName, details) => {
             assert.equal(typeof(eventName), 'string');
             if (details) {
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 631542a..271e2ff 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import '../../../styles/shared-styles.js';
 import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
@@ -29,7 +27,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRelatedChangesList extends mixinBehaviors( [
   PatchSetBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.js
index 1d8551d..687dbd7 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.js
@@ -17,145 +17,187 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      h3 {
-        margin: var(--spacing-m) 0 0;
-      }
-      section {
-        margin-bottom: 1.4em; /* Same as line height for collapse purposes */
-      }
-      a {
-        display: block;
-      }
-      .changeContainer,
-      a {
-        max-width: 100%;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-      .changeContainer {
-        display: flex;
-      }
-      .changeContainer.thisChange:before {
-        content: 'âž”';
-        width: 1.2em;
-      }
-      h4,
-      section div {
-        display: flex;
-      }
-      h4:before,
-      section div:before {
-        content: ' ';
-        flex-shrink: 0;
-        width: 1.2em;
-      }
-      .note {
-        color: var(--error-text-color);
-      }
-      .relatedChanges a {
-        display: inline-block;
-      }
-      .strikethrough {
-        color: var(--deemphasized-text-color);
-        text-decoration: line-through;
-      }
-      .status {
-        color: var(--deemphasized-text-color);
-        font-weight: var(--font-weight-bold);
-        margin-left: var(--spacing-xs);
-      }
-      .notCurrent {
-        color: #e65100;
-      }
-      .indirectAncestor {
-        color: #33691e;
-      }
-      .submittable {
-        color: #1b5e20;
-      }
-      .submittableCheck {
-        color: var(--vote-text-color-recommended);
-        display: none;
-      }
-      .submittableCheck.submittable {
-        display: inline;
-      }
-      .hidden,
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    h3 {
+      margin: var(--spacing-m) 0 0;
+    }
+    section {
+      margin-bottom: 1.4em; /* Same as line height for collapse purposes */
+    }
+    a {
+      display: block;
+    }
+    .changeContainer,
+    a {
+      max-width: 100%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    .changeContainer {
+      display: flex;
+    }
+    .changeContainer.thisChange:before {
+      content: 'âž”';
+      width: 1.2em;
+    }
+    h4,
+    section div {
+      display: flex;
+    }
+    h4:before,
+    section div:before {
+      content: ' ';
+      flex-shrink: 0;
+      width: 1.2em;
+    }
+    .note {
+      color: var(--error-text-color);
+    }
+    .relatedChanges a {
+      display: inline-block;
+    }
+    .strikethrough {
+      color: var(--deemphasized-text-color);
+      text-decoration: line-through;
+    }
+    .status {
+      color: var(--deemphasized-text-color);
+      font-weight: var(--font-weight-bold);
+      margin-left: var(--spacing-xs);
+    }
+    .notCurrent {
+      color: #e65100;
+    }
+    .indirectAncestor {
+      color: #33691e;
+    }
+    .submittable {
+      color: #1b5e20;
+    }
+    .submittableCheck {
+      color: var(--vote-text-color-recommended);
+      display: none;
+    }
+    .submittableCheck.submittable {
+      display: inline;
+    }
+    .hidden,
+    .mobile {
+      display: none;
+    }
+    @media screen and (max-width: 60em) {
       .mobile {
-        display: none;
+        display: block;
       }
-       @media screen and (max-width: 60em) {
-        .mobile {
-          display: block;
-        }
-      }
-    </style>
-    <div>
-      <section class="relatedChanges" hidden\$="[[!_relatedResponse.changes.length]]" hidden="">
-        <h4>Relation chain</h4>
-        <template is="dom-repeat" items="[[_relatedResponse.changes]]" as="related">
-          <div class\$="rightIndent [[_computeChangeContainerClass(change, related)]]">
-            <a href\$="[[_computeChangeURL(related._change_number, related.project, related._revision_number)]]" class\$="[[_computeLinkClass(related)]]" title\$="[[related.commit.subject]]">
-              [[related.commit.subject]]
-            </a>
-            <span class\$="[[_computeChangeStatusClass(related)]]">
-              ([[_computeChangeStatus(related)]])
-            </span>
-          </div>
-        </template>
-      </section>
-      <section id="submittedTogether" class\$="[[_computeSubmittedTogetherClass(_submittedTogether)]]">
-        <h4>Submitted together</h4>
-        <template is="dom-repeat" items="[[_submittedTogether.changes]]" as="related">
-          <div class\$="[[_computeChangeContainerClass(change, related)]]">
-            <a href\$="[[_computeChangeURL(related._number, related.project)]]" class\$="[[_computeLinkClass(related)]]" title\$="[[related.project]]: [[related.branch]]: [[related.subject]]">
-              [[related.project]]: [[related.branch]]: [[related.subject]]
-            </a>
-            <span tabindex="-1" title="Submittable" class\$="submittableCheck [[_computeLinkClass(related)]]">✓</span>
-          </div>
-        </template>
-        <template is="dom-if" if="[[_submittedTogether.non_visible_changes]]">
-          <div class="note">
-            [[_computeNonVisibleChangesNote(_submittedTogether.non_visible_changes)]]
-          </div>
-        </template>
-      </section>
-      <section hidden\$="[[!_sameTopic.length]]" hidden="">
-        <h4>Same topic</h4>
-        <template is="dom-repeat" items="[[_sameTopic]]" as="change">
-          <div>
-            <a href\$="[[_computeChangeURL(change._number, change.project)]]" class\$="[[_computeLinkClass(change)]]" title\$="[[change.project]]: [[change.branch]]: [[change.subject]]">
-              [[change.project]]: [[change.branch]]: [[change.subject]]
-            </a>
-          </div>
-        </template>
-      </section>
-      <section hidden\$="[[!_conflicts.length]]" hidden="">
-        <h4>Merge conflicts</h4>
-        <template is="dom-repeat" items="[[_conflicts]]" as="change">
-          <div>
-            <a href\$="[[_computeChangeURL(change._number, change.project)]]" class\$="[[_computeLinkClass(change)]]" title\$="[[change.subject]]">
-              [[change.subject]]
-            </a>
-          </div>
-        </template>
-      </section>
-      <section hidden\$="[[!_cherryPicks.length]]" hidden="">
-        <h4>Cherry picks</h4>
-        <template is="dom-repeat" items="[[_cherryPicks]]" as="change">
-          <div>
-            <a href\$="[[_computeChangeURL(change._number, change.project)]]" class\$="[[_computeLinkClass(change)]]" title\$="[[change.branch]]: [[change.subject]]">
-              [[change.branch]]: [[change.subject]]
-            </a>
-          </div>
-        </template>
-      </section>
-    </div>
-    <div hidden\$="[[!loading]]">Loading...</div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    }
+  </style>
+  <div>
+    <section
+      class="relatedChanges"
+      hidden$="[[!_relatedResponse.changes.length]]"
+      hidden=""
+    >
+      <h4>Relation chain</h4>
+      <template
+        is="dom-repeat"
+        items="[[_relatedResponse.changes]]"
+        as="related"
+      >
+        <div
+          class$="rightIndent [[_computeChangeContainerClass(change, related)]]"
+        >
+          <a
+            href$="[[_computeChangeURL(related._change_number, related.project, related._revision_number)]]"
+            class$="[[_computeLinkClass(related)]]"
+            title$="[[related.commit.subject]]"
+          >
+            [[related.commit.subject]]
+          </a>
+          <span class$="[[_computeChangeStatusClass(related)]]">
+            ([[_computeChangeStatus(related)]])
+          </span>
+        </div>
+      </template>
+    </section>
+    <section
+      id="submittedTogether"
+      class$="[[_computeSubmittedTogetherClass(_submittedTogether)]]"
+    >
+      <h4>Submitted together</h4>
+      <template
+        is="dom-repeat"
+        items="[[_submittedTogether.changes]]"
+        as="related"
+      >
+        <div class$="[[_computeChangeContainerClass(change, related)]]">
+          <a
+            href$="[[_computeChangeURL(related._number, related.project)]]"
+            class$="[[_computeLinkClass(related)]]"
+            title$="[[related.project]]: [[related.branch]]: [[related.subject]]"
+          >
+            [[related.project]]: [[related.branch]]: [[related.subject]]
+          </a>
+          <span
+            tabindex="-1"
+            title="Submittable"
+            class$="submittableCheck [[_computeLinkClass(related)]]"
+            >✓</span
+          >
+        </div>
+      </template>
+      <template is="dom-if" if="[[_submittedTogether.non_visible_changes]]">
+        <div class="note">
+          [[_computeNonVisibleChangesNote(_submittedTogether.non_visible_changes)]]
+        </div>
+      </template>
+    </section>
+    <section hidden$="[[!_sameTopic.length]]" hidden="">
+      <h4>Same topic</h4>
+      <template is="dom-repeat" items="[[_sameTopic]]" as="change">
+        <div>
+          <a
+            href$="[[_computeChangeURL(change._number, change.project)]]"
+            class$="[[_computeLinkClass(change)]]"
+            title$="[[change.project]]: [[change.branch]]: [[change.subject]]"
+          >
+            [[change.project]]: [[change.branch]]: [[change.subject]]
+          </a>
+        </div>
+      </template>
+    </section>
+    <section hidden$="[[!_conflicts.length]]" hidden="">
+      <h4>Merge conflicts</h4>
+      <template is="dom-repeat" items="[[_conflicts]]" as="change">
+        <div>
+          <a
+            href$="[[_computeChangeURL(change._number, change.project)]]"
+            class$="[[_computeLinkClass(change)]]"
+            title$="[[change.subject]]"
+          >
+            [[change.subject]]
+          </a>
+        </div>
+      </template>
+    </section>
+    <section hidden$="[[!_cherryPicks.length]]" hidden="">
+      <h4>Cherry picks</h4>
+      <template is="dom-repeat" items="[[_cherryPicks]]" as="change">
+        <div>
+          <a
+            href$="[[_computeChangeURL(change._number, change.project)]]"
+            class$="[[_computeLinkClass(change)]]"
+            title$="[[change.branch]]: [[change.subject]]"
+          >
+            [[change.branch]]: [[change.subject]]
+          </a>
+        </div>
+      </template>
+    </section>
+  </div>
+  <div hidden$="[[!loading]]">Loading...</div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
index 2425d8b..763a6da 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-related-changes-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></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 fba487b..d3232e9 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-reply-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 aa3481b..e5f1d47 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
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
 import '../../shared/gr-account-chip/gr-account-chip.js';
 import '../../shared/gr-textarea/gr-textarea.js';
@@ -43,6 +40,7 @@
 import {RESTClientBehavior} from '../../../behaviors/rest-client-behavior/rest-client-behavior.js';
 import {GrReviewerSuggestionsProvider, SUGGESTIONS_PROVIDERS_USERS_TYPES} from '../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
+import {appContext} from '../../../services/app-context.js';
 
 const STORAGE_DEBOUNCE_INTERVAL_MS = 400;
 
@@ -80,7 +78,7 @@
 const SEND_REPLY_TIMING_LABEL = 'SendReply';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrReplyDialog extends mixinBehaviors( [
   BaseUrlBehavior,
@@ -134,6 +132,7 @@
   constructor() {
     super();
     this.FocusTarget = FocusTarget;
+    this.reporting = appContext.reportingService;
   }
 
   static get properties() {
@@ -244,11 +243,15 @@
         type: String,
         value: '',
       },
+      _commentEditing: {
+        type: Boolean,
+        value: false,
+      },
       _sendDisabled: {
         type: Boolean,
         computed: '_computeSendButtonDisabled(_sendButtonLabel, ' +
           'draftCommentThreads, draft, _reviewersMutated, _labelsChanged, ' +
-          '_includeComments, disabled)',
+          '_includeComments, disabled, _commentEditing)',
         observer: '_sendDisabledChanged',
       },
       draftCommentThreads: {
@@ -279,6 +282,10 @@
     this._getAccount().then(account => {
       this._account = account || {};
     });
+
+    this.addEventListener('comment-editing-changed', e => {
+      this._commentEditing = e.detail;
+    });
   }
 
   /** @override */
@@ -469,7 +476,7 @@
   }
 
   send(includeComments, startReview) {
-    this.$.reporting.time(SEND_REPLY_TIMING_LABEL);
+    this.reporting.time(SEND_REPLY_TIMING_LABEL);
     const labels = this.$.labelScores.getLabelValues();
 
     const obj = {
@@ -876,7 +883,7 @@
 
   _computeSendButtonDisabled(
       buttonLabel, draftCommentThreads, text, reviewersMutated,
-      labelsChanged, includeComments, disabled) {
+      labelsChanged, includeComments, disabled, commentEditing) {
     // Polymer 2: check for undefined
     if ([
       buttonLabel,
@@ -886,11 +893,12 @@
       labelsChanged,
       includeComments,
       disabled,
+      commentEditing,
     ].some(arg => arg === undefined)) {
       return undefined;
     }
 
-    if (disabled) { return true; }
+    if (commentEditing || disabled) { return true; }
     if (buttonLabel === ButtonLabels.START_REVIEW) { return false; }
     const hasDrafts = includeComments && draftCommentThreads.length;
     return !hasDrafts && !text.length && !reviewersMutated && !labelsChanged;
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.js
index 0c98834..9cd8071 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.js
@@ -17,213 +17,305 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        background-color: var(--dialog-background-color);
-        display: block;
-        max-height: 90vh;
-      }
-      :host([disabled]) {
-        pointer-events: none;
-      }
-      :host([disabled]) .container {
-        opacity: .5;
-      }
-      .container {
-        display: flex;
-        flex-direction: column;
-        max-height: 100%;
-      }
-      section {
-        border-top: 1px solid var(--border-color);
-        flex-shrink: 0;
-        padding: var(--spacing-m) var(--spacing-xl);
-        width: 100%;
-      }
-      section.labelsContainer {
-        /* We want the :hover highlight to extend to the border of the dialog. */
-        padding: var(--spacing-m) 0;
-      }
-      .actions {
-        background-color: var(--dialog-background-color);
-        bottom: 0;
-        display: flex;
-        justify-content: space-between;
-        position: sticky;
-        /* @see Issue 8602 */
-        z-index: 1;
-      }
-      .actions .right gr-button {
-        margin-left: var(--spacing-l);
-      }
-      .peopleContainer,
-      .labelsContainer {
-        flex-shrink: 0;
-      }
-      .peopleContainer {
-        border-top: none;
-        display: table;
-      }
-      .peopleList {
-        display: flex;
-      }
-      .peopleListLabel {
-        color: var(--deemphasized-text-color);
-        margin-top: var(--spacing-xs);
-        min-width: 6em;
-        padding-right: var(--spacing-m);
-      }
-      gr-account-list {
-        display: flex;
-        flex-wrap: wrap;
-        flex: 1;
-      }
-      #reviewerConfirmationOverlay {
-        padding: var(--spacing-l);
-        text-align: center;
-      }
-      .reviewerConfirmationButtons {
-        margin-top: var(--spacing-l);
-      }
-      .groupName {
-        font-weight: var(--font-weight-bold);
-      }
-      .groupSize {
-        font-style: italic;
-      }
-      .textareaContainer {
-        min-height: 12em;
-        position: relative;
-      }
-      .textareaContainer,
-      #textarea,
-      gr-endpoint-decorator {
-        display: flex;
-        width: 100%;
-      }
-      gr-endpoint-decorator[name="reply-label-scores"] {
-        display: block;
-      }
-      .previewContainer gr-formatted-text {
-        background: var(--table-header-background-color);
-        padding: var(--spacing-l);
-      }
-      .draftsContainer h3 {
-        margin-top: var(--spacing-xs);
-      }
-      #checkingStatusLabel,
-      #notLatestLabel {
-        margin-left: var(--spacing-l);
-      }
-      #checkingStatusLabel {
-        color: var(--deemphasized-text-color);
-        font-style: italic;
-      }
-      #notLatestLabel,
-      #savingLabel {
-        color: var(--error-text-color);
-      }
-      #savingLabel {
-        display: none;
-      }
-      #savingLabel.saving {
-        display: inline;
-      }
-      #pluginMessage {
-        color: var(--deemphasized-text-color);
-        margin-left: var(--spacing-l);
-        margin-bottom: var(--spacing-m);
-      }
-      #pluginMessage:empty {
-        display: none;
-      }
-    </style>
-    <div class="container" tabindex="-1">
-      <section class="peopleContainer">
-        <div class="peopleList">
-          <div class="peopleListLabel">Reviewers</div>
-          <gr-account-list id="reviewers" accounts="{{_reviewers}}" removable-values="[[change.removable_reviewers]]" filter="[[filterReviewerSuggestion]]" pending-confirmation="{{_reviewerPendingConfirmation}}" placeholder="Add reviewer..." on-account-text-changed="_handleAccountTextEntry" suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
-          </gr-account-list>
+  <style include="shared-styles">
+    :host {
+      background-color: var(--dialog-background-color);
+      display: block;
+      max-height: 90vh;
+    }
+    :host([disabled]) {
+      pointer-events: none;
+    }
+    :host([disabled]) .container {
+      opacity: 0.5;
+    }
+    .container {
+      display: flex;
+      flex-direction: column;
+      max-height: 100%;
+    }
+    section {
+      border-top: 1px solid var(--border-color);
+      flex-shrink: 0;
+      padding: var(--spacing-m) var(--spacing-xl);
+      width: 100%;
+    }
+    section.labelsContainer {
+      /* We want the :hover highlight to extend to the border of the dialog. */
+      padding: var(--spacing-m) 0;
+    }
+    .actions {
+      background-color: var(--dialog-background-color);
+      bottom: 0;
+      display: flex;
+      justify-content: space-between;
+      position: sticky;
+      /* @see Issue 8602 */
+      z-index: 1;
+    }
+    .actions .right gr-button {
+      margin-left: var(--spacing-l);
+    }
+    .peopleContainer,
+    .labelsContainer {
+      flex-shrink: 0;
+    }
+    .peopleContainer {
+      border-top: none;
+      display: table;
+    }
+    .peopleList {
+      display: flex;
+    }
+    .peopleListLabel {
+      color: var(--deemphasized-text-color);
+      margin-top: var(--spacing-xs);
+      min-width: 6em;
+      padding-right: var(--spacing-m);
+    }
+    gr-account-list {
+      display: flex;
+      flex-wrap: wrap;
+      flex: 1;
+    }
+    #reviewerConfirmationOverlay {
+      padding: var(--spacing-l);
+      text-align: center;
+    }
+    .reviewerConfirmationButtons {
+      margin-top: var(--spacing-l);
+    }
+    .groupName {
+      font-weight: var(--font-weight-bold);
+    }
+    .groupSize {
+      font-style: italic;
+    }
+    .textareaContainer {
+      min-height: 12em;
+      position: relative;
+    }
+    .textareaContainer,
+    #textarea,
+    gr-endpoint-decorator {
+      display: flex;
+      width: 100%;
+    }
+    gr-endpoint-decorator[name='reply-label-scores'] {
+      display: block;
+    }
+    .previewContainer gr-formatted-text {
+      background: var(--table-header-background-color);
+      padding: var(--spacing-l);
+    }
+    .draftsContainer h3 {
+      margin-top: var(--spacing-xs);
+    }
+    #checkingStatusLabel,
+    #notLatestLabel {
+      margin-left: var(--spacing-l);
+    }
+    #checkingStatusLabel {
+      color: var(--deemphasized-text-color);
+      font-style: italic;
+    }
+    #notLatestLabel,
+    #savingLabel {
+      color: var(--error-text-color);
+    }
+    #savingLabel {
+      display: none;
+    }
+    #savingLabel.saving {
+      display: inline;
+    }
+    #pluginMessage {
+      color: var(--deemphasized-text-color);
+      margin-left: var(--spacing-l);
+      margin-bottom: var(--spacing-m);
+    }
+    #pluginMessage:empty {
+      display: none;
+    }
+  </style>
+  <div class="container" tabindex="-1">
+    <section class="peopleContainer">
+      <div class="peopleList">
+        <div class="peopleListLabel">Reviewers</div>
+        <gr-account-list
+          id="reviewers"
+          accounts="{{_reviewers}}"
+          removable-values="[[change.removable_reviewers]]"
+          filter="[[filterReviewerSuggestion]]"
+          pending-confirmation="{{_reviewerPendingConfirmation}}"
+          placeholder="Add reviewer..."
+          on-account-text-changed="_handleAccountTextEntry"
+          suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]"
+        >
+        </gr-account-list>
+      </div>
+      <div class="peopleList">
+        <div class="peopleListLabel">CC</div>
+        <gr-account-list
+          id="ccs"
+          accounts="{{_ccs}}"
+          filter="[[filterCCSuggestion]]"
+          pending-confirmation="{{_ccPendingConfirmation}}"
+          allow-any-input=""
+          placeholder="Add CC..."
+          on-account-text-changed="_handleAccountTextEntry"
+          suggestions-provider="[[_getCcSuggestionsProvider(change)]]"
+        >
+        </gr-account-list>
+      </div>
+      <gr-overlay
+        id="reviewerConfirmationOverlay"
+        on-iron-overlay-canceled="_cancelPendingReviewer"
+      >
+        <div class="reviewerConfirmation">
+          Group
+          <span class="groupName">
+            [[_pendingConfirmationDetails.group.name]]
+          </span>
+          has
+          <span class="groupSize">
+            [[_pendingConfirmationDetails.count]]
+          </span>
+          members.
+          <br />
+          Are you sure you want to add them all?
         </div>
-        <div class="peopleList">
-          <div class="peopleListLabel">CC</div>
-          <gr-account-list id="ccs" accounts="{{_ccs}}" filter="[[filterCCSuggestion]]" pending-confirmation="{{_ccPendingConfirmation}}" allow-any-input="" placeholder="Add CC..." on-account-text-changed="_handleAccountTextEntry" suggestions-provider="[[_getCcSuggestionsProvider(change)]]">
-          </gr-account-list>
+        <div class="reviewerConfirmationButtons">
+          <gr-button on-click="_confirmPendingReviewer">Yes</gr-button>
+          <gr-button on-click="_cancelPendingReviewer">No</gr-button>
         </div>
-        <gr-overlay id="reviewerConfirmationOverlay" on-iron-overlay-canceled="_cancelPendingReviewer">
-          <div class="reviewerConfirmation">
-            Group
-            <span class="groupName">
-              [[_pendingConfirmationDetails.group.name]]
-            </span>
-            has
-            <span class="groupSize">
-              [[_pendingConfirmationDetails.count]]
-            </span>
-            members.
-            <br>
-            Are you sure you want to add them all?
-          </div>
-          <div class="reviewerConfirmationButtons">
-            <gr-button on-click="_confirmPendingReviewer">Yes</gr-button>
-            <gr-button on-click="_cancelPendingReviewer">No</gr-button>
-          </div>
-        </gr-overlay>
-      </section>
-      <section class="textareaContainer">
-        <gr-endpoint-decorator name="reply-text">
-          <gr-textarea id="textarea" class="message" autocomplete="on" placeholder="[[_messagePlaceholder]]" fixed-position-dropdown="" hide-border="true" monospace="true" disabled="{{disabled}}" rows="4" text="{{draft}}" on-bind-value-changed="_handleHeightChanged">
-          </gr-textarea>
-        </gr-endpoint-decorator>
-      </section>
-      <section class="previewContainer">
-        <label>
-          <input type="checkbox" checked="{{_previewFormatting::change}}">
-          Preview formatting
-        </label>
-        <gr-formatted-text content="[[draft]]" hidden\$="[[!_previewFormatting]]" config="[[projectConfig.commentlinks]]"></gr-formatted-text>
-      </section>
-      <section class="labelsContainer">
-        <gr-endpoint-decorator name="reply-label-scores">
-          <gr-label-scores id="labelScores" account="[[_account]]" change="[[change]]" on-labels-changed="_handleLabelsChanged" permitted-labels="[[permittedLabels]]"></gr-label-scores>
-        </gr-endpoint-decorator>
-        <div id="pluginMessage">[[_pluginMessage]]</div>
-      </section>
-      <section class="draftsContainer" hidden\$="[[_computeHideDraftList(draftCommentThreads)]]">
-        <div class="includeComments">
-          <input type="checkbox" id="includeComments" checked="{{_includeComments::change}}">
-          <label for="includeComments">Publish [[_computeDraftsTitle(draftCommentThreads)]]</label>
-        </div>
-        <gr-thread-list id="commentList" hidden\$="[[!_includeComments]]" threads="[[draftCommentThreads]]" change="[[change]]" change-num="[[change._number]]" logged-in="true" hide-toggle-buttons="" on-thread-list-modified="_onThreadListModified">
-        </gr-thread-list>
-        <span id="savingLabel" class\$="[[_computeSavingLabelClass(_savingComments)]]">
-          Saving comments...
+      </gr-overlay>
+    </section>
+    <section class="textareaContainer">
+      <gr-endpoint-decorator name="reply-text">
+        <gr-textarea
+          id="textarea"
+          class="message"
+          autocomplete="on"
+          placeholder="[[_messagePlaceholder]]"
+          fixed-position-dropdown=""
+          hide-border="true"
+          monospace="true"
+          disabled="{{disabled}}"
+          rows="4"
+          text="{{draft}}"
+          on-bind-value-changed="_handleHeightChanged"
+        >
+        </gr-textarea>
+      </gr-endpoint-decorator>
+    </section>
+    <section class="previewContainer">
+      <label>
+        <input type="checkbox" checked="{{_previewFormatting::change}}" />
+        Preview formatting
+      </label>
+      <gr-formatted-text
+        content="[[draft]]"
+        hidden$="[[!_previewFormatting]]"
+        config="[[projectConfig.commentlinks]]"
+      ></gr-formatted-text>
+    </section>
+    <section class="labelsContainer">
+      <gr-endpoint-decorator name="reply-label-scores">
+        <gr-label-scores
+          id="labelScores"
+          account="[[_account]]"
+          change="[[change]]"
+          on-labels-changed="_handleLabelsChanged"
+          permitted-labels="[[permittedLabels]]"
+        ></gr-label-scores>
+      </gr-endpoint-decorator>
+      <div id="pluginMessage">[[_pluginMessage]]</div>
+    </section>
+    <section
+      class="draftsContainer"
+      hidden$="[[_computeHideDraftList(draftCommentThreads)]]"
+    >
+      <div class="includeComments">
+        <input
+          type="checkbox"
+          id="includeComments"
+          checked="{{_includeComments::change}}"
+        />
+        <label for="includeComments"
+          >Publish [[_computeDraftsTitle(draftCommentThreads)]]</label
+        >
+      </div>
+      <gr-thread-list
+        id="commentList"
+        hidden$="[[!_includeComments]]"
+        threads="[[draftCommentThreads]]"
+        change="[[change]]"
+        change-num="[[change._number]]"
+        logged-in="true"
+        hide-toggle-buttons=""
+        on-thread-list-modified="_onThreadListModified"
+      >
+      </gr-thread-list>
+      <span
+        id="savingLabel"
+        class$="[[_computeSavingLabelClass(_savingComments)]]"
+      >
+        Saving comments...
+      </span>
+    </section>
+    <section class="actions">
+      <div class="left">
+        <span
+          id="checkingStatusLabel"
+          hidden$="[[!_isState(knownLatestState, 'checking')]]"
+        >
+          Checking whether patch [[patchNum]] is latest...
         </span>
-      </section>
-      <section class="actions">
-        <div class="left">
-          <span id="checkingStatusLabel" hidden\$="[[!_isState(knownLatestState, 'checking')]]">
-            Checking whether patch [[patchNum]] is latest...
-          </span>
-          <span id="notLatestLabel" hidden\$="[[!_isState(knownLatestState, 'not-latest')]]">
-            [[_computePatchSetWarning(patchNum, _labelsChanged)]]
-            <gr-button link="" on-click="_reload">Reload</gr-button>
-          </span>
-        </div>
-        <div class="right">
-          <gr-button link="" id="cancelButton" class="action cancel" on-click="_cancelTapHandler">Cancel</gr-button>
-          <template is="dom-if" if="[[canBeStarted]]">
-            <!-- Use 'Send' here as the change may only about reviewers / ccs
+        <span
+          id="notLatestLabel"
+          hidden$="[[!_isState(knownLatestState, 'not-latest')]]"
+        >
+          [[_computePatchSetWarning(patchNum, _labelsChanged)]]
+          <gr-button link="" on-click="_reload">Reload</gr-button>
+        </span>
+      </div>
+      <div class="right">
+        <gr-button
+          link=""
+          id="cancelButton"
+          class="action cancel"
+          on-click="_cancelTapHandler"
+          >Cancel</gr-button
+        >
+        <template is="dom-if" if="[[canBeStarted]]">
+          <!-- Use 'Send' here as the change may only about reviewers / ccs
               and when this button is visible, the next button will always
               be 'Start review' -->
-            <gr-button link="" disabled="[[_isState(knownLatestState, 'not-latest')]]" class="action save" has-tooltip="" title="[[_saveTooltip]]" on-click="_saveClickHandler">Save</gr-button>
-          </template>
-          <gr-button id="sendButton" primary="" disabled="[[_sendDisabled]]" class="action send" has-tooltip="" title\$="[[_computeSendButtonTooltip(canBeStarted)]]" on-click="_sendTapHandler">[[_sendButtonLabel]]</gr-button>
-        </div>
-      </section>
-    </div>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
-    <gr-reporting id="reporting"></gr-reporting>
+          <gr-button
+            link=""
+            disabled="[[_isState(knownLatestState, 'not-latest')]]"
+            class="action save"
+            has-tooltip=""
+            title="[[_saveTooltip]]"
+            on-click="_saveClickHandler"
+            >Save</gr-button
+          >
+        </template>
+        <gr-button
+          id="sendButton"
+          primary=""
+          disabled="[[_sendDisabled]]"
+          class="action send"
+          has-tooltip=""
+          title$="[[_computeSendButtonTooltip(canBeStarted)]]"
+          on-click="_sendTapHandler"
+          >[[_sendButtonLabel]]</gr-button
+        >
+      </div>
+    </section>
+  </div>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-storage id="storage"></gr-storage>
 `;
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 98c703f..91b4db2 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-reply-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -1146,7 +1147,8 @@
         /* reviewersMutated= */ false,
         /* labelsChanged= */ false,
         /* includeComments= */ false,
-        /* disabled= */ false
+        /* disabled= */ false,
+        /* commentEditing= */ false
     ));
     assert.isTrue(fn(
         /* buttonLabel= */ 'Send',
@@ -1155,7 +1157,8 @@
         /* reviewersMutated= */ false,
         /* labelsChanged= */ false,
         /* includeComments= */ false,
-        /* disabled= */ false
+        /* disabled= */ false,
+        /* commentEditing= */ false
     ));
     // Mock nonempty comment draft array, with seding comments.
     assert.isFalse(fn(
@@ -1165,7 +1168,8 @@
         /* reviewersMutated= */ false,
         /* labelsChanged= */ false,
         /* includeComments= */ true,
-        /* disabled= */ false
+        /* disabled= */ false,
+        /* commentEditing= */ false
     ));
     // Mock nonempty comment draft array, without seding comments.
     assert.isTrue(fn(
@@ -1175,7 +1179,8 @@
         /* reviewersMutated= */ false,
         /* labelsChanged= */ false,
         /* includeComments= */ false,
-        /* disabled= */ false
+        /* disabled= */ false,
+        /* commentEditing= */ false
     ));
     // Mock nonempty change message.
     assert.isFalse(fn(
@@ -1185,7 +1190,8 @@
         /* reviewersMutated= */ false,
         /* labelsChanged= */ false,
         /* includeComments= */ false,
-        /* disabled= */ false
+        /* disabled= */ false,
+        /* commentEditing= */ false
     ));
     // Mock reviewers mutated.
     assert.isFalse(fn(
@@ -1195,7 +1201,8 @@
         /* reviewersMutated= */ true,
         /* labelsChanged= */ false,
         /* includeComments= */ false,
-        /* disabled= */ false
+        /* disabled= */ false,
+        /* commentEditing= */ false
     ));
     // Mock labels changed.
     assert.isFalse(fn(
@@ -1205,7 +1212,8 @@
         /* reviewersMutated= */ false,
         /* labelsChanged= */ true,
         /* includeComments= */ false,
-        /* disabled= */ false
+        /* disabled= */ false,
+        /* commentEditing= */ false
     ));
     // Whole dialog is disabled.
     assert.isTrue(fn(
@@ -1215,7 +1223,18 @@
         /* reviewersMutated= */ false,
         /* labelsChanged= */ true,
         /* includeComments= */ false,
-        /* disabled= */ true
+        /* disabled= */ true,
+        /* commentEditing= */ false
+    ));
+    assert.isTrue(fn(
+        /* buttonLabel= */ 'Send',
+        /* draftCommentThreads= */ {},
+        /* text= */ '',
+        /* reviewersMutated= */ false,
+        /* labelsChanged= */ true,
+        /* includeComments= */ false,
+        /* disabled= */ false,
+        /* commentEditing= */ true
     ));
   });
 
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 c933c7c..88e3160 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-account-chip/gr-account-chip.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -27,7 +25,7 @@
 import {htmlTemplate} from './gr-reviewer-list_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrReviewerList extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
index 300e38a..93926cf 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.js
@@ -17,41 +17,55 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) {
+      opacity: 0.8;
+      pointer-events: none;
+    }
+    .container {
+      display: block;
+    }
+    gr-button {
+      --gr-button: {
+        padding: 0px 0px;
       }
-      :host([disabled]) {
-        opacity: .8;
-        pointer-events: none;
-      }
-      .container {
-        display: block;
-      }
-      gr-button {
-        --gr-button: {
-          padding: 0px 0px;
-        }
-      }
-      gr-account-chip {
-        display: inline-block;
-      }
-    </style>
-    <div class="container">
-      <div>
-        <template is="dom-repeat" items="[[_displayedReviewers]]" as="reviewer">
-          <gr-account-chip class="reviewer"
-                           account="[[reviewer]]"
-                           on-remove="_handleRemove"
-                           voteable-text="[[_computeVoteableText(reviewer, change)]]"
-                           removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]">
-          </gr-account-chip>
-        </template>
-      </div>
-      <gr-button class="hiddenReviewers" link="" hidden\$="[[!_hiddenReviewerCount]]" on-click="_handleViewAll">and [[_hiddenReviewerCount]] more</gr-button>
-      <div class="controlsContainer" hidden\$="[[!mutable]]">
-        <gr-button link="" id="addReviewer" class="addReviewer" on-click="_handleAddTap">[[_addLabel]]</gr-button>
-      </div>
+    }
+    gr-account-chip {
+      display: inline-block;
+    }
+  </style>
+  <div class="container">
+    <div>
+      <template is="dom-repeat" items="[[_displayedReviewers]]" as="reviewer">
+        <gr-account-chip
+          class="reviewer"
+          account="[[reviewer]]"
+          on-remove="_handleRemove"
+          voteable-text="[[_computeVoteableText(reviewer, change)]]"
+          removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]"
+        >
+        </gr-account-chip>
+      </template>
     </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    <gr-button
+      class="hiddenReviewers"
+      link=""
+      hidden$="[[!_hiddenReviewerCount]]"
+      on-click="_handleViewAll"
+      >and [[_hiddenReviewerCount]] more</gr-button
+    >
+    <div class="controlsContainer" hidden$="[[!mutable]]">
+      <gr-button
+        link=""
+        id="addReviewer"
+        class="addReviewer"
+        on-click="_handleAddTap"
+        >[[_addLabel]]</gr-button
+      >
+    </div>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
index 9e51d16..6949afc 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-reviewer-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b8079eff..109e15d 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/paper-toggle-button/paper-toggle-button.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-comment-thread/gr-comment-thread.js';
@@ -32,7 +30,7 @@
  * Fired when a comment is saved or deleted
  *
  * @event thread-list-modified
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrThreadList extends GestureEventListeners(
     LegacyElementMixin(
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.js
index cf967ab..fd34b2d 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.js
@@ -17,59 +17,88 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      #threads {
-        display: block;
-        padding: var(--spacing-l);
-      }
-      gr-comment-thread {
-        display: block;
-        margin-bottom: var(--spacing-m);
-        max-width: 80ch;
-      }
-      .header {
-        align-items: center;
-        background-color: var(--table-header-background-color);
-        border-bottom: 1px solid var(--border-color);
-        border-top: 1px solid var(--border-color);
-        display: flex;
-        justify-content: left;
-        min-height: 3.2em;
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      .toggleItem.draftToggle {
-        display: none;
-      }
-      .toggleItem.draftToggle.show {
-        display: flex;
-      }
-      .toggleItem {
-        align-items: center;
-        display: flex;
-        margin-right: var(--spacing-l);
-      }
-      .draftsOnly:not(.unresolvedOnly) gr-comment-thread[has-draft],
-      .unresolvedOnly:not(.draftsOnly) gr-comment-thread[unresolved],
-      .draftsOnly.unresolvedOnly gr-comment-thread[has-draft][unresolved] {
-        display: block
-      }
-    </style>
-    <template is="dom-if" if="[[!hideToggleButtons]]">
-      <div class="header">
-        <div class="toggleItem">
-          <paper-toggle-button id="unresolvedToggle" checked="{{_unresolvedOnly}}"></paper-toggle-button>
-            Only unresolved threads</div>
-        <div class\$="toggleItem draftToggle [[_computeShowDraftToggle(loggedIn)]]">
-          <paper-toggle-button id="draftToggle" checked="{{_draftsOnly}}"></paper-toggle-button>
-            Only threads with drafts</div>
+  <style include="shared-styles">
+    #threads {
+      display: block;
+      padding: var(--spacing-l);
+    }
+    gr-comment-thread {
+      display: block;
+      margin-bottom: var(--spacing-m);
+      max-width: 80ch;
+    }
+    .header {
+      align-items: center;
+      background-color: var(--table-header-background-color);
+      border-bottom: 1px solid var(--border-color);
+      border-top: 1px solid var(--border-color);
+      display: flex;
+      justify-content: left;
+      min-height: 3.2em;
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    .toggleItem.draftToggle {
+      display: none;
+    }
+    .toggleItem.draftToggle.show {
+      display: flex;
+    }
+    .toggleItem {
+      align-items: center;
+      display: flex;
+      margin-right: var(--spacing-l);
+    }
+    .draftsOnly:not(.unresolvedOnly) gr-comment-thread[has-draft],
+    .unresolvedOnly:not(.draftsOnly) gr-comment-thread[unresolved],
+    .draftsOnly.unresolvedOnly gr-comment-thread[has-draft][unresolved] {
+      display: block;
+    }
+  </style>
+  <template is="dom-if" if="[[!hideToggleButtons]]">
+    <div class="header">
+      <div class="toggleItem">
+        <paper-toggle-button
+          id="unresolvedToggle"
+          checked="{{_unresolvedOnly}}"
+        ></paper-toggle-button>
+        Only unresolved threads
       </div>
-    </template>
-    <div id="threads">
-      <template is="dom-if" if="[[!threads.length]]">
-        [[emptyThreadMsg]]
-      </template>
-      <template is="dom-repeat" items="[[_filteredThreads]]" as="thread" initial-count="5" target-framerate="60">
-        <gr-comment-thread show-file-path="" change-num="[[changeNum]]" comments="[[thread.comments]]" comment-side="[[thread.commentSide]]" project-name="[[change.project]]" is-on-parent="[[_isOnParent(thread.commentSide)]]" line-num="[[thread.line]]" patch-num="[[thread.patchNum]]" path="[[thread.path]]" root-id="{{thread.rootId}}" on-thread-changed="_handleCommentsChanged" on-thread-discard="_handleThreadDiscard"></gr-comment-thread>
-      </template>
+      <div
+        class$="toggleItem draftToggle [[_computeShowDraftToggle(loggedIn)]]"
+      >
+        <paper-toggle-button
+          id="draftToggle"
+          checked="{{_draftsOnly}}"
+        ></paper-toggle-button>
+        Only threads with drafts
+      </div>
     </div>
+  </template>
+  <div id="threads">
+    <template is="dom-if" if="[[!threads.length]]">
+      [[emptyThreadMsg]]
+    </template>
+    <template
+      is="dom-repeat"
+      items="[[_filteredThreads]]"
+      as="thread"
+      initial-count="5"
+      target-framerate="60"
+    >
+      <gr-comment-thread
+        show-file-path=""
+        change-num="[[changeNum]]"
+        comments="[[thread.comments]]"
+        comment-side="[[thread.commentSide]]"
+        project-name="[[change.project]]"
+        is-on-parent="[[_isOnParent(thread.commentSide)]]"
+        line-num="[[thread.line]]"
+        patch-num="[[thread.patchNum]]"
+        path="[[thread.path]]"
+        root-id="{{thread.rootId}}"
+        on-thread-changed="_handleCommentsChanged"
+        on-thread-discard="_handleThreadDiscard"
+      ></gr-comment-thread>
+    </template>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
index 9ddc349..4b00d5a 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-thread-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 9171908..0658996 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import '../../shared/gr-shell-command/gr-shell-command.js';
@@ -36,7 +34,7 @@
 ];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrUploadHelpDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.js b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.js
index 3f4fc42..ec010a1 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.js
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_html.js
@@ -17,54 +17,54 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        background-color: var(--dialog-background-color);
-        display: block;
-      }
-      .main {
-        width: 100%;
-      }
-      ol {
-        margin-left: var(--spacing-xl);
-        list-style: decimal;
-      }
-      p {
-        margin-bottom: var(--spacing-m);
-      }
-    </style>
-    <gr-dialog confirm-label="Done" cancel-label="" on-confirm="_handleCloseTap">
-      <div class="header" slot="header">How to update this change:</div>
-      <div class="main" slot="main">
-        <ol>
-          <li>
-            <p>
-              Checkout this change locally and make your desired modifications
-              to the files.
-            </p>
-            <template is="dom-if" if="[[_fetchCommand]]">
-              <gr-shell-command command="[[_fetchCommand]]"></gr-shell-command>
-            </template>
-          </li>
-          <li>
-            <p>
-              Update the local commit with your modifications using the following
-              command.
-            </p>
-            <gr-shell-command command="[[_commitCommand]]"></gr-shell-command>
-            <p>
-              Leave the "Change-Id:" line of the commit message as is.
-            </p>
-          </li>
-          <li>
-            <p>Push the updated commit to Gerrit.</p>
-            <gr-shell-command command="[[_pushCommand]]"></gr-shell-command>
-          </li>
-          <li>
-            <p>Refresh this page to view the the update.</p>
-          </li>
-        </ol>
-      </div>
-    </gr-dialog>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      background-color: var(--dialog-background-color);
+      display: block;
+    }
+    .main {
+      width: 100%;
+    }
+    ol {
+      margin-left: var(--spacing-xl);
+      list-style: decimal;
+    }
+    p {
+      margin-bottom: var(--spacing-m);
+    }
+  </style>
+  <gr-dialog confirm-label="Done" cancel-label="" on-confirm="_handleCloseTap">
+    <div class="header" slot="header">How to update this change:</div>
+    <div class="main" slot="main">
+      <ol>
+        <li>
+          <p>
+            Checkout this change locally and make your desired modifications to
+            the files.
+          </p>
+          <template is="dom-if" if="[[_fetchCommand]]">
+            <gr-shell-command command="[[_fetchCommand]]"></gr-shell-command>
+          </template>
+        </li>
+        <li>
+          <p>
+            Update the local commit with your modifications using the following
+            command.
+          </p>
+          <gr-shell-command command="[[_commitCommand]]"></gr-shell-command>
+          <p>
+            Leave the "Change-Id:" line of the commit message as is.
+          </p>
+        </li>
+        <li>
+          <p>Push the updated commit to Gerrit.</p>
+          <gr-shell-command command="[[_pushCommand]]"></gr-shell-command>
+        </li>
+        <li>
+          <p>Refresh this page to view the the update.</p>
+        </li>
+      </ol>
+    </div>
+  </gr-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html
index bea8113..164c483 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-upload-help-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 2645c63..c194806 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-dropdown/gr-dropdown.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -30,7 +29,7 @@
 const INTERPOLATE_URL_PATTERN = /\$\{([\w]+)\}/g;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccountDropdown extends mixinBehaviors( [
   DisplayNameBehavior,
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.js b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.js
index e22db65..b47894e 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.js
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_html.js
@@ -17,25 +17,36 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      gr-dropdown {
-        padding: 0 var(--spacing-m);
-        --gr-button: {
-          color: var(--header-text-color);
-        }
-        --gr-dropdown-item: {
-          color: var(--primary-text-color);
-        }
+  <style include="shared-styles">
+    gr-dropdown {
+      padding: 0 var(--spacing-m);
+      --gr-button: {
+        color: var(--header-text-color);
       }
-      gr-avatar {
-        height: 2em;
-        width: 2em;
-        vertical-align: middle;
+      --gr-dropdown-item: {
+        color: var(--primary-text-color);
       }
-    </style>
-    <gr-dropdown link="" items="[[links]]" top-content="[[topContent]]" horizontal-align="right">
-        <span hidden\$="[[_hasAvatars]]" hidden="">[[_accountName(account)]]</span>
-        <gr-avatar account="[[account]]" hidden\$="[[!_hasAvatars]]" hidden="" image-size="56" aria-label="Account avatar"></gr-avatar>
-    </gr-dropdown>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    }
+    gr-avatar {
+      height: 2em;
+      width: 2em;
+      vertical-align: middle;
+    }
+  </style>
+  <gr-dropdown
+    link=""
+    items="[[links]]"
+    top-content="[[topContent]]"
+    horizontal-align="right"
+  >
+    <span hidden$="[[_hasAvatars]]" hidden="">[[_accountName(account)]]</span>
+    <gr-avatar
+      account="[[account]]"
+      hidden$="[[!_hasAvatars]]"
+      hidden=""
+      image-size="56"
+      aria-label="Account avatar"
+    ></gr-avatar>
+  </gr-dropdown>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
index d9d932a..6c8ed68 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-account-dropdown</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6814d89..99c4cb3 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-dialog/gr-dialog.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -23,7 +21,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-error-dialog_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrErrorDialog extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.js b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.js
index e18d1bd..39d4f2d 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.js
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_html.js
@@ -17,28 +17,40 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
+  <style include="shared-styles">
+    .main {
+      max-height: 40em;
+      max-width: 60em;
+      overflow-y: auto;
+      white-space: pre-wrap;
+    }
+    @media screen and (max-width: 50em) {
       .main {
-        max-height: 40em;
-        max-width: 60em;
-        overflow-y: auto;
-        white-space: pre-wrap;
+        max-height: none;
+        max-width: 50em;
       }
-      @media screen and (max-width: 50em) {
-        .main {
-          max-height: none;
-          max-width: 50em;
-        }
-      }
-      .signInLink {
-        text-decoration: none;
-      }
-    </style>
-    <gr-dialog id="dialog" cancel-label="" on-confirm="_handleConfirm" confirm-label="Dismiss" confirm-on-enter="">
-      <div class="header" slot="header">An error occurred</div>
-      <div class="main" slot="main">[[text]]</div>
-      <gr-button id="signIn" class\$="signInLink" hidden\$="[[!showSignInButton]]" link="" slot="footer">
-        <a href\$="[[loginUrl]]" class="signInLink">Sign in</a>
-      </gr-button>
-    </gr-dialog>
+    }
+    .signInLink {
+      text-decoration: none;
+    }
+  </style>
+  <gr-dialog
+    id="dialog"
+    cancel-label=""
+    on-confirm="_handleConfirm"
+    confirm-label="Dismiss"
+    confirm-on-enter=""
+  >
+    <div class="header" slot="header">An error occurred</div>
+    <div class="main" slot="main">[[text]]</div>
+    <gr-button
+      id="signIn"
+      class$="signInLink"
+      hidden$="[[!showSignInButton]]"
+      link=""
+      slot="footer"
+    >
+      <a href$="[[loginUrl]]" class="signInLink">Sign in</a>
+    </gr-button>
+  </gr-dialog>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html
index 2e6cff0..bd4991f 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-error-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 4b5969a..8ff445b 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
@@ -16,15 +16,7 @@
  */
 /* Import to get Gerrit interface */
 /* TODO(taoalpha): decouple gr-gerrit from gr-js-api-interface */
-/*
-  FIXME(polymer-modulizer): the above comments were extracted
-  from HTML and may be out of place here. Review them and
-  then delete this comment!
-*/
-
-import '../../../scripts/bundled-polymer.js';
 import '../gr-error-dialog/gr-error-dialog.js';
-import '../gr-reporting/gr-reporting.js';
 import '../../shared/gr-alert/gr-alert.js';
 import '../../shared/gr-overlay/gr-overlay.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -37,6 +29,7 @@
 import {BaseUrlBehavior} from '../../../behaviors/base-url-behavior/base-url-behavior.js';
 import {authService} from '../../shared/gr-rest-api-interface/gr-auth.js';
 import {gerritEventEmitter} from '../../shared/gr-event-emitter/gr-event-emitter.js';
+import {appContext} from '../../../services/app-context.js';
 
 const HIDE_ALERT_TIMEOUT_MS = 5000;
 const CHECK_SIGN_IN_INTERVAL_MS = 60 * 1000;
@@ -47,7 +40,7 @@
 const AUTHENTICATION_REQUIRED = 'Authentication required\n';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrErrorManager extends mixinBehaviors( [
   BaseUrlBehavior,
@@ -98,6 +91,8 @@
 
     /** @type {?Function} */
     this._authErrorHandlerDeregistrationHook;
+
+    this.reporting = appContext.reportingService;
   }
 
   /** @override */
@@ -406,7 +401,7 @@
   }
 
   _showErrorDialog(message, opt_options) {
-    this.$.reporting.reportErrorDialog(message);
+    this.reporting.reportErrorDialog(message);
     this.$.errorDialog.text = message;
     this.$.errorDialog.showSignInButton =
         opt_options && opt_options.showSignInButton;
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.js
index 5661d1e..2fa9b95 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_html.js
@@ -17,11 +17,22 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-overlay with-backdrop="" id="errorOverlay">
-      <gr-error-dialog id="errorDialog" on-dismiss="_handleDismissErrorDialog" confirm-label="Dismiss" confirm-on-enter="" login-url="[[loginUrl]]"></gr-error-dialog>
-    </gr-overlay>
-    <gr-overlay id="noInteractionOverlay" with-backdrop="" always-on-top="" no-cancel-on-esc-key="" no-cancel-on-outside-click="">
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-reporting id="reporting"></gr-reporting>
+  <gr-overlay with-backdrop="" id="errorOverlay">
+    <gr-error-dialog
+      id="errorDialog"
+      on-dismiss="_handleDismissErrorDialog"
+      confirm-label="Dismiss"
+      confirm-on-enter=""
+      login-url="[[loginUrl]]"
+    ></gr-error-dialog>
+  </gr-overlay>
+  <gr-overlay
+    id="noInteractionOverlay"
+    with-backdrop=""
+    always-on-top=""
+    no-cancel-on-esc-key=""
+    no-cancel-on-outside-click=""
+  >
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
index 93f0b4f..3b6d0d3 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-error-manager</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -489,7 +490,7 @@
       const openStub = sandbox.stub(element.$.errorOverlay, 'open');
       const closeStub = sandbox.stub(element.$.errorOverlay, 'close');
       const reportStub = sandbox.stub(
-          element.$.reporting,
+          element.reporting,
           'reportErrorDialog'
       );
 
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 5d7ec27..5c54011 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
@@ -14,15 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-key-binding-display_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrKeyBindingDisplay extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.js b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.js
index f98be3a..334a40a 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.js
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_html.js
@@ -17,24 +17,24 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .key {
-        background-color: var(--chip-background-color);
-        border: 1px solid var(--border-color);
-        border-radius: var(--border-radius);
-        display: inline-block;
-        font-weight: var(--font-weight-bold);
-        padding: var(--spacing-xxs) var(--spacing-m);
-        text-align: center;
-      }
-    </style>
-    <template is="dom-repeat" items="[[binding]]">
-      <template is="dom-if" if="[[index]]">
-        or
-      </template>
-      <template is="dom-repeat" items="[[_computeModifiers(item)]]" as="modifier">
-        <span class="key modifier">[[modifier]]</span>
-      </template>
-      <span class="key">[[_computeKey(item)]]</span>
+  <style include="shared-styles">
+    .key {
+      background-color: var(--chip-background-color);
+      border: 1px solid var(--border-color);
+      border-radius: var(--border-radius);
+      display: inline-block;
+      font-weight: var(--font-weight-bold);
+      padding: var(--spacing-xxs) var(--spacing-m);
+      text-align: center;
+    }
+  </style>
+  <template is="dom-repeat" items="[[binding]]">
+    <template is="dom-if" if="[[index]]">
+      or
     </template>
+    <template is="dom-repeat" items="[[_computeModifiers(item)]]" as="modifier">
+      <span class="key modifier">[[modifier]]</span>
+    </template>
+    <span class="key">[[_computeKey(item)]]</span>
+  </template>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html
index 1bf0b14..8ae0f69 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html
@@ -16,6 +16,7 @@
 limitations under the License.
 -->
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-key-binding-display</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 beb0f7e..0c0daea 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-button/gr-button.js';
 import '../gr-key-binding-display/gr-key-binding-display.js';
 import '../../../styles/shared-styles.js';
@@ -29,7 +27,7 @@
 const {ShortcutSection} = KeyboardShortcutBinder;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrKeyboardShortcutsDialog extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.js b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.js
index 380228f..78b576e 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.js
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.js
@@ -17,53 +17,74 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        max-height: 100vh;
-        overflow-y: auto;
-      }
-      header{
-        padding: var(--spacing-l);
-      }
-      main {
-        display: flex;
-        padding: 0 var(--spacing-xxl) var(--spacing-xxl);
-      }
-      header {
-        align-items: center;
-        border-bottom: 1px solid var(--border-color);
-        display: flex;
-        justify-content: space-between;
-      }
-      table:last-of-type {
-        margin-left: var(--spacing-xxl);
-      }
-      td {
-        padding: var(--spacing-xs) 0;
-      }
-      td:first-child {
-        padding-right: var(--spacing-m);
-        text-align: right;
-      }
-      .header {
-        font-weight: var(--font-weight-bold);
-        padding-top: var(--spacing-l);
-      }
-      .modifier {
-        font-weight: var(--font-weight-normal);
-      }
-    </style>
-    <header>
-      <h3>Keyboard shortcuts</h3>
-      <gr-button link="" on-click="_handleCloseTap">Close</gr-button>
-    </header>
-    <main>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      max-height: 100vh;
+      overflow-y: auto;
+    }
+    header {
+      padding: var(--spacing-l);
+    }
+    main {
+      display: flex;
+      padding: 0 var(--spacing-xxl) var(--spacing-xxl);
+    }
+    header {
+      align-items: center;
+      border-bottom: 1px solid var(--border-color);
+      display: flex;
+      justify-content: space-between;
+    }
+    table:last-of-type {
+      margin-left: var(--spacing-xxl);
+    }
+    td {
+      padding: var(--spacing-xs) 0;
+    }
+    td:first-child {
+      padding-right: var(--spacing-m);
+      text-align: right;
+    }
+    .header {
+      font-weight: var(--font-weight-bold);
+      padding-top: var(--spacing-l);
+    }
+    .modifier {
+      font-weight: var(--font-weight-normal);
+    }
+  </style>
+  <header>
+    <h3>Keyboard shortcuts</h3>
+    <gr-button link="" on-click="_handleCloseTap">Close</gr-button>
+  </header>
+  <main>
+    <table>
+      <tbody>
+        <template is="dom-repeat" items="[[_left]]">
+          <tr>
+            <td></td>
+            <td class="header">[[item.section]]</td>
+          </tr>
+          <template is="dom-repeat" items="[[item.shortcuts]]" as="shortcut">
+            <tr>
+              <td>
+                <gr-key-binding-display binding="[[shortcut.binding]]">
+                </gr-key-binding-display>
+              </td>
+              <td>[[shortcut.text]]</td>
+            </tr>
+          </template>
+        </template>
+      </tbody>
+    </table>
+    <template is="dom-if" if="[[_right]]">
       <table>
         <tbody>
-          <template is="dom-repeat" items="[[_left]]">
+          <template is="dom-repeat" items="[[_right]]">
             <tr>
-              <td></td><td class="header">[[item.section]]</td>
+              <td></td>
+              <td class="header">[[item.section]]</td>
             </tr>
             <template is="dom-repeat" items="[[item.shortcuts]]" as="shortcut">
               <tr>
@@ -77,26 +98,7 @@
           </template>
         </tbody>
       </table>
-      <template is="dom-if" if="[[_right]]">
-        <table>
-          <tbody>
-            <template is="dom-repeat" items="[[_right]]">
-              <tr>
-                <td></td><td class="header">[[item.section]]</td>
-              </tr>
-              <template is="dom-repeat" items="[[item.shortcuts]]" as="shortcut">
-                <tr>
-                  <td>
-                    <gr-key-binding-display binding="[[shortcut.binding]]">
-                    </gr-key-binding-display>
-                  </td>
-                  <td>[[shortcut.text]]</td>
-                </tr>
-              </template>
-            </template>
-          </tbody>
-        </table>
-      </template>
-    </main>
-    <footer></footer>
+    </template>
+  </main>
+  <footer></footer>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html
index e457bfb..1b5cd0f 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html
@@ -16,6 +16,7 @@
 limitations under the License.
 -->
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-key-binding-display</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 3294f5f..684b472 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
 import '../../shared/gr-dropdown/gr-dropdown.js';
 import '../../shared/gr-icons/gr-icons.js';
@@ -86,7 +84,7 @@
 ]);
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrMainHeader extends mixinBehaviors( [
   AdminNavBehavior,
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.js
index 307a081..19e833c 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_html.js
@@ -17,194 +17,217 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    nav {
+      align-items: center;
+      display: flex;
+    }
+    .bigTitle {
+      color: var(--header-text-color);
+      font-size: var(--header-title-font-size);
+      text-decoration: none;
+    }
+    .bigTitle:hover {
+      text-decoration: underline;
+    }
+    .titleText::before {
+      background-image: var(--header-icon);
+      background-size: var(--header-icon-size) var(--header-icon-size);
+      background-repeat: no-repeat;
+      content: '';
+      display: inline-block;
+      height: var(--header-icon-size);
+      margin-right: calc(var(--header-icon-size) / 4);
+      vertical-align: text-bottom;
+      width: var(--header-icon-size);
+    }
+    .titleText::after {
+      content: var(--header-title-content);
+    }
+    ul {
+      list-style: none;
+      padding-left: var(--spacing-l);
+    }
+    .links > li {
+      cursor: default;
+      display: inline-block;
+      padding: 0;
+      position: relative;
+    }
+    .linksTitle {
+      display: inline-block;
+      font-weight: var(--font-weight-bold);
+      position: relative;
+      text-transform: uppercase;
+    }
+    .linksTitle:hover {
+      opacity: 0.75;
+    }
+    .rightItems {
+      align-items: center;
+      display: flex;
+      flex: 1;
+      justify-content: flex-end;
+    }
+    .rightItems gr-endpoint-decorator:not(:empty) {
+      margin-left: var(--spacing-l);
+    }
+    gr-smart-search {
+      flex-grow: 1;
+      margin: 0 var(--spacing-m);
+      max-width: 500px;
+    }
+    gr-dropdown,
+    .browse {
+      padding: var(--spacing-m);
+    }
+    gr-dropdown {
+      --gr-dropdown-item: {
+        color: var(--primary-text-color);
       }
-      nav {
-        align-items: center;
-        display: flex;
-      }
-      .bigTitle {
-        color: var(--header-text-color);
-        font-size: var(--header-title-font-size);
-        text-decoration: none;
-      }
-      .bigTitle:hover {
-        text-decoration: underline;
-      }
-      .titleText::before {
-        background-image: var(--header-icon);
-        background-size: var(--header-icon-size) var(--header-icon-size);
-        background-repeat: no-repeat;
-        content: "";
-        display: inline-block;
-        height: var(--header-icon-size);
-        margin-right: calc(var(--header-icon-size) / 4);
-        vertical-align: text-bottom;
-        width: var(--header-icon-size);
-      }
-      .titleText::after {
-        content: var(--header-title-content);
-      }
-      ul {
-        list-style: none;
-        padding-left: var(--spacing-l);
-      }
-      .links > li {
-        cursor: default;
-        display: inline-block;
-        padding: 0;
-        position: relative;
-      }
-      .linksTitle {
-        display: inline-block;
-        font-weight: var(--font-weight-bold);
-        position: relative;
-        text-transform: uppercase;
-      }
-      .linksTitle:hover {
-        opacity: .75;
-      }
-      .rightItems {
-        align-items: center;
-        display: flex;
-        flex: 1;
-        justify-content: flex-end;
-      }
-      .rightItems gr-endpoint-decorator:not(:empty) {
-        margin-left: var(--spacing-l);
-      }
-      gr-smart-search {
-        flex-grow: 1;
-        margin: 0 var(--spacing-m);
-        max-width: 500px;
-      }
-      gr-dropdown,
-      .browse {
-        padding: var(--spacing-m);
-      }
-      gr-dropdown {
-        --gr-dropdown-item: {
-          color: var(--primary-text-color);
-        }
-      }
-      .settingsButton {
-        margin-left: var(--spacing-m);
-      }
-      .browse {
-        color: var(--header-text-color);
-        /* Same as gr-button */
-        margin: 5px 4px;
-        text-decoration: none;
-      }
-      .invisible,
-      .settingsButton,
-      gr-account-dropdown {
-        display: none;
-      }
-      :host([loading]) .accountContainer,
-      :host([logged-in]) .loginButton,
-      :host([logged-in]) .registerButton {
-        display: none;
-      }
-      :host([logged-in]) .settingsButton,
-      :host([logged-in]) gr-account-dropdown {
-        display: inline;
-      }
-      .accountContainer {
-        align-items: center;
-        display: flex;
-        margin: 0 calc(0 - var(--spacing-m)) 0 var(--spacing-m);
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-      .loginButton, .registerButton {
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      .dropdown-trigger {
-        text-decoration: none;
-      }
-      .dropdown-content {
-        background-color: var(--view-background-color);
-        box-shadow: var(--elevation-level-2);
-      }
-      /*
+    }
+    .settingsButton {
+      margin-left: var(--spacing-m);
+    }
+    .browse {
+      color: var(--header-text-color);
+      /* Same as gr-button */
+      margin: 5px 4px;
+      text-decoration: none;
+    }
+    .invisible,
+    .settingsButton,
+    gr-account-dropdown {
+      display: none;
+    }
+    :host([loading]) .accountContainer,
+    :host([logged-in]) .loginButton,
+    :host([logged-in]) .registerButton {
+      display: none;
+    }
+    :host([logged-in]) .settingsButton,
+    :host([logged-in]) gr-account-dropdown {
+      display: inline;
+    }
+    .accountContainer {
+      align-items: center;
+      display: flex;
+      margin: 0 calc(0 - var(--spacing-m)) 0 var(--spacing-m);
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    .loginButton,
+    .registerButton {
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    .dropdown-trigger {
+      text-decoration: none;
+    }
+    .dropdown-content {
+      background-color: var(--view-background-color);
+      box-shadow: var(--elevation-level-2);
+    }
+    /*
        * We are not using :host to do this, because :host has a lowest css priority
        * compared to others. This means that using :host to do this would break styles.
        */
-      .linksTitle,
-      .bigTitle,
-      .loginButton,
-      .registerButton,
-      iron-icon,
-      gr-account-dropdown {
-        color: var(--header-text-color);
+    .linksTitle,
+    .bigTitle,
+    .loginButton,
+    .registerButton,
+    iron-icon,
+    gr-account-dropdown {
+      color: var(--header-text-color);
+    }
+    #mobileSearch {
+      display: none;
+    }
+    @media screen and (max-width: 50em) {
+      .bigTitle {
+        font-family: var(--header-font-family);
+        font-size: var(--font-size-h3);
+        font-weight: var(--font-weight-h3);
+        line-height: var(--line-height-h3);
       }
-      #mobileSearch {
+      gr-smart-search,
+      .browse,
+      .rightItems .hideOnMobile,
+      .links > li.hideOnMobile {
         display: none;
       }
-      @media screen and (max-width: 50em) {
-        .bigTitle {
-          font-family: var(--header-font-family);
-          font-size: var(--font-size-h3);
-          font-weight: var(--font-weight-h3);
-          line-height: var(--line-height-h3);
-        }
-        gr-smart-search,
-        .browse,
-        .rightItems .hideOnMobile,
-        .links > li.hideOnMobile {
-          display: none;
-        }
-        #mobileSearch {
-          display: inline-flex;
-        }
-        .accountContainer {
-          margin-left: var(--spacing-m) !important;
-        }
-        gr-dropdown {
-          padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-m);
-        }
+      #mobileSearch {
+        display: inline-flex;
       }
-    </style>
-    <nav>
-      <a href\$="[[_computeRelativeURL('/')]]" class="bigTitle">
-        <gr-endpoint-decorator name="header-title">
-          <span class="titleText"></span>
-        </gr-endpoint-decorator>
-      </a>
-      <ul class="links">
-        <template is="dom-repeat" items="[[_links]]" as="linkGroup">
-          <li class\$="[[_computeLinkGroupClass(linkGroup)]]">
-            <gr-dropdown link="" down-arrow="" items="[[linkGroup.links]]" horizontal-align="left">
-              <span class="linksTitle" id="[[linkGroup.title]]">
-                [[linkGroup.title]]
-              </span>
-            </gr-dropdown>
-          </li>
-        </template>
-      </ul>
-      <div class="rightItems">
-        <gr-endpoint-decorator class="hideOnMobile" name="header-small-banner"></gr-endpoint-decorator>
-        <gr-smart-search id="search" search-query="{{searchQuery}}"></gr-smart-search>
-        <gr-endpoint-decorator class="hideOnMobile" name="header-browse-source"></gr-endpoint-decorator>
-        <div class="accountContainer" id="accountContainer">
-          <iron-icon id="mobileSearch" icon="gr-icons:search" on-tap="_onMobileSearchTap"></iron-icon>
-          <div class\$="[[_computeIsInvisible(_registerURL)]]">
-            <a class="registerButton" href\$="[[_registerURL]]">
-              [[_registerText]]
-            </a>
-          </div>
-          <a class="loginButton" href\$="[[loginUrl]]">Sign in</a>
-          <a class="settingsButton" href\$="[[_generateSettingsLink()]]" title="Settings">
-            <iron-icon icon="gr-icons:settings"></iron-icon>
+      .accountContainer {
+        margin-left: var(--spacing-m) !important;
+      }
+      gr-dropdown {
+        padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-m);
+      }
+    }
+  </style>
+  <nav>
+    <a href$="[[_computeRelativeURL('/')]]" class="bigTitle">
+      <gr-endpoint-decorator name="header-title">
+        <span class="titleText"></span>
+      </gr-endpoint-decorator>
+    </a>
+    <ul class="links">
+      <template is="dom-repeat" items="[[_links]]" as="linkGroup">
+        <li class$="[[_computeLinkGroupClass(linkGroup)]]">
+          <gr-dropdown
+            link=""
+            down-arrow=""
+            items="[[linkGroup.links]]"
+            horizontal-align="left"
+          >
+            <span class="linksTitle" id="[[linkGroup.title]]">
+              [[linkGroup.title]]
+            </span>
+          </gr-dropdown>
+        </li>
+      </template>
+    </ul>
+    <div class="rightItems">
+      <gr-endpoint-decorator
+        class="hideOnMobile"
+        name="header-small-banner"
+      ></gr-endpoint-decorator>
+      <gr-smart-search
+        id="search"
+        search-query="{{searchQuery}}"
+      ></gr-smart-search>
+      <gr-endpoint-decorator
+        class="hideOnMobile"
+        name="header-browse-source"
+      ></gr-endpoint-decorator>
+      <div class="accountContainer" id="accountContainer">
+        <iron-icon
+          id="mobileSearch"
+          icon="gr-icons:search"
+          on-tap="_onMobileSearchTap"
+        ></iron-icon>
+        <div class$="[[_computeIsInvisible(_registerURL)]]">
+          <a class="registerButton" href$="[[_registerURL]]">
+            [[_registerText]]
           </a>
-          <gr-account-dropdown account="[[_account]]"></gr-account-dropdown>
         </div>
+        <a class="loginButton" href$="[[loginUrl]]">Sign in</a>
+        <a
+          class="settingsButton"
+          href$="[[_generateSettingsLink()]]"
+          title="Settings"
+        >
+          <iron-icon icon="gr-icons:settings"></iron-icon>
+        </a>
+        <gr-account-dropdown account="[[_account]]"></gr-account-dropdown>
       </div>
-    </nav>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </div>
+  </nav>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
index c641575..336d873 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-main-header</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html
index 150c23b..8fc4c75 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-navigation</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
deleted file mode 100644
index d04d519..0000000
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
+++ /dev/null
@@ -1,436 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2016 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.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-reporting</title>
-
-<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-
-<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
-<script src="/components/wct-browser-legacy/browser.js"></script>
-
-<test-fixture id="basic">
-  <template>
-    <gr-reporting></gr-reporting>
-  </template>
-</test-fixture>
-
-<script type="module">
-import '../../../test/common-test-setup.js';
-import './gr-reporting.js';
-suite('gr-reporting tests', () => {
-  let element;
-  let sandbox;
-  let clock;
-  let fakePerformance;
-
-  const NOW_TIME = 100;
-
-  setup(() => {
-    sandbox = sinon.sandbox.create();
-    clock = sinon.useFakeTimers(NOW_TIME);
-    element = fixture('basic');
-    element._baselines = Object.assign({}, GrReporting.STARTUP_TIMERS);
-    fakePerformance = {
-      navigationStart: 1,
-      loadEventEnd: 2,
-    };
-    fakePerformance.toJSON = () => fakePerformance;
-    sinon.stub(element, 'performanceTiming',
-        {get() { return fakePerformance; }});
-    sandbox.stub(element, 'reporter');
-  });
-
-  teardown(() => {
-    sandbox.restore();
-    clock.restore();
-  });
-
-  test('appStarted', () => {
-    sandbox.stub(element, 'now').returns(42);
-    element.appStarted();
-    assert.isTrue(
-        element.reporter.calledWithMatch(
-            'timing-report', 'UI Latency', 'App Started', 42
-        ));
-    assert.isTrue(
-        element.reporter.calledWithExactly(
-            'timing-report', 'UI Latency', 'NavResTime - loadEventEnd',
-            fakePerformance.loadEventEnd - fakePerformance.navigationStart,
-            undefined, true)
-    );
-  });
-
-  test('WebComponentsReady', () => {
-    sandbox.stub(element, 'now').returns(42);
-    element.timeEnd('WebComponentsReady');
-    assert.isTrue(element.reporter.calledWithMatch(
-        'timing-report', 'UI Latency', 'WebComponentsReady', 42
-    ));
-  });
-
-  test('beforeLocationChanged', () => {
-    element._baselines['garbage'] = 'monster';
-    sandbox.stub(element, 'time');
-    element.beforeLocationChanged();
-    assert.isTrue(element.time.calledWithExactly('DashboardDisplayed'));
-    assert.isTrue(element.time.calledWithExactly('ChangeDisplayed'));
-    assert.isTrue(element.time.calledWithExactly('ChangeFullyLoaded'));
-    assert.isTrue(element.time.calledWithExactly('DiffViewDisplayed'));
-    assert.isTrue(element.time.calledWithExactly('FileListDisplayed'));
-    assert.isFalse(element._baselines.hasOwnProperty('garbage'));
-  });
-
-  test('changeDisplayed', () => {
-    sandbox.spy(element, 'timeEnd');
-    element.changeDisplayed();
-    assert.isFalse(element.timeEnd.calledWith('ChangeDisplayed'));
-    assert.isTrue(element.timeEnd.calledWith('StartupChangeDisplayed'));
-    element.changeDisplayed();
-    assert.isTrue(element.timeEnd.calledWith('ChangeDisplayed'));
-  });
-
-  test('changeFullyLoaded', () => {
-    sandbox.spy(element, 'timeEnd');
-    element.changeFullyLoaded();
-    assert.isFalse(
-        element.timeEnd.calledWithExactly('ChangeFullyLoaded'));
-    assert.isTrue(
-        element.timeEnd.calledWithExactly('StartupChangeFullyLoaded'));
-    element.changeFullyLoaded();
-    assert.isTrue(element.timeEnd.calledWithExactly('ChangeFullyLoaded'));
-  });
-
-  test('diffViewDisplayed', () => {
-    sandbox.spy(element, 'timeEnd');
-    element.diffViewDisplayed();
-    assert.isFalse(element.timeEnd.calledWith('DiffViewDisplayed'));
-    assert.isTrue(element.timeEnd.calledWith('StartupDiffViewDisplayed'));
-    element.diffViewDisplayed();
-    assert.isTrue(element.timeEnd.calledWith('DiffViewDisplayed'));
-  });
-
-  test('fileListDisplayed', () => {
-    sandbox.spy(element, 'timeEnd');
-    element.fileListDisplayed();
-    assert.isFalse(
-        element.timeEnd.calledWithExactly('FileListDisplayed'));
-    assert.isTrue(
-        element.timeEnd.calledWithExactly('StartupFileListDisplayed'));
-    element.fileListDisplayed();
-    assert.isTrue(element.timeEnd.calledWithExactly('FileListDisplayed'));
-  });
-
-  test('dashboardDisplayed', () => {
-    sandbox.spy(element, 'timeEnd');
-    element.dashboardDisplayed();
-    assert.isFalse(element.timeEnd.calledWith('DashboardDisplayed'));
-    assert.isTrue(element.timeEnd.calledWith('StartupDashboardDisplayed'));
-    element.dashboardDisplayed();
-    assert.isTrue(element.timeEnd.calledWith('DashboardDisplayed'));
-  });
-
-  test('dashboardDisplayed details', () => {
-    sandbox.spy(element, 'timeEnd');
-    sandbox.stub(window, 'performance', {
-      memory: {
-        usedJSHeapSize: 1024 * 1024,
-      },
-      measure: () => {},
-    });
-    sandbox.stub(element, 'now').returns(42);
-    element.reportRpcTiming('/changes/*~*/comments', 500);
-    element.dashboardDisplayed();
-    assert.isTrue(
-        element.timeEnd.calledWithExactly('StartupDashboardDisplayed',
-            {rpcList: [
-              {
-                anonymizedUrl: '/changes/*~*/comments',
-                elapsed: 500,
-              },
-            ],
-            screenSize: {
-              width: window.screen.width,
-              height: window.screen.height,
-            },
-            viewport: {
-              width: document.documentElement.clientWidth,
-              height: document.documentElement.clientHeight,
-            },
-            usedJSHeapSizeMb: 1,
-            }
-        ));
-  });
-
-  test('time and timeEnd', () => {
-    const nowStub = sandbox.stub(element, 'now').returns(0);
-    element.time('foo');
-    nowStub.returns(1);
-    element.time('bar');
-    nowStub.returns(2);
-    element.timeEnd('bar');
-    nowStub.returns(3);
-    element.timeEnd('foo');
-    assert.isTrue(element.reporter.calledWithMatch(
-        'timing-report', 'UI Latency', 'foo', 3
-    ));
-    assert.isTrue(element.reporter.calledWithMatch(
-        'timing-report', 'UI Latency', 'bar', 1
-    ));
-  });
-
-  test('timer object', () => {
-    const nowStub = sandbox.stub(element, 'now').returns(100);
-    const timer = element.getTimer('foo-bar');
-    nowStub.returns(150);
-    timer.end();
-    assert.isTrue(element.reporter.calledWithMatch(
-        'timing-report', 'UI Latency', 'foo-bar', 50));
-  });
-
-  test('timer object double call', () => {
-    const timer = element.getTimer('foo-bar');
-    timer.end();
-    assert.isTrue(element.reporter.calledOnce);
-    assert.throws(() => {
-      timer.end();
-    }, 'Timer for "foo-bar" already ended.');
-  });
-
-  test('timer object maximum', () => {
-    const nowStub = sandbox.stub(element, 'now').returns(100);
-    const timer = element.getTimer('foo-bar').withMaximum(100);
-    nowStub.returns(150);
-    timer.end();
-    assert.isTrue(element.reporter.calledOnce);
-
-    timer.reset();
-    nowStub.returns(260);
-    timer.end();
-    assert.isTrue(element.reporter.calledOnce);
-  });
-
-  test('recordDraftInteraction', () => {
-    const key = 'TimeBetweenDraftActions';
-    const nowStub = sandbox.stub(element, 'now').returns(100);
-    const timingStub = sandbox.stub(element, '_reportTiming');
-    element.recordDraftInteraction();
-    assert.isFalse(timingStub.called);
-
-    nowStub.returns(200);
-    element.recordDraftInteraction();
-    assert.isTrue(timingStub.calledOnce);
-    assert.equal(timingStub.lastCall.args[0], key);
-    assert.equal(timingStub.lastCall.args[1], 100);
-
-    nowStub.returns(350);
-    element.recordDraftInteraction();
-    assert.isTrue(timingStub.calledTwice);
-    assert.equal(timingStub.lastCall.args[0], key);
-    assert.equal(timingStub.lastCall.args[1], 150);
-
-    nowStub.returns(370 + 2 * 60 * 1000);
-    element.recordDraftInteraction();
-    assert.isFalse(timingStub.calledThrice);
-  });
-
-  test('timeEndWithAverage', () => {
-    const nowStub = sandbox.stub(element, 'now').returns(0);
-    nowStub.returns(1000);
-    element.time('foo');
-    nowStub.returns(1100);
-    element.timeEndWithAverage('foo', 'bar', 10);
-    assert.isTrue(element.reporter.calledTwice);
-    assert.isTrue(element.reporter.calledWithMatch(
-        'timing-report', 'UI Latency', 'foo', 100));
-    assert.isTrue(element.reporter.calledWithMatch(
-        'timing-report', 'UI Latency', 'bar', 10));
-  });
-
-  test('reportExtension', () => {
-    element.reportExtension('foo');
-    assert.isTrue(element.reporter.calledWithExactly(
-        'lifecycle', 'Extension detected', 'foo'
-    ));
-  });
-
-  test('reportInteraction', () => {
-    element.reporter.restore();
-    sandbox.spy(element, '_reportEvent');
-    element.pluginsLoaded(); // so we don't cache
-    element.reportInteraction('button-click', {name: 'sendReply'});
-    assert.isTrue(element._reportEvent.getCall(2).calledWithMatch(
-        {
-          type: 'interaction',
-          name: 'button-click',
-          eventDetails: JSON.stringify({name: 'sendReply'}),
-        }
-    ));
-  });
-
-  test('report start time', () => {
-    element.reporter.restore();
-    sandbox.stub(element, 'now').returns(42);
-    sandbox.spy(element, '_reportEvent');
-    const dispatchStub = sandbox.spy(document, 'dispatchEvent');
-    element.pluginsLoaded();
-    element.time('timeAction');
-    element.timeEnd('timeAction');
-    assert.isTrue(element._reportEvent.getCall(2).calledWithMatch(
-        {
-          type: 'timing-report',
-          category: 'UI Latency',
-          name: 'timeAction',
-          value: 0,
-          eventStart: 42,
-        }
-    ));
-    assert.equal(dispatchStub.getCall(2).args[0].detail.eventStart, 42);
-  });
-
-  suite('plugins', () => {
-    setup(() => {
-      element.reporter.restore();
-      sandbox.stub(element, '_reportEvent');
-    });
-
-    test('pluginsLoaded reports time', () => {
-      sandbox.stub(element, 'now').returns(42);
-      element.pluginsLoaded();
-      assert.isTrue(element._reportEvent.calledWithMatch(
-          {
-            type: 'timing-report',
-            category: 'UI Latency',
-            name: 'PluginsLoaded',
-            value: 42,
-          }
-      ));
-    });
-
-    test('pluginsLoaded reports plugins', () => {
-      element.pluginsLoaded(['foo', 'bar']);
-      assert.isTrue(element._reportEvent.calledWithMatch(
-          {
-            type: 'lifecycle',
-            category: 'Plugins installed',
-            eventDetails: JSON.stringify({pluginsList: ['foo', 'bar']}),
-          }
-      ));
-    });
-
-    test('caches reports if plugins are not loaded', () => {
-      element.timeEnd('foo');
-      assert.isFalse(element._reportEvent.called);
-    });
-
-    test('reports if plugins are loaded', () => {
-      element.pluginsLoaded();
-      assert.isTrue(element._reportEvent.called);
-    });
-
-    test('reports if metrics plugin xyz is loaded', () => {
-      element.pluginLoaded('metrics-xyz');
-      assert.isTrue(element._reportEvent.called);
-    });
-
-    test('reports cached events preserving order', () => {
-      element.time('foo');
-      element.time('bar');
-      element.timeEnd('foo');
-      element.pluginsLoaded();
-      element.timeEnd('bar');
-      assert.isTrue(element._reportEvent.getCall(0).calledWithMatch(
-          {type: 'timing-report', category: 'UI Latency', name: 'foo'}
-      ));
-      assert.isTrue(element._reportEvent.getCall(1).calledWithMatch(
-          {type: 'timing-report', category: 'UI Latency',
-            name: 'PluginsLoaded'}
-      ));
-      assert.isTrue(element._reportEvent.getCall(2).calledWithMatch(
-          {type: 'lifecycle', category: 'Plugins installed'}
-      ));
-      assert.isTrue(element._reportEvent.getCall(3).calledWithMatch(
-          {type: 'timing-report', category: 'UI Latency', name: 'bar'}
-      ));
-    });
-  });
-
-  test('search', () => {
-    element.locationChanged('_handleSomeRoute');
-    assert.isTrue(element.reporter.calledWithExactly(
-        'nav-report', 'Location Changed', 'Page', '_handleSomeRoute'));
-  });
-
-  suite('exception logging', () => {
-    let fakeWindow;
-    let reporter;
-
-    const emulateThrow = function(msg, url, line, column, error) {
-      return fakeWindow.onerror(msg, url, line, column, error);
-    };
-
-    setup(() => {
-      reporter = sandbox.stub(GrReporting.prototype, 'reporter');
-      fakeWindow = {
-        handlers: {},
-        addEventListener(type, handler) {
-          this.handlers[type] = handler;
-        },
-      };
-      sandbox.stub(console, 'error');
-      window.GrReporting._catchErrors(fakeWindow);
-    });
-
-    test('is reported', () => {
-      const error = new Error('bar');
-      error.stack = undefined;
-      emulateThrow('bar', 'http://url', 4, 2, error);
-      assert.isTrue(reporter.calledWith('error', 'exception', 'bar'));
-      const payload = reporter.lastCall.args[3];
-      assert.deepEqual(payload, {
-        url: 'http://url',
-        line: 4,
-        column: 2,
-        error,
-      });
-    });
-
-    test('is reported with 3 lines of stack', () => {
-      const error = new Error('bar');
-      emulateThrow('bar', 'http://url', 4, 2, error);
-      const expectedStack = error.stack.split('\n').slice(0, 3)
-          .join('\n');
-      assert.isTrue(reporter.calledWith('error', 'exception',
-          expectedStack));
-    });
-
-    test('prevent default event handler', () => {
-      assert.isTrue(emulateThrow());
-    });
-
-    test('unhandled rejection', () => {
-      fakeWindow.handlers['unhandledrejection']({
-        reason: {
-          message: 'bar',
-        },
-      });
-      assert.isTrue(reporter.calledWith('error', 'exception', 'bar'));
-    });
-  });
-});
-</script>
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 11465ba..34f23fe 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import '../gr-reporting/gr-reporting.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -28,6 +25,7 @@
 import {PatchSetBehavior} from '../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.js';
 import {URLEncodingBehavior} from '../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.js';
 import {GerritNav} from '../gr-navigation/gr-navigation.js';
+import {appContext} from '../../../services/app-context.js';
 
 const RoutePattern = {
   ROOT: '/',
@@ -210,15 +208,13 @@
 
 // Setup listeners outside of the router component initialization.
 (function() {
-  const reporting = document.createElement('gr-reporting');
-
   window.addEventListener('WebComponentsReady', () => {
-    reporting.timeEnd('WebComponentsReady');
+    appContext.reportingService.timeEnd('WebComponentsReady');
   });
 })();
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRouter extends mixinBehaviors( [
   BaseUrlBehavior,
@@ -247,6 +243,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   start() {
     if (!this._app) { return; }
     this._startRouter();
@@ -716,7 +717,7 @@
         (ctx, next) => this._loadUserMiddleware(ctx, next),
         (ctx, next) => this._queryStringMiddleware(ctx, next),
         data => {
-          this.$.reporting.locationChanged(handlerName);
+          this.reporting.locationChanged(handlerName);
           const promise = opt_authRedirect ?
             this._redirectIfNotLoggedIn(data) : Promise.resolve();
           promise.then(() => { this[handlerName](data); });
@@ -738,7 +739,7 @@
 
     page.exit('*', (ctx, next) => {
       if (!this._isRedirecting) {
-        this.$.reporting.beforeLocationChanged();
+        this.reporting.beforeLocationChanged();
       }
       this._isRedirecting = false;
       this._isInitialLoad = false;
@@ -1089,7 +1090,7 @@
       project,
       dashboard: decodeURIComponent(data.params[1]),
     });
-    this.$.reporting.setRepoName(project);
+    this.reporting.setRepoName(project);
   }
 
   _handleGroupInfoRoute(data) {
@@ -1170,7 +1171,7 @@
       detail: GerritNav.RepoDetailView.COMMANDS,
       repo,
     });
-    this.$.reporting.setRepoName(repo);
+    this.reporting.setRepoName(repo);
   }
 
   _handleRepoAccessRoute(data) {
@@ -1180,7 +1181,7 @@
       detail: GerritNav.RepoDetailView.ACCESS,
       repo,
     });
-    this.$.reporting.setRepoName(repo);
+    this.reporting.setRepoName(repo);
   }
 
   _handleRepoDashboardsRoute(data) {
@@ -1190,7 +1191,7 @@
       detail: GerritNav.RepoDetailView.DASHBOARDS,
       repo,
     });
-    this.$.reporting.setRepoName(repo);
+    this.reporting.setRepoName(repo);
   }
 
   _handleBranchListOffsetRoute(data) {
@@ -1296,7 +1297,7 @@
       view: GerritNav.View.REPO,
       repo,
     });
-    this.$.reporting.setRepoName(repo);
+    this.reporting.setRepoName(repo);
   }
 
   _handlePluginListOffsetRoute(data) {
@@ -1359,7 +1360,7 @@
       queryMap: ctx.queryMap,
     };
 
-    this.$.reporting.setRepoName(params.project);
+    this.reporting.setRepoName(params.project);
     this._redirectOrNavigate(params);
   }
 
@@ -1379,7 +1380,7 @@
       params.leftSide = address.leftSide;
       params.lineNum = address.lineNum;
     }
-    this.$.reporting.setRepoName(params.project);
+    this.reporting.setRepoName(params.project);
     this._redirectOrNavigate(params);
   }
 
@@ -1430,7 +1431,7 @@
       lineNum: ctx.hash,
       view: GerritNav.View.EDIT,
     });
-    this.$.reporting.setRepoName(project);
+    this.reporting.setRepoName(project);
   }
 
   _handleChangeEditRoute(ctx) {
@@ -1443,7 +1444,7 @@
       view: GerritNav.View.CHANGE,
       edit: true,
     });
-    this.$.reporting.setRepoName(project);
+    this.reporting.setRepoName(project);
   }
 
   /**
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js b/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js
index 01acaa3..8aa0835 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_html.js
@@ -17,6 +17,5 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-reporting id="reporting"></gr-reporting>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
index 9e3a7a8..b5bfd4e 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-router</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 f638f14..02b4efd 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-autocomplete/gr-autocomplete.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import '../../../styles/shared-styles.js';
@@ -114,7 +113,7 @@
 const TOKENIZE_REGEX = /(?:[^\s"]+|"[^"]*")+\s*/g;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrSearchBar extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.js
index 831b080..e26f8a3 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_html.js
@@ -17,19 +17,30 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      form {
-        display: flex;
-      }
-      gr-autocomplete {
-        background-color: var(--view-background-color);
-        border-radius: var(--border-radius);
-        flex: 1;
-        outline: none;
-      }
-    </style>
-    <form>
-      <gr-autocomplete show-search-icon="" id="searchInput" text="{{_inputVal}}" query="[[query]]" on-commit="_handleInputCommit" allow-non-suggested-values="" multi="" threshold="[[_threshold]]" tab-complete="" vertical-offset="30"></gr-autocomplete>
-    </form>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    form {
+      display: flex;
+    }
+    gr-autocomplete {
+      background-color: var(--view-background-color);
+      border-radius: var(--border-radius);
+      flex: 1;
+      outline: none;
+    }
+  </style>
+  <form>
+    <gr-autocomplete
+      show-search-icon=""
+      id="searchInput"
+      text="{{_inputVal}}"
+      query="[[query]]"
+      on-commit="_handleInputCommit"
+      allow-non-suggested-values=""
+      multi=""
+      threshold="[[_threshold]]"
+      tab-complete=""
+      vertical-offset="30"
+    ></gr-autocomplete>
+  </form>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
index a142e56..3b37e09 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-search-bar</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 dcece30..f6221c8 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import '../gr-search-bar/gr-search-bar.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
@@ -31,7 +29,7 @@
 const ME_EXPRESSION = 'me';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrSmartSearch extends mixinBehaviors( [
   DisplayNameBehavior,
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.js
index 78906a8..bb741ce 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.js
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_html.js
@@ -17,9 +17,14 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-
-    </style>
-    <gr-search-bar id="search" value="{{searchQuery}}" on-handle-search="_handleSearch" project-suggestions="[[_projectSuggestions]]" group-suggestions="[[_groupSuggestions]]" account-suggestions="[[_accountSuggestions]]"></gr-search-bar>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles"></style>
+  <gr-search-bar
+    id="search"
+    value="{{searchQuery}}"
+    on-handle-search="_handleSearch"
+    project-suggestions="[[_projectSuggestions]]"
+    group-suggestions="[[_groupSuggestions]]"
+    account-suggestions="[[_accountSuggestions]]"
+  ></gr-search-bar>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
index 7cc75bb..87dfaf4 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-smart-search</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/custom-dark-theme_test.html b/polygerrit-ui/app/elements/custom-dark-theme_test.html
index c04e4d0..6ac9c20 100644
--- a/polygerrit-ui/app/elements/custom-dark-theme_test.html
+++ b/polygerrit-ui/app/elements/custom-dark-theme_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-app-it_test</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/custom-light-theme_test.html b/polygerrit-ui/app/elements/custom-light-theme_test.html
index fbd3825..f8a749c 100644
--- a/polygerrit-ui/app/elements/custom-light-theme_test.html
+++ b/polygerrit-ui/app/elements/custom-light-theme_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-app-it_test</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
index e2dda2f..b63b87a 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-icon/iron-icon.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-dialog/gr-dialog.js';
@@ -29,7 +27,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrApplyFixDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.js
index f182979..a5a6ff2 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.js
@@ -17,61 +17,83 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      gr-diff {
-        --content-width: 90vw;
-      }
-      .diffContainer {
-        padding: var(--spacing-l) 0;
-        border-bottom: 1px solid var(--border-color);
-      }
-      .file-name {
-        display: block;
-        padding: var(--spacing-s) var(--spacing-l);
-        background-color: var(--background-color-secondary);
-        border-bottom: 1px solid var(--border-color);
-      }
-      .fixActions {
-        display: flex;
-        justify-content: flex-end;
-      }
-      gr-button {
-        margin-left: var(--spacing-m);
-      }
-      .fix-picker {
-        display: flex;
-        align-items: center;
-        margin-right: var(--spacing-l);
-      }
-    </style>
-    <gr-overlay id="applyFixOverlay" with-backdrop="">
-      <gr-dialog id="applyFixDialog"
-        on-confirm="_handleApplyFix"
-        confirm-label="[[_getApplyFixButtonLabel(_isApplyFixLoading)]]"
-        disabled="[[_disableApplyFixButton]]"
-        confirm-tooltip="[[_computeTooltip(change, _patchNum)]]"
-        on-cancel="onCancel">
-        <div slot="header">[[_robotId]] - [[getFixDescription(_currentFix)]]</div>
-        <div slot="main">
-          <template is="dom-repeat" items="[[_currentPreviews]]">
-            <div class="file-name">
-              <span>[[item.filepath]]</span>
-            </div>
-            <div class="diffContainer">
-              <gr-diff prefs="[[overridePartialPrefs(prefs)]]" change-num="[[changeNum]]" path="[[item.filepath]]" diff="[[item.preview]]"></gr-diff>
-            </div>
-          </template>
-        </div>
-        <div slot="footer" class="fix-picker" hidden\$="[[hasSingleFix(_fixSuggestions)]]">
-          <span>Suggested fix [[addOneTo(_selectedFixIdx)]] of [[_fixSuggestions.length]]</span>
-          <gr-button id="prevFix" on-click="_onPrevFixClick" disabled\$="[[_noPrevFix(_selectedFixIdx)]]">
-            <iron-icon icon="gr-icons:chevron-left"></iron-icon>
-          </gr-button>
-          <gr-button id="nextFix" on-click="_onNextFixClick" disabled\$="[[_noNextFix(_selectedFixIdx, _fixSuggestions)]]">
-            <iron-icon icon="gr-icons:chevron-right"></iron-icon>
-          </gr-button>
-        </div>
-      </gr-dialog>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    gr-diff {
+      --content-width: 90vw;
+    }
+    .diffContainer {
+      padding: var(--spacing-l) 0;
+      border-bottom: 1px solid var(--border-color);
+    }
+    .file-name {
+      display: block;
+      padding: var(--spacing-s) var(--spacing-l);
+      background-color: var(--background-color-secondary);
+      border-bottom: 1px solid var(--border-color);
+    }
+    .fixActions {
+      display: flex;
+      justify-content: flex-end;
+    }
+    gr-button {
+      margin-left: var(--spacing-m);
+    }
+    .fix-picker {
+      display: flex;
+      align-items: center;
+      margin-right: var(--spacing-l);
+    }
+  </style>
+  <gr-overlay id="applyFixOverlay" with-backdrop="">
+    <gr-dialog
+      id="applyFixDialog"
+      on-confirm="_handleApplyFix"
+      confirm-label="[[_getApplyFixButtonLabel(_isApplyFixLoading)]]"
+      disabled="[[_disableApplyFixButton]]"
+      confirm-tooltip="[[_computeTooltip(change, _patchNum)]]"
+      on-cancel="onCancel"
+    >
+      <div slot="header">[[_robotId]] - [[getFixDescription(_currentFix)]]</div>
+      <div slot="main">
+        <template is="dom-repeat" items="[[_currentPreviews]]">
+          <div class="file-name">
+            <span>[[item.filepath]]</span>
+          </div>
+          <div class="diffContainer">
+            <gr-diff
+              prefs="[[overridePartialPrefs(prefs)]]"
+              change-num="[[changeNum]]"
+              path="[[item.filepath]]"
+              diff="[[item.preview]]"
+            ></gr-diff>
+          </div>
+        </template>
+      </div>
+      <div
+        slot="footer"
+        class="fix-picker"
+        hidden$="[[hasSingleFix(_fixSuggestions)]]"
+      >
+        <span
+          >Suggested fix [[addOneTo(_selectedFixIdx)]] of
+          [[_fixSuggestions.length]]</span
+        >
+        <gr-button
+          id="prevFix"
+          on-click="_onPrevFixClick"
+          disabled$="[[_noPrevFix(_selectedFixIdx)]]"
+        >
+          <iron-icon icon="gr-icons:chevron-left"></iron-icon>
+        </gr-button>
+        <gr-button
+          id="nextFix"
+          on-click="_onNextFixClick"
+          disabled$="[[_noNextFix(_selectedFixIdx, _fixSuggestions)]]"
+        >
+          <iron-icon icon="gr-icons:chevron-right"></iron-icon>
+        </gr-button>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
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 3f7da5a..95531364 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -47,12 +45,33 @@
     this._getParentIndex =
       PatchSetBehavior.getParentIndex;
 
-    this._comments = comments || {};
-    this._robotComments = robotComments || {};
-    this._drafts = drafts || {};
+    this._comments = this._addPath(comments);
+    this._robotComments = this._addPath(robotComments);
+    this._drafts = this._addPath(drafts);
     this._changeNum = changeNum;
   }
 
+  /**
+   * Add path info to every comment as CommentInfo returned
+   * from server does not have that.
+   *
+   * TODO(taoalpha): should consider changing BE to send path
+   * back within CommentInfo
+   *
+   * @param {Object} - map between file path and comments
+   */
+  _addPath(comments = {}) {
+    const updatedComments = {};
+    for (const filePath of Object.keys(comments)) {
+      const allCommentsForPath = comments[filePath] || [];
+      if (allCommentsForPath.length) {
+        updatedComments[filePath] = allCommentsForPath
+            .map(comment => { return {...comment, path: filePath}; });
+      }
+    }
+    return updatedComments;
+  }
+
   get comments() {
     return this._comments;
   }
@@ -550,7 +569,7 @@
 }
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCommentApi extends mixinBehaviors( [
   PatchSetBehavior,
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.js
index 215bfac..8aa0835 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_html.js
@@ -17,5 +17,5 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html
index 0f6ed34..29262e3 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-comment-api</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js
index cdd6d8f..86537e8 100644
--- a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
@@ -29,7 +27,7 @@
   [CoverageType.NOT_INSTRUMENTED, 'Not instrumented by any tests.'],
 ]);
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrCoverageLayer extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.js b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.js
index 29757e5..3ed33d1 100644
--- a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_html.js
@@ -16,6 +16,4 @@
  */
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
-export const htmlTemplate = html`
-
-`;
+export const htmlTemplate = html``;
diff --git a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html
index cec75fd..b80c56f3 100644
--- a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-coverage-layer</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
index b38543c..1c4edec 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-coverage-layer/gr-coverage-layer.js';
 import '../gr-diff-processor/gr-diff-processor.js';
 import '../../shared/gr-hovercard/gr-hovercard.js';
@@ -46,7 +44,7 @@
 const COMMIT_MSG_LINE_LENGTH = 72;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiffBuilderElement extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.js
index c8df78f..4d6b890 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_html.js
@@ -17,11 +17,22 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <div class="contentWrapper">
-      <slot></slot>
-    </div>
-    <gr-ranged-comment-layer id="rangeLayer" comment-ranges="[[commentRanges]]"></gr-ranged-comment-layer>
-    <gr-coverage-layer id="coverageLayerLeft" coverage-ranges="[[_leftCoverageRanges]]" side="left"></gr-coverage-layer>
-    <gr-coverage-layer id="coverageLayerRight" coverage-ranges="[[_rightCoverageRanges]]" side="right"></gr-coverage-layer>
-    <gr-diff-processor id="processor" groups="{{_groups}}"></gr-diff-processor>
+  <div class="contentWrapper">
+    <slot></slot>
+  </div>
+  <gr-ranged-comment-layer
+    id="rangeLayer"
+    comment-ranges="[[commentRanges]]"
+  ></gr-ranged-comment-layer>
+  <gr-coverage-layer
+    id="coverageLayerLeft"
+    coverage-ranges="[[_leftCoverageRanges]]"
+    side="left"
+  ></gr-coverage-layer>
+  <gr-coverage-layer
+    id="coverageLayerRight"
+    coverage-ranges="[[_rightCoverageRanges]]"
+    side="right"
+  ></gr-coverage-layer>
+  <gr-diff-processor id="processor" groups="{{_groups}}"></gr-diff-processor>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.html
index 0edf9b0..7a448d68 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-builder</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -51,7 +52,7 @@
 import '../gr-diff/gr-diff-group.js';
 import './gr-diff-builder.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import {getMockDiffResponse} from '../../../test/mock-diff-response.js';
+import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
 import './gr-diff-builder-element.js';
 import {dom, flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {GrAnnotation} from '../gr-diff-highlight/gr-annotation.js';
@@ -95,44 +96,70 @@
     assert.isTrue(node.classList.contains('classes'));
   });
 
-  test('context control buttons', () => {
-    // Create 10 lines.
-    const lines = [];
-    for (let i = 0; i < 10; i++) {
-      const line = new GrDiffLine(GrDiffLine.Type.BOTH);
-      line.beforeNumber = i + 1;
-      line.afterNumber = i + 1;
-      line.text = 'lorem upsum';
-      lines.push(line);
+  suite('context control', () => {
+    function createContextLine(options) {
+      const offset = options.offset || 0;
+      const numLines = options.count || 10;
+      const lines = [];
+      for (let i = 0; i < numLines; i++) {
+        const line = new GrDiffLine(GrDiffLine.Type.BOTH);
+        line.beforeNumber = offset + i + 1;
+        line.afterNumber = offset + i + 1;
+        line.text = 'lorem upsum';
+        lines.push(line);
+      }
+
+      return {
+        contextGroups: [new GrDiffGroup(GrDiffGroup.Type.BOTH, lines)],
+      };
     }
 
-    const contextLine = {
-      contextGroups: [new GrDiffGroup(GrDiffGroup.Type.BOTH, lines)],
-    };
+    test('no +10 buttons for 10 or less lines', () => {
+      const contextLine = createContextLine({count: 10});
 
-    const section = {};
-    // Does not include +10 buttons when there are fewer than 11 lines.
-    let td = builder._createContextControl(section, contextLine);
-    let buttons = td.querySelectorAll('gr-button.showContext');
+      const td = builder._createContextControl({}, contextLine);
+      const buttons = td.querySelectorAll('gr-button.showContext');
 
-    assert.equal(buttons.length, 1);
-    assert.equal(dom(buttons[0]).textContent, 'Show 10 common lines');
+      assert.equal(buttons.length, 1);
+      assert.equal(dom(buttons[0]).textContent, 'Show 10 common lines');
+    });
 
-    // Add another line.
-    const line = new GrDiffLine(GrDiffLine.Type.BOTH);
-    line.text = 'lorem upsum';
-    line.beforeNumber = 11;
-    line.afterNumber = 11;
-    contextLine.contextGroups[0].addLine(line);
+    test('context control at the top', () => {
+      const contextLine = createContextLine({offset: 0, count: 20});
 
-    // Includes +10 buttons when there are at least 11 lines.
-    td = builder._createContextControl(section, contextLine);
-    buttons = td.querySelectorAll('gr-button.showContext');
+      builder._numLinesLeft = 50;
+      const td = builder._createContextControl({}, contextLine);
+      const buttons = td.querySelectorAll('gr-button.showContext');
 
-    assert.equal(buttons.length, 3);
-    assert.equal(dom(buttons[0]).textContent, '+10 above');
-    assert.equal(dom(buttons[1]).textContent, 'Show 11 common lines');
-    assert.equal(dom(buttons[2]).textContent, '+10 below');
+      assert.equal(buttons.length, 2);
+      assert.equal(dom(buttons[0]).textContent, 'Show 20 common lines');
+      assert.equal(dom(buttons[1]).textContent, '+10 below');
+    });
+
+    test('context control in the middle', () => {
+      const contextLine = createContextLine({offset: 10, count: 20});
+
+      builder._numLinesLeft = 50;
+      const td = builder._createContextControl({}, contextLine);
+      const buttons = td.querySelectorAll('gr-button.showContext');
+
+      assert.equal(buttons.length, 3);
+      assert.equal(dom(buttons[0]).textContent, '+10 above');
+      assert.equal(dom(buttons[1]).textContent, 'Show 20 common lines');
+      assert.equal(dom(buttons[2]).textContent, '+10 below');
+    });
+
+    test('context control at the top', () => {
+      const contextLine = createContextLine({offset: 30, count: 20});
+
+      builder._numLinesLeft = 50;
+      const td = builder._createContextControl({}, contextLine);
+      const buttons = td.querySelectorAll('gr-button.showContext');
+
+      assert.equal(buttons.length, 2);
+      assert.equal(dom(buttons[0]).textContent, '+10 above');
+      assert.equal(dom(buttons[1]).textContent, 'Show 20 common lines');
+    });
   });
 
   test('newlines 1', () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
index 1fc0d4f..6983af0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
@@ -16,6 +16,7 @@
  */
 
 import {GrDiffBuilderSideBySide} from './gr-diff-builder-side-by-side.js';
+import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 
 // MIME types for images we allow showing. Do not include SVG, it can contain
 // arbitrary JavaScript.
@@ -52,7 +53,7 @@
   // column limit.
   td.setAttribute('colspan', '4');
   const endpoint = this._createElement('gr-endpoint-decorator');
-  const endpointDomApi = Polymer.dom(endpoint);
+  const endpointDomApi = dom(endpoint);
   endpointDomApi.setAttribute('name', 'image-diff');
   endpointDomApi.appendChild(
       this._createEndpointParam('baseImage', this._baseImage));
@@ -106,7 +107,7 @@
 
 GrDiffBuilderImage.prototype._updateImageLabel = function(section, className,
     image) {
-  const label = Polymer.dom(section)
+  const label = dom(section)
       .querySelector('.' + className + ' span.label');
   this._setLabelText(label, image);
 };
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html
index 8528be3..2d26667 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>GrDiffBuilderUnified</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
index 4b91000..74a4591 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
@@ -16,6 +16,7 @@
  */
 import {GrDiffLine} from '../gr-diff/gr-diff-line.js';
 import {GrDiffGroup} from '../gr-diff/gr-diff-group.js';
+import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 
 /**
  * In JS, unicode code points above 0xFFFF occupy two elements of a string.
@@ -41,6 +42,11 @@
 
 export function GrDiffBuilder(diff, prefs, outputEl, layers) {
   this._diff = diff;
+  this._numLinesLeft = this._diff.content ? this._diff.content.reduce(
+      (sum, chunk) => {
+        const left = chunk.a || chunk.ab;
+        return sum + (left ? left.length : 0);
+      }, 0) : 0;
   this._prefs = prefs;
   this._outputEl = outputEl;
   this.groups = [];
@@ -142,7 +148,7 @@
 
 GrDiffBuilder.prototype.getContentByLine = function(lineNumber, opt_side,
     opt_root) {
-  const root = Polymer.dom(opt_root || this._outputEl);
+  const root = dom(opt_root || this._outputEl);
   const sideSelector = opt_side ? ('.' + opt_side) : '';
   return root.querySelector('td.lineNum[data-value="' + lineNumber +
       '"]' + sideSelector + ' ~ td.content .contentText');
@@ -221,16 +227,18 @@
 GrDiffBuilder.prototype._createContextControl = function(section, line) {
   if (!line.contextGroups) return null;
 
-  const numLines =
-      line.contextGroups[line.contextGroups.length - 1].lineRange.left.end -
-      line.contextGroups[0].lineRange.left.start + 1;
+  const leftStart = line.contextGroups[0].lineRange.left.start;
+  const leftEnd =
+      line.contextGroups[line.contextGroups.length - 1].lineRange.left.end;
+
+  const numLines = leftEnd - leftStart + 1;
 
   if (numLines === 0) return null;
 
   const td = this._createElement('td');
   const showPartialLinks = numLines > PARTIAL_CONTEXT_AMOUNT;
 
-  if (showPartialLinks) {
+  if (showPartialLinks && leftStart > 1) {
     td.appendChild(this._createContextButton(
         GrDiffBuilder.ContextButtonType.ABOVE, section, line, numLines));
   }
@@ -238,7 +246,7 @@
   td.appendChild(this._createContextButton(
       GrDiffBuilder.ContextButtonType.ALL, section, line, numLines));
 
-  if (showPartialLinks) {
+  if (showPartialLinks && leftEnd < this._numLinesLeft) {
     td.appendChild(this._createContextButton(
         GrDiffBuilder.ContextButtonType.BELOW, section, line, numLines));
   }
@@ -259,7 +267,7 @@
   if (type === GrDiffBuilder.ContextButtonType.ALL) {
     const icon = this._createElement('iron-icon', 'showContext');
     icon.setAttribute('icon', 'gr-icons:unfold-more');
-    Polymer.dom(button).appendChild(icon);
+    dom(button).appendChild(icon);
 
     text = 'Show ' + numLines + ' common line';
     if (numLines > 1) { text += 's'; }
@@ -274,8 +282,8 @@
         0, numLines - context);
   }
   const textSpan = this._createElement('span', 'showContext');
-  Polymer.dom(textSpan).textContent = text;
-  Polymer.dom(button).appendChild(textSpan);
+  dom(textSpan).textContent = text;
+  dom(button).appendChild(textSpan);
 
   button.addEventListener('tap', e => {
     e.detail = {
@@ -533,7 +541,7 @@
  * @return {HTMLTableDataCellElement}
  */
 GrDiffBuilder.prototype._getBlameByLineNum = function(lineNum) {
-  const root = Polymer.dom(this._outputEl);
+  const root = dom(this._outputEl);
   return root.querySelector(`td.blame[data-line-number="${lineNum}"]`);
 };
 
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 0b9ae5b..444fad4 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../shared/gr-cursor-manager/gr-cursor-manager.js';
 import {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js';
@@ -42,7 +41,7 @@
 const LEFT_SIDE_CLASS = 'target-side-left';
 const RIGHT_SIDE_CLASS = 'target-side-right';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrDiffCursor extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
   static get template() { return htmlTemplate; }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.js
index 81e0c9b..1ac47f6 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_html.js
@@ -17,5 +17,12 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-cursor-manager id="cursorManager" scroll-behavior="[[_scrollBehavior]]" cursor-target-class="target-row" focus-on-move="[[_focusOnMove]]" target="{{diffRow}}" scroll-top-margin="[[scrollTopMargin]]"></gr-cursor-manager>
+  <gr-cursor-manager
+    id="cursorManager"
+    scroll-behavior="[[_scrollBehavior]]"
+    cursor-target-class="target-row"
+    focus-on-move="[[_focusOnMove]]"
+    target="{{diffRow}}"
+    scroll-top-margin="[[scrollTopMargin]]"
+  ></gr-cursor-manager>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
index 4577696..68a2a77 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-cursor</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -43,7 +44,7 @@
 import '../gr-diff/gr-diff.js';
 import './gr-diff-cursor.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import {getMockDiffResponse} from '../../../test/mock-diff-response.js';
+import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 suite('gr-diff-cursor tests', () => {
   let sandbox;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.js
index 4006d13..c86760a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation.js
@@ -14,6 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
+import {sanitizeDOMValue} from '@polymer/polymer/lib/utils/settings.js';
 
 // TODO(wyatta): refactor this to be <MARK> rather than <HL>.
 const ANNOTATION_TAG = 'HL';
@@ -84,7 +86,7 @@
     }
 
     const wrapper = document.createElement(tagName);
-    const sanitizer = window.Polymer.sanitizeDOMValue;
+    const sanitizer = sanitizeDOMValue;
     for (const [name, value] of Object.entries(attributes)) {
       wrapper.setAttribute(
           name, sanitizer ?
@@ -149,8 +151,8 @@
     } else {
       hl = document.createElement(ANNOTATION_TAG);
       hl.className = cssClass;
-      Polymer.dom(node.parentElement).replaceChild(hl, node);
-      Polymer.dom(hl).appendChild(node);
+      dom(node.parentElement).replaceChild(hl, node);
+      dom(hl).appendChild(node);
     }
     return hl;
   },
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
index a519cab..2bda950 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-annotation</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 53b416b..083a97d 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../gr-selection-action-box/gr-selection-action-box.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
@@ -27,7 +25,7 @@
 import {GrRangeNormalizer} from './gr-range-normalizer.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiffHighlight extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.js
index 10b4f2d..08b21499 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_html.js
@@ -17,19 +17,19 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        position: relative;
-      }
-      gr-selection-action-box {
-        /**
+  <style include="shared-styles">
+    :host {
+      position: relative;
+    }
+    gr-selection-action-box {
+      /**
          * Needs z-index to apear above wrapped content, since it's inseted
          * into DOM before it.
          */
-        z-index: 10;
-      }
-    </style>
-    <div class="contentWrapper">
-      <slot></slot>
-    </div>
+      z-index: 10;
+    }
+  </style>
+  <div class="contentWrapper">
+    <slot></slot>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
index fe8d382..86f1505 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-highlight</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 2ed69b6..cb215c9 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
@@ -14,9 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import '../../shared/gr-comment-thread/gr-comment-thread.js';
 import '../../shared/gr-js-api-interface/gr-js-api-interface.js';
@@ -33,6 +30,7 @@
 import {util} from '../../../scripts/util.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 import {DiffSide, rangesEqual} from '../gr-diff/gr-diff-utils.js';
+import {appContext} from '../../../services/app-context.js';
 
 const MSG_EMPTY_BLAME = 'No blame information for this diff.';
 
@@ -85,7 +83,7 @@
  * Webcomponent fetching diffs and related data from restAPI and passing them
  * to the presentational gr-diff for rendering.
  *
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiffHost extends mixinBehaviors( [
   PatchSetBehavior,
@@ -259,6 +257,11 @@
     ];
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   created() {
     super.created();
@@ -360,21 +363,21 @@
               const needsSyntaxHighlighting = event.detail &&
                     event.detail.contentRendered;
               if (needsSyntaxHighlighting) {
-                this.$.reporting.time(TimingLabel.SYNTAX);
+                this.reporting.time(TimingLabel.SYNTAX);
                 this.$.syntaxLayer.process().then(() => {
-                  this.$.reporting.timeEnd(TimingLabel.SYNTAX);
-                  this.$.reporting.timeEnd(TimingLabel.TOTAL);
+                  this.reporting.timeEnd(TimingLabel.SYNTAX);
+                  this.reporting.timeEnd(TimingLabel.TOTAL);
                   resolve();
                 });
               } else {
-                this.$.reporting.timeEnd(TimingLabel.TOTAL);
+                this.reporting.timeEnd(TimingLabel.TOTAL);
                 resolve();
               }
               this.removeEventListener('render', callback);
               if (shouldReportMetric) {
                 // We report diffViewContentDisplayed only on reload caused
                 // by params changed - expected only on Diff Page.
-                this.$.reporting.diffViewContentDisplayed();
+                this.reporting.diffViewContentDisplayed();
               }
             };
             this.addEventListener('render', callback);
@@ -602,11 +605,11 @@
     // Report the due_to_rebase percentage in the "diff" category when
     // applicable.
     if (this.patchRange.basePatchNum === 'PARENT') {
-      this.$.reporting.reportInteraction(EVENT_AGAINST_PARENT);
+      this.reporting.reportInteraction(EVENT_AGAINST_PARENT);
     } else if (percentRebaseDelta === 0) {
-      this.$.reporting.reportInteraction(EVENT_ZERO_REBASE);
+      this.reporting.reportInteraction(EVENT_ZERO_REBASE);
     } else {
-      this.$.reporting.reportInteraction(EVENT_NONZERO_REBASE,
+      this.reporting.reportInteraction(EVENT_NONZERO_REBASE,
           {percentRebaseDelta});
     }
   }
@@ -726,7 +729,7 @@
         isOnParent);
     threadEl.addOrEditDraft(lineNum, range);
 
-    this.$.reporting.recordDraftInteraction();
+    this.reporting.recordDraftInteraction();
   }
 
   /**
@@ -1024,7 +1027,7 @@
   _listenToViewportRender() {
     const renderUpdateListener = start => {
       if (start > NUM_OF_LINES_THRESHOLD_FOR_VIEWPORT) {
-        this.$.reporting.diffViewDisplayed();
+        this.reporting.diffViewDisplayed();
         this.$.syntaxLayer.removeListener(renderUpdateListener);
       }
     };
@@ -1033,16 +1036,16 @@
   }
 
   _handleRenderStart() {
-    this.$.reporting.time(TimingLabel.TOTAL);
-    this.$.reporting.time(TimingLabel.CONTENT);
+    this.reporting.time(TimingLabel.TOTAL);
+    this.reporting.time(TimingLabel.CONTENT);
   }
 
   _handleRenderContent() {
-    this.$.reporting.timeEnd(TimingLabel.CONTENT);
+    this.reporting.timeEnd(TimingLabel.CONTENT);
   }
 
   _handleNormalizeRange(event) {
-    this.$.reporting.reportInteraction('normalize-range',
+    this.reporting.reportInteraction('normalize-range',
         {
           side: event.detail.side,
           lineNum: event.detail.lineNum,
@@ -1050,7 +1053,7 @@
   }
 
   _handleDiffContextExpanded(event) {
-    this.$.reporting.reportInteraction(
+    this.reporting.reportInteraction(
         'diff-context-expanded', {numLines: event.detail.numLines}
     );
   }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.js
index d48531b..aa05bb5 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.js
@@ -17,10 +17,40 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-diff id="diff" change-num="[[changeNum]]" no-auto-render="[[noAutoRender]]" patch-range="[[patchRange]]" path="[[path]]" prefs="[[prefs]]" project-name="[[projectName]]" display-line="[[displayLine]]" is-image-diff="[[isImageDiff]]" commit-range="[[commitRange]]" hidden\$="[[hidden]]" no-render-on-prefs-change="[[noRenderOnPrefsChange]]" line-wrapping="[[lineWrapping]]" view-mode="[[viewMode]]" line-of-interest="[[lineOfInterest]]" logged-in="[[_loggedIn]]" loading="[[_loading]]" error-message="[[_errorMessage]]" base-image="[[_baseImage]]" revision-image="[[_revisionImage]]" coverage-ranges="[[_coverageRanges]]" blame="[[_blame]]" layers="[[_layers]]" diff="[[diff]]" show-newline-warning-left="[[_showNewlineWarningLeft(diff)]]" show-newline-warning-right="[[_showNewlineWarningRight(diff)]]">
-    </gr-diff>
-    <gr-syntax-layer id="syntaxLayer" enabled="[[_syntaxHighlightingEnabled]]" diff="[[diff]]"></gr-syntax-layer>
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-reporting id="reporting" category="diff"></gr-reporting>
+  <gr-diff
+    id="diff"
+    change-num="[[changeNum]]"
+    no-auto-render="[[noAutoRender]]"
+    patch-range="[[patchRange]]"
+    path="[[path]]"
+    prefs="[[prefs]]"
+    project-name="[[projectName]]"
+    display-line="[[displayLine]]"
+    is-image-diff="[[isImageDiff]]"
+    commit-range="[[commitRange]]"
+    hidden$="[[hidden]]"
+    no-render-on-prefs-change="[[noRenderOnPrefsChange]]"
+    line-wrapping="[[lineWrapping]]"
+    view-mode="[[viewMode]]"
+    line-of-interest="[[lineOfInterest]]"
+    logged-in="[[_loggedIn]]"
+    loading="[[_loading]]"
+    error-message="[[_errorMessage]]"
+    base-image="[[_baseImage]]"
+    revision-image="[[_revisionImage]]"
+    coverage-ranges="[[_coverageRanges]]"
+    blame="[[_blame]]"
+    layers="[[_layers]]"
+    diff="[[diff]]"
+    show-newline-warning-left="[[_showNewlineWarningLeft(diff)]]"
+    show-newline-warning-right="[[_showNewlineWarningRight(diff)]]"
+  >
+  </gr-diff>
+  <gr-syntax-layer
+    id="syntaxLayer"
+    enabled="[[_syntaxHighlightingEnabled]]"
+    diff="[[diff]]"
+  ></gr-syntax-layer>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
index 86a8792..25ccf59 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -49,11 +50,9 @@
     stub('gr-rest-api-interface', {
       async getLoggedIn() { return getLoggedIn; },
     });
-    stub('gr-reporting', {
-      time: sandbox.stub(),
-      timeEnd: sandbox.stub(),
-    });
     element = fixture('basic');
+    sandbox.stub(element.reporting, 'time');
+    sandbox.stub(element.reporting, 'timeEnd');
   });
 
   teardown(() => {
@@ -306,9 +305,9 @@
     test('starts total and content timer on render-start', done => {
       element.dispatchEvent(
           new CustomEvent('render-start', {bubbles: true, composed: true}));
-      assert.isTrue(element.$.reporting.time.calledWithExactly(
+      assert.isTrue(element.reporting.time.calledWithExactly(
           'Diff Total Render'));
-      assert.isTrue(element.$.reporting.time.calledWithExactly(
+      assert.isTrue(element.reporting.time.calledWithExactly(
           'Diff Content Render'));
       done();
     });
@@ -316,11 +315,12 @@
     test('ends content timer on render-content', () => {
       element.dispatchEvent(
           new CustomEvent('render-content', {bubbles: true, composed: true}));
-      assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+      assert.isTrue(element.reporting.timeEnd.calledWithExactly(
           'Diff Content Render'));
     });
 
     test('ends total and syntax timer after syntax layer processing', done => {
+      sandbox.stub(element.reporting, 'diffViewContentDisplayed');
       let notifySyntaxProcessed;
       sandbox.stub(element.$.syntaxLayer, 'process').returns(new Promise(
           resolve => {
@@ -338,12 +338,11 @@
         notifySyntaxProcessed();
         // Assert after the notification task is processed.
         Promise.resolve().then(() => {
-          assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+          assert.isTrue(element.reporting.timeEnd.calledWithExactly(
               'Diff Total Render'));
-          assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+          assert.isTrue(element.reporting.timeEnd.calledWithExactly(
               'Diff Syntax Render'));
-          assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
-              'StartupDiffViewOnlyContent'));
+          assert.isTrue(element.reporting.diffViewContentDisplayed.called);
           done();
         });
       });
@@ -356,8 +355,8 @@
       element.reload();
       // Multiple cascading microtasks are scheduled.
       setTimeout(() => {
-        assert.isTrue(element.$.reporting.timeEnd.calledOnce);
-        assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+        assert.isTrue(element.reporting.timeEnd.calledOnce);
+        assert.isTrue(element.reporting.timeEnd.calledWithExactly(
             'Diff Total Render'));
         done();
       });
@@ -1026,7 +1025,7 @@
     setup(() => {
       element = fixture('basic');
       element.patchRange = {basePatchNum: 1};
-      reportStub = sandbox.stub(element.$.reporting, 'reportInteraction');
+      reportStub = sandbox.stub(element.reporting, 'reportInteraction');
     });
 
     test('null and content-less', () => {
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 acd9457..7c9d1ec 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-icon/iron-icon.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
@@ -25,7 +23,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-diff-mode-selector_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrDiffModeSelector extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.js b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.js
index 5fe516c..b5393ea 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.js
@@ -17,24 +17,38 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        /* Used to remove horizontal whitespace between the icons. */
-        display: flex;
-      }
-      gr-button.selected iron-icon {
-        color: var(--link-color);
-      }
-      iron-icon {
-        height: 1.3rem;
-        width: 1.3rem;
-      }
-    </style>
-    <gr-button id="sideBySideBtn" link="" has-tooltip="" class\$="[[_computeSelectedClass(mode, _VIEW_MODES.SIDE_BY_SIDE)]]" title="Side-by-side diff" on-click="_handleSideBySideTap">
-      <iron-icon icon="gr-icons:side-by-side"></iron-icon>
-    </gr-button>
-    <gr-button id="unifiedBtn" link="" has-tooltip="" title="Unified diff" class\$="[[_computeSelectedClass(mode, _VIEW_MODES.UNIFIED)]]" on-click="_handleUnifiedTap">
-      <iron-icon icon="gr-icons:unified"></iron-icon>
-    </gr-button>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      /* Used to remove horizontal whitespace between the icons. */
+      display: flex;
+    }
+    gr-button.selected iron-icon {
+      color: var(--link-color);
+    }
+    iron-icon {
+      height: 1.3rem;
+      width: 1.3rem;
+    }
+  </style>
+  <gr-button
+    id="sideBySideBtn"
+    link=""
+    has-tooltip=""
+    class$="[[_computeSelectedClass(mode, _VIEW_MODES.SIDE_BY_SIDE)]]"
+    title="Side-by-side diff"
+    on-click="_handleSideBySideTap"
+  >
+    <iron-icon icon="gr-icons:side-by-side"></iron-icon>
+  </gr-button>
+  <gr-button
+    id="unifiedBtn"
+    link=""
+    has-tooltip=""
+    title="Unified diff"
+    class$="[[_computeSelectedClass(mode, _VIEW_MODES.UNIFIED)]]"
+    on-click="_handleUnifiedTap"
+  >
+    <iron-icon icon="gr-icons:unified"></iron-icon>
+  </gr-button>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html
index be276ec..309f4ac 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-mode-selector</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 560cf41..c0089d6 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-diff-preferences/gr-diff-preferences.js';
@@ -26,7 +24,7 @@
 import {htmlTemplate} from './gr-diff-preferences-dialog_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiffPreferencesDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.js
index cd26a0b..1d70a4a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog_html.js
@@ -17,46 +17,58 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .diffHeader,
-      .diffActions {
-        padding: var(--spacing-l) var(--spacing-xl);
-      }
-      .diffHeader,
-      .diffActions {
-        background-color: var(--dialog-background-color);
-      }
-      .diffHeader {
-        border-bottom: 1px solid var(--border-color);
-        font-weight: var(--font-weight-bold);
-      }
-      .diffActions {
-        border-top: 1px solid var(--border-color);
-        display: flex;
-        justify-content: flex-end;
-      }
-      .diffPrefsOverlay gr-button {
-        margin-left: var(--spacing-l);
-      }
-      div.edited:after {
-        color: var(--deemphasized-text-color);
-        content: ' *';
-      }
-      #diffPreferences {
-        display: flex;
-        padding: var(--spacing-s) var(--spacing-xl);
-      }
-    </style>
-    <gr-overlay id="diffPrefsOverlay" with-backdrop="">
-      <div class\$="diffHeader [[_computeHeaderClass(_diffPrefsChanged)]]">Diff Preferences</div>
-      <gr-diff-preferences id="diffPreferences" diff-prefs="{{diffPrefs}}" has-unsaved-changes="{{_diffPrefsChanged}}"></gr-diff-preferences>
-      <div class="diffActions">
-        <gr-button id="cancelButton" link="" on-click="_handleCancelDiff">
-            Cancel
-        </gr-button>
-        <gr-button id="saveButton" link="" primary="" on-click="_handleSaveDiffPreferences" disabled\$="[[!_diffPrefsChanged]]">
-            Save
-        </gr-button>
-      </div>
-    </gr-overlay>
+  <style include="shared-styles">
+    .diffHeader,
+    .diffActions {
+      padding: var(--spacing-l) var(--spacing-xl);
+    }
+    .diffHeader,
+    .diffActions {
+      background-color: var(--dialog-background-color);
+    }
+    .diffHeader {
+      border-bottom: 1px solid var(--border-color);
+      font-weight: var(--font-weight-bold);
+    }
+    .diffActions {
+      border-top: 1px solid var(--border-color);
+      display: flex;
+      justify-content: flex-end;
+    }
+    .diffPrefsOverlay gr-button {
+      margin-left: var(--spacing-l);
+    }
+    div.edited:after {
+      color: var(--deemphasized-text-color);
+      content: ' *';
+    }
+    #diffPreferences {
+      display: flex;
+      padding: var(--spacing-s) var(--spacing-xl);
+    }
+  </style>
+  <gr-overlay id="diffPrefsOverlay" with-backdrop="">
+    <div class$="diffHeader [[_computeHeaderClass(_diffPrefsChanged)]]">
+      Diff Preferences
+    </div>
+    <gr-diff-preferences
+      id="diffPreferences"
+      diff-prefs="{{diffPrefs}}"
+      has-unsaved-changes="{{_diffPrefsChanged}}"
+    ></gr-diff-preferences>
+    <div class="diffActions">
+      <gr-button id="cancelButton" link="" on-click="_handleCancelDiff">
+        Cancel
+      </gr-button>
+      <gr-button
+        id="saveButton"
+        link=""
+        primary=""
+        on-click="_handleSaveDiffPreferences"
+        disabled$="[[!_diffPrefsChanged]]"
+      >
+        Save
+      </gr-button>
+    </div>
+  </gr-overlay>
 `;
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 62ddfee..b9c97a9 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
@@ -70,7 +68,7 @@
  *    that the part that is within the context or has comments is shown, while
  *    the rest is not.
  *
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiffProcessor extends GestureEventListeners(
     LegacyElementMixin(
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
index 5577b8c..50bfe107 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-processor test</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 08967e8..608e898 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import {addListener} from '@polymer/polymer/lib/utils/gestures.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
@@ -42,7 +40,7 @@
 const getNewCache = () => { return {left: null, right: null}; };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiffSelection extends mixinBehaviors( [
   DomUtilBehavior,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.js
index ce6008e..620ef02 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_html.js
@@ -17,7 +17,7 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <div class="contentWrapper">
-      <slot></slot>
-    </div>
+  <div class="contentWrapper">
+    <slot></slot>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
index e9405cb..1221b58 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-selection</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 43aee27..67bae7f 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
@@ -14,12 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-dropdown/iron-dropdown.js';
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/shared-styles.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-dropdown/gr-dropdown.js';
 import '../../shared/gr-dropdown-list/gr-dropdown-list.js';
@@ -48,6 +45,7 @@
 import {GrCountStringFormatter} from '../../shared/gr-count-string-formatter/gr-count-string-formatter.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 import {RevisionInfo} from '../../shared/revision-info/revision-info.js';
+import {appContext} from '../../../services/app-context.js';
 
 const ERR_REVIEW_STATUS = 'Couldn’t change file review status.';
 const MSG_LOADING_BLAME = 'Loading blame...';
@@ -67,7 +65,7 @@
 
 /**
  * @appliesMixin PatchSetMixin
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiffView extends mixinBehaviors( [
   KeyboardShortcutBehavior,
@@ -301,6 +299,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   attached() {
     super.attached();
@@ -790,9 +793,9 @@
           return this.$.diffHost.reload(true);
         })
         .then(() => {
-          this.$.reporting.diffViewFullyLoaded();
+          this.reporting.diffViewFullyLoaded();
           // If diff view displayed has not ended yet, it ends here.
-          this.$.reporting.diffViewDisplayed();
+          this.reporting.diffViewDisplayed();
         });
   }
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.js
index cf4cf92..dbbb2fb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.js
@@ -17,278 +17,407 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        background-color: var(--view-background-color);
+  <style include="shared-styles">
+    :host {
+      background-color: var(--view-background-color);
+    }
+    .hidden {
+      display: none;
+    }
+    gr-patch-range-select {
+      display: block;
+    }
+    gr-diff {
+      border: none;
+      --diff-container-styles: {
+        border-bottom: 1px solid var(--border-color);
       }
-      .hidden {
+    }
+    gr-fixed-panel {
+      background-color: var(--view-background-color);
+      border-bottom: 1px solid var(--border-color);
+      z-index: 1;
+    }
+    header,
+    .subHeader {
+      align-items: center;
+      display: flex;
+      justify-content: space-between;
+    }
+    header {
+      padding: var(--spacing-s) var(--spacing-xl);
+      border-bottom: 1px solid var(--border-color);
+    }
+    .changeNumberColon {
+      color: transparent;
+    }
+    .headerSubject {
+      margin-right: var(--spacing-m);
+      font-weight: var(--font-weight-bold);
+    }
+    .patchRangeLeft {
+      align-items: center;
+      display: flex;
+    }
+    .navLink:not([href]) {
+      color: var(--deemphasized-text-color);
+    }
+    .navLinks {
+      align-items: center;
+      display: flex;
+      white-space: nowrap;
+    }
+    .navLink {
+      padding: 0 var(--spacing-xs);
+    }
+    .reviewed {
+      display: inline-block;
+      margin: 0 var(--spacing-xs);
+      vertical-align: 0.15em;
+    }
+    .jumpToFileContainer {
+      display: inline-block;
+    }
+    .mobile {
+      display: none;
+    }
+    gr-button {
+      padding: var(--spacing-s) 0;
+      text-decoration: none;
+    }
+    .loading {
+      color: var(--deemphasized-text-color);
+      font-family: var(--header-font-family);
+      font-size: var(--font-size-h1);
+      font-weight: var(--font-weight-h1);
+      line-height: var(--line-height-h1);
+      height: 100%;
+      padding: var(--spacing-l);
+      text-align: center;
+    }
+    .subHeader {
+      background-color: var(--background-color-secondary);
+      flex-wrap: wrap;
+      padding: 0 var(--spacing-l);
+    }
+    .prefsButton {
+      text-align: right;
+    }
+    .noOverflow {
+      display: block;
+      overflow: auto;
+    }
+    .editMode .hideOnEdit {
+      display: none;
+    }
+    .blameLoader,
+    .fileNum {
+      display: none;
+    }
+    .blameLoader.show,
+    .fileNum.show,
+    .download,
+    .preferences,
+    .rightControls {
+      align-items: center;
+      display: flex;
+    }
+    .diffModeSelector,
+    .editButton {
+      align-items: center;
+      display: flex;
+    }
+    .diffModeSelector span,
+    .editButton span {
+      margin-right: var(--spacing-xs);
+    }
+    .diffModeSelector.hide,
+    .separator.hide {
+      display: none;
+    }
+    gr-dropdown-list {
+      --trigger-style: {
+        text-transform: none;
+      }
+    }
+    .editButtona a {
+      text-decoration: none;
+    }
+    @media screen and (max-width: 50em) {
+      header {
+        padding: var(--spacing-s) var(--spacing-l);
+      }
+      .dash {
         display: none;
       }
-      gr-patch-range-select {
+      .desktop {
+        display: none;
+      }
+      .fileNav {
+        align-items: flex-start;
+        display: flex;
+        margin: 0 var(--spacing-xs);
+      }
+      .fullFileName {
         display: block;
-      }
-      gr-diff {
-        border: none;
-        --diff-container-styles: {
-          border-bottom: 1px solid var(--border-color);
-        }
-      }
-      gr-fixed-panel {
-        background-color: var(--view-background-color);
-        border-bottom: 1px solid var(--border-color);
-        z-index: 1;
-      }
-      header,
-      .subHeader {
-        align-items: center;
-        display: flex;
-        justify-content: space-between;
-      }
-      header {
-        padding: var(--spacing-s) var(--spacing-xl);
-        border-bottom: 1px solid var(--border-color);
-      }
-      .changeNumberColon {
-        color: transparent;
-      }
-      .headerSubject {
-        margin-right: var(--spacing-m);
-        font-weight: var(--font-weight-bold);
-      }
-      .patchRangeLeft {
-        align-items: center;
-        display: flex;
-      }
-      .navLink:not([href]) {
-        color: var(--deemphasized-text-color);
-      }
-      .navLinks {
-        align-items: center;
-        display: flex;
-        white-space: nowrap;
-      }
-      .navLink {
-        padding: 0 var(--spacing-xs);
+        font-style: italic;
+        min-width: 50%;
+        padding: 0 var(--spacing-xxs);
+        text-align: center;
+        width: 100%;
+        word-wrap: break-word;
       }
       .reviewed {
-        display: inline-block;
-        margin: 0 var(--spacing-xs);
-        vertical-align: .15em;
+        vertical-align: -1px;
+      }
+      .mobileNavLink {
+        color: var(--primary-text-color);
+        font-family: var(--header-font-family);
+        font-size: var(--font-size-h2);
+        font-weight: var(--font-weight-h2);
+        line-height: var(--line-height-h2);
+        text-decoration: none;
+      }
+      .mobileNavLink:not([href]) {
+        color: var(--deemphasized-text-color);
       }
       .jumpToFileContainer {
-        display: inline-block;
-      }
-      .mobile {
-        display: none;
-      }
-      gr-button {
-        padding: var(--spacing-s) 0;
-        text-decoration: none;
-      }
-      .loading {
-        color: var(--deemphasized-text-color);
-        font-family: var(--header-font-family);
-        font-size: var(--font-size-h1);
-        font-weight: var(--font-weight-h1);
-        line-height: var(--line-height-h1);
-        height: 100%;
-        padding: var(--spacing-l);
-        text-align: center;
-      }
-      .subHeader {
-        background-color: var(--background-color-secondary);
-        flex-wrap: wrap;
-        padding: 0 var(--spacing-l);
-      }
-      .prefsButton {
-        text-align: right;
-      }
-      .noOverflow {
         display: block;
-        overflow: auto;
-      }
-      .editMode .hideOnEdit {
-        display: none;
-      }
-      .blameLoader,
-      .fileNum {
-        display: none;
-      }
-      .blameLoader.show,
-      .fileNum.show ,
-      .download,
-      .preferences,
-      .rightControls {
-        align-items: center;
-        display: flex;
-      }
-      .diffModeSelector,
-      .editButton {
-        align-items: center;
-        display: flex;
-      }
-      .diffModeSelector span,
-      .editButton span {
-        margin-right: var(--spacing-xs);
-      }
-      .diffModeSelector.hide,
-      .separator.hide {
-        display: none;
+        width: 100%;
       }
       gr-dropdown-list {
-        --trigger-style: {
-          text-transform: none;
-        }
-      }
-      .editButtona a {
-        text-decoration: none;
-      }
-      @media screen and (max-width: 50em) {
-        header {
-          padding: var(--spacing-s) var(--spacing-l);
-        }
-        .dash {
-          display: none;
-        }
-        .desktop {
-          display: none;
-        }
-        .fileNav {
-          align-items: flex-start;
-          display: flex;
-          margin: 0 var(--spacing-xs);
-        }
-        .fullFileName {
-          display: block;
-          font-style: italic;
-          min-width: 50%;
-          padding: 0 var(--spacing-xxs);
-          text-align: center;
-          width: 100%;
-          word-wrap: break-word;
-        }
-        .reviewed {
-          vertical-align: -1px;
-        }
-        .mobileNavLink {
-          color: var(--primary-text-color);
-          font-family: var(--header-font-family);
-          font-size: var(--font-size-h2);
-          font-weight: var(--font-weight-h2);
-          line-height: var(--line-height-h2);
-          text-decoration: none;
-        }
-        .mobileNavLink:not([href]) {
-          color: var(--deemphasized-text-color);
-        }
-        .jumpToFileContainer {
+        width: 100%;
+        --gr-select-style: {
           display: block;
           width: 100%;
         }
-        gr-dropdown-list {
+        --native-select-style: {
           width: 100%;
-          --gr-select-style: {
-            display: block;
-            width: 100%;
-          }
-          --native-select-style: {
-            width: 100%;
-          }
         }
       }
-    </style>
-    <gr-fixed-panel class\$="[[_computeContainerClass(_editMode)]]" floating-disabled="[[_panelFloatingDisabled]]" keep-on-scroll="" ready-for-measure="[[!_loading]]" on-floating-height-changed="_onChangeHeaderPanelHeightChanged">
-      <header>
-        <div>
-          <a href\$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]">[[_changeNum]]</a><!--
+    }
+  </style>
+  <gr-fixed-panel
+    class$="[[_computeContainerClass(_editMode)]]"
+    floating-disabled="[[_panelFloatingDisabled]]"
+    keep-on-scroll=""
+    ready-for-measure="[[!_loading]]"
+    on-floating-height-changed="_onChangeHeaderPanelHeightChanged"
+  >
+    <header>
+      <div>
+        <a
+          href$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]"
+          >[[_changeNum]]</a
+        ><!--
        --><span class="changeNumberColon">:</span>
-          <span class="headerSubject">[[_change.subject]]</span>
-          <input id="reviewed" class="reviewed hideOnEdit" type="checkbox" on-change="_handleReviewedChange" hidden\$="[[!_loggedIn]]" hidden=""><!--
-       --><div class="jumpToFileContainer">
-            <gr-dropdown-list id="dropdown" value="[[_path]]" on-value-change="_handleFileChange" items="[[_formattedFiles]]" initial-count="75">
-           </gr-dropdown-list>
-          </div>
-        </div>
-        <div class="navLinks desktop">
-          <span class\$="fileNum [[_computeFileNumClass(_fileNum, _formattedFiles)]]">
-            File [[_fileNum]] of [[_formattedFiles.length]]
-            <span class="separator"></span>
-          </span>
-          <a class="navLink" title="[[createTitle(Shortcut.PREV_FILE,
-                    ShortcutSection.NAVIGATION)]]" href\$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]">
-            Prev</a>
-          <span class="separator"></span>
-          <a class="navLink" title="[[createTitle(Shortcut.UP_TO_CHANGE,
-                ShortcutSection.NAVIGATION)]]" href\$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]">
-            Up</a>
-          <span class="separator"></span>
-          <a class="navLink" title="[[createTitle(Shortcut.NEXT_FILE,
-                ShortcutSection.NAVIGATION)]]" href\$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]">
-            Next</a>
-        </div>
-      </header>
-      <div class="subHeader">
-        <div class="patchRangeLeft">
-          <gr-patch-range-select id="rangeSelect" change-num="[[_changeNum]]" change-comments="[[_changeComments]]" patch-num="[[_patchRange.patchNum]]" base-patch-num="[[_patchRange.basePatchNum]]" files-weblinks="[[_filesWeblinks]]" available-patches="[[_allPatchSets]]" revisions="[[_change.revisions]]" revision-info="[[_revisionInfo]]" on-patch-range-change="_handlePatchChange">
-          </gr-patch-range-select>
-          <span class="download desktop">
-            <span class="separator"></span>
-            <gr-dropdown link="" down-arrow="" items="[[_computeDownloadDropdownLinks(_change.project, _changeNum, _patchRange, _path, _diff)]]" horizontal-align="left">
-              <span class="downloadTitle">
-                Download
-              </span>
-            </gr-dropdown>
-          </span>
-        </div>
-        <div class="rightControls">
-          <span class\$="blameLoader [[_computeBlameLoaderClass(_isImageDiff, _path)]]">
-            <gr-button link="" id="toggleBlame" title="[[createTitle(Shortcut.TOGGLE_BLAME, ShortcutSection.DIFFS)]]" disabled="[[_isBlameLoading]]" on-click="_toggleBlame">[[_computeBlameToggleLabel(_isBlameLoaded, _isBlameLoading)]]</gr-button>
-          </span>
-          <template is="dom-if" if="[[_computeIsLoggedIn(_loggedIn)]]">
-            <span class="separator"></span>
-            <span class="editButton">
-              <gr-button link="" title="Edit current file" on-click="_goToEditFile">edit</gr-button>
-            </span>
-          </template>
-          <span class="separator"></span>
-          <div class\$="diffModeSelector [[_computeModeSelectHideClass(_isImageDiff)]]">
-            <span>Diff view:</span>
-            <gr-diff-mode-selector id="modeSelect" save-on-change="[[!_diffPrefsDisabled]]" mode="{{changeViewState.diffMode}}"></gr-diff-mode-selector>
-          </div>
-          <span id="diffPrefsContainer" hidden\$="[[_computePrefsButtonHidden(_prefs, _diffPrefsDisabled)]]" hidden="">
-            <span class="preferences desktop">
-              <gr-button link="" class="prefsButton" has-tooltip="" title="Diff preferences" on-click="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
-            </span>
-          </span>
-          <gr-endpoint-decorator name="annotation-toggler">
-            <span hidden="" id="annotation-span">
-              <label for="annotation-checkbox" id="annotation-label"></label>
-              <iron-input type="checkbox" disabled="">
-                <input is="iron-input" type="checkbox" id="annotation-checkbox" disabled="">
-              </iron-input>
-            </span>
-          </gr-endpoint-decorator>
+        <span class="headerSubject">[[_change.subject]]</span>
+        <input
+          id="reviewed"
+          class="reviewed hideOnEdit"
+          type="checkbox"
+          on-change="_handleReviewedChange"
+          hidden$="[[!_loggedIn]]"
+          hidden=""
+        /><!--
+       -->
+        <div class="jumpToFileContainer">
+          <gr-dropdown-list
+            id="dropdown"
+            value="[[_path]]"
+            on-value-change="_handleFileChange"
+            items="[[_formattedFiles]]"
+            initial-count="75"
+          >
+          </gr-dropdown-list>
         </div>
       </div>
-      <div class="fileNav mobile">
-        <a class="mobileNavLink" href\$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]">
-          &lt;</a>
-        <div class="fullFileName mobile">[[computeDisplayPath(_path)]]
-        </div>
-        <a class="mobileNavLink" href\$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]">
-          &gt;</a>
+      <div class="navLinks desktop">
+        <span
+          class$="fileNum [[_computeFileNumClass(_fileNum, _formattedFiles)]]"
+        >
+          File [[_fileNum]] of [[_formattedFiles.length]]
+          <span class="separator"></span>
+        </span>
+        <a
+          class="navLink"
+          title="[[createTitle(Shortcut.PREV_FILE,
+                    ShortcutSection.NAVIGATION)]]"
+          href$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]"
+        >
+          Prev</a
+        >
+        <span class="separator"></span>
+        <a
+          class="navLink"
+          title="[[createTitle(Shortcut.UP_TO_CHANGE,
+                ShortcutSection.NAVIGATION)]]"
+          href$="[[_computeChangePath(_change, _patchRange.*, _change.revisions)]]"
+        >
+          Up</a
+        >
+        <span class="separator"></span>
+        <a
+          class="navLink"
+          title="[[createTitle(Shortcut.NEXT_FILE,
+                ShortcutSection.NAVIGATION)]]"
+          href$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]"
+        >
+          Next</a
+        >
       </div>
-    </gr-fixed-panel>
-    <div class="loading" hidden\$="[[!_loading]]">Loading...</div>
-    <gr-diff-host id="diffHost" hidden="" hidden\$="[[_loading]]" class\$="[[_computeDiffClass(_panelFloatingDisabled)]]" is-image-diff="{{_isImageDiff}}" files-weblinks="{{_filesWeblinks}}" diff="{{_diff}}" change-num="[[_changeNum]]" commit-range="[[_commitRange]]" patch-range="[[_patchRange]]" path="[[_path]]" prefs="[[_prefs]]" project-name="[[_change.project]]" view-mode="[[_diffMode]]" is-blame-loaded="{{_isBlameLoaded}}" on-comment-anchor-tap="_onLineSelected" on-line-selected="_onLineSelected">
-    </gr-diff-host>
-    <gr-apply-fix-dialog id="applyFixDialog" prefs="[[_prefs]]" change="[[_change]]" change-num="[[_changeNum]]">
-    </gr-apply-fix-dialog>
-    <gr-diff-preferences-dialog id="diffPreferencesDialog" diff-prefs="{{_prefs}}" on-reload-diff-preference="_handleReloadingDiffPreference">
-    </gr-diff-preferences-dialog>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
-    <gr-diff-cursor id="cursor" scroll-top-margin="[[_scrollTopMargin]]"></gr-diff-cursor>
-    <gr-comment-api id="commentAPI"></gr-comment-api>
-    <gr-reporting id="reporting"></gr-reporting>
+    </header>
+    <div class="subHeader">
+      <div class="patchRangeLeft">
+        <gr-patch-range-select
+          id="rangeSelect"
+          change-num="[[_changeNum]]"
+          change-comments="[[_changeComments]]"
+          patch-num="[[_patchRange.patchNum]]"
+          base-patch-num="[[_patchRange.basePatchNum]]"
+          files-weblinks="[[_filesWeblinks]]"
+          available-patches="[[_allPatchSets]]"
+          revisions="[[_change.revisions]]"
+          revision-info="[[_revisionInfo]]"
+          on-patch-range-change="_handlePatchChange"
+        >
+        </gr-patch-range-select>
+        <span class="download desktop">
+          <span class="separator"></span>
+          <gr-dropdown
+            link=""
+            down-arrow=""
+            items="[[_computeDownloadDropdownLinks(_change.project, _changeNum, _patchRange, _path, _diff)]]"
+            horizontal-align="left"
+          >
+            <span class="downloadTitle">
+              Download
+            </span>
+          </gr-dropdown>
+        </span>
+      </div>
+      <div class="rightControls">
+        <span
+          class$="blameLoader [[_computeBlameLoaderClass(_isImageDiff, _path)]]"
+        >
+          <gr-button
+            link=""
+            id="toggleBlame"
+            title="[[createTitle(Shortcut.TOGGLE_BLAME, ShortcutSection.DIFFS)]]"
+            disabled="[[_isBlameLoading]]"
+            on-click="_toggleBlame"
+            >[[_computeBlameToggleLabel(_isBlameLoaded,
+            _isBlameLoading)]]</gr-button
+          >
+        </span>
+        <template is="dom-if" if="[[_computeIsLoggedIn(_loggedIn)]]">
+          <span class="separator"></span>
+          <span class="editButton">
+            <gr-button
+              link=""
+              title="Edit current file"
+              on-click="_goToEditFile"
+              >edit</gr-button
+            >
+          </span>
+        </template>
+        <span class="separator"></span>
+        <div
+          class$="diffModeSelector [[_computeModeSelectHideClass(_isImageDiff)]]"
+        >
+          <span>Diff view:</span>
+          <gr-diff-mode-selector
+            id="modeSelect"
+            save-on-change="[[!_diffPrefsDisabled]]"
+            mode="{{changeViewState.diffMode}}"
+          ></gr-diff-mode-selector>
+        </div>
+        <span
+          id="diffPrefsContainer"
+          hidden$="[[_computePrefsButtonHidden(_prefs, _diffPrefsDisabled)]]"
+          hidden=""
+        >
+          <span class="preferences desktop">
+            <gr-button
+              link=""
+              class="prefsButton"
+              has-tooltip=""
+              title="Diff preferences"
+              on-click="_handlePrefsTap"
+              ><iron-icon icon="gr-icons:settings"></iron-icon
+            ></gr-button>
+          </span>
+        </span>
+        <gr-endpoint-decorator name="annotation-toggler">
+          <span hidden="" id="annotation-span">
+            <label for="annotation-checkbox" id="annotation-label"></label>
+            <iron-input type="checkbox" disabled="">
+              <input
+                is="iron-input"
+                type="checkbox"
+                id="annotation-checkbox"
+                disabled=""
+              />
+            </iron-input>
+          </span>
+        </gr-endpoint-decorator>
+      </div>
+    </div>
+    <div class="fileNav mobile">
+      <a
+        class="mobileNavLink"
+        href$="[[_computeNavLinkURL(_change, _path, _fileList, -1, 1)]]"
+      >
+        &lt;</a
+      >
+      <div class="fullFileName mobile">[[computeDisplayPath(_path)]]</div>
+      <a
+        class="mobileNavLink"
+        href$="[[_computeNavLinkURL(_change, _path, _fileList, 1, 1)]]"
+      >
+        &gt;</a
+      >
+    </div>
+  </gr-fixed-panel>
+  <div class="loading" hidden$="[[!_loading]]">Loading...</div>
+  <gr-diff-host
+    id="diffHost"
+    hidden=""
+    hidden$="[[_loading]]"
+    class$="[[_computeDiffClass(_panelFloatingDisabled)]]"
+    is-image-diff="{{_isImageDiff}}"
+    files-weblinks="{{_filesWeblinks}}"
+    diff="{{_diff}}"
+    change-num="[[_changeNum]]"
+    commit-range="[[_commitRange]]"
+    patch-range="[[_patchRange]]"
+    path="[[_path]]"
+    prefs="[[_prefs]]"
+    project-name="[[_change.project]]"
+    view-mode="[[_diffMode]]"
+    is-blame-loaded="{{_isBlameLoaded}}"
+    on-comment-anchor-tap="_onLineSelected"
+    on-line-selected="_onLineSelected"
+  >
+  </gr-diff-host>
+  <gr-apply-fix-dialog
+    id="applyFixDialog"
+    prefs="[[_prefs]]"
+    change="[[_change]]"
+    change-num="[[_changeNum]]"
+  >
+  </gr-apply-fix-dialog>
+  <gr-diff-preferences-dialog
+    id="diffPreferencesDialog"
+    diff-prefs="{{_prefs}}"
+    on-reload-diff-preference="_handleReloadingDiffPreference"
+  >
+  </gr-diff-preferences-dialog>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-storage id="storage"></gr-storage>
+  <gr-diff-cursor
+    id="cursor"
+    scroll-top-margin="[[_scrollTopMargin]]"
+  ></gr-diff-cursor>
+  <gr-comment-api id="commentAPI"></gr-comment-api>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
index 26b900d..aff74af 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -113,7 +114,7 @@
     });
 
     test('params change triggers diffViewDisplayed()', () => {
-      sandbox.stub(element.$.reporting, 'diffViewDisplayed');
+      sandbox.stub(element.reporting, 'diffViewDisplayed');
       sandbox.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
       sandbox.spy(element, '_paramsChanged');
       element.params = {
@@ -125,7 +126,7 @@
       };
 
       return element._paramsChanged.returnValues[0].then(() => {
-        assert.isTrue(element.$.reporting.diffViewDisplayed.calledOnce);
+        assert.isTrue(element.reporting.diffViewDisplayed.calledOnce);
       });
     });
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
index 12d7324..d50a7f4 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-group</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 a5eb233..4b831e2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
 import '../gr-diff-builder/gr-diff-builder-element.js';
@@ -69,7 +67,7 @@
 const RENDER_DIFF_TABLE_DEBOUNCE_NAME = 'renderDiffTable';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDiff extends mixinBehaviors( [
   PatchSetBehavior,
@@ -944,7 +942,7 @@
       messages.push(NO_NEWLINE_REVISION);
     }
     if (!messages.length) { return null; }
-    return messages.join(' — ');
+    return messages.join(' \u2014 ');// \u2014 - '—'
   }
 
   /**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.js
index 1ec8175..55abd38 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.js
@@ -17,384 +17,438 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host(.no-left) .sideBySide .left,
-      :host(.no-left) .sideBySide .left + td,
-      :host(.no-left) .sideBySide .right:not([data-value]),
-      :host(.no-left) .sideBySide .right:not([data-value]) + td {
-        display: none;
-      }
-      :host {
-        font-family: var(--monospace-font-family, ''), 'Roboto Mono';
-        font-size: var(--font-size, var(--font-size-code, 12px));
-        line-height: var(--line-height-code, 1.334);
-      }
+  <style include="shared-styles">
+    :host(.no-left) .sideBySide .left,
+    :host(.no-left) .sideBySide .left + td,
+    :host(.no-left) .sideBySide .right:not([data-value]),
+    :host(.no-left) .sideBySide .right:not([data-value]) + td {
+      display: none;
+    }
+    :host {
+      font-family: var(--monospace-font-family, ''), 'Roboto Mono';
+      font-size: var(--font-size, var(--font-size-code, 12px));
+      line-height: var(--line-height-code, 1.334);
+    }
 
-      .thread-group {
-        display: block;
-        max-width: var(--content-width, 80ch);
-        white-space: normal;
-        background-color: var(--diff-blank-background-color);
-      }
-      .diffContainer {
-        display: flex;
-        font-family: var(--monospace-font-family);
-        @apply --diff-container-styles;
-      }
-      .diffContainer.hiddenscroll {
-        margin-bottom: var(--spacing-m);
-      }
-      table {
-        border-collapse: collapse;
-        border-right: 1px solid var(--border-color);
-        table-layout: fixed;
-      }
-      .lineNumButton {
-        display: block;
-        width: 100%;
-        height: 100%;
-        background-color: var(--diff-blank-background-color);
-      }
-      /*
+    .thread-group {
+      display: block;
+      max-width: var(--content-width, 80ch);
+      white-space: normal;
+      background-color: var(--diff-blank-background-color);
+    }
+    .diffContainer {
+      display: flex;
+      font-family: var(--monospace-font-family);
+      @apply --diff-container-styles;
+    }
+    .diffContainer.hiddenscroll {
+      margin-bottom: var(--spacing-m);
+    }
+    table {
+      border-collapse: collapse;
+      border-right: 1px solid var(--border-color);
+      table-layout: fixed;
+    }
+    .lineNumButton {
+      display: block;
+      width: 100%;
+      height: 100%;
+      background-color: var(--diff-blank-background-color);
+    }
+    /*
       The only way to focus this (clicking) will apply our own focus styling,
       so this default styling is not needed and distracting.
       */
-      .lineNumButton:focus {
-        outline: none;
-      }
-      .image-diff .gr-diff {
-        text-align: center;
-      }
-      .image-diff img {
-        box-shadow: var(--elevation-level-1);
-        max-width: 50em;
-      }
-      .image-diff .right.lineNumButton {
-        border-left: 1px solid var(--border-color);
-      }
-      .image-diff label,
-      .binary-diff label {
-        font-family: var(--font-family);
-        font-style: italic;
-      }
-      .diff-row {
-        outline: none;
-        user-select: none;
-      }
-      .diff-row.target-row.target-side-left .lineNumButton.left,
-      .diff-row.target-row.target-side-right .lineNumButton.right,
-      .diff-row.target-row.unified .lineNumButton {
-        background-color: var(--diff-selection-background-color);
-        color: var(--primary-text-color);
-      }
-      .content {
-        background-color: var(--diff-blank-background-color);
-      }
-      .contentText {
-        background-color: var(--view-background-color);
-      }
-      .blank {
-        background-color: var(--diff-blank-background-color);
-      }
-      .image-diff .content {
-        background-color: var(--diff-blank-background-color);
-      }
-      .full-width {
-        width: 100%;
-      }
-      .full-width .contentText {
-        white-space: pre-wrap;
-        word-wrap: break-word;
-      }
-      .lineNumButton,
-      .content {
-        vertical-align: top;
-        white-space: pre;
-      }
-      .contextLineNum,
-      .lineNumButton {
-        -webkit-user-select: none;
-        -moz-user-select: none;
-        -ms-user-select: none;
-        user-select: none;
+    .lineNumButton:focus {
+      outline: none;
+    }
+    .image-diff .gr-diff {
+      text-align: center;
+    }
+    .image-diff img {
+      box-shadow: var(--elevation-level-1);
+      max-width: 50em;
+    }
+    .image-diff .right.lineNumButton {
+      border-left: 1px solid var(--border-color);
+    }
+    .image-diff label,
+    .binary-diff label {
+      font-family: var(--font-family);
+      font-style: italic;
+    }
+    .diff-row {
+      outline: none;
+      user-select: none;
+    }
+    .diff-row.target-row.target-side-left .lineNumButton.left,
+    .diff-row.target-row.target-side-right .lineNumButton.right,
+    .diff-row.target-row.unified .lineNumButton {
+      background-color: var(--diff-selection-background-color);
+      color: var(--primary-text-color);
+    }
+    .content {
+      background-color: var(--diff-blank-background-color);
+    }
+    .contentText {
+      background-color: var(--view-background-color);
+    }
+    .blank {
+      background-color: var(--diff-blank-background-color);
+    }
+    .image-diff .content {
+      background-color: var(--diff-blank-background-color);
+    }
+    .full-width {
+      width: 100%;
+    }
+    .full-width .contentText {
+      white-space: pre-wrap;
+      word-wrap: break-word;
+    }
+    .lineNumButton,
+    .content {
+      vertical-align: top;
+      white-space: pre;
+    }
+    .contextLineNum,
+    .lineNumButton {
+      -webkit-user-select: none;
+      -moz-user-select: none;
+      -ms-user-select: none;
+      user-select: none;
 
-        color: var(--deemphasized-text-color);
-        padding: 0 var(--spacing-m);
-        text-align: right;
-      }
-      .canComment .lineNumButton {
-        cursor: pointer;
-      }
-      .content {
-        /* Set min width since setting width on table cells still
+      color: var(--deemphasized-text-color);
+      padding: 0 var(--spacing-m);
+      text-align: right;
+    }
+    .canComment .lineNumButton {
+      cursor: pointer;
+    }
+    .content {
+      /* Set min width since setting width on table cells still
            allows them to shrink. Do not set max width because
            CJK (Chinese-Japanese-Korean) glyphs have variable width */
-        min-width: var(--content-width, 80ch);
-        width: var(--content-width, 80ch);
-      }
-      .content.add .contentText .intraline,
+      min-width: var(--content-width, 80ch);
+      width: var(--content-width, 80ch);
+    }
+    .content.add .contentText .intraline,
       /* If there are no intraline info, consider everything changed */
       .content.add.no-intraline-info .contentText,
       .delta.total .content.add .contentText {
-        background-color: var(--dark-add-highlight-color);
-      }
-      .content.add .contentText {
-        background-color: var(--light-add-highlight-color);
-      }
-      .content.remove .contentText .intraline,
+      background-color: var(--dark-add-highlight-color);
+    }
+    .content.add .contentText {
+      background-color: var(--light-add-highlight-color);
+    }
+    .content.remove .contentText .intraline,
       /* If there are no intraline info, consider everything changed */
       .content.remove.no-intraline-info .contentText,
-      .delta.total .content.remove .contentText  {
-        background-color: var(--dark-remove-highlight-color);
-      }
-      .content.remove .contentText {
-        background-color: var(--light-remove-highlight-color);
-      }
+      .delta.total .content.remove .contentText {
+      background-color: var(--dark-remove-highlight-color);
+    }
+    .content.remove .contentText {
+      background-color: var(--light-remove-highlight-color);
+    }
 
-      /* dueToRebase */
-      .dueToRebase .content.add .contentText .intraline,
-      .delta.total.dueToRebase .content.add .contentText {
-        background-color: var(--dark-rebased-add-highlight-color);
-      }
-      .dueToRebase .content.add .contentText {
-        background-color: var(--light-rebased-add-highlight-color);
-      }
-      .dueToRebase .content.remove .contentText .intraline,
-      .delta.total.dueToRebase .content.remove .contentText {
-        background-color: var(--dark-rebased-remove-highlight-color);
-      }
-      .dueToRebase .content.remove .contentText {
-        background-color: var(--light-remove-add-highlight-color);
-      }
+    /* dueToRebase */
+    .dueToRebase .content.add .contentText .intraline,
+    .delta.total.dueToRebase .content.add .contentText {
+      background-color: var(--dark-rebased-add-highlight-color);
+    }
+    .dueToRebase .content.add .contentText {
+      background-color: var(--light-rebased-add-highlight-color);
+    }
+    .dueToRebase .content.remove .contentText .intraline,
+    .delta.total.dueToRebase .content.remove .contentText {
+      background-color: var(--dark-rebased-remove-highlight-color);
+    }
+    .dueToRebase .content.remove .contentText {
+      background-color: var(--light-remove-add-highlight-color);
+    }
 
-      /* ignoredWhitespaceOnly */
-      .ignoredWhitespaceOnly .content.add .contentText .intraline,
-      .delta.total.ignoredWhitespaceOnly .content.add .contentText,
-      .ignoredWhitespaceOnly .content.add .contentText,
-      .ignoredWhitespaceOnly .content.remove .contentText .intraline,
-      .delta.total.ignoredWhitespaceOnly .content.remove .contentText,
-      .ignoredWhitespaceOnly .content.remove .contentText {
-        background-color: var(--view-background-color);
-      }
+    /* ignoredWhitespaceOnly */
+    .ignoredWhitespaceOnly .content.add .contentText .intraline,
+    .delta.total.ignoredWhitespaceOnly .content.add .contentText,
+    .ignoredWhitespaceOnly .content.add .contentText,
+    .ignoredWhitespaceOnly .content.remove .contentText .intraline,
+    .delta.total.ignoredWhitespaceOnly .content.remove .contentText,
+    .ignoredWhitespaceOnly .content.remove .contentText {
+      background-color: var(--view-background-color);
+    }
 
-      .content .contentText:empty:after {
-        /* Newline, to ensure empty lines are one line-height tall. */
-        content: '\\A';
-      }
-      .contextControl {
-        background-color: var(--diff-context-control-background-color);
-        border: 1px solid var(--diff-context-control-border-color);
+    .content .contentText:empty:after {
+      /* Newline, to ensure empty lines are one line-height tall. */
+      content: '\\A';
+    }
+    .contextControl {
+      background-color: var(--diff-context-control-background-color);
+      border: 1px solid var(--diff-context-control-border-color);
+      color: var(--diff-context-control-color);
+    }
+    .contextControl gr-button {
+      display: inline-block;
+      text-decoration: none;
+      vertical-align: top;
+      line-height: var(--line-height-mono, 18px);
+      --gr-button: {
         color: var(--diff-context-control-color);
+        padding: var(--spacing-xxs) var(--spacing-l);
       }
-      .contextControl gr-button {
-        display: inline-block;
-        text-decoration: none;
-        vertical-align: top;
-        line-height: var(--line-height-mono, 18px);
-        --gr-button: {
-          color: var(--diff-context-control-color);
-          padding: var(--spacing-xxs) var(--spacing-l);
-        }
-      }
-      .contextControl gr-button iron-icon {
-        /* should match line-height of gr-button */
-        width: var(--line-height-mono, 18px);
-        height: var(--line-height-mono, 18px);
-      }
-      .contextControl td:not(.lineNumButton) {
-        text-align: center;
-      }
-      .displayLine .diff-row.target-row td {
-        box-shadow: inset 0 -1px var(--border-color);
-      }
-      .br:after {
-        /* Line feed */
-        content: '\\A';
-      }
-      .tab {
-        display: inline-block;
-      }
-      .tab-indicator:before {
-        color: var(--diff-tab-indicator-color);
-        /* >> character */
-        content: '\\00BB';
-        position: absolute;
-      }
-      /* Is defined after other background-colors, such that this
+    }
+    .contextControl gr-button iron-icon {
+      /* should match line-height of gr-button */
+      width: var(--line-height-mono, 18px);
+      height: var(--line-height-mono, 18px);
+    }
+    .contextControl td:not(.lineNumButton) {
+      text-align: center;
+    }
+    .displayLine .diff-row.target-row td {
+      box-shadow: inset 0 -1px var(--border-color);
+    }
+    .br:after {
+      /* Line feed */
+      content: '\\A';
+    }
+    .tab {
+      display: inline-block;
+    }
+    .tab-indicator:before {
+      color: var(--diff-tab-indicator-color);
+      /* >> character */
+      content: '\\00BB';
+      position: absolute;
+    }
+    /* Is defined after other background-colors, such that this
          rule wins in case of same specificity. */
-      .trailing-whitespace,
-      .content .trailing-whitespace,
-      .trailing-whitespace .intraline,
-      .content .trailing-whitespace .intraline {
-        border-radius: var(--border-radius, 4px);
-        background-color: var(--diff-trailing-whitespace-indicator);
-      }
-      #diffHeader {
-        background-color: var(--table-header-background-color);
-        border-bottom: 1px solid var(--border-color);
-        color: var(--link-color);
-        padding: var(--spacing-m) 0 var(--spacing-m) 48px;
-      }
-      #loadingError,
-      #sizeWarning {
-        display: none;
-        margin: var(--spacing-l) auto;
-        max-width: 60em;
-        text-align: center;
-      }
-      #loadingError {
-        color: var(--error-text-color);
-      }
-      #sizeWarning gr-button {
-        margin: var(--spacing-l);
-      }
-      #loadingError.showError,
-      #sizeWarning.warn {
-        display: block;
-      }
-      .target-row td.blame {
-        background: var(--diff-selection-background-color);
-      }
-      col.blame {
-        display: none;
-      }
-      td.blame {
-        display: none;
-        padding: 0 var(--spacing-m);
-        white-space: pre;
-      }
-      :host(.showBlame) col.blame {
-        display: table-column;
-      }
-      :host(.showBlame) td.blame {
-        display: table-cell;
-      }
-      td.blame > span {
-        opacity: 0.6;
-      }
-      td.blame > span.startOfRange {
-        opacity: 1;
-      }
-      td.blame .blameDate {
-        font-family: var(--monospace-font-family);
-        color: var(--link-color);
-        text-decoration: none;
-      }
-      .full-width td.blame {
-        overflow: hidden;
-        width: 200px;
-      }
-      /** Support the line length indicator **/
-      .full-width td.content .contentText {
-        /* Base 64 encoded 1x1px of #ddd */
-        background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO8+x8AAr8B3gzOjaQAAAAASUVORK5CYII=');
-        background-position: var(--line-limit) 0;
-        background-repeat: repeat-y;
-      }
-      .newlineWarning {
-        color: var(--deemphasized-text-color);
-        text-align: center;
-      }
-      .newlineWarning.hidden {
-        display: none;
-      }
-      .lineNumButton.COVERED {
-         background-color: var(--coverage-covered, #e0f2f1);
-      }
-      .lineNumButton.NOT_COVERED {
-        background-color: var(--coverage-not-covered, #ffd1a4);
-      }
-      .lineNumButton.PARTIALLY_COVERED {
-        background: linear-gradient(to right bottom, var(--coverage-not-covered, #ffd1a4) 0%,
-                                                     var(--coverage-not-covered, #ffd1a4) 50%,
-                                                     var(--coverage-covered, #e0f2f1) 50%,
-                                                     var(--coverage-covered, #e0f2f1) 100%);
-      }
+    .trailing-whitespace,
+    .content .trailing-whitespace,
+    .trailing-whitespace .intraline,
+    .content .trailing-whitespace .intraline {
+      border-radius: var(--border-radius, 4px);
+      background-color: var(--diff-trailing-whitespace-indicator);
+    }
+    #diffHeader {
+      background-color: var(--table-header-background-color);
+      border-bottom: 1px solid var(--border-color);
+      color: var(--link-color);
+      padding: var(--spacing-m) 0 var(--spacing-m) 48px;
+    }
+    #loadingError,
+    #sizeWarning {
+      display: none;
+      margin: var(--spacing-l) auto;
+      max-width: 60em;
+      text-align: center;
+    }
+    #loadingError {
+      color: var(--error-text-color);
+    }
+    #sizeWarning gr-button {
+      margin: var(--spacing-l);
+    }
+    #loadingError.showError,
+    #sizeWarning.warn {
+      display: block;
+    }
+    .target-row td.blame {
+      background: var(--diff-selection-background-color);
+    }
+    col.blame {
+      display: none;
+    }
+    td.blame {
+      display: none;
+      padding: 0 var(--spacing-m);
+      white-space: pre;
+    }
+    :host(.showBlame) col.blame {
+      display: table-column;
+    }
+    :host(.showBlame) td.blame {
+      display: table-cell;
+    }
+    td.blame > span {
+      opacity: 0.6;
+    }
+    td.blame > span.startOfRange {
+      opacity: 1;
+    }
+    td.blame .blameDate {
+      font-family: var(--monospace-font-family);
+      color: var(--link-color);
+      text-decoration: none;
+    }
+    .full-width td.blame {
+      overflow: hidden;
+      width: 200px;
+    }
+    /** Support the line length indicator **/
+    .full-width td.content .contentText {
+      /* Base 64 encoded 1x1px of #ddd */
+      background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO8+x8AAr8B3gzOjaQAAAAASUVORK5CYII=');
+      background-position: var(--line-limit) 0;
+      background-repeat: repeat-y;
+    }
+    .newlineWarning {
+      color: var(--deemphasized-text-color);
+      text-align: center;
+    }
+    .newlineWarning.hidden {
+      display: none;
+    }
+    .lineNum.COVERED .lineNumButton {
+      background-color: var(--coverage-covered, #e0f2f1);
+    }
+    .lineNum.NOT_COVERED .lineNumButton {
+      background-color: var(--coverage-not-covered, #ffd1a4);
+    }
+    .lineNum.PARTIALLY_COVERED .lineNumButton {
+      background: linear-gradient(
+        to right bottom,
+        var(--coverage-not-covered, #ffd1a4) 0%,
+        var(--coverage-not-covered, #ffd1a4) 50%,
+        var(--coverage-covered, #e0f2f1) 50%,
+        var(--coverage-covered, #e0f2f1) 100%
+      );
+    }
 
-      /** BEGIN: Select and copy for Polymer 2 */
-      /** Below was copied and modified from the original css in gr-diff-selection.html */
-      .content,
-      .contextControl,
-      .blame {
-        -webkit-user-select: none;
-        -moz-user-select: none;
-        -ms-user-select: none;
-        user-select: none;
-      }
+    /** BEGIN: Select and copy for Polymer 2 */
+    /** Below was copied and modified from the original css in gr-diff-selection.html */
+    .content,
+    .contextControl,
+    .blame {
+      -webkit-user-select: none;
+      -moz-user-select: none;
+      -ms-user-select: none;
+      user-select: none;
+    }
 
-      .selected-left:not(.selected-comment) .side-by-side .left + .content .contentText,
-      .selected-right:not(.selected-comment) .side-by-side .right + .content .contentText,
-      .selected-left:not(.selected-comment) .unified .left.lineNumButton ~ .content:not(.both) .contentText,
-      .selected-right:not(.selected-comment) .unified .right.lineNumButton ~ .content .contentText,
-      .selected-left.selected-comment .side-by-side .left + .content .message,
-      .selected-right.selected-comment .side-by-side .right + .content .message :not(.collapsedContent),
-      .selected-comment .unified .message :not(.collapsedContent),
-      .selected-blame .blame {
-        -webkit-user-select: text;
-        -moz-user-select: text;
-        -ms-user-select: text;
-        user-select: text;
-      }
+    .selected-left:not(.selected-comment)
+      .side-by-side
+      .left
+      + .content
+      .contentText,
+    .selected-right:not(.selected-comment)
+      .side-by-side
+      .right
+      + .content
+      .contentText,
+    .selected-left:not(.selected-comment)
+      .unified
+      .left.lineNum
+      ~ .content:not(.both)
+      .contentText,
+    .selected-right:not(.selected-comment)
+      .unified
+      .right.lineNum
+      ~ .content
+      .contentText,
+    .selected-left.selected-comment .side-by-side .left + .content .message,
+    .selected-right.selected-comment
+      .side-by-side
+      .right
+      + .content
+      .message
+      :not(.collapsedContent),
+    .selected-comment .unified .message :not(.collapsedContent),
+    .selected-blame .blame {
+      -webkit-user-select: text;
+      -moz-user-select: text;
+      -ms-user-select: text;
+      user-select: text;
+    }
 
-      /** Make comments selectable when selected */
-      .selected-left.selected-comment ::slotted(gr-comment-thread[comment-side=left]),
-      .selected-right.selected-comment ::slotted(gr-comment-thread[comment-side=right]) {
-        -webkit-user-select: text;
-        -moz-user-select: text;
-        -ms-user-select: text;
-        user-select: text;
-      }
-      /** END: Select and copy for Polymer 2 */
+    /** Make comments selectable when selected */
+    .selected-left.selected-comment
+      ::slotted(gr-comment-thread[comment-side='left']),
+    .selected-right.selected-comment
+      ::slotted(gr-comment-thread[comment-side='right']) {
+      -webkit-user-select: text;
+      -moz-user-select: text;
+      -ms-user-select: text;
+      user-select: text;
+    }
+    /** END: Select and copy for Polymer 2 */
 
-      .whitespace-change-only-message {
-        background-color: var(--diff-context-control-background-color);
-        border: 1px solid var(--diff-context-control-border-color);
-        text-align: center;
-      }
-    </style>
-    <style include="gr-syntax-theme">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-ranged-comment-theme">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div id="diffHeader" hidden\$="[[_computeDiffHeaderHidden(_diffHeaderItems)]]">
-      <template is="dom-repeat" items="[[_diffHeaderItems]]">
-        <div>[[item]]</div>
-      </template>
-    </div>
-    <div class\$="[[_computeContainerClass(loggedIn, viewMode, displayLine)]]" on-tap="_handleTap">
-      <gr-diff-selection diff="[[diff]]">
-        <gr-diff-highlight id="highlights" logged-in="[[loggedIn]]" comment-ranges="{{_commentRanges}}">
-          <gr-diff-builder id="diffBuilder" comment-ranges="[[_commentRanges]]" coverage-ranges="[[coverageRanges]]" project-name="[[projectName]]" diff="[[diff]]" path="[[path]]" change-num="[[changeNum]]" patch-num="[[patchRange.patchNum]]" view-mode="[[viewMode]]" is-image-diff="[[isImageDiff]]" base-image="[[baseImage]]" layers="[[layers]]" revision-image="[[revisionImage]]">
-            <table id="diffTable" class\$="[[_diffTableClass]]" role="presentation"></table>
+    .whitespace-change-only-message {
+      background-color: var(--diff-context-control-background-color);
+      border: 1px solid var(--diff-context-control-border-color);
+      text-align: center;
+    }
+  </style>
+  <style include="gr-syntax-theme">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-ranged-comment-theme">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div id="diffHeader" hidden$="[[_computeDiffHeaderHidden(_diffHeaderItems)]]">
+    <template is="dom-repeat" items="[[_diffHeaderItems]]">
+      <div>[[item]]</div>
+    </template>
+  </div>
+  <div
+    class$="[[_computeContainerClass(loggedIn, viewMode, displayLine)]]"
+    on-tap="_handleTap"
+  >
+    <gr-diff-selection diff="[[diff]]">
+      <gr-diff-highlight
+        id="highlights"
+        logged-in="[[loggedIn]]"
+        comment-ranges="{{_commentRanges}}"
+      >
+        <gr-diff-builder
+          id="diffBuilder"
+          comment-ranges="[[_commentRanges]]"
+          coverage-ranges="[[coverageRanges]]"
+          project-name="[[projectName]]"
+          diff="[[diff]]"
+          path="[[path]]"
+          change-num="[[changeNum]]"
+          patch-num="[[patchRange.patchNum]]"
+          view-mode="[[viewMode]]"
+          is-image-diff="[[isImageDiff]]"
+          base-image="[[baseImage]]"
+          layers="[[layers]]"
+          revision-image="[[revisionImage]]"
+        >
+          <table
+            id="diffTable"
+            class$="[[_diffTableClass]]"
+            role="presentation"
+          ></table>
 
-            <template is="dom-if" if="[[showNoChangeMessage(loading, prefs, _diffLength)]]">
-              <div class="whitespace-change-only-message">
-                This file only contains whitespace changes.
-                Modify the whitespace setting to see the changes.
-              </div>
-            </template>
-          </gr-diff-builder>
-        </gr-diff-highlight>
-      </gr-diff-selection>
-    </div>
-    <div class\$="[[_computeNewlineWarningClass(_newlineWarning, loading)]]">
-      [[_newlineWarning]]
-    </div>
-    <div id="loadingError" class\$="[[_computeErrorClass(errorMessage)]]">
-      [[errorMessage]]
-    </div>
-    <div id="sizeWarning" class\$="[[_computeWarningClass(_showWarning)]]">
-      <p>
-        Prevented render because "Whole file" is enabled and this diff is very
-        large (about [[_diffLength]] lines).
-      </p>
-      <gr-button on-click="_handleLimitedBypass">
-        Render with limited context
-      </gr-button>
-      <gr-button on-click="_handleFullBypass">
-        Render anyway (may be slow)
-      </gr-button>
-    </div>
+          <template
+            is="dom-if"
+            if="[[showNoChangeMessage(loading, prefs, _diffLength)]]"
+          >
+            <div class="whitespace-change-only-message">
+              This file only contains whitespace changes. Modify the whitespace
+              setting to see the changes.
+            </div>
+          </template>
+        </gr-diff-builder>
+      </gr-diff-highlight>
+    </gr-diff-selection>
+  </div>
+  <div class$="[[_computeNewlineWarningClass(_newlineWarning, loading)]]">
+    [[_newlineWarning]]
+  </div>
+  <div id="loadingError" class$="[[_computeErrorClass(errorMessage)]]">
+    [[errorMessage]]
+  </div>
+  <div id="sizeWarning" class$="[[_computeWarningClass(_showWarning)]]">
+    <p>
+      Prevented render because "Whole file" is enabled and this diff is very
+      large (about [[_diffLength]] lines).
+    </p>
+    <gr-button on-click="_handleLimitedBypass">
+      Render with limited context
+    </gr-button>
+    <gr-button on-click="_handleFullBypass">
+      Render anyway (may be slow)
+    </gr-button>
+  </div>
 `;
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 f7769f5..f6d80ed 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -34,7 +35,7 @@
 <script type="module">
 import '../../../test/common-test-setup.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import {getMockDiffResponse} from '../../../test/mock-diff-response.js';
+import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
 import './gr-diff.js';
 import {dom, flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {GrDiffBuilderImage} from '../gr-diff-builder/gr-diff-builder-image.js';
@@ -317,7 +318,7 @@
             assert.isOk(leftImage);
             assert.equal(leftImage.getAttribute('src'),
                 'data:image/bmp;base64, ' + mockFile1.body);
-            assert.equal(leftLabelContent.textContent, '1×1 image/bmp');
+            assert.equal(leftLabelContent.textContent, '1\u00d71 image/bmp');// \u00d7 - '×'
             leftLoaded = true;
             if (rightLoaded) {
               element.removeEventListener('render', rendered);
@@ -329,7 +330,7 @@
             assert.isOk(rightImage);
             assert.equal(rightImage.getAttribute('src'),
                 'data:image/bmp;base64, ' + mockFile2.body);
-            assert.equal(rightLabelContent.textContent, '1×1 image/bmp');
+            assert.equal(rightLabelContent.textContent, '1\u00d71 image/bmp');// \u00d7 - '×'
 
             rightLoaded = true;
             if (leftLoaded) {
@@ -411,7 +412,7 @@
             assert.isOk(leftImage);
             assert.equal(leftImage.getAttribute('src'),
                 'data:image/bmp;base64, ' + mockFile1.body);
-            assert.equal(leftLabelContent.textContent, '1×1 image/bmp');
+            assert.equal(leftLabelContent.textContent, '1\u00d71 image/bmp');// \u00d7 - '×'
             leftLoaded = true;
             if (rightLoaded) {
               element.removeEventListener('render', rendered);
@@ -423,7 +424,7 @@
             assert.isOk(rightImage);
             assert.equal(rightImage.getAttribute('src'),
                 'data:image/bmp;base64, ' + mockFile2.body);
-            assert.equal(rightLabelContent.textContent, '1×1 image/bmp');
+            assert.equal(rightLabelContent.textContent, '1\u00d71 image/bmp');// \u00d7 - '×'
 
             rightLoaded = true;
             if (leftLoaded) {
@@ -856,7 +857,7 @@
       element.showNewlineWarningLeft = true;
       element.showNewlineWarningRight = true;
       assert.include(getWarning(element),
-          NO_NEWLINE_BASE + ' — ' + NO_NEWLINE_REVISION);
+          NO_NEWLINE_BASE + ' \u2014 ' + NO_NEWLINE_REVISION);// \u2014 - '—'
     });
 
     suite('showNewlineWarningLeft', () => {
@@ -1013,6 +1014,46 @@
     assertDiffTableWithContent();
   });
 
+  suite('selection test', () => {
+    test('user-select set correctly on side-by-side view', () => {
+      const content = [{
+        a: ['all work and no play make andybons a dull boy'],
+        b: ['elgoog elgoog elgoog'],
+      }, {
+        ab: [
+          'Non eram nescius, Brute, cum, quae summis ingeniis ',
+          'exquisitaque doctrina philosophi Graeco sermone tractavissent',
+        ],
+      }];
+      setupSampleDiff({content});
+      flushAsynchronousOperations();
+      const diffLine = element.shadowRoot.querySelectorAll('.contentText')[2];
+      assert.equal(getComputedStyle(diffLine).userSelect, 'none');
+      // click to mark it as selected
+      MockInteractions.tap(diffLine);
+      assert.equal(getComputedStyle(diffLine).userSelect, 'text');
+    });
+
+    test('user-select set correctly on unified view', () => {
+      const content = [{
+        a: ['all work and no play make andybons a dull boy'],
+        b: ['elgoog elgoog elgoog'],
+      }, {
+        ab: [
+          'Non eram nescius, Brute, cum, quae summis ingeniis ',
+          'exquisitaque doctrina philosophi Graeco sermone tractavissent',
+        ],
+      }];
+      setupSampleDiff({content});
+      element.viewMode = 'UNIFIED_DIFF';
+      flushAsynchronousOperations();
+      const diffLine = element.shadowRoot.querySelectorAll('.contentText')[2];
+      assert.equal(getComputedStyle(diffLine).userSelect, 'none');
+      MockInteractions.tap(diffLine);
+      assert.equal(getComputedStyle(diffLine).userSelect, 'text');
+    });
+  });
+
   suite('whitespace changes only message', () => {
     test('show the message if ignore_whitespace is criteria matches', () => {
       setupSampleDiff({content: [{skip: 100}]});
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 8bdc1a8..53bfae8 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-dropdown-list/gr-dropdown-list.js';
 import '../../shared/gr-select/gr-select.js';
@@ -38,7 +36,7 @@
  *
  * @property {string} patchNum
  * @property {string} basePatchNum
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrPatchRangeSelect extends mixinBehaviors( [
   PatchSetBehavior,
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.js
index 5779a903..1d4b440 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.js
@@ -17,57 +17,69 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        align-items: center;
-        display: flex;
-      }
-      select {
-        max-width: 15em;
-      }
-      .arrow {
+  <style include="shared-styles">
+    :host {
+      align-items: center;
+      display: flex;
+    }
+    select {
+      max-width: 15em;
+    }
+    .arrow {
+      color: var(--deemphasized-text-color);
+      margin: 0 var(--spacing-m);
+    }
+    gr-dropdown-list {
+      --trigger-style: {
         color: var(--deemphasized-text-color);
-        margin: 0 var(--spacing-m);
+        text-transform: none;
+        font-family: var(--font-family);
+      }
+      --trigger-hover-color: rgba(0, 0, 0, 0.6);
+    }
+    @media screen and (max-width: 50em) {
+      .filesWeblinks {
+        display: none;
       }
       gr-dropdown-list {
-        --trigger-style: {
-          color: var(--deemphasized-text-color);
-          text-transform: none;
-          font-family: var(--font-family);
+        --native-select-style: {
+          max-width: 5.25em;
         }
-        --trigger-hover-color: rgba(0,0,0,.6);
-      }
-      @media screen and (max-width: 50em) {
-        .filesWeblinks {
-          display: none;
-        }
-        gr-dropdown-list {
-          --native-select-style: {
-            max-width: 5.25em;
-          }
-          --dropdown-content-stype: {
-            max-width: 300px;
-          }
+        --dropdown-content-stype: {
+          max-width: 300px;
         }
       }
-    </style>
-    <span class="patchRange">
-      <gr-dropdown-list id="basePatchDropdown" value="[[basePatchNum]]" on-value-change="_handlePatchChange" items="[[_baseDropdownContent]]">
-      </gr-dropdown-list>
-    </span>
-    <span is="dom-if" if="[[filesWeblinks.meta_a]]" class="filesWeblinks">
-      <template is="dom-repeat" items="[[filesWeblinks.meta_a]]" as="weblink">
-        <a target="_blank" rel="noopener" href\$="[[weblink.url]]">[[weblink.name]]</a>
+    }
+  </style>
+  <span class="patchRange">
+    <gr-dropdown-list
+      id="basePatchDropdown"
+      value="[[basePatchNum]]"
+      on-value-change="_handlePatchChange"
+      items="[[_baseDropdownContent]]"
+    >
+    </gr-dropdown-list>
+  </span>
+  <span is="dom-if" if="[[filesWeblinks.meta_a]]" class="filesWeblinks">
+    <template is="dom-repeat" items="[[filesWeblinks.meta_a]]" as="weblink">
+      <a target="_blank" rel="noopener" href$="[[weblink.url]]"
+        >[[weblink.name]]</a
+      >
+    </template>
+  </span>
+  <span class="arrow">→</span>
+  <span class="patchRange">
+    <gr-dropdown-list
+      id="patchNumDropdown"
+      value="[[patchNum]]"
+      on-value-change="_handlePatchChange"
+      items="[[_patchDropdownContent]]"
+    >
+    </gr-dropdown-list>
+    <span is="dom-if" if="[[filesWeblinks.meta_b]]" class="filesWeblinks">
+      <template is="dom-repeat" items="[[filesWeblinks.meta_b]]" as="weblink">
+        <a target="_blank" href$="[[weblink.url]]">[[weblink.name]]</a>
       </template>
     </span>
-    <span class="arrow">→</span>
-    <span class="patchRange">
-      <gr-dropdown-list id="patchNumDropdown" value="[[patchNum]]" on-value-change="_handlePatchChange" items="[[_patchDropdownContent]]">
-      </gr-dropdown-list>
-      <span is="dom-if" if="[[filesWeblinks.meta_b]]" class="filesWeblinks">
-        <template is="dom-repeat" items="[[filesWeblinks.meta_b]]" as="weblink">
-          <a target="_blank" href\$="[[weblink.url]]">[[weblink.name]]</a>
-        </template>
-      </span>
-    </span>
+  </span>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
index af2458a..218ca7e 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-patch-range-select</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -44,7 +45,7 @@
 import '../gr-comment-api/gr-comment-api.js';
 import '../../shared/revision-info/revision-info.js';
 import './gr-patch-range-select.js';
-import '../gr-comment-api/gr-comment-api-mock_test.js';
+import '../../../test/mocks/comment-api.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {RevisionInfo} from '../../shared/revision-info/revision-info.js';
 suite('gr-patch-range-select tests', () => {
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 e6c49be..01b4859 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {GrAnnotation} from '../gr-diff-highlight/gr-annotation.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -29,7 +27,7 @@
 const RANGE_HIGHLIGHT = 'style-scope gr-diff range';
 const HOVER_HIGHLIGHT = 'style-scope gr-diff rangeHighlight';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrRangedCommentLayer extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.js b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.js
index 29757e5..3ed33d1 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_html.js
@@ -16,6 +16,4 @@
  */
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
-export const htmlTemplate = html`
-
-`;
+export const htmlTemplate = html``;
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
index eec6125..ff1f4a7 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-ranged-comment-layer</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 f16db3b..79f937c 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-tooltip/gr-tooltip.js';
 import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
@@ -25,7 +23,7 @@
 import {htmlTemplate} from './gr-selection-action-box_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrSelectionActionBox extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.js b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.js
index 670a755..e7795b9 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_html.js
@@ -17,13 +17,17 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        cursor: pointer;
-        font-family: var(--font-family);
-        position: absolute;
-        white-space: nowrap;
-      }
-    </style>
-    <gr-tooltip id="tooltip" text="Press c to comment" position-below="[[positionBelow]]"></gr-tooltip>
+  <style include="shared-styles">
+    :host {
+      cursor: pointer;
+      font-family: var(--font-family);
+      position: absolute;
+      white-space: nowrap;
+    }
+  </style>
+  <gr-tooltip
+    id="tooltip"
+    text="Press c to comment"
+    position-below="[[positionBelow]]"
+  ></gr-tooltip>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
index d934877..ff6fba7 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-selection-action-box</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 f1e930f..538a890 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-lib-loader/gr-lib-loader.js';
 import {GrAnnotation} from '../gr-diff-highlight/gr-annotation.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -140,7 +138,7 @@
 const GO_BACKSLASH_LITERAL = '\'\\\\\'';
 const GLOBAL_LT_PATTERN = /</g;
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrSyntaxLayer extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.js
index 183cbd1c..433f814 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_html.js
@@ -17,5 +17,5 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-lib-loader id="libLoader"></gr-lib-loader>
+  <gr-lib-loader id="libLoader"></gr-lib-loader>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
index 07f95f4..638b188 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-syntax-layer</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -32,7 +33,7 @@
 
 <script type="module">
 import '../../../test/common-test-setup.js';
-import {getMockDiffResponse} from '../../../test/mock-diff-response.js';
+import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
 import './gr-syntax-layer.js';
 import {GrAnnotation} from '../gr-diff-highlight/gr-annotation.js';
 import {GrDiffLine} from '../gr-diff/gr-diff-line.js';
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 4d66699..01268b1 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/gr-table-styles.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-list-view/gr-list-view.js';
@@ -28,7 +26,7 @@
 import {ListViewBehavior} from '../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDocumentationSearch extends mixinBehaviors( [
   ListViewBehavior,
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.js b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.js
index ced351a..b637b75 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.js
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.js
@@ -17,34 +17,42 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-table-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <gr-list-view filter="[[_filter]]" items="false" offset="0" loading="[[_loading]]" path="[[_path]]">
-      <table id="list" class="genericList">
-        <tbody><tr class="headerRow">
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-table-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <gr-list-view
+    filter="[[_filter]]"
+    items="false"
+    offset="0"
+    loading="[[_loading]]"
+    path="[[_path]]"
+  >
+    <table id="list" class="genericList">
+      <tbody>
+        <tr class="headerRow">
           <th class="name topHeader">Name</th>
           <th class="name topHeader"></th>
           <th class="name topHeader"></th>
         </tr>
-        <tr id="loading" class\$="loadingMsg [[computeLoadingClass(_loading)]]">
+        <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
           <td>Loading...</td>
         </tr>
-        </tbody><tbody class\$="[[computeLoadingClass(_loading)]]">
-          <template is="dom-repeat" items="[[_documentationSearches]]">
-            <tr class="table">
-              <td class="name">
-                <a href\$="[[_computeSearchUrl(item.url)]]">[[item.title]]</a>
-              </td>
-              <td></td>
-              <td></td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-    </gr-list-view>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </tbody>
+      <tbody class$="[[computeLoadingClass(_loading)]]">
+        <template is="dom-repeat" items="[[_documentationSearches]]">
+          <tr class="table">
+            <td class="name">
+              <a href$="[[_computeSearchUrl(item.url)]]">[[item.title]]</a>
+            </td>
+            <td></td>
+            <td></td>
+          </tr>
+        </template>
+      </tbody>
+    </table>
+  </gr-list-view>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html
index 36aca86..d0581ef 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-documentation-search</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 09f4abf..9acfd72 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
@@ -14,15 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-default-editor_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrDefaultEditor extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.js b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.js
index e7fc6fd..7368289 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_html.js
@@ -17,21 +17,25 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      textarea {
-        border: none;
-        box-sizing: border-box;
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-code);
-        line-height: var(--line-height-code);
-        min-height: 60vh;
-        resize: none;
-        white-space: pre;
-        width: 100%;
-      }
-      textarea:focus {
-        outline: none;
-      }
-    </style>
-    <textarea id="textarea" value="[[fileContent]]" on-input="_handleTextareaInput"></textarea>
+  <style include="shared-styles">
+    textarea {
+      border: none;
+      box-sizing: border-box;
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-code);
+      line-height: var(--line-height-code);
+      min-height: 60vh;
+      resize: none;
+      white-space: pre;
+      width: 100%;
+    }
+    textarea:focus {
+      outline: none;
+    }
+  </style>
+  <textarea
+    id="textarea"
+    value="[[fileContent]]"
+    on-input="_handleTextareaInput"
+  ></textarea>
 `;
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html
index 30cfe39..229c6c3 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html
@@ -16,6 +16,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-default-editor</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 89491c3..8fccbc2 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../shared/gr-autocomplete/gr-autocomplete.js';
 import '../../shared/gr-button/gr-button.js';
@@ -35,7 +33,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrEditControls extends mixinBehaviors( [
   PatchSetBehavior,
@@ -219,14 +217,11 @@
       this._closeDialog(this.$.openDialog, true);
       return;
     }
-    this.$.restAPI.saveFileUploadChangeEdit(this.change._number, path,
+    return this.$.restAPI.saveFileUploadChangeEdit(this.change._number, path,
         fileData).then(res => {
       if (!res.ok) { return; }
-
       this._closeDialog(this.$.openDialog, true);
-      const url = GerritNav.getUrlForChange(
-          this.change, this.patchNum, undefined, true);
-      GerritNav.navigateToRelativeUrl(url);
+      GerritNav.navigateToChange(this.change);
     });
   }
 
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.js
index 482ab1d..02639c0 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_html.js
@@ -17,110 +17,167 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        align-items: center;
-        display: flex;
-        justify-content: flex-end;
-      }
-      .invisible {
-        display: none;
-      }
-      gr-button {
-        margin-left: var(--spacing-l);
-        text-decoration: none;
-      }
+  <style include="shared-styles">
+    :host {
+      align-items: center;
+      display: flex;
+      justify-content: flex-end;
+    }
+    .invisible {
+      display: none;
+    }
+    gr-button {
+      margin-left: var(--spacing-l);
+      text-decoration: none;
+    }
+    gr-dialog {
+      width: 50em;
+    }
+    gr-dialog .main {
+      width: 100%;
+    }
+    gr-dialog .main > iron-input {
+      width: 100%;
+    }
+    input {
+      border: 1px solid var(--border-color);
+      border-radius: var(--border-radius);
+      margin: var(--spacing-m) 0;
+      padding: var(--spacing-s);
+      width: 100%;
+      box-sizing: content-box;
+    }
+    #fileUploadBrowse {
+      margin-left: 0;
+    }
+    #dragDropArea {
+      border: 2px dashed var(--border-color);
+      border-radius: var(--border-radius);
+      margin-top: var(--spacing-l);
+      padding: var(--spacing-xxl) var(--spacing-xxl);
+      text-align: center;
+    }
+    #dragDropArea > p {
+      font-weight: var(--font-weight-bold);
+      padding: var(--spacing-s);
+    }
+    @media screen and (max-width: 50em) {
       gr-dialog {
-        width: 50em;
+        width: 100vw;
       }
-      gr-dialog .main {
-        width: 100%;
-      }
-      gr-dialog .main > iron-input{
-        width: 100%;
-      }
-      input {
-        border: 1px solid var(--border-color);
-        border-radius: var(--border-radius);
-        margin: var(--spacing-m) 0;
-        padding: var(--spacing-s);
-        width: 100%;
-        box-sizing: content-box;
-      }
-      #fileUploadBrowse {
-        margin-left: 0;
-      }
-      #dragDropArea {
-        border: 2px dashed var(--border-color);
-        border-radius: var(--border-radius);
-        margin-top: var(--spacing-l);
-        padding: var(--spacing-xxl) var(--spacing-xxl);
-        text-align: center;
-      }
-      #dragDropArea > p {
-        font-weight: var(--font-weight-bold);
-        padding: var(--spacing-s);
-      }
-      @media screen and (max-width: 50em) {
-        gr-dialog {
-          width: 100vw;
-        }
-      }
-    </style>
-    <template is="dom-repeat" items="[[_actions]]" as="action">
-      <gr-button id\$="[[action.id]]" class\$="[[_computeIsInvisible(action.id, hiddenActions)]]" link="" on-click="_handleTap">[[action.label]]</gr-button>
-    </template>
-    <gr-overlay id="overlay" with-backdrop="">
-      <gr-dialog id="openDialog" class="invisible dialog" disabled\$="[[!_isValidPath(_path)]]" confirm-label="Confirm" confirm-on-enter="" on-confirm="_handleOpenConfirm" on-cancel="_handleDialogCancel">
-        <div class="header" slot="header">
-          Add a new file or open an existing file
+    }
+  </style>
+  <template is="dom-repeat" items="[[_actions]]" as="action">
+    <gr-button
+      id$="[[action.id]]"
+      class$="[[_computeIsInvisible(action.id, hiddenActions)]]"
+      link=""
+      on-click="_handleTap"
+      >[[action.label]]</gr-button
+    >
+  </template>
+  <gr-overlay id="overlay" with-backdrop="">
+    <gr-dialog
+      id="openDialog"
+      class="invisible dialog"
+      disabled$="[[!_isValidPath(_path)]]"
+      confirm-label="Confirm"
+      confirm-on-enter=""
+      on-confirm="_handleOpenConfirm"
+      on-cancel="_handleDialogCancel"
+    >
+      <div class="header" slot="header">
+        Add a new file or open an existing file
+      </div>
+      <div class="main" slot="main">
+        <gr-autocomplete
+          placeholder="Enter an existing or new full file path."
+          query="[[_query]]"
+          text="{{_path}}"
+        ></gr-autocomplete>
+        <div id="dragDropArea" on-drop="_handleDragAndDropUpload">
+          <p>Drag and drop a file here</p>
+          <p>or</p>
+          <p>
+            <iron-input>
+              <input
+                is="iron-input"
+                id="fileUploadInput"
+                type="file"
+                on-change="_handleFileUploadChanged"
+                hidden
+              />
+            </iron-input>
+            <label for="fileUploadInput">
+              <gr-button id="fileUploadBrowse">Browse</gr-button>
+            </label>
+          </p>
         </div>
-        <div class="main" slot="main">
-          <gr-autocomplete placeholder="Enter an existing or new full file path." query="[[_query]]" text="{{_path}}"></gr-autocomplete>
-          <div id="dragDropArea"
-               contenteditable="true"
-               on-drop="_handleDragAndDropUpload">
-            <p>Drag and drop a file here</p>
-            <p>or</p>
-            <p>
-              <iron-input>
-                <input id="fileUploadInput"
-                       type="file"
-                       on-change="_handleFileUploadChanged"
-                       slot="input">
-              </iron-input>
-              <label for="fileUploadInput">
-                <gr-button id="fileUploadBrowse">
-                  Browse
-                </gr-button>
-              </label>
-            </p>
-          </div>
-        </div>
-      </gr-dialog>
-      <gr-dialog id="deleteDialog" class="invisible dialog" disabled\$="[[!_isValidPath(_path)]]" confirm-label="Delete" confirm-on-enter="" on-confirm="_handleDeleteConfirm" on-cancel="_handleDialogCancel">
-        <div class="header" slot="header">Delete a file from the repo</div>
-        <div class="main" slot="main">
-          <gr-autocomplete placeholder="Enter an existing full file path." query="[[_query]]" text="{{_path}}"></gr-autocomplete>
-        </div>
-      </gr-dialog>
-      <gr-dialog id="renameDialog" class="invisible dialog" disabled\$="[[!_computeRenameDisabled(_path, _newPath)]]" confirm-label="Rename" confirm-on-enter="" on-confirm="_handleRenameConfirm" on-cancel="_handleDialogCancel">
-        <div class="header" slot="header">Rename a file in the repo</div>
-        <div class="main" slot="main">
-          <gr-autocomplete placeholder="Enter an existing full file path." query="[[_query]]" text="{{_path}}"></gr-autocomplete>
-          <iron-input class="newPathIronInput" bind-value="{{_newPath}}" placeholder="Enter the new path.">
-            <input class="newPathInput" is="iron-input" bind-value="{{_newPath}}" placeholder="Enter the new path.">
-          </iron-input>
-        </div>
-      </gr-dialog>
-      <gr-dialog id="restoreDialog" class="invisible dialog" confirm-label="Restore" confirm-on-enter="" on-confirm="_handleRestoreConfirm" on-cancel="_handleDialogCancel">
-        <div class="header" slot="header">Restore this file?</div>
-        <div class="main" slot="main">
-          <iron-input disabled="" bind-value="{{_path}}">
-            <input is="iron-input" disabled="" bind-value="{{_path}}">
-          </iron-input>
-        </div>
-      </gr-dialog>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </div>
+    </gr-dialog>
+    <gr-dialog
+      id="deleteDialog"
+      class="invisible dialog"
+      disabled$="[[!_isValidPath(_path)]]"
+      confirm-label="Delete"
+      confirm-on-enter=""
+      on-confirm="_handleDeleteConfirm"
+      on-cancel="_handleDialogCancel"
+    >
+      <div class="header" slot="header">Delete a file from the repo</div>
+      <div class="main" slot="main">
+        <gr-autocomplete
+          placeholder="Enter an existing full file path."
+          query="[[_query]]"
+          text="{{_path}}"
+        ></gr-autocomplete>
+      </div>
+    </gr-dialog>
+    <gr-dialog
+      id="renameDialog"
+      class="invisible dialog"
+      disabled$="[[!_computeRenameDisabled(_path, _newPath)]]"
+      confirm-label="Rename"
+      confirm-on-enter=""
+      on-confirm="_handleRenameConfirm"
+      on-cancel="_handleDialogCancel"
+    >
+      <div class="header" slot="header">Rename a file in the repo</div>
+      <div class="main" slot="main">
+        <gr-autocomplete
+          placeholder="Enter an existing full file path."
+          query="[[_query]]"
+          text="{{_path}}"
+        ></gr-autocomplete>
+        <iron-input
+          class="newPathIronInput"
+          bind-value="{{_newPath}}"
+          placeholder="Enter the new path."
+        >
+          <input
+            class="newPathInput"
+            is="iron-input"
+            bind-value="{{_newPath}}"
+            placeholder="Enter the new path."
+          />
+        </iron-input>
+      </div>
+    </gr-dialog>
+    <gr-dialog
+      id="restoreDialog"
+      class="invisible dialog"
+      confirm-label="Restore"
+      confirm-on-enter=""
+      on-confirm="_handleRestoreConfirm"
+      on-cancel="_handleDialogCancel"
+    >
+      <div class="header" slot="header">Restore this file?</div>
+      <div class="main" slot="main">
+        <iron-input disabled="" bind-value="{{_path}}">
+          <input is="iron-input" disabled="" bind-value="{{_path}}" />
+        </iron-input>
+      </div>
+    </gr-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
index 4b35d1b..1267525 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
@@ -16,6 +16,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-edit-controls</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -366,6 +367,36 @@
     });
   });
 
+  suite('save file upload', () => {
+    let navStub;
+    let fileStub;
+
+    setup(() => {
+      navStub = sandbox.stub(GerritNav, 'navigateToChange');
+      fileStub = sandbox.stub(element.$.restAPI, 'saveFileUploadChangeEdit');
+    });
+
+    test('_handleUploadConfirm', () => {
+      fileStub.returns(Promise.resolve({ok: true}));
+
+      element.change = {
+        _number: '1',
+        project: 'project',
+        revisions: {
+          abcd: {_number: 1},
+          efgh: {_number: 2},
+        },
+        current_revision: 'efgh',
+      };
+
+      element._handleUploadConfirm('test.php', 'base64').then(() => {
+        assert.equal(
+            navStub.lastCall.args,
+            '/c/project/+/1');
+      });
+    });
+  });
+
   test('openOpenDialog', done => {
     element.openOpenDialog('test/path.cpp')
         .then(() => {
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 8a24e23..b144bad 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-dropdown/gr-dropdown.js';
 import '../../../styles/shared-styles.js';
@@ -25,7 +23,7 @@
 import {htmlTemplate} from './gr-edit-file-controls_html.js';
 import {GrEditConstants} from '../gr-edit-constants.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrEditFileControls extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.js b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.js
index 7a7ba5d..ec0b8b4 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_html.js
@@ -17,29 +17,37 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        align-items: center;
-        display: flex;
-        justify-content: flex-end;
+  <style include="shared-styles">
+    :host {
+      align-items: center;
+      display: flex;
+      justify-content: flex-end;
+    }
+    #actions {
+      margin-right: var(--spacing-l);
+    }
+    gr-button,
+    gr-dropdown {
+      --gr-button: {
+        height: 1.8em;
       }
-      #actions {
-        margin-right: var(--spacing-l);
+    }
+    gr-dropdown {
+      --gr-dropdown-item: {
+        background-color: transparent;
+        border: none;
+        color: var(--link-color);
+        text-transform: uppercase;
       }
-      gr-button,
-      gr-dropdown {
-        --gr-button: {
-          height: 1.8em;
-        }
-      }
-      gr-dropdown {
-        --gr-dropdown-item: {
-          background-color: transparent;
-          border: none;
-          color: var(--link-color);
-          text-transform: uppercase;
-        }
-      }
-    </style>
-    <gr-dropdown id="actions" items="[[_fileActions]]" down-arrow="" vertical-offset="20" on-tap-item="_handleActionTap" link="">Actions</gr-dropdown>
+    }
+  </style>
+  <gr-dropdown
+    id="actions"
+    items="[[_fileActions]]"
+    down-arrow=""
+    vertical-offset="20"
+    on-tap-item="_handleActionTap"
+    link=""
+    >Actions</gr-dropdown
+  >
 `;
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html
index 94e18ff..e11a2bd 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html
@@ -16,6 +16,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-edit-file-controls</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 d2ffa56..886908a 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
 import '../../plugins/gr-endpoint-param/gr-endpoint-param.js';
 import '../../shared/gr-button/gr-button.js';
@@ -43,7 +41,7 @@
 const STORAGE_DEBOUNCE_INTERVAL_MS = 100;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrEditorView extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.js
index 72d29dd..9dc35b2 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.js
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_html.js
@@ -17,87 +17,110 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        background-color: var(--view-background-color);
+  <style include="shared-styles">
+    :host {
+      background-color: var(--view-background-color);
+    }
+    gr-fixed-panel {
+      background-color: var(--edit-mode-background-color);
+      border-bottom: 1px var(--border-color) solid;
+      z-index: 1;
+    }
+    header,
+    .subHeader {
+      align-items: center;
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    header gr-editable-label {
+      font-family: var(--header-font-family);
+      font-size: var(--font-size-h3);
+      font-weight: var(--font-weight-h3);
+      line-height: var(--line-height-h3);
+      --label-style: {
+        text-overflow: initial;
+        white-space: initial;
+        word-break: break-all;
       }
-      gr-fixed-panel {
-        background-color: var(--edit-mode-background-color);
-        border-bottom: 1px var(--border-color) solid;
-        z-index: 1;
+      --input-style: {
+        margin-top: var(--spacing-l);
       }
+    }
+    .textareaWrapper {
+      border: 1px solid var(--border-color);
+      border-radius: var(--border-radius);
+      margin: var(--spacing-l);
+    }
+    .textareaWrapper .editButtons {
+      display: none;
+    }
+    .controlGroup {
+      align-items: center;
+      display: flex;
+      font-family: var(--header-font-family);
+      font-size: var(--font-size-h3);
+      font-weight: var(--font-weight-h3);
+      line-height: var(--line-height-h3);
+    }
+    .rightControls {
+      justify-content: flex-end;
+    }
+    @media screen and (max-width: 50em) {
       header,
       .subHeader {
-        align-items: center;
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      header gr-editable-label {
-        font-family: var(--header-font-family);
-        font-size: var(--font-size-h3);
-        font-weight: var(--font-weight-h3);
-        line-height: var(--line-height-h3);
-        --label-style: {
-          text-overflow: initial;
-          white-space: initial;
-          word-break: break-all;
-        }
-        --input-style: {
-          margin-top: var(--spacing-l);
-        }
-      }
-      .textareaWrapper {
-        border: 1px solid var(--border-color);
-        border-radius: var(--border-radius);
-        margin: var(--spacing-l);
-      }
-      .textareaWrapper .editButtons {
-        display: none;
-      }
-      .controlGroup {
-        align-items: center;
-        display: flex;
-        font-family: var(--header-font-family);
-        font-size: var(--font-size-h3);
-        font-weight: var(--font-weight-h3);
-        line-height: var(--line-height-h3);
+        display: block;
       }
       .rightControls {
-        justify-content: flex-end;
+        float: right;
       }
-      @media screen and (max-width: 50em) {
-        header,
-        .subHeader {
-          display: block;
-        }
-        .rightControls {
-          float: right;
-        }
-      }
-    </style>
-    <gr-fixed-panel keep-on-scroll="">
-      <header>
-        <span class="controlGroup">
-          <span>Edit mode</span>
-          <span class="separator"></span>
-          <gr-editable-label label-text="File path" value="[[_path]]" placeholder="File path..." on-changed="_handlePathChanged"></gr-editable-label>
-        </span>
-        <span class="controlGroup rightControls">
-          <gr-button id="close" link="" on-click="_handleCloseTap">Close</gr-button>
-          <gr-button id="save" disabled\$="[[_saveDisabled]]" primary="" link="" on-click="_saveEdit">Save</gr-button>
-        </span>
-      </header>
-    </gr-fixed-panel>
-    <div class="textareaWrapper">
-      <gr-endpoint-decorator id="editorEndpoint" name="editor">
-        <gr-endpoint-param name="fileContent" value="[[_newContent]]"></gr-endpoint-param>
-        <gr-endpoint-param name="prefs" value="[[_prefs]]"></gr-endpoint-param>
-        <gr-endpoint-param name="fileType" value="[[_type]]"></gr-endpoint-param>
-        <gr-endpoint-param name="lineNum" value="[[_lineNum]]"></gr-endpoint-param>
-        <gr-default-editor id="file" file-content="[[_newContent]]"></gr-default-editor>
-      </gr-endpoint-decorator>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
+    }
+  </style>
+  <gr-fixed-panel keep-on-scroll="">
+    <header>
+      <span class="controlGroup">
+        <span>Edit mode</span>
+        <span class="separator"></span>
+        <gr-editable-label
+          label-text="File path"
+          value="[[_path]]"
+          placeholder="File path..."
+          on-changed="_handlePathChanged"
+        ></gr-editable-label>
+      </span>
+      <span class="controlGroup rightControls">
+        <gr-button id="close" link="" on-click="_handleCloseTap"
+          >Close</gr-button
+        >
+        <gr-button
+          id="save"
+          disabled$="[[_saveDisabled]]"
+          primary=""
+          link=""
+          on-click="_saveEdit"
+          >Save</gr-button
+        >
+      </span>
+    </header>
+  </gr-fixed-panel>
+  <div class="textareaWrapper">
+    <gr-endpoint-decorator id="editorEndpoint" name="editor">
+      <gr-endpoint-param
+        name="fileContent"
+        value="[[_newContent]]"
+      ></gr-endpoint-param>
+      <gr-endpoint-param name="prefs" value="[[_prefs]]"></gr-endpoint-param>
+      <gr-endpoint-param name="fileType" value="[[_type]]"></gr-endpoint-param>
+      <gr-endpoint-param
+        name="lineNum"
+        value="[[_lineNum]]"
+      ></gr-endpoint-param>
+      <gr-default-editor
+        id="file"
+        file-content="[[_newContent]]"
+      ></gr-default-editor>
+    </gr-endpoint-decorator>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-storage id="storage"></gr-storage>
 `;
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html
index 79fbfd00..e385854 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html
@@ -16,6 +16,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-editor-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
index 53c1e54..8a68d19 100644
--- a/polygerrit-ui/app/elements/gr-app-element.js
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../scripts/bundled-polymer.js';
 import '../styles/shared-styles.js';
 import '../styles/themes/app-theme.js';
 import './admin/gr-admin-view/gr-admin-view.js';
@@ -25,7 +24,6 @@
 import './core/gr-error-manager/gr-error-manager.js';
 import './core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js';
 import './core/gr-main-header/gr-main-header.js';
-import './core/gr-reporting/gr-reporting.js';
 import './core/gr-router/gr-router.js';
 import './core/gr-smart-search/gr-smart-search.js';
 import './diff/gr-diff-view/gr-diff-view.js';
@@ -48,9 +46,10 @@
 import {BaseUrlBehavior} from '../behaviors/base-url-behavior/base-url-behavior.js';
 import {KeyboardShortcutBehavior} from '../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 import {GerritNav} from './core/gr-navigation/gr-navigation.js';
+import {appContext} from '../services/app-context.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAppElement extends mixinBehaviors( [
   BaseUrlBehavior,
@@ -156,6 +155,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   created() {
     super.created();
@@ -176,11 +180,13 @@
   ready() {
     super.ready();
     this._updateLoginUrl();
-    this.$.reporting.appStarted();
+    this.reporting.appStarted();
     this.$.router.start();
 
     this.$.restAPI.getAccount().then(account => {
       this._account = account;
+      const role = account ? 'user' : 'guest';
+      this.reporting.reportLifeCycle(`Started as ${role}`);
     });
     this.$.restAPI.getConfig().then(config => {
       this._serverConfig = config;
@@ -411,7 +417,7 @@
     if (e.ctrlKey) key = 'ctrl+' + key;
     if (e.metaKey) key = 'meta+' + key;
     if (e.altKey) key = 'alt+' + key;
-    this.$.reporting.reportInteraction('shortcut-triggered', {
+    this.reporting.reportInteraction('shortcut-triggered', {
       key,
       from: event.path && event.path[0]
         && event.path[0].nodeName || 'unknown',
@@ -562,7 +568,7 @@
    * that would create a cyclic dependency.
    */
   _handleRpcLog(e) {
-    this.$.reporting.reportRpcTiming(e.detail.anonymizedUrl,
+    this.reporting.reportRpcTiming(e.detail.anonymizedUrl,
         e.detail.elapsed);
   }
 
diff --git a/polygerrit-ui/app/elements/gr-app-element_html.js b/polygerrit-ui/app/elements/gr-app-element_html.js
index 3951d9d..ad47d52 100644
--- a/polygerrit-ui/app/elements/gr-app-element_html.js
+++ b/polygerrit-ui/app/elements/gr-app-element_html.js
@@ -17,158 +17,217 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        background-color: var(--background-color-tertiary);
-        display: flex;
-        flex-direction: column;
-        min-height: 100%;
-      }
-      gr-fixed-panel {
-        /**
+  <style include="shared-styles">
+    :host {
+      background-color: var(--background-color-tertiary);
+      display: flex;
+      flex-direction: column;
+      min-height: 100%;
+    }
+    gr-fixed-panel {
+      /**
          * This one should be greater that the z-index in gr-diff-view
          * because gr-main-header contains overlay.
          */
-        z-index: 10;
-      }
-      gr-main-header,
-      footer {
-        color: var(--primary-text-color);
-      }
-      gr-main-header {
-        background: var(--header-background, var(--header-background-color, #eee));
-        padding: var(--header-padding);
-        border-bottom: var(--header-border-bottom);
-        border-image: var(--header-border-image);
-        border-right: 0;
-        border-left: 0;
-        border-top: 0;
-        box-shadow: var(--header-box-shadow);
-      }
-      footer {
-        background: var(--footer-background, var(--footer-background-color, #eee));
-        border-top: var(--footer-border-top);
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-m) var(--spacing-l);
-        z-index: 100;
-      }
-      main {
-        flex: 1;
-        padding-bottom: var(--spacing-xxl);
-        position: relative;
-      }
-      .errorView {
-        align-items: center;
-        display: none;
-        flex-direction: column;
-        justify-content: center;
-        position: absolute;
-        top: 0;
-        right: 0;
-        bottom: 0;
-        left: 0;
-      }
-      .errorView.show {
-        display: flex;
-      }
-      .errorEmoji {
-        font-size: 2.6rem;
-      }
-      .errorText,
-      .errorMoreInfo {
-        margin-top: var(--spacing-m);
-      }
-      .errorText {
-        font-family: var(--header-font-family);
-        font-size: var(--font-size-h3);
-        font-weight: var(--font-weight-h3);
-        line-height: var(--line-height-h3);
-      }
-      .errorMoreInfo {
-        color: var(--deemphasized-text-color);
-      }
-      .feedback {
-        color: var(--error-text-color);
-      }
-    </style>
-    <gr-endpoint-decorator name="banner"></gr-endpoint-decorator>
-    <gr-fixed-panel id="header">
-      <gr-main-header id="mainHeader" search-query="{{params.query}}" on-mobile-search="_mobileSearchToggle" login-url="[[_loginUrl]]">
-      </gr-main-header>
-    </gr-fixed-panel>
-    <main>
-      <gr-smart-search id="search" search-query="{{params.query}}" hidden="[[!mobileSearch]]">
-      </gr-smart-search>
-      <template is="dom-if" if="[[_showChangeListView]]" restamp="true">
-        <gr-change-list-view params="[[params]]" account="[[_account]]" view-state="{{_viewState.changeListView}}"></gr-change-list-view>
+      z-index: 10;
+    }
+    gr-main-header,
+    footer {
+      color: var(--primary-text-color);
+    }
+    gr-main-header {
+      background: var(
+        --header-background,
+        var(--header-background-color, #eee)
+      );
+      padding: var(--header-padding);
+      border-bottom: var(--header-border-bottom);
+      border-image: var(--header-border-image);
+      border-right: 0;
+      border-left: 0;
+      border-top: 0;
+      box-shadow: var(--header-box-shadow);
+    }
+    footer {
+      background: var(
+        --footer-background,
+        var(--footer-background-color, #eee)
+      );
+      border-top: var(--footer-border-top);
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-m) var(--spacing-l);
+      z-index: 100;
+    }
+    main {
+      flex: 1;
+      padding-bottom: var(--spacing-xxl);
+      position: relative;
+    }
+    .errorView {
+      align-items: center;
+      display: none;
+      flex-direction: column;
+      justify-content: center;
+      position: absolute;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+    }
+    .errorView.show {
+      display: flex;
+    }
+    .errorEmoji {
+      font-size: 2.6rem;
+    }
+    .errorText,
+    .errorMoreInfo {
+      margin-top: var(--spacing-m);
+    }
+    .errorText {
+      font-family: var(--header-font-family);
+      font-size: var(--font-size-h3);
+      font-weight: var(--font-weight-h3);
+      line-height: var(--line-height-h3);
+    }
+    .errorMoreInfo {
+      color: var(--deemphasized-text-color);
+    }
+    .feedback {
+      color: var(--error-text-color);
+    }
+  </style>
+  <gr-endpoint-decorator name="banner"></gr-endpoint-decorator>
+  <gr-fixed-panel id="header">
+    <gr-main-header
+      id="mainHeader"
+      search-query="{{params.query}}"
+      on-mobile-search="_mobileSearchToggle"
+      login-url="[[_loginUrl]]"
+    >
+    </gr-main-header>
+  </gr-fixed-panel>
+  <main>
+    <gr-smart-search
+      id="search"
+      search-query="{{params.query}}"
+      hidden="[[!mobileSearch]]"
+    >
+    </gr-smart-search>
+    <template is="dom-if" if="[[_showChangeListView]]" restamp="true">
+      <gr-change-list-view
+        params="[[params]]"
+        account="[[_account]]"
+        view-state="{{_viewState.changeListView}}"
+      ></gr-change-list-view>
+    </template>
+    <template is="dom-if" if="[[_showDashboardView]]" restamp="true">
+      <gr-dashboard-view
+        account="[[_account]]"
+        params="[[params]]"
+        view-state="{{_viewState.dashboardView}}"
+      ></gr-dashboard-view>
+    </template>
+    <template is="dom-if" if="[[_showChangeView]]" restamp="true">
+      <gr-change-view
+        params="[[params]]"
+        view-state="{{_viewState.changeView}}"
+        back-page="[[_lastSearchPage]]"
+      ></gr-change-view>
+    </template>
+    <template is="dom-if" if="[[_showEditorView]]" restamp="true">
+      <gr-editor-view params="[[params]]"></gr-editor-view>
+    </template>
+    <template is="dom-if" if="[[_showDiffView]]" restamp="true">
+      <gr-diff-view
+        params="[[params]]"
+        change-view-state="{{_viewState.changeView}}"
+      ></gr-diff-view>
+    </template>
+    <template is="dom-if" if="[[_showSettingsView]]" restamp="true">
+      <gr-settings-view
+        params="[[params]]"
+        on-account-detail-update="_handleAccountDetailUpdate"
+      >
+      </gr-settings-view>
+    </template>
+    <template is="dom-if" if="[[_showAdminView]]" restamp="true">
+      <gr-admin-view path="[[_path]]" params="[[params]]"></gr-admin-view>
+    </template>
+    <template is="dom-if" if="[[_showPluginScreen]]" restamp="true">
+      <gr-endpoint-decorator name="[[_pluginScreenName]]">
+        <gr-endpoint-param
+          name="token"
+          value="[[params.screen]]"
+        ></gr-endpoint-param>
+      </gr-endpoint-decorator>
+    </template>
+    <template is="dom-if" if="[[_showCLAView]]" restamp="true">
+      <gr-cla-view></gr-cla-view>
+    </template>
+    <template is="dom-if" if="[[_showDocumentationSearch]]" restamp="true">
+      <gr-documentation-search params="[[params]]"> </gr-documentation-search>
+    </template>
+    <div id="errorView" class="errorView">
+      <div class="errorEmoji">[[_lastError.emoji]]</div>
+      <div class="errorText">[[_lastError.text]]</div>
+      <div class="errorMoreInfo">[[_lastError.moreInfo]]</div>
+    </div>
+  </main>
+  <footer r="contentinfo">
+    <div>
+      Powered by
+      <a href="https://www.gerritcodereview.com/" rel="noopener" target="_blank"
+        >Gerrit Code Review</a
+      >
+      ([[_version]])
+      <gr-endpoint-decorator name="footer-left"></gr-endpoint-decorator>
+    </div>
+    <div>
+      <template is="dom-if" if="[[_feedbackUrl]]">
+        <a
+          class="feedback"
+          href$="[[_feedbackUrl]]"
+          rel="noopener"
+          target="_blank"
+          >Report bug</a
+        >
+        |
       </template>
-      <template is="dom-if" if="[[_showDashboardView]]" restamp="true">
-        <gr-dashboard-view account="[[_account]]" params="[[params]]" view-state="{{_viewState.dashboardView}}"></gr-dashboard-view>
-      </template>
-      <template is="dom-if" if="[[_showChangeView]]" restamp="true">
-        <gr-change-view params="[[params]]" view-state="{{_viewState.changeView}}" back-page="[[_lastSearchPage]]"></gr-change-view>
-      </template>
-      <template is="dom-if" if="[[_showEditorView]]" restamp="true">
-        <gr-editor-view params="[[params]]"></gr-editor-view>
-      </template>
-      <template is="dom-if" if="[[_showDiffView]]" restamp="true">
-          <gr-diff-view params="[[params]]" change-view-state="{{_viewState.changeView}}"></gr-diff-view>
-        </template>
-      <template is="dom-if" if="[[_showSettingsView]]" restamp="true">
-        <gr-settings-view params="[[params]]" on-account-detail-update="_handleAccountDetailUpdate">
-        </gr-settings-view>
-      </template>
-      <template is="dom-if" if="[[_showAdminView]]" restamp="true">
-        <gr-admin-view path="[[_path]]" params="[[params]]"></gr-admin-view>
-      </template>
-      <template is="dom-if" if="[[_showPluginScreen]]" restamp="true">
-        <gr-endpoint-decorator name="[[_pluginScreenName]]">
-          <gr-endpoint-param name="token" value="[[params.screen]]"></gr-endpoint-param>
-        </gr-endpoint-decorator>
-      </template>
-      <template is="dom-if" if="[[_showCLAView]]" restamp="true">
-        <gr-cla-view></gr-cla-view>
-      </template>
-      <template is="dom-if" if="[[_showDocumentationSearch]]" restamp="true">
-        <gr-documentation-search params="[[params]]">
-        </gr-documentation-search>
-      </template>
-      <div id="errorView" class="errorView">
-        <div class="errorEmoji">[[_lastError.emoji]]</div>
-        <div class="errorText">[[_lastError.text]]</div>
-        <div class="errorMoreInfo">[[_lastError.moreInfo]]</div>
-      </div>
-    </main>
-    <footer r="contentinfo">
-      <div>
-        Powered by <a href="https://www.gerritcodereview.com/" rel="noopener" target="_blank">Gerrit Code Review</a>
-        ([[_version]])
-        <gr-endpoint-decorator name="footer-left"></gr-endpoint-decorator>
-      </div>
-      <div>
-        <template is="dom-if" if="[[_feedbackUrl]]">
-          <a class="feedback" href\$="[[_feedbackUrl]]" rel="noopener" target="_blank">Report bug</a> |
-        </template>
-        Press “?” for keyboard shortcuts
-        <gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator>
-      </div>
-    </footer>
-    <gr-overlay id="keyboardShortcuts" with-backdrop="">
-      <gr-keyboard-shortcuts-dialog on-close="_handleKeyboardShortcutDialogClose"></gr-keyboard-shortcuts-dialog>
-    </gr-overlay>
-    <gr-overlay id="registrationOverlay" with-backdrop="">
-      <gr-registration-dialog id="registrationDialog" settings-url="[[_settingsUrl]]" on-account-detail-update="_handleAccountDetailUpdate" on-close="_handleRegistrationDialogClose">
-      </gr-registration-dialog>
-    </gr-overlay>
-    <gr-endpoint-decorator name="plugin-overlay"></gr-endpoint-decorator>
-    <gr-error-manager id="errorManager" login-url="[[_loginUrl]]"></gr-error-manager>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-reporting id="reporting"></gr-reporting>
-    <gr-router id="router"></gr-router>
-    <gr-plugin-host id="plugins" config="[[_serverConfig]]">
-    </gr-plugin-host>
-    <gr-lib-loader id="libLoader"></gr-lib-loader>
-    <gr-external-style id="externalStyleForAll" name="app-theme"></gr-external-style>
-    <gr-external-style id="externalStyleForTheme" name="[[getThemeEndpoint()]]"></gr-external-style>
+      Press “?” for keyboard shortcuts
+      <gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator>
+    </div>
+  </footer>
+  <gr-overlay id="keyboardShortcuts" with-backdrop="">
+    <gr-keyboard-shortcuts-dialog
+      on-close="_handleKeyboardShortcutDialogClose"
+    ></gr-keyboard-shortcuts-dialog>
+  </gr-overlay>
+  <gr-overlay id="registrationOverlay" with-backdrop="">
+    <gr-registration-dialog
+      id="registrationDialog"
+      settings-url="[[_settingsUrl]]"
+      on-account-detail-update="_handleAccountDetailUpdate"
+      on-close="_handleRegistrationDialogClose"
+    >
+    </gr-registration-dialog>
+  </gr-overlay>
+  <gr-endpoint-decorator name="plugin-overlay"></gr-endpoint-decorator>
+  <gr-error-manager
+    id="errorManager"
+    login-url="[[_loginUrl]]"
+  ></gr-error-manager>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-router id="router"></gr-router>
+  <gr-plugin-host id="plugins" config="[[_serverConfig]]"> </gr-plugin-host>
+  <gr-lib-loader id="libLoader"></gr-lib-loader>
+  <gr-external-style
+    id="externalStyleForAll"
+    name="app-theme"
+  ></gr-external-style>
+  <gr-external-style
+    id="externalStyleForTheme"
+    name="[[getThemeEndpoint()]]"
+  ></gr-external-style>
 `;
diff --git a/polygerrit-ui/app/elements/gr-app-init.js b/polygerrit-ui/app/elements/gr-app-init.js
index 7caec6d..780e64a 100644
--- a/polygerrit-ui/app/elements/gr-app-init.js
+++ b/polygerrit-ui/app/elements/gr-app-init.js
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 import {initAppContext} from '../services/app-context-init.js';
+import {initVisibilityReporter, initPerformanceReporter, initErrorReporter} from '../services/gr-reporting/gr-reporting.js';
+import {appContext} from '../services/app-context.js';
 
 if (!window.Polymer) {
   window.Polymer = {
@@ -24,4 +26,7 @@
 }
 window.Gerrit = window.Gerrit || {};
 
-initAppContext();
\ No newline at end of file
+initAppContext();
+initVisibilityReporter(appContext);
+initPerformanceReporter(appContext);
+initErrorReporter(appContext);
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index c9910d3..bcf79fb 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -15,13 +15,9 @@
  * limitations under the License.
  */
 
-/*
-  FIXME(polymer-modulizer): the above comments were extracted
-  from HTML and may be out of place here. Review them and
-  then delete this comment!
-*/
 import './gr-app-init.js';
 import './font-roboto-local-loader.js';
+// Sets up global Polymer variable, because plugins requires it.
 import '../scripts/bundled-polymer.js';
 
 /**
@@ -50,7 +46,7 @@
   safeTypesBridge: SafeTypes.safeTypesBridge,
 });
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrApp extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/gr-app_html.js b/polygerrit-ui/app/elements/gr-app_html.js
index fcf773f..3da1b69 100644
--- a/polygerrit-ui/app/elements/gr-app_html.js
+++ b/polygerrit-ui/app/elements/gr-app_html.js
@@ -17,5 +17,5 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-app-element id="app-element"></gr-app-element>
+  <gr-app-element id="app-element"></gr-app-element>
 `;
diff --git a/polygerrit-ui/app/elements/gr-app_test.html b/polygerrit-ui/app/elements/gr-app_test.html
index c65edb0..6322518 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-app</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -33,6 +34,7 @@
 <script type="module">
 import '../test/common-test-setup.js';
 import './gr-app.js';
+import {appContext} from '../services/app-context.js';
 import {GerritNav} from './core/gr-navigation/gr-navigation.js';
 
 suite('gr-app tests', () => {
@@ -41,9 +43,7 @@
 
   setup(done => {
     sandbox = sinon.sandbox.create();
-    stub('gr-reporting', {
-      appStarted: sandbox.stub(),
-    });
+    sandbox.stub(appContext.reportingService, 'appStarted');
     stub('gr-account-dropdown', {
       _getTopContent: sinon.stub(),
     });
@@ -79,12 +79,12 @@
   const appElement = () => element.$['app-element'];
 
   test('reporting', () => {
-    assert.isTrue(appElement().$.reporting.appStarted.calledOnce);
+    assert.isTrue(appElement().reporting.appStarted.calledOnce);
   });
 
   test('reporting called before router start', () => {
     const element = appElement();
-    const appStartedStub = element.$.reporting.appStarted;
+    const appStartedStub = element.reporting.appStarted;
     const routerStartStub = element.$.router.start;
     sinon.assert.callOrder(appStartedStub, routerStartStub);
   });
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.js b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.js
index d5ebb65..52c4f4e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.js
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 const $_documentContainer = document.createElement('template');
 
 $_documentContainer.innerHTML = `<dom-module id="gr-attribute-helper">
diff --git a/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.js b/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.js
index 8be50b1..ea60dc6 100644
--- a/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.js
+++ b/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 const $_documentContainer = document.createElement('template');
 
 $_documentContainer.innerHTML = `<dom-module id="gr-change-metadata-api">
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 5ef9dbf..41d5fd5 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
 const $_documentContainer = document.createElement('template');
 
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 62057a1..9bfb7da 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-js-api-interface/gr-js-api-interface.js';
 import {importHref} from '../../../scripts/import-href.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
@@ -28,7 +26,7 @@
 
 const INIT_PROPERTIES_TIMEOUT_MS = 10000;
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrEndpointDecorator extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js
index 1644c07..c4310fc 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_html.js
@@ -16,6 +16,4 @@
  */
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
-export const htmlTemplate = html`
-    <slot></slot>
-`;
+export const htmlTemplate = html` <slot></slot> `;
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 9574391..82402c0 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
@@ -14,13 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrEndpointParam extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
index 63d40fc..da85b9e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 const $_documentContainer = document.createElement('template');
 
 $_documentContainer.innerHTML = `<dom-module id="gr-event-helper">
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 68b1494..f27053d 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-js-api-interface/gr-js-api-interface.js';
 import {importHref} from '../../../scripts/import-href.js';
 import {updateStyles} from '@polymer/polymer/lib/mixins/element-mixin.js';
@@ -26,7 +24,7 @@
 import {pluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints.js';
 import {pluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrExternalStyle extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.js
index 1644c07..c4310fc 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.js
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_html.js
@@ -16,6 +16,4 @@
  */
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
-export const htmlTemplate = html`
-    <slot></slot>
-`;
+export const htmlTemplate = html` <slot></slot> `;
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 d1b2106..1833efa9 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
@@ -14,15 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-js-api-interface/gr-js-api-interface.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {pluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrPluginHost extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
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 db44cea..eaecd29 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../shared/gr-overlay/gr-overlay.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -25,7 +23,7 @@
 (function(window) {
   'use strict';
 
-  /** @extends Polymer.Element */
+  /** @extends PolymerElement */
   class GrPluginPopup extends GestureEventListeners(
       LegacyElementMixin(
           PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.js
index 779cbad..5d2cae7 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_html.js
@@ -17,10 +17,10 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <gr-overlay id="overlay" with-backdrop="">
-      <slot></slot>
-    </gr-overlay>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <gr-overlay id="overlay" with-backdrop="">
+    <slot></slot>
+  </gr-overlay>
 `;
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.js
index 3363d72..738b276 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import './gr-plugin-popup.js';
 import {dom, flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 const $_documentContainer = document.createElement('template');
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.js b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.js
index f9a2bdf..752570f 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.js
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../admin/gr-repo-command/gr-repo-command.js';
 import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.js b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.js
index 36d822c..445356d 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.js
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import './gr-plugin-repo-command.js';
 const $_documentContainer = document.createElement('template');
 
diff --git a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.js b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.js
index 4fb971f..35396cf 100644
--- a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.js
+++ b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../settings/gr-settings-view/gr-settings-item.js';
 import '../../settings/gr-settings-view/gr-settings-menu-item.js';
 const $_documentContainer = document.createElement('template');
diff --git a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js
index 3da60db..8a1b601 100644
--- a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js
+++ b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import {useShadow} from '@polymer/polymer/lib/utils/settings.js';
 
 let styleObjectCount = 0;
 
@@ -34,7 +35,7 @@
  * @return {string} Appropriate class name for the element is returned
  */
 GrStyleObject.prototype.getClassName = function(element) {
-  let rootNode = Polymer.Settings.useShadow
+  let rootNode = useShadow
     ? element.getRootNode() : document.body;
   if (rootNode === document) {
     rootNode = document.head;
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.js b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.js
index 411a7c8..ae8b8ab 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.js
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 Polymer({
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.js b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.js
index c987af3..be427ec 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.js
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import './gr-custom-plugin-header.js';
 const $_documentContainer = document.createElement('template');
 
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 7e312d3..14b6cfe 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '@polymer/iron-input/iron-input.js';
 import '../../shared/gr-avatar/gr-avatar.js';
 import '../../shared/gr-date-formatter/gr-date-formatter.js';
@@ -27,7 +26,7 @@
 import {htmlTemplate} from './gr-account-info_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccountInfo extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.js
index f6ae4b8..fd94821 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.js
@@ -17,83 +17,116 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      gr-avatar {
-        height: 120px;
-        width: 120px;
-        margin-right: var(--spacing-xs);
-        vertical-align: -.25em;
-      }
-      div section.hide {
-        display: none;
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div class="gr-form-styles">
-      <section>
-        <span class="title"></span>
-        <span class="value">
-          <gr-avatar account="[[_account]]" image-size="120"></gr-avatar>
-        </span>
-      </section>
-      <section class\$="[[_hideAvatarChangeUrl(_avatarChangeUrl)]]">
-        <span class="title"></span>
-        <span class="value">
-          <a href\$="[[_avatarChangeUrl]]">
-            Change avatar
-          </a>
-        </span>
-      </section>
-      <section>
-        <span class="title">ID</span>
-        <span class="value">[[_account._account_id]]</span>
-      </section>
-      <section>
-        <span class="title">Email</span>
-        <span class="value">[[_account.email]]</span>
-      </section>
-      <section>
-        <span class="title">Registered</span>
-        <span class="value">
-          <gr-date-formatter has-tooltip="" date-str="[[_account.registered_on]]"></gr-date-formatter>
-        </span>
-      </section>
-      <section id="usernameSection">
-        <span class="title">Username</span>
-        <span hidden\$="[[usernameMutable]]" class="value">[[_username]]</span>
-        <span hidden\$="[[!usernameMutable]]" class="value">
-          <iron-input on-keydown="_handleKeydown" bind-value="{{_username}}">
-            <input is="iron-input" id="usernameInput" disabled="[[_saving]]" on-keydown="_handleKeydown" bind-value="{{_username}}">
-          </iron-input>
-        </span>
-      </section>
-      <section id="nameSection">
-        <span class="title">Full name</span>
-        <span hidden\$="[[nameMutable]]" class="value">[[_account.name]]</span>
-        <span hidden\$="[[!nameMutable]]" class="value">
-          <iron-input on-keydown="_handleKeydown" bind-value="{{_account.name}}">
-            <input is="iron-input" id="nameInput" disabled="[[_saving]]" on-keydown="_handleKeydown" bind-value="{{_account.name}}">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Display name</span>
-        <span class="value">
-          <iron-input on-keydown="_handleKeydown" bind-value="{{_account.display_name}}">
-            <input is="iron-input" id="displayNameInput" disabled="[[_saving]]" on-keydown="_handleKeydown" bind-value="{{_account.display_name}}">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Status (e.g. "Vacation")</span>
-        <span class="value">
-          <iron-input on-keydown="_handleKeydown" bind-value="{{_account.status}}">
-            <input is="iron-input" id="statusInput" disabled="[[_saving]]" on-keydown="_handleKeydown" bind-value="{{_account.status}}">
-          </iron-input>
-        </span>
-      </section>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    gr-avatar {
+      height: 120px;
+      width: 120px;
+      margin-right: var(--spacing-xs);
+      vertical-align: -0.25em;
+    }
+    div section.hide {
+      display: none;
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div class="gr-form-styles">
+    <section>
+      <span class="title"></span>
+      <span class="value">
+        <gr-avatar account="[[_account]]" image-size="120"></gr-avatar>
+      </span>
+    </section>
+    <section class$="[[_hideAvatarChangeUrl(_avatarChangeUrl)]]">
+      <span class="title"></span>
+      <span class="value">
+        <a href$="[[_avatarChangeUrl]]">
+          Change avatar
+        </a>
+      </span>
+    </section>
+    <section>
+      <span class="title">ID</span>
+      <span class="value">[[_account._account_id]]</span>
+    </section>
+    <section>
+      <span class="title">Email</span>
+      <span class="value">[[_account.email]]</span>
+    </section>
+    <section>
+      <span class="title">Registered</span>
+      <span class="value">
+        <gr-date-formatter
+          has-tooltip=""
+          date-str="[[_account.registered_on]]"
+        ></gr-date-formatter>
+      </span>
+    </section>
+    <section id="usernameSection">
+      <span class="title">Username</span>
+      <span hidden$="[[usernameMutable]]" class="value">[[_username]]</span>
+      <span hidden$="[[!usernameMutable]]" class="value">
+        <iron-input on-keydown="_handleKeydown" bind-value="{{_username}}">
+          <input
+            is="iron-input"
+            id="usernameInput"
+            disabled="[[_saving]]"
+            on-keydown="_handleKeydown"
+            bind-value="{{_username}}"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section id="nameSection">
+      <span class="title">Full name</span>
+      <span hidden$="[[nameMutable]]" class="value">[[_account.name]]</span>
+      <span hidden$="[[!nameMutable]]" class="value">
+        <iron-input on-keydown="_handleKeydown" bind-value="{{_account.name}}">
+          <input
+            is="iron-input"
+            id="nameInput"
+            disabled="[[_saving]]"
+            on-keydown="_handleKeydown"
+            bind-value="{{_account.name}}"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Display name</span>
+      <span class="value">
+        <iron-input
+          on-keydown="_handleKeydown"
+          bind-value="{{_account.display_name}}"
+        >
+          <input
+            is="iron-input"
+            id="displayNameInput"
+            disabled="[[_saving]]"
+            on-keydown="_handleKeydown"
+            bind-value="{{_account.display_name}}"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Status (e.g. "Vacation")</span>
+      <span class="value">
+        <iron-input
+          on-keydown="_handleKeydown"
+          bind-value="{{_account.status}}"
+        >
+          <input
+            is="iron-input"
+            id="statusInput"
+            disabled="[[_saving]]"
+            on-keydown="_handleKeydown"
+            bind-value="{{_account.status}}"
+          />
+        </iron-input>
+      </span>
+    </section>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
index 14bc5de..53641d9 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-account-info</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 390baf6..0a84f56c 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
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -27,7 +26,7 @@
 import {BaseUrlBehavior} from '../../../behaviors/base-url-behavior/base-url-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAgreementsList extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.js b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.js
index 4bd9365..1cd9ce2 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_html.js
@@ -17,40 +17,40 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      #agreements .nameColumn {
-        min-width: 15em;
-        width: auto;
-      }
-      #agreements .descriptionColumn {
-        width: auto;
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div class="gr-form-styles">
-      <table id="agreements">
-        <thead>
+  <style include="shared-styles">
+    #agreements .nameColumn {
+      min-width: 15em;
+      width: auto;
+    }
+    #agreements .descriptionColumn {
+      width: auto;
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div class="gr-form-styles">
+    <table id="agreements">
+      <thead>
+        <tr>
+          <th class="nameColumn">Name</th>
+          <th class="descriptionColumn">Description</th>
+        </tr>
+      </thead>
+      <tbody>
+        <template is="dom-repeat" items="[[_agreements]]">
           <tr>
-            <th class="nameColumn">Name</th>
-            <th class="descriptionColumn">Description</th>
+            <td class="nameColumn">
+              <a href$="[[getUrlBase(item.url)]]" rel="external">
+                [[item.name]]
+              </a>
+            </td>
+            <td class="descriptionColumn">[[item.description]]</td>
           </tr>
-        </thead>
-        <tbody>
-          <template is="dom-repeat" items="[[_agreements]]">
-            <tr>
-              <td class="nameColumn">
-                <a href\$="[[getUrlBase(item.url)]]" rel="external">
-                  [[item.name]]
-                </a>
-              </td>
-              <td class="descriptionColumn">[[item.description]]</td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-      <a href\$="[[getUrl()]]">New Contributor Agreement</a>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+        </template>
+      </tbody>
+    </table>
+    <a href$="[[getUrl()]]">New Contributor Agreement</a>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html
index 0a008a4..3a2b86d 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-settings-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 61e8e93..2051947 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-date-formatter/gr-date-formatter.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -29,7 +28,7 @@
 import {ChangeTableBehavior} from '../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrChangeTableEditor extends mixinBehaviors( [
   ChangeTableBehavior,
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.js b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.js
index 7aa785c..d63e627 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_html.js
@@ -17,51 +17,67 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      #changeCols {
-        width: auto;
-      }
-      #changeCols .visibleHeader {
-        text-align: center;
-      }
-      .checkboxContainer {
-        cursor: pointer;
-        text-align: center;
-      }
-      .checkboxContainer input {
-        cursor: pointer;
-      }
-      .checkboxContainer:hover {
-        outline: 1px solid var(--border-color);
-      }
-    </style>
-    <div class="gr-form-styles">
-      <table id="changeCols">
-        <thead>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    #changeCols {
+      width: auto;
+    }
+    #changeCols .visibleHeader {
+      text-align: center;
+    }
+    .checkboxContainer {
+      cursor: pointer;
+      text-align: center;
+    }
+    .checkboxContainer input {
+      cursor: pointer;
+    }
+    .checkboxContainer:hover {
+      outline: 1px solid var(--border-color);
+    }
+  </style>
+  <div class="gr-form-styles">
+    <table id="changeCols">
+      <thead>
+        <tr>
+          <th class="nameHeader">Column</th>
+          <th class="visibleHeader">Visible</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>Number</td>
+          <td
+            class="checkboxContainer"
+            on-click="_handleCheckboxContainerClick"
+          >
+            <input
+              type="checkbox"
+              name="number"
+              on-click="_handleNumberCheckboxClick"
+              checked$="[[showNumber]]"
+            />
+          </td>
+        </tr>
+        <template is="dom-repeat" items="[[columnNames]]">
           <tr>
-            <th class="nameHeader">Column</th>
-            <th class="visibleHeader">Visible</th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr>
-            <td>Number</td>
-            <td class="checkboxContainer" on-click="_handleCheckboxContainerClick">
-              <input type="checkbox" name="number" on-click="_handleNumberCheckboxClick" checked\$="[[showNumber]]">
+            <td>[[item]]</td>
+            <td
+              class="checkboxContainer"
+              on-click="_handleCheckboxContainerClick"
+            >
+              <input
+                type="checkbox"
+                name="[[item]]"
+                on-click="_handleTargetClick"
+                checked$="[[!isColumnHidden(item, displayedColumns)]]"
+              />
             </td>
           </tr>
-          <template is="dom-repeat" items="[[columnNames]]">
-            <tr>
-              <td>[[item]]</td>
-              <td class="checkboxContainer" on-click="_handleCheckboxContainerClick">
-                <input type="checkbox" name="[[item]]" on-click="_handleTargetClick" checked\$="[[!isColumnHidden(item, displayedColumns)]]">
-              </td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-    </div>
+        </template>
+      </tbody>
+    </table>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
index 919be86..79d1390 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-settings-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 957eb48..991b3d1 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
@@ -16,7 +16,6 @@
  */
 
 import '@polymer/iron-input/iron-input.js';
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
 import '../../shared/gr-button/gr-button.js';
@@ -29,7 +28,7 @@
 import {BaseUrlBehavior} from '../../../behaviors/base-url-behavior/base-url-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrClaView extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.js b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.js
index 2c2fca0..2d371e2 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_html.js
@@ -17,80 +17,110 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      h1 {
-        margin-bottom: var(--spacing-m);
-      }
-      h3 {
-        margin-bottom: var(--spacing-m);
-      }
-      .agreementsUrl {
-        border: 1px solid #b0bdcc;
-        margin-bottom: var(--spacing-xl);
-        margin-left: var(--spacing-xl);
-        margin-right: var(--spacing-xl);
-        padding: var(--spacing-s);
-      }
-      #claNewAgreementsLabel {
-        font-weight: var(--font-weight-bold);
-      }
-      #claNewAgreement {
-        display: none;
-      }
-      #claNewAgreement.show {
-        display: block;
-      }
-      .contributorAgreementButton {
-        font-weight: var(--font-weight-bold);
-      }
-      .alreadySubmittedText {
-        color: var(--error-text-color);
-        margin: 0 var(--spacing-xxl);
-        padding: var(--spacing-m);
-      }
-      .alreadySubmittedText.hide,
-      .hideAgreementsTextBox {
-        display: none;
-      }
-      main {
-        margin: var(--spacing-xxl) auto;
-        max-width: 50em;
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <main>
-      <h1>New Contributor Agreement</h1>
-      <h3>Select an agreement type:</h3>
-      <template is="dom-repeat" items="[[_serverConfig.auth.contributor_agreements]]">
-        <span class="contributorAgreementButton">
-          <input id\$="claNewAgreementsInput[[item.name]]" name="claNewAgreementsRadio" type="radio" data-name\$="[[item.name]]" data-url\$="[[item.url]]" on-click="_handleShowAgreement" disabled\$="[[_disableAgreements(item, _groups, _signedAgreements)]]">
-          <label id="claNewAgreementsLabel">[[item.name]]</label>
-        </span>
-        <div class\$="alreadySubmittedText [[_hideAgreements(item, _groups, _signedAgreements)]]">
-          Agreement already submitted.
-        </div>
-        <div class="agreementsUrl">
-          [[item.description]]
-        </div>
-      </template>
-      <div id="claNewAgreement" class\$="[[_computeShowAgreementsClass(_showAgreements)]]">
-        <h3 class="smallHeading">Review the agreement:</h3>
-        <div id="agreementsUrl" class="agreementsUrl">
-          <a href\$="[[_agreementsUrl]]" target="blank" rel="noopener">
-            Please review the agreement.</a>
-        </div>
-        <div class\$="agreementsTextBox [[_computeHideAgreementClass(_agreementName, _serverConfig.auth.contributor_agreements)]]">
-          <h3 class="smallHeading">Complete the agreement:</h3>
-          <iron-input bind-value="{{_agreementsText}}" placeholder="Enter 'I agree' here">
-            <input id="input-agreements" is="iron-input" bind-value="{{_agreementsText}}" placeholder="Enter 'I agree' here">
-          </iron-input>
-          <gr-button on-click="_handleSaveAgreements" disabled="[[_disableAgreementsText(_agreementsText)]]">
-            Submit
-          </gr-button>
-        </div>
+  <style include="shared-styles">
+    h1 {
+      margin-bottom: var(--spacing-m);
+    }
+    h3 {
+      margin-bottom: var(--spacing-m);
+    }
+    .agreementsUrl {
+      border: 1px solid #b0bdcc;
+      margin-bottom: var(--spacing-xl);
+      margin-left: var(--spacing-xl);
+      margin-right: var(--spacing-xl);
+      padding: var(--spacing-s);
+    }
+    #claNewAgreementsLabel {
+      font-weight: var(--font-weight-bold);
+    }
+    #claNewAgreement {
+      display: none;
+    }
+    #claNewAgreement.show {
+      display: block;
+    }
+    .contributorAgreementButton {
+      font-weight: var(--font-weight-bold);
+    }
+    .alreadySubmittedText {
+      color: var(--error-text-color);
+      margin: 0 var(--spacing-xxl);
+      padding: var(--spacing-m);
+    }
+    .alreadySubmittedText.hide,
+    .hideAgreementsTextBox {
+      display: none;
+    }
+    main {
+      margin: var(--spacing-xxl) auto;
+      max-width: 50em;
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <main>
+    <h1>New Contributor Agreement</h1>
+    <h3>Select an agreement type:</h3>
+    <template
+      is="dom-repeat"
+      items="[[_serverConfig.auth.contributor_agreements]]"
+    >
+      <span class="contributorAgreementButton">
+        <input
+          id$="claNewAgreementsInput[[item.name]]"
+          name="claNewAgreementsRadio"
+          type="radio"
+          data-name$="[[item.name]]"
+          data-url$="[[item.url]]"
+          on-click="_handleShowAgreement"
+          disabled$="[[_disableAgreements(item, _groups, _signedAgreements)]]"
+        />
+        <label id="claNewAgreementsLabel">[[item.name]]</label>
+      </span>
+      <div
+        class$="alreadySubmittedText [[_hideAgreements(item, _groups, _signedAgreements)]]"
+      >
+        Agreement already submitted.
       </div>
-    </main>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      <div class="agreementsUrl">
+        [[item.description]]
+      </div>
+    </template>
+    <div
+      id="claNewAgreement"
+      class$="[[_computeShowAgreementsClass(_showAgreements)]]"
+    >
+      <h3 class="smallHeading">Review the agreement:</h3>
+      <div id="agreementsUrl" class="agreementsUrl">
+        <a href$="[[_agreementsUrl]]" target="blank" rel="noopener">
+          Please review the agreement.</a
+        >
+      </div>
+      <div
+        class$="agreementsTextBox [[_computeHideAgreementClass(_agreementName, _serverConfig.auth.contributor_agreements)]]"
+      >
+        <h3 class="smallHeading">Complete the agreement:</h3>
+        <iron-input
+          bind-value="{{_agreementsText}}"
+          placeholder="Enter 'I agree' here"
+        >
+          <input
+            id="input-agreements"
+            is="iron-input"
+            bind-value="{{_agreementsText}}"
+            placeholder="Enter 'I agree' here"
+          />
+        </iron-input>
+        <gr-button
+          on-click="_handleSaveAgreements"
+          disabled="[[_disableAgreementsText(_agreementsText)]]"
+        >
+          Submit
+        </gr-button>
+      </div>
+    </div>
+  </main>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html
index a1874db..bc3c10c 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-cla-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 2a7ac06..6973292 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/gr-form-styles.js';
 import '../../../styles/shared-styles.js';
@@ -26,7 +24,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-edit-preferences_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrEditPreferences extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.js b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.js
index de22dbb..f2c476a 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_html.js
@@ -17,73 +17,148 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div id="editPreferences" class="gr-form-styles">
-      <section>
-        <span class="title">Tab width</span>
-        <span class="value">
-          <iron-input type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{editPrefs.tab_size}}" on-keypress="_handleEditPrefsChanged" on-change="_handleEditPrefsChanged">
-            <input is="iron-input" type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{editPrefs.tab_size}}" on-keypress="_handleEditPrefsChanged" on-change="_handleEditPrefsChanged">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Columns</span>
-        <span class="value">
-          <iron-input type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{editPrefs.line_length}}" on-keypress="_handleEditPrefsChanged" on-change="_handleEditPrefsChanged">
-            <input is="iron-input" type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{editPrefs.line_length}}" on-keypress="_handleEditPrefsChanged" on-change="_handleEditPrefsChanged">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Indent unit</span>
-        <span class="value">
-          <iron-input type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{editPrefs.indent_unit}}" on-keypress="_handleEditPrefsChanged" on-change="_handleEditPrefsChanged">
-            <input is="iron-input" type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{editPrefs.indent_unit}}" on-keypress="_handleEditPrefsChanged" on-change="_handleEditPrefsChanged">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Syntax highlighting</span>
-        <span class="value">
-          <input id="editSyntaxHighlighting" type="checkbox" checked\$="[[editPrefs.syntax_highlighting]]" on-change="_handleEditSyntaxHighlightingChanged">
-        </span>
-      </section>
-      <section>
-        <span class="title">Show tabs</span>
-        <span class="value">
-          <input id="editShowTabs" type="checkbox" checked\$="[[editPrefs.show_tabs]]" on-change="_handleEditShowTabsChanged">
-        </span>
-      </section>
-      <section>
-        <span class="title">Match brackets</span>
-        <span class="value">
-          <input id="showMatchBrackets" type="checkbox" checked\$="[[editPrefs.match_brackets]]" on-change="_handleMatchBracketsChanged">
-        </span>
-      </section>
-      <section>
-        <span class="title">Line wrapping</span>
-        <span class="value">
-          <input id="editShowLineWrapping" type="checkbox" checked\$="[[editPrefs.line_wrapping]]" on-change="_handleEditLineWrappingChanged">
-        </span>
-      </section>
-      <section>
-        <span class="title">Indent with tabs</span>
-        <span class="value">
-          <input id="showIndentWithTabs" type="checkbox" checked\$="[[editPrefs.indent_with_tabs]]" on-change="_handleIndentWithTabsChanged">
-        </span>
-      </section>
-      <section>
-        <span class="title">Auto close brackets</span>
-        <span class="value">
-          <input id="showAutoCloseBrackets" type="checkbox" checked\$="[[editPrefs.auto_close_brackets]]" on-change="_handleAutoCloseBracketsChanged">
-        </span>
-      </section>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div id="editPreferences" class="gr-form-styles">
+    <section>
+      <span class="title">Tab width</span>
+      <span class="value">
+        <iron-input
+          type="number"
+          prevent-invalid-input=""
+          allowed-pattern="[0-9]"
+          bind-value="{{editPrefs.tab_size}}"
+          on-keypress="_handleEditPrefsChanged"
+          on-change="_handleEditPrefsChanged"
+        >
+          <input
+            is="iron-input"
+            type="number"
+            prevent-invalid-input=""
+            allowed-pattern="[0-9]"
+            bind-value="{{editPrefs.tab_size}}"
+            on-keypress="_handleEditPrefsChanged"
+            on-change="_handleEditPrefsChanged"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Columns</span>
+      <span class="value">
+        <iron-input
+          type="number"
+          prevent-invalid-input=""
+          allowed-pattern="[0-9]"
+          bind-value="{{editPrefs.line_length}}"
+          on-keypress="_handleEditPrefsChanged"
+          on-change="_handleEditPrefsChanged"
+        >
+          <input
+            is="iron-input"
+            type="number"
+            prevent-invalid-input=""
+            allowed-pattern="[0-9]"
+            bind-value="{{editPrefs.line_length}}"
+            on-keypress="_handleEditPrefsChanged"
+            on-change="_handleEditPrefsChanged"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Indent unit</span>
+      <span class="value">
+        <iron-input
+          type="number"
+          prevent-invalid-input=""
+          allowed-pattern="[0-9]"
+          bind-value="{{editPrefs.indent_unit}}"
+          on-keypress="_handleEditPrefsChanged"
+          on-change="_handleEditPrefsChanged"
+        >
+          <input
+            is="iron-input"
+            type="number"
+            prevent-invalid-input=""
+            allowed-pattern="[0-9]"
+            bind-value="{{editPrefs.indent_unit}}"
+            on-keypress="_handleEditPrefsChanged"
+            on-change="_handleEditPrefsChanged"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Syntax highlighting</span>
+      <span class="value">
+        <input
+          id="editSyntaxHighlighting"
+          type="checkbox"
+          checked$="[[editPrefs.syntax_highlighting]]"
+          on-change="_handleEditSyntaxHighlightingChanged"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Show tabs</span>
+      <span class="value">
+        <input
+          id="editShowTabs"
+          type="checkbox"
+          checked$="[[editPrefs.show_tabs]]"
+          on-change="_handleEditShowTabsChanged"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Match brackets</span>
+      <span class="value">
+        <input
+          id="showMatchBrackets"
+          type="checkbox"
+          checked$="[[editPrefs.match_brackets]]"
+          on-change="_handleMatchBracketsChanged"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Line wrapping</span>
+      <span class="value">
+        <input
+          id="editShowLineWrapping"
+          type="checkbox"
+          checked$="[[editPrefs.line_wrapping]]"
+          on-change="_handleEditLineWrappingChanged"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Indent with tabs</span>
+      <span class="value">
+        <input
+          id="showIndentWithTabs"
+          type="checkbox"
+          checked$="[[editPrefs.indent_with_tabs]]"
+          on-change="_handleIndentWithTabsChanged"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Auto close brackets</span>
+      <span class="value">
+        <input
+          id="showAutoCloseBrackets"
+          type="checkbox"
+          checked$="[[editPrefs.auto_close_brackets]]"
+          on-change="_handleAutoCloseBracketsChanged"
+        />
+      </span>
+    </section>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html
index 47e295c..3cc7bfe 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-edit-preferences</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 fc97079..0cc5c2c 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -26,7 +24,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-email-editor_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrEmailEditor extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.js b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.js
index b02df3c..977e95d 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_html.js
@@ -17,59 +17,83 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      th {
-        color: var(--deemphasized-text-color);
-        text-align: left;
-      }
-      #emailTable .emailColumn {
-        min-width: 32.5em;
-        width: auto;
-      }
-      #emailTable .preferredHeader {
-        text-align: center;
-        width: 6em;
-      }
-      #emailTable .preferredControl {
-        cursor: pointer;
-        height: auto;
-        text-align: center;
-      }
-      #emailTable .preferredControl .preferredRadio {
-        height: auto;
-      }
-      .preferredControl:hover {
-        outline: 1px solid var(--border-color);
-      }
-    </style>
-    <div class="gr-form-styles">
-      <table id="emailTable">
-        <thead>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    th {
+      color: var(--deemphasized-text-color);
+      text-align: left;
+    }
+    #emailTable .emailColumn {
+      min-width: 32.5em;
+      width: auto;
+    }
+    #emailTable .preferredHeader {
+      text-align: center;
+      width: 6em;
+    }
+    #emailTable .preferredControl {
+      cursor: pointer;
+      height: auto;
+      text-align: center;
+    }
+    #emailTable .preferredControl .preferredRadio {
+      height: auto;
+    }
+    .preferredControl:hover {
+      outline: 1px solid var(--border-color);
+    }
+  </style>
+  <div class="gr-form-styles">
+    <table id="emailTable">
+      <thead>
+        <tr>
+          <th class="emailColumn">Email</th>
+          <th class="preferredHeader">Preferred</th>
+          <th></th>
+        </tr>
+      </thead>
+      <tbody>
+        <template is="dom-repeat" items="[[_emails]]">
           <tr>
-            <th class="emailColumn">Email</th>
-            <th class="preferredHeader">Preferred</th>
-            <th></th>
+            <td class="emailColumn">[[item.email]]</td>
+            <td
+              class="preferredControl"
+              on-click="_handlePreferredControlClick"
+            >
+              <iron-input
+                class="preferredRadio"
+                type="radio"
+                on-change="_handlePreferredChange"
+                name="preferred"
+                bind-value="[[item.email]]"
+                checked$="[[item.preferred]]"
+              >
+                <input
+                  is="iron-input"
+                  class="preferredRadio"
+                  type="radio"
+                  on-change="_handlePreferredChange"
+                  name="preferred"
+                  value="[[item.email]]"
+                  checked$="[[item.preferred]]"
+                />
+              </iron-input>
+            </td>
+            <td>
+              <gr-button
+                data-index$="[[index]]"
+                on-click="_handleDeleteButton"
+                disabled="[[item.preferred]]"
+                class="remove-button"
+                >Delete</gr-button
+              >
+            </td>
           </tr>
-        </thead>
-        <tbody>
-          <template is="dom-repeat" items="[[_emails]]">
-            <tr>
-              <td class="emailColumn">[[item.email]]</td>
-              <td class="preferredControl" on-click="_handlePreferredControlClick">
-                <iron-input class="preferredRadio" type="radio" on-change="_handlePreferredChange" name="preferred" bind-value="[[item.email]]" checked\$="[[item.preferred]]">
-                  <input is="iron-input" class="preferredRadio" type="radio" on-change="_handlePreferredChange" name="preferred" value="[[item.email]]" checked\$="[[item.preferred]]">
-                </iron-input>
-              </td>
-              <td>
-                <gr-button data-index\$="[[index]]" on-click="_handleDeleteButton" disabled="[[item.preferred]]" class="remove-button">Delete</gr-button>
-              </td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+        </template>
+      </tbody>
+    </table>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
index dbdd2d6..ad2553d 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-email-editor</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 90631c7..6c6ad01 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/gr-form-styles.js';
 import '../../shared/gr-button/gr-button.js';
@@ -29,7 +27,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-gpg-editor_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrGpgEditor extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.js b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.js
index 3ec4642..19b8d0c 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_html.js
@@ -17,98 +17,121 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      .keyHeader {
-        width: 9em;
-      }
-      .userIdHeader {
-        width: 15em;
-      }
-      #viewKeyOverlay {
-        padding: var(--spacing-xxl);
-        width: 50em;
-      }
-      .publicKey {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        overflow-x: scroll;
-        overflow-wrap: break-word;
-        width: 30em;
-      }
-      .closeButton {
-        bottom: 2em;
-        position: absolute;
-        right: 2em;
-      }
-      #existing {
-        margin-bottom: var(--spacing-l);
-      }
-    </style>
-    <div class="gr-form-styles">
-      <fieldset id="existing">
-        <table>
-          <thead>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    .keyHeader {
+      width: 9em;
+    }
+    .userIdHeader {
+      width: 15em;
+    }
+    #viewKeyOverlay {
+      padding: var(--spacing-xxl);
+      width: 50em;
+    }
+    .publicKey {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      overflow-x: scroll;
+      overflow-wrap: break-word;
+      width: 30em;
+    }
+    .closeButton {
+      bottom: 2em;
+      position: absolute;
+      right: 2em;
+    }
+    #existing {
+      margin-bottom: var(--spacing-l);
+    }
+  </style>
+  <div class="gr-form-styles">
+    <fieldset id="existing">
+      <table>
+        <thead>
+          <tr>
+            <th class="idColumn">ID</th>
+            <th class="fingerPrintColumn">Fingerprint</th>
+            <th class="userIdHeader">User IDs</th>
+            <th class="keyHeader">Public Key</th>
+            <th></th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          <template is="dom-repeat" items="[[_keys]]" as="key">
             <tr>
-              <th class="idColumn">ID</th>
-              <th class="fingerPrintColumn">Fingerprint</th>
-              <th class="userIdHeader">User IDs</th>
-              <th class="keyHeader">Public Key</th>
-              <th></th>
-              <th></th>
+              <td class="idColumn">[[key.id]]</td>
+              <td class="fingerPrintColumn">[[key.fingerprint]]</td>
+              <td class="userIdHeader">
+                <template is="dom-repeat" items="[[key.user_ids]]">
+                  [[item]]
+                </template>
+              </td>
+              <td class="keyHeader">
+                <gr-button on-click="_showKey" data-index$="[[index]]" link=""
+                  >Click to View</gr-button
+                >
+              </td>
+              <td>
+                <gr-copy-clipboard
+                  has-tooltip=""
+                  button-title="Copy GPG public key to clipboard"
+                  hide-input=""
+                  text="[[key.key]]"
+                >
+                </gr-copy-clipboard>
+              </td>
+              <td>
+                <gr-button data-index$="[[index]]" on-click="_handleDeleteKey"
+                  >Delete</gr-button
+                >
+              </td>
             </tr>
-          </thead>
-          <tbody>
-            <template is="dom-repeat" items="[[_keys]]" as="key">
-              <tr>
-                <td class="idColumn">[[key.id]]</td>
-                <td class="fingerPrintColumn">[[key.fingerprint]]</td>
-                <td class="userIdHeader">
-                  <template is="dom-repeat" items="[[key.user_ids]]">
-                    [[item]]
-                  </template>
-                </td>
-                <td class="keyHeader">
-                  <gr-button on-click="_showKey" data-index\$="[[index]]" link="">Click to View</gr-button>
-                </td>
-                <td>
-                  <gr-copy-clipboard has-tooltip="" button-title="Copy GPG public key to clipboard" hide-input="" text="[[key.key]]">
-                  </gr-copy-clipboard>
-                </td>
-                <td>
-                  <gr-button data-index\$="[[index]]" on-click="_handleDeleteKey">Delete</gr-button>
-                </td>
-              </tr>
-            </template>
-          </tbody>
-        </table>
-        <gr-overlay id="viewKeyOverlay" with-backdrop="">
-          <fieldset>
-            <section>
-              <span class="title">Status</span>
-              <span class="value">[[_keyToView.status]]</span>
-            </section>
-            <section>
-              <span class="title">Key</span>
-              <span class="value">[[_keyToView.key]]</span>
-            </section>
-          </fieldset>
-          <gr-button class="closeButton" on-click="_closeOverlay">Close</gr-button>
-        </gr-overlay>
-        <gr-button on-click="save" disabled\$="[[!hasUnsavedChanges]]">Save changes</gr-button>
-      </fieldset>
-      <fieldset>
-        <section>
-          <span class="title">New GPG key</span>
-          <span class="value">
-            <iron-autogrow-textarea id="newKey" autocomplete="on" bind-value="{{_newKey}}" placeholder="New GPG Key"></iron-autogrow-textarea>
-          </span>
-        </section>
-        <gr-button id="addButton" disabled\$="[[_computeAddButtonDisabled(_newKey)]]" on-click="_handleAddKey">Add new GPG key</gr-button>
-      </fieldset>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+          </template>
+        </tbody>
+      </table>
+      <gr-overlay id="viewKeyOverlay" with-backdrop="">
+        <fieldset>
+          <section>
+            <span class="title">Status</span>
+            <span class="value">[[_keyToView.status]]</span>
+          </section>
+          <section>
+            <span class="title">Key</span>
+            <span class="value">[[_keyToView.key]]</span>
+          </section>
+        </fieldset>
+        <gr-button class="closeButton" on-click="_closeOverlay"
+          >Close</gr-button
+        >
+      </gr-overlay>
+      <gr-button on-click="save" disabled$="[[!hasUnsavedChanges]]"
+        >Save changes</gr-button
+      >
+    </fieldset>
+    <fieldset>
+      <section>
+        <span class="title">New GPG key</span>
+        <span class="value">
+          <iron-autogrow-textarea
+            id="newKey"
+            autocomplete="on"
+            bind-value="{{_newKey}}"
+            placeholder="New GPG Key"
+          ></iron-autogrow-textarea>
+        </span>
+      </section>
+      <gr-button
+        id="addButton"
+        disabled$="[[_computeAddButtonDisabled(_newKey)]]"
+        on-click="_handleAddKey"
+        >Add new GPG key</gr-button
+      >
+    </fieldset>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html
index c9daa89..4a0af5b 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-gpg-editor</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 1cc1369..429a7c7 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../../styles/gr-form-styles.js';
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
@@ -25,7 +23,7 @@
 import {htmlTemplate} from './gr-group-list_html.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrGroupList extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.js b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.js
index ddacd31..d5350aa 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_html.js
@@ -17,45 +17,45 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-        #groups .nameColumn {
-          min-width: 11em;
-          width: auto;
-        }
-        .descriptionHeader {
-          min-width: 21.5em;
-        }
-        .visibleCell {
-          text-align: center;
-          width: 6em;
-        }
-      </style>
-    <div class="gr-form-styles">
-      <table id="groups">
-        <thead>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    #groups .nameColumn {
+      min-width: 11em;
+      width: auto;
+    }
+    .descriptionHeader {
+      min-width: 21.5em;
+    }
+    .visibleCell {
+      text-align: center;
+      width: 6em;
+    }
+  </style>
+  <div class="gr-form-styles">
+    <table id="groups">
+      <thead>
+        <tr>
+          <th class="nameHeader">Name</th>
+          <th class="descriptionHeader">Description</th>
+          <th class="visibleCell">Visible to all</th>
+        </tr>
+      </thead>
+      <tbody>
+        <template is="dom-repeat" items="[[_groups]]">
           <tr>
-            <th class="nameHeader">Name</th>
-            <th class="descriptionHeader">Description</th>
-            <th class="visibleCell">Visible to all</th>
+            <td class="nameColumn">
+              <a href$="[[_computeGroupPath(item)]]">
+                [[item.name]]
+              </a>
+            </td>
+            <td>[[item.description]]</td>
+            <td class="visibleCell">[[_computeVisibleToAll(item)]]</td>
           </tr>
-        </thead>
-        <tbody>
-          <template is="dom-repeat" items="[[_groups]]">
-            <tr>
-              <td class="nameColumn">
-                <a href\$="[[_computeGroupPath(item)]]">
-                  [[item.name]]
-                </a>
-              </td>
-              <td>[[item.description]]</td>
-              <td class="visibleCell">[[_computeVisibleToAll(item)]]</td>
-            </tr>
-          </template>
-        </tbody>
-      </table>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+        </template>
+      </tbody>
+    </table>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
index 0d24948..0525003 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-settings-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 02657f8..164bdee 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/gr-form-styles.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-copy-clipboard/gr-copy-clipboard.js';
@@ -27,7 +25,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-http-password_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrHttpPassword extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.js b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.js
index b75f56e..0474b99 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_html.js
@@ -17,68 +17,82 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .password {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-      }
-      #generatedPasswordOverlay {
-        padding: var(--spacing-xxl);
-        width: 50em;
-      }
-      #generatedPasswordDisplay {
-        margin: var(--spacing-l) 0;
-      }
-      #generatedPasswordDisplay .title {
-        width: unset;
-      }
-      #generatedPasswordDisplay .value {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-      }
-      #passwordWarning {
-        font-style: italic;
-        text-align: center;
-      }
-      .closeButton {
-        bottom: 2em;
-        position: absolute;
-        right: 2em;
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div class="gr-form-styles">
-      <div hidden\$="[[_passwordUrl]]">
-        <section>
-          <span class="title">Username</span>
-          <span class="value">[[_username]]</span>
-        </section>
-        <gr-button id="generateButton" on-click="_handleGenerateTap">Generate new password</gr-button>
-      </div>
-      <span hidden\$="[[!_passwordUrl]]">
-        <a href\$="[[_passwordUrl]]" target="_blank" rel="noopener">
-          Obtain password</a>
-        (opens in a new tab)
-      </span>
+  <style include="shared-styles">
+    .password {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+    }
+    #generatedPasswordOverlay {
+      padding: var(--spacing-xxl);
+      width: 50em;
+    }
+    #generatedPasswordDisplay {
+      margin: var(--spacing-l) 0;
+    }
+    #generatedPasswordDisplay .title {
+      width: unset;
+    }
+    #generatedPasswordDisplay .value {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+    }
+    #passwordWarning {
+      font-style: italic;
+      text-align: center;
+    }
+    .closeButton {
+      bottom: 2em;
+      position: absolute;
+      right: 2em;
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div class="gr-form-styles">
+    <div hidden$="[[_passwordUrl]]">
+      <section>
+        <span class="title">Username</span>
+        <span class="value">[[_username]]</span>
+      </section>
+      <gr-button id="generateButton" on-click="_handleGenerateTap"
+        >Generate new password</gr-button
+      >
     </div>
-    <gr-overlay id="generatedPasswordOverlay" on-iron-overlay-closed="_generatedPasswordOverlayClosed" with-backdrop="">
-      <div class="gr-form-styles">
-        <section id="generatedPasswordDisplay">
-          <span class="title">New Password:</span>
-          <span class="value">[[_generatedPassword]]</span>
-          <gr-copy-clipboard has-tooltip="" button-title="Copy password to clipboard" hide-input="" text="[[_generatedPassword]]">
-          </gr-copy-clipboard>
-        </section>
-        <section id="passwordWarning">
-          This password will not be displayed again.<br>
-          If you lose it, you will need to generate a new one.
-        </section>
-        <gr-button link="" class="closeButton" on-click="_closeOverlay">Close</gr-button>
-      </div>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    <span hidden$="[[!_passwordUrl]]">
+      <a href$="[[_passwordUrl]]" target="_blank" rel="noopener">
+        Obtain password</a
+      >
+      (opens in a new tab)
+    </span>
+  </div>
+  <gr-overlay
+    id="generatedPasswordOverlay"
+    on-iron-overlay-closed="_generatedPasswordOverlayClosed"
+    with-backdrop=""
+  >
+    <div class="gr-form-styles">
+      <section id="generatedPasswordDisplay">
+        <span class="title">New Password:</span>
+        <span class="value">[[_generatedPassword]]</span>
+        <gr-copy-clipboard
+          has-tooltip=""
+          button-title="Copy password to clipboard"
+          hide-input=""
+          text="[[_generatedPassword]]"
+        >
+        </gr-copy-clipboard>
+      </section>
+      <section id="passwordWarning">
+        This password will not be displayed again.<br />
+        If you lose it, you will need to generate a new one.
+      </section>
+      <gr-button link="" class="closeButton" on-click="_closeOverlay"
+        >Close</gr-button
+      >
+    </div>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
index b31fa50..26fa84d 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-settings-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 74c5eed..d0d30ea 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../../../styles/gr-form-styles.js';
 import '../../admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js';
@@ -35,7 +33,7 @@
 ];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrIdentities extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.js b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.js
index f1424cc..bf50124 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_html.js
@@ -17,74 +17,91 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      tr th.emailAddressHeader,
-      tr th.identityHeader {
-        width: 15em;
-        padding: 0 10px;
-      }
-      tr td.statusColumn,
-      tr td.emailAddressColumn,
-      tr td.identityColumn {
-        word-break: break-word;
-      }
-      tr td.emailAddressColumn,
-      tr td.identityColumn {
-        padding: 4px 10px;
-        width: 15em;
-      }
-      .deleteButton {
-        float: right;
-      }
-      .deleteButton:not(.show) {
-        display: none;
-      }
-      .space {
-        margin-bottom: var(--spacing-l);
-      }
-    </style>
-    <div class="gr-form-styles">
-      <fieldset class="space">
-        <table>
-          <thead>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    tr th.emailAddressHeader,
+    tr th.identityHeader {
+      width: 15em;
+      padding: 0 10px;
+    }
+    tr td.statusColumn,
+    tr td.emailAddressColumn,
+    tr td.identityColumn {
+      word-break: break-word;
+    }
+    tr td.emailAddressColumn,
+    tr td.identityColumn {
+      padding: 4px 10px;
+      width: 15em;
+    }
+    .deleteButton {
+      float: right;
+    }
+    .deleteButton:not(.show) {
+      display: none;
+    }
+    .space {
+      margin-bottom: var(--spacing-l);
+    }
+  </style>
+  <div class="gr-form-styles">
+    <fieldset class="space">
+      <table>
+        <thead>
+          <tr>
+            <th class="statusHeader">Status</th>
+            <th class="emailAddressHeader">Email Address</th>
+            <th class="identityHeader">Identity</th>
+            <th class="deleteHeader"></th>
+          </tr>
+        </thead>
+        <tbody>
+          <template
+            is="dom-repeat"
+            items="[[_identities]]"
+            filter="filterIdentities"
+          >
             <tr>
-              <th class="statusHeader">Status</th>
-              <th class="emailAddressHeader">Email Address</th>
-              <th class="identityHeader">Identity</th>
-              <th class="deleteHeader"></th>
+              <td class="statusColumn">
+                [[_computeIsTrusted(item.trusted)]]
+              </td>
+              <td class="emailAddressColumn">[[item.email_address]]</td>
+              <td class="identityColumn">
+                [[_computeIdentity(item.identity)]]
+              </td>
+              <td class="deleteColumn">
+                <gr-button
+                  class$="deleteButton [[_computeHideDeleteClass(item.can_delete)]]"
+                  on-click="_handleDeleteItem"
+                >
+                  Delete
+                </gr-button>
+              </td>
             </tr>
-          </thead>
-          <tbody>
-            <template is="dom-repeat" items="[[_identities]]" filter="filterIdentities">
-              <tr>
-                <td class="statusColumn">
-                  [[_computeIsTrusted(item.trusted)]]
-                </td>
-                <td class="emailAddressColumn">[[item.email_address]]</td>
-                <td class="identityColumn">[[_computeIdentity(item.identity)]]</td>
-                <td class="deleteColumn">
-                  <gr-button class\$="deleteButton [[_computeHideDeleteClass(item.can_delete)]]" on-click="_handleDeleteItem">
-                    Delete
-                  </gr-button>
-                </td>
-              </tr>
-            </template>
-          </tbody>
-        </table>
+          </template>
+        </tbody>
+      </table>
+    </fieldset>
+    <template is="dom-if" if="[[_showLinkAnotherIdentity]]">
+      <fieldset>
+        <a href$="[[_computeLinkAnotherIdentity()]]">
+          <gr-button id="linkAnotherIdentity" link=""
+            >Link Another Identity</gr-button
+          >
+        </a>
       </fieldset>
-      <template is="dom-if" if="[[_showLinkAnotherIdentity]]">
-        <fieldset>
-          <a href\$="[[_computeLinkAnotherIdentity()]]">
-            <gr-button id="linkAnotherIdentity" link="">Link Another Identity</gr-button>
-          </a>
-        </fieldset>
-      </template>
-    </div>
-    <gr-overlay id="overlay" with-backdrop="">
-      <gr-confirm-delete-item-dialog class="confirmDialog" on-confirm="_handleDeleteItemConfirm" on-cancel="_handleConfirmDialogCancel" item="[[_idName]]" item-type="id"></gr-confirm-delete-item-dialog>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </template>
+  </div>
+  <gr-overlay id="overlay" with-backdrop="">
+    <gr-confirm-delete-item-dialog
+      class="confirmDialog"
+      on-confirm="_handleDeleteItemConfirm"
+      on-cancel="_handleConfirmDialogCancel"
+      item="[[_idName]]"
+      item-type="id"
+    ></gr-confirm-delete-item-dialog>
+  </gr-overlay>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html
index 5c40b47..0965826 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-identities</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -79,8 +80,8 @@
       row.querySelectorAll('td')[2].textContent
     );
 
-    assert.equal(nameCells[0], 'gerrit:gerrit');
-    assert.equal(nameCells[1], '');
+    assert.equal(nameCells[0].trim(), 'gerrit:gerrit');
+    assert.equal(nameCells[1].trim(), '');
   });
 
   test('renders email', () => {
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 42982fd..b68915a 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../shared/gr-button/gr-button.js';
 import '../../shared/gr-date-formatter/gr-date-formatter.js';
@@ -28,7 +26,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-menu-editor_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrMenuEditor extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.js b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.js
index 58b654f..ceb8958 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.js
@@ -17,72 +17,115 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .buttonColumn {
-        width: 2em;
-      }
-      .moveUpButton,
-      .moveDownButton {
-        width: 100%
-      }
-      tbody tr:first-of-type td .moveUpButton,
-      tbody tr:last-of-type td .moveDownButton {
-        display: none;
-      }
-      td.urlCell {
-        word-break: break-word;
-      }
-      .newUrlInput {
-        min-width: 23em;
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div class="gr-form-styles">
-      <table>
-        <thead>
+  <style include="shared-styles">
+    .buttonColumn {
+      width: 2em;
+    }
+    .moveUpButton,
+    .moveDownButton {
+      width: 100%;
+    }
+    tbody tr:first-of-type td .moveUpButton,
+    tbody tr:last-of-type td .moveDownButton {
+      display: none;
+    }
+    td.urlCell {
+      word-break: break-word;
+    }
+    .newUrlInput {
+      min-width: 23em;
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div class="gr-form-styles">
+    <table>
+      <thead>
+        <tr>
+          <th class="nameHeader">Name</th>
+          <th class="url-header">URL</th>
+        </tr>
+      </thead>
+      <tbody>
+        <template is="dom-repeat" items="[[menuItems]]">
           <tr>
-            <th class="nameHeader">Name</th>
-            <th class="url-header">URL</th>
+            <td>[[item.name]]</td>
+            <td class="urlCell">[[item.url]]</td>
+            <td class="buttonColumn">
+              <gr-button
+                link=""
+                data-index$="[[index]]"
+                on-click="_handleMoveUpButton"
+                class="moveUpButton"
+                >↑</gr-button
+              >
+            </td>
+            <td class="buttonColumn">
+              <gr-button
+                link=""
+                data-index$="[[index]]"
+                on-click="_handleMoveDownButton"
+                class="moveDownButton"
+                >↓</gr-button
+              >
+            </td>
+            <td>
+              <gr-button
+                link=""
+                data-index$="[[index]]"
+                on-click="_handleDeleteButton"
+                class="remove-button"
+                >Delete</gr-button
+              >
+            </td>
           </tr>
-        </thead>
-        <tbody>
-          <template is="dom-repeat" items="[[menuItems]]">
-            <tr>
-              <td>[[item.name]]</td>
-              <td class="urlCell">[[item.url]]</td>
-              <td class="buttonColumn">
-                <gr-button link="" data-index\$="[[index]]" on-click="_handleMoveUpButton" class="moveUpButton">↑</gr-button>
-              </td>
-              <td class="buttonColumn">
-                <gr-button link="" data-index\$="[[index]]" on-click="_handleMoveDownButton" class="moveDownButton">↓</gr-button>
-              </td>
-              <td>
-                <gr-button link="" data-index\$="[[index]]" on-click="_handleDeleteButton" class="remove-button">Delete</gr-button>
-              </td>
-            </tr>
-          </template>
-        </tbody>
-        <tfoot>
-          <tr>
-            <th>
-              <iron-input placeholder="New Title" on-keydown="_handleInputKeydown" bind-value="{{_newName}}">
-                <input is="iron-input" placeholder="New Title" on-keydown="_handleInputKeydown" bind-value="{{_newName}}">
-              </iron-input>
-            </th>
-            <th>
-              <iron-input class="newUrlInput" placeholder="New URL" on-keydown="_handleInputKeydown" bind-value="{{_newUrl}}">
-                <input class="newUrlInput" is="iron-input" placeholder="New URL" on-keydown="_handleInputKeydown" bind-value="{{_newUrl}}">
-              </iron-input>
-            </th>
-            <th></th>
-            <th></th>
-            <th>
-              <gr-button link="" disabled\$="[[_computeAddDisabled(_newName, _newUrl)]]" on-click="_handleAddButton">Add</gr-button>
-            </th>
-          </tr>
-        </tfoot>
-      </table>
-    </div>
+        </template>
+      </tbody>
+      <tfoot>
+        <tr>
+          <th>
+            <iron-input
+              placeholder="New Title"
+              on-keydown="_handleInputKeydown"
+              bind-value="{{_newName}}"
+            >
+              <input
+                is="iron-input"
+                placeholder="New Title"
+                on-keydown="_handleInputKeydown"
+                bind-value="{{_newName}}"
+              />
+            </iron-input>
+          </th>
+          <th>
+            <iron-input
+              class="newUrlInput"
+              placeholder="New URL"
+              on-keydown="_handleInputKeydown"
+              bind-value="{{_newUrl}}"
+            >
+              <input
+                class="newUrlInput"
+                is="iron-input"
+                placeholder="New URL"
+                on-keydown="_handleInputKeydown"
+                bind-value="{{_newUrl}}"
+              />
+            </iron-input>
+          </th>
+          <th></th>
+          <th></th>
+          <th>
+            <gr-button
+              link=""
+              disabled$="[[_computeAddDisabled(_newName, _newUrl)]]"
+              on-click="_handleAddButton"
+              >Add</gr-button
+            >
+          </th>
+        </tr>
+      </tfoot>
+    </table>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
index b3e9a15..9c8db6d 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-settings-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6635de2..1da513b 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/gr-form-styles.js';
 import '../../shared/gr-button/gr-button.js';
@@ -27,7 +25,7 @@
 import {htmlTemplate} from './gr-registration-dialog_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRegistrationDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.js
index 737e6d5..3559ba6 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_html.js
@@ -17,94 +17,117 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      main {
-        max-width: 46em;
-      }
-      :host(.loading) main {
-        display: none;
-      }
-      .loadingMessage {
-        display: none;
-        font-style: italic;
-      }
-      :host(.loading) .loadingMessage {
-        display: block;
-      }
-      hr {
-        margin-top: var(--spacing-l);
-        margin-bottom: var(--spacing-l);
-      }
-      header {
-        border-bottom: 1px solid var(--border-color);
-        font-weight: var(--font-weight-bold);
-        margin-bottom: var(--spacing-l);
-      }
-      .container {
-        padding: var(--spacing-m) var(--spacing-xl);
-      }
-      footer {
-        display: flex;
-        justify-content: flex-end;
-      }
-      footer gr-button {
-        margin-left: var(--spacing-l);
-      }
-      input {
-        width: 20em;
-      }
-      section.hide {
-        display: none;
-      }
-    </style>
-    <div class="container gr-form-styles">
-      <header>Please confirm your contact information</header>
-      <div class="loadingMessage">Loading...</div>
-      <main>
-        <p>
-          The following contact information was automatically obtained when you
-          signed in to the site. This information is used to display who you are
-          to others, and to send updates to code reviews you have either started
-          or subscribed to.
-        </p>
-        <hr>
-        <section>
-          <div class="title">Full Name</div>
-          <iron-input bind-value="{{_account.name}}">
-            <input is="iron-input" id="name" bind-value="{{_account.name}}" disabled="[[_saving]]">
-          </iron-input>
-        </section>
-        <section class\$="[[_computeUsernameClass(_usernameMutable)]]">
-          <div class="title">Username</div>
-          <iron-input bind-value="{{_account.username}}">
-            <input is="iron-input" id="username" bind-value="{{_account.username}}" disabled="[[_saving]]">
-          </iron-input>
-        </section>
-        <section>
-          <div class="title">Preferred Email</div>
-          <select id="email" disabled="[[_saving]]">
-            <option value="[[_account.email]]">[[_account.email]]</option>
-            <template is="dom-repeat" items="[[_account.secondary_emails]]">
-              <option value="[[item]]">[[item]]</option>
-            </template>
-          </select>
-        </section>
-        <hr>
-        <p>
-          More configuration options for Gerrit may be found in the
-          <a on-click="close" href\$="[[settingsUrl]]">settings</a>.
-        </p>
-      </main>
-      <footer>
-        <gr-button id="closeButton" link="" disabled="[[_saving]]" on-click="_handleClose">Close</gr-button>
-        <gr-button id="saveButton" primary="" link="" disabled="[[_computeSaveDisabled(_account.name, _account.email, _saving)]]" on-click="_handleSave">Save</gr-button>
-      </footer>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    main {
+      max-width: 46em;
+    }
+    :host(.loading) main {
+      display: none;
+    }
+    .loadingMessage {
+      display: none;
+      font-style: italic;
+    }
+    :host(.loading) .loadingMessage {
+      display: block;
+    }
+    hr {
+      margin-top: var(--spacing-l);
+      margin-bottom: var(--spacing-l);
+    }
+    header {
+      border-bottom: 1px solid var(--border-color);
+      font-weight: var(--font-weight-bold);
+      margin-bottom: var(--spacing-l);
+    }
+    .container {
+      padding: var(--spacing-m) var(--spacing-xl);
+    }
+    footer {
+      display: flex;
+      justify-content: flex-end;
+    }
+    footer gr-button {
+      margin-left: var(--spacing-l);
+    }
+    input {
+      width: 20em;
+    }
+    section.hide {
+      display: none;
+    }
+  </style>
+  <div class="container gr-form-styles">
+    <header>Please confirm your contact information</header>
+    <div class="loadingMessage">Loading...</div>
+    <main>
+      <p>
+        The following contact information was automatically obtained when you
+        signed in to the site. This information is used to display who you are
+        to others, and to send updates to code reviews you have either started
+        or subscribed to.
+      </p>
+      <hr />
+      <section>
+        <div class="title">Full Name</div>
+        <iron-input bind-value="{{_account.name}}">
+          <input
+            is="iron-input"
+            id="name"
+            bind-value="{{_account.name}}"
+            disabled="[[_saving]]"
+          />
+        </iron-input>
+      </section>
+      <section class$="[[_computeUsernameClass(_usernameMutable)]]">
+        <div class="title">Username</div>
+        <iron-input bind-value="{{_account.username}}">
+          <input
+            is="iron-input"
+            id="username"
+            bind-value="{{_account.username}}"
+            disabled="[[_saving]]"
+          />
+        </iron-input>
+      </section>
+      <section>
+        <div class="title">Preferred Email</div>
+        <select id="email" disabled="[[_saving]]">
+          <option value="[[_account.email]]">[[_account.email]]</option>
+          <template is="dom-repeat" items="[[_account.secondary_emails]]">
+            <option value="[[item]]">[[item]]</option>
+          </template>
+        </select>
+      </section>
+      <hr />
+      <p>
+        More configuration options for Gerrit may be found in the
+        <a on-click="close" href$="[[settingsUrl]]">settings</a>.
+      </p>
+    </main>
+    <footer>
+      <gr-button
+        id="closeButton"
+        link=""
+        disabled="[[_saving]]"
+        on-click="_handleClose"
+        >Close</gr-button
+      >
+      <gr-button
+        id="saveButton"
+        primary=""
+        link=""
+        disabled="[[_computeSaveDisabled(_account.name, _account.email, _saving)]]"
+        on-click="_handleSave"
+        >Save</gr-button
+      >
+    </footer>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
index 501dae5..a3f8548 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-registration-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 3884a15..2455cec 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
@@ -14,13 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-settings-item_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrSettingsItem extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.js
index accb8c8..e26faab 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item_html.js
@@ -17,12 +17,12 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style>
-      :host {
-        display: block;
-        margin-bottom: var(--spacing-xxl);
-      }
-    </style>
-    <h2 id="[[anchor]]">[[title]]</h2>
-    <slot></slot>
+  <style>
+    :host {
+      display: block;
+      margin-bottom: var(--spacing-xxl);
+    }
+  </style>
+  <h2 id="[[anchor]]">[[title]]</h2>
+  <slot></slot>
 `;
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 5b11516..4d839f8 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
@@ -14,15 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/gr-page-nav-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-settings-menu-item_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrSettingsMenuItem extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.js
index 5cb129f..95433ac 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item_html.js
@@ -17,13 +17,13 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-page-nav-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div class="navStyles">
-      <li><a href\$="[[href]]">[[title]]</a></li>
-    </div>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-page-nav-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div class="navStyles">
+    <li><a href$="[[href]]">[[title]]</a></li>
+  </div>
 `;
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 c2f97ca..95f7a2c 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '@polymer/paper-toggle-button/paper-toggle-button.js';
 import '../../../styles/gr-form-styles.js';
@@ -78,7 +76,7 @@
 ];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrSettingsView extends mixinBehaviors( [
   DocsUrlBehavior,
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.js
index 0f03ec1..7e04b29 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.js
@@ -17,367 +17,520 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        color: var(--primary-text-color);
-      }
-      .newEmailInput {
-        width: 20em;
-      }
-      #email {
-        margin-bottom: var(--spacing-l);
-      }
-      main section.darkToggle {
-        display: block;
-      }
-      .filters p,
-      .darkToggle p {
-        margin-bottom: var(--spacing-l);
-      }
-      .queryExample em {
-        color: violet;
-      }
-      .toggle {
-        align-items: center;
-        display: flex;
-        margin-bottom: var(--spacing-l);
-        margin-right: var(--spacing-l);
-      }
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-menu-page-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-page-nav-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div class="loading" hidden\$="[[!_loading]]">Loading...</div>
-    <div hidden\$="[[_loading]]" hidden="">
-      <gr-page-nav class="navStyles">
-        <ul>
-          <li><a href="#Profile">Profile</a></li>
-          <li><a href="#Preferences">Preferences</a></li>
-          <li><a href="#DiffPreferences">Diff Preferences</a></li>
-          <li><a href="#EditPreferences">Edit Preferences</a></li>
-          <li><a href="#Menu">Menu</a></li>
-          <li><a href="#ChangeTableColumns">Change Table Columns</a></li>
-          <li><a href="#Notifications">Notifications</a></li>
-          <li><a href="#EmailAddresses">Email Addresses</a></li>
-          <template is="dom-if" if="[[_showHttpAuth(_serverConfig)]]">
-            <li><a href="#HTTPCredentials">HTTP Credentials</a></li>
-          </template>
-          <li hidden\$="[[!_serverConfig.sshd]]"><a href="#SSHKeys">
+  <style include="shared-styles">
+    :host {
+      color: var(--primary-text-color);
+    }
+    .newEmailInput {
+      width: 20em;
+    }
+    #email {
+      margin-bottom: var(--spacing-l);
+    }
+    main section.darkToggle {
+      display: block;
+    }
+    .filters p,
+    .darkToggle p {
+      margin-bottom: var(--spacing-l);
+    }
+    .queryExample em {
+      color: violet;
+    }
+    .toggle {
+      align-items: center;
+      display: flex;
+      margin-bottom: var(--spacing-l);
+      margin-right: var(--spacing-l);
+    }
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-menu-page-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-page-nav-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div class="loading" hidden$="[[!_loading]]">Loading...</div>
+  <div hidden$="[[_loading]]" hidden="">
+    <gr-page-nav class="navStyles">
+      <ul>
+        <li><a href="#Profile">Profile</a></li>
+        <li><a href="#Preferences">Preferences</a></li>
+        <li><a href="#DiffPreferences">Diff Preferences</a></li>
+        <li><a href="#EditPreferences">Edit Preferences</a></li>
+        <li><a href="#Menu">Menu</a></li>
+        <li><a href="#ChangeTableColumns">Change Table Columns</a></li>
+        <li><a href="#Notifications">Notifications</a></li>
+        <li><a href="#EmailAddresses">Email Addresses</a></li>
+        <template is="dom-if" if="[[_showHttpAuth(_serverConfig)]]">
+          <li><a href="#HTTPCredentials">HTTP Credentials</a></li>
+        </template>
+        <li hidden$="[[!_serverConfig.sshd]]">
+          <a href="#SSHKeys">
             SSH Keys
-          </a></li>
-          <li hidden\$="[[!_serverConfig.receive.enable_signed_push]]"><a href="#GPGKeys">
+          </a>
+        </li>
+        <li hidden$="[[!_serverConfig.receive.enable_signed_push]]">
+          <a href="#GPGKeys">
             GPG Keys
-          </a></li>
-          <li><a href="#Groups">Groups</a></li>
-          <li><a href="#Identities">Identities</a></li>
-          <template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
-            <li>
-              <a href="#Agreements">Agreements</a>
-            </li>
-          </template>
-          <li><a href="#MailFilters">Mail Filters</a></li>
-          <gr-endpoint-decorator name="settings-menu-item">
-          </gr-endpoint-decorator>
-        </ul>
-      </gr-page-nav>
-      <main class="gr-form-styles">
-        <h1>User Settings</h1>
-        <section class="darkToggle">
-          <div class="toggle">
-            <paper-toggle-button checked="[[_isDark]]" on-change="_handleToggleDark"></paper-toggle-button>
-            <div>Dark theme (alpha)</div>
-          </div>
+          </a>
+        </li>
+        <li><a href="#Groups">Groups</a></li>
+        <li><a href="#Identities">Identities</a></li>
+        <template
+          is="dom-if"
+          if="[[_serverConfig.auth.use_contributor_agreements]]"
+        >
+          <li>
+            <a href="#Agreements">Agreements</a>
+          </li>
+        </template>
+        <li><a href="#MailFilters">Mail Filters</a></li>
+        <gr-endpoint-decorator name="settings-menu-item">
+        </gr-endpoint-decorator>
+      </ul>
+    </gr-page-nav>
+    <main class="gr-form-styles">
+      <h1>User Settings</h1>
+      <section class="darkToggle">
+        <div class="toggle">
+          <paper-toggle-button
+            checked="[[_isDark]]"
+            on-change="_handleToggleDark"
+          ></paper-toggle-button>
+          <div>Dark theme (alpha)</div>
+        </div>
+        <p>
+          Gerrit's dark theme is in early alpha, and almost definitely will not
+          play nicely with themes set by specific Gerrit hosts. Filing feedback
+          via the link in the app footer is strongly encouraged!
+        </p>
+      </section>
+      <h2 id="Profile" class$="[[_computeHeaderClass(_accountInfoChanged)]]">
+        Profile
+      </h2>
+      <fieldset id="profile">
+        <gr-account-info
+          id="accountInfo"
+          has-unsaved-changes="{{_accountInfoChanged}}"
+        ></gr-account-info>
+        <gr-button
+          on-click="_handleSaveAccountInfo"
+          disabled="[[!_accountInfoChanged]]"
+          >Save changes</gr-button
+        >
+      </fieldset>
+      <h2 id="Preferences" class$="[[_computeHeaderClass(_prefsChanged)]]">
+        Preferences
+      </h2>
+      <fieldset id="preferences">
+        <section>
+          <span class="title">Changes per page</span>
+          <span class="value">
+            <gr-select bind-value="{{_localPrefs.changes_per_page}}">
+              <select>
+                <option value="10">10 rows per page</option>
+                <option value="25">25 rows per page</option>
+                <option value="50">50 rows per page</option>
+                <option value="100">100 rows per page</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Date/time format</span>
+          <span class="value">
+            <gr-select bind-value="{{_localPrefs.date_format}}">
+              <select>
+                <option value="STD">Jun 3 ; Jun 3, 2016</option>
+                <option value="US">06/03 ; 06/03/16</option>
+                <option value="ISO">06-03 ; 2016-06-03</option>
+                <option value="EURO">3. Jun ; 03.06.2016</option>
+                <option value="UK">03/06 ; 03/06/2016</option>
+              </select>
+            </gr-select>
+            <gr-select bind-value="{{_localPrefs.time_format}}">
+              <select>
+                <option value="HHMM_12">4:10 PM</option>
+                <option value="HHMM_24">16:10</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Email notifications</span>
+          <span class="value">
+            <gr-select bind-value="{{_localPrefs.email_strategy}}">
+              <select>
+                <option value="CC_ON_OWN_COMMENTS">Every comment</option>
+                <option value="ENABLED">Only comments left by others</option>
+                <option value="DISABLED">None</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section hidden$="[[!_localPrefs.email_format]]">
+          <span class="title">Email format</span>
+          <span class="value">
+            <gr-select bind-value="{{_localPrefs.email_format}}">
+              <select>
+                <option value="HTML_PLAINTEXT">HTML and plaintext</option>
+                <option value="PLAINTEXT">Plaintext only</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section hidden$="[[!_localPrefs.default_base_for_merges]]">
+          <span class="title">Default Base For Merges</span>
+          <span class="value">
+            <gr-select bind-value="{{_localPrefs.default_base_for_merges}}">
+              <select>
+                <option value="AUTO_MERGE">Auto Merge</option>
+                <option value="FIRST_PARENT">First Parent</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Show Relative Dates In Changes Table</span>
+          <span class="value">
+            <input
+              id="relativeDateInChangeTable"
+              type="checkbox"
+              checked$="[[_localPrefs.relative_date_in_change_table]]"
+              on-change="_handleRelativeDateInChangeTable"
+            />
+          </span>
+        </section>
+        <section>
+          <span class="title">Diff view</span>
+          <span class="value">
+            <gr-select bind-value="{{_localPrefs.diff_view}}">
+              <select>
+                <option value="SIDE_BY_SIDE">Side by side</option>
+                <option value="UNIFIED_DIFF">Unified diff</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Show size bars in file list</span>
+          <span class="value">
+            <input
+              id="showSizeBarsInFileList"
+              type="checkbox"
+              checked$="[[_localPrefs.size_bar_in_change_table]]"
+              on-change="_handleShowSizeBarsInFileListChanged"
+            />
+          </span>
+        </section>
+        <section>
+          <span class="title">Publish comments on push</span>
+          <span class="value">
+            <input
+              id="publishCommentsOnPush"
+              type="checkbox"
+              checked$="[[_localPrefs.publish_comments_on_push]]"
+              on-change="_handlePublishCommentsOnPushChanged"
+            />
+          </span>
+        </section>
+        <section>
+          <span class="title"
+            >Set new changes to "work in progress" by default</span
+          >
+          <span class="value">
+            <input
+              id="workInProgressByDefault"
+              type="checkbox"
+              checked$="[[_localPrefs.work_in_progress_by_default]]"
+              on-change="_handleWorkInProgressByDefault"
+            />
+          </span>
+        </section>
+        <section>
+          <span class="title">
+            Insert Signed-off-by Footer For Inline Edit Changes
+          </span>
+          <span class="value">
+            <input
+              id="insertSignedOff"
+              type="checkbox"
+              checked$="[[_localPrefs.signed_off_by]]"
+              on-change="_handleInsertSignedOff"
+            />
+          </span>
+        </section>
+        <gr-button
+          id="savePrefs"
+          on-click="_handleSavePreferences"
+          disabled="[[!_prefsChanged]]"
+          >Save changes</gr-button
+        >
+      </fieldset>
+      <h2
+        id="DiffPreferences"
+        class$="[[_computeHeaderClass(_diffPrefsChanged)]]"
+      >
+        Diff Preferences
+      </h2>
+      <fieldset id="diffPreferences">
+        <gr-diff-preferences
+          id="diffPrefs"
+          has-unsaved-changes="{{_diffPrefsChanged}}"
+        ></gr-diff-preferences>
+        <gr-button
+          id="saveDiffPrefs"
+          on-click="_handleSaveDiffPreferences"
+          disabled$="[[!_diffPrefsChanged]]"
+          >Save changes</gr-button
+        >
+      </fieldset>
+      <h2
+        id="EditPreferences"
+        class$="[[_computeHeaderClass(_editPrefsChanged)]]"
+      >
+        Edit Preferences
+      </h2>
+      <fieldset id="editPreferences">
+        <gr-edit-preferences
+          id="editPrefs"
+          has-unsaved-changes="{{_editPrefsChanged}}"
+        ></gr-edit-preferences>
+        <gr-button
+          id="saveEditPrefs"
+          on-click="_handleSaveEditPreferences"
+          disabled$="[[!_editPrefsChanged]]"
+          >Save changes</gr-button
+        >
+      </fieldset>
+      <h2 id="Menu" class$="[[_computeHeaderClass(_menuChanged)]]">Menu</h2>
+      <fieldset id="menu">
+        <gr-menu-editor menu-items="{{_localMenu}}"></gr-menu-editor>
+        <gr-button
+          id="saveMenu"
+          on-click="_handleSaveMenu"
+          disabled="[[!_menuChanged]]"
+          >Save changes</gr-button
+        >
+        <gr-button id="resetMenu" link="" on-click="_handleResetMenuButton"
+          >Reset</gr-button
+        >
+      </fieldset>
+      <h2
+        id="ChangeTableColumns"
+        class$="[[_computeHeaderClass(_changeTableChanged)]]"
+      >
+        Change Table Columns
+      </h2>
+      <fieldset id="changeTableColumns">
+        <gr-change-table-editor
+          show-number="{{_showNumber}}"
+          displayed-columns="{{_localChangeTableColumns}}"
+        >
+        </gr-change-table-editor>
+        <gr-button
+          id="saveChangeTable"
+          on-click="_handleSaveChangeTable"
+          disabled="[[!_changeTableChanged]]"
+          >Save changes</gr-button
+        >
+      </fieldset>
+      <h2
+        id="Notifications"
+        class$="[[_computeHeaderClass(_watchedProjectsChanged)]]"
+      >
+        Notifications
+      </h2>
+      <fieldset id="watchedProjects">
+        <gr-watched-projects-editor
+          has-unsaved-changes="{{_watchedProjectsChanged}}"
+          id="watchedProjectsEditor"
+        ></gr-watched-projects-editor>
+        <gr-button
+          on-click="_handleSaveWatchedProjects"
+          disabled$="[[!_watchedProjectsChanged]]"
+          id="_handleSaveWatchedProjects"
+          >Save changes</gr-button
+        >
+      </fieldset>
+      <h2 id="EmailAddresses" class$="[[_computeHeaderClass(_emailsChanged)]]">
+        Email Addresses
+      </h2>
+      <fieldset id="email">
+        <gr-email-editor
+          id="emailEditor"
+          has-unsaved-changes="{{_emailsChanged}}"
+        ></gr-email-editor>
+        <gr-button on-click="_handleSaveEmails" disabled$="[[!_emailsChanged]]"
+          >Save changes</gr-button
+        >
+      </fieldset>
+      <fieldset id="newEmail">
+        <section>
+          <span class="title">New email address</span>
+          <span class="value">
+            <iron-input
+              class="newEmailInput"
+              bind-value="{{_newEmail}}"
+              type="text"
+              on-keydown="_handleNewEmailKeydown"
+              placeholder="email@example.com"
+            >
+              <input
+                class="newEmailInput"
+                bind-value="{{_newEmail}}"
+                is="iron-input"
+                type="text"
+                disabled="[[_addingEmail]]"
+                on-keydown="_handleNewEmailKeydown"
+                placeholder="email@example.com"
+              />
+            </iron-input>
+          </span>
+        </section>
+        <section
+          id="verificationSentMessage"
+          hidden$="[[!_lastSentVerificationEmail]]"
+        >
           <p>
-            Gerrit's dark theme is in early alpha, and almost definitely will
-            not play nicely with themes set by specific Gerrit hosts. Filing
-            feedback via the link in the app footer is strongly encouraged!
+            A verification email was sent to
+            <em>[[_lastSentVerificationEmail]]</em>. Please check your inbox.
           </p>
         </section>
-        <h2 id="Profile" class\$="[[_computeHeaderClass(_accountInfoChanged)]]">Profile</h2>
-        <fieldset id="profile">
-          <gr-account-info id="accountInfo" has-unsaved-changes="{{_accountInfoChanged}}"></gr-account-info>
-          <gr-button on-click="_handleSaveAccountInfo" disabled="[[!_accountInfoChanged]]">Save changes</gr-button>
-        </fieldset>
-        <h2 id="Preferences" class\$="[[_computeHeaderClass(_prefsChanged)]]">Preferences</h2>
-        <fieldset id="preferences">
-          <section>
-            <span class="title">Changes per page</span>
-            <span class="value">
-              <gr-select bind-value="{{_localPrefs.changes_per_page}}">
-                <select>
-                  <option value="10">10 rows per page</option>
-                  <option value="25">25 rows per page</option>
-                  <option value="50">50 rows per page</option>
-                  <option value="100">100 rows per page</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section>
-            <span class="title">Date/time format</span>
-            <span class="value">
-              <gr-select bind-value="{{_localPrefs.date_format}}">
-                <select>
-                  <option value="STD">Jun 3 ; Jun 3, 2016</option>
-                  <option value="US">06/03 ; 06/03/16</option>
-                  <option value="ISO">06-03 ; 2016-06-03</option>
-                  <option value="EURO">3. Jun ; 03.06.2016</option>
-                  <option value="UK">03/06 ; 03/06/2016</option>
-                </select>
-              </gr-select>
-              <gr-select bind-value="{{_localPrefs.time_format}}">
-                <select>
-                  <option value="HHMM_12">4:10 PM</option>
-                  <option value="HHMM_24">16:10</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section>
-            <span class="title">Email notifications</span>
-            <span class="value">
-              <gr-select bind-value="{{_localPrefs.email_strategy}}">
-                <select>
-                  <option value="CC_ON_OWN_COMMENTS">Every comment</option>
-                  <option value="ENABLED">Only comments left by others</option>
-                  <option value="DISABLED">None</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section hidden\$="[[!_localPrefs.email_format]]">
-            <span class="title">Email format</span>
-            <span class="value">
-              <gr-select bind-value="{{_localPrefs.email_format}}">
-                <select>
-                  <option value="HTML_PLAINTEXT">HTML and plaintext</option>
-                  <option value="PLAINTEXT">Plaintext only</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section hidden\$="[[!_localPrefs.default_base_for_merges]]">
-            <span class="title">Default Base For Merges</span>
-            <span class="value">
-              <gr-select bind-value="{{_localPrefs.default_base_for_merges}}">
-                <select>
-                  <option value="AUTO_MERGE">Auto Merge</option>
-                  <option value="FIRST_PARENT">First Parent</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section>
-            <span class="title">Show Relative Dates In Changes Table</span>
-            <span class="value">
-              <input id="relativeDateInChangeTable" type="checkbox" checked\$="[[_localPrefs.relative_date_in_change_table]]" on-change="_handleRelativeDateInChangeTable">
-            </span>
-          </section>
-          <section>
-            <span class="title">Diff view</span>
-            <span class="value">
-              <gr-select bind-value="{{_localPrefs.diff_view}}">
-                <select>
-                  <option value="SIDE_BY_SIDE">Side by side</option>
-                  <option value="UNIFIED_DIFF">Unified diff</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section>
-            <span class="title">Show size bars in file list</span>
-            <span class="value">
-              <input id="showSizeBarsInFileList" type="checkbox" checked\$="[[_localPrefs.size_bar_in_change_table]]" on-change="_handleShowSizeBarsInFileListChanged">
-            </span>
-          </section>
-          <section>
-            <span class="title">Publish comments on push</span>
-            <span class="value">
-              <input id="publishCommentsOnPush" type="checkbox" checked\$="[[_localPrefs.publish_comments_on_push]]" on-change="_handlePublishCommentsOnPushChanged">
-            </span>
-          </section>
-          <section>
-            <span class="title">Set new changes to "work in progress" by default</span>
-            <span class="value">
-              <input id="workInProgressByDefault" type="checkbox" checked\$="[[_localPrefs.work_in_progress_by_default]]" on-change="_handleWorkInProgressByDefault">
-            </span>
-          </section>
-          <section>
-            <span class="title">
-              Insert Signed-off-by Footer For Inline Edit Changes
-            </span>
-            <span class="value">
-              <input id="insertSignedOff" type="checkbox" checked\$="[[_localPrefs.signed_off_by]]" on-change="_handleInsertSignedOff">
-            </span>
-          </section>
-          <gr-button id="savePrefs" on-click="_handleSavePreferences" disabled="[[!_prefsChanged]]">Save changes</gr-button>
-        </fieldset>
-        <h2 id="DiffPreferences" class\$="[[_computeHeaderClass(_diffPrefsChanged)]]">
-          Diff Preferences
-        </h2>
-        <fieldset id="diffPreferences">
-          <gr-diff-preferences id="diffPrefs" has-unsaved-changes="{{_diffPrefsChanged}}"></gr-diff-preferences>
-          <gr-button id="saveDiffPrefs" on-click="_handleSaveDiffPreferences" disabled\$="[[!_diffPrefsChanged]]">Save changes</gr-button>
-        </fieldset>
-        <h2 id="EditPreferences" class\$="[[_computeHeaderClass(_editPrefsChanged)]]">
-          Edit Preferences
-        </h2>
-        <fieldset id="editPreferences">
-          <gr-edit-preferences id="editPrefs" has-unsaved-changes="{{_editPrefsChanged}}"></gr-edit-preferences>
-          <gr-button id="saveEditPrefs" on-click="_handleSaveEditPreferences" disabled\$="[[!_editPrefsChanged]]">Save changes</gr-button>
-        </fieldset>
-        <h2 id="Menu" class\$="[[_computeHeaderClass(_menuChanged)]]">Menu</h2>
-        <fieldset id="menu">
-          <gr-menu-editor menu-items="{{_localMenu}}"></gr-menu-editor>
-          <gr-button id="saveMenu" on-click="_handleSaveMenu" disabled="[[!_menuChanged]]">Save changes</gr-button>
-          <gr-button id="resetMenu" link="" on-click="_handleResetMenuButton">Reset</gr-button>
-        </fieldset>
-        <h2 id="ChangeTableColumns" class\$="[[_computeHeaderClass(_changeTableChanged)]]">
-          Change Table Columns
-        </h2>
-        <fieldset id="changeTableColumns">
-          <gr-change-table-editor show-number="{{_showNumber}}" displayed-columns="{{_localChangeTableColumns}}">
-          </gr-change-table-editor>
-          <gr-button id="saveChangeTable" on-click="_handleSaveChangeTable" disabled="[[!_changeTableChanged]]">Save changes</gr-button>
-        </fieldset>
-        <h2 id="Notifications" class\$="[[_computeHeaderClass(_watchedProjectsChanged)]]">
-          Notifications
-        </h2>
-        <fieldset id="watchedProjects">
-          <gr-watched-projects-editor has-unsaved-changes="{{_watchedProjectsChanged}}" id="watchedProjectsEditor"></gr-watched-projects-editor>
-          <gr-button on-click="_handleSaveWatchedProjects" disabled\$="[[!_watchedProjectsChanged]]" id="_handleSaveWatchedProjects">Save changes</gr-button>
-        </fieldset>
-        <h2 id="EmailAddresses" class\$="[[_computeHeaderClass(_emailsChanged)]]">
-          Email Addresses
-        </h2>
-        <fieldset id="email">
-          <gr-email-editor id="emailEditor" has-unsaved-changes="{{_emailsChanged}}"></gr-email-editor>
-          <gr-button on-click="_handleSaveEmails" disabled\$="[[!_emailsChanged]]">Save changes</gr-button>
-        </fieldset>
-        <fieldset id="newEmail">
-          <section>
-            <span class="title">New email address</span>
-            <span class="value">
-              <iron-input class="newEmailInput" bind-value="{{_newEmail}}" type="text" on-keydown="_handleNewEmailKeydown" placeholder="email@example.com">
-                <input class="newEmailInput" bind-value="{{_newEmail}}" is="iron-input" type="text" disabled="[[_addingEmail]]" on-keydown="_handleNewEmailKeydown" placeholder="email@example.com">
-              </iron-input>
-            </span>
-          </section>
-          <section id="verificationSentMessage" hidden\$="[[!_lastSentVerificationEmail]]">
-            <p>
-              A verification email was sent to
-              <em>[[_lastSentVerificationEmail]]</em>. Please check your inbox.
-            </p>
-          </section>
-          <gr-button disabled="[[!_computeAddEmailButtonEnabled(_newEmail, _addingEmail)]]" on-click="_handleAddEmailButton">Send verification</gr-button>
-        </fieldset>
-        <template is="dom-if" if="[[_showHttpAuth(_serverConfig)]]">
-          <div>
-            <h2 id="HTTPCredentials">HTTP Credentials</h2>
-            <fieldset>
-              <gr-http-password id="httpPass"></gr-http-password>
-            </fieldset>
-          </div>
-        </template>
-        <div hidden\$="[[!_serverConfig.sshd]]">
-          <h2 id="SSHKeys" class\$="[[_computeHeaderClass(_keysChanged)]]">SSH keys</h2>
-          <gr-ssh-editor id="sshEditor" has-unsaved-changes="{{_keysChanged}}"></gr-ssh-editor>
-        </div>
-        <div hidden\$="[[!_serverConfig.receive.enable_signed_push]]">
-          <h2 id="GPGKeys" class\$="[[_computeHeaderClass(_gpgKeysChanged)]]">GPG keys</h2>
-          <gr-gpg-editor id="gpgEditor" has-unsaved-changes="{{_gpgKeysChanged}}"></gr-gpg-editor>
-        </div>
-        <h2 id="Groups">Groups</h2>
-        <fieldset>
-          <gr-group-list id="groupList"></gr-group-list>
-        </fieldset>
-        <h2 id="Identities">Identities</h2>
-        <fieldset>
-          <gr-identities id="identities" server-config="[[_serverConfig]]"></gr-identities>
-        </fieldset>
-        <template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
-          <h2 id="Agreements">Agreements</h2>
+        <gr-button
+          disabled="[[!_computeAddEmailButtonEnabled(_newEmail, _addingEmail)]]"
+          on-click="_handleAddEmailButton"
+          >Send verification</gr-button
+        >
+      </fieldset>
+      <template is="dom-if" if="[[_showHttpAuth(_serverConfig)]]">
+        <div>
+          <h2 id="HTTPCredentials">HTTP Credentials</h2>
           <fieldset>
-            <gr-agreements-list id="agreementsList"></gr-agreements-list>
+            <gr-http-password id="httpPass"></gr-http-password>
           </fieldset>
-        </template>
-        <h2 id="MailFilters">Mail Filters</h2>
-        <fieldset class="filters">
-          <p>
-            Gerrit emails include metadata about the change to support
-            writing mail filters.
-          </p>
-          <p>
-            Here are some example Gmail queries that can be used for filters or
-            for searching through archived messages. View the
-            <a href\$="[[_getFilterDocsLink(_docsBaseUrl)]]" target="_blank" rel="nofollow">Gerrit documentation</a>
-            for the complete set of footers.
-          </p>
-          <table>
-            <tbody>
-              <tr><th>Name</th><th>Query</th></tr>
-              <tr>
-                <td>Changes requesting my review</td>
-                <td>
-                  <code class="queryExample">
-                    "Gerrit-Reviewer: <em>Your Name</em>
-                    &lt;<em>your.email@example.com</em>&gt;"
-                  </code>
-                </td>
-              </tr>
-              <tr>
-                <td>Changes from a specific owner</td>
-                <td>
-                  <code class="queryExample">
-                    "Gerrit-Owner: <em>Owner name</em>
-                    &lt;<em>owner.email@example.com</em>&gt;"
-                  </code>
-                </td>
-              </tr>
-              <tr>
-                <td>Changes targeting a specific branch</td>
-                <td>
-                  <code class="queryExample">
-                    "Gerrit-Branch: <em>branch-name</em>"
-                  </code>
-                </td>
-              </tr>
-              <tr>
-                <td>Changes in a specific project</td>
-                <td>
-                  <code class="queryExample">
-                    "Gerrit-Project: <em>project-name</em>"
-                  </code>
-                </td>
-              </tr>
-              <tr>
-                <td>Messages related to a specific Change ID</td>
-                <td>
-                  <code class="queryExample">
-                    "Gerrit-Change-Id: <em>Change ID</em>"
-                  </code>
-                </td>
-              </tr>
-              <tr>
-                <td>Messages related to a specific change number</td>
-                <td>
-                  <code class="queryExample">
-                    "Gerrit-Change-Number: <em>change number</em>"
-                  </code>
-                </td>
-              </tr>
-            </tbody>
-          </table>
+        </div>
+      </template>
+      <div hidden$="[[!_serverConfig.sshd]]">
+        <h2 id="SSHKeys" class$="[[_computeHeaderClass(_keysChanged)]]">
+          SSH keys
+        </h2>
+        <gr-ssh-editor
+          id="sshEditor"
+          has-unsaved-changes="{{_keysChanged}}"
+        ></gr-ssh-editor>
+      </div>
+      <div hidden$="[[!_serverConfig.receive.enable_signed_push]]">
+        <h2 id="GPGKeys" class$="[[_computeHeaderClass(_gpgKeysChanged)]]">
+          GPG keys
+        </h2>
+        <gr-gpg-editor
+          id="gpgEditor"
+          has-unsaved-changes="{{_gpgKeysChanged}}"
+        ></gr-gpg-editor>
+      </div>
+      <h2 id="Groups">Groups</h2>
+      <fieldset>
+        <gr-group-list id="groupList"></gr-group-list>
+      </fieldset>
+      <h2 id="Identities">Identities</h2>
+      <fieldset>
+        <gr-identities
+          id="identities"
+          server-config="[[_serverConfig]]"
+        ></gr-identities>
+      </fieldset>
+      <template
+        is="dom-if"
+        if="[[_serverConfig.auth.use_contributor_agreements]]"
+      >
+        <h2 id="Agreements">Agreements</h2>
+        <fieldset>
+          <gr-agreements-list id="agreementsList"></gr-agreements-list>
         </fieldset>
-        <gr-endpoint-decorator name="settings-screen">
-        </gr-endpoint-decorator>
-      </main>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </template>
+      <h2 id="MailFilters">Mail Filters</h2>
+      <fieldset class="filters">
+        <p>
+          Gerrit emails include metadata about the change to support writing
+          mail filters.
+        </p>
+        <p>
+          Here are some example Gmail queries that can be used for filters or
+          for searching through archived messages. View the
+          <a
+            href$="[[_getFilterDocsLink(_docsBaseUrl)]]"
+            target="_blank"
+            rel="nofollow"
+            >Gerrit documentation</a
+          >
+          for the complete set of footers.
+        </p>
+        <table>
+          <tbody>
+            <tr>
+              <th>Name</th>
+              <th>Query</th>
+            </tr>
+            <tr>
+              <td>Changes requesting my review</td>
+              <td>
+                <code class="queryExample">
+                  "Gerrit-Reviewer: <em>Your Name</em>
+                  &lt;<em>your.email@example.com</em>&gt;"
+                </code>
+              </td>
+            </tr>
+            <tr>
+              <td>Changes from a specific owner</td>
+              <td>
+                <code class="queryExample">
+                  "Gerrit-Owner: <em>Owner name</em>
+                  &lt;<em>owner.email@example.com</em>&gt;"
+                </code>
+              </td>
+            </tr>
+            <tr>
+              <td>Changes targeting a specific branch</td>
+              <td>
+                <code class="queryExample">
+                  "Gerrit-Branch: <em>branch-name</em>"
+                </code>
+              </td>
+            </tr>
+            <tr>
+              <td>Changes in a specific project</td>
+              <td>
+                <code class="queryExample">
+                  "Gerrit-Project: <em>project-name</em>"
+                </code>
+              </td>
+            </tr>
+            <tr>
+              <td>Messages related to a specific Change ID</td>
+              <td>
+                <code class="queryExample">
+                  "Gerrit-Change-Id: <em>Change ID</em>"
+                </code>
+              </td>
+            </tr>
+            <tr>
+              <td>Messages related to a specific change number</td>
+              <td>
+                <code class="queryExample">
+                  "Gerrit-Change-Number: <em>change number</em>"
+                </code>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </fieldset>
+      <gr-endpoint-decorator name="settings-screen"> </gr-endpoint-decorator>
+    </main>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
index a99bfca..e430ecb 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-settings-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 814eb7a..d869128 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/gr-form-styles.js';
 import '../../shared/gr-button/gr-button.js';
@@ -29,7 +27,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-ssh-editor_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrSshEditor extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.js
index 3cefb90e..1f3c793 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_html.js
@@ -17,100 +17,127 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      .statusHeader {
-        width: 4em;
-      }
-      .keyHeader {
-        width: 7.5em;
-      }
-      #viewKeyOverlay {
-        padding: var(--spacing-xxl);
-        width: 50em;
-      }
-      .publicKey {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        overflow-x: scroll;
-        overflow-wrap: break-word;
-        width: 30em;
-      }
-      .closeButton {
-        bottom: 2em;
-        position: absolute;
-        right: 2em;
-      }
-      #existing {
-        margin-bottom: var(--spacing-l);
-      }
-      #existing .commentColumn {
-        min-width: 27em;
-        width: auto;
-      }
-    </style>
-    <div class="gr-form-styles">
-      <fieldset id="existing">
-        <table>
-          <thead>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    .statusHeader {
+      width: 4em;
+    }
+    .keyHeader {
+      width: 7.5em;
+    }
+    #viewKeyOverlay {
+      padding: var(--spacing-xxl);
+      width: 50em;
+    }
+    .publicKey {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      overflow-x: scroll;
+      overflow-wrap: break-word;
+      width: 30em;
+    }
+    .closeButton {
+      bottom: 2em;
+      position: absolute;
+      right: 2em;
+    }
+    #existing {
+      margin-bottom: var(--spacing-l);
+    }
+    #existing .commentColumn {
+      min-width: 27em;
+      width: auto;
+    }
+  </style>
+  <div class="gr-form-styles">
+    <fieldset id="existing">
+      <table>
+        <thead>
+          <tr>
+            <th class="commentColumn">Comment</th>
+            <th class="statusHeader">Status</th>
+            <th class="keyHeader">Public key</th>
+            <th></th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          <template is="dom-repeat" items="[[_keys]]" as="key">
             <tr>
-              <th class="commentColumn">Comment</th>
-              <th class="statusHeader">Status</th>
-              <th class="keyHeader">Public key</th>
-              <th></th>
-              <th></th>
+              <td class="commentColumn">[[key.comment]]</td>
+              <td>[[_getStatusLabel(key.valid)]]</td>
+              <td>
+                <gr-button link="" on-click="_showKey" data-index$="[[index]]"
+                  >Click to View</gr-button
+                >
+              </td>
+              <td>
+                <gr-copy-clipboard
+                  has-tooltip=""
+                  button-title="Copy SSH public key to clipboard"
+                  hide-input=""
+                  text="[[key.ssh_public_key]]"
+                >
+                </gr-copy-clipboard>
+              </td>
+              <td>
+                <gr-button
+                  link=""
+                  data-index$="[[index]]"
+                  on-click="_handleDeleteKey"
+                  >Delete</gr-button
+                >
+              </td>
             </tr>
-          </thead>
-          <tbody>
-            <template is="dom-repeat" items="[[_keys]]" as="key">
-              <tr>
-                <td class="commentColumn">[[key.comment]]</td>
-                <td>[[_getStatusLabel(key.valid)]]</td>
-                <td>
-                  <gr-button link="" on-click="_showKey" data-index\$="[[index]]">Click to View</gr-button>
-                </td>
-                <td>
-                  <gr-copy-clipboard has-tooltip="" button-title="Copy SSH public key to clipboard" hide-input="" text="[[key.ssh_public_key]]">
-                  </gr-copy-clipboard>
-                </td>
-                <td>
-                  <gr-button link="" data-index\$="[[index]]" on-click="_handleDeleteKey">Delete</gr-button>
-                </td>
-              </tr>
-            </template>
-          </tbody>
-        </table>
-        <gr-overlay id="viewKeyOverlay" with-backdrop="">
-          <fieldset>
-            <section>
-              <span class="title">Algorithm</span>
-              <span class="value">[[_keyToView.algorithm]]</span>
-            </section>
-            <section>
-              <span class="title">Public key</span>
-              <span class="value publicKey">[[_keyToView.encoded_key]]</span>
-            </section>
-            <section>
-              <span class="title">Comment</span>
-              <span class="value">[[_keyToView.comment]]</span>
-            </section>
-          </fieldset>
-          <gr-button class="closeButton" on-click="_closeOverlay">Close</gr-button>
-        </gr-overlay>
-        <gr-button on-click="save" disabled\$="[[!hasUnsavedChanges]]">Save changes</gr-button>
-      </fieldset>
-      <fieldset>
-        <section>
-          <span class="title">New SSH key</span>
-          <span class="value">
-            <iron-autogrow-textarea id="newKey" autocomplete="on" bind-value="{{_newKey}}" placeholder="New SSH Key"></iron-autogrow-textarea>
-          </span>
-        </section>
-        <gr-button id="addButton" link="" disabled\$="[[_computeAddButtonDisabled(_newKey)]]" on-click="_handleAddKey">Add new SSH key</gr-button>
-      </fieldset>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+          </template>
+        </tbody>
+      </table>
+      <gr-overlay id="viewKeyOverlay" with-backdrop="">
+        <fieldset>
+          <section>
+            <span class="title">Algorithm</span>
+            <span class="value">[[_keyToView.algorithm]]</span>
+          </section>
+          <section>
+            <span class="title">Public key</span>
+            <span class="value publicKey">[[_keyToView.encoded_key]]</span>
+          </section>
+          <section>
+            <span class="title">Comment</span>
+            <span class="value">[[_keyToView.comment]]</span>
+          </section>
+        </fieldset>
+        <gr-button class="closeButton" on-click="_closeOverlay"
+          >Close</gr-button
+        >
+      </gr-overlay>
+      <gr-button on-click="save" disabled$="[[!hasUnsavedChanges]]"
+        >Save changes</gr-button
+      >
+    </fieldset>
+    <fieldset>
+      <section>
+        <span class="title">New SSH key</span>
+        <span class="value">
+          <iron-autogrow-textarea
+            id="newKey"
+            autocomplete="on"
+            bind-value="{{_newKey}}"
+            placeholder="New SSH Key"
+          ></iron-autogrow-textarea>
+        </span>
+      </section>
+      <gr-button
+        id="addButton"
+        link=""
+        disabled$="[[_computeAddButtonDisabled(_newKey)]]"
+        on-click="_handleAddKey"
+        >Add new SSH key</gr-button
+      >
+    </fieldset>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
index ed1a7ca..56625ae 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-ssh-editor</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b8960e8..2af1bc7 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../shared/gr-autocomplete/gr-autocomplete.js';
 import '../../shared/gr-button/gr-button.js';
@@ -36,7 +34,7 @@
   {name: 'Abandons', key: 'notify_abandoned_changes'},
 ];
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrWatchedProjectsEditor extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.js b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.js
index bc381e0..b1ca653 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.js
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_html.js
@@ -17,77 +17,108 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      #watchedProjects .notifType {
-        text-align: center;
-        padding: 0 var(--spacing-s);
-      }
-      .notifControl {
-        cursor: pointer;
-        text-align: center;
-      }
-      .notifControl:hover {
-        outline: 1px solid var(--border-color);
-      }
-      .projectFilter {
-        color: var(--deemphasized-text-color);
-        font-style: italic;
-        margin-left: var(--spacing-l);
-      }
-      .newFilterInput {
-        width: 100%;
-      }
-    </style>
-    <div class="gr-form-styles">
-      <table id="watchedProjects">
-        <thead>
-          <tr>
-            <th>Repo</th>
-            <template is="dom-repeat" items="[[_getTypes()]]">
-              <th class="notifType">[[item.name]]</th>
-            </template>
-            <th></th>
-          </tr>
-        </thead>
-        <tbody>
-          <template is="dom-repeat" items="[[_projects]]" as="project" index-as="projectIndex">
-            <tr>
-              <td>
-                [[project.project]]
-                <template is="dom-if" if="[[project.filter]]">
-                  <div class="projectFilter">[[project.filter]]</div>
-                </template>
-              </td>
-              <template is="dom-repeat" items="[[_getTypes()]]" as="type">
-                <td class="notifControl" on-click="_handleNotifCellClick">
-                  <input type="checkbox" data-index\$="[[projectIndex]]" data-key\$="[[type.key]]" on-change="_handleCheckboxChange" checked\$="[[_computeCheckboxChecked(project, type.key)]]">
-                </td>
-              </template>
-              <td>
-                <gr-button link="" data-index\$="[[projectIndex]]" on-click="_handleRemoveProject">Delete</gr-button>
-              </td>
-            </tr>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    #watchedProjects .notifType {
+      text-align: center;
+      padding: 0 var(--spacing-s);
+    }
+    .notifControl {
+      cursor: pointer;
+      text-align: center;
+    }
+    .notifControl:hover {
+      outline: 1px solid var(--border-color);
+    }
+    .projectFilter {
+      color: var(--deemphasized-text-color);
+      font-style: italic;
+      margin-left: var(--spacing-l);
+    }
+    .newFilterInput {
+      width: 100%;
+    }
+  </style>
+  <div class="gr-form-styles">
+    <table id="watchedProjects">
+      <thead>
+        <tr>
+          <th>Repo</th>
+          <template is="dom-repeat" items="[[_getTypes()]]">
+            <th class="notifType">[[item.name]]</th>
           </template>
-        </tbody>
-        <tfoot>
+          <th></th>
+        </tr>
+      </thead>
+      <tbody>
+        <template
+          is="dom-repeat"
+          items="[[_projects]]"
+          as="project"
+          index-as="projectIndex"
+        >
           <tr>
-            <th>
-              <gr-autocomplete id="newProject" query="[[_query]]" threshold="1" allow-non-suggested-values="" tab-complete="" placeholder="Repo"></gr-autocomplete>
-            </th>
-            <th colspan\$="[[_getTypeCount()]]">
-              <iron-input class="newFilterInput" placeholder="branch:name, or other search expression">
-                <input id="newFilter" class="newFilterInput" is="iron-input" placeholder="branch:name, or other search expression">
-              </iron-input>
-            </th>
-            <th>
-              <gr-button link="" on-click="_handleAddProject">Add</gr-button>
-            </th>
+            <td>
+              [[project.project]]
+              <template is="dom-if" if="[[project.filter]]">
+                <div class="projectFilter">[[project.filter]]</div>
+              </template>
+            </td>
+            <template is="dom-repeat" items="[[_getTypes()]]" as="type">
+              <td class="notifControl" on-click="_handleNotifCellClick">
+                <input
+                  type="checkbox"
+                  data-index$="[[projectIndex]]"
+                  data-key$="[[type.key]]"
+                  on-change="_handleCheckboxChange"
+                  checked$="[[_computeCheckboxChecked(project, type.key)]]"
+                />
+              </td>
+            </template>
+            <td>
+              <gr-button
+                link=""
+                data-index$="[[projectIndex]]"
+                on-click="_handleRemoveProject"
+                >Delete</gr-button
+              >
+            </td>
           </tr>
-        </tfoot>
-      </table>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+        </template>
+      </tbody>
+      <tfoot>
+        <tr>
+          <th>
+            <gr-autocomplete
+              id="newProject"
+              query="[[_query]]"
+              threshold="1"
+              allow-non-suggested-values=""
+              tab-complete=""
+              placeholder="Repo"
+            ></gr-autocomplete>
+          </th>
+          <th colspan$="[[_getTypeCount()]]">
+            <iron-input
+              class="newFilterInput"
+              placeholder="branch:name, or other search expression"
+            >
+              <input
+                id="newFilter"
+                class="newFilterInput"
+                is="iron-input"
+                placeholder="branch:name, or other search expression"
+              />
+            </iron-input>
+          </th>
+          <th>
+            <gr-button link="" on-click="_handleAddProject">Add</gr-button>
+          </th>
+        </tr>
+      </tfoot>
+    </table>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
index 2bf0ca7..2a08e4f 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-settings-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6ceee26..c74a396 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-account-link/gr-account-link.js';
 import '../gr-button/gr-button.js';
 import '../gr-icons/gr-icons.js';
@@ -27,7 +25,7 @@
 import {htmlTemplate} from './gr-account-chip_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccountChip extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
index cfa0243..96e0160 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_html.js
@@ -17,77 +17,88 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        overflow: hidden;
-      }
-      .container {
-        align-items: center;
-        background: var(--chip-background-color);
-        border-radius: .75em;
-        display: inline-flex;
-        padding: 0 var(--spacing-m);
-      }
-      :host([show-avatar]) .container {
-        padding-left: 0;
-      }
-      gr-button.remove {
-        --gr-remove-button-style: {
-          border: 0;
-          color: var(--deemphasized-text-color);
-          font-weight: var(--font-weight-normal);
-          height: .6em;
-          line-height: 10px;
-          margin-left: var(--spacing-xs);
-          padding: 0;
-          text-decoration: none;
-        }
-      }
-
-      gr-button.remove:hover,
-      gr-button.remove:focus {
-        --gr-button: {
-          @apply --gr-remove-button-style;
-          color: #333;
-        }
-      }
-      gr-button.remove {
-        --gr-button: {
-          @apply --gr-remove-button-style;
-        }
-      }
-      :host:focus {
-        border-color: transparent;
-        box-shadow: none;
-        outline: none;
-      }
-      :host:focus .container,
-      :host:focus gr-button {
-        background: #ccc;
-      }
-      .transparentBackground,
-      gr-button.transparentBackground {
-        background-color: transparent;
+  <style include="shared-styles">
+    :host {
+      display: block;
+      overflow: hidden;
+    }
+    .container {
+      align-items: center;
+      background: var(--chip-background-color);
+      border-radius: 0.75em;
+      display: inline-flex;
+      padding: 0 var(--spacing-m);
+    }
+    :host([show-avatar]) .container {
+      padding-left: 0;
+    }
+    gr-button.remove {
+      --gr-remove-button-style: {
+        border: 0;
+        color: var(--deemphasized-text-color);
+        font-weight: var(--font-weight-normal);
+        height: 0.6em;
+        line-height: 10px;
+        margin-left: var(--spacing-xs);
         padding: 0;
+        text-decoration: none;
       }
-      :host([disabled]) {
-        opacity: .6;
-        pointer-events: none;
+    }
+
+    gr-button.remove:hover,
+    gr-button.remove:focus {
+      --gr-button: {
+        @apply --gr-remove-button-style;
+        color: #333;
       }
-      iron-icon {
-        height: 1.2rem;
-        width: 1.2rem;
+    }
+    gr-button.remove {
+      --gr-button: {
+        @apply --gr-remove-button-style;
       }
-    </style>
-    <div class\$="container [[_getBackgroundClass(transparentBackground)]]">
-      <gr-account-link account="[[account]]"
-                       show-attention="[[showAttention]]"
-                       voteable-text="[[voteableText]]">
-      </gr-account-link>
-      <gr-button id="remove" link="" hidden\$="[[!removable]]" hidden="" tabindex="-1" aria-label="Remove" class\$="remove [[_getBackgroundClass(transparentBackground)]]" on-click="_handleRemoveTap">
-        <iron-icon icon="gr-icons:close"></iron-icon>
-      </gr-button>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    }
+    :host:focus {
+      border-color: transparent;
+      box-shadow: none;
+      outline: none;
+    }
+    :host:focus .container,
+    :host:focus gr-button {
+      background: #ccc;
+    }
+    .transparentBackground,
+    gr-button.transparentBackground {
+      background-color: transparent;
+      padding: 0;
+    }
+    :host([disabled]) {
+      opacity: 0.6;
+      pointer-events: none;
+    }
+    iron-icon {
+      height: 1.2rem;
+      width: 1.2rem;
+    }
+  </style>
+  <div class$="container [[_getBackgroundClass(transparentBackground)]]">
+    <gr-account-link
+      account="[[account]]"
+      show-attention="[[showAttention]]"
+      voteable-text="[[voteableText]]"
+    >
+    </gr-account-link>
+    <gr-button
+      id="remove"
+      link=""
+      hidden$="[[!removable]]"
+      hidden=""
+      tabindex="-1"
+      aria-label="Remove"
+      class$="remove [[_getBackgroundClass(transparentBackground)]]"
+      on-click="_handleRemoveTap"
+    >
+      <iron-icon icon="gr-icons:close"></iron-icon>
+    </gr-button>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
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 c991a37..807aa81 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../gr-autocomplete/gr-autocomplete.js';
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
@@ -28,7 +26,7 @@
  * gr-account-entry is an element for entering account
  * and/or group with autocomplete support.
  *
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccountEntry extends GestureEventListeners(
     LegacyElementMixin(
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.js b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.js
index 281526d..afd427a 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.js
@@ -17,13 +17,25 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      gr-autocomplete {
-        display: inline-block;
-        flex: 1;
-        overflow: hidden;
-      }
-    </style>
-    <gr-autocomplete id="input" borderless="[[borderless]]" placeholder="[[placeholder]]" threshold="[[suggestFrom]]" query="[[querySuggestions]]" allow-non-suggested-values="[[allowAnyInput]]" on-commit="_handleInputCommit" clear-on-commit="" warn-uncommitted="" text="{{_inputText}}" vertical-offset="24">
-    </gr-autocomplete>
+  <style include="shared-styles">
+    gr-autocomplete {
+      display: inline-block;
+      flex: 1;
+      overflow: hidden;
+    }
+  </style>
+  <gr-autocomplete
+    id="input"
+    borderless="[[borderless]]"
+    placeholder="[[placeholder]]"
+    threshold="[[suggestFrom]]"
+    query="[[querySuggestions]]"
+    allow-non-suggested-values="[[allowAnyInput]]"
+    on-commit="_handleInputCommit"
+    clear-on-commit=""
+    warn-uncommitted=""
+    text="{{_inputText}}"
+    vertical-offset="24"
+  >
+  </gr-autocomplete>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html
index f478e9e..5899ad4 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-account-entry</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 110d884..8c2b7da 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-icon/iron-icon.js';
 import '../../../styles/shared-styles.js';
 import '../gr-avatar/gr-avatar.js';
@@ -29,7 +27,7 @@
 import {DisplayNameBehavior} from '../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccountLabel extends mixinBehaviors( [
   DisplayNameBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
index f642af3..ba2d9cb 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_html.js
@@ -17,69 +17,73 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: inline;
-        position: relative;
-      }
-      :host::after {
-        content: var(--account-label-suffix);
-      }
-      :host(:not([blurred])) .overlay {
-        display: none;
-      }
-      .overlay {
-        position: absolute;
-        pointer-events: none;
-        height: var(--line-height-normal);
-        right: 0;
-        left: 0;
-        background-color: var(--background-color-primary);
-        opacity: 0.5;
-      }
-      gr-avatar {
-        height: var(--line-height-normal);
-        width: var(--line-height-normal);
-        vertical-align: top;
-      }
-      .text {
-        @apply --gr-account-label-text-style;
-      }
-      .text:hover {
-        @apply --gr-account-label-text-hover-style;
-      }
-      iron-icon.attention {
-        vertical-align: top;
-      }
-      iron-icon.status {
-        width: 14px;
-        height: 14px;
-        vertical-align: top;
-        position: relative;
-        top: 2px;
-      }
-    </style>
-    <div class="overlay"></div>
-    <span>
-      <gr-hovercard-account attention="[[showAttention]]"
-                            account="[[account]]"
-                            voteable-text="[[voteableText]]">
-      </gr-hovercard-account>
-      <template is="dom-if" if="[[showAttention]]">
-        <iron-icon class="attention" icon="gr-icons:attention"></iron-icon><!--
-   --></template><!--
-   --><template is="dom-if" if="[[!hideAvatar]]"><!--
+  <style include="shared-styles">
+    :host {
+      display: inline;
+      position: relative;
+    }
+    :host::after {
+      content: var(--account-label-suffix);
+    }
+    :host(:not([blurred])) .overlay {
+      display: none;
+    }
+    .overlay {
+      position: absolute;
+      pointer-events: none;
+      height: var(--line-height-normal);
+      right: 0;
+      left: 0;
+      background-color: var(--background-color-primary);
+      opacity: 0.5;
+    }
+    gr-avatar {
+      height: var(--line-height-normal);
+      width: var(--line-height-normal);
+      vertical-align: top;
+    }
+    .text {
+      @apply --gr-account-label-text-style;
+    }
+    .text:hover {
+      @apply --gr-account-label-text-hover-style;
+    }
+    iron-icon.attention {
+      vertical-align: top;
+    }
+    iron-icon.status {
+      width: 14px;
+      height: 14px;
+      vertical-align: top;
+      position: relative;
+      top: 2px;
+    }
+  </style>
+  <div class="overlay"></div>
+  <span>
+    <gr-hovercard-account
+      attention="[[showAttention]]"
+      account="[[account]]"
+      voteable-text="[[voteableText]]"
+    >
+    </gr-hovercard-account>
+    <template is="dom-if" if="[[showAttention]]">
+      <iron-icon class="attention" icon="gr-icons:attention"></iron-icon
+      ><!--
+   --></template
+    ><!--
+   --><template is="dom-if" if="[[!hideAvatar]]"
+      ><!--
      --><gr-avatar account="[[account]]" image-size="32"></gr-avatar>
-      </template>
-      <span class="text">
-        <span class="name">
-          [[_computeName(account, _serverConfig)]]</span>
-        <template is="dom-if" if="[[!hideStatus]]">
-          <template is="dom-if" if="[[account.status]]">
-            <iron-icon class="status" icon="gr-icons:calendar"></iron-icon>
-          </template>
+    </template>
+    <span class="text">
+      <span class="name"> [[_computeName(account, _serverConfig)]]</span>
+      <template is="dom-if" if="[[!hideStatus]]">
+        <template is="dom-if" if="[[account.status]]">
+          <iron-icon class="status" icon="gr-icons:calendar"></iron-icon>
         </template>
-      </span>
+      </template>
     </span>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </span>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
index 8951bf0..4cc66c4 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-account-label</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 27de4b3..d7dd88d 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
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-import '../../../scripts/bundled-polymer.js';
 import '../gr-account-label/gr-account-label.js';
 import '../../../styles/shared-styles.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
@@ -27,7 +26,7 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccountLink extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
index 422d004..17e7f49 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_html.js
@@ -17,36 +17,38 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: inline-block;
-        /* Setting this really high, so all the following rules don't change
+  <style include="shared-styles">
+    :host {
+      display: inline-block;
+      /* Setting this really high, so all the following rules don't change
            anything, only if --account-max-length is actually set to something
            smaller like 20ch. */
-        max-width: var(--account-max-length, 500px);
-        overflow: hidden;
-        text-overflow: ellipsis;
-        vertical-align: top;
-        white-space: nowrap;
+      max-width: var(--account-max-length, 500px);
+      overflow: hidden;
+      text-overflow: ellipsis;
+      vertical-align: top;
+      white-space: nowrap;
+    }
+    a {
+      color: var(--primary-text-color);
+      text-decoration: none;
+    }
+    gr-account-label {
+      --gr-account-label-text-hover-style: {
+        text-decoration: underline;
       }
-      a {
-        color: var(--primary-text-color);
-        text-decoration: none;
-      }
-      gr-account-label {
-        --gr-account-label-text-hover-style: {
-          text-decoration: underline;
-        };
-      }
-    </style>
-    <span>
-      <a href\$="[[_computeOwnerLink(account)]]" tabindex="-1">
-        <gr-account-label show-attention="[[showAttention]]" 
-                          hide-avatar="[[hideAvatar]]"
-                          hide-status="[[hideStatus]]"
-                          account="[[account]]"
-                          voteable-text="[[voteableText]]">
-        </gr-account-label>
-      </a>
-    </span>
+    }
+  </style>
+  <span>
+    <a href$="[[_computeOwnerLink(account)]]" tabindex="-1">
+      <gr-account-label
+        show-attention="[[showAttention]]"
+        hide-avatar="[[hideAvatar]]"
+        hide-status="[[hideStatus]]"
+        account="[[account]]"
+        voteable-text="[[voteableText]]"
+      >
+      </gr-account-label>
+    </a>
+  </span>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
index aa1c575..f3bff6e 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-account-link</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 73ccf7d..ebfc48b 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-account-chip/gr-account-chip.js';
 import '../gr-account-entry/gr-account-entry.js';
 import '../../../styles/shared-styles.js';
@@ -28,7 +26,7 @@
 const VALID_EMAIL_ALERT = 'Please input a valid email.';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAccountList extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.js
index 2438bc1..6fee9f3 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_html.js
@@ -17,41 +17,57 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      gr-account-chip {
-        display: inline-block;
-        margin: var(--spacing-xs) var(--spacing-xs) var(--spacing-xs) 0;
-      }
-      gr-account-entry {
-        display: flex;
-        flex: 1;
-        min-width: 10em;
-        margin: var(--spacing-xs) var(--spacing-xs) var(--spacing-xs) 0;
-      }
-      .group {
-        --account-label-suffix: ' (group)';
-      }
-      .pending-add {
-        font-style: italic;
-      }
-      .list {
-        align-items: center;
-        display: flex;
-        flex-wrap: wrap;
-        @apply --account-list-style;
-      }
-    </style>
-    <!--
+  <style include="shared-styles">
+    gr-account-chip {
+      display: inline-block;
+      margin: var(--spacing-xs) var(--spacing-xs) var(--spacing-xs) 0;
+    }
+    gr-account-entry {
+      display: flex;
+      flex: 1;
+      min-width: 10em;
+      margin: var(--spacing-xs) var(--spacing-xs) var(--spacing-xs) 0;
+    }
+    .group {
+      --account-label-suffix: ' (group)';
+    }
+    .pending-add {
+      font-style: italic;
+    }
+    .list {
+      align-items: center;
+      display: flex;
+      flex-wrap: wrap;
+      @apply --account-list-style;
+    }
+  </style>
+  <!--
       NOTE(Issue 6419): Nest the inner dom-repeat template in a div rather than
       as a direct child of the dom-module's template.
     -->
-    <div class="list">
-      <template id="chips" is="dom-repeat" items="[[accounts]]" as="account">
-        <gr-account-chip account="[[account]]" class\$="[[_computeChipClass(account)]]" data-account-id\$="[[account._account_id]]" removable="[[_computeRemovable(account, readonly)]]" on-keydown="_handleChipKeydown" tabindex="-1">
-        </gr-account-chip>
-      </template>
-    </div>
-    <gr-account-entry borderless="" hidden\$="[[_computeEntryHidden(maxCount, accounts.*, readonly)]]" id="entry" placeholder="[[placeholder]]" on-add="_handleAdd" on-input-keydown="_handleInputKeydown" allow-any-input="[[allowAnyInput]]" query-suggestions="[[_querySuggestions]]">
-    </gr-account-entry>
-    <slot></slot>
+  <div class="list">
+    <template id="chips" is="dom-repeat" items="[[accounts]]" as="account">
+      <gr-account-chip
+        account="[[account]]"
+        class$="[[_computeChipClass(account)]]"
+        data-account-id$="[[account._account_id]]"
+        removable="[[_computeRemovable(account, readonly)]]"
+        on-keydown="_handleChipKeydown"
+        tabindex="-1"
+      >
+      </gr-account-chip>
+    </template>
+  </div>
+  <gr-account-entry
+    borderless=""
+    hidden$="[[_computeEntryHidden(maxCount, accounts.*, readonly)]]"
+    id="entry"
+    placeholder="[[placeholder]]"
+    on-add="_handleAdd"
+    on-input-keydown="_handleInputKeydown"
+    allow-any-input="[[allowAnyInput]]"
+    query-suggestions="[[_querySuggestions]]"
+  >
+  </gr-account-entry>
+  <slot></slot>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
index 2023dab..b3b32606 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-account-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 1ec453e..d3a6fad 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-button/gr-button.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -24,7 +22,7 @@
 import {htmlTemplate} from './gr-alert_html.js';
 import {getRootElement} from '../../../scripts/rootElement.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrAlert extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.js b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.js
index 1190516..e9f386d 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_html.js
@@ -17,27 +17,27 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /**
+  <style include="shared-styles">
+    /**
        * ALERT: DO NOT ADD TRANSITION PROPERTIES WITHOUT PROPERLY UNDERSTANDING
        * HOW THEY ARE USED IN THE CODE.
        */
-      :host([toast]) {
-        background-color: var(--tooltip-background-color);
-        bottom: 1.25rem;
-        border-radius: var(--border-radius);
-        box-shadow: var(--elevation-level-2);
-        color: var(--view-background-color);
-        left: 1.25rem;
-        position: fixed;
-        transform: translateY(5rem);
-        transition: transform var(--gr-alert-transition-duration, 80ms) ease-out;
-        z-index: 1000;
-      }
-      :host([shown]) {
-        transform: translateY(0);
-      }
-      /**
+    :host([toast]) {
+      background-color: var(--tooltip-background-color);
+      bottom: 1.25rem;
+      border-radius: var(--border-radius);
+      box-shadow: var(--elevation-level-2);
+      color: var(--view-background-color);
+      left: 1.25rem;
+      position: fixed;
+      transform: translateY(5rem);
+      transition: transform var(--gr-alert-transition-duration, 80ms) ease-out;
+      z-index: 1000;
+    }
+    :host([shown]) {
+      transform: translateY(0);
+    }
+    /**
        * NOTE: To avoid style being overwritten by outside of the shadow DOM
        * (as outside styles always win), .content-wrapper is introduced as a
        * wrapper around main content to have better encapsulation, styles that
@@ -45,29 +45,35 @@
        * In this case, \`padding:0px\` is defined in main.css for all elements
        * with the universal selector: *.
        */
-      .content-wrapper {
-        padding: var(--spacing-l) var(--spacing-xl);
+    .content-wrapper {
+      padding: var(--spacing-l) var(--spacing-xl);
+    }
+    .text {
+      color: var(--tooltip-text-color);
+      display: inline-block;
+      max-height: 10rem;
+      max-width: 80vw;
+      vertical-align: bottom;
+      word-break: break-all;
+    }
+    .action {
+      color: var(--link-color);
+      font-weight: var(--font-weight-bold);
+      margin-left: var(--spacing-l);
+      text-decoration: none;
+      --gr-button: {
+        padding: 0;
       }
-      .text {
-        color: var(--tooltip-text-color);
-        display: inline-block;
-        max-height: 10rem;
-        max-width: 80vw;
-        vertical-align: bottom;
-        word-break: break-all;
-      }
-      .action {
-        color: var(--link-color);
-        font-weight: var(--font-weight-bold);
-        margin-left: var(--spacing-l);
-        text-decoration: none;
-        --gr-button: {
-          padding: 0;
-        }
-      }
-    </style>
-    <div class="content-wrapper">
-      <span class="text">[[text]]</span>
-      <gr-button link="" class="action" hidden\$="[[_hideActionButton]]" on-click="_handleActionTap">[[actionText]]</gr-button>
-    </div>
+    }
+  </style>
+  <div class="content-wrapper">
+    <span class="text">[[text]]</span>
+    <gr-button
+      link=""
+      class="action"
+      hidden$="[[_hideActionButton]]"
+      on-click="_handleActionTap"
+      >[[actionText]]</gr-button
+    >
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
index 582dcf9..557ec28 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-alert</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 46e8829..3cf91fe 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '@polymer/iron-dropdown/iron-dropdown.js';
 import {IronFitBehavior} from '@polymer/iron-fit-behavior/iron-fit-behavior.js';
 import '../gr-cursor-manager/gr-cursor-manager.js';
@@ -28,7 +27,7 @@
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAutocompleteDropdown extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.js
index 711315d..b31af73 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_html.js
@@ -17,64 +17,86 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        z-index: 100;
-      }
-      :host([is-hidden]) {
-        display: none;
-      }
-      ul {
-        list-style: none;
-      }
-      li {
-        border-bottom: 1px solid var(--border-color);
-        cursor: pointer;
-        display: flex;
-        justify-content: space-between;
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      li:last-of-type {
-        border: none;
-      }
-      li:focus {
-        outline: none;
-      }
-      li:hover {
-        background-color: var(--hover-background-color);
-      }
-      li.selected {
-        background-color: var(--selection-background-color);
-      }
+  <style include="shared-styles">
+    :host {
+      z-index: 100;
+    }
+    :host([is-hidden]) {
+      display: none;
+    }
+    ul {
+      list-style: none;
+    }
+    li {
+      border-bottom: 1px solid var(--border-color);
+      cursor: pointer;
+      display: flex;
+      justify-content: space-between;
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    li:last-of-type {
+      border: none;
+    }
+    li:focus {
+      outline: none;
+    }
+    li:hover {
+      background-color: var(--hover-background-color);
+    }
+    li.selected {
+      background-color: var(--selection-background-color);
+    }
+    .dropdown-content {
+      background: var(--dropdown-background-color);
+      box-shadow: var(--elevation-level-2);
+      border-radius: var(--border-radius);
+      max-height: 50vh;
+      overflow: auto;
+    }
+    @media only screen and (max-height: 35em) {
       .dropdown-content {
-        background: var(--dropdown-background-color);
-        box-shadow: var(--elevation-level-2);
-        border-radius: var(--border-radius);
-        max-height: 50vh;
-        overflow: auto;
+        max-height: 80vh;
       }
-      @media only screen and (max-height: 35em) {
-        .dropdown-content {
-          max-height: 80vh;
-        }
-      }
-      .label {
-        color: var(--deemphasized-text-color);
-        padding-left: var(--spacing-l);
-      }
-      .hide {
-        display: none;
-      }
-    </style>
-    <div class="dropdown-content" slot="dropdown-content" id="suggestions" role="listbox">
-      <ul>
-        <template is="dom-repeat" items="[[suggestions]]">
-          <li data-index\$="[[index]]" data-value\$="[[item.dataValue]]" tabindex="-1" aria-label\$="[[item.name]]" class="autocompleteOption" role="option" on-click="_handleClickItem">
-            <span>[[item.text]]</span>
-            <span class\$="label [[_computeLabelClass(item)]]">[[item.label]]</span>
-          </li>
-        </template>
-      </ul>
-    </div>
-    <gr-cursor-manager id="cursor" index="{{index}}" cursor-target-class="selected" scroll-behavior="never" focus-on-move="" stops="[[_suggestionEls]]"></gr-cursor-manager>
+    }
+    .label {
+      color: var(--deemphasized-text-color);
+      padding-left: var(--spacing-l);
+    }
+    .hide {
+      display: none;
+    }
+  </style>
+  <div
+    class="dropdown-content"
+    slot="dropdown-content"
+    id="suggestions"
+    role="listbox"
+  >
+    <ul>
+      <template is="dom-repeat" items="[[suggestions]]">
+        <li
+          data-index$="[[index]]"
+          data-value$="[[item.dataValue]]"
+          tabindex="-1"
+          aria-label$="[[item.name]]"
+          class="autocompleteOption"
+          role="option"
+          on-click="_handleClickItem"
+        >
+          <span>[[item.text]]</span>
+          <span class$="label [[_computeLabelClass(item)]]"
+            >[[item.label]]</span
+          >
+        </li>
+      </template>
+    </ul>
+  </div>
+  <gr-cursor-manager
+    id="cursor"
+    index="{{index}}"
+    cursor-target-class="selected"
+    scroll-behavior="never"
+    focus-on-move=""
+    stops="[[_suggestionEls]]"
+  ></gr-cursor-manager>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
index 4f3d806..d836155 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-autocomplete-dropdown</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 7f9ed72..e6a809e 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/paper-input/paper-input.js';
 import '../gr-autocomplete-dropdown/gr-autocomplete-dropdown.js';
 import '../gr-cursor-manager/gr-cursor-manager.js';
@@ -33,7 +31,7 @@
 const DEBOUNCE_WAIT_MS = 200;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAutocomplete extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.js
index 11726d9..eae8741 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_html.js
@@ -17,71 +17,95 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .searchIcon {
-        display: none;
-      }
-      .searchIcon.showSearchIcon {
-        display: inline-block;
-      }
-      iron-icon {
-        margin: 0 var(--spacing-xs);
-        vertical-align: top;
-      }
-      paper-input.borderless {
-        border: none;
+  <style include="shared-styles">
+    .searchIcon {
+      display: none;
+    }
+    .searchIcon.showSearchIcon {
+      display: inline-block;
+    }
+    iron-icon {
+      margin: 0 var(--spacing-xs);
+      vertical-align: top;
+    }
+    paper-input.borderless {
+      border: none;
+      padding: 0;
+    }
+    paper-input {
+      background-color: var(--view-background-color);
+      color: var(--primary-text-color);
+      border: 1px solid var(--border-color);
+      border-radius: var(--border-radius);
+      padding: var(--spacing-s);
+      --paper-input-container: {
         padding: 0;
       }
-      paper-input {
-        background-color: var(--view-background-color);
-        color: var(--primary-text-color);
-        border: 1px solid var(--border-color);
-        border-radius: var(--border-radius);
-        padding: var(--spacing-s);
-        --paper-input-container: {
-          padding: 0;
-        };
-        --paper-input-container-input: {
-          font-size: var(--font-size-normal);
-          line-height: var(--line-height-normal);
-        };
-        /* This is a hack for not being able to set height:0 on the underline
+      --paper-input-container-input: {
+        font-size: var(--font-size-normal);
+        line-height: var(--line-height-normal);
+      }
+      /* This is a hack for not being able to set height:0 on the underline
            of a paper-input 2.2.3 element. All the underline fixes below only
            actually work in 3.x.x, so the height must be adjusted directly as
            a workaround until we are on Polymer 3. */
-        height: var(--line-height-normal);
-        --paper-input-container-underline-height: 0;
-        --paper-input-container-underline-wrapper-height: 0;
-        --paper-input-container-underline-focus-height: 0;
-        --paper-input-container-underline-legacy-height: 0;
-        --paper-input-container-underline: {
-          height: 0;
-          display: none;
-        };
-        --paper-input-container-underline-focus: {
-          height: 0;
-          display: none;
-        };
-        --paper-input-container-underline-disabled: {
-          height: 0;
-          display: none;
-        };
+      height: var(--line-height-normal);
+      --paper-input-container-underline-height: 0;
+      --paper-input-container-underline-wrapper-height: 0;
+      --paper-input-container-underline-focus-height: 0;
+      --paper-input-container-underline-legacy-height: 0;
+      --paper-input-container-underline: {
+        height: 0;
+        display: none;
       }
-      paper-input.warnUncommitted {
-        --paper-input-container-input: {
-          color: var(--error-text-color);
-          font-size: inherit;
-        }
+      --paper-input-container-underline-focus: {
+        height: 0;
+        display: none;
       }
-    </style>
-    <paper-input no-label-float="" id="input" class\$="[[_computeClass(borderless)]]" disabled\$="[[disabled]]" value="{{text}}" placeholder="[[placeholder]]" on-keydown="_handleKeydown" on-focus="_onInputFocus" on-blur="_onInputBlur" autocomplete="off">
-
-      <!-- prefix as attribute is required to for polymer 1 -->
-      <div slot="prefix" prefix="">
-        <iron-icon icon="gr-icons:search" class\$="searchIcon [[_computeShowSearchIconClass(showSearchIcon)]]">
-        </iron-icon>
-      </div>
-    </paper-input>
-    <gr-autocomplete-dropdown vertical-align="top" vertical-offset="[[verticalOffset]]" horizontal-align="left" id="suggestions" on-item-selected="_handleItemSelect" on-keydown="_handleKeydown" suggestions="[[_suggestions]]" role="listbox" index="[[_index]]" position-target="[[_inputElement]]">
-    </gr-autocomplete-dropdown>
+      --paper-input-container-underline-disabled: {
+        height: 0;
+        display: none;
+      }
+    }
+    paper-input.warnUncommitted {
+      --paper-input-container-input: {
+        color: var(--error-text-color);
+        font-size: inherit;
+      }
+    }
+  </style>
+  <paper-input
+    no-label-float=""
+    id="input"
+    class$="[[_computeClass(borderless)]]"
+    disabled$="[[disabled]]"
+    value="{{text}}"
+    placeholder="[[placeholder]]"
+    on-keydown="_handleKeydown"
+    on-focus="_onInputFocus"
+    on-blur="_onInputBlur"
+    autocomplete="off"
+  >
+    <!-- prefix as attribute is required to for polymer 1 -->
+    <div slot="prefix" prefix="">
+      <iron-icon
+        icon="gr-icons:search"
+        class$="searchIcon [[_computeShowSearchIconClass(showSearchIcon)]]"
+      >
+      </iron-icon>
+    </div>
+  </paper-input>
+  <gr-autocomplete-dropdown
+    vertical-align="top"
+    vertical-offset="[[verticalOffset]]"
+    horizontal-align="left"
+    id="suggestions"
+    on-item-selected="_handleItemSelect"
+    on-keydown="_handleKeydown"
+    suggestions="[[_suggestions]]"
+    role="listbox"
+    index="[[_index]]"
+    position-target="[[_inputElement]]"
+  >
+  </gr-autocomplete-dropdown>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
index 68de4fc..6dd5a97 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-reviewer-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 75181b1..1385f6d 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../gr-js-api-interface/gr-js-api-interface.js';
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
@@ -28,7 +26,7 @@
 import {pluginLoader} from '../gr-js-api-interface/gr-plugin-loader.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrAvatar extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.js
index be4c350..cc8a42f 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_html.js
@@ -17,13 +17,13 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: inline-block;
-        border-radius: 50%;
-        background-size: cover;
-        background-color: var(--avatar-background-color, #f1f2f3);
-      }
-    </style>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      display: inline-block;
+      border-radius: 50%;
+      background-size: cover;
+      background-color: var(--avatar-background-color, #f1f2f3);
+    }
+  </style>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
index 294d86d..dddc3d8 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-avatar</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 3169c56..6ef4807 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
@@ -14,11 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/paper-button/paper-button.js';
 import '../../../styles/shared-styles.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -27,9 +24,10 @@
 import {TooltipBehavior} from '../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js';
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 import {util} from '../../../scripts/util.js';
+import {appContext} from '../../../services/app-context.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrButton extends mixinBehaviors( [
   KeyboardShortcutBehavior,
@@ -80,6 +78,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   created() {
     super.created();
@@ -104,7 +107,7 @@
       return;
     }
 
-    this.$.reporting.reportInteraction('button-click',
+    this.reporting.reportInteraction('button-click',
         {path: util.getEventPath(e)});
   }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.js b/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.js
index ad5c00d..4621242 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.js
@@ -17,92 +17,93 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* general styles for all buttons */
-      :host {
-        --background-color: var(--button-background-color, var(--default-button-background-color));
-        --text-color: var(--default-button-text-color);
-        display: inline-block;
-        position: relative;
-      }
-      :host([hidden]) {
-        display: none;
-      }
-      :host([no-uppercase]) paper-button {
-        text-transform: none;
-      }
-      paper-button {
-        /* The next lines contains a copy of paper-button style.
+  <style include="shared-styles">
+    /* general styles for all buttons */
+    :host {
+      --background-color: var(
+        --button-background-color,
+        var(--default-button-background-color)
+      );
+      --text-color: var(--default-button-text-color);
+      display: inline-block;
+      position: relative;
+    }
+    :host([hidden]) {
+      display: none;
+    }
+    :host([no-uppercase]) paper-button {
+      text-transform: none;
+    }
+    paper-button {
+      /* The next lines contains a copy of paper-button style.
           Without a copy, the @apply works incorrectly with Polymer 2.
           @apply is deprecated and is not recommended to use. It is expected
           that @apply will be replaced with the ::part CSS pseudo-element.
           After replacecment copied lines can be removed.
         */
-        @apply --layout-inline;
-        @apply --layout-center-center;
-        position: relative;
-        box-sizing: border-box;
-        min-width: 5.14em;
-        margin: 0 0.29em;
-        background: transparent;
-        -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-        -webkit-tap-highlight-color: transparent;
-        font: inherit;
-        text-transform: uppercase;
-        outline-width: 0;
-        border-radius: var(--border-radius);
-        -moz-user-select: none;
-        -ms-user-select: none;
-        -webkit-user-select: none;
-        user-select: none;
-        cursor: pointer;
-        z-index: 0;
-        padding: var(--spacing-m);
+      @apply --layout-inline;
+      @apply --layout-center-center;
+      position: relative;
+      box-sizing: border-box;
+      min-width: 5.14em;
+      margin: 0 0.29em;
+      background: transparent;
+      -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+      -webkit-tap-highlight-color: transparent;
+      font: inherit;
+      text-transform: uppercase;
+      outline-width: 0;
+      border-radius: var(--border-radius);
+      -moz-user-select: none;
+      -ms-user-select: none;
+      -webkit-user-select: none;
+      user-select: none;
+      cursor: pointer;
+      z-index: 0;
+      padding: var(--spacing-m);
 
-        @apply --paper-font-common-base;
-        @apply --paper-button;
-        /* End of copy*/
+      @apply --paper-font-common-base;
+      @apply --paper-button;
+      /* End of copy*/
 
-        /* paper-button sets this to anti-aliased, which appears different than
+      /* paper-button sets this to anti-aliased, which appears different than
           bold font elsewhere on macOS. */
-        -webkit-font-smoothing: initial;
-        align-items: center;
-        background-color: var(--background-color);
-        color: var(--text-color);
-        display: flex;
-        font-family: inherit;
-        justify-content: center;
-        margin: var(--margin, 0);
-        min-width: var(--border, 0);
-        padding: var(--padding, 4px 8px);
-        @apply --gr-button;
-      }
-      /* https://github.com/PolymerElements/paper-button/blob/2.x/paper-button.html */
-      /* BEGIN: Copy from paper-button */
-      paper-button[elevation="1"] {
-        @apply --paper-material-elevation-1;
-      }
-      paper-button[elevation="2"] {
-        @apply --paper-material-elevation-2;
-      }
-      paper-button[elevation="3"] {
-        @apply --paper-material-elevation-3;
-      }
-      paper-button[elevation="4"] {
-        @apply --paper-material-elevation-4;
-      }
-      paper-button[elevation="5"] {
-        @apply --paper-material-elevation-5;
-      }
-      /* END: Copy from paper-button */
-      paper-button:hover {
-        background: linear-gradient(
-            rgba(0, 0, 0, .12),
-            rgba(0, 0, 0, .12)
-        ), var(--background-color);
-      }
+      -webkit-font-smoothing: initial;
+      align-items: center;
+      background-color: var(--background-color);
+      color: var(--text-color);
+      display: flex;
+      font-family: inherit;
+      justify-content: center;
+      margin: var(--margin, 0);
+      min-width: var(--border, 0);
+      padding: var(--padding, 4px 8px);
+      @apply --gr-button;
+    }
+    /* https://github.com/PolymerElements/paper-button/blob/2.x/paper-button.html */
+    /* BEGIN: Copy from paper-button */
+    paper-button[elevation='1'] {
+      @apply --paper-material-elevation-1;
+    }
+    paper-button[elevation='2'] {
+      @apply --paper-material-elevation-2;
+    }
+    paper-button[elevation='3'] {
+      @apply --paper-material-elevation-3;
+    }
+    paper-button[elevation='4'] {
+      @apply --paper-material-elevation-4;
+    }
+    paper-button[elevation='5'] {
+      @apply --paper-material-elevation-5;
+    }
+    /* END: Copy from paper-button */
+    paper-button:hover {
+      background: linear-gradient(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12)),
+        var(--background-color);
+    }
 
-      /* Some mobile browsers treat focused element as hovered element.
+    /* Some mobile browsers treat focused element as hovered element.
       As a result, element remains hovered after click (has grey background in default theme).
       Use @media (hover:none) to remove background if
       user's primary input mechanism can't hover over elements.
@@ -113,63 +114,63 @@
       If browser doesn't support it, then the whole content of @media .. is ignored.
       This is why the default behavior is placed outside of @media.
       */
-      @media (hover:none) {
-        paper-button:hover {
-          background: transparent;
-        }
+    @media (hover: none) {
+      paper-button:hover {
+        background: transparent;
       }
+    }
 
-      :host([primary]) {
-        --background-color: var(--primary-button-background-color);
-        --text-color: var(--primary-button-text-color);
-      }
-      :host([link][primary]) {
-        --text-color: var(--primary-button-background-color);
-      }
+    :host([primary]) {
+      --background-color: var(--primary-button-background-color);
+      --text-color: var(--primary-button-text-color);
+    }
+    :host([link][primary]) {
+      --text-color: var(--primary-button-background-color);
+    }
 
-      /* Keep below color definition for primary so that this takes precedence
+    /* Keep below color definition for primary so that this takes precedence
         when disabled. */
-      :host([disabled]), :host([loading]) {
-        --background-color: var(--disabled-button-background-color);
-        --text-color: var(--deemphasized-text-color);
-        cursor: default;
-      }
+    :host([disabled]),
+    :host([loading]) {
+      --background-color: var(--disabled-button-background-color);
+      --text-color: var(--deemphasized-text-color);
+      cursor: default;
+    }
 
-      /* Styles for link buttons specifically */
-      :host([link]) {
-        --background-color: transparent;
-        --margin: 0;
-        --padding: 5px 4px;
-      }
-      :host([disabled][link]),
-      :host([loading][link]) {
-        --background-color: transparent;
-        --text-color: var(--deemphasized-text-color);
-        cursor: default;
-      }
+    /* Styles for link buttons specifically */
+    :host([link]) {
+      --background-color: transparent;
+      --margin: 0;
+      --padding: 5px 4px;
+    }
+    :host([disabled][link]),
+    :host([loading][link]) {
+      --background-color: transparent;
+      --text-color: var(--deemphasized-text-color);
+      cursor: default;
+    }
 
-      /* Styles for the optional down arrow */
-      :host(:not([down-arrow])) .downArrow {
-        display: none;
-      }
-      :host([down-arrow]) .downArrow {
-        border-top: .36em solid #ccc;
-        border-left: .36em solid transparent;
-        border-right: .36em solid transparent;
-        margin-bottom: var(--spacing-xxs);
-        margin-left: var(--spacing-m);
-        transition: border-top-color 200ms;
-      }
-      :host([down-arrow]) paper-button:hover .downArrow {
-        border-top-color: var(--deemphasized-text-color);
-      }
-    </style>
-    <paper-button raised="[[!link]]" disabled="[[_disabled]]" tabindex="-1">
-      <template is="dom-if" if="[[loading]]">
-        <span class="loadingSpin"></span>
-      </template>
-      <slot></slot>
-      <i class="downArrow"></i>
-    </paper-button>
-    <gr-reporting id="reporting"></gr-reporting>
+    /* Styles for the optional down arrow */
+    :host(:not([down-arrow])) .downArrow {
+      display: none;
+    }
+    :host([down-arrow]) .downArrow {
+      border-top: 0.36em solid #ccc;
+      border-left: 0.36em solid transparent;
+      border-right: 0.36em solid transparent;
+      margin-bottom: var(--spacing-xxs);
+      margin-left: var(--spacing-m);
+      transition: border-top-color 200ms;
+    }
+    :host([down-arrow]) paper-button:hover .downArrow {
+      border-top-color: var(--deemphasized-text-color);
+    }
+  </style>
+  <paper-button raised="[[!link]]" disabled="[[_disabled]]" tabindex="-1">
+    <template is="dom-if" if="[[loading]]">
+      <span class="loadingSpin"></span>
+    </template>
+    <slot></slot>
+    <i class="downArrow"></i>
+  </paper-button>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
index 9454d15..dfc3d75 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-button</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -48,6 +49,8 @@
 import '../../../test/common-test-setup.js';
 import './gr-button.js';
 import {addListener} from '@polymer/polymer/lib/utils/gestures.js';
+import {appContext} from '../../../services/app-context.js';
+
 suite('gr-button tests', () => {
   let element;
   let sandbox;
@@ -189,13 +192,10 @@
   });
 
   suite('reporting', () => {
-    const reportStub = sinon.stub();
+    let reportStub;
     setup(() => {
-      stub('gr-reporting', {
-        reportInteraction: (...args) => {
-          reportStub(...args);
-        },
-      });
+      reportStub = sandbox.stub(appContext.reportingService,
+          'reportInteraction');
       reportStub.reset();
     });
 
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 10e06dd..886f0c1 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-icons/gr-icons.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -23,7 +21,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-change-star_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrChangeStar extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.js b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.js
index a4925aa..f723717a 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_html.js
@@ -17,21 +17,30 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      button {
-        background-color: transparent;
-        cursor: pointer;
-      }
-      iron-icon.active {
-        fill: var(--link-color);
-      }
-      iron-icon {
-        vertical-align: top;
-        --iron-icon-height: var(--gr-change-star-size, var(--line-height-normal, 20px));
-        --iron-icon-width: var(--gr-change-star-size, var(--line-height-normal, 20px));
-      }
-    </style>
-    <button aria-label="Change star" on-click="toggleStar">
-      <iron-icon class\$="[[_computeStarClass(change.starred)]]" icon\$="[[_computeStarIcon(change.starred)]]"></iron-icon>
-    </button>
+  <style include="shared-styles">
+    button {
+      background-color: transparent;
+      cursor: pointer;
+    }
+    iron-icon.active {
+      fill: var(--link-color);
+    }
+    iron-icon {
+      vertical-align: top;
+      --iron-icon-height: var(
+        --gr-change-star-size,
+        var(--line-height-normal, 20px)
+      );
+      --iron-icon-width: var(
+        --gr-change-star-size,
+        var(--line-height-normal, 20px)
+      );
+    }
+  </style>
+  <button aria-label="Change star" on-click="toggleStar">
+    <iron-icon
+      class$="[[_computeStarClass(change.starred)]]"
+      icon$="[[_computeStarIcon(change.starred)]]"
+    ></iron-icon>
+  </button>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
index 66bbd36..1ea9071 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-star</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b99612e..915d171 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
 import '../gr-tooltip-content/gr-tooltip-content.js';
 import '../../../styles/shared-styles.js';
@@ -43,7 +41,7 @@
 const PRIVATE_TOOLTIP = 'This change is only visible to its owner and ' +
     'current reviewers (or anyone with "View Private Changes" permission).';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrChangeStatus extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.js b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.js
index 1a1bc1b8..904ef1d 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_html.js
@@ -17,56 +17,61 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .chip {
-        border-radius: var(--border-radius);
-        background-color: var(--chip-background-color);
-        padding: 0 var(--spacing-m);
-        white-space: nowrap;
-      }
-      :host(.merged) .chip {
-        background-color: #5b9d52;
-        color: #5b9d52;
-      }
-      :host(.abandoned) .chip {
-        background-color: #afafaf;
-        color: #afafaf;
-      }
-      :host(.wip) .chip {
-        background-color: #8f756c;
-        color: #8f756c;
-      }
-      :host(.private) .chip {
-        background-color: #c17ccf;
-        color: #c17ccf;
-      }
-      :host(.merge-conflict) .chip {
-        background-color: #dc5c60;
-        color: #dc5c60;
-      }
-      :host(.active) .chip {
-        background-color: #29b6f6;
-        color: #29b6f6;
-      }
-      :host(.ready-to-submit) .chip {
-        background-color: #e10ca3;
-        color: #e10ca3;
-      }
-      :host(.custom) .chip {
-        background-color: #825cc2;
-        color: #825cc2;
-      }
-      :host([flat]) .chip {
-        background-color: transparent;
-        padding: 0;
-      }
-      :host(:not([flat])) .chip {
-        color: white;
-      }
-    </style>
-    <gr-tooltip-content has-tooltip="" position-below="" title="[[tooltipText]]" max-width="40em">
-      <div class="chip" aria-label\$="Label: [[status]]">
-        [[_computeStatusString(status)]]
-      </div>
-    </gr-tooltip-content>
+  <style include="shared-styles">
+    .chip {
+      border-radius: var(--border-radius);
+      background-color: var(--chip-background-color);
+      padding: 0 var(--spacing-m);
+      white-space: nowrap;
+    }
+    :host(.merged) .chip {
+      background-color: #5b9d52;
+      color: #5b9d52;
+    }
+    :host(.abandoned) .chip {
+      background-color: #afafaf;
+      color: #afafaf;
+    }
+    :host(.wip) .chip {
+      background-color: #8f756c;
+      color: #8f756c;
+    }
+    :host(.private) .chip {
+      background-color: #c17ccf;
+      color: #c17ccf;
+    }
+    :host(.merge-conflict) .chip {
+      background-color: #dc5c60;
+      color: #dc5c60;
+    }
+    :host(.active) .chip {
+      background-color: #29b6f6;
+      color: #29b6f6;
+    }
+    :host(.ready-to-submit) .chip {
+      background-color: #e10ca3;
+      color: #e10ca3;
+    }
+    :host(.custom) .chip {
+      background-color: #825cc2;
+      color: #825cc2;
+    }
+    :host([flat]) .chip {
+      background-color: transparent;
+      padding: 0;
+    }
+    :host(:not([flat])) .chip {
+      color: white;
+    }
+  </style>
+  <gr-tooltip-content
+    has-tooltip=""
+    position-below=""
+    title="[[tooltipText]]"
+    max-width="40em"
+  >
+    <div class="chip" aria-label$="Label: [[status]]">
+      [[_computeStatusString(status)]]
+    </div>
+  </gr-tooltip-content>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html
index 48c1495..806203b 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-status</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 f4e4d80..52e81c5 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
@@ -14,10 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
 import '../gr-storage/gr-storage.js';
 import '../gr-comment/gr-comment.js';
@@ -31,12 +28,13 @@
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 import {util} from '../../../scripts/util.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
+import {appContext} from '../../../services/app-context.js';
 
 const UNRESOLVED_EXPAND_COUNT = 5;
 const NEWLINE_PATTERN = /\n/g;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrCommentThread extends mixinBehaviors( [
   /**
@@ -171,6 +169,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   created() {
     super.created();
@@ -295,7 +298,9 @@
         // False if it's an unresolved comment under UNRESOLVED_EXPAND_COUNT.
         const resolvedThread = !this.unresolved ||
               this._orderedComments.length - i - 1 >= UNRESOLVED_EXPAND_COUNT;
-        comment.collapsed = !isRobotComment && resolvedThread;
+        if (comment.collapsed === undefined) {
+          comment.collapsed = !isRobotComment && resolvedThread;
+        }
       }
     }
   }
@@ -316,15 +321,13 @@
     });
   }
 
-  _createReplyComment(parent, content, opt_isEditing,
+  _createReplyComment(content, opt_isEditing,
       opt_unresolved) {
-    this.$.reporting.recordDraftInteraction();
+    this.reporting.recordDraftInteraction();
     const reply = this._newReply(
         this._orderedComments[this._orderedComments.length - 1].id,
-        parent.line,
         content,
-        opt_unresolved,
-        parent.range);
+        opt_unresolved);
 
     // If there is currently a comment in an editing state, add an attribute
     // so that the gr-comment knows not to populate the draft text.
@@ -364,25 +367,23 @@
       const msg = comment.message;
       quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
     }
-    this._createReplyComment(comment, quoteStr, true, comment.unresolved);
+    this._createReplyComment(quoteStr, true, comment.unresolved);
   }
 
-  _handleCommentReply(e) {
+  _handleCommentReply() {
     this._processCommentReply();
   }
 
-  _handleCommentQuote(e) {
+  _handleCommentQuote() {
     this._processCommentReply(true);
   }
 
-  _handleCommentAck(e) {
-    const comment = this._lastComment;
-    this._createReplyComment(comment, 'Ack', false, false);
+  _handleCommentAck() {
+    this._createReplyComment('Ack', false, false);
   }
 
-  _handleCommentDone(e) {
-    const comment = this._lastComment;
-    this._createReplyComment(comment, 'Done', false, false);
+  _handleCommentDone() {
+    this._createReplyComment('Done', false, false);
   }
 
   _handleCommentFix(e) {
@@ -390,7 +391,7 @@
     const msg = comment.message;
     const quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
     const response = quoteStr + 'Please fix.';
-    this._createReplyComment(comment, response, false, true);
+    this._createReplyComment(response, false, true);
   }
 
   _commentElWithDraftID(id) {
@@ -403,11 +404,9 @@
     return null;
   }
 
-  _newReply(inReplyTo, opt_lineNum, opt_message, opt_unresolved,
-      opt_range) {
-    const d = this._newDraft(opt_lineNum);
+  _newReply(inReplyTo, opt_message, opt_unresolved) {
+    const d = this._newDraft();
     d.in_reply_to = inReplyTo;
-    d.range = opt_range;
     if (opt_message != null) {
       d.message = opt_message;
     }
@@ -426,19 +425,40 @@
       __draft: true,
       __draftID: Math.random().toString(36),
       __date: new Date(),
-      path: this.path,
-      patchNum: this.patchNum,
-      side: this._getSide(this.isOnParent),
-      __commentSide: this.commentSide,
     };
-    if (opt_lineNum) {
-      d.line = opt_lineNum;
-    }
-    if (opt_range) {
-      d.range = opt_range;
-    }
-    if (this.parentIndex) {
-      d.parent = this.parentIndex;
+
+    // For replies, always use same meta info as root.
+    if (this.comments && this.comments.length >= 1) {
+      const rootComment = this.comments[0];
+      [
+        'path',
+        'patchNum',
+        'side',
+        '__commentSide',
+        'line',
+        'range',
+        'parent',
+      ].forEach(key => {
+        if (rootComment.hasOwnProperty(key)) {
+          d[key] = rootComment[key];
+        }
+      });
+    } else {
+      // Set meta info for root comment.
+      d.path = this.path;
+      d.patchNum = this.patchNum;
+      d.side = this._getSide(this.isOnParent);
+      d.__commentSide = this.commentSide;
+
+      if (opt_lineNum) {
+        d.line = opt_lineNum;
+      }
+      if (opt_range) {
+        d.range = opt_range;
+      }
+      if (this.parentIndex) {
+        d.parent = this.parentIndex;
+      }
     }
     return d;
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.js b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.js
index 109e413..01b3660 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.js
@@ -17,86 +17,137 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        font-family: var(--font-family);
-        font-size: var(--font-size-normal);
-        font-weight: var(--font-weight-normal);
-        line-height: var(--line-height-normal);
-      }
-      gr-button {
-        margin-left: var(--spacing-m);
-      }
-      gr-comment:not(:last-of-type) {
-        border-bottom: 1px solid var(--comment-separator-color);
-      }
-      #actions {
-        margin-left: auto;
-        padding: var(--spacing-m);
-      }
-      #container {
-        background-color: var(--comment-background-color);
-        color: var(--comment-text-color);
-        display: block;
-        margin: 0 var(--spacing-s) var(--spacing-s);
-        white-space: normal;
-        box-shadow: var(--elevation-level-2);
-        border-radius: var(--border-radius);
-        /** This is required for firefox to continue the inheritance */
-        -webkit-user-select: inherit;
-        -moz-user-select: inherit;
-        -ms-user-select: inherit;
-        user-select: inherit;
-      }
-      #container.unresolved {
-        background-color: var(--unresolved-comment-background-color);
-      }
-      #container.robotComment {
-        background-color: var(--robot-comment-background-color);
-      }
-      #commentInfoContainer {
-        border-top: 1px dotted var(--border-color);
-        display: flex;
-      }
-      #unresolvedLabel {
-        font-family: var(--font-family);
-        margin: auto 0;
-        padding: var(--spacing-m);
-      }
-      .pathInfo {
-        display: flex;
-        align-items: baseline;
-        justify-content: space-between;
-        padding: 0 var(--spacing-s) var(--spacing-s);
-      }
-      .descriptionText {
-        margin-left: var(--spacing-m);
-        font-style: italic;
-      }
-    </style>
-    <template is="dom-if" if="[[showFilePath]]">
-      <div class="pathInfo">
-        <a href\$="[[_getDiffUrlForComment(projectName, changeNum, path, patchNum)]]">[[_computeDisplayPath(path)]]</a>
-        <span class="descriptionText">Patchset [[patchNum]]</span>
-      </div>
+  <style include="shared-styles">
+    :host {
+      font-family: var(--font-family);
+      font-size: var(--font-size-normal);
+      font-weight: var(--font-weight-normal);
+      line-height: var(--line-height-normal);
+    }
+    gr-button {
+      margin-left: var(--spacing-m);
+    }
+    gr-comment:not(:last-of-type) {
+      border-bottom: 1px solid var(--comment-separator-color);
+    }
+    #actions {
+      margin-left: auto;
+      padding: var(--spacing-m);
+    }
+    #container {
+      background-color: var(--comment-background-color);
+      color: var(--comment-text-color);
+      display: block;
+      margin: 0 var(--spacing-s) var(--spacing-s);
+      white-space: normal;
+      box-shadow: var(--elevation-level-2);
+      border-radius: var(--border-radius);
+      /** This is required for firefox to continue the inheritance */
+      -webkit-user-select: inherit;
+      -moz-user-select: inherit;
+      -ms-user-select: inherit;
+      user-select: inherit;
+    }
+    #container.unresolved {
+      background-color: var(--unresolved-comment-background-color);
+    }
+    #container.robotComment {
+      background-color: var(--robot-comment-background-color);
+    }
+    #commentInfoContainer {
+      border-top: 1px dotted var(--border-color);
+      display: flex;
+    }
+    #unresolvedLabel {
+      font-family: var(--font-family);
+      margin: auto 0;
+      padding: var(--spacing-m);
+    }
+    .pathInfo {
+      display: flex;
+      align-items: baseline;
+      justify-content: space-between;
+      padding: 0 var(--spacing-s) var(--spacing-s);
+    }
+    .descriptionText {
+      margin-left: var(--spacing-m);
+      font-style: italic;
+    }
+  </style>
+  <template is="dom-if" if="[[showFilePath]]">
+    <div class="pathInfo">
+      <a
+        href$="[[_getDiffUrlForComment(projectName, changeNum, path, patchNum)]]"
+        >[[_computeDisplayPath(path)]]</a
+      >
+      <span class="descriptionText">Patchset [[patchNum]]</span>
+    </div>
+  </template>
+  <div
+    id="container"
+    class$="[[_computeHostClass(unresolved, isRobotComment)]]"
+  >
+    <template
+      id="commentList"
+      is="dom-repeat"
+      items="[[_orderedComments]]"
+      as="comment"
+    >
+      <gr-comment
+        comment="{{comment}}"
+        comments="{{comments}}"
+        robot-button-disabled="[[_shouldDisableAction(_showActions, _lastComment)]]"
+        change-num="[[changeNum]]"
+        patch-num="[[patchNum]]"
+        draft="[[_isDraft(comment)]]"
+        show-actions="[[_showActions]]"
+        comment-side="[[comment.__commentSide]]"
+        side="[[comment.side]]"
+        project-config="[[_projectConfig]]"
+        on-create-fix-comment="_handleCommentFix"
+        on-comment-discard="_handleCommentDiscard"
+        on-comment-save="_handleCommentSavedOrDiscarded"
+      ></gr-comment>
     </template>
-    <div id="container" class\$="[[_computeHostClass(unresolved, isRobotComment)]]">
-      <template id="commentList" is="dom-repeat" items="[[_orderedComments]]" as="comment">
-        <gr-comment comment="{{comment}}" comments="{{comments}}" robot-button-disabled="[[_shouldDisableAction(_showActions, _lastComment)]]" change-num="[[changeNum]]" patch-num="[[patchNum]]" draft="[[_isDraft(comment)]]" show-actions="[[_showActions]]" comment-side="[[comment.__commentSide]]" side="[[comment.side]]" project-config="[[_projectConfig]]" on-create-fix-comment="_handleCommentFix" on-comment-discard="_handleCommentDiscard" on-comment-save="_handleCommentSavedOrDiscarded"></gr-comment>
-      </template>
-      <div id="commentInfoContainer" hidden\$="[[_hideActions(_showActions, _lastComment)]]">
-        <span id="unresolvedLabel" hidden\$="[[!unresolved]]">Unresolved</span>
-        <div id="actions">
-          <gr-button id="replyBtn" link="" class="action reply" on-click="_handleCommentReply">Reply</gr-button>
-          <gr-button id="quoteBtn" link="" class="action quote" on-click="_handleCommentQuote">Quote</gr-button>
-          <template is="dom-if" if="[[unresolved]]">
-            <gr-button id="ackBtn" link="" class="action ack" on-click="_handleCommentAck">Ack</gr-button>
-            <gr-button id="doneBtn" link="" class="action done" on-click="_handleCommentDone">Done</gr-button>
-          </template>
-        </div>
+    <div
+      id="commentInfoContainer"
+      hidden$="[[_hideActions(_showActions, _lastComment)]]"
+    >
+      <span id="unresolvedLabel" hidden$="[[!unresolved]]">Unresolved</span>
+      <div id="actions">
+        <gr-button
+          id="replyBtn"
+          link=""
+          class="action reply"
+          on-click="_handleCommentReply"
+          >Reply</gr-button
+        >
+        <gr-button
+          id="quoteBtn"
+          link=""
+          class="action quote"
+          on-click="_handleCommentQuote"
+          >Quote</gr-button
+        >
+        <template is="dom-if" if="[[unresolved]]">
+          <gr-button
+            id="ackBtn"
+            link=""
+            class="action ack"
+            on-click="_handleCommentAck"
+            >Ack</gr-button
+          >
+          <gr-button
+            id="doneBtn"
+            link=""
+            class="action done"
+            on-click="_handleCommentDone"
+            >Done</gr-button
+          >
+        </template>
       </div>
     </div>
-    <gr-reporting id="reporting"></gr-reporting>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-storage id="storage"></gr-storage>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html
index d7c493c..eaa6547 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-comment-thread</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -267,6 +268,8 @@
       updated: '2015-12-08 19:48:33.843000000',
       path: '/path/to/file.txt',
       unresolved: true,
+      patchNum: 3,
+      __commentSide: 'left',
     }];
     flushAsynchronousOperations();
   });
@@ -278,7 +281,7 @@
   test('reply', () => {
     const commentEl = element.shadowRoot
         .querySelector('gr-comment');
-    const reportStub = sandbox.stub(element.$.reporting,
+    const reportStub = sandbox.stub(element.reporting,
         'recordDraftInteraction');
     assert.ok(commentEl);
 
@@ -296,7 +299,7 @@
   test('quote reply', () => {
     const commentEl = element.shadowRoot
         .querySelector('gr-comment');
-    const reportStub = sandbox.stub(element.$.reporting,
+    const reportStub = sandbox.stub(element.reporting,
         'recordDraftInteraction');
     assert.ok(commentEl);
 
@@ -312,7 +315,7 @@
   });
 
   test('quote reply multiline', () => {
-    const reportStub = sandbox.stub(element.$.reporting,
+    const reportStub = sandbox.stub(element.reporting,
         'recordDraftInteraction');
     element.comments = [{
       author: {
@@ -343,7 +346,7 @@
   });
 
   test('ack', done => {
-    const reportStub = sandbox.stub(element.$.reporting,
+    const reportStub = sandbox.stub(element.reporting,
         'recordDraftInteraction');
     element.changeNum = '42';
     element.patchNum = '1';
@@ -366,7 +369,7 @@
   });
 
   test('done', done => {
-    const reportStub = sandbox.stub(element.$.reporting,
+    const reportStub = sandbox.stub(element.reporting,
         'recordDraftInteraction');
     element.changeNum = '42';
     element.patchNum = '1';
@@ -439,7 +442,6 @@
     element.path = '/path/to/file.txt';
     element.push('comments', element._newReply(
         element.comments[0].id,
-        element.comments[0].line,
         element.comments[0].path,
         'it’s pronouced jiff, not giff'));
     flushAsynchronousOperations();
@@ -633,7 +635,7 @@
     });
 
     test('comment in_reply_to is either null or most recent comment', () => {
-      element._createReplyComment(element.comments[3], 'dummy', true);
+      element._createReplyComment('dummy', true);
       flushAsynchronousOperations();
       assert.equal(element._orderedComments.length, 5);
       assert.equal(element._orderedComments[4].in_reply_to, 'jacks_reply');
@@ -641,22 +643,28 @@
 
     test('resolvable comments', () => {
       assert.isFalse(element.unresolved);
-      element._createReplyComment(element.comments[3], 'dummy', true, true);
+      element._createReplyComment('dummy', true, true);
       flushAsynchronousOperations();
       assert.isTrue(element.unresolved);
     });
 
-    test('_setInitialExpandedState', () => {
+    test('_setInitialExpandedState with unresolved', () => {
       element.unresolved = true;
       element._setInitialExpandedState();
       for (let i = 0; i < element.comments.length; i++) {
         assert.isFalse(element.comments[i].collapsed);
       }
+    });
+
+    test('_setInitialExpandedState without unresolved', () => {
       element.unresolved = false;
       element._setInitialExpandedState();
       for (let i = 0; i < element.comments.length; i++) {
         assert.isTrue(element.comments[i].collapsed);
       }
+    });
+
+    test('_setInitialExpandedState with robot_ids', () => {
       for (let i = 0; i < element.comments.length; i++) {
         element.comments[i].robot_id = 123;
       }
@@ -665,6 +673,16 @@
         assert.isFalse(element.comments[i].collapsed);
       }
     });
+
+    test('_setInitialExpandedState with collapsed state', () => {
+      element.comments[0].collapsed = false;
+      element.unresolved = false;
+      element._setInitialExpandedState();
+      assert.isFalse(element.comments[0].collapsed);
+      for (let i = 1; i < element.comments.length; i++) {
+        assert.isTrue(element.comments[i].collapsed);
+      }
+    });
   });
 
   test('_computeHostClass', () => {
@@ -688,14 +706,21 @@
     assert.equal(element.comments[0].unresolved, true);
   });
 
-  test('_newDraft', () => {
-    element.commentSide = 'left';
-    element.patchNum = 3;
+  test('_newDraft with root', () => {
     const draft = element._newDraft();
     assert.equal(draft.__commentSide, 'left');
     assert.equal(draft.patchNum, 3);
   });
 
+  test('_newDraft with no root', () => {
+    element.comments = [];
+    element.commentSide = 'right';
+    element.patchNum = 2;
+    const draft = element._newDraft();
+    assert.equal(draft.__commentSide, 'right');
+    assert.equal(draft.patchNum, 2);
+  });
+
   test('new comment gets created', () => {
     element.comments = [];
     element.addOrEditDraft(1);
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 5b6888a..4967440 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -14,11 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/shared-styles.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
 import '../../plugins/gr-endpoint-param/gr-endpoint-param.js';
 import '../gr-button/gr-button.js';
@@ -40,6 +37,8 @@
 import {htmlTemplate} from './gr-comment_html.js';
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
 import {getRootElement} from '../../../scripts/rootElement.js';
+import {GrDisplayNameUtils} from '../../../scripts/gr-display-name-utils/gr-display-name-utils.js';
+import {appContext} from '../../../services/app-context.js';
 
 const STORAGE_DEBOUNCE_INTERVAL = 400;
 const TOAST_DEBOUNCE_INTERVAL = 200;
@@ -68,7 +67,7 @@
 ];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrComment extends mixinBehaviors( [
   KeyboardShortcutBehavior,
@@ -109,6 +108,12 @@
    */
 
   /**
+   * Fired when editing status changed.
+   *
+   * @event comment-editing-changed
+   */
+
+  /**
    * Fired when the comment's timestamp is tapped.
    *
    * @event comment-anchor-tap
@@ -212,6 +217,7 @@
         type: Boolean,
         value: false,
       },
+      _serverConfig: Object,
     };
   }
 
@@ -233,6 +239,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   attached() {
     super.attached();
@@ -244,6 +255,9 @@
     this._getIsAdmin().then(isAdmin => {
       this._isAdmin = isAdmin;
     });
+    this.$.restAPI.getConfig().then(cfg => {
+      this._serverConfig = cfg;
+    });
   }
 
   /** @override */
@@ -256,6 +270,11 @@
   }
 
   _onEditingChange(editing) {
+    this.dispatchEvent(new CustomEvent('comment-editing-changed', {
+      detail: !!editing,
+      bubbles: true,
+      composed: true,
+    }));
     if (!editing) return;
     // visibility based on cache this will make sure we only and always show
     // a tip once every Math.max(a day, period between creating comments)
@@ -267,7 +286,7 @@
       this._showRespectfulTip = true;
       const randomIdx = this.getRandomNum(0, RESPECTFUL_REVIEW_TIPS.length);
       this._respectfulReviewTip = RESPECTFUL_REVIEW_TIPS[randomIdx];
-      this.$.reporting.reportInteraction(
+      this.reporting.reportInteraction(
           'respectful-tip-appeared',
           {tip: this._respectfulReviewTip}
       );
@@ -287,7 +306,7 @@
 
   _dismissRespectfulTip() {
     this._respectfulTipDismissed = true;
-    this.$.reporting.reportInteraction(
+    this.reporting.reportInteraction(
         'respectful-tip-dismissed',
         {tip: this._respectfulReviewTip}
     );
@@ -296,7 +315,7 @@
   }
 
   _onRespectfulReadMoreClick() {
-    this.$.reporting.reportInteraction('respectful-read-more-clicked');
+    this.reporting.reportInteraction('respectful-read-more-clicked');
   }
 
   get textarea() {
@@ -567,7 +586,7 @@
     e.preventDefault();
     this._messageText = this.comment.message;
     this.editing = true;
-    this.$.reporting.recordDraftInteraction();
+    this.reporting.recordDraftInteraction();
   }
 
   _handleSave(e) {
@@ -579,7 +598,7 @@
     }
     const timingLabel = this.comment.id ?
       REPORT_UPDATE_DRAFT : REPORT_CREATE_DRAFT;
-    const timer = this.$.reporting.getTimer(timingLabel);
+    const timer = this.reporting.getTimer(timingLabel);
     this.set('comment.__editing', false);
     return this.save().then(() => { timer.end(); });
   }
@@ -627,7 +646,7 @@
 
   _handleDiscard(e) {
     e.preventDefault();
-    this.$.reporting.recordDraftInteraction();
+    this.reporting.recordDraftInteraction();
 
     if (!this._messageText) {
       this._discardDraft();
@@ -642,7 +661,7 @@
 
   _handleConfirmDiscard(e) {
     e.preventDefault();
-    const timer = this.$.reporting.getTimer(REPORT_DISCARD_DRAFT);
+    const timer = this.reporting.getTimer(REPORT_DISCARD_DRAFT);
     this._closeConfirmDiscardOverlay();
     return this._discardDraft().then(() => { timer.end(); });
   }
@@ -783,7 +802,7 @@
   }
 
   _handleToggleResolved() {
-    this.$.reporting.recordDraftInteraction();
+    this.reporting.recordDraftInteraction();
     this.resolved = !this.resolved;
     // Modify payload instead of this.comment, as this.comment is passed from
     // the parent by ref.
@@ -812,12 +831,15 @@
     return overlay.open();
   }
 
-  _computeAuthorName(comment) {
-    if (!comment) return '';
+  _computeAuthorName(comment, serverConfig) {
+    if ([comment, serverConfig].includes(undefined)) return '';
     if (comment.robot_id) {
       return comment.robot_id;
     }
-    return comment.author && comment.author.name;
+    if (comment.author) {
+      return GrDisplayNameUtils.getDisplayName(serverConfig, comment.author);
+    }
+    return '';
   }
 
   _computeHideRunDetails(comment, collapsed) {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.js
index 5c20cd5..02731ce 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.js
@@ -17,331 +17,445 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        font-family: var(--font-family);
-        padding: var(--spacing-m);
+  <style include="shared-styles">
+    :host {
+      display: block;
+      font-family: var(--font-family);
+      padding: var(--spacing-m);
+    }
+    :host([disabled]) {
+      pointer-events: none;
+    }
+    :host([disabled]) .actions,
+    :host([disabled]) .robotActions,
+    :host([disabled]) .date {
+      opacity: 0.5;
+    }
+    :host([discarding]) {
+      display: none;
+    }
+    .header {
+      align-items: center;
+      cursor: pointer;
+      display: flex;
+      margin: calc(0px - var(--spacing-m)) calc(0px - var(--spacing-m)) 0
+        calc(0px - var(--spacing-m));
+      padding: var(--spacing-m);
+    }
+    .headerLeft > span {
+      font-weight: var(--font-weight-bold);
+    }
+    .container.collapsed .header {
+      margin-bottom: calc(0 - var(--spacing-m));
+    }
+    .headerMiddle {
+      color: var(--deemphasized-text-color);
+      flex: 1;
+      overflow: hidden;
+    }
+    .draftLabel,
+    .draftTooltip {
+      color: var(--deemphasized-text-color);
+      display: none;
+    }
+    .date {
+      justify-content: flex-end;
+      margin-left: 5px;
+      min-width: 4.5em;
+      text-align: right;
+      white-space: nowrap;
+    }
+    span.date {
+      color: var(--deemphasized-text-color);
+    }
+    span.date:hover {
+      text-decoration: underline;
+    }
+    .actions,
+    .robotActions {
+      display: flex;
+      justify-content: flex-end;
+      padding-top: 0;
+    }
+    .action {
+      margin-left: var(--spacing-l);
+    }
+    .rightActions {
+      display: flex;
+      justify-content: flex-end;
+    }
+    .rightActions gr-button {
+      --gr-button: {
+        height: 20px;
+        padding: 0 var(--spacing-s);
       }
-      :host([disabled]) {
-        pointer-events: none;
-      }
-      :host([disabled]) .actions,
-      :host([disabled]) .robotActions,
-      :host([disabled]) .date {
-        opacity: .5;
-      }
-      :host([discarding]) {
-        display: none;
-      }
-      .header {
-        align-items: center;
-        cursor: pointer;
-        display: flex;
-        margin: calc(0px - var(--spacing-m)) calc(0px - var(--spacing-m)) 0 calc(0px - var(--spacing-m));
-        padding: var(--spacing-m);
-      }
-      .headerLeft > span {
-        font-weight: var(--font-weight-bold);
-      }
-      .container.collapsed .header {
-        margin-bottom: calc(0 - var(--spacing-m));
-      }
-      .headerMiddle {
-        color: var(--deemphasized-text-color);
-        flex: 1;
-        overflow: hidden;
-      }
-      .draftLabel,
-      .draftTooltip {
-        color: var(--deemphasized-text-color);
-        display: none;
-      }
-      .date {
-        justify-content: flex-end;
-        margin-left: 5px;
-        min-width: 4.5em;
-        text-align: right;
-        white-space: nowrap;
-      }
-      span.date {
-        color: var(--deemphasized-text-color);
-      }
-      span.date:hover {
-        text-decoration: underline;
-      }
-      .actions, .robotActions {
-        display: flex;
-        justify-content: flex-end;
-        padding-top: 0;
-      }
-      .action {
-        margin-left: var(--spacing-l);
-      }
-      .rightActions {
-        display: flex;
-        justify-content: flex-end;
-      }
-      .rightActions gr-button {
-        --gr-button: {
-          height: 20px;
-          padding: 0 var(--spacing-s);
-        }
-      }
-      .editMessage {
-        display: none;
-        margin: var(--spacing-m) 0;
-        width: 100%;
-      }
-      .container:not(.draft) .actions .hideOnPublished {
-        display: none;
-      }
-      .draft .reply,
-      .draft .quote,
-      .draft .ack,
-      .draft .done {
-        display: none;
-      }
-      .draft .draftLabel,
-      .draft .draftTooltip {
-        display: inline;
-      }
-      .draft:not(.editing) .save,
-      .draft:not(.editing) .cancel {
-        display: none;
-      }
-      .editing .message,
-      .editing .reply,
-      .editing .quote,
-      .editing .ack,
-      .editing .done,
-      .editing .edit,
-      .editing .discard,
-      .editing .unresolved {
-        display: none;
-      }
-      .editing .editMessage {
-        display: block;
-      }
-      .show-hide {
-        margin-left: var(--spacing-s);
-      }
-      .robotId {
-        color: var(--deemphasized-text-color);
-        margin-bottom: var(--spacing-m);
-        margin-top: -.4em;
-      }
-      .robotIcon {
-        margin-right: var(--spacing-xs);
-        /* because of the antenna of the robot, it looks off center even when it
+    }
+    .editMessage {
+      display: none;
+      margin: var(--spacing-m) 0;
+      width: 100%;
+    }
+    .container:not(.draft) .actions .hideOnPublished {
+      display: none;
+    }
+    .draft .reply,
+    .draft .quote,
+    .draft .ack,
+    .draft .done {
+      display: none;
+    }
+    .draft .draftLabel,
+    .draft .draftTooltip {
+      display: inline;
+    }
+    .draft:not(.editing) .save,
+    .draft:not(.editing) .cancel {
+      display: none;
+    }
+    .editing .message,
+    .editing .reply,
+    .editing .quote,
+    .editing .ack,
+    .editing .done,
+    .editing .edit,
+    .editing .discard,
+    .editing .unresolved {
+      display: none;
+    }
+    .editing .editMessage {
+      display: block;
+    }
+    .show-hide {
+      margin-left: var(--spacing-s);
+    }
+    .robotId {
+      color: var(--deemphasized-text-color);
+      margin-bottom: var(--spacing-m);
+      margin-top: -0.4em;
+    }
+    .robotIcon {
+      margin-right: var(--spacing-xs);
+      /* because of the antenna of the robot, it looks off center even when it
          is centered. artificially adjust margin to account for this. */
-        margin-top: -4px;
+      margin-top: -4px;
+    }
+    .runIdInformation {
+      margin: var(--spacing-m) 0;
+    }
+    .robotRun {
+      margin-left: var(--spacing-m);
+    }
+    .robotRunLink {
+      margin-left: var(--spacing-m);
+    }
+    input.show-hide {
+      display: none;
+    }
+    label.show-hide {
+      cursor: pointer;
+      display: block;
+    }
+    label.show-hide iron-icon {
+      vertical-align: top;
+    }
+    #container .collapsedContent {
+      display: none;
+    }
+    #container.collapsed {
+      padding-bottom: 3px;
+    }
+    #container.collapsed .collapsedContent {
+      display: block;
+      overflow: hidden;
+      padding-left: 5px;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    #container.collapsed .actions,
+    #container.collapsed gr-formatted-text,
+    #container.collapsed gr-textarea,
+    #container.collapsed .respectfulReviewTip {
+      display: none;
+    }
+    .resolve,
+    .unresolved {
+      align-items: center;
+      display: flex;
+      flex: 1;
+      margin: 0;
+    }
+    .resolve label {
+      color: var(--comment-text-color);
+    }
+    gr-dialog .main {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+    }
+    .comment-extra-note {
+      color: var(--deemphasized-text-color);
+      border: 1px solid var(--deemphasized-text-color);
+      border-radius: var(--border-radius);
+      padding: 0px var(--spacing-s);
+    }
+    #deleteBtn {
+      display: none;
+      --gr-button: {
+        color: var(--deemphasized-text-color);
+        padding: 0;
       }
-      .runIdInformation {
-        margin: var(--spacing-m) 0;
-      }
-      .robotRun {
-        margin-left: var(--spacing-m);
-      }
-      .robotRunLink {
-        margin-left: var(--spacing-m);
-      }
-      input.show-hide {
-        display: none;
-      }
-      label.show-hide {
-        cursor: pointer;
-        display: block;
-      }
-      label.show-hide iron-icon {
-        vertical-align: top;
-      }
-      #container .collapsedContent {
-        display: none;
-      }
-      #container.collapsed {
-        padding-bottom: 3px;
-      }
-      #container.collapsed .collapsedContent {
-        display: block;
-        overflow: hidden;
-        padding-left: 5px;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-      #container.collapsed .actions,
-      #container.collapsed gr-formatted-text,
-      #container.collapsed gr-textarea,
-      #container.collapsed .respectfulReviewTip{
-        display: none;
-      }
-      .resolve,
-      .unresolved {
-        align-items: center;
-        display: flex;
-        flex: 1;
-        margin: 0;
-      }
-      .resolve label {
-        color: var(--comment-text-color);
-      }
-      gr-dialog .main {
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-      }
-      #deleteBtn {
-        display: none;
-        --gr-button: {
-          color: var(--deemphasized-text-color);
-          padding: 0;
-        }
-      }
-      #deleteBtn.showDeleteButtons {
-        display: block;
-      }
+    }
+    #deleteBtn.showDeleteButtons {
+      display: block;
+    }
 
-      /** Disable select for the caret and actions */
-      .actions,
-      .show-hide {
-        -webkit-user-select: none;
-        -moz-user-select: none;
-        -ms-user-select: none;
-        user-select: none;
-      }
+    /** Disable select for the caret and actions */
+    .actions,
+    .show-hide {
+      -webkit-user-select: none;
+      -moz-user-select: none;
+      -ms-user-select: none;
+      user-select: none;
+    }
 
-      .respectfulReviewTip {
-        justify-content: space-between;
-        display: flex;
-        padding: var(--spacing-m);
-        border: 1px solid var(--border-color);
-        border-radius: var(--border-radius);
-        margin-bottom: var(--spacing-m);
-      }
-      .respectfulReviewTip div {
-        display: flex;
-      }
-      .respectfulReviewTip div iron-icon {
-        margin-right: var(--spacing-s);
-      }
-      .respectfulReviewTip a {
-        white-space: nowrap;
-        margin-right: var(--spacing-s);
-        padding-left: var(--spacing-m);
-        text-decoration: none;
-      }
-      .pointer {
-        cursor: pointer;
-      }
-    </style>
-    <div id="container" class="container">
-      <div class="header" id="header" on-click="_handleToggleCollapsed">
-        <div class="headerLeft">
-          <span class="authorName">[[_computeAuthorName(comment)]]</span>
-          <span class="draftLabel">DRAFT</span>
-          <gr-tooltip-content class="draftTooltip" has-tooltip="" title="This draft is only visible to you. To publish drafts, click the 'Reply' or 'Start review' button at the top of the change or press the 'A' key." max-width="20em" show-icon=""></gr-tooltip-content>
-        </div>
-        <div class="headerMiddle">
-          <span class="collapsedContent">[[comment.message]]</span>
-        </div>
-        <div hidden\$="[[_computeHideRunDetails(comment, collapsed)]]" class="runIdMessage message">
-          <div class="runIdInformation">
-            <a class="robotRunLink" href\$="[[comment.url]]">
-              <span class="robotRun link">Run Details</span>
-            </a>
-          </div>
-        </div>
-        <gr-button id="deleteBtn" link="" class\$="action delete [[_computeDeleteButtonClass(_isAdmin, draft)]]" hidden\$="[[isRobotComment]]" on-click="_handleCommentDelete">
-          <iron-icon id="icon" icon="gr-icons:delete"></iron-icon>
-        </gr-button>
-        <span class="date" on-click="_handleAnchorClick">
-          <gr-date-formatter has-tooltip="" date-str="[[comment.updated]]"></gr-date-formatter>
+    .respectfulReviewTip {
+      justify-content: space-between;
+      display: flex;
+      padding: var(--spacing-m);
+      border: 1px solid var(--border-color);
+      border-radius: var(--border-radius);
+      margin-bottom: var(--spacing-m);
+    }
+    .respectfulReviewTip div {
+      display: flex;
+    }
+    .respectfulReviewTip div iron-icon {
+      margin-right: var(--spacing-s);
+    }
+    .respectfulReviewTip a {
+      white-space: nowrap;
+      margin-right: var(--spacing-s);
+      padding-left: var(--spacing-m);
+      text-decoration: none;
+    }
+    .pointer {
+      cursor: pointer;
+    }
+  </style>
+  <div id="container" class="container">
+    <div class="header" id="header" on-click="_handleToggleCollapsed">
+      <div class="headerLeft">
+        <span class="authorName">
+          [[_computeAuthorName(comment, _serverConfig)]]
         </span>
-        <div class="show-hide">
-          <label class="show-hide">
-            <input type="checkbox" class="show-hide" checked\$="[[collapsed]]" on-change="_handleToggleCollapsed">
-            <iron-icon id="icon" icon="[[_computeShowHideIcon(collapsed)]]">
-            </iron-icon>
-          </label>
+        <span class="draftLabel">DRAFT</span>
+        <gr-tooltip-content
+          class="draftTooltip"
+          has-tooltip=""
+          title="This draft is only visible to you. To publish drafts, click the 'Reply' or 'Start review' button at the top of the change or press the 'A' key."
+          max-width="20em"
+          show-icon=""
+        ></gr-tooltip-content>
+      </div>
+      <div class="headerMiddle">
+        <span class="collapsedContent">[[comment.message]]</span>
+      </div>
+      <div
+        hidden$="[[_computeHideRunDetails(comment, collapsed)]]"
+        class="runIdMessage message"
+      >
+        <div class="runIdInformation">
+          <a class="robotRunLink" href$="[[comment.url]]">
+            <span class="robotRun link">Run Details</span>
+          </a>
         </div>
       </div>
-      <div class="body">
-        <template is="dom-if" if="[[isRobotComment]]">
-          <div class="robotId" hidden\$="[[collapsed]]">
-            [[comment.author.name]]
-          </div>
-        </template>
-        <template is="dom-if" if="[[editing]]">
-          <gr-textarea id="editTextarea" class="editMessage" autocomplete="on" code="" disabled="{{disabled}}" rows="4" text="{{_messageText}}"></gr-textarea>
-          <template is="dom-if" if="[[_computeVisibilityOfTip(_showRespectfulTip, _respectfulTipDismissed)]]">
-            <div class="respectfulReviewTip">
-              <div>
-                <gr-tooltip-content has-tooltip="" title="Tips for respectful code reviews.">
-                  <iron-icon class="pointer" icon="gr-icons:lightbulb-outline"></iron-icon>
-                </gr-tooltip-content>
-                [[_respectfulReviewTip]]
-              </div>
-              <div>
-                <a tabindex="-1" on-click="_onRespectfulReadMoreClick" href="https://testing.googleblog.com/2019/11/code-health-respectful-reviews-useful.html" target="_blank">
-                  Read more
-                </a>
-                <a tabindex="-1" class="close pointer" on-click="_dismissRespectfulTip">Not helpful</a>
-              </div>
-            </div>
-          </template>
-        </template>
-        <!--The message class is needed to ensure selectability from
-        gr-diff-selection.-->
-        <gr-formatted-text class="message" content="[[comment.message]]" no-trailing-margin="[[!comment.__draft]]" config="[[projectConfig.commentlinks]]"></gr-formatted-text>
-        <div class="actions humanActions" hidden\$="[[!_showHumanActions]]">
-          <div class="action resolve hideOnPublished">
-            <label>
-              <input type="checkbox" id="resolvedCheckbox" checked="[[resolved]]" on-change="_handleToggleResolved">
-              Resolved
-            </label>
-          </div>
-          <div class="rightActions">
-            <gr-button link="" class="action cancel hideOnPublished" on-click="_handleCancel">Cancel</gr-button>
-            <gr-button link="" class="action discard hideOnPublished" on-click="_handleDiscard">Discard</gr-button>
-            <gr-button link="" class="action edit hideOnPublished" on-click="_handleEdit">Edit</gr-button>
-            <gr-button link="" disabled\$="[[_computeSaveDisabled(_messageText, comment, resolved)]]" class="action save hideOnPublished" on-click="_handleSave">Save</gr-button>
-          </div>
-        </div>
-        <div class="robotActions" hidden\$="[[!_showRobotActions]]">
-          <template is="dom-if" if="[[isRobotComment]]">
-            <gr-endpoint-decorator name="robot-comment-controls">
-              <gr-endpoint-param name="comment" value="[[comment]]">
-              </gr-endpoint-param>
-            </gr-endpoint-decorator>
-            <gr-button link="" secondary="" class="action show-fix" hidden\$="[[_hasNoFix(comment)]]" on-click="_handleShowFix">
-              Show Fix
-            </gr-button>
-            <template is="dom-if" if="[[!_hasHumanReply]]">
-              <gr-button link="" class="action fix" on-click="_handleFix" disabled="[[robotButtonDisabled]]">
-                Please Fix
-              </gr-button>
-            </template>
-          </template>
-        </div>
+      <template is="dom-if" if="[[comment.extraNote]]">
+        <span class="comment-extra-note">[[comment.extraNote]]</span>
+      </template>
+      <gr-button
+        id="deleteBtn"
+        link=""
+        class$="action delete [[_computeDeleteButtonClass(_isAdmin, draft)]]"
+        hidden$="[[isRobotComment]]"
+        on-click="_handleCommentDelete"
+      >
+        <iron-icon id="icon" icon="gr-icons:delete"></iron-icon>
+      </gr-button>
+      <span class="date" on-click="_handleAnchorClick">
+        <gr-date-formatter
+          has-tooltip=""
+          date-str="[[comment.updated]]"
+        ></gr-date-formatter>
+      </span>
+      <div class="show-hide">
+        <label class="show-hide">
+          <input
+            type="checkbox"
+            class="show-hide"
+            checked$="[[collapsed]]"
+            on-change="_handleToggleCollapsed"
+          />
+          <iron-icon id="icon" icon="[[_computeShowHideIcon(collapsed)]]">
+          </iron-icon>
+        </label>
       </div>
     </div>
-    <template is="dom-if" if="[[_enableOverlay]]">
-      <gr-overlay id="confirmDeleteOverlay" with-backdrop="">
-        <gr-confirm-delete-comment-dialog id="confirmDeleteComment" on-confirm="_handleConfirmDeleteComment" on-cancel="_handleCancelDeleteComment">
-        </gr-confirm-delete-comment-dialog>
-      </gr-overlay>
-      <gr-overlay id="confirmDiscardOverlay" with-backdrop="">
-        <gr-dialog id="confirmDiscardDialog" confirm-label="Discard" confirm-on-enter="" on-confirm="_handleConfirmDiscard" on-cancel="_closeConfirmDiscardOverlay">
-          <div class="header" slot="header">
-            Discard comment
+    <div class="body">
+      <template is="dom-if" if="[[isRobotComment]]">
+        <div class="robotId" hidden$="[[collapsed]]">
+          [[comment.author.name]]
+        </div>
+      </template>
+      <template is="dom-if" if="[[editing]]">
+        <gr-textarea
+          id="editTextarea"
+          class="editMessage"
+          autocomplete="on"
+          code=""
+          disabled="{{disabled}}"
+          rows="4"
+          text="{{_messageText}}"
+        ></gr-textarea>
+        <template
+          is="dom-if"
+          if="[[_computeVisibilityOfTip(_showRespectfulTip, _respectfulTipDismissed)]]"
+        >
+          <div class="respectfulReviewTip">
+            <div>
+              <gr-tooltip-content
+                has-tooltip=""
+                title="Tips for respectful code reviews."
+              >
+                <iron-icon
+                  class="pointer"
+                  icon="gr-icons:lightbulb-outline"
+                ></iron-icon>
+              </gr-tooltip-content>
+              [[_respectfulReviewTip]]
+            </div>
+            <div>
+              <a
+                tabindex="-1"
+                on-click="_onRespectfulReadMoreClick"
+                href="https://testing.googleblog.com/2019/11/code-health-respectful-reviews-useful.html"
+                target="_blank"
+              >
+                Read more
+              </a>
+              <a
+                tabindex="-1"
+                class="close pointer"
+                on-click="_dismissRespectfulTip"
+                >Not helpful</a
+              >
+            </div>
           </div>
-          <div class="main" slot="main">
-            Are you sure you want to discard this draft comment?
-          </div>
-        </gr-dialog>
-      </gr-overlay>
-    </template>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
-    <gr-reporting id="reporting"></gr-reporting>
+        </template>
+      </template>
+      <!--The message class is needed to ensure selectability from
+        gr-diff-selection.-->
+      <gr-formatted-text
+        class="message"
+        content="[[comment.message]]"
+        no-trailing-margin="[[!comment.__draft]]"
+        config="[[projectConfig.commentlinks]]"
+      ></gr-formatted-text>
+      <div class="actions humanActions" hidden$="[[!_showHumanActions]]">
+        <div class="action resolve hideOnPublished">
+          <label>
+            <input
+              type="checkbox"
+              id="resolvedCheckbox"
+              checked="[[resolved]]"
+              on-change="_handleToggleResolved"
+            />
+            Resolved
+          </label>
+        </div>
+        <div class="rightActions">
+          <gr-button
+            link=""
+            class="action cancel hideOnPublished"
+            on-click="_handleCancel"
+            >Cancel</gr-button
+          >
+          <gr-button
+            link=""
+            class="action discard hideOnPublished"
+            on-click="_handleDiscard"
+            >Discard</gr-button
+          >
+          <gr-button
+            link=""
+            class="action edit hideOnPublished"
+            on-click="_handleEdit"
+            >Edit</gr-button
+          >
+          <gr-button
+            link=""
+            disabled$="[[_computeSaveDisabled(_messageText, comment, resolved)]]"
+            class="action save hideOnPublished"
+            on-click="_handleSave"
+            >Save</gr-button
+          >
+        </div>
+      </div>
+      <div class="robotActions" hidden$="[[!_showRobotActions]]">
+        <template is="dom-if" if="[[isRobotComment]]">
+          <gr-endpoint-decorator name="robot-comment-controls">
+            <gr-endpoint-param name="comment" value="[[comment]]">
+            </gr-endpoint-param>
+          </gr-endpoint-decorator>
+          <gr-button
+            link=""
+            secondary=""
+            class="action show-fix"
+            hidden$="[[_hasNoFix(comment)]]"
+            on-click="_handleShowFix"
+          >
+            Show Fix
+          </gr-button>
+          <template is="dom-if" if="[[!_hasHumanReply]]">
+            <gr-button
+              link=""
+              class="action fix"
+              on-click="_handleFix"
+              disabled="[[robotButtonDisabled]]"
+            >
+              Please Fix
+            </gr-button>
+          </template>
+        </template>
+      </div>
+    </div>
+  </div>
+  <template is="dom-if" if="[[_enableOverlay]]">
+    <gr-overlay id="confirmDeleteOverlay" with-backdrop="">
+      <gr-confirm-delete-comment-dialog
+        id="confirmDeleteComment"
+        on-confirm="_handleConfirmDeleteComment"
+        on-cancel="_handleCancelDeleteComment"
+      >
+      </gr-confirm-delete-comment-dialog>
+    </gr-overlay>
+    <gr-overlay id="confirmDiscardOverlay" with-backdrop="">
+      <gr-dialog
+        id="confirmDiscardDialog"
+        confirm-label="Discard"
+        confirm-on-enter=""
+        on-confirm="_handleConfirmDiscard"
+        on-cancel="_closeConfirmDiscardOverlay"
+      >
+        <div class="header" slot="header">
+          Discard comment
+        </div>
+        <div class="main" slot="main">
+          Are you sure you want to discard this draft comment?
+        </div>
+      </gr-dialog>
+    </gr-overlay>
+  </template>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <gr-storage id="storage"></gr-storage>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html
index 6493766ee..9cd8906 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-comment</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -257,6 +258,15 @@
         assert.isTrue(element._handleSave.called);
       });
     });
+
+    test('extra note shown if exists', () => {
+      element.comment = {id: 'abc_123', extraNote: 'asd'};
+      flushAsynchronousOperations();
+      assert.equal(element.shadowRoot
+          .querySelector('.comment-extra-note')
+          .textContent, 'asd');
+    });
+
     test('delete comment button for non-admins is hidden', () => {
       element._isAdmin = false;
       assert.isFalse(element.shadowRoot
@@ -310,7 +320,7 @@
         sandbox.stub(element, '_discardDraft')
             .returns(Promise.resolve({}));
         endStub = sinon.stub();
-        getTimerStub = sandbox.stub(element.$.reporting, 'getTimer')
+        getTimerStub = sandbox.stub(element.reporting, 'getTimer')
             .returns({end: endStub});
       });
 
@@ -344,7 +354,7 @@
     });
 
     test('edit reports interaction', () => {
-      const reportStub = sandbox.stub(element.$.reporting,
+      const reportStub = sandbox.stub(element.reporting,
           'recordDraftInteraction');
       MockInteractions.tap(element.shadowRoot
           .querySelector('.edit'));
@@ -352,7 +362,7 @@
     });
 
     test('discard reports interaction', () => {
-      const reportStub = sandbox.stub(element.$.reporting,
+      const reportStub = sandbox.stub(element.reporting,
           'recordDraftInteraction');
       element.draft = true;
       MockInteractions.tap(element.shadowRoot
@@ -368,6 +378,7 @@
     setup(() => {
       stub('gr-rest-api-interface', {
         getAccount() { return Promise.resolve(null); },
+        getConfig() { return Promise.resolve({}); },
         saveDiffDraft() {
           return Promise.resolve({
             ok: true,
@@ -596,7 +607,7 @@
       'header middle content is not visible');
     });
 
-    test('robot comment layout', () => {
+    test('robot comment layout', done => {
       const comment = Object.assign({
         robot_id: 'happy_robot_id',
         url: '/robot/comment',
@@ -606,30 +617,48 @@
       }, element.comment);
       element.comment = comment;
       element.collapsed = false;
-      flushAsynchronousOperations();
+      flush(() => {
+        let runIdMessage;
+        runIdMessage = element.shadowRoot
+            .querySelector('.runIdMessage');
+        assert.isFalse(runIdMessage.hidden);
 
-      let runIdMessage;
-      runIdMessage = element.shadowRoot
-          .querySelector('.runIdMessage');
-      assert.isFalse(runIdMessage.hidden);
+        const runDetailsLink = element.shadowRoot
+            .querySelector('.robotRunLink');
+        assert.isTrue(runDetailsLink.href.indexOf(element.comment.url) !== -1);
 
-      const runDetailsLink = element.shadowRoot
-          .querySelector('.robotRunLink');
-      assert.isTrue(runDetailsLink.href.indexOf(element.comment.url) !== -1);
+        const robotServiceName = element.shadowRoot
+            .querySelector('.authorName');
+        assert.equal(robotServiceName.textContent.trim(), 'happy_robot_id');
 
-      const robotServiceName = element.shadowRoot
-          .querySelector('.authorName');
-      assert.isTrue(robotServiceName.textContent === 'happy_robot_id');
+        const authorName = element.shadowRoot
+            .querySelector('.robotId');
+        assert.isTrue(authorName.innerText === 'Happy Robot');
 
-      const authorName = element.shadowRoot
-          .querySelector('.robotId');
-      assert.isTrue(authorName.innerText === 'Happy Robot');
+        element.collapsed = true;
+        flushAsynchronousOperations();
+        runIdMessage = element.shadowRoot
+            .querySelector('.runIdMessage');
+        assert.isTrue(runIdMessage.hidden);
+        done();
+      });
+    });
 
-      element.collapsed = true;
-      flushAsynchronousOperations();
-      runIdMessage = element.shadowRoot
-          .querySelector('.runIdMessage');
-      assert.isTrue(runIdMessage.hidden);
+    test('author name fallback to email', done => {
+      const comment = Object.assign({
+        url: '/robot/comment',
+        author: {
+          email: 'test@test.com',
+        },
+      }, element.comment);
+      element.comment = comment;
+      element.collapsed = false;
+      flush(() => {
+        const authorName = element.shadowRoot
+            .querySelector('.authorName');
+        assert.equal(authorName.innerText.trim(), 'test@test.com');
+        done();
+      });
     });
 
     test('draft creation/cancellation', done => {
@@ -772,12 +801,12 @@
       element.flushDebouncer('fire-update');
       element.flushDebouncer('store');
       assert.equal(dispatchEventStub.lastCall.args[0].type, 'comment-update'),
-      assert.isTrue(dispatchEventStub.calledOnce);
+      assert.isTrue(dispatchEventStub.calledTwice);
 
       element._messageText = 'good news, everyone!';
       element.flushDebouncer('fire-update');
       element.flushDebouncer('store');
-      assert.isTrue(dispatchEventStub.calledOnce);
+      assert.isTrue(dispatchEventStub.calledTwice);
 
       MockInteractions.tap(element.shadowRoot
           .querySelector('.save'));
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 b0f387b..f9ce12e 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
@@ -14,9 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-
-import '../../../scripts/bundled-polymer.js';
 import '../gr-dialog/gr-dialog.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -25,7 +22,7 @@
 import {htmlTemplate} from './gr-confirm-delete-comment-dialog_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrConfirmDeleteCommentDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.js b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.js
index f6caaa1..2d0fa6f 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog_html.js
@@ -17,40 +17,52 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      .main {
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-      }
-      p {
-        margin-bottom: var(--spacing-l);
-      }
-      label {
-        cursor: pointer;
-        display: block;
-        width: 100%;
-      }
-      iron-autogrow-textarea {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        width: 73ch; /* Add a char to account for the border. */
-      }
-    </style>
-    <gr-dialog confirm-label="Delete" on-confirm="_handleConfirmTap" on-cancel="_handleCancelTap">
-      <div class="header" slot="header">Delete Comment</div>
-      <div class="main" slot="main">
-        <p>This is an admin function. Please only use in exceptional circumstances.</p>
-        <label for="messageInput">Enter comment delete reason</label>
-        <iron-autogrow-textarea id="messageInput" class="message" autocomplete="on" placeholder="<Insert reasoning here>" bind-value="{{message}}"></iron-autogrow-textarea>
-      </div>
-    </gr-dialog>
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) {
+      opacity: 0.5;
+      pointer-events: none;
+    }
+    .main {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+    }
+    p {
+      margin-bottom: var(--spacing-l);
+    }
+    label {
+      cursor: pointer;
+      display: block;
+      width: 100%;
+    }
+    iron-autogrow-textarea {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      width: 73ch; /* Add a char to account for the border. */
+    }
+  </style>
+  <gr-dialog
+    confirm-label="Delete"
+    on-confirm="_handleConfirmTap"
+    on-cancel="_handleCancelTap"
+  >
+    <div class="header" slot="header">Delete Comment</div>
+    <div class="main" slot="main">
+      <p>
+        This is an admin function. Please only use in exceptional circumstances.
+      </p>
+      <label for="messageInput">Enter comment delete reason</label>
+      <iron-autogrow-textarea
+        id="messageInput"
+        class="message"
+        autocomplete="on"
+        placeholder="<Insert reasoning here>"
+        bind-value="{{message}}"
+      ></iron-autogrow-textarea>
+    </div>
+  </gr-dialog>
 `;
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 0f6168e..39c149f 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/shared-styles.js';
 import '../gr-button/gr-button.js';
@@ -28,7 +26,7 @@
 
 const COPY_TIMEOUT_MS = 1000;
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrCopyClipboard extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.js b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.js
index 29becbb..8378de5 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_html.js
@@ -17,49 +17,70 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .text {
-        align-items: center;
-        display: flex;
-        flex-wrap: wrap;
-      }
-      .copyText {
-        flex-grow: 1;
-        margin-right: var(--spacing-s);
-      }
-      .hideInput {
-        display: none;
-      }
-      input#input {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        @apply --text-container-style;
-        width: 100%;
-      }
-      /*
+  <style include="shared-styles">
+    .text {
+      align-items: center;
+      display: flex;
+      flex-wrap: wrap;
+    }
+    .copyText {
+      flex-grow: 1;
+      margin-right: var(--spacing-s);
+    }
+    .hideInput {
+      display: none;
+    }
+    input#input {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      @apply --text-container-style;
+      width: 100%;
+    }
+    /*
        * Typically icons are 20px, which is the normal line-height.
        * The copy icon is too prominent at 20px, so we choose 16px
        * here, but add 2x2px padding below, so the entire
        * component should still fit nicely into a normal inline
        * layout flow.
        */
-      #icon {
-        height: 16px;
-        width: 16px;
+    #icon {
+      height: 16px;
+      width: 16px;
+    }
+    gr-button {
+      --gr-button: {
+        padding: 2px;
       }
-      gr-button {
-        --gr-button: {
-          padding: 2px;
-        }
-      }
-    </style>
-    <div class="text">
-      <iron-input class="copyText" type="text" bind-value="[[text]]" on-tap="_handleInputClick" readonly="">
-        <input id="input" is="iron-input" class\$="[[_computeInputClass(hideInput)]]" type="text" bind-value="[[text]]" on-click="_handleInputClick" readonly="">
-      </iron-input>
-      <gr-button id="button" link="" has-tooltip="[[hasTooltip]]" class="copyToClipboard" title="[[buttonTitle]]" on-click="_copyToClipboard">
-        <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
-      </gr-button>
-    </div>
+    }
+  </style>
+  <div class="text">
+    <iron-input
+      class="copyText"
+      type="text"
+      bind-value="[[text]]"
+      on-tap="_handleInputClick"
+      readonly=""
+    >
+      <input
+        id="input"
+        is="iron-input"
+        class$="[[_computeInputClass(hideInput)]]"
+        type="text"
+        bind-value="[[text]]"
+        on-click="_handleInputClick"
+        readonly=""
+      />
+    </iron-input>
+    <gr-button
+      id="button"
+      link=""
+      has-tooltip="[[hasTooltip]]"
+      class="copyToClipboard"
+      title="[[buttonTitle]]"
+      on-click="_copyToClipboard"
+    >
+      <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
+    </gr-button>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html
index 84cb166..398f7f0 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-copy-clipboard</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html
index 7f08603..63435d2 100644
--- a/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-count-string-formatter</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 222109e..2901ea6 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
@@ -25,7 +24,7 @@
   KEEP_VISIBLE: 'keep-visible',
 };
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrCursorManager extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.js
index 29757e5..3ed33d1 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_html.js
@@ -16,6 +16,4 @@
  */
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
-export const htmlTemplate = html`
-
-`;
+export const htmlTemplate = html``;
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
index 1f5b4c0..98a7d24 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-cursor-manager</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6f19587..aefb72f 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
 import '../../../styles/shared-styles.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
@@ -63,7 +61,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDateFormatter extends mixinBehaviors( [
   TooltipBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.js
index 19aa143..2571065 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.js
@@ -17,14 +17,15 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        color: inherit;
-        display: inline;
-      }
-    </style>
-    <span>
-      [[_computeDateStr(dateStr, _timeFormat, _dateFormat, _relative, showDateAndTime)]]
-    </span>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      color: inherit;
+      display: inline;
+    }
+  </style>
+  <span>
+    [[_computeDateStr(dateStr, _timeFormat, _dateFormat, _relative,
+    showDateAndTime)]]
+  </span>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
index 92e4bc8..7169ef27 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-date-formatter</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 db64661..2292ae7a 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-button/gr-button.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -24,7 +22,7 @@
 import {htmlTemplate} from './gr-dialog_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDialog extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.js b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.js
index 4bfd13f..b32f871 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_html.js
@@ -17,63 +17,75 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        color: var(--primary-text-color);
-        display: block;
-        max-height: 90vh;
-        overflow: auto;
-      }
-      .container {
-        display: flex;
-        flex-direction: column;
-        max-height: 90vh;
-        padding: var(--spacing-xl);
-      }
-      header {
-        flex-shrink: 0;
-        padding-bottom: var(--spacing-xl);
-      }
-      main {
-        display: flex;
-        flex-shrink: 1;
-        width: 100%;
-        flex: 1;
-        /* IMPORTANT: required for firefox */
-        min-height: 0px;
-      }
-      main .overflow-container {
-        flex: 1;
-        overflow: auto;
-      }
-      footer {
-        display: flex;
-        flex-shrink: 0;
-        justify-content: flex-end;
-        padding-top: var(--spacing-xl);
-      }
-      gr-button {
-        margin-left: var(--spacing-l);
-      }
-      .hidden {
-        display: none;
-      }
-    </style>
-    <div class="container" on-keydown="_handleKeydown">
-      <header class="font-h3"><slot name="header"></slot></header>
-      <main>
-        <div class="overflow-container">
-          <slot name="main"></slot>
-        </div>
-      </main>
-      <footer>
-        <slot name="footer"></slot>
-        <gr-button id="cancel" class\$="[[_computeCancelClass(cancelLabel)]]" link="" on-click="_handleCancelTap">
-          [[cancelLabel]]
-        </gr-button>
-        <gr-button id="confirm" link="" primary="" on-click="_handleConfirm" disabled="[[disabled]]" title\$="[[confirmTooltip]]">
-          [[confirmLabel]]
-        </gr-button>
-      </footer>
-    </div>
+  <style include="shared-styles">
+    :host {
+      color: var(--primary-text-color);
+      display: block;
+      max-height: 90vh;
+      overflow: auto;
+    }
+    .container {
+      display: flex;
+      flex-direction: column;
+      max-height: 90vh;
+      padding: var(--spacing-xl);
+    }
+    header {
+      flex-shrink: 0;
+      padding-bottom: var(--spacing-xl);
+    }
+    main {
+      display: flex;
+      flex-shrink: 1;
+      width: 100%;
+      flex: 1;
+      /* IMPORTANT: required for firefox */
+      min-height: 0px;
+    }
+    main .overflow-container {
+      flex: 1;
+      overflow: auto;
+    }
+    footer {
+      display: flex;
+      flex-shrink: 0;
+      justify-content: flex-end;
+      padding-top: var(--spacing-xl);
+    }
+    gr-button {
+      margin-left: var(--spacing-l);
+    }
+    .hidden {
+      display: none;
+    }
+  </style>
+  <div class="container" on-keydown="_handleKeydown">
+    <header class="font-h3"><slot name="header"></slot></header>
+    <main>
+      <div class="overflow-container">
+        <slot name="main"></slot>
+      </div>
+    </main>
+    <footer>
+      <slot name="footer"></slot>
+      <gr-button
+        id="cancel"
+        class$="[[_computeCancelClass(cancelLabel)]]"
+        link=""
+        on-click="_handleCancelTap"
+      >
+        [[cancelLabel]]
+      </gr-button>
+      <gr-button
+        id="confirm"
+        link=""
+        primary=""
+        on-click="_handleConfirm"
+        disabled="[[disabled]]"
+        title$="[[confirmTooltip]]"
+      >
+        [[confirmLabel]]
+      </gr-button>
+    </footer>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html
index 919ed88..1060e82 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-dialog</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 00f9078..1d00941 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '../../../styles/shared-styles.js';
 import '../gr-button/gr-button.js';
@@ -26,7 +24,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-diff-preferences_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrDiffPreferences extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.js
index 7869c2c..3ea40d9 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_html.js
@@ -17,98 +17,179 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="gr-form-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <div id="diffPreferences" class="gr-form-styles">
-      <section>
-        <span class="title">Context</span>
+  <style include="shared-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="gr-form-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <div id="diffPreferences" class="gr-form-styles">
+    <section>
+      <span class="title">Context</span>
+      <span class="value">
+        <gr-select id="contextSelect" bind-value="{{diffPrefs.context}}">
+          <select
+            on-keypress="_handleDiffPrefsChanged"
+            on-change="_handleDiffPrefsChanged"
+          >
+            <option value="3">3 lines</option>
+            <option value="10">10 lines</option>
+            <option value="25">25 lines</option>
+            <option value="50">50 lines</option>
+            <option value="75">75 lines</option>
+            <option value="100">100 lines</option>
+            <option value="-1">Whole file</option>
+          </select>
+        </gr-select>
+      </span>
+    </section>
+    <section>
+      <span class="title">Fit to screen</span>
+      <span class="value">
+        <input
+          id="lineWrappingInput"
+          type="checkbox"
+          checked$="[[diffPrefs.line_wrapping]]"
+          on-change="_handleLineWrappingTap"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Diff width</span>
+      <span class="value">
+        <iron-input
+          type="number"
+          prevent-invalid-input=""
+          allowed-pattern="[0-9]"
+          bind-value="{{diffPrefs.line_length}}"
+          on-keypress="_handleDiffPrefsChanged"
+          on-change="_handleDiffPrefsChanged"
+        >
+          <input
+            is="iron-input"
+            type="number"
+            id="columnsInput"
+            prevent-invalid-input=""
+            allowed-pattern="[0-9]"
+            bind-value="{{diffPrefs.line_length}}"
+            on-keypress="_handleDiffPrefsChanged"
+            on-change="_handleDiffPrefsChanged"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Tab width</span>
+      <span class="value">
+        <iron-input
+          type="number"
+          prevent-invalid-input=""
+          allowed-pattern="[0-9]"
+          bind-value="{{diffPrefs.tab_size}}"
+          on-keypress="_handleDiffPrefsChanged"
+          on-change="_handleDiffPrefsChanged"
+        >
+          <input
+            is="iron-input"
+            type="number"
+            id="tabSizeInput"
+            prevent-invalid-input=""
+            allowed-pattern="[0-9]"
+            bind-value="{{diffPrefs.tab_size}}"
+            on-keypress="_handleDiffPrefsChanged"
+            on-change="_handleDiffPrefsChanged"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section hidden$="[[!diffPrefs.font_size]]">
+      <span class="title">Font size</span>
+      <span class="value">
+        <iron-input
+          type="number"
+          prevent-invalid-input=""
+          allowed-pattern="[0-9]"
+          bind-value="{{diffPrefs.font_size}}"
+          on-keypress="_handleDiffPrefsChanged"
+          on-change="_handleDiffPrefsChanged"
+        >
+          <input
+            is="iron-input"
+            type="number"
+            id="fontSizeInput"
+            prevent-invalid-input=""
+            allowed-pattern="[0-9]"
+            bind-value="{{diffPrefs.font_size}}"
+            on-keypress="_handleDiffPrefsChanged"
+            on-change="_handleDiffPrefsChanged"
+          />
+        </iron-input>
+      </span>
+    </section>
+    <section>
+      <span class="title">Show tabs</span>
+      <span class="value">
+        <input
+          id="showTabsInput"
+          type="checkbox"
+          checked$="[[diffPrefs.show_tabs]]"
+          on-change="_handleShowTabsTap"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Show trailing whitespace</span>
+      <span class="value">
+        <input
+          id="showTrailingWhitespaceInput"
+          type="checkbox"
+          checked$="[[diffPrefs.show_whitespace_errors]]"
+          on-change="_handleShowTrailingWhitespaceTap"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Syntax highlighting</span>
+      <span class="value">
+        <input
+          id="syntaxHighlightInput"
+          type="checkbox"
+          checked$="[[diffPrefs.syntax_highlighting]]"
+          on-change="_handleSyntaxHighlightTap"
+        />
+      </span>
+    </section>
+    <section>
+      <span class="title">Automatically mark viewed files reviewed</span>
+      <span class="value">
+        <input
+          id="automaticReviewInput"
+          type="checkbox"
+          checked$="[[!diffPrefs.manual_review]]"
+          on-change="_handleAutomaticReviewTap"
+        />
+      </span>
+    </section>
+    <section>
+      <div class="pref">
+        <span class="title">Ignore Whitespace</span>
         <span class="value">
-          <gr-select id="contextSelect" bind-value="{{diffPrefs.context}}">
-            <select on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-              <option value="3">3 lines</option>
-              <option value="10">10 lines</option>
-              <option value="25">25 lines</option>
-              <option value="50">50 lines</option>
-              <option value="75">75 lines</option>
-              <option value="100">100 lines</option>
-              <option value="-1">Whole file</option>
+          <gr-select bind-value="{{diffPrefs.ignore_whitespace}}">
+            <select
+              on-keypress="_handleDiffPrefsChanged"
+              on-change="_handleDiffPrefsChanged"
+            >
+              <option value="IGNORE_NONE">None</option>
+              <option value="IGNORE_TRAILING">Trailing</option>
+              <option value="IGNORE_LEADING_AND_TRAILING"
+                >Leading &amp; trailing</option
+              >
+              <option value="IGNORE_ALL">All</option>
             </select>
           </gr-select>
         </span>
-      </section>
-      <section>
-        <span class="title">Fit to screen</span>
-        <span class="value">
-          <input id="lineWrappingInput" type="checkbox" checked\$="[[diffPrefs.line_wrapping]]" on-change="_handleLineWrappingTap">
-        </span>
-      </section>
-      <section>
-        <span class="title">Diff width</span>
-        <span class="value">
-          <iron-input type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{diffPrefs.line_length}}" on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-            <input is="iron-input" type="number" id="columnsInput" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{diffPrefs.line_length}}" on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Tab width</span>
-        <span class="value">
-          <iron-input type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{diffPrefs.tab_size}}" on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-            <input is="iron-input" type="number" id="tabSizeInput" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{diffPrefs.tab_size}}" on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-          </iron-input>
-        </span>
-      </section>
-      <section hidden\$="[[!diffPrefs.font_size]]">
-        <span class="title">Font size</span>
-        <span class="value">
-          <iron-input type="number" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{diffPrefs.font_size}}" on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-            <input is="iron-input" type="number" id="fontSizeInput" prevent-invalid-input="" allowed-pattern="[0-9]" bind-value="{{diffPrefs.font_size}}" on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-          </iron-input>
-        </span>
-      </section>
-      <section>
-        <span class="title">Show tabs</span>
-        <span class="value">
-          <input id="showTabsInput" type="checkbox" checked\$="[[diffPrefs.show_tabs]]" on-change="_handleShowTabsTap">
-        </span>
-      </section>
-      <section>
-        <span class="title">Show trailing whitespace</span>
-        <span class="value">
-          <input id="showTrailingWhitespaceInput" type="checkbox" checked\$="[[diffPrefs.show_whitespace_errors]]" on-change="_handleShowTrailingWhitespaceTap">
-        </span>
-      </section>
-      <section>
-        <span class="title">Syntax highlighting</span>
-        <span class="value">
-          <input id="syntaxHighlightInput" type="checkbox" checked\$="[[diffPrefs.syntax_highlighting]]" on-change="_handleSyntaxHighlightTap">
-        </span>
-      </section>
-      <section>
-        <span class="title">Automatically mark viewed files reviewed</span>
-        <span class="value">
-          <input id="automaticReviewInput" type="checkbox" checked\$="[[!diffPrefs.manual_review]]" on-change="_handleAutomaticReviewTap">
-        </span>
-      </section>
-      <section>
-        <div class="pref">
-          <span class="title">Ignore Whitespace</span>
-          <span class="value">
-            <gr-select bind-value="{{diffPrefs.ignore_whitespace}}">
-              <select on-keypress="_handleDiffPrefsChanged" on-change="_handleDiffPrefsChanged">
-                <option value="IGNORE_NONE">None</option>
-                <option value="IGNORE_TRAILING">Trailing</option>
-                <option value="IGNORE_LEADING_AND_TRAILING">Leading &amp; trailing</option>
-                <option value="IGNORE_ALL">All</option>
-              </select>
-            </gr-select>
-          </span>
-        </div>
-      </section>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+      </div>
+    </section>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
index 5607a3d..2750d67 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-diff-preferences</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 fcc09c4..7b92e3d 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/paper-tabs/paper-tabs.js';
 import '../gr-shell-command/gr-shell-command.js';
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
@@ -28,7 +26,7 @@
 import {RESTClientBehavior} from '../../../behaviors/rest-client-behavior/rest-client-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDownloadCommands extends mixinBehaviors( [
   RESTClientBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.js
index 12a8d01..7248e65 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.js
@@ -17,51 +17,59 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      paper-tabs {
-        height: 3rem;
-        margin-bottom: var(--spacing-m);
-        --paper-tabs-selection-bar-color: var(--link-color);
-      }
-      paper-tab {
-        max-width: 15rem;
-        text-transform: uppercase;
-        --paper-tab-ink: var(--link-color);
-      }
-      label,
-      input {
-        display: block;
-      }
-      label {
-        font-weight: var(--font-weight-bold);
-      }
-      .schemes {
-        display: flex;
-        justify-content: space-between;
-      }
-      .commands {
-        display: flex;
-        flex-direction: column;
-      }
-      gr-shell-command {
-        width: 60em;
-        margin-bottom: var(--spacing-m);
-      }
-      .hidden {
-        display: none;
-      }
-    </style>
-    <div class="schemes">
-      <paper-tabs id="downloadTabs" class\$="[[_computeShowTabs(schemes)]]" selected="[[_computeSelected(schemes, selectedScheme)]]" on-selected-changed="_handleTabChange">
-        <template is="dom-repeat" items="[[schemes]]" as="scheme">
-          <paper-tab data-scheme\$="[[scheme]]">[[scheme]]</paper-tab>
-        </template>
-      </paper-tabs>
-    </div>
-    <div class="commands" hidden\$="[[!schemes.length]]" hidden="">
-      <template is="dom-repeat" items="[[commands]]" as="command">
-        <gr-shell-command label="[[command.title]]" command="[[command.command]]"></gr-shell-command>
+  <style include="shared-styles">
+    paper-tabs {
+      height: 3rem;
+      margin-bottom: var(--spacing-m);
+      --paper-tabs-selection-bar-color: var(--link-color);
+    }
+    paper-tab {
+      max-width: 15rem;
+      text-transform: uppercase;
+      --paper-tab-ink: var(--link-color);
+    }
+    label,
+    input {
+      display: block;
+    }
+    label {
+      font-weight: var(--font-weight-bold);
+    }
+    .schemes {
+      display: flex;
+      justify-content: space-between;
+    }
+    .commands {
+      display: flex;
+      flex-direction: column;
+    }
+    gr-shell-command {
+      width: 60em;
+      margin-bottom: var(--spacing-m);
+    }
+    .hidden {
+      display: none;
+    }
+  </style>
+  <div class="schemes">
+    <paper-tabs
+      id="downloadTabs"
+      class$="[[_computeShowTabs(schemes)]]"
+      selected="[[_computeSelected(schemes, selectedScheme)]]"
+      on-selected-changed="_handleTabChange"
+    >
+      <template is="dom-repeat" items="[[schemes]]" as="scheme">
+        <paper-tab data-scheme$="[[scheme]]">[[scheme]]</paper-tab>
       </template>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    </paper-tabs>
+  </div>
+  <div class="commands" hidden$="[[!schemes.length]]" hidden="">
+    <template is="dom-repeat" items="[[commands]]" as="command">
+      <gr-shell-command
+        label="[[command.title]]"
+        command="[[command.command]]"
+      ></gr-shell-command>
+    </template>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html
index 2543e28..237fbe0 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-download-commands</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6b250de..f84ef4a 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-dropdown/iron-dropdown.js';
 import '@polymer/paper-item/paper-item.js';
 import '@polymer/paper-listbox/paper-listbox.js';
@@ -56,7 +54,7 @@
  */
 Defs.item;
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrDropdownList extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.js b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.js
index 3b454c2..0f80af2 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.js
@@ -17,135 +17,158 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: inline-block;
-      }
-      #triggerText {
-        -moz-user-select: text;
-        -ms-user-select: text;
-        -webkit-user-select: text;
-        user-select: text;
-      }
-      .dropdown-trigger {
-        cursor: pointer;
+  <style include="shared-styles">
+    :host {
+      display: inline-block;
+    }
+    #triggerText {
+      -moz-user-select: text;
+      -ms-user-select: text;
+      -webkit-user-select: text;
+      user-select: text;
+    }
+    .dropdown-trigger {
+      cursor: pointer;
+      padding: 0;
+    }
+    .dropdown-content {
+      background-color: var(--dropdown-background-color);
+      box-shadow: var(--elevation-level-2);
+      max-height: 70vh;
+      margin-top: var(--spacing-xxl);
+      min-width: 266px;
+      @apply --dropdown-content-style;
+    }
+    paper-listbox {
+      --paper-listbox: {
         padding: 0;
       }
-      .dropdown-content {
-        background-color: var(--dropdown-background-color);
-        box-shadow: var(--elevation-level-2);
-        max-height: 70vh;
-        margin-top: var(--spacing-xxl);
-        min-width: 266px;
-        @apply --dropdown-content-style;
-      }
-      paper-listbox {
-        --paper-listbox: {
-          padding: 0;
-        }
-      }
-      paper-item {
-        cursor: pointer;
-        flex-direction: column;
-        font-size: inherit;
-        /* This variable was introduced in Dec 2019. We keep both min-height
+    }
+    paper-item {
+      cursor: pointer;
+      flex-direction: column;
+      font-size: inherit;
+      /* This variable was introduced in Dec 2019. We keep both min-height
          * rules around, because --paper-item-min-height is not yet upstreamed.
          */
-        --paper-item-min-height: 0;
-        --paper-item: {
-          min-height: 0;
-          padding: 10px 16px;
-        };
-        --paper-item-focused-before: {
-          background-color: var(--selection-background-color);
-        };
-        --paper-item-focused: {
-          background-color: var(--selection-background-color);
-        };
+      --paper-item-min-height: 0;
+      --paper-item: {
+        min-height: 0;
+        padding: 10px 16px;
       }
-      paper-item:hover {
-        background-color: var(--hover-background-color);
+      --paper-item-focused-before: {
+        background-color: var(--selection-background-color);
       }
-      paper-item:not(:last-of-type) {
-        border-bottom: 1px solid var(--border-color);
+      --paper-item-focused: {
+        background-color: var(--selection-background-color);
       }
-      .bottomContent {
-        color: var(--deemphasized-text-color);
+    }
+    paper-item:hover {
+      background-color: var(--hover-background-color);
+    }
+    paper-item:not(:last-of-type) {
+      border-bottom: 1px solid var(--border-color);
+    }
+    .bottomContent {
+      color: var(--deemphasized-text-color);
+    }
+    .bottomContent,
+    .topContent {
+      display: flex;
+      justify-content: space-between;
+      flex-direction: row;
+      width: 100%;
+    }
+    gr-button {
+      --gr-button: {
+        @apply --trigger-style;
       }
-      .bottomContent,
-      .topContent {
-        display: flex;
-        justify-content: space-between;
-        flex-direction: row;
-        width: 100%;
-      }
-       gr-button {
-        --gr-button: {
-          @apply --trigger-style;
-        }
-      }
-      gr-date-formatter {
-        color: var(--deemphasized-text-color);
-        margin-left: var(--spacing-xxl);
-        white-space: nowrap;
-      }
-      gr-select {
-        display: none;
-      }
-      /* Because the iron dropdown 'area' includes the trigger, and the entire
+    }
+    gr-date-formatter {
+      color: var(--deemphasized-text-color);
+      margin-left: var(--spacing-xxl);
+      white-space: nowrap;
+    }
+    gr-select {
+      display: none;
+    }
+    /* Because the iron dropdown 'area' includes the trigger, and the entire
        width of the dropdown, we want to treat tapping the area above the
        dropdown content as if it is tapping whatever content is underneath it.
        The next two styles allow this to happen. */
+    iron-dropdown {
+      max-width: none;
+      pointer-events: none;
+    }
+    paper-listbox {
+      pointer-events: auto;
+    }
+    @media only screen and (max-width: 50em) {
+      gr-select {
+        display: inline;
+        @apply --gr-select-style;
+      }
+      gr-button,
       iron-dropdown {
-        max-width: none;
-        pointer-events: none;
+        display: none;
       }
-      paper-listbox {
-        pointer-events: auto;
+      select {
+        @apply --native-select-style;
       }
-      @media only screen and (max-width: 50em) {
-        gr-select {
-          display: inline;
-          @apply --gr-select-style;
-        }
-        gr-button,
-        iron-dropdown {
-          display: none;
-        }
-        select {
-          @apply --native-select-style;
-        }
-      }
-    </style>
-    <gr-button disabled="[[disabled]]" down-arrow="" link="" id="trigger" class="dropdown-trigger" on-click="_showDropdownTapHandler" slot="dropdown-trigger">
-      <span id="triggerText">[[text]]</span>
-    </gr-button>
-    <iron-dropdown id="dropdown" vertical-align="top" allow-outside-scroll="true" on-click="_handleDropdownClick">
-      <paper-listbox class="dropdown-content" slot="dropdown-content" attr-for-selected="data-value" selected="{{value}}" on-tap="_handleDropdownTap">
-        <template is="dom-repeat" items="[[items]]" initial-count="[[initialCount]]">
-          <paper-item disabled="[[item.disabled]]" data-value\$="[[item.value]]">
-            <div class="topContent">
-              <div>[[item.text]]</div>
-              <template is="dom-if" if="[[item.date]]">
-                  <gr-date-formatter date-str="[[item.date]]"></gr-date-formatter>
-              </template>
-            </div>
-            <template is="dom-if" if="[[item.bottomText]]">
-              <div class="bottomContent">
-                <div>[[item.bottomText]]</div>
-              </div>
+    }
+  </style>
+  <gr-button
+    disabled="[[disabled]]"
+    down-arrow=""
+    link=""
+    id="trigger"
+    class="dropdown-trigger"
+    on-click="_showDropdownTapHandler"
+    slot="dropdown-trigger"
+  >
+    <span id="triggerText">[[text]]</span>
+  </gr-button>
+  <iron-dropdown
+    id="dropdown"
+    vertical-align="top"
+    allow-outside-scroll="true"
+    on-click="_handleDropdownClick"
+  >
+    <paper-listbox
+      class="dropdown-content"
+      slot="dropdown-content"
+      attr-for-selected="data-value"
+      selected="{{value}}"
+      on-tap="_handleDropdownTap"
+    >
+      <template
+        is="dom-repeat"
+        items="[[items]]"
+        initial-count="[[initialCount]]"
+      >
+        <paper-item disabled="[[item.disabled]]" data-value$="[[item.value]]">
+          <div class="topContent">
+            <div>[[item.text]]</div>
+            <template is="dom-if" if="[[item.date]]">
+              <gr-date-formatter date-str="[[item.date]]"></gr-date-formatter>
             </template>
-          </paper-item>
-        </template>
-      </paper-listbox>
-    </iron-dropdown>
-    <gr-select bind-value="{{value}}">
-      <select>
-        <template is="dom-repeat" items="[[items]]">
-          <option disabled\$="[[item.disabled]]" value="[[item.value]]">
-            [[_computeMobileText(item)]]
-          </option>
-        </template>
-      </select>
-    </gr-select>
+          </div>
+          <template is="dom-if" if="[[item.bottomText]]">
+            <div class="bottomContent">
+              <div>[[item.bottomText]]</div>
+            </div>
+          </template>
+        </paper-item>
+      </template>
+    </paper-listbox>
+  </iron-dropdown>
+  <gr-select bind-value="{{value}}">
+    <select>
+      <template is="dom-repeat" items="[[items]]">
+        <option disabled$="[[item.disabled]]" value="[[item.value]]">
+          [[_computeMobileText(item)]]
+        </option>
+      </template>
+    </select>
+  </gr-select>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
index 7d39f9e..b64d5f7 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-dropdown-list</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 12a2025..b5c6016 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import '../../../scripts/bundled-polymer.js';
 import '@polymer/iron-dropdown/iron-dropdown.js';
 import '../gr-button/gr-button.js';
 import '../gr-cursor-manager/gr-cursor-manager.js';
@@ -35,7 +33,7 @@
 const REL_EXTERNAL = 'external';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrDropdown extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.js
index 99028af..d0b0d09 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.js
@@ -17,102 +17,153 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: inline-block;
-      }
-      .dropdown-trigger {
-        text-decoration: none;
-        width: 100%;
-      }
-      .dropdown-content {
-        background-color: var(--dropdown-background-color);
-        box-shadow: var(--elevation-level-2);
-      }
-      gr-button {
-        @apply --gr-button;
-      }
-      gr-avatar {
-        height: 2em;
-        width: 2em;
-        vertical-align: middle;
-      }
-      gr-button[link]:focus {
-        outline: 5px auto -webkit-focus-ring-color;
-      }
-      ul {
-        list-style: none;
-      }
-      .topContent,
-      li {
-        border-bottom: 1px solid var(--border-color);
-      }
-      li:last-of-type {
-        border: none;
-      }
-      li .itemAction {
-        cursor: pointer;
-        display: block;
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      li .itemAction {
-        @apply --gr-dropdown-item;
-      }
-      li .itemAction.disabled {
-        color: var(--deemphasized-text-color);
-        cursor: default;
-      }
-      li .itemAction:link,
-      li .itemAction:visited {
-        text-decoration: none;
-      }
-      li .itemAction:not(.disabled):hover {
-        background-color: var(--hover-background-color);
-      }
-      li:focus,
-      li.selected {
-        background-color: var(--selection-background-color);
-        outline: none;
-      }
-      li:focus .itemAction,
-      li.selected .itemAction {
-        background-color: transparent;
-      }
-      .topContent {
-        display: block;
-        padding: var(--spacing-m) var(--spacing-l);
-        @apply --gr-dropdown-item;
-      }
-      .bold-text {
-        font-weight: var(--font-weight-bold);
-      }
-    </style>
-    <gr-button link="[[link]]" class="dropdown-trigger" id="trigger" down-arrow="[[downArrow]]" on-click="_dropdownTriggerTapHandler">
-      <slot></slot>
-    </gr-button>
-    <iron-dropdown id="dropdown" vertical-align="top" vertical-offset="[[verticalOffset]]" allow-outside-scroll="true" horizontal-align="[[horizontalAlign]]" on-click="_handleDropdownClick">
-      <div class="dropdown-content" slot="dropdown-content">
-        <ul>
-          <template is="dom-if" if="[[topContent]]">
-            <div class="topContent">
-              <template is="dom-repeat" items="[[topContent]]" as="item" initial-count="75">
-                <div class\$="[[_getClassIfBold(item.bold)]] top-item" tabindex="-1">
-                  [[item.text]]
-                </div>
-              </template>
-            </div>
-          </template>
-          <template is="dom-repeat" items="[[items]]" as="link" initial-count="75">
-            <li tabindex="-1">
-              <gr-tooltip-content has-tooltip="[[_computeHasTooltip(link.tooltip)]]" title\$="[[link.tooltip]]">
-                <span class\$="itemAction [[_computeDisabledClass(link.id, disabledIds.*)]]" data-id\$="[[link.id]]" on-click="_handleItemTap" hidden\$="[[link.url]]" tabindex="-1">[[link.name]]</span>
-                <a class="itemAction" href\$="[[_computeLinkURL(link)]]" download\$="[[_computeIsDownload(link)]]" rel\$="[[_computeLinkRel(link)]]" target\$="[[link.target]]" hidden\$="[[!link.url]]" tabindex="-1">[[link.name]]</a>
-              </gr-tooltip-content>
-            </li>
-          </template>
-        </ul>
-      </div>
-    </iron-dropdown>
-    <gr-cursor-manager id="cursor" cursor-target-class="selected" scroll-behavior="never" focus-on-move="" stops="[[_listElements]]"></gr-cursor-manager>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      display: inline-block;
+    }
+    .dropdown-trigger {
+      text-decoration: none;
+      width: 100%;
+    }
+    .dropdown-content {
+      background-color: var(--dropdown-background-color);
+      box-shadow: var(--elevation-level-2);
+    }
+    gr-button {
+      @apply --gr-button;
+    }
+    gr-avatar {
+      height: 2em;
+      width: 2em;
+      vertical-align: middle;
+    }
+    gr-button[link]:focus {
+      outline: 5px auto -webkit-focus-ring-color;
+    }
+    ul {
+      list-style: none;
+    }
+    .topContent,
+    li {
+      border-bottom: 1px solid var(--border-color);
+    }
+    li:last-of-type {
+      border: none;
+    }
+    li .itemAction {
+      cursor: pointer;
+      display: block;
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    li .itemAction {
+      @apply --gr-dropdown-item;
+    }
+    li .itemAction.disabled {
+      color: var(--deemphasized-text-color);
+      cursor: default;
+    }
+    li .itemAction:link,
+    li .itemAction:visited {
+      text-decoration: none;
+    }
+    li .itemAction:not(.disabled):hover {
+      background-color: var(--hover-background-color);
+    }
+    li:focus,
+    li.selected {
+      background-color: var(--selection-background-color);
+      outline: none;
+    }
+    li:focus .itemAction,
+    li.selected .itemAction {
+      background-color: transparent;
+    }
+    .topContent {
+      display: block;
+      padding: var(--spacing-m) var(--spacing-l);
+      @apply --gr-dropdown-item;
+    }
+    .bold-text {
+      font-weight: var(--font-weight-bold);
+    }
+  </style>
+  <gr-button
+    link="[[link]]"
+    class="dropdown-trigger"
+    id="trigger"
+    down-arrow="[[downArrow]]"
+    on-click="_dropdownTriggerTapHandler"
+  >
+    <slot></slot>
+  </gr-button>
+  <iron-dropdown
+    id="dropdown"
+    vertical-align="top"
+    vertical-offset="[[verticalOffset]]"
+    allow-outside-scroll="true"
+    horizontal-align="[[horizontalAlign]]"
+    on-click="_handleDropdownClick"
+  >
+    <div class="dropdown-content" slot="dropdown-content">
+      <ul>
+        <template is="dom-if" if="[[topContent]]">
+          <div class="topContent">
+            <template
+              is="dom-repeat"
+              items="[[topContent]]"
+              as="item"
+              initial-count="75"
+            >
+              <div
+                class$="[[_getClassIfBold(item.bold)]] top-item"
+                tabindex="-1"
+              >
+                [[item.text]]
+              </div>
+            </template>
+          </div>
+        </template>
+        <template
+          is="dom-repeat"
+          items="[[items]]"
+          as="link"
+          initial-count="75"
+        >
+          <li tabindex="-1">
+            <gr-tooltip-content
+              has-tooltip="[[_computeHasTooltip(link.tooltip)]]"
+              title$="[[link.tooltip]]"
+            >
+              <span
+                class$="itemAction [[_computeDisabledClass(link.id, disabledIds.*)]]"
+                data-id$="[[link.id]]"
+                on-click="_handleItemTap"
+                hidden$="[[link.url]]"
+                tabindex="-1"
+                >[[link.name]]</span
+              >
+              <a
+                class="itemAction"
+                href$="[[_computeLinkURL(link)]]"
+                download$="[[_computeIsDownload(link)]]"
+                rel$="[[_computeLinkRel(link)]]"
+                target$="[[link.target]]"
+                hidden$="[[!link.url]]"
+                tabindex="-1"
+                >[[link.name]]</a
+              >
+            </gr-tooltip-content>
+          </li>
+        </template>
+      </ul>
+    </div>
+  </iron-dropdown>
+  <gr-cursor-manager
+    id="cursor"
+    cursor-target-class="selected"
+    scroll-behavior="never"
+    focus-on-move=""
+    stops="[[_listElements]]"
+  ></gr-cursor-manager>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
index a05634bce..d17cc1a 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-dropdown</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 804eb16..b2bcda9 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/shared-styles.js';
 import '../gr-storage/gr-storage.js';
@@ -29,7 +27,7 @@
 const STORAGE_DEBOUNCE_INTERVAL_MS = 400;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrEditableContent extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.js b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.js
index e0e5047..24eb0b0 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_html.js
@@ -17,51 +17,59 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) iron-autogrow-textarea {
-        opacity: .5;
-      }
-      .viewer {
-        background-color: var(--view-background-color);
-        border: 1px solid var(--view-background-color);
-        border-radius: var(--border-radius);
-        box-shadow: var(--elevation-level-1);
-        padding: var(--spacing-m);
-      }
-      :host([collapsed]) .viewer {
-        max-height: 36em;
-        overflow: hidden;
-      }
-      .editor iron-autogrow-textarea {
-        background-color: var(--view-background-color);
-        width: 100%;
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([disabled]) iron-autogrow-textarea {
+      opacity: 0.5;
+    }
+    .viewer {
+      background-color: var(--view-background-color);
+      border: 1px solid var(--view-background-color);
+      border-radius: var(--border-radius);
+      box-shadow: var(--elevation-level-1);
+      padding: var(--spacing-m);
+    }
+    :host([collapsed]) .viewer {
+      max-height: 36em;
+      overflow: hidden;
+    }
+    .editor iron-autogrow-textarea {
+      background-color: var(--view-background-color);
+      width: 100%;
 
-        /* You have to also repeat everything from shared-styles here, because
+      /* You have to also repeat everything from shared-styles here, because
            you can only *replace* --iron-autogrow-textarea vars as a whole. */
-        --iron-autogrow-textarea: {
-          box-sizing: border-box;
-          padding: var(--spacing-m);
-          overflow-y: hidden;
-          white-space: pre;
-        };
+      --iron-autogrow-textarea: {
+        box-sizing: border-box;
+        padding: var(--spacing-m);
+        overflow-y: hidden;
+        white-space: pre;
       }
-      .editButtons {
-        display: flex;
-        justify-content: space-between;
-      }
-    </style>
-    <div class="viewer" hidden\$="[[editing]]">
-      <slot></slot>
+    }
+    .editButtons {
+      display: flex;
+      justify-content: space-between;
+    }
+  </style>
+  <div class="viewer" hidden$="[[editing]]">
+    <slot></slot>
+  </div>
+  <div class="editor" hidden$="[[!editing]]">
+    <iron-autogrow-textarea
+      autocomplete="on"
+      bind-value="{{_newContent}}"
+      disabled="[[disabled]]"
+    ></iron-autogrow-textarea>
+    <div class="editButtons">
+      <gr-button primary="" on-click="_handleSave" disabled="[[_saveDisabled]]"
+        >Save</gr-button
+      >
+      <gr-button on-click="_handleCancel" disabled="[[disabled]]"
+        >Cancel</gr-button
+      >
     </div>
-    <div class="editor" hidden\$="[[!editing]]">
-      <iron-autogrow-textarea autocomplete="on" bind-value="{{_newContent}}" disabled="[[disabled]]"></iron-autogrow-textarea>
-      <div class="editButtons">
-        <gr-button primary="" on-click="_handleSave" disabled="[[_saveDisabled]]">Save</gr-button>
-        <gr-button on-click="_handleCancel" disabled="[[disabled]]">Cancel</gr-button>
-      </div>
-    </div>
-    <gr-storage id="storage"></gr-storage>
+  </div>
+  <gr-storage id="storage"></gr-storage>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
index 8b9f39d..c50920e 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-editable-content</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 8669f03..6c3c72f 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {IronOverlayBehaviorImpl} from '@polymer/iron-overlay-behavior/iron-overlay-behavior.js';
 import '@polymer/iron-dropdown/iron-dropdown.js';
 import '@polymer/paper-input/paper-input.js';
@@ -33,7 +31,7 @@
 const AWAIT_STEP = 5;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrEditableLabel extends mixinBehaviors( [
   KeyboardShortcutBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.js b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.js
index 9bc31a3..a226e30 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_html.js
@@ -17,68 +17,87 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        align-items: center;
-        display: inline-flex;
+  <style include="shared-styles">
+    :host {
+      align-items: center;
+      display: inline-flex;
+    }
+    :host([uppercase]) label {
+      text-transform: uppercase;
+    }
+    input,
+    label {
+      width: 100%;
+    }
+    label {
+      color: var(--deemphasized-text-color);
+      display: inline-block;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      @apply --label-style;
+    }
+    label.editable {
+      color: var(--link-color);
+      cursor: pointer;
+    }
+    #dropdown {
+      box-shadow: var(--elevation-level-2);
+    }
+    .inputContainer {
+      background-color: var(--dialog-background-color);
+      padding: var(--spacing-m);
+      @apply --input-style;
+    }
+    .buttons {
+      display: flex;
+      justify-content: flex-end;
+      padding-top: var(--spacing-l);
+      width: 100%;
+    }
+    .buttons gr-button {
+      margin-left: var(--spacing-m);
+    }
+    paper-input {
+      --paper-input-container: {
+        padding: 0;
+        min-width: 15em;
       }
-      :host([uppercase]) label {
-        text-transform: uppercase;
+      --paper-input-container-input: {
+        font-size: inherit;
       }
-      input,
-      label {
-        width: 100%;
-      }
-      label {
-        color: var(--deemphasized-text-color);
-        display: inline-block;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        @apply --label-style;
-      }
-      label.editable {
-        color: var(--link-color);
-        cursor: pointer;
-      }
-      #dropdown {
-        box-shadow: var(--elevation-level-2);
-      }
-      .inputContainer {
-        background-color: var(--dialog-background-color);
-        padding: var(--spacing-m);
-        @apply --input-style;
-      }
-      .buttons {
-        display: flex;
-        justify-content: flex-end;
-        padding-top: var(--spacing-l);
-        width: 100%;
-      }
-      .buttons gr-button {
-        margin-left: var(--spacing-m);
-      }
-      paper-input {
-        --paper-input-container: {
-          padding: 0;
-          min-width: 15em;
-        }
-        --paper-input-container-input: {
-          font-size: inherit;
-        }
-        --paper-input-container-focus-color: var(--link-color);
-      }
-    </style>
-      <label class\$="[[_computeLabelClass(readOnly, value, placeholder)]]" title\$="[[_computeLabel(value, placeholder)]]" on-click="_showDropdown">[[_computeLabel(value, placeholder)]]</label>
-      <iron-dropdown id="dropdown" vertical-align="auto" horizontal-align="auto" vertical-offset="[[_verticalOffset]]" allow-outside-scroll="true" on-iron-overlay-canceled="_cancel">
-        <div class="dropdown-content" slot="dropdown-content">
-          <div class="inputContainer">
-            <paper-input id="input" label="[[labelText]]" maxlength="[[maxLength]]" value="{{_inputText}}"></paper-input>
-            <div class="buttons">
-              <gr-button link="" id="cancelBtn" on-click="_cancel">cancel</gr-button>
-              <gr-button link="" id="saveBtn" on-click="_save">save</gr-button>
-            </div>
-          </div>
+      --paper-input-container-focus-color: var(--link-color);
+    }
+  </style>
+  <label
+    class$="[[_computeLabelClass(readOnly, value, placeholder)]]"
+    title$="[[_computeLabel(value, placeholder)]]"
+    on-click="_showDropdown"
+    >[[_computeLabel(value, placeholder)]]</label
+  >
+  <iron-dropdown
+    id="dropdown"
+    vertical-align="auto"
+    horizontal-align="auto"
+    vertical-offset="[[_verticalOffset]]"
+    allow-outside-scroll="true"
+    on-iron-overlay-canceled="_cancel"
+  >
+    <div class="dropdown-content" slot="dropdown-content">
+      <div class="inputContainer">
+        <paper-input
+          id="input"
+          label="[[labelText]]"
+          maxlength="[[maxLength]]"
+          value="{{_inputText}}"
+        ></paper-input>
+        <div class="buttons">
+          <gr-button link="" id="cancelBtn" on-click="_cancel"
+            >cancel</gr-button
+          >
+          <gr-button link="" id="saveBtn" on-click="_save">save</gr-button>
         </div>
-    </iron-dropdown>
+      </div>
+    </div>
+  </iron-dropdown>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
index ef392df..5673194 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-editable-label</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html b/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html
index d2245b1..74936ad 100644
--- a/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-event-interface/gr-event-interface_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-api-interface</title>
 
 <script src="../../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
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 0d19f00..bc79737 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
@@ -14,15 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-fixed-panel_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrFixedPanel extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.js
index 69ae735..61e8b24 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_html.js
@@ -17,31 +17,34 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        box-sizing: border-box;
-        display: block;
-        min-height: var(--header-height);
-        position: relative;
-      }
-      header {
-        background: inherit;
-        border: inherit;
-        display: inline;
-        height: inherit;
-      }
-      .floating {
-        left: 0;
-        position: fixed;
-        width: 100%;
-        will-change: top;
-      }
-      .fixedAtTop {
-        border-bottom: 1px solid #a4a4a4;
-        box-shadow: var(--elevation-level-2);
-      }
-    </style>
-    <header id="header" class\$="[[_computeHeaderClass(_headerFloating, _topLast)]]">
-      <slot></slot>
-    </header>
+  <style include="shared-styles">
+    :host {
+      box-sizing: border-box;
+      display: block;
+      min-height: var(--header-height);
+      position: relative;
+    }
+    header {
+      background: inherit;
+      border: inherit;
+      display: inline;
+      height: inherit;
+    }
+    .floating {
+      left: 0;
+      position: fixed;
+      width: 100%;
+      will-change: top;
+    }
+    .fixedAtTop {
+      border-bottom: 1px solid #a4a4a4;
+      box-shadow: var(--elevation-level-2);
+    }
+  </style>
+  <header
+    id="header"
+    class$="[[_computeHeaderClass(_headerFloating, _topLast)]]"
+  >
+    <slot></slot>
+  </header>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
index cc2fb70..ef31382 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-fixed-panel</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 139e09c..b0eb93e 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-linked-text/gr-linked-text.js';
 import '../../../styles/shared-styles.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
@@ -28,7 +26,7 @@
 const QUOTE_MARKER_PATTERN = /\n\s?>\s/g;
 const CODE_MARKER_PATTERN = /^(`{1,3})([^`]+?)\1$/;
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrFormattedText extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.js
index 2b50565..5cb8670 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_html.js
@@ -17,50 +17,49 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        font-family: var(--font-family);
-      }
-      p,
-      ul,
-      code,
-      blockquote,
-      gr-linked-text.pre {
-        margin: 0 0 var(--spacing-m) 0;
-      }
-      p,
-      ul,
-      code,
-      blockquote {
-        max-width: var(--gr-formatted-text-prose-max-width, none);
-      }
-      :host(.noTrailingMargin) p:last-child,
-      :host(.noTrailingMargin) ul:last-child,
-      :host(.noTrailingMargin) blockquote:last-child,
-      :host(.noTrailingMargin) gr-linked-text.pre:last-child {
-        margin: 0;
-      }
-      code,
-      blockquote {
-        border-left: 1px solid #aaa;
-        padding: 0 var(--spacing-m);
-      }
-      code {
-        display: block;
-        white-space: pre-wrap;
-        color: var(--deemphasized-text-color);
-      }
-      li {
-        list-style-type: disc;
-        margin-left: var(--spacing-xl);
-      }
-      gr-linked-text.pre {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-code);
-        line-height: var(--line-height-code);
-      }
-
-    </style>
-    <div id="container"></div>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      font-family: var(--font-family);
+    }
+    p,
+    ul,
+    code,
+    blockquote,
+    gr-linked-text.pre {
+      margin: 0 0 var(--spacing-m) 0;
+    }
+    p,
+    ul,
+    code,
+    blockquote {
+      max-width: var(--gr-formatted-text-prose-max-width, none);
+    }
+    :host(.noTrailingMargin) p:last-child,
+    :host(.noTrailingMargin) ul:last-child,
+    :host(.noTrailingMargin) blockquote:last-child,
+    :host(.noTrailingMargin) gr-linked-text.pre:last-child {
+      margin: 0;
+    }
+    code,
+    blockquote {
+      border-left: 1px solid #aaa;
+      padding: 0 var(--spacing-m);
+    }
+    code {
+      display: block;
+      white-space: pre-wrap;
+      color: var(--deemphasized-text-color);
+    }
+    li {
+      list-style-type: disc;
+      margin-left: var(--spacing-xl);
+    }
+    gr-linked-text.pre {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-code);
+      line-height: var(--line-height-code);
+    }
+  </style>
+  <div id="container"></div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
index c48ec8a..083eac4 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-editable-label</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
index 0bc9cb7..49aff7e 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.js
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '@polymer/iron-icon/iron-icon.js';
 import '../../../styles/shared-styles.js';
@@ -26,7 +25,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-hovercard-account_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrHovercardAccount extends GestureEventListeners(
     hovercardBehaviorMixin(LegacyElementMixin(
         PolymerElement))) {
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js
index 0763420..8d14ff4 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.js
@@ -18,79 +18,79 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-hovercard-shared-style">
-      .top,
-      .attention,
-      .status,
-      .voteable {
-        padding: var(--spacing-s) var(--spacing-l);
+  <style include="gr-hovercard-shared-style">
+    .top,
+    .attention,
+    .status,
+    .voteable {
+      padding: var(--spacing-s) var(--spacing-l);
+    }
+    .top {
+      display: flex;
+      padding-top: var(--spacing-xl);
+      min-width: 300px;
+    }
+    gr-avatar {
+      height: 48px;
+      width: 48px;
+      margin-right: var(--spacing-l);
+    }
+    .title,
+    .email {
+      color: var(--deemphasized-text-color);
+    }
+    .status iron-icon {
+      width: 14px;
+      height: 14px;
+      vertical-align: top;
+      position: relative;
+      top: 2px;
+    }
+    .action {
+      border-top: 1px solid var(--border-color);
+      padding: var(--spacing-s) var(--spacing-l);
+      --gr-button: {
+        padding: var(--spacing-s) 0;
       }
-      .top {
-        display: flex;
-        padding-top: var(--spacing-xl);
-        min-width: 300px;
-      }
-      gr-avatar {
-        height: 48px;
-        width: 48px;
-        margin-right: var(--spacing-l);
-      }
-      .title,
-      .email {
-        color: var(--deemphasized-text-color);
-      }
-      .status iron-icon {
-        width: 14px;
-        height: 14px;
-        vertical-align: top;
-        position: relative;
-        top: 2px;
-      }
-      .action {
-        border-top: 1px solid var(--border-color);
-        padding: var(--spacing-s) var(--spacing-l);
-        --gr-button: {
-          padding: var(--spacing-s) 0;
-        };
-      }
-      :host(:not([attention])) .attention {
-        display: none;
-      }
-      .attention {
-        background-color: var(--emphasis-color);
-      }
-      .attention iron-icon {
-        vertical-align: top;
-      }
-    </style>
-    <div id="container" role="tooltip" tabindex="-1">
-      <div class="top">
-        <div class="avatar">
-          <gr-avatar account="[[account]]" image-size="56"></gr-avatar>
-        </div>
-        <div class="account">
-          <h3 class="name">[[account.name]]</h3>
-          <div class="email">[[account.email]]</div>
-        </div>
+    }
+    :host(:not([attention])) .attention {
+      display: none;
+    }
+    .attention {
+      background-color: var(--emphasis-color);
+    }
+    .attention iron-icon {
+      vertical-align: top;
+    }
+  </style>
+  <div id="container" role="tooltip" tabindex="-1">
+    <div class="top">
+      <div class="avatar">
+        <gr-avatar account="[[account]]" image-size="56"></gr-avatar>
       </div>
-      <template is="dom-if" if="[[account.status]]">
-        <div class="status">
-          <span class="title">
-            <iron-icon icon="gr-icons:calendar"></iron-icon>
-            Status:
-          </span>
-          <span class="value">[[account.status]]</span>
-        </div>
-      </template>
-      <template is="dom-if" if="[[voteableText]]">
-        <div class="voteable">
-          <span class="title">Voteable:</span>
-          <span class="value">[[voteableText]]</span>
-        </div>
-      </template>
-      <div class="attention">
-        <iron-icon icon="gr-icons:attention"></iron-icon>
-        <span>It is this user's turn to take action.</span>
+      <div class="account">
+        <h3 class="name">[[account.name]]</h3>
+        <div class="email">[[account.email]]</div>
       </div>
     </div>
+    <template is="dom-if" if="[[account.status]]">
+      <div class="status">
+        <span class="title">
+          <iron-icon icon="gr-icons:calendar"></iron-icon>
+          Status:
+        </span>
+        <span class="value">[[account.status]]</span>
+      </div>
+    </template>
+    <template is="dom-if" if="[[voteableText]]">
+      <div class="voteable">
+        <span class="title">Voteable:</span>
+        <span class="value">[[voteableText]]</span>
+      </div>
+    </template>
+    <div class="attention">
+      <iron-icon icon="gr-icons:attention"></iron-icon>
+      <span>It is this user's turn to take action.</span>
+    </div>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
index 956c68c..1190908 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard-behavior.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {getRootElement} from '../../../scripts/rootElement.js';
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 e77a4c5..e334064 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -24,7 +23,7 @@
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import './gr-hovercard-shared-style.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrHovercard extends GestureEventListeners(
     hovercardBehaviorMixin(LegacyElementMixin(PolymerElement))
 ) {
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
index 69fd4c5..67a3545 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_html.js
@@ -17,12 +17,12 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-hovercard-shared-style">
-      #container {
-        padding: var(--spacing-l);
-      }
-    </style>
-    <div id="container" role="tooltip" tabindex="-1">
-      <slot></slot>
-    </div>
+  <style include="gr-hovercard-shared-style">
+    #container {
+      padding: var(--spacing-l);
+    }
+  </style>
+  <div id="container" role="tooltip" tabindex="-1">
+    <slot></slot>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html
index 81c73ab..21f692d 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-hovercard</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html
index c8ef2f8..a14612b 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-annotation-actions-context</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html
index 7a6b254..e72fc63 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-annotation-actions-js-api-js-api</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
index 13e3a06..d01566a 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-api-interface</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
index 6f44b1a..1d5e423 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-actions-js-api</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
index a67c8eb..0360f85 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-change-reply-js-api</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
index 3bbed62..2d87497 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-api-interface</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js
index 997e08c..a98936f 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -46,7 +44,7 @@
 };
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrJsApiInterface extends mixinBehaviors( [
   PatchSetBehavior,
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 1cdb20f..6f0ade9 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import '../gr-rest-api-interface/gr-rest-api-interface.js';
 import './gr-js-api-interface-element.js';
 import './gr-public-js-api.js';
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index a7fcfab..ea1ac91 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-api-interface</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context.js
index e3256a1..63555da 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context.js
@@ -14,6 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
+
 export function GrPluginActionContext(plugin, action, change, revision) {
   this.action = action;
   this.plugin = plugin;
@@ -47,14 +49,14 @@
 
 GrPluginActionContext.prototype.msg = function(text) {
   const label = document.createElement('gr-label');
-  Polymer.dom(label).appendChild(document.createTextNode(text));
+  dom(label).appendChild(document.createTextNode(text));
   return label;
 };
 
 GrPluginActionContext.prototype.div = function(...els) {
   const div = document.createElement('div');
   for (const el of els) {
-    Polymer.dom(div).appendChild(el);
+    dom(div).appendChild(el);
   }
   return div;
 };
@@ -62,7 +64,7 @@
 GrPluginActionContext.prototype.button = function(label, callbacks) {
   const onClick = callbacks && callbacks.onclick;
   const button = document.createElement('gr-button');
-  Polymer.dom(button).appendChild(document.createTextNode(label));
+  dom(button).appendChild(document.createTextNode(label));
   if (onClick) {
     this.plugin.eventHelper(button).onTap(onClick);
   }
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html
index 8f82352..08c784ab 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-plugin-action-context</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html
index 5617d73..ec8710b 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-plugin-endpoints</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
index 2f27304..6c5546e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
@@ -1,3 +1,5 @@
+import {appContext} from '../../../services/app-context.js';
+
 /**
  * @license
  * Copyright (C) 2019 The Android Open Source Project
@@ -15,6 +17,7 @@
  * limitations under the License.
  */
 import './gr-api-utils.js';
+import {importHref} from '../../../scripts/import-href.js';
 
 import {
   PLUGIN_LOADING_TIMEOUT_MS,
@@ -85,7 +88,7 @@
 
   _getReporting() {
     if (!this._reporting) {
-      this._reporting = document.createElement('gr-reporting');
+      this._reporting = appContext.reportingService;
     }
     return this._reporting;
   }
@@ -333,7 +336,7 @@
       };
     }
 
-    (Polymer.importHref || Polymer.Base.importHref)(
+    importHref(
         url, () => {},
         onerror,
         !sync);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
index 1cbe231..ea1fcf2 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-plugin-host</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -98,12 +99,14 @@
   });
 
   test('report pluginsLoaded', done => {
-    stub('gr-reporting', {
-      pluginsLoaded() {
-        done();
-      },
+    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+        'pluginsLoaded');
+    pluginsLoadedStub.reset();
+    window.Gerrit._loadPlugins([]);
+    flush(() => {
+      assert.isTrue(pluginsLoadedStub.calledOnce);
+      done();
     });
-    pluginLoader.loadPlugins([]);
   });
 
   test('arePluginsLoaded', done => {
@@ -128,10 +131,8 @@
     sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
       pluginApi.install(() => void 0, undefined, url);
     });
-    const pluginsLoadedStub = sandbox.stub();
-    stub('gr-reporting', {
-      pluginsLoaded: (...args) => pluginsLoadedStub(...args),
-    });
+    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+        'pluginsLoaded');
 
     const plugins = [
       'http://test.com/plugins/foo/static/test.js',
@@ -150,10 +151,6 @@
     sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
       pluginApi.install(() => void 0, undefined, url);
     });
-    const pluginsLoadedStub = sandbox.stub();
-    stub('gr-reporting', {
-      pluginsLoaded: (...args) => pluginsLoadedStub(...args),
-    });
 
     const plugins = [
       'http://test.com/plugins/foo/static/test.js',
@@ -192,10 +189,8 @@
       }, undefined, url);
     });
 
-    const pluginsLoadedStub = sandbox.stub();
-    stub('gr-reporting', {
-      pluginsLoaded: (...args) => pluginsLoadedStub(...args),
-    });
+    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+        'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
 
@@ -224,10 +219,8 @@
       }, undefined, url);
     });
 
-    const pluginsLoadedStub = sandbox.stub();
-    stub('gr-reporting', {
-      pluginsLoaded: (...args) => pluginsLoadedStub(...args),
-    });
+    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+        'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
     assert.isTrue(
@@ -259,10 +252,8 @@
       }, undefined, url);
     });
 
-    const pluginsLoadedStub = sandbox.stub();
-    stub('gr-reporting', {
-      pluginsLoaded: (...args) => pluginsLoadedStub(...args),
-    });
+    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+        'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
 
@@ -288,10 +279,8 @@
       }, url === plugins[0] ? '' : 'alpha', url);
     });
 
-    const pluginsLoadedStub = sandbox.stub();
-    stub('gr-reporting', {
-      pluginsLoaded: (...args) => pluginsLoadedStub(...args),
-    });
+    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+        'pluginsLoaded');
 
     pluginLoader.loadPlugins(plugins);
 
@@ -307,10 +296,8 @@
     sandbox.stub(pluginLoader, '_loadJsPlugin', url => {
       pluginApi.install(() => void 0, undefined, url);
     });
-    const pluginsLoadedStub = sandbox.stub();
-    stub('gr-reporting', {
-      pluginsLoaded: (...args) => pluginsLoadedStub(...args),
-    });
+    const pluginsLoadedStub = sandbox.stub(pluginLoader._getReporting(),
+        'pluginsLoaded');
 
     const plugins = [
       'http://test.com/plugins/foo/static/test.js',
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html
index 4c17f46..fcc3b66 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-plugin-rest-api</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 2c5589f..3161468 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/gr-voting-styles.js';
 import '../../../styles/shared-styles.js';
 import '../gr-account-label/gr-account-label.js';
@@ -31,7 +29,7 @@
 import {htmlTemplate} from './gr-label-info_html.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrLabelInfo extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.js
index d19467b..2a86669 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.js
@@ -17,89 +17,109 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="gr-voting-styles">
-      /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-    </style>
-    <style include="shared-styles">
-      .placeholder {
-        color: var(--deemphasized-text-color);
-        padding-top: var(--spacing-xs);
-      }
-      .hidden {
-        display: none;
-      }
-      .voteChip {
-        display: flex;
-        justify-content: center;
-        margin-right: var(--spacing-s);
+  <style include="gr-voting-styles">
+    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
+  </style>
+  <style include="shared-styles">
+    .placeholder {
+      color: var(--deemphasized-text-color);
+      padding-top: var(--spacing-xs);
+    }
+    .hidden {
+      display: none;
+    }
+    .voteChip {
+      display: flex;
+      justify-content: center;
+      margin-right: var(--spacing-s);
+      padding: 0;
+      @apply --vote-chip-styles;
+      border-width: 0;
+    }
+    .max {
+      background-color: var(--vote-color-approved);
+    }
+    .min {
+      background-color: var(--vote-color-rejected);
+    }
+    .positive {
+      background-color: var(--vote-color-recommended);
+    }
+    .negative {
+      background-color: var(--vote-color-disliked);
+    }
+    .hidden {
+      display: none;
+    }
+    td {
+      vertical-align: top;
+    }
+    tr {
+      min-height: var(--line-height-normal);
+    }
+    gr-button {
+      vertical-align: top;
+      --gr-button: {
+        height: var(--line-height-normal);
+        width: var(--line-height-normal);
         padding: 0;
-        @apply --vote-chip-styles;
-        border-width: 0;
       }
-      .max {
-        background-color: var(--vote-color-approved);
-      }
-      .min {
-        background-color: var(--vote-color-rejected);
-      }
-      .positive {
-        background-color: var(--vote-color-recommended);
-      }
-      .negative {
-        background-color: var(--vote-color-disliked);
-      }
-      .hidden {
-        display: none;
-      }
-      td {
-        vertical-align: top;
-      }
-      tr {
-        min-height: var(--line-height-normal);
-      }
-      gr-button {
-        vertical-align: top;
-        --gr-button: {
-          height: var(--line-height-normal);
-          width: var(--line-height-normal);
-          padding: 0;
-        }
-      }
-      gr-button[disabled] iron-icon {
-        color: var(--border-color);
-      }
-      gr-account-chip {
-        margin-right: var(--spacing-xs);
-      }
-      iron-icon {
-        height: calc(var(--line-height-normal) - 2px);
-        width: calc(var(--line-height-normal) - 2px);
-      }
-      .labelValueContainer:not(:first-of-type) td {
-        padding-top: var(--spacing-s);
-      }
-    </style>
-    <p class\$="placeholder [[_computeShowPlaceholder(labelInfo, change.labels.*)]]">
-        No votes.
-      </p><table>
-      
-      <template is="dom-repeat" items="[[_mapLabelInfo(labelInfo, account, change.labels.*)]]" as="mappedLabel">
-        <tr class="labelValueContainer">
-          <td>
-            <gr-label has-tooltip="" title="[[_computeValueTooltip(labelInfo, mappedLabel.value)]]" class\$="[[mappedLabel.className]] voteChip">
-              [[mappedLabel.value]]
-            </gr-label>
-          </td>
-          <td>
-            <gr-account-chip account="[[mappedLabel.account]]" transparent-background=""></gr-account-chip>
-          </td>
-          <td>
-            <gr-button link="" aria-label="Remove" on-click="_onDeleteVote" tooltip="Remove vote" data-account-id\$="[[mappedLabel.account._account_id]]" class\$="deleteBtn [[_computeDeleteClass(mappedLabel.account, mutable, change)]]">
-              <iron-icon icon="gr-icons:delete"></iron-icon>
-            </gr-button>
-          </td>
-        </tr>
-      </template>
-    </table>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+    }
+    gr-button[disabled] iron-icon {
+      color: var(--border-color);
+    }
+    gr-account-chip {
+      margin-right: var(--spacing-xs);
+    }
+    iron-icon {
+      height: calc(var(--line-height-normal) - 2px);
+      width: calc(var(--line-height-normal) - 2px);
+    }
+    .labelValueContainer:not(:first-of-type) td {
+      padding-top: var(--spacing-s);
+    }
+  </style>
+  <p
+    class$="placeholder [[_computeShowPlaceholder(labelInfo, change.labels.*)]]"
+  >
+    No votes.
+  </p>
+  <table>
+    <template
+      is="dom-repeat"
+      items="[[_mapLabelInfo(labelInfo, account, change.labels.*)]]"
+      as="mappedLabel"
+    >
+      <tr class="labelValueContainer">
+        <td>
+          <gr-label
+            has-tooltip=""
+            title="[[_computeValueTooltip(labelInfo, mappedLabel.value)]]"
+            class$="[[mappedLabel.className]] voteChip"
+          >
+            [[mappedLabel.value]]
+          </gr-label>
+        </td>
+        <td>
+          <gr-account-chip
+            account="[[mappedLabel.account]]"
+            transparent-background=""
+          ></gr-account-chip>
+        </td>
+        <td>
+          <gr-button
+            link=""
+            aria-label="Remove"
+            on-click="_onDeleteVote"
+            tooltip="Remove vote"
+            data-account-id$="[[mappedLabel.account._account_id]]"
+            class$="deleteBtn [[_computeDeleteClass(mappedLabel.account, mutable, change)]]"
+          >
+            <iron-icon icon="gr-icons:delete"></iron-icon>
+          </gr-button>
+        </td>
+      </tr>
+    </template>
+  </table>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
index a3af731..a340550 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
@@ -16,6 +16,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-label-info</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 014e85e..fa1f758 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -24,7 +22,7 @@
 import {TooltipBehavior} from '../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrLabel extends mixinBehaviors( [
   TooltipBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.js b/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.js
index 1644c07..c4310fc 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label_html.js
@@ -16,6 +16,4 @@
  */
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
-export const htmlTemplate = html`
-    <slot></slot>
-`;
+export const htmlTemplate = html` <slot></slot> `;
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 f585347..d17f7d8 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-autocomplete/gr-autocomplete.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -23,7 +21,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-labeled-autocomplete_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrLabeledAutocomplete extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.js b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.js
index fe0b03c..615a525 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_html.js
@@ -17,38 +17,45 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        width: 12em;
-      }
-      #container {
-        background: var(--chip-background-color);
-        border-radius: 1em;
-        padding: var(--spacing-m);
-      }
-      #header {
-        color: var(--deemphasized-text-color);
-        font-weight: var(--font-weight-bold);
-        font-size: var(--font-size-small);
-      }
-      #body {
-        display: flex;
-      }
-      #trigger {
-        color: var(--deemphasized-text-color);
-        cursor: pointer;
-        padding-left: var(--spacing-s);
-      }
-      #trigger:hover {
-        color: var(--primary-text-color);
-      }
-    </style>
-    <div id="container">
-      <div id="header">[[label]]</div>
-      <div id="body">
-        <gr-autocomplete id="autocomplete" threshold="[[_autocompleteThreshold]]" query="[[query]]" disabled="[[disabled]]" placeholder="[[placeholder]]" borderless=""></gr-autocomplete>
-        <div id="trigger" on-click="_handleTriggerClick">â–¼</div>
-      </div>
+  <style include="shared-styles">
+    :host {
+      display: block;
+      width: 12em;
+    }
+    #container {
+      background: var(--chip-background-color);
+      border-radius: 1em;
+      padding: var(--spacing-m);
+    }
+    #header {
+      color: var(--deemphasized-text-color);
+      font-weight: var(--font-weight-bold);
+      font-size: var(--font-size-small);
+    }
+    #body {
+      display: flex;
+    }
+    #trigger {
+      color: var(--deemphasized-text-color);
+      cursor: pointer;
+      padding-left: var(--spacing-s);
+    }
+    #trigger:hover {
+      color: var(--primary-text-color);
+    }
+  </style>
+  <div id="container">
+    <div id="header">[[label]]</div>
+    <div id="body">
+      <gr-autocomplete
+        id="autocomplete"
+        threshold="[[_autocompleteThreshold]]"
+        query="[[query]]"
+        disabled="[[disabled]]"
+        placeholder="[[placeholder]]"
+        borderless=""
+      ></gr-autocomplete>
+      <div id="trigger" on-click="_handleTriggerClick">â–¼</div>
     </div>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html
index 7e83aaf..99a038e 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html
@@ -16,6 +16,7 @@
 limitations under the License.
 -->
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-labeled-autocomplete</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 9bd8a11..e9cd441 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-js-api-interface/gr-js-api-interface.js';
 import {importHref} from '../../../scripts/import-href.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
@@ -27,7 +25,7 @@
 const HLJS_PATH = 'bower_components/highlightjs/highlight.min.js';
 const DARK_THEME_PATH = 'styles/themes/dark-theme.html';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrLibLoader extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.js b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.js
index 3bc0d72..204aa87 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_html.js
@@ -17,5 +17,5 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+  <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
index 89672a1..f2e5e3d 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-lib-loader</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b7bfbf3..40cd505 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -29,7 +27,7 @@
  * configured limit, then an ellipsis indicates that the text was truncated
  * and a tooltip containing the full text is enabled.
  *
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrLimitedText extends mixinBehaviors( [
   TooltipBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.js b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.js
index c14f9f9..6bcce8c 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_html.js
@@ -16,6 +16,4 @@
  */
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
-export const htmlTemplate = html`
-[[_computeDisplayText(text, limit)]]
-`;
+export const htmlTemplate = html` [[_computeDisplayText(text, limit)]] `;
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html
index 1c6358c..889b786 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-limited-text</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 077ca74..46588ef 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
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 
 import '../gr-button/gr-button.js';
 import '../gr-icons/gr-icons.js';
@@ -26,7 +25,7 @@
 import {htmlTemplate} from './gr-linked-chip_html.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrLinkedChip extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.js b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.js
index c028d02..f1f5f46 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_html.js
@@ -17,65 +17,72 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-        overflow: hidden;
+  <style include="shared-styles">
+    :host {
+      display: block;
+      overflow: hidden;
+    }
+    .container {
+      align-items: center;
+      background: var(--chip-background-color);
+      border-radius: 0.75em;
+      display: inline-flex;
+      padding: 0 var(--spacing-m);
+    }
+    gr-button.remove {
+      --gr-remove-button-style: {
+        border: 0;
+        color: var(--deemphasized-text-color);
+        font-weight: var(--font-weight-normal);
+        height: 0.6em;
+        line-height: 10px;
+        margin-left: var(--spacing-xs);
+        padding: 0;
+        text-decoration: none;
       }
-      .container {
-        align-items: center;
-        background: var(--chip-background-color);
-        border-radius: .75em;
-        display: inline-flex;
-        padding: 0 var(--spacing-m);
-      }
-      gr-button.remove {
-        --gr-remove-button-style: {
-          border: 0;
-          color: var(--deemphasized-text-color);
-          font-weight: var(--font-weight-normal);
-          height: .6em;
-          line-height: 10px;
-          margin-left: var(--spacing-xs);
-          padding: 0;
-          text-decoration: none;
-        }
-      }
+    }
 
-      gr-button.remove:hover,
-      gr-button.remove:focus {
-        --gr-button: {
-          @apply --gr-remove-button-style;
-          color: #333;
-        }
+    gr-button.remove:hover,
+    gr-button.remove:focus {
+      --gr-button: {
+        @apply --gr-remove-button-style;
+        color: #333;
       }
-      gr-button.remove {
-        --gr-button: {
-          @apply --gr-remove-button-style;
-        }
+    }
+    gr-button.remove {
+      --gr-button: {
+        @apply --gr-remove-button-style;
       }
-      .transparentBackground,
-      gr-button.transparentBackground {
-        background-color: transparent;
-      }
-      :host([disabled]) {
-        opacity: .6;
-        pointer-events: none;
-      }
-      a {
-       color: var(--linked-chip-text-color);
-      }
-      iron-icon {
-        height: 1.2rem;
-        width: 1.2rem;
-      }
-    </style>
-    <div class\$="container [[_getBackgroundClass(transparentBackground)]]">
-      <a href\$="[[href]]">
-        <gr-limited-text limit="[[limit]]" text="[[text]]"></gr-limited-text>
-      </a>
-      <gr-button id="remove" link="" hidden\$="[[!removable]]" hidden="" class\$="remove [[_getBackgroundClass(transparentBackground)]]" on-click="_handleRemoveTap">
-        <iron-icon icon="gr-icons:close"></iron-icon>
-      </gr-button>
-    </div>
+    }
+    .transparentBackground,
+    gr-button.transparentBackground {
+      background-color: transparent;
+    }
+    :host([disabled]) {
+      opacity: 0.6;
+      pointer-events: none;
+    }
+    a {
+      color: var(--linked-chip-text-color);
+    }
+    iron-icon {
+      height: 1.2rem;
+      width: 1.2rem;
+    }
+  </style>
+  <div class$="container [[_getBackgroundClass(transparentBackground)]]">
+    <a href$="[[href]]">
+      <gr-limited-text limit="[[limit]]" text="[[text]]"></gr-limited-text>
+    </a>
+    <gr-button
+      id="remove"
+      link=""
+      hidden$="[[!removable]]"
+      hidden=""
+      class$="remove [[_getBackgroundClass(transparentBackground)]]"
+      on-click="_handleRemoveTap"
+    >
+      <iron-icon icon="gr-icons:close"></iron-icon>
+    </gr-button>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
index 0ba6194..c8de3df 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-linked-chip</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 6ea4b78..b969d1d 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -26,7 +24,7 @@
 import {GrLinkTextParser} from './link-text-parser.js';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrLinkedText extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.js
index 43d7144..59bed1e 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_html.js
@@ -17,19 +17,19 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([pre]) span {
-        white-space: var(--linked-text-white-space, pre-wrap);
-        word-wrap: var(--linked-text-word-wrap, break-word);
-      }
-      :host([disabled]) a {
-        color: inherit;
-        text-decoration: none;
-        pointer-events: none;
-      }
-    </style>
-    <span id="output"></span>
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    :host([pre]) span {
+      white-space: var(--linked-text-white-space, pre-wrap);
+      word-wrap: var(--linked-text-word-wrap, break-word);
+    }
+    :host([disabled]) a {
+      color: inherit;
+      text-decoration: none;
+      pointer-events: none;
+    }
+  </style>
+  <span id="output"></span>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index 6e46df1..4fa4390 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-linked-text</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 7a44c67..595694c 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-input/iron-input.js';
 import '@polymer/iron-icon/iron-icon.js';
 import '../../../styles/shared-styles.js';
@@ -32,7 +30,7 @@
 const REQUEST_DEBOUNCE_INTERVAL_MS = 200;
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrListView extends mixinBehaviors( [
   BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.js b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.js
index 0d33dd0..ff73d4d 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_html.js
@@ -17,68 +17,83 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      #filter {
-        max-width: 25em;
-      }
-      #filter:focus {
-        outline: none;
-      }
-      #topContainer {
-        align-items: center;
-        display: flex;
-        height: 3rem;
-        justify-content: space-between;
-        margin: 0 var(--spacing-l);
-      }
-      #createNewContainer:not(.show) {
-        display: none;
-      }
-      a {
-        color: var(--primary-text-color);
-        text-decoration: none;
-      }
-      a:hover {
-        text-decoration: underline;
-      }
-      nav {
-        align-items: center;
-        display: flex;
-        height: 3rem;
-        justify-content: flex-end;
-        margin-right: 20px;
-      }
-      nav,
-      iron-icon {
-        color: var(--deemphasized-text-color);
-      }
-      iron-icon {
-        height: 1.85rem;
-        margin-left: 16px;
-        width: 1.85rem;
-      }
-    </style>
-    <div id="topContainer">
-      <div class="filterContainer">
-        <label>Filter:</label>
-        <iron-input type="text" bind-value="{{filter}}">
-          <input is="iron-input" type="text" id="filter" bind-value="{{filter}}">
-        </iron-input>
-      </div>
-      <div id="createNewContainer" class\$="[[_computeCreateClass(createNew)]]">
-        <gr-button primary="" link="" id="createNew" on-click="_createNewItem">
-          Create New
-        </gr-button>
-      </div>
+  <style include="shared-styles">
+    #filter {
+      max-width: 25em;
+    }
+    #filter:focus {
+      outline: none;
+    }
+    #topContainer {
+      align-items: center;
+      display: flex;
+      height: 3rem;
+      justify-content: space-between;
+      margin: 0 var(--spacing-l);
+    }
+    #createNewContainer:not(.show) {
+      display: none;
+    }
+    a {
+      color: var(--primary-text-color);
+      text-decoration: none;
+    }
+    a:hover {
+      text-decoration: underline;
+    }
+    nav {
+      align-items: center;
+      display: flex;
+      height: 3rem;
+      justify-content: flex-end;
+      margin-right: 20px;
+    }
+    nav,
+    iron-icon {
+      color: var(--deemphasized-text-color);
+    }
+    iron-icon {
+      height: 1.85rem;
+      margin-left: 16px;
+      width: 1.85rem;
+    }
+  </style>
+  <div id="topContainer">
+    <div class="filterContainer">
+      <label>Filter:</label>
+      <iron-input type="text" bind-value="{{filter}}">
+        <input
+          is="iron-input"
+          type="text"
+          id="filter"
+          bind-value="{{filter}}"
+        />
+      </iron-input>
     </div>
-    <slot></slot>
-    <nav>
-      Page [[_computePage(offset, itemsPerPage)]]
-      <a id="prevArrow" href\$="[[_computeNavLink(offset, -1, itemsPerPage, filter, path)]]" hidden\$="[[_hidePrevArrow(loading, offset)]]" hidden="">
-        <iron-icon icon="gr-icons:chevron-left"></iron-icon>
-      </a>
-      <a id="nextArrow" href\$="[[_computeNavLink(offset, 1, itemsPerPage, filter, path)]]" hidden\$="[[_hideNextArrow(loading, items)]]" hidden="">
-        <iron-icon icon="gr-icons:chevron-right"></iron-icon>
-      </a>
-    </nav>
+    <div id="createNewContainer" class$="[[_computeCreateClass(createNew)]]">
+      <gr-button primary="" link="" id="createNew" on-click="_createNewItem">
+        Create New
+      </gr-button>
+    </div>
+  </div>
+  <slot></slot>
+  <nav>
+    Page [[_computePage(offset, itemsPerPage)]]
+    <a
+      id="prevArrow"
+      href$="[[_computeNavLink(offset, -1, itemsPerPage, filter, path)]]"
+      hidden$="[[_hidePrevArrow(loading, offset)]]"
+      hidden=""
+    >
+      <iron-icon icon="gr-icons:chevron-left"></iron-icon>
+    </a>
+    <a
+      id="nextArrow"
+      href$="[[_computeNavLink(offset, 1, itemsPerPage, filter, path)]]"
+      hidden$="[[_hideNextArrow(loading, items)]]"
+      hidden=""
+    >
+      <iron-icon icon="gr-icons:chevron-right"></iron-icon>
+    </a>
+  </nav>
 `;
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 95eebc9..55aab82 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
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-list-view</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 a5a3fb4..5437ca5 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {IronOverlayBehaviorImpl, IronOverlayBehavior} from '@polymer/iron-overlay-behavior/iron-overlay-behavior.js';
 import '../../../styles/shared-styles.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
@@ -29,7 +27,7 @@
 const BREAKPOINT_FULLSCREEN_OVERLAY = '50em';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrOverlay extends mixinBehaviors( [
   IronOverlayBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.js b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.js
index 5fe000a..7123adb 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_html.js
@@ -17,24 +17,24 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        background: var(--dialog-background-color);
-        border-radius: var(--border-radius);
-        box-shadow: var(--elevation-level-5);
-      }
+  <style include="shared-styles">
+    :host {
+      background: var(--dialog-background-color);
+      border-radius: var(--border-radius);
+      box-shadow: var(--elevation-level-5);
+    }
 
-      @media screen and (max-width: 50em) {
-        :host {
-          height: 100%;
-          left: 0;
-          position: fixed;
-          right: 0;
-          top: 0;
-          border-radius: 0;
-          box-shadow: none;
-        }
+    @media screen and (max-width: 50em) {
+      :host {
+        height: 100%;
+        left: 0;
+        position: fixed;
+        right: 0;
+        top: 0;
+        border-radius: 0;
+        box-shadow: none;
       }
-    </style>
-    <slot></slot>
+    }
+  </style>
+  <slot></slot>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html
index 52bba7a..d43c739 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-overlay</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 8366463..f191981 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
@@ -14,14 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-page-nav_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrPageNav extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.js b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.js
index fe17a1b..c5e9142 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_html.js
@@ -17,26 +17,26 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
+  <style include="shared-styles">
+    #nav {
+      background-color: var(--table-header-background-color);
+      border: 1px solid var(--border-color);
+      border-top: none;
+      height: 100%;
+      position: absolute;
+      top: 0;
+      width: 14em;
+    }
+    #nav.pinned {
+      position: fixed;
+    }
+    @media only screen and (max-width: 53em) {
       #nav {
-        background-color: var(--table-header-background-color);
-        border: 1px solid var(--border-color);
-        border-top: none;
-        height: 100%;
-        position: absolute;
-        top: 0;
-        width: 14em;
+        display: none;
       }
-      #nav.pinned {
-        position: fixed;
-      }
-      @media only screen and (max-width: 53em) {
-        #nav {
-          display: none;
-        }
-      }
-    </style>
-    <nav id="nav">
-      <slot></slot>
-    </nav>
+    }
+  </style>
+  <nav id="nav">
+    <slot></slot>
+  </nav>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html
index c1f7ace..a54d7a3 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-page-nav</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 c499f56..9439c08 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '@polymer/iron-icon/iron-icon.js';
 import '../../../styles/shared-styles.js';
 import '../gr-icons/gr-icons.js';
@@ -32,7 +30,7 @@
 const REF_PREFIX = 'refs/heads/';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRepoBranchPicker extends mixinBehaviors( [
   URLEncodingBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.js b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.js
index fe6b522..0ce885a 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_html.js
@@ -17,24 +17,37 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      gr-labeled-autocomplete,
-      iron-icon {
-        display: inline-block;
-      }
-      iron-icon {
-        margin-bottom: var(--spacing-l);
-      }
-    </style>
-    <div>
-      <gr-labeled-autocomplete id="repoInput" label="Repository" placeholder="Select repo" on-commit="_repoCommitted" query="[[_repoQuery]]">
-      </gr-labeled-autocomplete>
-      <iron-icon icon="gr-icons:chevron-right"></iron-icon>
-      <gr-labeled-autocomplete id="branchInput" label="Branch" placeholder="Select branch" disabled="[[_branchDisabled]]" on-commit="_branchCommitted" query="[[_query]]">
-      </gr-labeled-autocomplete>
-    </div>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  <style include="shared-styles">
+    :host {
+      display: block;
+    }
+    gr-labeled-autocomplete,
+    iron-icon {
+      display: inline-block;
+    }
+    iron-icon {
+      margin-bottom: var(--spacing-l);
+    }
+  </style>
+  <div>
+    <gr-labeled-autocomplete
+      id="repoInput"
+      label="Repository"
+      placeholder="Select repo"
+      on-commit="_repoCommitted"
+      query="[[_repoQuery]]"
+    >
+    </gr-labeled-autocomplete>
+    <iron-icon icon="gr-icons:chevron-right"></iron-icon>
+    <gr-labeled-autocomplete
+      id="branchInput"
+      label="Branch"
+      placeholder="Select branch"
+      disabled="[[_branchDisabled]]"
+      on-commit="_branchCommitted"
+      query="[[_query]]"
+    >
+    </gr-labeled-autocomplete>
+  </div>
+  <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
index 8e50b09..67b82f9 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
@@ -16,6 +16,7 @@
 limitations under the License.
 -->
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-repo-branch-picker</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
index dd5f0ba..5fa476f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-auth</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
index 23b8de7..7402e22 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 const $_documentContainer = document.createElement('template');
 
 $_documentContainer.innerHTML = `<dom-module id="gr-etag-decorator">
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
index f82779a..cfa164f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-etag-decorator</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 b675df7..207aa29 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
@@ -16,12 +16,6 @@
  */
 /* NB: es6-promise Needed for IE11 and fetch polyfill support, see Issue 4308 */
 /* NB: Order is important, because of namespaced classes. */
-/*
-  FIXME(polymer-modulizer): the above comments were extracted
-  from HTML and may be out of place here. Review them and
-  then delete this comment!
-*/
-import '../../../scripts/bundled-polymer.js';
 
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -61,7 +55,7 @@
     '/revisions/*';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrRestApiInterface extends mixinBehaviors( [
   PathListBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index 1b06685..0a51d26 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-rest-api-interface</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html
index 6a6623d..32d2166 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-rest-api-helper</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
index 3d1ce05..224eb5f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
@@ -16,6 +16,7 @@
  */
 
 import {util} from '../../../scripts/util.js';
+import {MessageTags} from '../../../constants/constants.js';
 
 /** @constructor */
 export function GrReviewerUpdatesParser(change) {
@@ -52,9 +53,7 @@
  */
 GrReviewerUpdatesParser.prototype._filterRemovedMessages = function() {
   this.result.messages = this.result.messages
-      .filter(
-          message => message.tag !== 'autogenerated:gerrit:deleteReviewer'
-      );
+      .filter(message => message.tag !== MessageTags.TAG_DELETE_REVIEWER);
 };
 
 /**
@@ -68,6 +67,7 @@
     author: update.updated_by,
     date: update.updated,
     type: 'REVIEWER_UPDATE',
+    tag: MessageTags.TAG_REVIEWER_UPDATE,
   };
 };
 
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
index bbdbb31..f2ccfb7 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-reviewer-updates-parser</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 e061e93..c89378c 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
@@ -29,7 +27,7 @@
 document.head.appendChild($_documentContainer.content);
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrSelect extends GestureEventListeners(
     LegacyElementMixin(PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
index 757e38e..670f383 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-select</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 151498c..a5212fc 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import '../gr-copy-clipboard/gr-copy-clipboard.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -23,7 +21,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-shell-command_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrShellCommand extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.js b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.js
index 8fbf2b6..4a4480e 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_html.js
@@ -17,41 +17,42 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      .commandContainer {
-        margin-bottom: var(--spacing-m);
+  <style include="shared-styles">
+    .commandContainer {
+      margin-bottom: var(--spacing-m);
+    }
+    .commandContainer {
+      background-color: var(--shell-command-background-color);
+      /* Should be spacing-m larger than the :before width. */
+      padding: var(--spacing-m) var(--spacing-m) var(--spacing-m)
+        calc(3 * var(--spacing-m) + 0.5em);
+      position: relative;
+      width: 100%;
+    }
+    .commandContainer:before {
+      content: '$';
+      position: absolute;
+      display: block;
+      box-sizing: border-box;
+      background: var(--shell-command-decoration-background-color);
+      top: 0;
+      bottom: 0;
+      left: 0;
+      /* Should be spacing-m smaller than the .commandContainer padding-left. */
+      width: calc(2 * var(--spacing-m) + 0.5em);
+      /* Should vertically match the padding of .commandContainer. */
+      padding: var(--spacing-m);
+      /* Should roughly match the height of .commandContainer without padding. */
+      line-height: 26px;
+    }
+    .commandContainer gr-copy-clipboard {
+      --text-container-style: {
+        border: none;
       }
-      .commandContainer {
-        background-color: var(--shell-command-background-color);
-        /* Should be spacing-m larger than the :before width. */
-        padding: var(--spacing-m) var(--spacing-m) var(--spacing-m) calc(3*var(--spacing-m) + 0.5em);
-        position: relative;
-        width: 100%;
-      }
-      .commandContainer:before {
-        content: '\$';
-        position: absolute;
-        display: block;
-        box-sizing: border-box;
-        background: var(--shell-command-decoration-background-color);
-        top: 0;
-        bottom: 0;
-        left: 0;
-        /* Should be spacing-m smaller than the .commandContainer padding-left. */
-        width: calc(2*var(--spacing-m) + 0.5em);
-        /* Should vertically match the padding of .commandContainer. */
-        padding: var(--spacing-m);
-        /* Should roughly match the height of .commandContainer without padding. */
-        line-height: 26px;
-      }
-      .commandContainer gr-copy-clipboard {
-        --text-container-style: {
-          border: none;
-        }
-      }
-    </style>
-    <label>[[label]]</label>
-    <div class="commandContainer">
-      <gr-copy-clipboard text="[[command]]"></gr-copy-clipboard>
-    </div>
+    }
+  </style>
+  <label>[[label]]</label>
+  <div class="commandContainer">
+    <gr-copy-clipboard text="[[command]]"></gr-copy-clipboard>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html
index cc779c7..ee0b64f 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-shell-command</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 8f5c486..90fdcd3 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
@@ -32,7 +30,7 @@
   'editablecontent:': DURATION_DAY,
 };
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrStorage extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
index 5642e15..b560c56 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
@@ -16,6 +16,7 @@
 limitations under the License.
 -->
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-storage</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 15ab8e4..e98e11d 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -14,15 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-autocomplete-dropdown/gr-autocomplete-dropdown.js';
 import '../gr-cursor-manager/gr-cursor-manager.js';
 import '../gr-overlay/gr-overlay.js';
 import '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
 import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
 import '../../../styles/shared-styles.js';
-import '../../core/gr-reporting/gr-reporting.js';
 import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -30,6 +27,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-textarea_html.js';
 import {KeyboardShortcutBehavior} from '../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js';
+import {appContext} from '../../../services/app-context.js';
 
 const MAX_ITEMS_DROPDOWN = 10;
 
@@ -67,7 +65,7 @@
 ];
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrTextarea extends mixinBehaviors( [
   KeyboardShortcutBehavior,
@@ -139,6 +137,11 @@
     };
   }
 
+  constructor() {
+    super();
+    this.reporting = appContext.reportingService;
+  }
+
   /** @override */
   ready() {
     super.ready();
@@ -214,7 +217,7 @@
     this.text = this._getText(text);
     this.$.textarea.selectionStart = colonIndex + 1;
     this.$.textarea.selectionEnd = colonIndex + 1;
-    this.$.reporting.reportInteraction('select-emoji', {type: text});
+    this.reporting.reportInteraction('select-emoji', {type: text});
     this._resetEmojiDropdown();
   }
 
@@ -302,7 +305,7 @@
 
   _openEmojiDropdown() {
     this.$.emojiSuggestions.open();
-    this.$.reporting.reportInteraction('open-emoji-dropdown');
+    this.reporting.reportInteraction('open-emoji-dropdown');
   }
 
   _formatSuggestions(matchedSuggestions) {
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.js
index 99dd52d..17e609f 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_html.js
@@ -17,62 +17,80 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        display: flex;
-        position: relative;
-      }
-      :host(.monospace) {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-mono);
-        line-height: var(--line-height-mono);
-        font-weight: var(--font-weight-normal);
-      }
-      :host(.code) {
-        font-family: var(--monospace-font-family);
-        font-size: var(--font-size-code);
-        line-height: var(--line-height-code);
-        font-weight: var(--font-weight-normal);
-      }
-      #emojiSuggestions {
-        font-family: var(--font-family);
-      }
-      gr-autocomplete {
-        display: inline-block
-      }
-      #textarea {
-        background-color: var(--view-background-color);
-        width: 100%;
-      }
-      #hiddenText #emojiSuggestions {
-        visibility: visible;
-        white-space: normal;
-      }
-      iron-autogrow-textarea {
-        position: relative;
+  <style include="shared-styles">
+    :host {
+      display: flex;
+      position: relative;
+    }
+    :host(.monospace) {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-mono);
+      line-height: var(--line-height-mono);
+      font-weight: var(--font-weight-normal);
+    }
+    :host(.code) {
+      font-family: var(--monospace-font-family);
+      font-size: var(--font-size-code);
+      line-height: var(--line-height-code);
+      font-weight: var(--font-weight-normal);
+    }
+    #emojiSuggestions {
+      font-family: var(--font-family);
+    }
+    gr-autocomplete {
+      display: inline-block;
+    }
+    #textarea {
+      background-color: var(--view-background-color);
+      width: 100%;
+    }
+    #hiddenText #emojiSuggestions {
+      visibility: visible;
+      white-space: normal;
+    }
+    iron-autogrow-textarea {
+      position: relative;
 
-        /** This is needed for firefox */
-        --iron-autogrow-textarea_-_white-space: pre-wrap;
-      }
-      #textarea.noBorder {
-        border: none;
-      }
-      #hiddenText {
-        display: block;
-        float: left;
-        position: absolute;
-        visibility: hidden;
-        width: 100%;
-        white-space: pre-wrap;
-      }
-    </style>
-    <div id="hiddenText"></div>
-    <!-- When the autocomplete is open, the span is moved at the end of
+      /** This is needed for firefox */
+      --iron-autogrow-textarea_-_white-space: pre-wrap;
+    }
+    #textarea.noBorder {
+      border: none;
+    }
+    #hiddenText {
+      display: block;
+      float: left;
+      position: absolute;
+      visibility: hidden;
+      width: 100%;
+      white-space: pre-wrap;
+    }
+  </style>
+  <div id="hiddenText"></div>
+  <!-- When the autocomplete is open, the span is moved at the end of
       hiddenText in order to correctly position the dropdown. After being moved,
       it is set as the positionTarget for the emojiSuggestions dropdown. -->
-    <span id="caratSpan"></span>
-    <gr-autocomplete-dropdown vertical-align="top" horizontal-align="left" dynamic-align="" id="emojiSuggestions" suggestions="[[_suggestions]]" index="[[_index]]" vertical-offset="[[_verticalOffset]]" on-dropdown-closed="_resetEmojiDropdown" on-item-selected="_handleEmojiSelect">
-    </gr-autocomplete-dropdown>
-    <iron-autogrow-textarea id="textarea" autocomplete="[[autocomplete]]" placeholder="[[placeholder]]" disabled="[[disabled]]" rows="[[rows]]" max-rows="[[maxRows]]" value="{{text}}" on-bind-value-changed="_onValueChanged"></iron-autogrow-textarea>
-    <gr-reporting id="reporting"></gr-reporting>
+  <span id="caratSpan"></span>
+  <gr-autocomplete-dropdown
+    vertical-align="top"
+    horizontal-align="left"
+    dynamic-align=""
+    id="emojiSuggestions"
+    suggestions="[[_suggestions]]"
+    index="[[_index]]"
+    vertical-offset="[[_verticalOffset]]"
+    on-dropdown-closed="_resetEmojiDropdown"
+    on-item-selected="_handleEmojiSelect"
+  >
+  </gr-autocomplete-dropdown>
+  <iron-autogrow-textarea
+    id="textarea"
+    autocomplete="[[autocomplete]]"
+    placeholder="[[placeholder]]"
+    disabled="[[disabled]]"
+    rows="[[rows]]"
+    max-rows="[[maxRows]]"
+    value="{{text}}"
+    on-bind-value-changed="_onValueChanged"
+  ></iron-autogrow-textarea>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
index cd0a8c4..bcf1207 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-textarea</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
@@ -52,7 +53,7 @@
   setup(() => {
     sandbox = sinon.sandbox.create();
     element = fixture('basic');
-    sandbox.stub(element.$.reporting, 'reportInteraction');
+    sandbox.stub(element.reporting, 'reportInteraction');
   });
 
   teardown(() => {
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 160f50a..502c82e 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
@@ -14,8 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../gr-icons/gr-icons.js';
 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
@@ -25,7 +23,7 @@
 import {TooltipBehavior} from '../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js';
 
 /**
- * @extends Polymer.Element
+ * @extends PolymerElement
  */
 class GrTooltipContent extends mixinBehaviors( [
   TooltipBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.js b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.js
index e4b5891..e5a2813 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_html.js
@@ -17,13 +17,14 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style>
-      iron-icon {
-        width: var(--line-height-normal);
-        height: var(--line-height-normal);
-        vertical-align: top;
-      }
-    </style>
-    <slot></slot><!--
- --><iron-icon icon="gr-icons:info" hidden\$="[[!showIcon]]"></iron-icon>
+  <style>
+    iron-icon {
+      width: var(--line-height-normal);
+      height: var(--line-height-normal);
+      vertical-align: top;
+    }
+  </style>
+  <slot></slot
+  ><!--
+ --><iron-icon icon="gr-icons:info" hidden$="[[!showIcon]]"></iron-icon>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
index e2c2f60..a8fc18a 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
@@ -16,6 +16,7 @@
 limitations under the License.
 -->
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-storage</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
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 0cd2d7c..2244cca 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
@@ -14,15 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
 import '../../../styles/shared-styles.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
 import {PolymerElement} from '@polymer/polymer/polymer-element.js';
 import {htmlTemplate} from './gr-tooltip_html.js';
 
-/** @extends Polymer.Element */
+/** @extends PolymerElement */
 class GrTooltip extends GestureEventListeners(
     LegacyElementMixin(
         PolymerElement)) {
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.js b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.js
index 5f9ce51..3f02fc5 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_html.js
@@ -17,50 +17,52 @@
 import {html} from '@polymer/polymer/lib/utils/html-tag.js';
 
 export const htmlTemplate = html`
-    <style include="shared-styles">
-      :host {
-        --gr-tooltip-arrow-size: .5em;
-        --gr-tooltip-arrow-center-offset: 0;
+  <style include="shared-styles">
+    :host {
+      --gr-tooltip-arrow-size: 0.5em;
+      --gr-tooltip-arrow-center-offset: 0;
 
-        background-color: var(--tooltip-background-color);
-        box-shadow: var(--elevation-level-2);
-        color: var(--tooltip-text-color);
-        font-size: var(--font-size-small);
-        position: absolute;
-        z-index: 1000;
-        max-width: var(--tooltip-max-width);
-      }
-      :host .tooltip {
-        padding: var(--spacing-m) var(--spacing-l);
-      }
-      :host .arrowPositionBelow,
-      :host([position-below]) .arrowPositionAbove  {
-        display: none;
-      }
-      :host([position-below]) .arrowPositionBelow {
-        display: initial;
-      }
-      .arrow {
-        border-left: var(--gr-tooltip-arrow-size) solid transparent;
-        border-right: var(--gr-tooltip-arrow-size) solid transparent;
-        height: 0;
-        position: absolute;
-        left: calc(50% - var(--gr-tooltip-arrow-size));
-        margin-left: var(--gr-tooltip-arrow-center-offset);
-        width: 0;
-      }
-      .arrowPositionAbove {
-        border-top: var(--gr-tooltip-arrow-size) solid var(--tooltip-background-color);
-        bottom: calc(-1 * var(--gr-tooltip-arrow-size));
-      }
-      .arrowPositionBelow {
-        border-bottom: var(--gr-tooltip-arrow-size) solid var(--tooltip-background-color);
-        top: calc(-1 * var(--gr-tooltip-arrow-size));
-      }
-    </style>
-    <div class="tooltip">
-      <i class="arrowPositionBelow arrow"></i>
-      [[text]]
-      <i class="arrowPositionAbove arrow"></i>
-    </div>
+      background-color: var(--tooltip-background-color);
+      box-shadow: var(--elevation-level-2);
+      color: var(--tooltip-text-color);
+      font-size: var(--font-size-small);
+      position: absolute;
+      z-index: 1000;
+      max-width: var(--tooltip-max-width);
+    }
+    :host .tooltip {
+      padding: var(--spacing-m) var(--spacing-l);
+    }
+    :host .arrowPositionBelow,
+    :host([position-below]) .arrowPositionAbove {
+      display: none;
+    }
+    :host([position-below]) .arrowPositionBelow {
+      display: initial;
+    }
+    .arrow {
+      border-left: var(--gr-tooltip-arrow-size) solid transparent;
+      border-right: var(--gr-tooltip-arrow-size) solid transparent;
+      height: 0;
+      position: absolute;
+      left: calc(50% - var(--gr-tooltip-arrow-size));
+      margin-left: var(--gr-tooltip-arrow-center-offset);
+      width: 0;
+    }
+    .arrowPositionAbove {
+      border-top: var(--gr-tooltip-arrow-size) solid
+        var(--tooltip-background-color);
+      bottom: calc(-1 * var(--gr-tooltip-arrow-size));
+    }
+    .arrowPositionBelow {
+      border-bottom: var(--gr-tooltip-arrow-size) solid
+        var(--tooltip-background-color);
+      top: calc(-1 * var(--gr-tooltip-arrow-size));
+    }
+  </style>
+  <div class="tooltip">
+    <i class="arrowPositionBelow arrow"></i>
+    [[text]]
+    <i class="arrowPositionAbove arrow"></i>
+  </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
index 7d599b23..b69d945 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
@@ -16,6 +16,7 @@
 limitations under the License.
 -->
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-storage</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html b/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html
index 4eb9337..2d89b30 100644
--- a/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html
+++ b/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>revision-info</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/embed/app-context-init.js b/polygerrit-ui/app/embed/gr-diff-app-context-init.js
similarity index 75%
rename from polygerrit-ui/app/embed/app-context-init.js
rename to polygerrit-ui/app/embed/gr-diff-app-context-init.js
index 55a5866..8a7fb66 100644
--- a/polygerrit-ui/app/embed/app-context-init.js
+++ b/polygerrit-ui/app/embed/gr-diff-app-context-init.js
@@ -16,6 +16,7 @@
  */
 
 import {appContext} from '../services/app-context.js';
+import {grReportingMock} from '../services/gr-reporting/gr-reporting_mock.js';
 
 class MockFlagsService {
   isEnabled(experimentId) {
@@ -34,5 +35,13 @@
 // This is a temporary solution
 // TODO(dmfilippov): find a better solution for gr-diff
 export function initDiffAppContext() {
-  appContext.flagsService = new MockFlagsService();
+  function setMock(serviceName, setupMock) {
+    Object.defineProperty(appContext, serviceName, {
+      get() {
+        return setupMock;
+      },
+    });
+  }
+  setMock('flagsService', new MockFlagsService);
+  setMock('reportingService', grReportingMock);
 }
diff --git a/polygerrit-ui/app/embed/gr-diff-app-context-init_test.html b/polygerrit-ui/app/embed/gr-diff-app-context-init_test.html
new file mode 100644
index 0000000..3aed13f
--- /dev/null
+++ b/polygerrit-ui/app/embed/gr-diff-app-context-init_test.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2020 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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/components/wct-browser-legacy/browser.js"></script>
+
+<script type="module">
+  import '../test/common-test-setup.js';
+  import {appContext} from '../services/app-context.js';
+  import {initDiffAppContext} from './gr-diff-app-context-init.js';
+  suite('gr diff app context initializer tests', () => {
+    setup(() => {
+      initDiffAppContext();
+    });
+
+    test('all services initialized and are singletons', () => {
+      Object.keys(appContext).forEach(serviceName => {
+        const service = appContext[serviceName];
+        assert.isNotNull(service);
+        const service2 = appContext[serviceName];
+        assert.strictEqual(service, service2);
+      });
+    });
+  });
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/embed/gr-diff.js b/polygerrit-ui/app/embed/gr-diff.js
index a8b7e03..5907842 100644
--- a/polygerrit-ui/app/embed/gr-diff.js
+++ b/polygerrit-ui/app/embed/gr-diff.js
@@ -16,9 +16,16 @@
  */
 
 window.Gerrit = window.Gerrit || {};
+// TODO(dmfilippov): remove bundled-polymer.js imports when the following issue
+// https://github.com/Polymer/polymer-resin/issues/9 is resolved.
+// Because gr-diff.js is a shared component, it shouldn' pollute global
+// variables. If an application wants to use Polymer global variable -
+// the app must assign/import it and do not rely on the Polymer variable
+// exposed by shared gr-diff component.
+import '../scripts/bundled-polymer.js';
 import '../elements/diff/gr-diff/gr-diff.js';
 import '../elements/diff/gr-diff-cursor/gr-diff-cursor.js';
-import {initDiffAppContext} from './app-context-init.js';
+import {initDiffAppContext} from './gr-diff-app-context-init.js';
 import {GrDiffLine} from '../elements/diff/gr-diff/gr-diff-line.js';
 import {GrAnnotation} from '../elements/diff/gr-diff-highlight/gr-annotation.js';
 
diff --git a/polygerrit-ui/app/scripts/bundled-polymer.js b/polygerrit-ui/app/scripts/bundled-polymer.js
index 711d587..780d82a 100644
--- a/polygerrit-ui/app/scripts/bundled-polymer.js
+++ b/polygerrit-ui/app/scripts/bundled-polymer.js
@@ -17,9 +17,9 @@
 
 // This file is a replacement for the
 // polymer-bridges/polymer/polymer.html file. The polymer.html file loads
-// other scripts to setup different global variables. Because polygerrit
-// code still uses global variables (like Polymer.importHref and other),
-// we must setup this global variables after conversion to es6 modules.
+// other scripts to setup different global variables. Because plugins
+// expects that Polymer is available we must setup all Polymer global
+// variables
 //
 // The bundled-polymer.js imports all scripts in the same order as the
 // polymer.html does and must be imported in all es6-modules instead
@@ -68,4 +68,4 @@
 import 'polymer-bridges/polymer/polymer-legacy_bridge.js';
 import {importHref} from './import-href.js';
 
-Polymer.importHref = importHref;
+window.Polymer.importHref = importHref;
diff --git a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html
index 01b36e9..818ddaa 100644
--- a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html
+++ b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-display-name-utils</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
index 979fe80..80d3590 100644
--- a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
+++ b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-email-suggestions-provider</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
index 5bd3fbd..2111b7e 100644
--- a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-group-suggestions-provider</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
index 7bcecb6..8774d48 100644
--- a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
+++ b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <title>gr-reviewer-suggestions-provider</title>
 
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
diff --git a/polygerrit-ui/app/scripts/util_test.html b/polygerrit-ui/app/scripts/util_test.html
index 9409519..a3893d2 100644
--- a/polygerrit-ui/app/scripts/util_test.html
+++ b/polygerrit-ui/app/scripts/util_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
 <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
 <script src="/components/wct-browser-legacy/browser.js"></script>
diff --git a/polygerrit-ui/app/services/app-context-init.js b/polygerrit-ui/app/services/app-context-init.js
index 1c32eee..8608615 100644
--- a/polygerrit-ui/app/services/app-context-init.js
+++ b/polygerrit-ui/app/services/app-context-init.js
@@ -16,6 +16,7 @@
  */
 import {appContext} from './app-context.js';
 import {FlagsService} from './flags.js';
+import {GrReporting} from './gr-reporting/gr-reporting.js';
 
 const initializedServices = new Map();
 
@@ -43,6 +44,8 @@
   }
 
   addService('flagsService', () => new FlagsService());
+  addService('reportingService',
+      () => new GrReporting(appContext.flagsService));
 
   Object.defineProperties(appContext, registeredServices);
 }
diff --git a/polygerrit-ui/app/services/app-context-init_test.html b/polygerrit-ui/app/services/app-context-init_test.html
index 8ee78ba..f5dc7d1 100644
--- a/polygerrit-ui/app/services/app-context-init_test.html
+++ b/polygerrit-ui/app/services/app-context-init_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
 <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
 <script src="/components/wct-browser-legacy/browser.js"></script>
diff --git a/polygerrit-ui/app/services/app-context.js b/polygerrit-ui/app/services/app-context.js
index e10ced5..62b396d 100644
--- a/polygerrit-ui/app/services/app-context.js
+++ b/polygerrit-ui/app/services/app-context.js
@@ -23,4 +23,5 @@
  */
 export const appContext = {
   flagsService: null,
+  reportingService: null,
 };
\ No newline at end of file
diff --git a/polygerrit-ui/app/services/flags.js b/polygerrit-ui/app/services/flags.js
index 8f04f4a..9953afc 100644
--- a/polygerrit-ui/app/services/flags.js
+++ b/polygerrit-ui/app/services/flags.js
@@ -16,6 +16,14 @@
  */
 
 /**
+ * @enum
+ * @desc Experiment ids used in Gerrit.
+ */
+export const ExperimentIds = {
+  CLEANER_CHANGELOG: 'UiFeature__cleaner_changelog',
+};
+
+/**
  * Flags service.
  *
  * Provides all related methods / properties regarding on feature flags.
diff --git a/polygerrit-ui/app/services/flags_test.html b/polygerrit-ui/app/services/flags_test.html
index ff94494..51efb0d 100644
--- a/polygerrit-ui/app/services/flags_test.html
+++ b/polygerrit-ui/app/services/flags_test.html
@@ -17,6 +17,7 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
 <script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
 <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
 <script src="/components/wct-browser-legacy/browser.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/services/gr-reporting/gr-reporting.js
similarity index 71%
rename from polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
rename to polygerrit-ui/app/services/gr-reporting/gr-reporting.js
index c8b4ff5..732b004 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting.js
@@ -14,49 +14,53 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../scripts/bundled-polymer.js';
-
-import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
-import {appContext} from '../../../services/app-context.js';
 
 // Latency reporting constants.
+
 const TIMING = {
   TYPE: 'timing-report',
-  CATEGORY_UI_LATENCY: 'UI Latency',
-  CATEGORY_RPC: 'RPC Timing',
-  // Reported events - alphabetize below.
-  APP_STARTED: 'App Started',
+  CATEGORY: {
+    UI_LATENCY: 'UI Latency',
+    RPC: 'RPC Timing',
+  },
+  EVENT: {
+    APP_STARTED: 'App Started',
+  },
 };
 
-// Plugin-related reporting constants.
-const PLUGINS = {
+const LIFECYCLE = {
   TYPE: 'lifecycle',
-  // Reported events - alphabetize below.
-  INSTALLED: 'Plugins installed',
+  CATEGORY: {
+    DEFAULT: 'Default',
+    EXTENSION_DETECTED: 'Extension detected',
+    PLUGINS_INSTALLED: 'Plugins installed',
+  },
 };
 
-// Chrome extension-related reporting constants.
-const EXTENSION = {
-  TYPE: 'lifecycle',
-  // Reported events - alphabetize below.
-  DETECTED: 'Extension detected',
+const INTERACTION = {
+  TYPE: 'interaction',
+  CATEGORY: {
+    DEFAULT: 'Default',
+    VISIBILITY: 'Visibility',
+  },
 };
 
-// Navigation reporting constants.
 const NAVIGATION = {
   TYPE: 'nav-report',
-  CATEGORY: 'Location Changed',
-  PAGE: 'Page',
+  CATEGORY: {
+    LOCATION_CHANGED: 'Location Changed',
+  },
+  EVENT: {
+    PAGE: 'Page',
+  },
 };
 
 const ERROR = {
   TYPE: 'error',
-  CATEGORY: 'exception',
-};
-
-const ERROR_DIALOG = {
-  TYPE: 'error',
-  CATEGORY: 'Error Dialog',
+  CATEGORY: {
+    EXCEPTION: 'exception',
+    ERROR_DIALOG: 'Error Dialog',
+  },
 };
 
 const TIMER = {
@@ -89,125 +93,125 @@
 STARTUP_TIMERS[TIMER.STARTUP_DIFF_VIEW_DISPLAYED] = 0;
 STARTUP_TIMERS[TIMER.STARTUP_DIFF_VIEW_LOAD_FULL] = 0;
 STARTUP_TIMERS[TIMER.STARTUP_FILE_LIST_DISPLAYED] = 0;
-STARTUP_TIMERS[TIMING.APP_STARTED] = 0;
+STARTUP_TIMERS[TIMING.EVENT.APP_STARTED] = 0;
 // WebComponentsReady timer is triggered from gr-router.
 STARTUP_TIMERS[TIMER.WEB_COMPONENTS_READY] = 0;
 
-const INTERACTION_TYPE = 'interaction';
-
 const DRAFT_ACTION_TIMER = 'TimeBetweenDraftActions';
 const DRAFT_ACTION_TIMER_MAX = 2 * 60 * 1000; // 2 minutes.
-
-let pending = [];
-let slowRpcList = [];
 const SLOW_RPC_THRESHOLD = 500;
 
-// Variables that hold context info in global scope
-let reportRepoName = undefined;
-
-const onError = function(oldOnError, msg, url, line, column, error) {
-  if (oldOnError) {
-    oldOnError(msg, url, line, column, error);
-  }
-  if (error) {
-    line = line || error.lineNumber;
-    column = column || error.columnNumber;
-    let shortenedErrorStack = msg;
-    if (error.stack) {
-      const errorStackLines = error.stack.split('\n');
-      shortenedErrorStack = errorStackLines.slice(0,
-          Math.min(3, errorStackLines.length)).join('\n');
+export function initErrorReporter(appContext) {
+  const reportingService = appContext.reportingService;
+  const onError = function(oldOnError, msg, url, line, column, error) {
+    if (oldOnError) {
+      oldOnError(msg, url, line, column, error);
     }
-    msg = shortenedErrorStack || error.toString();
-  }
-  const payload = {
-    url,
-    line,
-    column,
-    error,
-  };
-  GrReporting.prototype.reporter(ERROR.TYPE, ERROR.CATEGORY, msg, payload);
-  return true;
-};
-
-const catchErrors = function(opt_context) {
-  const context = opt_context || window;
-  context.onerror = onError.bind(null, context.onerror);
-  context.addEventListener('unhandledrejection', e => {
-    const msg = e.reason.message;
-    const payload = {
-      error: e.reason,
-    };
-    GrReporting.prototype.reporter(ERROR.TYPE, ERROR.CATEGORY, msg, payload);
-  });
-};
-catchErrors();
-
-// PerformanceObserver interface is a browser API.
-if (window.PerformanceObserver) {
-  const supportedEntryTypes = PerformanceObserver.supportedEntryTypes || [];
-  // Safari doesn't support longtask yet
-  if (supportedEntryTypes.includes('longtask')) {
-    const catchLongJsTasks = new PerformanceObserver(list => {
-      for (const task of list.getEntries()) {
-        // We are interested in longtask longer than 200 ms (default is 50 ms)
-        if (task.duration > 200) {
-          GrReporting.prototype.reporter(TIMING.TYPE,
-              TIMING.CATEGORY_UI_LATENCY, `Task ${task.name}`,
-              Math.round(task.duration), {}, false);
-        }
+    if (error) {
+      line = line || error.lineNumber;
+      column = column || error.columnNumber;
+      let shortenedErrorStack = msg;
+      if (error.stack) {
+        const errorStackLines = error.stack.split('\n');
+        shortenedErrorStack = errorStackLines.slice(0,
+            Math.min(3, errorStackLines.length)).join('\n');
       }
+      msg = shortenedErrorStack || error.toString();
+    }
+    const payload = {
+      url,
+      line,
+      column,
+      error,
+    };
+    reportingService.reporter(ERROR.TYPE, ERROR.CATEGORY.EXCEPTION,
+        msg, payload);
+    return true;
+  };
+
+  const catchErrors = function(opt_context) {
+    const context = opt_context || window;
+    context.onerror = onError.bind(null, context.onerror);
+    context.addEventListener('unhandledrejection', e => {
+      const msg = e.reason.message;
+      const payload = {
+        error: e.reason,
+      };
+      reportingService.reporter(ERROR.TYPE,
+          ERROR.CATEGORY.EXCEPTION, msg, payload);
     });
-    catchLongJsTasks.observe({entryTypes: ['longtask']});
+  };
+
+  catchErrors();
+
+  // for testing
+  return {catchErrors};
+}
+
+export function initPerformanceReporter(appContext) {
+  const reportingService = appContext.reportingService;
+  // PerformanceObserver interface is a browser API.
+  if (window.PerformanceObserver) {
+    const supportedEntryTypes = PerformanceObserver.supportedEntryTypes || [];
+    // Safari doesn't support longtask yet
+    if (supportedEntryTypes.includes('longtask')) {
+      const catchLongJsTasks = new PerformanceObserver(list => {
+        for (const task of list.getEntries()) {
+          // We are interested in longtask longer than 200 ms (default is 50 ms)
+          if (task.duration > 200) {
+            reportingService.reporter(TIMING.TYPE,
+                TIMING.CATEGORY.UI_LATENCY, `Task ${task.name}`,
+                Math.round(task.duration), {}, false);
+          }
+        }
+      });
+      catchLongJsTasks.observe({entryTypes: ['longtask']});
+    }
   }
 }
 
-document.addEventListener('visibilitychange', () => {
-  const eventName = `Visibility changed to ${document.visibilityState}`;
-  GrReporting.prototype.reporter(INTERACTION_TYPE, undefined, eventName,
-      undefined, {}, true);
-});
+export function initVisibilityReporter(appContext) {
+  const reportingService = appContext.reportingService;
+  document.addEventListener('visibilitychange', () => {
+    const eventName = `Visibility changed to ${document.visibilityState}`;
+    reportingService.reporter(LIFECYCLE.TYPE, LIFECYCLE.CATEGORY.VISIBILITY,
+        eventName, undefined, {}, true);
+  });
+}
 
-// The Polymer pass of JSCompiler requires this to be reassignable
-// eslint-disable-next-line prefer-const
-let GrReporting = Polymer({
-  is: 'gr-reporting',
-
-  properties: {
-    category: String,
-
-    _baselines: {
-      type: Object,
-      value: STARTUP_TIMERS, // Shared across all instances.
-    },
-
-    _timers: {
-      type: Object,
-      value: {timeBetweenDraftActions: null}, // Shared across all instances.
-    },
-  },
+export class GrReporting {
+  constructor(flagsService) {
+    this._flagsService = flagsService;
+    this._baselines = STARTUP_TIMERS;
+    this._timers = {
+      timeBetweenDraftActions: null,
+    };
+    this._reportRepoName = undefined;
+    this._pending = [];
+    this._slowRpcList = [];
+  }
 
   get performanceTiming() {
     return window.performance.timing;
-  },
+  }
 
   get slowRpcSnapshot() {
-    return slowRpcList.slice();
-  },
+    return (this._slowRpcList || []).slice();
+  }
 
   now() {
     return Math.round(window.performance.now());
-  },
+  }
 
   _arePluginsLoaded() {
     return this._baselines &&
       !this._baselines.hasOwnProperty(TIMER.PLUGINS_LOADED);
-  },
+  }
 
   _isMetricsPluginLoaded() {
     return this._arePluginsLoaded() || this._baselines &&
       !this._baselines.hasOwnProperty(TIMER.METRICS_PLUGIN_LOADED);
-  },
+  }
 
   /**
    * Reporter reports events. Events will be queued if metrics plugin is not
@@ -224,24 +228,24 @@
   reporter(type, category, eventName, eventValue, eventDetails, opt_noLog) {
     const eventInfo = this._createEventInfo(type, category,
         eventName, eventValue, eventDetails);
-    if (type === ERROR.TYPE && category === ERROR.CATEGORY) {
+    if (type === ERROR.TYPE && category === ERROR.CATEGORY.EXCEPTION) {
       console.error(eventValue && eventValue.error || eventName);
     }
 
     // We report events immediately when metrics plugin is loaded
-    if (this._isMetricsPluginLoaded() && !pending.length) {
+    if (this._isMetricsPluginLoaded() && !this._pending.length) {
       this._reportEvent(eventInfo, opt_noLog);
     } else {
       // We cache until metrics plugin is loaded
-      pending.push([eventInfo, opt_noLog]);
+      this._pending.push([eventInfo, opt_noLog]);
       if (this._isMetricsPluginLoaded()) {
-        pending.forEach(([eventInfo, opt_noLog]) => {
+        this._pending.forEach(([eventInfo, opt_noLog]) => {
           this._reportEvent(eventInfo, opt_noLog);
         });
-        pending = [];
+        this._pending = [];
       }
     }
-  },
+  }
 
   _reportEvent(eventInfo, opt_noLog) {
     const {type, value, name} = eventInfo;
@@ -254,7 +258,7 @@
         console.log(`Reporting: ${name}`);
       }
     }
-  },
+  }
 
   _createEventInfo(type, category, name, value, eventDetails) {
     const eventInfo = {
@@ -270,8 +274,8 @@
       eventInfo.eventDetails = JSON.stringify(eventDetails);
     }
 
-    if (reportRepoName) {
-      eventInfo.repoName = reportRepoName;
+    if (this._reportRepoName) {
+      eventInfo.repoName = this._reportRepoName;
     }
 
     const isInBackgroundTab = document.visibilityState === 'hidden';
@@ -279,21 +283,21 @@
       eventInfo.inBackgroundTab = isInBackgroundTab;
     }
 
-    const enabledExperiments = appContext.flagsService.enabledExperiments;
-    if (enabledExperiments.length) {
-      eventInfo.enabledExperiments = JSON.stringify(enabledExperiments);
+    if (this._flagsService.enabledExperiments.length) {
+      eventInfo.enabledExperiments =
+        JSON.stringify(this._flagsService.enabledExperiments);
     }
 
     return eventInfo;
-  },
+  }
 
   /**
    * User-perceived app start time, should be reported when the app is ready.
    */
   appStarted() {
-    this.timeEnd(TIMING.APP_STARTED);
+    this.timeEnd(TIMING.EVENT.APP_STARTED);
     this._reportNavResTimes();
-  },
+  }
 
   /**
    * Browser's navigation and resource timings
@@ -303,7 +307,7 @@
     perfEvents.forEach(
         eventName => this._reportPerformanceTiming(eventName)
     );
-  },
+  }
 
   _reportPerformanceTiming(eventName, eventDetails) {
     const eventTiming = this.performanceTiming[eventName];
@@ -311,10 +315,10 @@
       const elapsedTime = eventTiming -
           this.performanceTiming.navigationStart;
       // NavResTime - Navigation and resource timings.
-      this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
+      this.reporter(TIMING.TYPE, TIMING.CATEGORY.UI_LATENCY,
           `NavResTime - ${eventName}`, elapsedTime, eventDetails, true);
     }
-  },
+  }
 
   beforeLocationChanged() {
     for (const prop of Object.keys(this._baselines)) {
@@ -327,15 +331,15 @@
     this.time(TIMER.DIFF_VIEW_DISPLAYED);
     this.time(TIMER.DIFF_VIEW_LOAD_FULL);
     this.time(TIMER.FILE_LIST_DISPLAYED);
-    reportRepoName = undefined;
+    this._reportRepoName = undefined;
     // reset slow rpc list since here start page loads which report these rpcs
-    slowRpcList = [];
-  },
+    this._slowRpcList = [];
+  }
 
   locationChanged(page) {
-    this.reporter(
-        NAVIGATION.TYPE, NAVIGATION.CATEGORY, NAVIGATION.PAGE, page);
-  },
+    this.reporter(NAVIGATION.TYPE, NAVIGATION.CATEGORY.LOCATION_CHANGED,
+        NAVIGATION.EVENT.PAGE, page);
+  }
 
   dashboardDisplayed() {
     if (this._baselines.hasOwnProperty(TIMER.STARTUP_DASHBOARD_DISPLAYED)) {
@@ -343,7 +347,7 @@
     } else {
       this.timeEnd(TIMER.DASHBOARD_DISPLAYED, this._pageLoadDetails());
     }
-  },
+  }
 
   changeDisplayed() {
     if (this._baselines.hasOwnProperty(TIMER.STARTUP_CHANGE_DISPLAYED)) {
@@ -351,7 +355,7 @@
     } else {
       this.timeEnd(TIMER.CHANGE_DISPLAYED, this._pageLoadDetails());
     }
-  },
+  }
 
   changeFullyLoaded() {
     if (this._baselines.hasOwnProperty(TIMER.STARTUP_CHANGE_LOAD_FULL)) {
@@ -359,7 +363,7 @@
     } else {
       this.timeEnd(TIMER.CHANGE_LOAD_FULL);
     }
-  },
+  }
 
   diffViewDisplayed() {
     if (this._baselines.hasOwnProperty(TIMER.STARTUP_DIFF_VIEW_DISPLAYED)) {
@@ -367,7 +371,7 @@
     } else {
       this.timeEnd(TIMER.DIFF_VIEW_DISPLAYED, this._pageLoadDetails());
     }
-  },
+  }
 
   diffViewFullyLoaded() {
     if (this._baselines.hasOwnProperty(TIMER.STARTUP_DIFF_VIEW_LOAD_FULL)) {
@@ -375,7 +379,7 @@
     } else {
       this.timeEnd(TIMER.DIFF_VIEW_LOAD_FULL);
     }
-  },
+  }
 
   diffViewContentDisplayed() {
     if (this._baselines.hasOwnProperty(
@@ -384,7 +388,7 @@
     } else {
       this.timeEnd(TIMER.DIFF_VIEW_CONTENT_DISPLAYED);
     }
-  },
+  }
 
   fileListDisplayed() {
     if (this._baselines.hasOwnProperty(TIMER.STARTUP_FILE_LIST_DISPLAYED)) {
@@ -392,7 +396,7 @@
     } else {
       this.timeEnd(TIMER.FILE_LIST_DISPLAYED);
     }
-  },
+  }
 
   _pageLoadDetails() {
     const details = {
@@ -420,24 +424,25 @@
     }
 
     return details;
-  },
+  }
 
   reportExtension(name) {
-    this.reporter(EXTENSION.TYPE, EXTENSION.DETECTED, name);
-  },
+    this.reporter(LIFECYCLE.TYPE, LIFECYCLE.CATEGORY.EXTENSION_DETECTED, name);
+  }
 
   pluginLoaded(name) {
     if (name.startsWith('metrics-')) {
       this.timeEnd(TIMER.METRICS_PLUGIN_LOADED);
     }
-  },
+  }
 
   pluginsLoaded(pluginsList) {
     this.timeEnd(TIMER.PLUGINS_LOADED);
     this.reporter(
-        PLUGINS.TYPE, PLUGINS.INSTALLED, PLUGINS.INSTALLED, undefined,
+        LIFECYCLE.TYPE, LIFECYCLE.CATEGORY.PLUGINS_INSTALLED,
+        LIFECYCLE.CATEGORY.PLUGINS_INSTALLED, undefined,
         {pluginsList: pluginsList || []}, true);
-  },
+  }
 
   /**
    * Reset named timer.
@@ -445,7 +450,7 @@
   time(name) {
     this._baselines[name] = this.now();
     window.performance.mark(`${name}-start`);
-  },
+  }
 
   /**
    * Finish named timer and report it to server.
@@ -465,7 +470,7 @@
       // (if undefined).
       window.performance.measure(name);
     }
-  },
+  }
 
   /**
    * Reports just line timeEnd, but additionally reports an average given a
@@ -485,7 +490,7 @@
     if (!denominator) { return; }
     const time = this.now() - baseTime;
     this._reportTiming(averageName, time / denominator);
-  },
+  }
 
   /**
    * Send a timing report with an arbitrary time value.
@@ -495,9 +500,9 @@
    * @param {Object} eventDetails non sensitive details
    */
   _reportTiming(name, time, eventDetails) {
-    this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY, name, time,
+    this.reporter(TIMING.TYPE, TIMING.CATEGORY.UI_LATENCY, name, time,
         eventDetails);
-  },
+  }
 
   /**
    * Get a timer object to for reporing a user timing. The start time will be
@@ -546,7 +551,7 @@
 
     // The timer is initialized to its creation time.
     return timer.reset();
-  },
+  }
 
   /**
    * Log timing information for an RPC.
@@ -555,17 +560,22 @@
    * @param {number} elapsed The time elapsed of the RPC.
    */
   reportRpcTiming(anonymizedUrl, elapsed) {
-    this.reporter(TIMING.TYPE, TIMING.CATEGORY_RPC, 'RPC-' + anonymizedUrl,
+    this.reporter(TIMING.TYPE, TIMING.CATEGORY.RPC, 'RPC-' + anonymizedUrl,
         elapsed, {}, true);
     if (elapsed >= SLOW_RPC_THRESHOLD) {
-      slowRpcList.push({anonymizedUrl, elapsed});
+      this._slowRpcList.push({anonymizedUrl, elapsed});
     }
-  },
+  }
+
+  reportLifeCycle(eventName, details) {
+    this.reporter(LIFECYCLE.TYPE, LIFECYCLE.CATEGORY.DEFAULT, eventName,
+        undefined, details, true);
+  }
 
   reportInteraction(eventName, details) {
-    this.reporter(INTERACTION_TYPE, this.category, eventName, undefined,
-        details, true);
-  },
+    this.reporter(INTERACTION.TYPE, INTERACTION.CATEGORY.DEFAULT, eventName,
+        undefined, details, true);
+  }
 
   /**
    * A draft interaction was started. Update the time-betweeen-draft-actions
@@ -585,19 +595,16 @@
 
     // Mark the time and reinitialize the timer.
     timer.end().reset();
-  },
+  }
 
   reportErrorDialog(message) {
-    this.reporter(ERROR_DIALOG.TYPE, ERROR_DIALOG.CATEGORY,
+    this.reporter(ERROR.TYPE, ERROR.CATEGORY.ERROR_DIALOG,
         'ErrorDialog: ' + message, {error: new Error(message)});
-  },
+  }
 
   setRepoName(repoName) {
-    reportRepoName = repoName;
-  },
-});
+    this._reportRepoName = repoName;
+  }
+}
 
-window.GrReporting = GrReporting;
-// Expose onerror installation so it would be accessible from tests.
-window.GrReporting._catchErrors = catchErrors;
-window.GrReporting.STARTUP_TIMERS = Object.assign({}, STARTUP_TIMERS);
+export const DEFAULT_STARTUP_TIMERS = Object.assign({}, STARTUP_TIMERS);
diff --git a/polygerrit-ui/app/services/gr-reporting/gr-reporting_mock.js b/polygerrit-ui/app/services/gr-reporting/gr-reporting_mock.js
new file mode 100644
index 0000000..158b9d2
--- /dev/null
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting_mock.js
@@ -0,0 +1,45 @@
+/**
+ * @license
+ * Copyright (C) 2020 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.
+ */
+export const grReportingMock = {
+  reportInteraction: () => {},
+  reportLifeCycle: () => {},
+  appStarted: () => {},
+  dashboardDisplayed: () => {},
+  changeDisplayed: () => {},
+  changeFullyLoaded: () => {},
+  fileListDisplayed: () => {},
+  diffViewContentDisplayed: () => {},
+  diffViewFullyLoaded: () => {},
+  diffViewDisplayed: () => {},
+  recordDraftInteraction: () => {},
+  now: () => {},
+  time: () => {},
+  timeEnd: () => {},
+  pluginLoaded: () => {},
+  pluginsLoaded: () => {},
+  timeEndWithAverage: () => {},
+  beforeLocationChanged: () => {},
+  locationChanged: () => {},
+  setRepoName: () => {},
+  reporter: () => {},
+  reportErrorDialog: () => {},
+  reportRpcTiming: () => {},
+  reportExtension: () => {},
+  getTimer: () => {
+    return {end: () => {}};
+  },
+};
diff --git a/polygerrit-ui/app/services/gr-reporting/gr-reporting_mock_test.html b/polygerrit-ui/app/services/gr-reporting/gr-reporting_mock_test.html
new file mode 100644
index 0000000..e33a214
--- /dev/null
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting_mock_test.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2020 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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/components/wct-browser-legacy/browser.js"></script>
+
+<script type="module">
+  import '../../test/common-test-setup.js';
+  import {GrReporting} from './gr-reporting.js';
+  import {grReportingMock} from './gr-reporting_mock.js';
+  suite('gr-reporting_mock tests', () => {
+    test('mocks all public methods', () => {
+      const methods = Object.getOwnPropertyNames(GrReporting.prototype)
+          .filter(name => typeof GrReporting.prototype[name] === 'function')
+          .filter(name => !name.startsWith('_') && name !== 'constructor')
+          .sort();
+      const mockMethods = Object.getOwnPropertyNames(grReportingMock)
+          .sort();
+      assert.deepEqual(methods, mockMethods);
+    });
+  });
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.html
new file mode 100644
index 0000000..c44cae1
--- /dev/null
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting_test.html
@@ -0,0 +1,438 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2016 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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<meta charset="utf-8">
+<title>gr-reporting</title>
+
+<script src="/node_modules/@webcomponents/webcomponentsjs/custom-services-es5-adapter.js"></script>
+
+<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/components/wct-browser-legacy/browser.js"></script>
+
+<script type="module">
+import '../../test/common-test-setup.js';
+import {GrReporting, DEFAULT_STARTUP_TIMERS, initErrorReporter} from './gr-reporting.js';
+import {appContext} from '../app-context.js';
+suite('gr-reporting tests', () => {
+  let service;
+  let sandbox;
+  let clock;
+  let fakePerformance;
+
+  const NOW_TIME = 100;
+
+  setup(() => {
+    sandbox = sinon.sandbox.create();
+    clock = sinon.useFakeTimers(NOW_TIME);
+    service = new GrReporting(appContext.flagsService);
+    service._baselines = Object.assign({}, DEFAULT_STARTUP_TIMERS);
+    sandbox.stub(service, 'reporter');
+  });
+
+  teardown(() => {
+    sandbox.restore();
+    clock.restore();
+  });
+
+  test('appStarted', () => {
+    fakePerformance = {
+      navigationStart: 1,
+      loadEventEnd: 2,
+    };
+    fakePerformance.toJSON = () => fakePerformance;
+    sinon.stub(service, 'performanceTiming',
+        {get() { return fakePerformance; }});
+    sandbox.stub(service, 'now').returns(42);
+    service.appStarted();
+    assert.isTrue(
+        service.reporter.calledWithMatch(
+            'timing-report', 'UI Latency', 'App Started', 42
+        ));
+    assert.isTrue(
+        service.reporter.calledWithExactly(
+            'timing-report', 'UI Latency', 'NavResTime - loadEventEnd',
+            fakePerformance.loadEventEnd - fakePerformance.navigationStart,
+            undefined, true)
+    );
+  });
+
+  test('WebComponentsReady', () => {
+    sandbox.stub(service, 'now').returns(42);
+    service.timeEnd('WebComponentsReady');
+    assert.isTrue(service.reporter.calledWithMatch(
+        'timing-report', 'UI Latency', 'WebComponentsReady', 42
+    ));
+  });
+
+  test('beforeLocationChanged', () => {
+    service._baselines['garbage'] = 'monster';
+    sandbox.stub(service, 'time');
+    service.beforeLocationChanged();
+    assert.isTrue(service.time.calledWithExactly('DashboardDisplayed'));
+    assert.isTrue(service.time.calledWithExactly('ChangeDisplayed'));
+    assert.isTrue(service.time.calledWithExactly('ChangeFullyLoaded'));
+    assert.isTrue(service.time.calledWithExactly('DiffViewDisplayed'));
+    assert.isTrue(service.time.calledWithExactly('FileListDisplayed'));
+    assert.isFalse(service._baselines.hasOwnProperty('garbage'));
+  });
+
+  test('changeDisplayed', () => {
+    sandbox.spy(service, 'timeEnd');
+    service.changeDisplayed();
+    assert.isFalse(service.timeEnd.calledWith('ChangeDisplayed'));
+    assert.isTrue(service.timeEnd.calledWith('StartupChangeDisplayed'));
+    service.changeDisplayed();
+    assert.isTrue(service.timeEnd.calledWith('ChangeDisplayed'));
+  });
+
+  test('changeFullyLoaded', () => {
+    sandbox.spy(service, 'timeEnd');
+    service.changeFullyLoaded();
+    assert.isFalse(
+        service.timeEnd.calledWithExactly('ChangeFullyLoaded'));
+    assert.isTrue(
+        service.timeEnd.calledWithExactly('StartupChangeFullyLoaded'));
+    service.changeFullyLoaded();
+    assert.isTrue(service.timeEnd.calledWithExactly('ChangeFullyLoaded'));
+  });
+
+  test('diffViewDisplayed', () => {
+    sandbox.spy(service, 'timeEnd');
+    service.diffViewDisplayed();
+    assert.isFalse(service.timeEnd.calledWith('DiffViewDisplayed'));
+    assert.isTrue(service.timeEnd.calledWith('StartupDiffViewDisplayed'));
+    service.diffViewDisplayed();
+    assert.isTrue(service.timeEnd.calledWith('DiffViewDisplayed'));
+  });
+
+  test('fileListDisplayed', () => {
+    sandbox.spy(service, 'timeEnd');
+    service.fileListDisplayed();
+    assert.isFalse(
+        service.timeEnd.calledWithExactly('FileListDisplayed'));
+    assert.isTrue(
+        service.timeEnd.calledWithExactly('StartupFileListDisplayed'));
+    service.fileListDisplayed();
+    assert.isTrue(service.timeEnd.calledWithExactly('FileListDisplayed'));
+  });
+
+  test('dashboardDisplayed', () => {
+    sandbox.spy(service, 'timeEnd');
+    service.dashboardDisplayed();
+    assert.isFalse(service.timeEnd.calledWith('DashboardDisplayed'));
+    assert.isTrue(service.timeEnd.calledWith('StartupDashboardDisplayed'));
+    service.dashboardDisplayed();
+    assert.isTrue(service.timeEnd.calledWith('DashboardDisplayed'));
+  });
+
+  test('dashboardDisplayed details', () => {
+    sandbox.spy(service, 'timeEnd');
+    sandbox.stub(window, 'performance', {
+      memory: {
+        usedJSHeapSize: 1024 * 1024,
+      },
+      measure: () => {},
+    });
+    sandbox.stub(service, 'now').returns(42);
+    service.reportRpcTiming('/changes/*~*/comments', 500);
+    service.dashboardDisplayed();
+    assert.isTrue(
+        service.timeEnd.calledWithExactly('StartupDashboardDisplayed',
+            {rpcList: [
+              {
+                anonymizedUrl: '/changes/*~*/comments',
+                elapsed: 500,
+              },
+            ],
+            screenSize: {
+              width: window.screen.width,
+              height: window.screen.height,
+            },
+            viewport: {
+              width: document.documentElement.clientWidth,
+              height: document.documentElement.clientHeight,
+            },
+            usedJSHeapSizeMb: 1,
+            }
+        ));
+  });
+
+  test('time and timeEnd', () => {
+    const nowStub = sandbox.stub(service, 'now').returns(0);
+    service.time('foo');
+    nowStub.returns(1);
+    service.time('bar');
+    nowStub.returns(2);
+    service.timeEnd('bar');
+    nowStub.returns(3);
+    service.timeEnd('foo');
+    assert.isTrue(service.reporter.calledWithMatch(
+        'timing-report', 'UI Latency', 'foo', 3
+    ));
+    assert.isTrue(service.reporter.calledWithMatch(
+        'timing-report', 'UI Latency', 'bar', 1
+    ));
+  });
+
+  test('timer object', () => {
+    const nowStub = sandbox.stub(service, 'now').returns(100);
+    const timer = service.getTimer('foo-bar');
+    nowStub.returns(150);
+    timer.end();
+    assert.isTrue(service.reporter.calledWithMatch(
+        'timing-report', 'UI Latency', 'foo-bar', 50));
+  });
+
+  test('timer object double call', () => {
+    const timer = service.getTimer('foo-bar');
+    timer.end();
+    assert.isTrue(service.reporter.calledOnce);
+    assert.throws(() => {
+      timer.end();
+    }, 'Timer for "foo-bar" already ended.');
+  });
+
+  test('timer object maximum', () => {
+    const nowStub = sandbox.stub(service, 'now').returns(100);
+    const timer = service.getTimer('foo-bar').withMaximum(100);
+    nowStub.returns(150);
+    timer.end();
+    assert.isTrue(service.reporter.calledOnce);
+
+    timer.reset();
+    nowStub.returns(260);
+    timer.end();
+    assert.isTrue(service.reporter.calledOnce);
+  });
+
+  test('recordDraftInteraction', () => {
+    const key = 'TimeBetweenDraftActions';
+    const nowStub = sandbox.stub(service, 'now').returns(100);
+    const timingStub = sandbox.stub(service, '_reportTiming');
+    service.recordDraftInteraction();
+    assert.isFalse(timingStub.called);
+
+    nowStub.returns(200);
+    service.recordDraftInteraction();
+    assert.isTrue(timingStub.calledOnce);
+    assert.equal(timingStub.lastCall.args[0], key);
+    assert.equal(timingStub.lastCall.args[1], 100);
+
+    nowStub.returns(350);
+    service.recordDraftInteraction();
+    assert.isTrue(timingStub.calledTwice);
+    assert.equal(timingStub.lastCall.args[0], key);
+    assert.equal(timingStub.lastCall.args[1], 150);
+
+    nowStub.returns(370 + 2 * 60 * 1000);
+    service.recordDraftInteraction();
+    assert.isFalse(timingStub.calledThrice);
+  });
+
+  test('timeEndWithAverage', () => {
+    const nowStub = sandbox.stub(service, 'now').returns(0);
+    nowStub.returns(1000);
+    service.time('foo');
+    nowStub.returns(1100);
+    service.timeEndWithAverage('foo', 'bar', 10);
+    assert.isTrue(service.reporter.calledTwice);
+    assert.isTrue(service.reporter.calledWithMatch(
+        'timing-report', 'UI Latency', 'foo', 100));
+    assert.isTrue(service.reporter.calledWithMatch(
+        'timing-report', 'UI Latency', 'bar', 10));
+  });
+
+  test('reportExtension', () => {
+    service.reportExtension('foo');
+    assert.isTrue(service.reporter.calledWithExactly(
+        'lifecycle', 'Extension detected', 'foo'
+    ));
+  });
+
+  test('reportInteraction', () => {
+    service.reporter.restore();
+    sandbox.spy(service, '_reportEvent');
+    service.pluginsLoaded(); // so we don't cache
+    service.reportInteraction('button-click', {name: 'sendReply'});
+    assert.isTrue(service._reportEvent.getCall(2).calledWithMatch(
+        {
+          type: 'interaction',
+          name: 'button-click',
+          eventDetails: JSON.stringify({name: 'sendReply'}),
+        }
+    ));
+  });
+
+  test('report start time', () => {
+    service.reporter.restore();
+    sandbox.stub(service, 'now').returns(42);
+    sandbox.spy(service, '_reportEvent');
+    const dispatchStub = sandbox.spy(document, 'dispatchEvent');
+    service.pluginsLoaded();
+    service.time('timeAction');
+    service.timeEnd('timeAction');
+    assert.isTrue(service._reportEvent.getCall(2).calledWithMatch(
+        {
+          type: 'timing-report',
+          category: 'UI Latency',
+          name: 'timeAction',
+          value: 0,
+          eventStart: 42,
+        }
+    ));
+    assert.equal(dispatchStub.getCall(2).args[0].detail.eventStart, 42);
+  });
+
+  suite('plugins', () => {
+    setup(() => {
+      service.reporter.restore();
+      sandbox.stub(service, '_reportEvent');
+    });
+
+    test('pluginsLoaded reports time', () => {
+      sandbox.stub(service, 'now').returns(42);
+      service.pluginsLoaded();
+      assert.isTrue(service._reportEvent.calledWithMatch(
+          {
+            type: 'timing-report',
+            category: 'UI Latency',
+            name: 'PluginsLoaded',
+            value: 42,
+          }
+      ));
+    });
+
+    test('pluginsLoaded reports plugins', () => {
+      service.pluginsLoaded(['foo', 'bar']);
+      assert.isTrue(service._reportEvent.calledWithMatch(
+          {
+            type: 'lifecycle',
+            category: 'Plugins installed',
+            eventDetails: JSON.stringify({pluginsList: ['foo', 'bar']}),
+          }
+      ));
+    });
+
+    test('caches reports if plugins are not loaded', () => {
+      service.timeEnd('foo');
+      assert.isFalse(service._reportEvent.called);
+    });
+
+    test('reports if plugins are loaded', () => {
+      service.pluginsLoaded();
+      assert.isTrue(service._reportEvent.called);
+    });
+
+    test('reports if metrics plugin xyz is loaded', () => {
+      service.pluginLoaded('metrics-xyz');
+      assert.isTrue(service._reportEvent.called);
+    });
+
+    test('reports cached events preserving order', () => {
+      service.time('foo');
+      service.time('bar');
+      service.timeEnd('foo');
+      service.pluginsLoaded();
+      service.timeEnd('bar');
+      assert.isTrue(service._reportEvent.getCall(0).calledWithMatch(
+          {type: 'timing-report', category: 'UI Latency', name: 'foo'}
+      ));
+      assert.isTrue(service._reportEvent.getCall(1).calledWithMatch(
+          {type: 'timing-report', category: 'UI Latency',
+            name: 'PluginsLoaded'}
+      ));
+      assert.isTrue(service._reportEvent.getCall(2).calledWithMatch(
+          {type: 'lifecycle', category: 'Plugins installed'}
+      ));
+      assert.isTrue(service._reportEvent.getCall(3).calledWithMatch(
+          {type: 'timing-report', category: 'UI Latency', name: 'bar'}
+      ));
+    });
+  });
+
+  test('search', () => {
+    service.locationChanged('_handleSomeRoute');
+    assert.isTrue(service.reporter.calledWithExactly(
+        'nav-report', 'Location Changed', 'Page', '_handleSomeRoute'));
+  });
+
+  suite('exception logging', () => {
+    let fakeWindow;
+    let reporter;
+
+    const emulateThrow = function(msg, url, line, column, error) {
+      return fakeWindow.onerror(msg, url, line, column, error);
+    };
+
+    setup(() => {
+      reporter = service.reporter;
+      fakeWindow = {
+        handlers: {},
+        addEventListener(type, handler) {
+          this.handlers[type] = handler;
+        },
+      };
+      sandbox.stub(console, 'error');
+      Object.defineProperty(appContext, 'reportingService', {
+        get() {
+          return service;
+        },
+      });
+      const errorReporter = initErrorReporter(appContext);
+      errorReporter.catchErrors(fakeWindow);
+    });
+
+    test('is reported', () => {
+      const error = new Error('bar');
+      error.stack = undefined;
+      emulateThrow('bar', 'http://url', 4, 2, error);
+      assert.isTrue(reporter.calledWith('error', 'exception', 'bar'));
+      const payload = reporter.lastCall.args[3];
+      assert.deepEqual(payload, {
+        url: 'http://url',
+        line: 4,
+        column: 2,
+        error,
+      });
+    });
+
+    test('is reported with 3 lines of stack', () => {
+      const error = new Error('bar');
+      emulateThrow('bar', 'http://url', 4, 2, error);
+      const expectedStack = error.stack.split('\n').slice(0, 3)
+          .join('\n');
+      assert.isTrue(reporter.calledWith('error', 'exception',
+          expectedStack));
+    });
+
+    test('prevent default event handler', () => {
+      assert.isTrue(emulateThrow());
+    });
+
+    test('unhandled rejection', () => {
+      fakeWindow.handlers['unhandledrejection']({
+        reason: {
+          message: 'bar',
+        },
+      });
+      assert.isTrue(reporter.calledWith('error', 'exception', 'bar'));
+    });
+  });
+});
+</script>
diff --git a/polygerrit-ui/app/test/common-test-setup.js b/polygerrit-ui/app/test/common-test-setup.js
index aea24da..c4c7435 100644
--- a/polygerrit-ui/app/test/common-test-setup.js
+++ b/polygerrit-ui/app/test/common-test-setup.js
@@ -14,15 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO(dmfilippov): remove bundled-polymer.js imports when the following issue
+// https://github.com/Polymer/polymer-resin/issues/9 is resolved.
 import '../scripts/bundled-polymer.js';
 
 import 'polymer-resin/standalone/polymer-resin.js';
 import '@polymer/iron-test-helpers/iron-test-helpers.js';
 import './test-router.js';
 import {SafeTypes} from '../behaviors/safe-types-behavior/safe-types-behavior.js';
+import {appContext} from '../services/app-context.js';
 import {initAppContext} from '../services/app-context-init.js';
 import {_testOnly_resetPluginLoader} from '../elements/shared/gr-js-api-interface/gr-plugin-loader.js';
-
+import {grReportingMock} from '../services/gr-reporting/gr-reporting_mock.js';
 security.polymer_resin.install({
   allowedIdentifierPrefixes: [''],
   reportHandler(isViolation, fmt, ...args) {
@@ -75,7 +79,16 @@
   assert.equal(cleanups.length, 0);
 
   _testOnly_resetPluginLoader();
+
   initAppContext();
+  function setMock(serviceName, setupMock) {
+    Object.defineProperty(appContext, serviceName, {
+      get() {
+        return setupMock;
+      },
+    });
+  }
+  setMock('reportingService', grReportingMock);
 });
 
 if (window.stub) {
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index a0456d8..63df0be 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -15,8 +15,8 @@
 <!DOCTYPE html>
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>Elements Test Runner</title>
 <meta charset="utf-8">
+<title>Elements Test Runner</title>
 <script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
 <script src="/node_modules/web-component-tester/browser.js"></script>
 <style>
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock_test.js b/polygerrit-ui/app/test/mocks/comment-api.js
similarity index 100%
rename from polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock_test.js
rename to polygerrit-ui/app/test/mocks/comment-api.js
diff --git a/polygerrit-ui/app/test/mock-diff-response.js b/polygerrit-ui/app/test/mocks/diff-response.js
similarity index 100%
rename from polygerrit-ui/app/test/mock-diff-response.js
rename to polygerrit-ui/app/test/mocks/diff-response.js
diff --git a/polygerrit-ui/app/test/tests.js b/polygerrit-ui/app/test/tests.js
index 504a176..7ff9264 100644
--- a/polygerrit-ui/app/test/tests.js
+++ b/polygerrit-ui/app/test/tests.js
@@ -94,7 +94,6 @@
   'core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html',
   'core/gr-main-header/gr-main-header_test.html',
   'core/gr-navigation/gr-navigation_test.html',
-  'core/gr-reporting/gr-reporting_test.html',
   'core/gr-router/gr-router_test.html',
   'core/gr-search-bar/gr-search-bar_test.html',
   'core/gr-smart-search/gr-smart-search_test.html',
@@ -260,13 +259,19 @@
 }
 
 const services = [
+  'app-context-init_test.html',
   'flags_test.html',
+  'gr-reporting/gr-reporting_test.html',
+  'gr-reporting/gr-reporting_mock_test.html',
 ];
 for (let file of services) {
   file = servicesPath + file;
   testFiles.push(file);
 }
 
+// embed test
+testFiles.push('../embed/gr-diff-app-context-init_test.html');
+
 /**
  * Converts multiline string to a map<file_name, test_count>.
  *
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index 5a3e79a..cf9da4a 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -102,8 +102,8 @@
 
     maven_jar(
         name = "jackson-core",
-        artifact = "com.fasterxml.jackson.core:jackson-core:2.10.3",
-        sha1 = "f7ee7b55c7d292ac72fbaa7648c089f069c938d2",
+        artifact = "com.fasterxml.jackson.core:jackson-core:2.11.0",
+        sha1 = "f84302e14648f9f63c0c73951054aeb2ff0b810a",
     )
 
     # Test-only dependencies below.
diff --git a/yarn.lock b/yarn.lock
index 7b9f9d3..8379dd7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3385,6 +3385,13 @@
     semver "^6.3.0"
     spdx-expression-parse "^3.0.0"
 
+eslint-plugin-prettier@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca"
+  integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==
+  dependencies:
+    prettier-linter-helpers "^1.0.0"
+
 eslint-scope@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
@@ -3698,6 +3705,11 @@
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
   integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
 
+fast-diff@^1.1.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+  integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
+
 fast-glob@^2.0.2:
   version "2.2.7"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
@@ -6916,6 +6928,18 @@
   resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
   integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
 
+prettier-linter-helpers@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+  integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+  dependencies:
+    fast-diff "^1.1.2"
+
+prettier@2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
+  integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
+
 pretty-bytes@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9"