Merge "Swapping the position of comments and attention section in reply dialog"
diff --git a/Documentation/dev-crafting-changes.txt b/Documentation/dev-crafting-changes.txt
index e1b2147..02ac2f5 100644
--- a/Documentation/dev-crafting-changes.txt
+++ b/Documentation/dev-crafting-changes.txt
@@ -147,7 +147,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 3.2.1). Unused dependencies are found and removed using the
+tool (version 3.3.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 8fe8f4e..78b2c15 100644
--- a/Documentation/dev-e2e-tests.txt
+++ b/Documentation/dev-e2e-tests.txt
@@ -116,7 +116,7 @@
     "cmd": "clone"
   },
   {
-    "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
     "cmd": "clone"
   }
 ]
@@ -153,10 +153,12 @@
 * `-Dcom.google.gerrit.scenarios.hostname=localhost`
 * `-Dcom.google.gerrit.scenarios.ssh_port=29418`
 * `-Dcom.google.gerrit.scenarios.http_port=8080`
+* `-Dcom.google.gerrit.scenarios.http_scheme=http`
 
 Above, the properties can be set with values matching specific deployment topologies under test.
-The example values shown above are the currently coded default ones. The framework could support
-differing or more properties over time.
+The example values shown above are the currently coded default ones. For example, the `http` scheme
+above could be replaced with `https`. The framework could support differing or more properties over
+time.
 
 Plugin or otherwise non-core scenarios may do so just as well. The core java package
 `com.google.gerrit.scenarios` from the example above has to be replaced with the one under which
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 6fbedb0..86f546d 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1284,6 +1284,7 @@
   )]}'
   {
     "changes_per_page": 25,
+    "theme": "LIGHT",
     "date_format": "STD",
     "time_format": "HHMM_12",
     "diff_view": "SIDE_BY_SIDE",
@@ -1336,6 +1337,7 @@
 
   {
     "changes_per_page": 50,
+    "theme": "DARK",
     "expand_inline_diffs": true,
     "date_format": "STD",
     "time_format": "HHMM_12",
@@ -1383,6 +1385,7 @@
   )]}'
   {
     "changes_per_page": 50,
+    "theme" "DARK",
     "expand_inline_diffs": true,
     "date_format": "STD",
     "time_format": "HHMM_12",
@@ -2759,6 +2762,9 @@
 |`changes_per_page`             ||
 The number of changes to show on each page.
 Allowed values are `10`, `25`, `50`, `100`.
+|`theme`                        ||
+Which theme to use.
+Allowed values are `DARK` or `LIGHT`.
 |`expand_inline_diffs`          |not set if `false`|
 Whether to expand diffs inline instead of opening as separate page
 (PolyGerrit only).
@@ -2821,6 +2827,9 @@
 |`changes_per_page`             |optional|
 The number of changes to show on each page.
 Allowed values are `10`, `25`, `50`, `100`.
+|`theme`                        |optional|
+Which theme to use.
+Allowed values are `DARK` or `LIGHT`.
 |`expand_inline_diffs`          |not set if `false`|
 Whether to expand diffs inline instead of opening as separate page
 (PolyGerrit only).
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
index 3577a6a..665cc4d 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
index 54c54f8..5b892aa 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT1/a/projects/PROJECT/branches/master"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/a/projects/PROJECT/branches/master"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
index 6210deb..467661b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects",
     "entries": "PROJECTS_ENTRIES"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
index 2389124..30f5f23 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
@@ -4,7 +4,7 @@
     "cmd": "clone"
   },
   {
-    "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
index b4ee549..70e79ca 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
     "project": "PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
index 40e5a45..cd90739 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
index 3577a6a..665cc4d 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
@@ -1,6 +1,6 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
     "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
index 7cc8293..5720f53 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT/delete-project~delete"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/delete-project~delete"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
index 9ff15a7..e30a2cf 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
index 2b8809a..86a3c28 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT/branches/master"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/branches/master"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
index fcf4bc9..e4e2643 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
index 2389124..30f5f23 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
@@ -4,7 +4,7 @@
     "cmd": "clone"
   },
   {
-    "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
index a371757..301c65b 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://HOSTNAME:HTTP_PORT/a/changes/"
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/"
   }
 ]
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 fc68f97..4832392 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
@@ -62,6 +62,7 @@
       var in = replaceOverride(url.toString)
       in = replaceProperty("hostname", "localhost", in)
       in = replaceProperty("http_port", 8080, in)
+      in = replaceProperty("http_scheme", "http", in)
       replaceProperty("ssh_port", 29418, in)
     case ("number", number) =>
       val precedes = replaceKeyWith("_number", 0, number.toString)
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index 3ccbe4d..afd451a 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -25,6 +25,8 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.common.UsedAt.Project;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.PatchSet;
@@ -91,6 +93,14 @@
         @Assisted String subject,
         @Assisted Map<String, String> files);
 
+    @UsedAt(Project.PLUGIN_CODE_OWNERS)
+    PushOneCommit create(
+        PersonIdent i,
+        TestRepository<?> testRepo,
+        @Assisted("subject") String subject,
+        @Assisted Map<String, String> files,
+        @Assisted("changeId") String changeId);
+
     PushOneCommit create(
         PersonIdent i,
         TestRepository<?> testRepo,
@@ -227,15 +237,16 @@
         changeId);
   }
 
-  private PushOneCommit(
+  @AssistedInject
+  PushOneCommit(
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      PersonIdent i,
-      TestRepository<?> testRepo,
-      String subject,
-      Map<String, String> files,
-      String changeId)
+      @Assisted PersonIdent i,
+      @Assisted TestRepository<?> testRepo,
+      @Assisted("subject") String subject,
+      @Assisted Map<String, String> files,
+      @Nullable @Assisted("changeId") String changeId)
       throws Exception {
     this.testRepo = testRepo;
     this.notesFactory = notesFactory;
diff --git a/java/com/google/gerrit/common/UsedAt.java b/java/com/google/gerrit/common/UsedAt.java
index 9f8b255..e38ad9a 100644
--- a/java/com/google/gerrit/common/UsedAt.java
+++ b/java/com/google/gerrit/common/UsedAt.java
@@ -34,6 +34,7 @@
     GOOGLE,
     COLLABNET,
     PLUGIN_CHECKS,
+    PLUGIN_CODE_OWNERS,
     PLUGIN_DELETE_PROJECT,
     PLUGIN_SERVICEUSER,
     PLUGINS_ALL, // Use this project if a method/type is generally made available to all plugins.
diff --git a/java/com/google/gerrit/entities/BUILD b/java/com/google/gerrit/entities/BUILD
index 26265ae..66d1869 100644
--- a/java/com/google/gerrit/entities/BUILD
+++ b/java/com/google/gerrit/entities/BUILD
@@ -16,6 +16,7 @@
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
         "//lib/errorprone:annotations",
+        "//lib/flogger:api",
         "//proto:cache_java_proto",
         "//proto:entities_java_proto",
     ],
diff --git a/java/com/google/gerrit/server/project/ConfiguredMimeTypes.java b/java/com/google/gerrit/entities/ConfiguredMimeTypes.java
similarity index 66%
rename from java/com/google/gerrit/server/project/ConfiguredMimeTypes.java
rename to java/com/google/gerrit/entities/ConfiguredMimeTypes.java
index 0447edb..c28a573 100644
--- a/java/com/google/gerrit/server/project/ConfiguredMimeTypes.java
+++ b/java/com/google/gerrit/entities/ConfiguredMimeTypes.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.entities;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
+import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -34,7 +35,7 @@
 
   protected abstract ImmutableList<TypeMatcher> matchers();
 
-  static ConfiguredMimeTypes create(String projectName, Config rc) {
+  public static ConfiguredMimeTypes create(String projectName, Config rc) {
     Set<String> types = rc.getSubsections(MIMETYPE);
     ImmutableList.Builder<TypeMatcher> matchers = ImmutableList.builder();
     if (!types.isEmpty()) {
@@ -67,21 +68,31 @@
     return null;
   }
 
-  protected abstract static class TypeMatcher {
+  public abstract static class TypeMatcher {
     private final String type;
+    private final String pattern;
 
-    private TypeMatcher(String type) {
+    private TypeMatcher(String type, String pattern) {
       this.type = type;
+      this.pattern = pattern;
+    }
+
+    public String getPattern() {
+      return pattern;
+    }
+
+    public String getType() {
+      return type;
     }
 
     protected abstract boolean matches(String path);
   }
 
-  protected static class FnType extends TypeMatcher {
+  public static class FnType extends TypeMatcher {
     private final FileNameMatcher matcher;
 
-    private FnType(String type, String pattern) throws InvalidPatternException {
-      super(type);
+    public FnType(String type, String pattern) throws InvalidPatternException {
+      super(type, pattern);
       this.matcher = new FileNameMatcher(pattern, null);
     }
 
@@ -91,13 +102,28 @@
       m.append(input);
       return m.isMatch();
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof FnType)) {
+        return false;
+      }
+      FnType other = (FnType) o;
+      return Objects.equals(other.getType(), getType())
+          && Objects.equals(other.getPattern(), getPattern());
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(getType(), getPattern());
+    }
   }
 
-  protected static class ReType extends TypeMatcher {
+  public static class ReType extends TypeMatcher {
     private final Pattern re;
 
-    private ReType(String type, String pattern) throws PatternSyntaxException {
-      super(type);
+    public ReType(String type, String pattern) throws PatternSyntaxException {
+      super(type, pattern);
       this.re = Pattern.compile(pattern);
     }
 
@@ -105,5 +131,20 @@
     protected boolean matches(String input) {
       return re.matcher(input).matches();
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof ReType)) {
+        return false;
+      }
+      ReType other = (ReType) o;
+      return Objects.equals(other.getType(), getType())
+          && Objects.equals(other.getPattern(), getPattern());
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(getType(), getPattern());
+    }
   }
 }
diff --git a/java/com/google/gerrit/server/project/StoredCommentLinkInfo.java b/java/com/google/gerrit/entities/StoredCommentLinkInfo.java
similarity index 96%
rename from java/com/google/gerrit/server/project/StoredCommentLinkInfo.java
rename to java/com/google/gerrit/entities/StoredCommentLinkInfo.java
index 4e311b8..e70bf1e 100644
--- a/java/com/google/gerrit/server/project/StoredCommentLinkInfo.java
+++ b/java/com/google/gerrit/entities/StoredCommentLinkInfo.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.project;
+package com.google.gerrit.entities;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
@@ -68,7 +68,7 @@
   }
 
   /** Creates and returns a new {@link StoredCommentLinkInfo} instance with the same values. */
-  static StoredCommentLinkInfo fromInfo(CommentLinkInfo src, boolean enabled) {
+  public static StoredCommentLinkInfo fromInfo(CommentLinkInfo src, boolean enabled) {
     return builder(src.name)
         .setMatch(src.match)
         .setLink(src.link)
@@ -79,7 +79,7 @@
   }
 
   /** Returns an {@link CommentLinkInfo} instance with the same values. */
-  CommentLinkInfo toInfo() {
+  public CommentLinkInfo toInfo() {
     CommentLinkInfo info = new CommentLinkInfo();
     info.name = getName();
     info.match = getMatch();
diff --git a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
index 212f6da..c6555b9 100644
--- a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
@@ -102,6 +102,11 @@
     }
   }
 
+  public enum Theme {
+    DARK,
+    LIGHT
+  }
+
   public enum TimeFormat {
     /** 12-hour clock: 1:15 am, 2:13 pm */
     HHMM_12("h:mm a"),
@@ -125,6 +130,7 @@
   /** Type of download URL the user prefers to use. */
   public String downloadScheme;
 
+  public Theme theme;
   public DateFormat dateFormat;
   public TimeFormat timeFormat;
   public Boolean expandInlineDiffs;
@@ -182,6 +188,7 @@
     GeneralPreferencesInfo p = new GeneralPreferencesInfo();
     p.changesPerPage = DEFAULT_PAGESIZE;
     p.downloadScheme = null;
+    p.theme = Theme.LIGHT;
     p.dateFormat = DateFormat.STD;
     p.timeFormat = TimeFormat.HHMM_12;
     p.expandInlineDiffs = false;
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializer.java
new file mode 100644
index 0000000..6e0c923
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializer.java
@@ -0,0 +1,40 @@
+// 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.cache.serialize.entities;
+
+import com.google.gerrit.entities.ConfiguredMimeTypes;
+import com.google.gerrit.server.cache.proto.Cache;
+import java.util.regex.PatternSyntaxException;
+import org.eclipse.jgit.errors.InvalidPatternException;
+
+public class ConfiguredMimeTypeSerializer {
+  public static ConfiguredMimeTypes.TypeMatcher deserialize(Cache.ConfiguredMimeTypeProto proto) {
+    try {
+      return proto.getIsRegularExpression()
+          ? new ConfiguredMimeTypes.ReType(proto.getType(), proto.getPattern())
+          : new ConfiguredMimeTypes.FnType(proto.getType(), proto.getPattern());
+    } catch (PatternSyntaxException | InvalidPatternException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
+  public static Cache.ConfiguredMimeTypeProto serialize(ConfiguredMimeTypes.TypeMatcher value) {
+    return Cache.ConfiguredMimeTypeProto.newBuilder()
+        .setType(value.getType())
+        .setPattern(value.getPattern())
+        .setIsRegularExpression(value instanceof ConfiguredMimeTypes.ReType)
+        .build();
+  }
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializer.java
new file mode 100644
index 0000000..d7bd373
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializer.java
@@ -0,0 +1,47 @@
+// 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.cache.serialize.entities;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.nullToEmpty;
+
+import com.google.gerrit.entities.StoredCommentLinkInfo;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class StoredCommentLinkInfoSerializer {
+  public static StoredCommentLinkInfo deserialize(Cache.StoredCommentLinkInfoProto proto) {
+    return StoredCommentLinkInfo.builder(proto.getName())
+        .setMatch(emptyToNull(proto.getMatch()))
+        .setLink(emptyToNull(proto.getLink()))
+        .setHtml(emptyToNull(proto.getHtml()))
+        .setEnabled(proto.getEnabled())
+        .setOverrideOnly(proto.getOverrideOnly())
+        .build();
+  }
+
+  public static Cache.StoredCommentLinkInfoProto serialize(StoredCommentLinkInfo autoValue) {
+    return Cache.StoredCommentLinkInfoProto.newBuilder()
+        .setName(autoValue.getName())
+        .setMatch(nullToEmpty(autoValue.getMatch()))
+        .setLink(nullToEmpty(autoValue.getLink()))
+        .setHtml(nullToEmpty(autoValue.getHtml()))
+        .setEnabled(autoValue.getEnabled())
+        .setOverrideOnly(autoValue.getOverrideOnly())
+        .build();
+  }
+
+  private StoredCommentLinkInfoSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializer.java b/java/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializer.java
new file mode 100644
index 0000000..6125818d
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializer.java
@@ -0,0 +1,40 @@
+// 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.cache.serialize.entities;
+
+import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.cache.proto.Cache;
+
+/** Helper to (de)serialize values for caches. */
+public class SubscribeSectionSerializer {
+  public static SubscribeSection deserialize(Cache.SubscribeSectionProto proto) {
+    SubscribeSection.Builder builder =
+        SubscribeSection.builder(Project.nameKey(proto.getProjectName()));
+    proto.getMatchingRefSpecsList().forEach(rs -> builder.addMatchingRefSpec(rs));
+    proto.getMultiMatchRefSpecsList().forEach(rs -> builder.addMultiMatchRefSpec(rs));
+    return builder.build();
+  }
+
+  public static Cache.SubscribeSectionProto serialize(SubscribeSection autoValue) {
+    Cache.SubscribeSectionProto.Builder builder =
+        Cache.SubscribeSectionProto.newBuilder().setProjectName(autoValue.project().get());
+    autoValue.multiMatchRefSpecsAsString().forEach(rs -> builder.addMultiMatchRefSpecs(rs));
+    autoValue.matchingRefSpecsAsString().forEach(rs -> builder.addMatchingRefSpecs(rs));
+    return builder.build();
+  }
+
+  private SubscribeSectionSerializer() {}
+}
diff --git a/java/com/google/gerrit/server/project/CachedProjectConfig.java b/java/com/google/gerrit/server/project/CachedProjectConfig.java
index 308053cd..8af2f80 100644
--- a/java/com/google/gerrit/server/project/CachedProjectConfig.java
+++ b/java/com/google/gerrit/server/project/CachedProjectConfig.java
@@ -25,9 +25,11 @@
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.BranchOrderSection;
+import com.google.gerrit.entities.ConfiguredMimeTypes;
 import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.NotifyConfig;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
diff --git a/java/com/google/gerrit/server/project/CommentLinkProvider.java b/java/com/google/gerrit/server/project/CommentLinkProvider.java
index 500e163..1b9dc37 100644
--- a/java/com/google/gerrit/server/project/CommentLinkProvider.java
+++ b/java/com/google/gerrit/server/project/CommentLinkProvider.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
 import com.google.gerrit.server.config.ConfigUpdatedEvent;
 import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index f8d2cf0..0c69722 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -30,6 +30,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import com.google.common.primitives.Shorts;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.UsedAt;
@@ -46,6 +47,7 @@
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BooleanProjectConfig;
 import com.google.gerrit.entities.BranchOrderSection;
+import com.google.gerrit.entities.ConfiguredMimeTypes;
 import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.LabelValue;
@@ -53,6 +55,7 @@
 import com.google.gerrit.entities.NotifyConfig.NotifyType;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ProjectState;
@@ -1337,7 +1340,7 @@
         rc.setStringList(NOTIFY, nc.getName(), KEY_EMAIL, email);
       }
 
-      if (nc.getNotify().equals(EnumSet.of(NotifyType.ALL))) {
+      if (nc.getNotify().equals(Sets.immutableEnumSet(NotifyType.ALL))) {
         rc.unset(NOTIFY, nc.getName(), KEY_TYPE);
       } else {
         List<String> types = new ArrayList<>(4);
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 69b4c65..42e09d3 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -33,6 +33,7 @@
 import com.google.gerrit.entities.BranchOrderSection;
 import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index 746e6fe..f66bc8d 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.Theme;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
 import com.google.gerrit.extensions.client.MenuItem;
 import com.google.gerrit.extensions.config.DownloadScheme;
@@ -68,6 +69,7 @@
 
     // change all default values
     i.changesPerPage *= -1;
+    i.theme = Theme.DARK;
     i.dateFormat = DateFormat.US;
     i.timeFormat = TimeFormat.HHMM_24;
     i.emailStrategy = EmailStrategy.DISABLED;
@@ -90,6 +92,7 @@
     assertPrefs(o, i, "my");
     assertThat(o.my).containsExactlyElementsIn(i.my);
     assertThat(o.changeTable).containsExactlyElementsIn(i.changeTable);
+    assertThat(o.theme).isEqualTo(i.theme);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializerTest.java
new file mode 100644
index 0000000..f0e4932
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/ConfiguredMimeTypeSerializerTest.java
@@ -0,0 +1,36 @@
+// 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.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.ConfiguredMimeTypeSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.ConfiguredMimeTypeSerializer.serialize;
+
+import com.google.gerrit.entities.ConfiguredMimeTypes;
+import org.junit.Test;
+
+public class ConfiguredMimeTypeSerializerTest {
+  @Test
+  public void reType_roundTrip() {
+    ConfiguredMimeTypes.ReType value = new ConfiguredMimeTypes.ReType("type", "pattern");
+    assertThat(deserialize(serialize(value))).isEqualTo(value);
+  }
+
+  @Test
+  public void fnType_roundTrip() throws Exception {
+    ConfiguredMimeTypes.FnType value = new ConfiguredMimeTypes.FnType("type", "pattern");
+    assertThat(deserialize(serialize(value))).isEqualTo(value);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializerTest.java
new file mode 100644
index 0000000..ccd2378
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/StoredCommentLinkInfoSerializerTest.java
@@ -0,0 +1,58 @@
+// 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.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.StoredCommentLinkInfoSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.StoredCommentLinkInfoSerializer.serialize;
+
+import com.google.gerrit.entities.StoredCommentLinkInfo;
+import org.junit.Test;
+
+public class StoredCommentLinkInfoSerializerTest {
+  @Test
+  public void htmlOnly_roundTrip() {
+    StoredCommentLinkInfo autoValue =
+        StoredCommentLinkInfo.builder("name")
+            .setEnabled(true)
+            .setHtml("<p>html")
+            .setMatch("*")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void linkOnly_roundTrip() {
+    StoredCommentLinkInfo autoValue =
+        StoredCommentLinkInfo.builder("name")
+            .setEnabled(true)
+            .setLink("<p>html")
+            .setMatch("*")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+
+  @Test
+  public void overrideOnly_roundTrip() {
+    StoredCommentLinkInfo autoValue =
+        StoredCommentLinkInfo.builder("name")
+            .setEnabled(true)
+            .setOverrideOnly(true)
+            .setLink("<p>html")
+            .setMatch("*")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializerTest.java
new file mode 100644
index 0000000..fc96932
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/SubscribeSectionSerializerTest.java
@@ -0,0 +1,37 @@
+// 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.cache.serialize.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.cache.serialize.entities.SubscribeSectionSerializer.deserialize;
+import static com.google.gerrit.server.cache.serialize.entities.SubscribeSectionSerializer.serialize;
+
+import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.Project;
+import org.junit.Test;
+
+public class SubscribeSectionSerializerTest {
+  @Test
+  public void roundTrip() {
+    SubscribeSection autoValue =
+        SubscribeSection.builder(Project.nameKey("project"))
+            .addMultiMatchRefSpec("multi")
+            .addMultiMatchRefSpec("multi2")
+            .addMatchingRefSpec("matching1")
+            .addMatchingRefSpec("matching2")
+            .build();
+    assertThat(deserialize(serialize(autoValue))).isEqualTo(autoValue);
+  }
+}
diff --git a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
index a7c1435..606e147 100644
--- a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
+++ b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.StoredCommentLinkInfo;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.PluginConfig;
diff --git a/package.json b/package.json
index 329e3cb..7ba108d 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,6 @@
     "eslint-plugin-import": "^2.20.1",
     "eslint-plugin-jsdoc": "^19.2.0",
     "eslint-plugin-prettier": "^3.1.3",
-    "fried-twinkie": "^0.2.2",
     "gts": "^2.0.2",
     "polymer-cli": "^1.9.11",
     "prettier": "2.0.5",
@@ -29,7 +28,6 @@
     "safe_bazelisk": "if which bazelisk >/dev/null; then bazel_bin=bazelisk; else bazel_bin=bazel; fi && $bazel_bin",
     "eslint": "npm run safe_bazelisk test polygerrit-ui/app:lint_test",
     "eslintfix": "npm run safe_bazelisk run polygerrit-ui/app:lint_bin -- -- --fix $(pwd)/polygerrit-ui/app",
-    "test-template": "./polygerrit-ui/app/run_template_test.sh",
     "polylint": "npm run safe_bazelisk test polygerrit-ui/app:polylint_test",
     "test:debug": "npm run safe_bazelisk run //polygerrit-ui:karma_bin -- -- start $(pwd)/polygerrit-ui/karma.conf.js --browsers ChromeDev --no-single-run --testFiles",
     "test:single": "npm run safe_bazelisk run //polygerrit-ui:karma_bin -- -- start $(pwd)/polygerrit-ui/karma.conf.js --testFiles"
diff --git a/plugins/download-commands b/plugins/download-commands
index ea7b331..fd650ca 160000
--- a/plugins/download-commands
+++ b/plugins/download-commands
@@ -1 +1 @@
-Subproject commit ea7b33154997f4eae6481fbb3d5f3e47a63187a4
+Subproject commit fd650ca386c382b42d30e7ad72279bfeb311aee4
diff --git a/polygerrit-ui/README.md b/polygerrit-ui/README.md
index 3a66e97..3e95e42 100644
--- a/polygerrit-ui/README.md
+++ b/polygerrit-ui/README.md
@@ -279,47 +279,6 @@
 npm run polylint
 ```
 
-## Template Type Safety
-
-> **Warning**: This feature is temporary disabled, because it doesn't work with Polymer 2 and Polymer 3. Some of the checks are made by polymer linter.
-
-Polymer elements are not type checked against the element definition, making it
-trivial to break the display when refactoring or moving code. We now run
-additional tests to help ensure that template types are checked.
-
-A few notes to ensure that these tests pass
-- Any functions with optional parameters will need closure annotations.
-- Any Polymer parameters that are nullable or can be multiple types (other than
-  the one explicitly delared) will need type annotations.
-
-These tests require the `typescript` and `fried-twinkie` npm packages.
-
-To run on all files, execute the following command:
-
-```sh
-./polygerrit-ui/app/run_template_test.sh
-```
-
-or
-
-```sh
-npm run test-template
-```
-
-To run on a specific top level directory (ex: change-list)
-```sh
-TEMPLATE_NO_DEFAULT=true ./polygerrit-ui/app/run_template_test.sh //polygerrit-ui/app:template_test_change-list
-```
-
-To run on a specific file (ex: gr-change-list-view), execute the following command:
-```sh
-TEMPLATE_NO_DEFAULT=true ./polygerrit-ui/app/run_template_test.sh //polygerrit-ui/app:template_test_<TOP_LEVEL_DIRECTORY> --test_arg=<VIEW_NAME>
-```
-
-```sh
-TEMPLATE_NO_DEFAULT=true ./polygerrit-ui/app/run_template_test.sh //polygerrit-ui/app:template_test_change-list --test_arg=gr-change-list-view
-```
-
 ## Contributing
 
 Our users report bugs / feature requests related to the UI through [Monorail Issues - PolyGerrit](https://bugs.chromium.org/p/gerrit/issues/list?q=component%3APolyGerrit).
diff --git a/polygerrit-ui/app/.eslintrc.js b/polygerrit-ui/app/.eslintrc.js
index 6669762..1106ade 100644
--- a/polygerrit-ui/app/.eslintrc.js
+++ b/polygerrit-ui/app/.eslintrc.js
@@ -216,7 +216,7 @@
       }
     },
     {
-      "files": ["*.html", "test.js", "test-infra.js", "template_test.js"],
+      "files": ["*.html", "test.js", "test-infra.js"],
       "rules": {
         "jsdoc/require-file-overview": "off"
       },
@@ -270,7 +270,7 @@
       }
     },
     {
-      "files": ["test/functional/**/*.js", "template_test.js"],
+      "files": ["test/functional/**/*.js"],
       // Settings for functional tests. These scripts are node scripts.
       // Turn off "no-undef" to allow any global variable
       "env": {
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index ada507c..41c3f17 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -163,40 +163,3 @@
         "manual",
     ],
 )
-
-DIRECTORIES = [
-    "admin",
-    "change",
-    "change-list",
-    "core",
-    "diff",
-    "edit",
-    "plugins",
-    "settings",
-    "shared",
-    "gr-app",
-]
-
-[sh_test(
-    name = "template_test_" + directory,
-    size = "enormous",
-    srcs = ["template_test.sh"],
-    args = [directory],
-    data = [
-        ":pg_code",
-        ":template_test_srcs",
-    ],
-    tags = [
-        # Should not run sandboxed.
-        "local",
-        "template",
-    ],
-) for directory in DIRECTORIES]
-
-filegroup(
-    name = "template_test_srcs",
-    srcs = [
-        "template_test_srcs/convert_for_template_tests.py",
-        "template_test_srcs/template_test.js",
-    ],
-)
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 7e4e568..7423df7 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
@@ -37,6 +37,21 @@
 const TOO_MANY_FILES = 'too many files to find conflicts';
 const AUTHENTICATION_REQUIRED = 'Authentication required\n';
 
+const ErrorType = {
+  AUTH: 'AUTH',
+  NETWORK: 'NETWORK',
+  GENERIC: 'GENERIC',
+};
+
+// Bigger number has higher priority
+const ErrorTypePriority = {
+  [ErrorType.AUTH]: 3,
+  [ErrorType.NETWORK]: 2,
+  [ErrorType.GENERIC]: 1,
+};
+
+export const __testOnly_ErrorType = ErrorType;
+
 /**
  * @extends PolymerElement
  */
@@ -224,17 +239,24 @@
     console.error(e.detail.error.message);
   }
 
+  // TODO(dhruvsr): allow less priority alerts to override high priority alerts
+  // In some use cases we may want generic alerts to show along/over errors
+  _canOverride(incoming = ErrorType.GENERIC, existing = ErrorType.GENERIC) {
+    return ErrorTypePriority[incoming] >= ErrorTypePriority[existing];
+  }
+
   /**
    * @param {string} text
    * @param {?string=} opt_actionText
    * @param {?Function=} opt_actionCallback
    * @param {?boolean=} opt_dismissOnNavigation
+   * @param {?string=} opt_type
    */
   _showAlert(text, opt_actionText, opt_actionCallback,
-      opt_dismissOnNavigation) {
+      opt_dismissOnNavigation, opt_type) {
     if (this._alertElement) {
-      // do not override auth alerts
-      if (this._alertElement.type === 'AUTH') return;
+      // check priority before hiding
+      if (!this._canOverride(opt_type, this._alertElement.type)) return;
       this._hideAlert();
     }
 
@@ -276,7 +298,7 @@
     }
 
     this._alertElement = this._createToastAlert();
-    this._alertElement.type = 'AUTH';
+    this._alertElement.type = ErrorType.AUTH;
     this._alertElement.show(errorText, actionText,
         this._createLoginPopup.bind(this));
 
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
index 934f244..b527786 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.js
@@ -19,6 +19,7 @@
 import './gr-error-manager.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit.js';
+import {__testOnly_ErrorType} from './gr-error-manager.js';
 
 const basicFixture = fixtureFromElement('gr-error-manager');
 
@@ -219,6 +220,26 @@
       });
     });
 
+    test('_canOverride alerts', () => {
+      assert.isFalse(element._canOverride(undefined,
+          __testOnly_ErrorType.AUTH));
+      assert.isFalse(element._canOverride(undefined,
+          __testOnly_ErrorType.NETWORK));
+      assert.isTrue(element._canOverride(undefined,
+          __testOnly_ErrorType.GENERIC));
+      assert.isTrue(element._canOverride(undefined, undefined));
+
+      assert.isTrue(element._canOverride(__testOnly_ErrorType.NETWORK,
+          undefined));
+      assert.isTrue(element._canOverride(__testOnly_ErrorType.AUTH,
+          undefined));
+      assert.isFalse(element._canOverride(__testOnly_ErrorType.NETWORK,
+          __testOnly_ErrorType.AUTH));
+
+      assert.isTrue(element._canOverride(__testOnly_ErrorType.AUTH,
+          __testOnly_ErrorType.NETWORK));
+    });
+
     test('show auth refresh toast', async () => {
       // starts with authed state
       element.$.restAPI.getLoggedIn();
diff --git a/polygerrit-ui/app/template_test.sh b/polygerrit-ui/app/template_test.sh
deleted file mode 100755
index d42f23f..0000000
--- a/polygerrit-ui/app/template_test.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-
-# TODO(dmfilippov): Update template_test to support Polymer 2/Polymer 3 or delete it completely
-# The following line temporary disable template tests. Existing implementation doesn't compatible
-# with Polymer 2 & 3 class-based components. Polymer linter makes some checks regarding
-# templates and binding, but not all.
-exit 0
-
-set -ex
-
-node_bin=$(which node) && true
-if [ -z "$node_bin" ]; then
-    echo "node must be on the path."
-    exit 1
-fi
-
-npm_bin=$(which npm) && true
-if [[ -z "$npm_bin" ]]; then
-    echo "NPM must be on the path. (https://www.npmjs.com/)"
-    exit 1
-fi
-
-# Have to find where node_modules are installed and set the NODE_PATH
-
-get_node_path() {
-    cd $(dirname $node_bin)
-    cd ../lib/node_modules
-    pwd
-}
-
-export NODE_PATH=$(get_node_path)
-
-unzip -o polygerrit-ui/polygerrit_components.bower_components.zip -d polygerrit-ui/app
-python $TEST_SRCDIR/gerrit/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
-# Pass a file name argument from the --test_args (example: --test_arg=gr-list-view)
-${node_bin} $TEST_SRCDIR/gerrit/polygerrit-ui/app/template_test_srcs/template_test.js $1 $2
diff --git a/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py b/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
deleted file mode 100644
index 579e783..0000000
--- a/polygerrit-ui/app/template_test_srcs/convert_for_template_tests.py
+++ /dev/null
@@ -1,129 +0,0 @@
-import json
-import os
-import re
-
-polymerRegex = r"Polymer\({"
-polymerCompiledRegex = re.compile(polymerRegex)
-
-removeSelfInvokeRegex = r"\(function\(\) {\n(.+)}\)\(\);"
-fnCompiledRegex = re.compile(removeSelfInvokeRegex, re.DOTALL)
-
-regexBehavior = r"<script>(.+)<\/script>"
-behaviorCompiledRegex = re.compile(regexBehavior, re.DOTALL)
-
-
-def _open(filename, mode="r"):
-    try:
-        return open(filename, mode, encoding="utf-8")
-    except TypeError:
-        return open(filename, mode)
-
-
-def replaceBehaviorLikeHTML(fileIn, fileOut):
-    with _open(fileIn) as f:
-        file_str = f.read()
-        match = behaviorCompiledRegex.search(file_str)
-        if match:
-            with _open("polygerrit-ui/temp/behaviors/" +
-                       fileOut.replace("html", "js"), "w+") as f:
-                f.write(match.group(1))
-
-
-def replaceBehaviorLikeJS(fileIn, fileOut):
-    with _open(fileIn) as f:
-        file_str = f.read()
-        with _open("polygerrit-ui/temp/behaviors/" + fileOut, "w+") as f:
-            f.write(file_str)
-
-
-def generateStubBehavior(behaviorName):
-    with _open("polygerrit-ui/temp/behaviors/" +
-               behaviorName + ".js", "w+") as f:
-        f.write("/** @polymerBehavior **/\n" + behaviorName + "= {};")
-
-
-def replacePolymerElement(fileIn, fileOut, root):
-    with _open(fileIn) as f:
-        key = fileOut.split('.')[0]
-        # Removed self invoked function
-        file_str = f.read()
-        file_str_no_fn = fnCompiledRegex.search(file_str)
-
-        if file_str_no_fn:
-            package = root.replace("/", ".") + "." + fileOut
-
-            with _open("polygerrit-ui/temp/" + fileOut, "w+") as f:
-                mainFileContents = re.sub(
-                    polymerCompiledRegex,
-                    "exports = Polymer({",
-                    file_str_no_fn.group(1)).replace("'use strict';", "")
-                f.write("/** \n"
-                        "* @fileoverview \n"
-                        "* @suppress {missingProperties} \n"
-                        "*/ \n\n"
-                        "goog.module('polygerrit." + package + "')\n\n" +
-                        mainFileContents)
-
-            # Add package and javascript to files object.
-            elements[key]["js"] = "polygerrit-ui/temp/" + fileOut
-            elements[key]["package"] = package
-
-
-def writeTempFile(file, root):
-    # This is included in an extern because it is directly on the window object
-    # (for now at least).
-    if "gr-reporting" in file:
-        return
-    key = file.split('.')[0]
-    if key not in elements:
-        # gr-app doesn't have an additional level
-        elements[key] = {
-            "directory":
-                'gr-app' if len(root.split("/")) < 4 else root.split("/")[3]
-        }
-    if file.endswith(".html") and not file.endswith("_test.html"):
-        # gr-navigation is treated like a behavior rather than a standard
-        # element because of the way it added to the Gerrit object.
-        if file.endswith("gr-navigation.html"):
-            replaceBehaviorLikeHTML(os.path.join(root, file), file)
-        else:
-            elements[key]["html"] = os.path.join(root, file)
-    if file.endswith(".js"):
-        replacePolymerElement(os.path.join(root, file), file, root)
-
-
-if __name__ == "__main__":
-    # Create temp directory.
-    if not os.path.exists("polygerrit-ui/temp"):
-        os.makedirs("polygerrit-ui/temp")
-
-    # Within temp directory create behavior directory.
-    if not os.path.exists("polygerrit-ui/temp/behaviors"):
-        os.makedirs("polygerrit-ui/temp/behaviors")
-
-    elements = {}
-
-    # Go through every file in app/elements, and re-write accordingly to temp
-    # directory, and also added to elements object, which is used to generate a
-    # map of html files, package names, and javascript files.
-    for root, dirs, files in os.walk("polygerrit-ui/app/elements"):
-        for file in files:
-            writeTempFile(file, root)
-
-    # Special case for polymer behaviors we are using.
-    replaceBehaviorLikeHTML("polygerrit-ui/app/bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html", "iron-a11y-keys-behavior.html")
-    generateStubBehavior("Polymer.IronOverlayBehavior")
-    generateStubBehavior("Polymer.IronFitBehavior")
-
-    # TODO figure out something to do with iron-overlay-behavior.
-    # it is hard-coded reformatted.
-
-    with _open("polygerrit-ui/temp/map.json", "w+") as f:
-        f.write(json.dumps(elements))
-
-    for root, dirs, files in os.walk("polygerrit-ui/app/behaviors"):
-        for file in files:
-            if file.endswith("behavior.html"):
-                replaceBehaviorLikeHTML(os.path.join(root, file), file)
-            elif file.endswith("behavior.js"):
-                replaceBehaviorLikeJS(os.path.join(root, file), file)
diff --git a/polygerrit-ui/app/template_test_srcs/template_test.js b/polygerrit-ui/app/template_test_srcs/template_test.js
deleted file mode 100644
index 5592825..0000000
--- a/polygerrit-ui/app/template_test_srcs/template_test.js
+++ /dev/null
@@ -1,87 +0,0 @@
-const fs = require('fs');
-const twinkie = require('fried-twinkie');
-
-fs.readdir('./polygerrit-ui/temp/behaviors/', (err, data) => {
-  if (err) {
-    console.log('error /polygerrit-ui/temp/behaviors/ directory');
-  }
-  const behaviors = data;
-  const additionalSources = [];
-  const externMap = {};
-
-  for (const behavior of behaviors) {
-    if (!externMap[behavior]) {
-      additionalSources.push({
-        path: `./polygerrit-ui/temp/behaviors/${behavior}`,
-        src: fs.readFileSync(
-            `./polygerrit-ui/temp/behaviors/${behavior}`, 'utf-8'),
-      });
-      externMap[behavior] = true;
-    }
-  }
-
-  let mappings = JSON.parse(fs.readFileSync(
-      `./polygerrit-ui/temp/map.json`, 'utf-8'));
-
-  // The directory is passed as arg2 by the test target.
-  const directory = process.argv[2];
-  if (directory) {
-    const mappingSpecificDirectory = {};
-
-    for (key of Object.keys(mappings)) {
-      if (directory === mappings[key].directory) {
-        mappingSpecificDirectory[key] = mappings[key];
-      }
-    }
-    mappings = mappingSpecificDirectory;
-  }
-
-  // If a particular file was passed by the user, don't test everything.
-  const file = process.argv[3];
-  if (file) {
-    const mappingSpecificFile = {};
-    for (key of Object.keys(mappings)) {
-      if (key.includes(file)) {
-        mappingSpecificFile[key] = mappings[key];
-      }
-    }
-    mappings = mappingSpecificFile;
-  }
-
-  /**
-   * Types in Gerrit.
-   * All types should be under `./polygerrit-ui/app/types` folder and end with `js`.
-   */
-  fs.readdir('./polygerrit-ui/app/types/', (err, typeFiles) => {
-    for (const typeFile of typeFiles) {
-      if (!typeFile.endsWith('.js')) continue;
-      additionalSources.push({
-        path: `./polygerrit-ui/app/types/${typeFile}`,
-        src: fs.readFileSync(
-            `./polygerrit-ui/app/types/${typeFile}`, 'utf-8'),
-      });
-    }
-
-    const toCheck = [];
-    for (key of Object.keys(mappings)) {
-      if (mappings[key].html && mappings[key].js) {
-        toCheck.push({
-          htmlSrcPath: mappings[key].html,
-          jsSrcPath: mappings[key].js,
-          jsModule: 'polygerrit.' + mappings[key].package,
-        });
-      }
-    }
-
-    twinkie.checkTemplate(toCheck, additionalSources)
-        .then(() => {}, joinedErrors => {
-          if (joinedErrors) {
-            process.exit(1);
-          }
-        })
-        .catch(e => {
-          console.error(e);
-          process.exit(1);
-        });
-  });
-});
diff --git a/proto/cache.proto b/proto/cache.proto
index e9a7cdd..29b5870 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -436,3 +436,30 @@
   bool can_override = 17;
   repeated string ref_patterns = 18;
 }
+
+// Serialized form of com.google.gerrit.server.project.ConfiguredMimeTypes.
+// Next ID: 4
+message ConfiguredMimeTypeProto {
+  string type = 1;
+  string pattern = 2;
+  bool is_regular_expression = 3;
+}
+
+// Serialized form of com.google.gerrit.common.data.SubscribeSection.
+// Next ID: 4
+message SubscribeSectionProto {
+  string project_name = 1;
+  repeated string multi_match_ref_specs = 2;
+  repeated string matching_ref_specs = 3;
+}
+
+// Serialized form of com.google.gerrit.entities.StoredCommentLinkInfo.
+// Next ID: 7
+message StoredCommentLinkInfoProto {
+  string name = 1;
+  string match = 2;
+  string link = 3;
+  string html = 4;
+  bool enabled = 5;
+  bool override_only = 6;
+}
diff --git a/yarn.lock b/yarn.lock
index 0c4383f..a4f83bb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -711,13 +711,6 @@
   dependencies:
     chalk "*"
 
-"@types/cheerio@^0.22.2":
-  version "0.22.15"
-  resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.15.tgz#69040ffa92c309beeeeb7e92db66ac3f80700c0b"
-  integrity sha512-UGiiVtJK5niCqMKYmLEFz1Wl/3L5zF/u78lu8CwoUywWXRr9LDimeYuOzXVLXBMO758fcTdFtgjvqlztMH90MA==
-  dependencies:
-    "@types/node" "*"
-
 "@types/clean-css@*":
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/@types/clean-css/-/clean-css-4.2.1.tgz#cb0134241ec5e6ede1b5344bc829668fd9871a8d"
@@ -1367,11 +1360,6 @@
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-amdefine@>=0.0.4:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
-  integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
-
 ansi-align@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-1.1.0.tgz#2f0c1658829739add5ebb15e6b0c6e3423f016ba"
@@ -2089,11 +2077,6 @@
     raw-body "2.4.0"
     type-is "~1.6.17"
 
-boolbase@~1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
-  integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
-
 bower-config@^1.4.0, bower-config@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/bower-config/-/bower-config-1.4.1.tgz#85fd9df367c2b8dbbd0caa4c5f2bad40cd84c2cc"
@@ -2441,18 +2424,6 @@
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
   integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
 
-cheerio@^1.0.0-rc.2:
-  version "1.0.0-rc.3"
-  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
-  integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
-  dependencies:
-    css-select "~1.2.0"
-    dom-serializer "~0.1.1"
-    entities "~1.1.1"
-    htmlparser2 "^3.9.1"
-    lodash "^4.15.0"
-    parse5 "^3.0.1"
-
 chokidar@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
@@ -2967,16 +2938,6 @@
   resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
   integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
 
-css-select@~1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
-  integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
-  dependencies:
-    boolbase "~1.0.0"
-    css-what "2.1"
-    domutils "1.5.1"
-    nth-check "~1.0.1"
-
 css-slam@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/css-slam/-/css-slam-2.1.2.tgz#3d35b1922cb3e0002a45c89ab189492508c493e5"
@@ -2988,7 +2949,7 @@
     parse5 "^4.0.0"
     shady-css-parser "^0.1.0"
 
-css-what@2.1, css-what@^2.1.0:
+css-what@^2.1.0:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
   integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
@@ -3265,14 +3226,6 @@
     domelementtype "^2.0.1"
     entities "^2.0.0"
 
-dom-serializer@~0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
-  integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
-  dependencies:
-    domelementtype "^1.3.0"
-    entities "^1.1.1"
-
 dom-urls@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/dom-urls/-/dom-urls-1.1.0.tgz#001ddf81628cd1e706125c7176f53ccec55d918e"
@@ -3289,7 +3242,7 @@
     clone "^2.1.0"
     parse5 "^4.0.0"
 
-domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
+domelementtype@1, domelementtype@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
   integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
@@ -3306,14 +3259,6 @@
   dependencies:
     domelementtype "1"
 
-domutils@1.5.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
-  integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
-  dependencies:
-    dom-serializer "0"
-    domelementtype "1"
-
 domutils@^1.5.1:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
@@ -3477,7 +3422,7 @@
     engine.io-parser "~2.2.0"
     ws "^7.1.2"
 
-entities@^1.1.1, entities@~1.1.1:
+entities@^1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
   integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
@@ -4282,19 +4227,6 @@
   resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
   integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
 
-fried-twinkie@^0.2.2:
-  version "0.2.2"
-  resolved "https://registry.yarnpkg.com/fried-twinkie/-/fried-twinkie-0.2.2.tgz#fafa52b3b7957dc78d7867b28f74b6e01bdb2aee"
-  integrity sha512-rb/i+7VXEToBjrYZ2jSew4bI9znWF15P52dAGuoJcxpaBibWz2PI5tRx0ZSjDM+a/gZI2Pgr2XHT6wwNVZQ7/g==
-  dependencies:
-    "@types/cheerio" "^0.22.2"
-    chalk "^2.1.0"
-    google-closure-compiler-js "^20170626.0.0"
-    tmp "^0.0.31"
-    tsickle "^0.23.4"
-    twinkie "0.0.11"
-    typescript "^2.4.1"
-
 fs-constants@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@@ -4605,15 +4537,6 @@
     pify "^3.0.0"
     slash "^1.0.0"
 
-google-closure-compiler-js@^20170626.0.0:
-  version "20170626.0.0"
-  resolved "https://registry.yarnpkg.com/google-closure-compiler-js/-/google-closure-compiler-js-20170626.0.0.tgz#5df265b277d1ec6fdea12eed131d1491cd8a8d71"
-  integrity sha512-LQvWXN3yS2l88TsXiHZ0aWtGR51tep/bNvS7cuUldnKkppgknTo35jThYwE+JOU9lviERZjMHhiqxz2CXzIRuw==
-  dependencies:
-    minimist "^1.2.0"
-    vinyl "^2.0.1"
-    webpack-core "^0.6.8"
-
 got@^5.0.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35"
@@ -4933,7 +4856,7 @@
     relateurl "0.2.x"
     uglify-js "3.4.x"
 
-htmlparser2@^3.10.1, htmlparser2@^3.9.1:
+htmlparser2@^3.10.1:
   version "3.10.1"
   resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
   integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
@@ -6009,7 +5932,7 @@
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
   integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
 
-lodash@^4.0.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.14, lodash@^4.17.15:
+lodash@^4.0.0, lodash@^4.16.6, lodash@^4.17.14, lodash@^4.17.15:
   version "4.17.15"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
   integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -6675,13 +6598,6 @@
     gauge "~2.7.3"
     set-blocking "~2.0.0"
 
-nth-check@~1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
-  integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
-  dependencies:
-    boolbase "~1.0.0"
-
 number-is-nan@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -7055,13 +6971,6 @@
   resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
   integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
 
-parse5@^3.0.1:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
-  integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==
-  dependencies:
-    "@types/node" "*"
-
 parse5@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
@@ -8615,11 +8524,6 @@
   dependencies:
     is-plain-obj "^1.0.0"
 
-source-list-map@~0.1.7:
-  version "0.1.8"
-  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
-  integrity sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=
-
 source-map-resolve@^0.5.0:
   version "0.5.3"
   resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@@ -8639,13 +8543,6 @@
     buffer-from "^1.0.0"
     source-map "^0.6.0"
 
-source-map-support@^0.4.2:
-  version "0.4.18"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
-  integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==
-  dependencies:
-    source-map "^0.5.6"
-
 source-map-support@~0.5.12:
   version "0.5.19"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
@@ -8669,13 +8566,6 @@
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-source-map@~0.4.1:
-  version "0.4.4"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
-  integrity sha1-66T12pwNyZneaAMti092FzZSA2s=
-  dependencies:
-    amdefine ">=0.0.4"
-
 spawn-sync@^1.0.15:
   version "1.0.15"
   resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
@@ -9253,13 +9143,6 @@
   dependencies:
     os-tmpdir "~1.0.1"
 
-tmp@^0.0.31:
-  version "0.0.31"
-  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
-  integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc=
-  dependencies:
-    os-tmpdir "~1.0.1"
-
 tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -9364,16 +9247,6 @@
   resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
   integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
 
-tsickle@^0.23.4:
-  version "0.23.6"
-  resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.23.6.tgz#fcee57a5cb7f92a8c3a9e578ee0a286427dcfacd"
-  integrity sha1-/O5Xpct/kqjDqeV47gooZCfc+s0=
-  dependencies:
-    minimist "^1.2.0"
-    mkdirp "^0.5.1"
-    source-map "^0.5.6"
-    source-map-support "^0.4.2"
-
 tslib@^1.8.1:
   version "1.13.0"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
@@ -9410,13 +9283,6 @@
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
   integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
 
-twinkie@0.0.11:
-  version "0.0.11"
-  resolved "https://registry.yarnpkg.com/twinkie/-/twinkie-0.0.11.tgz#013c5e4b6b23ac8dec5d4eb8f9f84858bc143a74"
-  integrity sha1-ATxeS2sjrI3sXU64+fhIWLwUOnQ=
-  dependencies:
-    cheerio "^1.0.0-rc.2"
-
 type-check@~0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@@ -9469,11 +9335,6 @@
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.2.tgz#91d6868aaead7da74f493c553aeff76c0c0b1d5a"
   integrity sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==
 
-typescript@^2.4.1:
-  version "2.9.2"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
-  integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
-
 typical@^2.6.1:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d"
@@ -9930,14 +9791,6 @@
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
   integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
 
-webpack-core@^0.6.8:
-  version "0.6.9"
-  resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2"
-  integrity sha1-/FcViMhVjad76e+23r3Fo7FyvcI=
-  dependencies:
-    source-list-map "~0.1.7"
-    source-map "~0.4.1"
-
 whatwg-url@^6.4.0:
   version "6.5.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"