Merge "PushPermissionsIT: add hint about addPatchSetDenied "
diff --git a/Documentation/dev-design-doc-conclusion-template.md b/Documentation/dev-design-doc-conclusion-template.md
new file mode 100644
index 0000000..0625f2b
--- /dev/null
+++ b/Documentation/dev-design-doc-conclusion-template.md
@@ -0,0 +1,18 @@
+---
+title: "Design Doc - ${title} - Conclusion"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-conclusion.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Conclusion
+
+Describe which decision was made and what were the reasons for it.
+
+## <a id="implementation-plan"> Implementation Plan
+
+If known, say who is driving the implementation, for when the
+implementation is planned and which priority it has.
diff --git a/Documentation/dev-design-doc-index-template.md b/Documentation/dev-design-doc-index-template.md
new file mode 100644
index 0000000..10b4a81
--- /dev/null
+++ b/Documentation/dev-design-doc-index-template.md
@@ -0,0 +1,18 @@
+---
+title: "Design Doc - ${title}"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Design Doc - ${title}
+
+* [Use Cases](use-cases.html)
+* [Solution - ${solution-name-1}](solution-1.html)
+* [Solution - ${solution-name-2}](solution-2.html)
+* ...
+* [Conclusion](conclusion.html)
+
diff --git a/Documentation/dev-design-doc-solution-template.md b/Documentation/dev-design-doc-solution-template.md
new file mode 100644
index 0000000..8935902
--- /dev/null
+++ b/Documentation/dev-design-doc-solution-template.md
@@ -0,0 +1,72 @@
+---
+title: "Design Doc - ${title} - Solution - ${solution-name}"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-solution-${solution-name}.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Solution - ${solution-name}
+
+## <a id="overview"> Overview
+
+High-level overview; put details in the next section and background in
+the 'Background' section (see dev-design-doc-use-cases-template.txt).
+
+Should be understandable by engineers that are not working on Gerrit.
+
+If a solution is a variant of another solution, that other solution
+should be linked here.
+
+## <a id="detailed-design"> Detailed Design
+
+How does the overall design work? Details about the algorithms,
+storage format, APIs, etc., should be included here.
+
+For the initial review, it is ok for this to lack implementation
+details of minor importance.
+
+### <a id="scalability"> Scalability
+
+How does the solution scale?
+
+If applicable, consider:
+
+* data size increase
+* traffic increase
+* effects on replication across sites (master-slave and master-master)
+
+## <a id="alternatives-considered"> Alternatives Considered
+
+Within the scope of this solution you may need to describe what you did
+not do or why simpler approaches don't work. Mention other things to
+watch out for (if any).
+
+Do not describe alternative solutions in this section, as each solution
+should be described in a separate file.
+
+## <a id="pros-and-cons"> Pros and Cons
+
+Objectively list all points that speak in favor/against this solution.
+
+## <a id="implementation-plan"> Implementation Plan
+
+If known, say who would be willing to drive the implementation.
+
+It is possible to contribute solutions without having resources to do
+the implementation. In this case, say so here.
+
+If mentor support is desired, say so here. Also briefly describe any
+circumstances that can help with finding a suitable mentor.
+
+## <a id="time-estimation"> Time Estimation
+
+A rough itemized estimation of how much time it takes to implement this
+feature. Break down the feature into work items and estimate each item
+separately.
+
+If a mentor is assigned, this section must define a maximum time frame
+after which the mentorship automatically ends even if the feature isn't
+fully done yet.
diff --git a/Documentation/dev-design-doc-template.txt b/Documentation/dev-design-doc-template.txt
deleted file mode 100644
index 9480d97..0000000
--- a/Documentation/dev-design-doc-template.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-= Gerrit Code Review - ${title}
-
-[[objective]]
-== Objective
-
-In a few sentences, describe the key system objectives. Define the
-goals and non-goals.
-
-[[background]]
-== Background
-
-Stuff one needs to know to understand this doc (e.g. motivating
-examples, previous versions and problems, links to related
-changes/design docs, etc.
-
-Note: this is background; do not write about your design or ideas to
-solve problems here.
-
-[[overview]]
-== Overview
-
-High-level overview; put details in the next section and background in
-the previous section. Should be understandable by engineers that are
-not working on Gerrit.
-
-[[detailed-design]]
-== Detailed Design
-
-How does the overall design work? Details about the algorithms,
-storage format, APIs, etc., should be included here.
-
-It is ok for this to lack in detail at first for initial review.
-
-[[alternatives-considered]]
-== Alternatives Considered
-
-You may need to describe what you did not do or why simpler approaches
-don't work. Mention other things to watch out for (if any).
-
-[[implemenation-plan]]
-== Implementation Plan
-
-If known, say who is driving the implementation, for when the
-implementation is planned and which priority it has for you.
-
-It is possible to contribute designs without having resources to do the
-implementation. In this case, say so here.
-
-If mentor support is desired, say so here. Also briefly describe any
-circumstances that can help with finding a suitable mentor.
-
-[[time-estimation]]
-=== Time Estimation
-
-A rough itemized estimation of how much time it takes to implement this
-feature. Break down the feature into work items and estimate each item
-separately.
-
-If a mentor is assigned, this section must define a maximum time frame
-after which the mentorship automatically ends even if the feature isn't
-fully done yet.
-
-[[done-criteria]]
-== Done Criteria
-
-Describe the conditions that must be satisfied to consider this feature
-as done.
-
-If a mentor is assigned, the mentorship ends when this state is reached.
-Please note that a mentorship can also end earlier if the maximum time
-frame for the mentorship has exceeded (see section 'Time Estimation'
-above).
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/dev-design-doc-use-cases-template.md b/Documentation/dev-design-doc-use-cases-template.md
new file mode 100644
index 0000000..02c2fb5
--- /dev/null
+++ b/Documentation/dev-design-doc-use-cases-template.md
@@ -0,0 +1,48 @@
+---
+title: "Design Doc - ${title} - Use Cases"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-use-cases.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Use Cases
+
+In a few sentences, describe the use-cases as interactions between a
+user and a system to attain particular goals.
+
+Should be understandable by anyone who is familiar with using Gerrit.
+
+Optionally, differentiate between primary and secondary use-cases.
+Secondary use-cases are related to the primary use-cases, but
+addressing them within the scope of this design is not mandatory. This
+means they may not be covered by all proposed solutions. Secondary
+use-cases that are not addressed by the concluded solution, may be
+discussed in separate design docs. In this case links to these design
+docs should be added here.
+
+Optionally, define non-goals.
+
+It is possible that use-cases are specific to custom setups (e.g. the
+multi-master setup at Google). In this case, say so here.
+
+## <a id="acceptance-criteria"> Acceptance Criteria
+
+Describe conditions that must be satisfied to consider the feature as
+done.
+
+If a mentor is assigned, the mentorship ends when this state is reached.
+Please note that a mentorship can also end earlier if the maximum time
+frame for the mentorship has exceeded (see section 'Time Estimation'
+in dev-design-doc-conclusion-template.txt).
+
+## <a id="background"> Background
+
+Stuff one needs to know to understand the use-cases (e.g. motivating
+examples, previous versions and problems, links to related
+changes/design docs, etc.).
+
+Note: this is background; do not write about your design or ideas to
+solve problems here.
diff --git a/Documentation/dev-design-docs.txt b/Documentation/dev-design-docs.txt
index 621fd70..ac53bf8 100644
--- a/Documentation/dev-design-docs.txt
+++ b/Documentation/dev-design-docs.txt
@@ -4,14 +4,77 @@
 design-driven contribution process] it is required to specify features
 upfront in a design doc.
 
+[[structure]]
+== Design Doc Structure
+
+A design doc should discuss the following aspects:
+
+* Use-Cases:
+  The interactions between a user and a system to attain particular
+  goals.
+* Acceptance Criteria
+  Conditions that must be satisfied to consider the feature as done.
+* Background:
+  Stuff one needs to know to understand the use-cases (e.g. motivating
+  examples, previous versions and problems, links to related
+  changes/design docs, etc.)
+* Possible Solutions:
+  Possible solutions with the pros and cons, and explanation of
+  implementation details.
+* Conclusion:
+  Which decision was made and what were the reasons for it.
+
+[[collaboration]]
+As community we want to collaborate on design docs as much as possible
+and write them together, in an iterative manner. To make this work well
+design docs are split into multiple files that can be written and
+refined by several persons in parallel:
+
+* `index.md`:
+  Entry file that links to the files below (also see
+  'dev-design-doc-index-template.md').
+* `use-cases.md`:
+  Describes the use-cases, acceptance criteria and background (also see
+  'dev-design-doc-use-cases-template.md').
+* `solution-<n>.md`:
+  Each possible solution (with the pros and cons, and implementation
+  details) is described in a separate file (also see
+  'dev-design-doc-solution-template.md').
+* `conclusion.md`:
+  Describes the conclusion of the design discussion (also see
+  'dev-design-doc-conclusion-template.md').
+
+[[expectation]]
+It is expected that:
+
+* An agreement on the use-cases is achieved before solutions are being
+  discussed in detail.
+* Anyone who has ideas for an alternative solution uploads a change
+  with a `solution-<n>.md` that describes their solution. In case of
+  doubt whether an idea is a refinement of an existing solution or an
+  alternative solution, it's up to the owner of the discussed solution
+  to decide if the solution should be updated, or if the proposer
+  should start a new alternative solution.
+* All possible solutions are fairly discussed with their pros and cons,
+  and treated equally until a conclusion is made.
+* Unrelated issues (judged by the design doc owner) that are identified
+  during discussions are extracted into new design docs (initially
+  consisting only of an `index.md` and a `use-cases.md` file).
+* Changes making iterative improvements can be submitted frequently
+  (e.g. additional uses-cases can be added later, solutions can be
+  submitted without describing implementation details, etc.).
+* After a conclusion has been approved contributors are expected to
+  keep the design doc updated and fill in gaps while they go forward
+  with the implementation.
+
 [[propose]]
 == How to propose a new design?
 
-To propose a new design, add a `design-${title}.txt` file to this
-folder and push it as change for review. The design doc should follow
-the structure of the link:dev-design-doc-template.html[design doc
-template] and the change should be marked with the hashtag
-`design-doc`.
+To propose a new design, upload a change to the
+link:https://gerrit-review.googlesource.com/admin/repos/homepage[
+homepage] repository that adds a new folder under `pages/design-docs/`
+which contains at least an `index.md` and a `uses-cases.md` file (see
+link:#structure[design doc structure] above).
 
 Pushing a design doc for review requires to be a
 link:dev-roles.html#contributor[contributor].
@@ -28,22 +91,24 @@
 Everyone in the link:dev-roles.html[Gerrit community] is welcome to
 take part in the design review and comment on the design.
 
-Changes with new design docs should stay open for a minimum of 10
-calendar days so that everyone has a fair chance to see them. It is
-important that concerns regarding a feature are raised during this time
-frame since once a design is approved and submitted the implementation
-may start immediately.
+Ideas for alternative solutions should be uploaded as a change that
+describes the solution (see link:#collaboration[above]).
 
-Within the 10 calendar days time frame, the contributor should hear back
-from the link:dev-processes.html#steering-committee[engineering steering committee]
-whether the proposed feature is in scope of the project and if it can
-be accepted.
+Changes which make a conclusion on a design (changes that add/change
+the `conclusion.md` file, see link:#structure[Design Doc Structure])
+should stay open for a minimum of 10 calendar days so that everyone has
+a fair chance to see them. It is important that concerns regarding a
+feature are raised during this time frame since once a conclusion is
+approved and submitted the implementation may start immediately.
 
-In order to be accepted/submitted, it is not necessary that the design
-doc fully specifies all the details, but the idea of the feature and
-how it fits into Gerrit should be sufficiently clear (judged by the
-engineering steering committee). Contributors are expected to keep the design doc
-updated and fill in gaps while they go forward with the implementation.
+Other design doc changes can and should be submitted quickly so that
+collaboration and iterative refinements work smoothly (see
+link:#collaboration[above]).
+
+For proposed features the contributor should hear back from the
+link:dev-processes.html#steering-committee[engineering steering
+committee] within 14 calendar days whether the proposed feature is in
+scope of the project and if it can be accepted.
 
 [[watch-designs]]
 == How to get notified for new design docs?
@@ -51,8 +116,8 @@
 . Go to the
   link:https://gerrit-review.googlesource.com/settings/#Notifications[
   notification settings]
-. Add a project watch for the `gerrit` repository with the following
-  query: `hashtag:design-doc`
+. Add a project watch for the `homepage` repository with the following
+  query: `dir:pages/design-docs`
 
 GERRIT
 ------
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index 63cbd7c..ae9f9ff 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -19,6 +19,7 @@
 by RetryHelper to execute an action (0 == single attempt, no retry)
 * `action/retry_timeout_count`: Number of action executions of RetryHelper
 that ultimately timed out
+* `action/auto_retry_count`: Number of automatic retries with tracing
 
 === Pushes
 
@@ -56,6 +57,11 @@
 * `caches/disk_cached`: Disk entries used by persistent cache.
 * `caches/disk_hit_ratio`: Disk hit ratio for persistent cache.
 
+=== Change
+
+* `change/submit_rule_evaluation`: Latency for evaluating submit rules on a change.
+* `change/submit_type_evaluation`: Latency for evaluating the submit type on a change.
+
 === HTTP
 
 * `http/server/error_count`: Rate of REST API error responses.
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 56376fa..ec269e4 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6831,13 +6831,6 @@
 === Requirement
 The `Requirement` entity contains information about a requirement relative to a change.
 
-type:: Alphanumerical (plus hyphens or underscores) string to identify what the requirement is and
-why it was triggered. Can be seen as a class: requirements sharing the same type were created for a
-similar reason, and the data structure will follow one set of rules.
-
-data:: (Optional) Additional key-value data linked to this requirement. This is used in templates to
-render rich status messages.
-
 [options="header",cols="1,^1,5"]
 |===========================
 |Field Name      | |Description
diff --git a/WORKSPACE b/WORKSPACE
index 350dba1..6a86c19 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1251,8 +1251,8 @@
 bower_archive(
     name = "page",
     package = "visionmedia/page.js",
-    sha1 = "51a05428dd4f68fae1df5f12d0e2b61ba67f7757",
-    version = "1.7.1",
+    sha1 = "4a31889cd75cc5e7f68a4c7f256eecaf27102eee",
+    version = "1.11.4",
 )
 
 bower_archive(
diff --git a/contrib/refresh_plugin_in_testsite.sh b/contrib/refresh_plugin_in_testsite.sh
new file mode 100755
index 0000000..bb42ce8
--- /dev/null
+++ b/contrib/refresh_plugin_in_testsite.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script compiles a Gerrit plugin whose name is passed as first parameter
+# and copies it over to the plugin folder of the testsite. The path to the
+# testsite needs to be provided by the variable GERRIT_TESTSITE or as second
+# parameter.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+if [ "$#" -lt 1 ]
+then
+  echo "No plugin name provided as first argument. Stopping."
+  exit 1
+else
+  PLUGIN_NAME="$1"
+fi
+
+
+if [ "$#" -lt 2 ]
+then
+  if [ -z ${GERRIT_TESTSITE+x} ]
+  then
+    echo "Path to local testsite is neiter set as GERRIT_TESTSITE nor passed as second argument. Stopping."
+    exit 1
+  fi
+else
+  GERRIT_TESTSITE="$2"
+fi
+
+if [ ! -d "$GERRIT_TESTSITE" ]
+then
+  echo "Testsite directory $GERRIT_TESTSITE does not exist. Stopping."
+  exit 1
+fi
+
+bazel build //plugins/"$PLUGIN_NAME"/...
+if [ $? -ne 0 ]
+then
+  echo "Building the $PLUGIN_NAME plugin failed"
+  exit 1
+fi
+
+yes | cp -f "$GERRIT_CODE_DIR/bazel-genfiles/plugins/$PLUGIN_NAME/$PLUGIN_NAME.jar" "$GERRIT_TESTSITE/plugins/"
+if [ $? -eq 0 ]
+then
+  echo "Plugin $PLUGIN_NAME copied successfully to testsite."
+fi
diff --git a/contrib/show_new_gerrit_doc_in_chrome.sh b/contrib/show_new_gerrit_doc_in_chrome.sh
new file mode 100755
index 0000000..d57bc8a
--- /dev/null
+++ b/contrib/show_new_gerrit_doc_in_chrome.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script builds Gerrit's documentation and shows the current state in
+# Chrome. Specific pages (e.g. rest-api-changes.txt) including anchors can be
+# passed as parameter to jump directly to them.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+bazel build Documentation:searchfree
+if [ $? -ne 0 ]
+then
+  echo "Building the documentation failed. Stopping."
+  exit 1
+fi
+
+TMP_DOCS_DIR=/tmp/gerrit_docs
+rm -rf "$TMP_DOCS_DIR"
+unzip bazel-bin/Documentation/searchfree.zip -d "$TMP_DOCS_DIR" </dev/null >/dev/null 2>&1 & disown
+if [ $? -ne 0 ]
+then
+  echo "Unzipping the documentation to $TMP_DOCS_DIR failed. Stopping."
+  exit 1
+fi
+
+if [ "$#" -lt 1 ]
+then
+  FILE_NAME="index.html"
+else
+  FILE_NAME="$1"
+fi
+DOC_FILE_NAME="${FILE_NAME/.txt/.html}"
+google-chrome "file:///$TMP_DOCS_DIR/Documentation/$DOC_FILE_NAME" </dev/null >/dev/null 2>&1 & disown
diff --git a/contrib/start_testsite.sh b/contrib/start_testsite.sh
new file mode 100755
index 0000000..014eba9
--- /dev/null
+++ b/contrib/start_testsite.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script starts the local testsite in debug mode. If the flag "-u" is
+# passed, Gerrit is built from the current state of the repository and the
+# testsite is refreshed. The path to the testsite needs to be provided by
+# the variable GERRIT_TESTSITE or as parameter (after any used flags).
+# The testsite can be stopped by interrupting this script.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+UPDATE=false
+while getopts ':u' flag; do
+  case "${flag}" in
+    u) UPDATE=true ;;
+  esac
+done
+shift $(($OPTIND-1))
+
+if [ "$#" -lt 1 ]
+then
+  if [ -z ${GERRIT_TESTSITE+x} ]
+  then
+    echo "Path to local testsite is neither set as GERRIT_TESTSITE nor passed as first argument. Stopping."
+    exit 1
+  fi
+else
+  GERRIT_TESTSITE="$1"
+fi
+
+if [ "$UPDATE" = true ]
+then
+  echo "Refreshing testsite"
+  bazel build gerrit
+  if [ $? -ne 0 ]
+  then
+    echo "Build failed. Stopping."
+    exit 1
+  fi
+  $(bazel info output_base)/external/local_jdk/bin/java -jar bazel-bin/gerrit.war init --batch -d "$GERRIT_TESTSITE"
+  if [ $? -ne 0 ]
+  then
+    echo "Patching the testsite failed. Stopping."
+    exit 1
+  fi
+fi
+
+$(bazel info output_base)/external/local_jdk/bin/java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 bazel-bin/gerrit.war daemon -d "$GERRIT_TESTSITE" --console-log
diff --git a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
index e3ab70d..aa38c27 100644
--- a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
@@ -31,6 +31,7 @@
 import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.account.AuthResult;
 import com.google.gerrit.server.account.AuthenticationFailedException;
+import com.google.gerrit.server.account.externalids.PasswordVerifier;
 import com.google.gerrit.server.auth.NoSuchUserException;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.inject.Inject;
@@ -140,7 +141,7 @@
     GitBasicAuthPolicy gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy();
     if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP
         || gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) {
-      if (who.checkPassword(password, username)) {
+      if (PasswordVerifier.checkPassword(who.getExternalIds(), username, password)) {
         return succeedAuthentication(who);
       }
     }
@@ -157,7 +158,7 @@
       setUserIdentified(whoAuthResult.getAccountId());
       return true;
     } catch (NoSuchUserException e) {
-      if (who.checkPassword(password, username)) {
+      if (PasswordVerifier.checkPassword(who.getExternalIds(), username, password)) {
         return succeedAuthentication(who);
       }
       logger.atWarning().withCause(e).log(authenticationFailedMsg(username, req));
diff --git a/java/com/google/gerrit/json/EnumTypeAdapterFactory.java b/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
index dc74f67..21c4891 100644
--- a/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
+++ b/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
@@ -34,7 +34,7 @@
 public class EnumTypeAdapterFactory implements TypeAdapterFactory {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  @SuppressWarnings({"unchecked"})
+  @SuppressWarnings({"rawtypes", "unchecked"})
   @Override
   public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
     TypeAdapter<T> defaultEnumAdapter = TypeAdapters.ENUM_FACTORY.create(gson, typeToken);
@@ -43,7 +43,7 @@
       return null;
     }
 
-    return (TypeAdapter<T>) new EnumTypeAdapter(defaultEnumAdapter, typeToken);
+    return new EnumTypeAdapter(defaultEnumAdapter, typeToken);
   }
 
   private static class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
diff --git a/java/com/google/gerrit/server/account/AccountState.java b/java/com/google/gerrit/server/account/AccountState.java
index 8555166..6eb6ca1 100644
--- a/java/com/google/gerrit/server/account/AccountState.java
+++ b/java/com/google/gerrit/server/account/AccountState.java
@@ -14,13 +14,9 @@
 
 package com.google.gerrit.server.account;
 
-import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
-
 import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-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;
@@ -34,7 +30,6 @@
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Optional;
-import org.apache.commons.codec.DecoderException;
 import org.eclipse.jgit.lib.ObjectId;
 
 /**
@@ -45,8 +40,6 @@
  * account cache (see {@link AccountCache#get(Account.Id)}).
  */
 public class AccountState {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
   /**
    * Creates an AccountState from the given account config.
    *
@@ -175,29 +168,6 @@
     return userName;
   }
 
-  public boolean checkPassword(@Nullable String password, String username) {
-    if (password == null) {
-      return false;
-    }
-    for (ExternalId id : getExternalIds()) {
-      // Only process the "username:$USER" entry, which is unique.
-      if (!id.isScheme(SCHEME_USERNAME) || !username.equals(id.key().id())) {
-        continue;
-      }
-
-      String hashedStr = id.password();
-      if (!Strings.isNullOrEmpty(hashedStr)) {
-        try {
-          return HashedPassword.decode(hashedStr).checkPassword(password);
-        } catch (DecoderException e) {
-          logger.atSevere().log("DecoderException for user %s: %s ", username, e.getMessage());
-          return false;
-        }
-      }
-    }
-    return false;
-  }
-
   /** The external identities that identify the account holder. */
   public ImmutableSet<ExternalId> getExternalIds() {
     return externalIds;
diff --git a/java/com/google/gerrit/server/account/Emails.java b/java/com/google/gerrit/server/account/Emails.java
index 14f279b..1d53ed2 100644
--- a/java/com/google/gerrit/server/account/Emails.java
+++ b/java/com/google/gerrit/server/account/Emails.java
@@ -14,12 +14,14 @@
 
 package com.google.gerrit.server.account;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Streams;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SetMultimap;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.externalids.ExternalId;
@@ -32,6 +34,8 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 
 /** Class to access accounts by email. */
 @Singleton
@@ -65,15 +69,20 @@
    * have no external ID for the preferred email. Having accounts with a preferred email that does
    * not exist as external ID is an inconsistency, but existing functionality relies on still
    * getting those accounts, which is why they are included. Accounts by preferred email are fetched
-   * from the account index.
+   * from the account index as a fallback for email addresses that could not be resolved using
+   * {@link ExternalIds}.
    *
    * @see #getAccountsFor(String...)
    */
   public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException {
-    return Streams.concat(
-            externalIds.byEmail(email).stream().map(ExternalId::accountId),
-            executeIndexQuery(() -> queryProvider.get().byPreferredEmail(email).stream())
-                .map(a -> a.getAccount().id()))
+    ImmutableSet<Account.Id> accounts =
+        externalIds.byEmail(email).stream().map(ExternalId::accountId).collect(toImmutableSet());
+    if (!accounts.isEmpty()) {
+      return accounts;
+    }
+
+    return executeIndexQuery(() -> queryProvider.get().byPreferredEmail(email).stream())
+        .map(a -> a.getAccount().id())
         .collect(toImmutableSet());
   }
 
@@ -84,12 +93,18 @@
    */
   public ImmutableSetMultimap<String, Account.Id> getAccountsFor(String... emails)
       throws IOException {
-    ImmutableSetMultimap.Builder<String, Account.Id> builder = ImmutableSetMultimap.builder();
+    SetMultimap<String, Account.Id> result =
+        MultimapBuilder.hashKeys(emails.length).hashSetValues(1).build();
     externalIds.byEmails(emails).entries().stream()
-        .forEach(e -> builder.put(e.getKey(), e.getValue().accountId()));
-    executeIndexQuery(() -> queryProvider.get().byPreferredEmail(emails).entries().stream())
-        .forEach(e -> builder.put(e.getKey(), e.getValue().getAccount().id()));
-    return builder.build();
+        .forEach(e -> result.put(e.getKey(), e.getValue().accountId()));
+    List<String> emailsToBackfill =
+        Arrays.stream(emails).filter(e -> !result.containsKey(e)).collect(toImmutableList());
+    if (!emailsToBackfill.isEmpty()) {
+      executeIndexQuery(
+              () -> queryProvider.get().byPreferredEmail(emailsToBackfill).entries().stream())
+          .forEach(e -> result.put(e.getKey(), e.getValue().getAccount().id()));
+    }
+    return ImmutableSetMultimap.copyOf(result);
   }
 
   /**
diff --git a/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
new file mode 100644
index 0000000..e4bf27b
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids;
+
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+
+import com.google.common.base.Strings;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.server.account.HashedPassword;
+import java.util.Collection;
+import org.apache.commons.codec.DecoderException;
+
+/** Checks if a given username and password match a user's external IDs. */
+public class PasswordVerifier {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  /** Returns {@code true} if there is an external ID matching both the username and password. */
+  public static boolean checkPassword(
+      Collection<ExternalId> externalIds, String username, @Nullable String password) {
+    if (password == null) {
+      return false;
+    }
+    for (ExternalId id : externalIds) {
+      // Only process the "username:$USER" entry, which is unique.
+      if (!id.isScheme(SCHEME_USERNAME) || !username.equals(id.key().id())) {
+        continue;
+      }
+
+      String hashedStr = id.password();
+      if (!Strings.isNullOrEmpty(hashedStr)) {
+        try {
+          return HashedPassword.decode(hashedStr).checkPassword(password);
+        } catch (DecoderException e) {
+          logger.atSevere().log("DecoderException for user %s: %s ", username, e.getMessage());
+          return false;
+        }
+      }
+    }
+    return false;
+  }
+}
diff --git a/java/com/google/gerrit/server/auth/InternalAuthBackend.java b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
index c06c66b..2821bf6 100644
--- a/java/com/google/gerrit/server/auth/InternalAuthBackend.java
+++ b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.externalids.PasswordVerifier;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -62,7 +63,7 @@
               + ": account inactive or not provisioned in Gerrit");
     }
 
-    if (!who.checkPassword(req.getPassword().get(), username)) {
+    if (!PasswordVerifier.checkPassword(who.getExternalIds(), username, req.getPassword().get())) {
       throw new InvalidCredentialsException();
     }
     return new AuthUser(AuthUser.UUID.create(username), username);
diff --git a/java/com/google/gerrit/server/logging/Metadata.java b/java/com/google/gerrit/server/logging/Metadata.java
index 7eba4de..7184a92 100644
--- a/java/com/google/gerrit/server/logging/Metadata.java
+++ b/java/com/google/gerrit/server/logging/Metadata.java
@@ -89,6 +89,9 @@
   // One or more resources
   public abstract Optional<Boolean> multiple();
 
+  // The name of an operation that is performed.
+  public abstract Optional<String> operationName();
+
   // Partial or full computation
   public abstract Optional<Boolean> partial();
 
@@ -185,6 +188,8 @@
 
     public abstract Builder multiple(boolean multiple);
 
+    public abstract Builder operationName(String operationName);
+
     public abstract Builder partial(boolean partial);
 
     public abstract Builder noteDbFilePath(@Nullable String noteDbFilePath);
diff --git a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 1b1869c..36c6d36 100644
--- a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -19,6 +19,10 @@
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitTypeRecord;
 import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Description.Units;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.OnlineReindexMode;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -44,6 +48,8 @@
   private final ProjectCache projectCache;
   private final PrologRule prologRule;
   private final PluginSetContext<SubmitRule> submitRules;
+  private final Timer0 submitRuleEvaluationLatency;
+  private final Timer0 submitTypeEvaluationLatency;
   private final SubmitRuleOptions opts;
 
   public interface Factory {
@@ -56,10 +62,23 @@
       ProjectCache projectCache,
       PrologRule prologRule,
       PluginSetContext<SubmitRule> submitRules,
+      MetricMaker metricMaker,
       @Assisted SubmitRuleOptions options) {
     this.projectCache = projectCache;
     this.prologRule = prologRule;
     this.submitRules = submitRules;
+    this.submitRuleEvaluationLatency =
+        metricMaker.newTimer(
+            "change/submit_rule_evaluation",
+            new Description("Latency for evaluating submit rules on a change.")
+                .setCumulative()
+                .setUnit(Units.MILLISECONDS));
+    this.submitTypeEvaluationLatency =
+        metricMaker.newTimer(
+            "change/submit_type_evaluation",
+            new Description("Latency for evaluating the submit type on a change.")
+                .setCumulative()
+                .setUnit(Units.MILLISECONDS));
 
     this.opts = options;
   }
@@ -87,46 +106,41 @@
    * @param cd ChangeData to evaluate
    */
   public List<SubmitRecord> evaluate(ChangeData cd) {
-    Change change;
-    ProjectState projectState;
-    try {
-      change = cd.change();
-      if (change == null) {
-        throw new StorageException("Change not found");
+    try (Timer0.Context ignored = submitRuleEvaluationLatency.start()) {
+      Change change;
+      ProjectState projectState;
+      try {
+        change = cd.change();
+        if (change == null) {
+          throw new StorageException("Change not found");
+        }
+
+        projectState = projectCache.get(cd.project());
+        if (projectState == null) {
+          throw new NoSuchProjectException(cd.project());
+        }
+      } catch (StorageException | NoSuchProjectException e) {
+        return ruleError("Error looking up change " + cd.getId(), e);
       }
 
-      projectState = projectCache.get(cd.project());
-      if (projectState == null) {
-        throw new NoSuchProjectException(cd.project());
+      if ((!opts.allowClosed() || OnlineReindexMode.isActive()) && change.isClosed()) {
+        SubmitRecord rec = new SubmitRecord();
+        rec.status = SubmitRecord.Status.CLOSED;
+        return Collections.singletonList(rec);
       }
-    } catch (StorageException | NoSuchProjectException e) {
-      return ruleError("Error looking up change " + cd.getId(), e);
-    }
 
-    if ((!opts.allowClosed() || OnlineReindexMode.isActive()) && change.isClosed()) {
-      SubmitRecord rec = new SubmitRecord();
-      rec.status = SubmitRecord.Status.CLOSED;
-      return Collections.singletonList(rec);
+      // We evaluate all the plugin-defined evaluators,
+      // and then we collect the results in one list.
+      return Streams.stream(submitRules)
+          .map(c -> c.call(s -> s.evaluate(cd)))
+          .flatMap(Collection::stream)
+          .collect(Collectors.toList());
     }
-
-    // We evaluate all the plugin-defined evaluators,
-    // and then we collect the results in one list.
-    return Streams.stream(submitRules)
-        .map(c -> c.call(s -> s.evaluate(cd, opts)))
-        .flatMap(Collection::stream)
-        .collect(Collectors.toList());
   }
 
   private List<SubmitRecord> ruleError(String err, Exception e) {
-    if (opts.logErrors()) {
-      if (e == null) {
-        logger.atSevere().log(err);
-      } else {
-        logger.atSevere().withCause(e).log(err);
-      }
-      return defaultRuleError();
-    }
-    return createRuleError(err);
+    logger.atSevere().withCause(e).log(err);
+    return defaultRuleError();
   }
 
   /**
@@ -136,24 +150,23 @@
    * @param cd
    */
   public SubmitTypeRecord getSubmitType(ChangeData cd) {
-    ProjectState projectState;
-    try {
-      projectState = projectCache.get(cd.project());
-      if (projectState == null) {
-        throw new NoSuchProjectException(cd.project());
+    try (Timer0.Context ignored = submitTypeEvaluationLatency.start()) {
+      ProjectState projectState;
+      try {
+        projectState = projectCache.get(cd.project());
+        if (projectState == null) {
+          throw new NoSuchProjectException(cd.project());
+        }
+      } catch (NoSuchProjectException e) {
+        return typeError("Error looking up change " + cd.getId(), e);
       }
-    } catch (NoSuchProjectException e) {
-      return typeError("Error looking up change " + cd.getId(), e);
-    }
 
-    return prologRule.getSubmitType(cd, opts);
+      return prologRule.getSubmitType(cd);
+    }
   }
 
   private SubmitTypeRecord typeError(String err, Exception e) {
-    if (opts.logErrors()) {
-      logger.atSevere().withCause(e).log(err);
-      return defaultTypeError();
-    }
-    return SubmitTypeRecord.error(err);
+    logger.atSevere().withCause(e).log(err);
+    return defaultTypeError();
   }
 }
diff --git a/java/com/google/gerrit/server/project/SubmitRuleOptions.java b/java/com/google/gerrit/server/project/SubmitRuleOptions.java
index a4340b2..ad077c0 100644
--- a/java/com/google/gerrit/server/project/SubmitRuleOptions.java
+++ b/java/com/google/gerrit/server/project/SubmitRuleOptions.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.project;
 
 import com.google.auto.value.AutoValue;
-import com.google.gerrit.common.Nullable;
 
 /**
  * Stable identifier for options passed to a particular submit rule evaluator.
@@ -26,12 +25,7 @@
 @AutoValue
 public abstract class SubmitRuleOptions {
   private static final SubmitRuleOptions defaults =
-      new AutoValue_SubmitRuleOptions.Builder()
-          .allowClosed(false)
-          .skipFilters(false)
-          .logErrors(true)
-          .rule(null)
-          .build();
+      new AutoValue_SubmitRuleOptions.Builder().allowClosed(false).build();
 
   public static SubmitRuleOptions defaults() {
     return defaults;
@@ -43,25 +37,12 @@
 
   public abstract boolean allowClosed();
 
-  public abstract boolean skipFilters();
-
-  public abstract boolean logErrors();
-
-  @Nullable
-  public abstract String rule();
-
   public abstract Builder toBuilder();
 
   @AutoValue.Builder
   public abstract static class Builder {
     public abstract SubmitRuleOptions.Builder allowClosed(boolean allowClosed);
 
-    public abstract SubmitRuleOptions.Builder skipFilters(boolean skipFilters);
-
-    public abstract SubmitRuleOptions.Builder rule(@Nullable String rule);
-
-    public abstract SubmitRuleOptions.Builder logErrors(boolean logErrors);
-
     public abstract SubmitRuleOptions build();
   }
 }
diff --git a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index 0253ede..c38c92f 100644
--- a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.index.account.AccountField;
 import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.inject.Inject;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 
@@ -93,15 +92,13 @@
    * @return multimap of the given emails to accounts that have a preferred email that exactly
    *     matches this email
    */
-  public Multimap<String, AccountState> byPreferredEmail(String... emails) {
-    List<String> emailList = Arrays.asList(emails);
-
+  public Multimap<String, AccountState> byPreferredEmail(List<String> emails) {
     if (hasPreferredEmailExact()) {
       List<List<AccountState>> r =
-          query(emailList.stream().map(AccountPredicates::preferredEmailExact).collect(toList()));
+          query(emails.stream().map(AccountPredicates::preferredEmailExact).collect(toList()));
       Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
-      for (int i = 0; i < emailList.size(); i++) {
-        accountsByEmail.putAll(emailList.get(i), r.get(i));
+      for (int i = 0; i < emails.size(); i++) {
+        accountsByEmail.putAll(emails.get(i), r.get(i));
       }
       return accountsByEmail;
     }
@@ -111,10 +108,10 @@
     }
 
     List<List<AccountState>> r =
-        query(emailList.stream().map(AccountPredicates::preferredEmail).collect(toList()));
+        query(emails.stream().map(AccountPredicates::preferredEmail).collect(toList()));
     Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
-    for (int i = 0; i < emailList.size(); i++) {
-      String email = emailList.get(i);
+    for (int i = 0; i < emails.size(); i++) {
+      String email = emails.get(i);
       Set<AccountState> matchingAccounts =
           r.get(i).stream()
               .filter(a -> a.getAccount().preferredEmail().equals(email))
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
index afd02a9..d5ed9a4 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
@@ -31,9 +31,8 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.rules.DefaultSubmitRule;
+import com.google.gerrit.server.rules.PrologOptions;
 import com.google.gerrit.server.rules.PrologRule;
 import com.google.gerrit.server.rules.RulesCache;
 import com.google.inject.Inject;
@@ -46,7 +45,6 @@
   private final RulesCache rules;
   private final AccountLoader.Factory accountInfoFactory;
   private final ProjectCache projectCache;
-  private final DefaultSubmitRule defaultSubmitRule;
   private final PrologRule prologRule;
 
   @Option(name = "--filters", usage = "impact of filters in parent projects")
@@ -58,13 +56,11 @@
       RulesCache rules,
       AccountLoader.Factory infoFactory,
       ProjectCache projectCache,
-      DefaultSubmitRule defaultSubmitRule,
       PrologRule prologRule) {
     this.changeDataFactory = changeDataFactory;
     this.rules = rules;
     this.accountInfoFactory = infoFactory;
     this.projectCache = projectCache;
-    this.defaultSubmitRule = defaultSubmitRule;
     this.prologRule = prologRule;
   }
 
@@ -74,33 +70,23 @@
     if (input == null) {
       input = new TestSubmitRuleInput();
     }
-    if (input.rule != null && !rules.isProjectRulesEnabled()) {
+    if (input.rule == null) {
+      throw new BadRequestException("rule is required");
+    }
+    if (!rules.isProjectRulesEnabled()) {
       throw new AuthException("project rules are disabled");
     }
     input.filters = MoreObjects.firstNonNull(input.filters, filters);
 
-    SubmitRuleOptions opts =
-        SubmitRuleOptions.builder()
-            .skipFilters(input.filters == Filters.SKIP)
-            .rule(input.rule)
-            .logErrors(false)
-            .build();
-
     ProjectState projectState = projectCache.get(rsrc.getProject());
     if (projectState == null) {
       throw new BadRequestException("project not found");
     }
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
-    List<SubmitRecord> records;
-    if (projectState.hasPrologRules() || input.rule != null) {
-      records = ImmutableList.copyOf(prologRule.evaluate(cd, opts));
-    } else {
-      // No rules were provided as input and we have no rules.pl. This means we are supposed to run
-      // the default rules. Nowadays, the default rules are implemented in Java, not Prolog.
-      // Therefore, we call the DefaultRuleEvaluator instead.
-      records = ImmutableList.copyOf(defaultSubmitRule.evaluate(cd, opts));
-    }
-
+    List<SubmitRecord> records =
+        ImmutableList.copyOf(
+            prologRule.evaluate(
+                cd, PrologOptions.dryRunOptions(input.rule, input.filters == Filters.SKIP)));
     List<TestSubmitRuleInfo> out = Lists.newArrayListWithCapacity(records.size());
     AccountLoader accounts = accountInfoFactory.create(true);
     for (SubmitRecord r : records) {
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
index 9e8ee67..cb52fcb 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestReadView;
@@ -28,6 +29,8 @@
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.rules.PrologOptions;
+import com.google.gerrit.server.rules.PrologRule;
 import com.google.gerrit.server.rules.RulesCache;
 import com.google.inject.Inject;
 import org.kohsuke.args4j.Option;
@@ -35,19 +38,16 @@
 public class TestSubmitType implements RestModifyView<RevisionResource, TestSubmitRuleInput> {
   private final ChangeData.Factory changeDataFactory;
   private final RulesCache rules;
-  private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
+  private final PrologRule prologRule;
 
   @Option(name = "--filters", usage = "impact of filters in parent projects")
   private Filters filters = Filters.RUN;
 
   @Inject
-  TestSubmitType(
-      ChangeData.Factory changeDataFactory,
-      RulesCache rules,
-      SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
+  TestSubmitType(ChangeData.Factory changeDataFactory, RulesCache rules, PrologRule prologRule) {
     this.changeDataFactory = changeDataFactory;
     this.rules = rules;
-    this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
+    this.prologRule = prologRule;
   }
 
   @Override
@@ -56,21 +56,18 @@
     if (input == null) {
       input = new TestSubmitRuleInput();
     }
-    if (input.rule != null && !rules.isProjectRulesEnabled()) {
+    if (input.rule == null) {
+      throw new BadRequestException("rule is required");
+    }
+    if (!rules.isProjectRulesEnabled()) {
       throw new AuthException("project rules are disabled");
     }
     input.filters = MoreObjects.firstNonNull(input.filters, filters);
 
-    SubmitRuleOptions opts =
-        SubmitRuleOptions.builder()
-            .logErrors(false)
-            .skipFilters(input.filters == Filters.SKIP)
-            .rule(input.rule)
-            .build();
-
-    SubmitRuleEvaluator evaluator = submitRuleEvaluatorFactory.create(opts);
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
-    SubmitTypeRecord rec = evaluator.getSubmitType(cd);
+    SubmitTypeRecord rec =
+        prologRule.getSubmitType(
+            cd, PrologOptions.dryRunOptions(input.rule, input.filters == Filters.SKIP));
 
     if (rec.status != SubmitTypeRecord.Status.OK) {
       throw new BadRequestException(String.format("rule produced invalid result: %s", rec));
@@ -80,17 +77,30 @@
   }
 
   public static class Get implements RestReadView<RevisionResource> {
-    private final TestSubmitType test;
+    private final ChangeData.Factory changeDataFactory;
+    private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
 
     @Inject
-    Get(TestSubmitType test) {
-      this.test = test;
+    Get(
+        ChangeData.Factory changeDataFactory,
+        SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
+      this.changeDataFactory = changeDataFactory;
+      this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
     }
 
     @Override
     public Response<SubmitType> apply(RevisionResource resource)
-        throws AuthException, BadRequestException {
-      return test.apply(resource, null);
+        throws AuthException, ResourceConflictException {
+      SubmitRuleEvaluator evaluator =
+          submitRuleEvaluatorFactory.create(SubmitRuleOptions.defaults());
+      ChangeData cd = changeDataFactory.create(resource.getNotes());
+      SubmitTypeRecord rec = evaluator.getSubmitType(cd);
+
+      if (rec.status != SubmitTypeRecord.Status.OK) {
+        throw new ResourceConflictException(String.format("rule produced invalid result: %s", rec));
+      }
+
+      return Response.ok(rec.type);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
index 8401c1d..ee997b2 100644
--- a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
+++ b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -63,7 +62,7 @@
   }
 
   @Override
-  public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
+  public Collection<SubmitRecord> evaluate(ChangeData cd) {
     ProjectState projectState = projectCache.get(cd.project());
 
     // In case at least one project has a rules.pl file, we let Prolog handle it.
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
index 4695800..ff5d99e 100644
--- a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -60,7 +59,7 @@
   IgnoreSelfApprovalRule() {}
 
   @Override
-  public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
+  public Collection<SubmitRecord> evaluate(ChangeData cd) {
     List<LabelType> labelTypes;
     List<PatchSetApproval> approvals;
     try {
diff --git a/java/com/google/gerrit/server/rules/PrologOptions.java b/java/com/google/gerrit/server/rules/PrologOptions.java
new file mode 100644
index 0000000..da9b3ab
--- /dev/null
+++ b/java/com/google/gerrit/server/rules/PrologOptions.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.rules;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.common.Nullable;
+import java.util.Optional;
+
+@AutoValue
+public abstract class PrologOptions {
+  public static PrologOptions defaultOptions() {
+    return new AutoValue_PrologOptions.Builder().logErrors(true).skipFilters(false).build();
+  }
+
+  public static PrologOptions dryRunOptions(String ruleToTest, boolean skipFilters) {
+    return new AutoValue_PrologOptions.Builder()
+        .logErrors(false)
+        .skipFilters(skipFilters)
+        .rule(ruleToTest)
+        .build();
+  }
+
+  /** Whether errors should be logged. */
+  abstract boolean logErrors();
+
+  /** Whether Prolog filters from parent projects should be skipped. */
+  abstract boolean skipFilters();
+
+  /**
+   * Prolog rule that should be run. If not given, the Prolog rule that is configured for the
+   * project is used (the rule from rules.pl in refs/meta/config).
+   */
+  abstract Optional<String> rule();
+
+  @AutoValue.Builder
+  abstract static class Builder {
+    abstract PrologOptions.Builder logErrors(boolean logErrors);
+
+    abstract PrologOptions.Builder skipFilters(boolean skipFilters);
+
+    abstract PrologOptions.Builder rule(@Nullable String rule);
+
+    abstract PrologOptions build();
+  }
+}
diff --git a/java/com/google/gerrit/server/rules/PrologRule.java b/java/com/google/gerrit/server/rules/PrologRule.java
index 0c54f40..e15b4b5 100644
--- a/java/com/google/gerrit/server/rules/PrologRule.java
+++ b/java/com/google/gerrit/server/rules/PrologRule.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.common.data.SubmitTypeRecord;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -37,21 +36,29 @@
   }
 
   @Override
-  public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions opts) {
+  public Collection<SubmitRecord> evaluate(ChangeData cd) {
     ProjectState projectState = projectCache.get(cd.project());
     // We only want to run the Prolog engine if we have at least one rules.pl file to use.
-    if ((projectState == null || !projectState.hasPrologRules()) && opts.rule() == null) {
+    if ((projectState == null || !projectState.hasPrologRules())) {
       return Collections.emptyList();
     }
 
+    return evaluate(cd, PrologOptions.defaultOptions());
+  }
+
+  public Collection<SubmitRecord> evaluate(ChangeData cd, PrologOptions opts) {
     return getEvaluator(cd, opts).evaluate();
   }
 
-  private PrologRuleEvaluator getEvaluator(ChangeData cd, SubmitRuleOptions opts) {
-    return factory.create(cd, opts);
+  public SubmitTypeRecord getSubmitType(ChangeData cd) {
+    return getSubmitType(cd, PrologOptions.defaultOptions());
   }
 
-  public SubmitTypeRecord getSubmitType(ChangeData cd, SubmitRuleOptions opts) {
+  public SubmitTypeRecord getSubmitType(ChangeData cd, PrologOptions opts) {
     return getEvaluator(cd, opts).getSubmitType();
   }
+
+  private PrologRuleEvaluator getEvaluator(ChangeData cd, PrologOptions opts) {
+    return factory.create(cd, opts);
+  }
 }
diff --git a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
index c036c86..7f6450d 100644
--- a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
+++ b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.RuleEvalException;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -73,7 +72,7 @@
 
   public interface Factory {
     /** Returns a new {@link PrologRuleEvaluator} with the specified options */
-    PrologRuleEvaluator create(ChangeData cd, SubmitRuleOptions options);
+    PrologRuleEvaluator create(ChangeData cd, PrologOptions options);
   }
 
   /**
@@ -95,7 +94,7 @@
   private final PrologEnvironment.Factory envFactory;
   private final ChangeData cd;
   private final ProjectState projectState;
-  private final SubmitRuleOptions opts;
+  private final PrologOptions opts;
   private Term submitRule;
 
   @AssistedInject
@@ -107,7 +106,7 @@
       PrologEnvironment.Factory envFactory,
       ProjectCache projectCache,
       @Assisted ChangeData cd,
-      @Assisted SubmitRuleOptions options) {
+      @Assisted PrologOptions options) {
     this.accountCache = accountCache;
     this.accounts = accounts;
     this.emails = emails;
@@ -159,12 +158,6 @@
       return ruleError("Error looking up change " + cd.getId(), e);
     }
 
-    if (!opts.allowClosed() && change.isClosed()) {
-      SubmitRecord rec = new SubmitRecord();
-      rec.status = SubmitRecord.Status.CLOSED;
-      return Collections.singletonList(rec);
-    }
-
     List<Term> results;
     try {
       results =
@@ -465,22 +458,22 @@
     PrologEnvironment env;
     try {
       PrologMachineCopy pmc;
-      if (opts.rule() == null) {
+      if (opts.rule().isPresent()) {
+        pmc = rulesCache.loadMachine("stdin", new StringReader(opts.rule().get()));
+      } else {
         pmc =
             rulesCache.loadMachine(
                 projectState.getNameKey(), projectState.getConfig().getRulesId());
-      } else {
-        pmc = rulesCache.loadMachine("stdin", new StringReader(opts.rule()));
       }
       env = envFactory.create(pmc);
     } catch (CompileException err) {
       String msg;
-      if (opts.rule() == null) {
+      if (opts.rule().isPresent()) {
+        msg = err.getMessage();
+      } else {
         msg =
             String.format(
                 "Cannot load rules.pl for %s: %s", projectState.getName(), err.getMessage());
-      } else {
-        msg = err.getMessage();
       }
       throw new RuleEvalException(msg, err);
     }
diff --git a/java/com/google/gerrit/server/rules/SubmitRule.java b/java/com/google/gerrit/server/rules/SubmitRule.java
index 2a68683..20cb8fb 100644
--- a/java/com/google/gerrit/server/rules/SubmitRule.java
+++ b/java/com/google/gerrit/server/rules/SubmitRule.java
@@ -15,7 +15,6 @@
 
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import java.util.Collection;
 
@@ -40,5 +39,5 @@
 @ExtensionPoint
 public interface SubmitRule {
   /** Returns a {@link Collection} of {@link SubmitRecord} status for the change. */
-  Collection<SubmitRecord> evaluate(ChangeData changeData, SubmitRuleOptions options);
+  Collection<SubmitRecord> evaluate(ChangeData changeData);
 }
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index f50f857..871dfe0 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.git.LockFailureException;
 import com.google.gerrit.metrics.Counter1;
+import com.google.gerrit.metrics.Counter2;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.Field;
 import com.google.gerrit.metrics.MetricMaker;
@@ -120,6 +121,7 @@
   public static class Metrics {
     final Counter1<ActionType> attemptCounts;
     final Counter1<ActionType> timeoutCount;
+    final Counter2<ActionType, String> autoRetryCount;
 
     @Inject
     Metrics(MetricMaker metricMaker) {
@@ -142,6 +144,16 @@
                   .setCumulative()
                   .setUnit("timeouts"),
               actionTypeField);
+      autoRetryCount =
+          metricMaker.newCounter(
+              "action/auto_retry_count",
+              new Description("Number of automatic retries with tracing")
+                  .setCumulative()
+                  .setUnit("retries"),
+              actionTypeField,
+              Field.ofString("operation_name", Metadata.Builder::operationName)
+                  .description("The name of the operation that was retried.")
+                  .build());
     }
   }
 
@@ -285,15 +297,23 @@
                 // of the failure. If a trace was already done there is no need to retry.
                 if (retryWithTraceOnFailure
                     && opts.retryWithTrace().isPresent()
-                    && opts.retryWithTrace().get().test(t)
-                    && !traceContext.isTracing()) {
-                  traceContext
-                      .addTag(RequestId.Type.TRACE_ID, "retry-on-failure-" + new RequestId())
-                      .forceLogging();
-                  logger.atFine().withCause(t).log(
-                      "%s failed, retry with tracing enabled",
-                      opts.caller().map(Class::getSimpleName).orElse("N/A"));
-                  return true;
+                    && opts.retryWithTrace().get().test(t)) {
+                  String caller = opts.caller().map(Class::getSimpleName).orElse("N/A");
+                  if (!traceContext.isTracing()) {
+                    traceContext
+                        .addTag(RequestId.Type.TRACE_ID, "retry-on-failure-" + new RequestId())
+                        .forceLogging();
+                    logger.atFine().withCause(t).log(
+                        "%s failed, retry with tracing enabled", caller);
+                    metrics.autoRetryCount.increment(actionType, caller);
+                    return true;
+                  }
+
+                  // A non-recoverable failure occurred. We retried the operation with tracing
+                  // enabled and it failed again. Log the failure so that admin can see if it
+                  // differs from the failure that triggered the retry.
+                  logger.atFine().withCause(t).log("auto-retry of %s has failed", caller);
+                  return false;
                 }
 
                 return false;
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
index f087b78..1842a9ec 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.common.SubmitRequirementInfo;
 import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.SubmitRule;
 import com.google.inject.Module;
@@ -67,7 +66,7 @@
 
   private static class CustomSubmitRule implements SubmitRule {
     @Override
-    public Collection<SubmitRecord> evaluate(ChangeData changeData, SubmitRuleOptions options) {
+    public Collection<SubmitRecord> evaluate(ChangeData changeData) {
       SubmitRecord record = new SubmitRecord();
       record.labels = new ArrayList<>();
       record.status = SubmitRecord.Status.NOT_READY;
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index 56a9b69..15b9a93 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -46,7 +46,6 @@
 import com.google.gerrit.server.logging.PerformanceLogger;
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.project.CreateProjectArgs;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.SubmitRule;
 import com.google.gerrit.server.validators.ProjectCreationValidationListener;
@@ -680,7 +679,7 @@
     boolean failOnce;
 
     @Override
-    public Collection<SubmitRecord> evaluate(ChangeData changeData, SubmitRuleOptions options) {
+    public Collection<SubmitRecord> evaluate(ChangeData changeData) {
       if (failOnce) {
         failOnce = false;
         throw new IllegalStateException("forced failure from test");
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
index 83782c9..37237c6 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitRequirement;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
 import com.google.inject.Inject;
 import java.util.Collection;
@@ -42,8 +41,7 @@
     PushOneCommit.Result r = createChange();
     approve(r.getChangeId());
 
-    Collection<SubmitRecord> submitRecords =
-        rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+    Collection<SubmitRecord> submitRecords = rule.evaluate(r.getChange());
 
     assertThat(submitRecords).hasSize(1);
     SubmitRecord result = submitRecords.iterator().next();
@@ -69,8 +67,7 @@
     // Approve as admin
     approve(r.getChangeId());
 
-    Collection<SubmitRecord> submitRecords =
-        rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+    Collection<SubmitRecord> submitRecords = rule.evaluate(r.getChange());
     assertThat(submitRecords).isEmpty();
   }
 
@@ -81,8 +78,7 @@
     PushOneCommit.Result r = createChange();
     approve(r.getChangeId());
 
-    Collection<SubmitRecord> submitRecords =
-        rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+    Collection<SubmitRecord> submitRecords = rule.evaluate(r.getChange());
     assertThat(submitRecords).isEmpty();
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
index c6f2024..efc3b5b 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
@@ -21,8 +21,8 @@
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.rules.PrologOptions;
 import com.google.gerrit.server.rules.PrologRuleEvaluator;
 import com.google.gerrit.testing.TestChanges;
 import com.google.inject.Inject;
@@ -156,6 +156,6 @@
   }
 
   private PrologRuleEvaluator makeEvaluator() {
-    return evaluatorFactory.create(makeChangeData(), SubmitRuleOptions.defaults());
+    return evaluatorFactory.create(makeChangeData(), PrologOptions.defaultOptions());
   }
 }
diff --git a/javatests/com/google/gerrit/json/JsonEnumMappingTest.java b/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
index 6e57b01..dd710f9 100644
--- a/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
+++ b/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
@@ -71,10 +71,6 @@
 
   private static class TestData {
     TestEnum value;
-
-    public TestData(TestEnum value) {
-      this.value = value;
-    }
   }
 
   private enum TestEnum {
diff --git a/polygerrit-ui/app/behaviors/fire-behavior/fire-behavior.html b/polygerrit-ui/app/behaviors/fire-behavior/fire-behavior.html
new file mode 100644
index 0000000..b5afab1
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/fire-behavior/fire-behavior.html
@@ -0,0 +1,55 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<script>
+(function(window) {
+  'use strict';
+
+  window.Gerrit = window.Gerrit || {};
+
+  /** @polymerBehavior Gerrit.FireBehavior */
+  Gerrit.FireBehavior = {
+    /**
+     * Dispatches a custom event with an optional detail value.
+     *
+     * @param {string} type Name of event type.
+     * @param {*=} detail Detail value containing event-specific
+     *   payload.
+     * @param {{ bubbles: (boolean|undefined), cancelable: (boolean|undefined),
+     *     composed: (boolean|undefined) }=}
+     *  options Object specifying options.  These may include:
+     *  `bubbles` (boolean, defaults to `true`),
+     *  `cancelable` (boolean, defaults to false), and
+     *  `composed` (boolean, defaults to true).
+     * @return {!Event} The new event that was fired.
+     * @override
+     */
+    fire(type, detail, options) {
+      options = options || {};
+      detail = (detail === null || detail === undefined) ? {} : detail;
+      const event = new Event(type, {
+        bubbles: options.bubbles === undefined ? true : options.bubbles,
+        cancelable: Boolean(options.cancelable),
+        composed: options.composed === undefined ? true: options.composed,
+      });
+      event.detail = detail;
+      this.dispatchEvent(event);
+      return event;
+    },
+  };
+})(window);
+</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html
index cddbbf3..6eceb25 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
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 9841e67..fbde87b 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
@@ -71,6 +71,11 @@
 
     behaviors: [
       Gerrit.AccessBehavior,
+      /**
+       * Unused in this element, but called by other elements in tests
+       * e.g gr-repo-access_test.
+       */
+      Gerrit.FireBehavior,
     ],
 
     listeners: {
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html
index b2a3105..bf1086c 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
 <link rel="import" href="../../../styles/gr-table-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 aca691a..edf5845 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
@@ -68,6 +68,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.ListViewBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
index b7932dd..9d8ee18 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
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 9c0e405..a99c9b1 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
@@ -44,6 +44,10 @@
       itemType: String,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     _handleConfirmTap(e) {
       e.preventDefault();
       e.stopPropagation();
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
index 518905c..7b88f9e 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
@@ -19,6 +19,7 @@
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 7eec959..128eb85 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
@@ -50,6 +50,11 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      /**
+       * Unused in this element, but called by other elements in tests
+       * e.g gr-repo-commands_test.
+       */
+      Gerrit.FireBehavior,
       Gerrit.URLEncodingBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
index 08131ab..b2e8045 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/gr-table-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
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 966f3c9..3b41811 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
@@ -33,6 +33,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.ListViewBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
index 52cbfb3..d37b49f 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
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 8a262b8..915f748 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
@@ -66,6 +66,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
       Gerrit.URLEncodingBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.html b/polygerrit-ui/app/elements/admin/gr-group/gr-group.html
index a3bbddc..b7654c8 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.html
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.html
@@ -15,6 +15,7 @@
 limitations under the License.
 -->
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
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 095a0f9..deccab6 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
@@ -89,6 +89,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     observers: [
       '_handleConfigName(_groupConfig.name)',
       '_handleConfigOwner(_groupConfig.owner, _groupConfigOwner)',
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
index 78c30ff..bf11f37 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
 <link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
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 64e156b..02914e5 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
@@ -76,6 +76,11 @@
 
     behaviors: [
       Gerrit.AccessBehavior,
+      /**
+       * Unused in this element, but called by other elements in tests
+       * e.g gr-access-section_test.
+       */
+      Gerrit.FireBehavior,
     ],
 
     observers: [
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
index 26f37e1..6ef84bf 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
@@ -16,6 +16,7 @@
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
 <link rel="import" href="../../../styles/gr-table-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 d533f98..9656eee 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
@@ -65,6 +65,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.ListViewBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
index 40d32d4..408a4d1 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
@@ -17,8 +17,9 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
-<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
+<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 
 <link rel="import" href="../../../styles/gr-menu-page-styles.html">
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 7d47d08..61362a8 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
@@ -118,6 +118,7 @@
     behaviors: [
       Gerrit.AccessBehavior,
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
       Gerrit.URLEncodingBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html
index 63c2a97..5089f34 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../../styles/gr-subpage-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 169672a..b6f7039 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
@@ -42,6 +42,10 @@
       _canCreate: Boolean,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     attached() {
       this._loadRepo();
 
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
index 6cc0958..29f70dc 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
@@ -15,6 +15,7 @@
 limitations under the License.
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
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 72ee83d..6b20289 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
@@ -33,6 +33,10 @@
       _dashboards: Array,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     _repoChanged(repo) {
       this._loading = true;
       if (!repo) { return Promise.resolve(); }
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html
index e284201..964ee11 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html
@@ -19,6 +19,7 @@
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../../styles/gr-table-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 8b91977..e50c498 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
@@ -85,6 +85,7 @@
 
     behaviors: [
       Gerrit.ListViewBehavior,
+      Gerrit.FireBehavior,
       Gerrit.URLEncodingBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
index 52f143c..7eb10337 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
@@ -18,6 +18,7 @@
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 
 <link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
 <link rel="import" href="../../shared/gr-download-commands/gr-download-commands.html">
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 fded936..37eff2c 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
@@ -109,6 +109,10 @@
       _schemesObj: Object,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     observers: [
       '_handleConfigChanged(_repoConfig.*)',
     ],
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html
index 7ba541f..5a90878 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html
@@ -18,6 +18,7 @@
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
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 4b01b18..f206514 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
@@ -95,6 +95,11 @@
     behaviors: [
       Gerrit.AccessBehavior,
       Gerrit.BaseUrlBehavior,
+      /**
+       * Unused in this element, but called by other elements in tests
+       * e.g gr-permission_test.
+       */
+      Gerrit.FireBehavior,
       Gerrit.URLEncodingBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
index 1ca5668..8a16c47 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
@@ -18,6 +18,7 @@
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../shared/gr-icons/gr-icons.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
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 d546fe3..e72b788 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
@@ -41,6 +41,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
       Gerrit.URLEncodingBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
index 6167630..71ef6e4 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
@@ -21,6 +21,7 @@
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/gr-change-list-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
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 57d6f8a..8500e3b 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
@@ -105,6 +105,7 @@
     behaviors: [
       Gerrit.BaseUrlBehavior,
       Gerrit.ChangeTableBehavior,
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.RESTClientBehavior,
       Gerrit.URLEncodingBehavior,
@@ -158,6 +159,11 @@
     },
 
     _computePreferences(account, preferences) {
+      // Polymer 2: check for undefined
+      if ([account, preferences].some(arg => arg === undefined)) {
+        return;
+      }
+
       this.changeTableColumns = this.columnNames;
 
       if (account) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
index 4360d5d..6e58166 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../change-list/gr-change-list/gr-change-list.html">
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 608aaf7..2b3c5c8 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
@@ -76,6 +76,7 @@
     ],
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.RESTClientBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
index 95ef881..03fc606 100644
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
+++ b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
index 147d1f2..1cb1ca5 100644
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
+++ b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
@@ -76,6 +76,7 @@
 
     behaviors: [
       Gerrit.AnonymousNameBehavior,
+      Gerrit.FireBehavior,
     ],
 
     attached() {
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
index 9cb936a..31c1be5 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
+++ b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
 <link rel="import" href="../gr-account-entry/gr-account-entry.html">
 <link rel="import" href="../../../styles/shared-styles.html">
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
index c10f3d5..479fee2 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
+++ b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
@@ -84,6 +84,11 @@
       },
     },
 
+    behaviors: [
+      // Used in the tests for gr-account-list and other elements tests.
+      Gerrit.FireBehavior,
+    ],
+
     listeners: {
       remove: '_handleRemove',
     },
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index 1e50e4b..8de59f4 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="../../admin/gr-create-change-dialog/gr-create-change-dialog.html">
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 881e7f4..54e2edd 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
@@ -408,6 +408,7 @@
     RevisionActions,
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.RESTClientBehavior,
     ],
@@ -589,6 +590,15 @@
 
     _actionsChanged(actionsChangeRecord, revisionActionsChangeRecord,
         additionalActionsChangeRecord) {
+      // Polymer 2: check for undefined
+      if ([
+        actionsChangeRecord,
+        revisionActionsChangeRecord,
+        additionalActionsChangeRecord,
+      ].some(arg => arg === undefined)) {
+        return;
+      }
+
       const additionalActions = (additionalActionsChangeRecord &&
           additionalActionsChangeRecord.base) || [];
       this.hidden = this._keyCount(actionsChangeRecord) === 0 &&
@@ -619,6 +629,15 @@
 
     _editStatusChanged(editMode, editPatchsetLoaded,
         editBasedOnCurrentPatchSet, disableEdit) {
+      // Polymer 2: check for undefined
+      if ([
+        editMode,
+        editBasedOnCurrentPatchSet,
+        disableEdit,
+      ].some(arg => arg === undefined)) {
+        return;
+      }
+
       if (disableEdit) {
         this._deleteAndNotify('publishEdit');
         this._deleteAndNotify('rebaseEdit');
@@ -1343,6 +1362,17 @@
      */
     _computeAllActions(changeActionsRecord, revisionActionsRecord,
         primariesRecord, additionalActionsRecord, change) {
+      // Polymer 2: check for undefined
+      if ([
+        changeActionsRecord,
+        revisionActionsRecord,
+        primariesRecord,
+        additionalActionsRecord,
+        change,
+      ].some(arg => arg === undefined)) {
+        return [];
+      }
+
       const revisionActionValues = this._getActionValues(revisionActionsRecord,
           primariesRecord, additionalActionsRecord, ActionType.REVISION);
       const changeActionValues = this._getActionValues(changeActionsRecord,
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 154fc36..6ee5a24 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
@@ -250,6 +250,7 @@
 
     _computeTopicReadOnly(mutable, change) {
       return !mutable ||
+          !change ||
           !change.actions ||
           !change.actions.topic ||
           !change.actions.topic.enabled;
@@ -257,6 +258,7 @@
 
     _computeHashtagReadOnly(mutable, change) {
       return !mutable ||
+          !change ||
           !change.actions ||
           !change.actions.hashtags ||
           !change.actions.hashtags.enabled;
@@ -264,6 +266,7 @@
 
     _computeAssigneeReadOnly(mutable, change) {
       return !mutable ||
+          !change ||
           !change.actions ||
           !change.actions.assignee ||
           !change.actions.assignee.enabled;
@@ -297,7 +300,7 @@
      *     the push validation.
      */
     _computePushCertificateValidation(serverConfig, change) {
-      if (!serverConfig || !serverConfig.receive ||
+      if (!change || !serverConfig || !serverConfig.receive ||
           !serverConfig.receive.enable_signed_push) {
         return null;
       }
@@ -438,7 +441,7 @@
     },
 
     _computeParents(change) {
-      if (!change.current_revision ||
+      if (!change || !change.current_revision ||
           !change.revisions[change.current_revision] ||
           !change.revisions[change.current_revision].commit) {
         return undefined;
@@ -447,13 +450,13 @@
     },
 
     _computeParentsLabel(parents) {
-      return parents.length > 1 ? 'Parents' : 'Parent';
+      return parents && parents.length > 1 ? 'Parents' : 'Parent';
     },
 
     _computeParentListClass(parents, parentIsCurrent) {
       return [
         'parentList',
-        parents.length > 1 ? 'merge' : 'nonMerge',
+        parents && parents.length > 1 ? 'merge' : 'nonMerge',
         parentIsCurrent ? 'current' : 'notCurrent',
       ].join(' ');
     },
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 4860490..ec79438 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
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 ae361d7..4935937 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
@@ -60,6 +60,7 @@
   };
 
   const CHANGE_DATA_TIMING_LABEL = 'ChangeDataLoaded';
+  const CHANGE_RELOAD_TIMING_LABEL = 'ChangeReloaded';
   const SEND_REPLY_TIMING_LABEL = 'SendReply';
 
   Polymer({
@@ -281,6 +282,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.RESTClientBehavior,
@@ -302,6 +304,7 @@
       '_labelsChanged(_change.labels.*)',
       '_paramsAndChangeChanged(params, _change)',
       '_patchNumChanged(_patchRange.patchNum)',
+      '_loadDynamicTabHeaderAndContent(_change, _selectedRevision)',
     ],
 
     keyboardShortcuts() {
@@ -336,17 +339,6 @@
         this._setDiffViewMode();
       });
 
-      Gerrit.awaitPluginsLoaded().then(() => {
-        this._dynamicTabHeaderEndpoints =
-            Gerrit._endpoints.getDynamicEndpoints('change-view-tab-header');
-        this._dynamicTabContentEndpoints =
-            Gerrit._endpoints.getDynamicEndpoints('change-view-tab-content');
-        if (this._dynamicTabContentEndpoints.length
-            !== this._dynamicTabHeaderEndpoints.length) {
-          console.warn('Different number of tab headers and tab content.');
-        }
-      });
-
       this.addEventListener('comment-save', this._handleCommentSave.bind(this));
       this.addEventListener('comment-refresh', this._reloadDrafts.bind(this));
       this.addEventListener('comment-discard',
@@ -463,8 +455,20 @@
     },
 
     _computeChangeStatusChips(change, mergeable) {
+      // Polymer 2: check for undefined
+      if ([
+        change,
+        mergeable,
+      ].some(arg => arg === undefined)) {
+        // To keep consistent with Polymer 1, we are returning undefined
+        // if not all dependencies are defined
+        return undefined;
+      }
+
       // Show no chips until mergeability is loaded.
-      if (mergeable === null || mergeable === undefined) { return []; }
+      if (mergeable === null) {
+        return [];
+      }
 
       const options = {
         includeDerived: true,
@@ -475,7 +479,8 @@
     },
 
     _computeHideEditCommitMessage(loggedIn, editing, change, editMode) {
-      if (!loggedIn || editing || change.status === this.ChangeStatus.MERGED ||
+      if (!loggedIn || editing ||
+          (change && change.status === this.ChangeStatus.MERGED) ||
           editMode) {
         return true;
       }
@@ -769,7 +774,47 @@
       });
     },
 
-    _paramsAndChangeChanged(value) {
+    /**
+     * We use an observer to observe 'change' and 'selectedRevision'
+     * variables. This fixes an issue under Polymer 2 so that the dynamic
+     * plugins loads when these variables load.
+     */
+    _loadDynamicTabHeaderAndContent(change, selectedRevision) {
+      // These vars are unused, but because primaryTabs extension point
+      // uses it, we makes sure we doin't load the plugin until these vars
+      // exist.
+      if (!change || !selectedRevision) return;
+
+      // We cache the _dynamicTabHeaderEndpoints and _dynamicTabContentEndpoints
+      // var so that we doin't keep loading the same dynamic plugin
+      // over and over when 'change' or 'selectedRevision' change.
+      if (this._dynamicTabHeaderEndpoints || this._dynamicTabContentEndpoints) {
+        return;
+      }
+
+      Gerrit.awaitPluginsLoaded().then(() => {
+        this._dynamicTabHeaderEndpoints =
+            Gerrit._endpoints.getDynamicEndpoints('change-view-tab-header');
+        this._dynamicTabContentEndpoints =
+            Gerrit._endpoints.getDynamicEndpoints('change-view-tab-content');
+        if (this._dynamicTabContentEndpoints.length
+            !== this._dynamicTabHeaderEndpoints.length) {
+          console.warn('Different number of tab headers and tab content.');
+        }
+      }).then(() => {
+        // We need a second then(..) to ensure that the dynamic endpoints
+        // are created before we call _performPostLoadTasks(). This ensures it has
+        // enough time before the primary tab gets selected.
+        this._performPostLoadTasks();
+      });
+    },
+
+    _paramsAndChangeChanged(value, change) {
+      // Polymer 2: check for undefined
+      if ([value, change].some(arg => arg === undefined)) {
+        return;
+      }
+
       // If the change number or patch range is different, then reset the
       // selected file index.
       const patchRangeState = this.viewState.patchRange;
@@ -908,8 +953,8 @@
       return 'PARENT';
     },
 
-    _computeShowPrimaryTabs(dynamicTabContentEndpoints) {
-      return dynamicTabContentEndpoints.length > 0;
+    _computeShowPrimaryTabs(dynamicTabHeaderEndpoints) {
+      return dynamicTabHeaderEndpoints && dynamicTabHeaderEndpoints.length > 0;
     },
 
     _computeChangeUrl(change) {
@@ -942,6 +987,11 @@
     },
 
     _computeChangeIdCommitMessageError(commitMessage, change) {
+      // Polymer 2: check for undefined
+      if ([commitMessage, change].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (!commitMessage) { return CHANGE_ID_ERROR.MISSING; }
 
       // Find the last match in the commit message:
@@ -996,6 +1046,11 @@
     },
 
     _computeReplyButtonLabel(changeRecord, canStartReview) {
+      // Polymer 2: check for undefined
+      if ([changeRecord, canStartReview].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (canStartReview) {
         return 'Start review';
       }
@@ -1357,15 +1412,17 @@
 
     /**
      * Reload the change.
-     * @param {boolean=} opt_reloadRelatedChanges Reloads the related chanegs
-     *     when true.
+     * @param {boolean=} opt_isLocationChange Reloads the related changes
+     *     when true and ends reporting events that started on location change.
      * @return {Promise} A promise that resolves when the core data has loaded.
      *     Some non-core data loading may still be in-flight when the core data
      *     promise resolves.
      */
-    _reload(opt_reloadRelatedChanges) {
+    _reload(opt_isLocationChange) {
       this._loading = true;
       this._relatedChangesCollapsed = true;
+      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 = [];
@@ -1379,7 +1436,12 @@
       // change content may start appearing.
       const loadingFlagSet = detailCompletes
           .then(() => { this._loading = false; })
-          .then(() => { this.$.reporting.changeDisplayed(); });
+          .then(() => {
+            this.$.reporting.timeEnd(CHANGE_RELOAD_TIMING_LABEL);
+            if (opt_isLocationChange) {
+              this.$.reporting.changeDisplayed();
+            }
+          });
 
       // Resolves when the project config has loaded.
       const projectConfigLoaded = detailCompletes
@@ -1439,16 +1501,17 @@
         coreDataPromise = mergeabilityLoaded;
       }
 
-      if (opt_reloadRelatedChanges) {
+      if (opt_isLocationChange) {
         const relatedChangesLoaded = coreDataPromise
             .then(() => this.$.relatedChanges.reload());
         allDataPromises.push(relatedChangesLoaded);
       }
 
-      this.$.reporting.time(CHANGE_DATA_TIMING_LABEL);
       Promise.all(allDataPromises).then(() => {
         this.$.reporting.timeEnd(CHANGE_DATA_TIMING_LABEL);
-        this.$.reporting.changeFullyLoaded();
+        if (opt_isLocationChange) {
+          this.$.reporting.changeFullyLoaded();
+        }
       });
 
       return coreDataPromise;
@@ -1698,6 +1761,10 @@
     },
 
     _computeEditMode(patchRangeRecord, paramsRecord) {
+      if ([patchRangeRecord, paramsRecord].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (paramsRecord.base && paramsRecord.base.edit) { return true; }
 
       const patchRange = patchRangeRecord.base || {};
@@ -1783,7 +1850,7 @@
     },
 
     _computeCurrentRevision(currentRevision, revisions) {
-      return revisions && revisions[currentRevision];
+      return currentRevision && revisions && revisions[currentRevision];
     },
 
     _computeDiffPrefsDisabled(disableDiffPrefs, loggedIn) {
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 e8de93b..79d8d93 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
@@ -1844,5 +1844,50 @@
       MockInteractions.tap(element.$.changeStar.$$('button'));
       assert.isTrue(stub.called);
     });
+
+    suite('gr-reporting tests', () => {
+      setup(() => {
+        element._patchRange = {
+          basePatchNum: 'PARENT',
+          patchNum: 1,
+        };
+        sandbox.stub(element, '_getChangeDetail').returns(Promise.resolve());
+        sandbox.stub(element, '_getProjectConfig').returns(Promise.resolve());
+        sandbox.stub(element, '_reloadComments').returns(Promise.resolve());
+        sandbox.stub(element, '_getMergeability').returns(Promise.resolve());
+        sandbox.stub(element, '_getLatestCommitMessage')
+            .returns(Promise.resolve());
+      });
+
+      test('don\'t report changedDisplayed on reply', done => {
+        const changeDisplayStub =
+          sandbox.stub(element.$.reporting, 'changeDisplayed');
+        const changeFullyLoadedStub =
+          sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+        element._handleReplySent();
+        flush(() => {
+          assert.isFalse(changeDisplayStub.called);
+          assert.isFalse(changeFullyLoadedStub.called);
+          done();
+        });
+      });
+
+      test('report changedDisplayed on _paramsChanged', done => {
+        const changeDisplayStub =
+          sandbox.stub(element.$.reporting, 'changeDisplayed');
+        const changeFullyLoadedStub =
+          sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+        element._paramsChanged({
+          view: Gerrit.Nav.View.CHANGE,
+          changeNum: 101,
+          project: 'test-project',
+        });
+        flush(() => {
+          assert.isTrue(changeDisplayStub.called);
+          assert.isTrue(changeFullyLoadedStub.called);
+          done();
+        });
+      });
+    });
   });
 </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 ddab319..7e25180 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
@@ -47,11 +47,21 @@
     },
 
     _computeShowWebLink(change, commitInfo, serverConfig) {
+      // Polymer 2: check for undefined
+      if ([change, commitInfo, serverConfig].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const weblink = this._getWeblink(change, commitInfo, serverConfig);
       return !!weblink && !!weblink.url;
     },
 
     _computeWebLink(change, commitInfo, serverConfig) {
+      // Polymer 2: check for undefined
+      if ([change, commitInfo, serverConfig].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const {url} = this._getWeblink(change, commitInfo, serverConfig) || {};
       return url;
     },
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 d25a871..f271a70 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
@@ -121,6 +121,7 @@
           },
         ],
       };
+      element.serverConfig = {};
 
       assert.isOk(element._computeShowWebLink(element.change,
           element.commitInfo, element.serverConfig));
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
index 145d126..3a27c01 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
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 a371e13..6b7163a 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
@@ -38,6 +38,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html
index 55b14b1..b9e9155 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
 
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 2e8af0e..b26842a 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
@@ -21,6 +21,10 @@
     is: 'gr-confirm-cherrypick-conflict-dialog',
     _legacyUndefinedCheck: true,
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     /**
      * Fired when the confirm button is pressed.
      *
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
index 767b62d..303a55c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
@@ -18,6 +18,7 @@
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
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 fa281ec..2f098de 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
@@ -51,11 +51,24 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     observers: [
       '_computeMessage(changeStatus, commitNum, commitMessage)',
     ],
 
     _computeMessage(changeStatus, commitNum, commitMessage) {
+      // Polymer 2: check for undefined
+      if ([
+        changeStatus,
+        commitNum,
+        commitMessage,
+      ].some(arg => arg === undefined)) {
+        return;
+      }
+
       let newMessage = commitMessage;
 
       if (changeStatus === 'MERGED') {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
index c07bb37..1db5b52 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
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 093dca6..2d3c0e3 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
@@ -47,6 +47,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     _handleConfirmTap(e) {
       e.preventDefault();
       this.fire('confirm', null, {bubbles: false});
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 1c3bcfb..22ce27e 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
@@ -144,6 +144,11 @@
      * the corresponding value to be submitted.
      */
     _updateSelectedOption(rebaseOnCurrent, hasParent) {
+      // Polymer 2: check for undefined
+      if ([rebaseOnCurrent, hasParent].some(arg => arg === undefined)) {
+        return;
+      }
+
       if (this._displayParentOption(rebaseOnCurrent, hasParent)) {
         this.$.rebaseOnParentInput.checked = true;
       } else if (this._displayTipOption(rebaseOnCurrent, hasParent)) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
index c68912c..e7dd6ac 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
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 93e21c7..f4ad002 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
@@ -40,6 +40,10 @@
       message: String,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     populateRevertMessage(message, commitHash) {
       // Figure out what the revert title should be.
       const originalTitle = message.split('\n')[0];
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
index ab7525f..95f63ee 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 9bfe3a9..eb6407d 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
@@ -48,6 +48,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.RESTClientBehavior,
     ],
@@ -156,6 +157,11 @@
     },
 
     _computeSchemes(change, patchNum) {
+      // Polymer 2: check for undefined
+      if ([change, patchNum].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       for (const rev of Object.values(change.revisions || {})) {
         if (this.patchNumEquals(rev._number, patchNum)) {
           const fetch = rev.fetch;
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
index bdb4295..224e944 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
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 250900a..d6bf92c 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
@@ -94,6 +94,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.PatchSetBehavior,
     ],
 
@@ -132,10 +133,20 @@
     },
 
     _computeDescriptionReadOnly(loggedIn, change, account) {
+      // Polymer 2: check for undefined
+      if ([loggedIn, change, account].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       return !(loggedIn && (account._account_id === change.owner._account_id));
     },
 
     _computePatchSetDescription(change, patchNum) {
+      // Polymer 2: check for undefined
+      if ([change, patchNum].some(arg => arg === undefined)) {
+        return;
+      }
+
       const rev = this.getRevisionByPatchNum(change.revisions, patchNum);
       this._patchsetDescription = (rev && rev.description) ?
           rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index f087b21..61fdac2 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -18,6 +18,7 @@
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="../../../behaviors/async-foreach-behavior/async-foreach-behavior.html">
 <link rel="import" href="../../../behaviors/dom-util-behavior/dom-util-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 db41da0..37beff4 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
@@ -185,6 +185,7 @@
     behaviors: [
       Gerrit.AsyncForeachBehavior,
       Gerrit.DomUtilBehavior,
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.PathListBehavior,
@@ -816,6 +817,17 @@
     },
 
     _computeFiles(filesByPath, changeComments, patchRange, reviewed, loading) {
+      // Polymer 2: check for undefined
+      if ([
+        filesByPath,
+        changeComments,
+        patchRange,
+        reviewed,
+        loading,
+      ].some(arg => arg === undefined)) {
+        return;
+      }
+
       // Await all promises resolving from reload. @See Issue 9057
       if (loading || !changeComments) { return; }
 
@@ -835,6 +847,11 @@
     },
 
     _computeFilesShown(numFilesShown, files) {
+      // Polymer 2: check for undefined
+      if ([numFilesShown, files].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const previousNumFilesShown = this._shownFiles ?
           this._shownFiles.length : 0;
 
@@ -903,6 +920,11 @@
     },
 
     _computePatchSetDescription(revisions, patchNum) {
+      // Polymer 2: check for undefined
+      if ([revisions, patchNum].some(arg => arg === undefined)) {
+        return '';
+      }
+
       const rev = this.getRevisionByPatchNum(revisions, patchNum);
       return (rev && rev.description) ?
           rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html
index d5d8128..8fa6b08 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 
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 7755a60..4f63626 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
@@ -45,6 +45,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     loadData() {
       if (!this.changeNum) { return; }
       this._filterText = '';
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 fe28a1d..9c3d69a 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
@@ -148,6 +148,11 @@
     },
 
     _computePermittedLabelValues(permittedLabels, label) {
+      // Polymer 2: check for undefined
+      if ([permittedLabels, label].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       return permittedLabels[label];
     },
 
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 f18680e..548798f 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
@@ -82,7 +82,12 @@
       return null;
     },
 
-    _computeLabels(labelRecord) {
+    _computeLabels(labelRecord, account) {
+      // Polymer 2: check for undefined
+      if ([labelRecord, account].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const labelsObj = labelRecord.base;
       if (!labelsObj) { return []; }
       return Object.keys(labelsObj).sort().map(key => {
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index df3cc37..b828327 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-icon/iron-icon.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-account-label/gr-account-label.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
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 a2ec28c..4072508 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -104,6 +104,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     observers: [
       '_updateExpandedClass(message.expanded)',
     ],
@@ -140,7 +144,7 @@
     },
 
     _computeShowReplyButton(message, loggedIn) {
-      return !!message.message && loggedIn &&
+      return message && !!message.message && loggedIn &&
           !this._computeIsAutomated(message);
     },
 
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 81c3322..13c1754 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
@@ -117,6 +117,11 @@
     },
 
     _computeItems(messages, reviewerUpdates) {
+      // Polymer 2: check for undefined
+      if ([messages, reviewerUpdates].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       messages = messages || [];
       reviewerUpdates = reviewerUpdates || [];
       let mi = 0;
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
index 4a70506..5d3291d 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
@@ -16,6 +16,7 @@
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
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 07bf139..aa56550 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
@@ -78,6 +78,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.RESTClientBehavior,
     ],
@@ -291,6 +292,17 @@
 
     _resultsChanged(related, submittedTogether, conflicts,
         cherryPicks, sameTopic) {
+      // Polymer 2: check for undefined
+      if ([
+        related,
+        submittedTogether,
+        conflicts,
+        cherryPicks,
+        sameTopic,
+      ].some(arg => arg === undefined)) {
+        return;
+      }
+
       const results = [
         related && related.changes,
         submittedTogether && submittedTogether.changes,
@@ -313,6 +325,11 @@
     },
 
     _computeConnectedRevisions(change, patchNum, relatedChanges) {
+      // Polymer 2: check for undefined
+      if ([change, patchNum, relatedChanges].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const connected = [];
       let changeRevision;
       if (!change) { return []; }
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index 6612180..08a3ec3 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
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 cd93a2c..3b97439 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
@@ -226,6 +226,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.RESTClientBehavior,
@@ -330,8 +331,8 @@
         let account;
         // Remove any accounts that already exist as a CC.
         for (const splice of splices.indexSplices) {
-          for (const addedKey of splice.addedKeys) {
-            account = this.get(`_reviewers.${addedKey}`);
+          for (let i = 0; i < splice.addedCount; i++) {
+            account = splice.object[splice.index + i];
             key = this._accountOrGroupKey(account);
             index = this._ccs.findIndex(
                 account => this._accountOrGroupKey(account) === key);
@@ -622,6 +623,11 @@
     },
 
     _changeUpdated(changeRecord, owner) {
+      // Polymer 2: check for undefined
+      if ([changeRecord, owner].some(arg => arg === undefined)) {
+        return;
+      }
+
       this._rebuildReviewerArrays(changeRecord.base, owner);
     },
 
@@ -855,6 +861,19 @@
 
     _computeSendButtonDisabled(buttonLabel, drafts, text, reviewersMutated,
         labelsChanged, includeComments, disabled) {
+      // Polymer 2: check for undefined
+      if ([
+        buttonLabel,
+        drafts,
+        text,
+        reviewersMutated,
+        labelsChanged,
+        includeComments,
+        disabled,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (disabled) { return true; }
       if (buttonLabel === ButtonLabels.START_REVIEW) { return false; }
       const hasDrafts = includeComments && Object.keys(drafts).length;
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 e137eef..665ca63 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
@@ -1093,20 +1093,84 @@
 
     test('_computeSendButtonDisabled', () => {
       const fn = element._computeSendButtonDisabled.bind(element);
-      assert.isFalse(fn('Start review'));
-      assert.isTrue(fn('Send', {}, '', false, false, false));
+      assert.isFalse(fn(
+          /* buttonLabel= */ 'Start review',
+          /* drafts= */ {},
+          /* text= */ '',
+          /* reviewersMutated= */ false,
+          /* labelsChanged= */ false,
+          /* includeComments= */ false,
+          /* disabled= */ false,
+      ));
+      assert.isTrue(fn(
+          /* buttonLabel= */ 'Send',
+          /* drafts= */ {},
+          /* text= */ '',
+          /* reviewersMutated= */ false,
+          /* labelsChanged= */ false,
+          /* includeComments= */ false,
+          /* disabled= */ false,
+      ));
       // Mock nonempty comment draft array, with seding comments.
-      assert.isFalse(fn('Send', {file: ['draft']}, '', false, false, true));
+      assert.isFalse(fn(
+          /* buttonLabel= */ 'Send',
+          /* drafts= */ {file: ['draft']},
+          /* text= */ '',
+          /* reviewersMutated= */ false,
+          /* labelsChanged= */ false,
+          /* includeComments= */ true,
+          /* disabled= */ false,
+      ));
       // Mock nonempty comment draft array, without seding comments.
-      assert.isTrue(fn('Send', {file: ['draft']}, '', false, false, false));
+      assert.isTrue(fn(
+          /* buttonLabel= */ 'Send',
+          /* drafts= */ {file: ['draft']},
+          /* text= */ '',
+          /* reviewersMutated= */ false,
+          /* labelsChanged= */ false,
+          /* includeComments= */ false,
+          /* disabled= */ false,
+      ));
       // Mock nonempty change message.
-      assert.isFalse(fn('Send', {}, 'test', false, false, false));
+      assert.isFalse(fn(
+          /* buttonLabel= */ 'Send',
+          /* drafts= */ {},
+          /* text= */ 'test',
+          /* reviewersMutated= */ false,
+          /* labelsChanged= */ false,
+          /* includeComments= */ false,
+          /* disabled= */ false,
+      ));
       // Mock reviewers mutated.
-      assert.isFalse(fn('Send', {}, '', true, false, false));
+      assert.isFalse(fn(
+          /* buttonLabel= */ 'Send',
+          /* drafts= */ {},
+          /* text= */ '',
+          /* reviewersMutated= */ true,
+          /* labelsChanged= */ false,
+          /* includeComments= */ false,
+          /* disabled= */ false,
+      ));
       // Mock labels changed.
-      assert.isFalse(fn('Send', {}, '', false, true, false));
+      assert.isFalse(fn(
+          /* buttonLabel= */ 'Send',
+          /* drafts= */ {},
+          /* text= */ '',
+          /* reviewersMutated= */ false,
+          /* labelsChanged= */ true,
+          /* includeComments= */ false,
+          /* disabled= */ false,
+      ));
       // Whole dialog is disabled.
-      assert.isTrue(fn('Send', {}, '', false, true, false, true));
+      assert.isTrue(fn(
+          /* buttonLabel= */ 'Send',
+          /* drafts= */ {},
+          /* text= */ '',
+          /* reviewersMutated= */ false,
+          /* labelsChanged= */ true,
+          /* includeComments= */ false,
+          /* disabled= */ true,
+      ));
     });
 
     test('_submit blocked when no mutations exist', () => {
@@ -1141,4 +1205,4 @@
       assert.equal(element.$.pluginMessage.textContent, 'foo');
     });
   });
-</script>
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
index 46f973e..6e2686e 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
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 1cae50a..42bcd09 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
@@ -75,6 +75,10 @@
       _xhrPromise: Object,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     observers: [
       '_reviewersChanged(change.reviewers.*, change.owner)',
     ],
@@ -161,6 +165,11 @@
     },
 
     _reviewersChanged(changeRecord, owner) {
+      // Polymer 2: check for undefined
+      if ([changeRecord, owner].some(arg => arg === undefined)) {
+        return;
+      }
+
       let result = [];
       const reviewers = changeRecord.base;
       for (const key in reviewers) {
@@ -190,6 +199,11 @@
     },
 
     _computeHiddenCount(reviewers, displayedReviewers) {
+      // Polymer 2: check for undefined
+      if ([reviewers, displayedReviewers].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       return reviewers.length - displayedReviewers.length;
     },
 
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 8f15a06..407ef0f 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
@@ -94,6 +94,15 @@
     },
 
     _computeFilteredThreads(sortedThreads, unresolvedOnly, draftsOnly) {
+      // Polymer 2: check for undefined
+      if ([
+        sortedThreads,
+        unresolvedOnly,
+        draftsOnly,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       return sortedThreads.filter(c => {
         if (draftsOnly) {
           return c.hasDraft;
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html
index ff4ab39..72b93be 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../../shared/gr-shell-command/gr-shell-command.html">
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 df96be2..1c9013e 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
@@ -58,6 +58,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     attached() {
       this.$.restAPI.getLoggedIn().then(loggedIn => {
         if (loggedIn) {
@@ -78,6 +82,15 @@
 
     _computeFetchCommand(revision, preferredDownloadCommand,
         preferredDownloadScheme) {
+      // Polymer 2: check for undefined
+      if ([
+        revision,
+        preferredDownloadCommand,
+        preferredDownloadScheme,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (!revision) { return; }
       if (!revision || !revision.fetch) { return; }
 
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 66d0a01..577b978 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
@@ -75,31 +75,40 @@
         assert.isUndefined(element._computeFetchCommand({fetch: {}}));
       });
 
+      test('not all defined', () => {
+        assert.isUndefined(
+            element._computeFetchCommand(testRev, undefined, ''));
+        assert.isUndefined(
+            element._computeFetchCommand(testRev, '', undefined));
+        assert.isUndefined(
+            element._computeFetchCommand(undefined, '', ''));
+      });
+
       test('insufficiently defined scheme', () => {
         assert.isUndefined(
-            element._computeFetchCommand(testRev, undefined, 'badscheme'));
+            element._computeFetchCommand(testRev, '', 'badscheme'));
 
         const rev = Object.assign({}, testRev);
         rev.fetch = Object.assign({}, testRev.fetch, {nocmds: {commands: {}}});
         assert.isUndefined(
-            element._computeFetchCommand(rev, undefined, 'nocmds'));
+            element._computeFetchCommand(rev, '', 'nocmds'));
 
         rev.fetch.nocmds.commands.unsupported = 'unsupported';
         assert.isUndefined(
-            element._computeFetchCommand(rev, undefined, 'nocmds'));
+            element._computeFetchCommand(rev, '', 'nocmds'));
       });
 
       test('default scheme and command', () => {
-        const cmd = element._computeFetchCommand(testRev);
+        const cmd = element._computeFetchCommand(testRev, '', '');
         assert.isTrue(cmd === 'http checkout' || cmd === 'ssh pull');
       });
 
       test('default command', () => {
         assert.strictEqual(
-            element._computeFetchCommand(testRev, undefined, 'http'),
+            element._computeFetchCommand(testRev, '', 'http'),
             'http checkout');
         assert.strictEqual(
-            element._computeFetchCommand(testRev, undefined, 'ssh'),
+            element._computeFetchCommand(testRev, '', 'ssh'),
             'ssh pull');
       });
 
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 af4510d..2ade782 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
@@ -66,6 +66,11 @@
     },
 
     _getLinks(switchAccountUrl, path) {
+      // Polymer 2: check for undefined
+      if ([switchAccountUrl, path].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const links = [{name: 'Settings', url: '/settings/'}];
       if (switchAccountUrl) {
         const replacements = {path};
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 37d8882..e29faa8 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
@@ -80,11 +80,15 @@
     });
 
     test('switch account', () => {
+      // Missing params.
+      assert.isUndefined(element._getLinks());
+      assert.isUndefined(element._getLinks(null));
+
       // No switch account link.
-      assert.equal(element._getLinks(null).length, 2);
+      assert.equal(element._getLinks(null, '').length, 2);
 
       // Unparameterized switch account link.
-      let links = element._getLinks('/switch-account');
+      let links = element._getLinks('/switch-account', '');
       assert.equal(links.length, 3);
       assert.deepEqual(links[1], {
         name: 'Switch account',
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
index db40496..048d392 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../core/gr-error-dialog/gr-error-dialog.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../shared/gr-alert/gr-alert.html">
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 38dfecb..54cfe19 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
@@ -31,6 +31,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
     ],
 
     properties: {
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
index e153074..8b2751c 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../gr-key-binding-display/gr-key-binding-display.html">
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 f206db1..4ecbfb3 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
@@ -51,6 +51,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index 37cb317..60ccac1 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -18,6 +18,7 @@
 
 <link rel="import" href="../../../behaviors/docs-url-behavior/docs-url-behavior.html">
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html">
 <link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
 <link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
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 496f8d3..50ba9d0 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
@@ -138,6 +138,7 @@
       Gerrit.AdminNavBehavior,
       Gerrit.BaseUrlBehavior,
       Gerrit.DocsUrlBehavior,
+      Gerrit.FireBehavior,
     ],
 
     observers: [
@@ -180,6 +181,17 @@
     },
 
     _computeLinks(defaultLinks, userLinks, adminLinks, topMenus, docBaseUrl) {
+      // Polymer 2: check for undefined
+      if ([
+        defaultLinks,
+        userLinks,
+        adminLinks,
+        topMenus,
+        docBaseUrl,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const links = defaultLinks.map(menu => {
         return {
           title: menu.title,
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 7bf6728..ef14370 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
@@ -111,13 +111,24 @@
       }];
 
       // When no admin links are passed, it should use the default.
-      assert.deepEqual(element._computeLinks(defaultLinks, [], adminLinks, []),
+      assert.deepEqual(element._computeLinks(
+          defaultLinks,
+          /* userLinks= */[],
+          adminLinks,
+          /* topMenus= */[],
+          /* docBaseUrl= */ '',
+      ),
           defaultLinks.concat({
             title: 'Browse',
             links: adminLinks,
           }));
-      assert.deepEqual(
-          element._computeLinks(defaultLinks, userLinks, adminLinks, []),
+      assert.deepEqual(element._computeLinks(
+          defaultLinks,
+          userLinks,
+          adminLinks,
+          /* topMenus= */[],
+          /* docBaseUrl= */ ''
+        ),
           defaultLinks.concat([
             {
               title: 'Your',
@@ -126,7 +137,8 @@
             {
               title: 'Browse',
               links: adminLinks,
-            }]));
+            }])
+      );
     });
 
     test('documentation links', () => {
@@ -168,7 +180,13 @@
           url: 'https://gerrit/plugins/plugin-manager/static/index.html',
         }],
       }];
-      assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+      assert.deepEqual(element._computeLinks(
+          /* defaultLinks= */ [],
+          /* userLinks= */ [],
+          adminLinks,
+          topMenus,
+          /* baseDocUrl= */ '',
+      ), [{
         title: 'Browse',
         links: adminLinks,
       },
@@ -198,7 +216,13 @@
           url: '/plugins/myplugin/index.html',
         }],
       }];
-      assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+      assert.deepEqual(element._computeLinks(
+          /* defaultLinks= */ [],
+          /* userLinks= */ [],
+          adminLinks,
+          topMenus,
+          /* baseDocUrl= */ '',
+      ), [{
         title: 'Browse',
         links: adminLinks,
       },
@@ -231,7 +255,13 @@
           url: 'https://gerrit/plugins/plugin-manager/static/create.html',
         }],
       }];
-      assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+      assert.deepEqual(element._computeLinks(
+          /* defaultLinks= */ [],
+          /* userLinks= */ [],
+          adminLinks,
+          topMenus,
+          /* baseDocUrl= */ '',
+      ), [{
         title: 'Browse',
         links: adminLinks,
       }, {
@@ -262,7 +292,13 @@
           url: 'https://gerrit/plugins/plugin-manager/static/index.html',
         }],
       }];
-      assert.deepEqual(element._computeLinks(defaultLinks, [], [], topMenus), [{
+      assert.deepEqual(element._computeLinks(
+          defaultLinks,
+          /* userLinks= */ [],
+          /* adminLinks= */ [],
+          topMenus,
+          /* baseDocUrl= */ '',
+      ), [{
         title: 'Faves',
         links: defaultLinks[0].links.concat([{
           name: 'Manage',
@@ -287,7 +323,13 @@
           url: 'https://gerrit/plugins/plugin-manager/static/index.html',
         }],
       }];
-      assert.deepEqual(element._computeLinks([], userLinks, [], topMenus), [{
+      assert.deepEqual(element._computeLinks(
+          /* defaultLinks= */ [],
+          userLinks,
+          /* adminLinks= */ [],
+          topMenus,
+          /* baseDocUrl= */ '',
+      ), [{
         title: 'Your',
         links: userLinks.concat([{
           name: 'Manage',
@@ -312,7 +354,13 @@
           url: 'https://gerrit/plugins/plugin-manager/static/index.html',
         }],
       }];
-      assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+      assert.deepEqual(element._computeLinks(
+          /* defaultLinks= */ [],
+          /* userLinks= */ [],
+          adminLinks,
+          topMenus,
+          /* baseDocUrl= */ '',
+      ), [{
         title: 'Browse',
         links: adminLinks.concat([{
           name: 'Manage',
@@ -350,4 +398,4 @@
       assert.equal(element._registerText, 'Sign up');
     });
   });
-</script>
+      </script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.html b/polygerrit-ui/app/elements/core/gr-router/gr-router.html
index 6035069..71a5832 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.html
@@ -17,6 +17,7 @@
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
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 4d70bdc..f19052c 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -227,6 +227,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.URLEncodingBehavior,
     ],
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index ddbb1df..ca2c928 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -15,6 +15,7 @@
 limitations under the License.
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 <link rel="import" href="../gr-coverage-layer/gr-coverage-layer.html">
 <link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
@@ -139,6 +140,10 @@
           _cancelableRenderPromise: Object,
         },
 
+        behaviors: [
+          Gerrit.FireBehavior,
+        ],
+
         get diffElement() {
           return this.queryEffectiveChildren('#diffTable');
         },
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 5ddf97a..a019388 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
@@ -332,8 +332,11 @@
     if (line.type !== GrDiffLine.Type.BLANK) {
       td.classList.add('content');
     }
-    if (line.highlights.length === 0) {
-      td.classList.add('no-highlights');
+
+    // If intraline info is not available, the entire line will be
+    // considered as changed and marked as dark red / green color
+    if (!line.hasIntralineInfo) {
+      td.classList.add('no-intraline-info');
     }
     td.classList.add(line.type);
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
index 612aab6..0364ceb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
@@ -16,8 +16,9 @@
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
-<link rel="import" href="../gr-selection-action-box/gr-selection-action-box.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../gr-selection-action-box/gr-selection-action-box.html">
 
 <dom-module id="gr-diff-highlight">
   <template>
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 ad9e99b..1f6b372 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
@@ -36,6 +36,10 @@
       _cachedDiffBuilder: Object,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     listeners: {
       'comment-thread-mouseleave': '_handleCommentThreadMouseleave',
       'comment-thread-mouseenter': '_handleCommentThreadMouseenter',
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
index fc71be6..f25d56e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
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 2d42b00..bce3d07 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
@@ -207,6 +207,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.PatchSetBehavior,
     ],
 
@@ -741,6 +742,15 @@
     _whitespaceChanged(
         preferredWhitespaceLevel, loadedWhitespaceLevel,
         noRenderOnPrefsChange) {
+      // Polymer 2: check for undefined
+      if ([
+        preferredWhitespaceLevel,
+        loadedWhitespaceLevel,
+        noRenderOnPrefsChange,
+      ].some(arg => arg === undefined)) {
+        return;
+      }
+
       if (preferredWhitespaceLevel !== loadedWhitespaceLevel &&
           !noRenderOnPrefsChange) {
         this.reload();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
index 10d2828..02a70e1 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-diff-preferences/gr-diff-preferences.html">
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 bcc3c2c..9a9a6d7 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
@@ -28,6 +28,10 @@
       _diffPrefsChanged: Boolean,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     getFocusStops() {
       return {
         start: this.$.contextSelect,
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 ccc3bb2..d4b4e2b 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
@@ -437,7 +437,10 @@
       if (type !== GrDiffLine.Type.ADD) line.beforeNumber = offsetLeft + i;
       if (type !== GrDiffLine.Type.REMOVE) line.afterNumber = offsetRight + i;
       if (opt_highlights) {
+        line.hasIntralineInfo = true;
         line.highlights = opt_highlights.filter(hl => hl.contentIndex === i);
+      } else {
+        line.hasIntralineInfo = false;
       }
       return line;
     },
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
index c992a51..146c592 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
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 d37c97d..eb38ba7 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
@@ -177,6 +177,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.PathListBehavior,
@@ -259,6 +260,11 @@
     },
 
     _getFiles(changeNum, patchRangeRecord) {
+      // Polymer 2: check for undefined
+      if ([changeNum, patchRangeRecord].some(arg => arg === undefined)) {
+        return;
+      }
+
       const patchRange = patchRangeRecord.base;
       return this.$.restAPI.getChangeFilePathsAsSpeciallySortedArray(
           changeNum, patchRange).then(files => {
@@ -597,11 +603,11 @@
       this._initCursor(this.params);
 
       this._changeNum = value.changeNum;
+      this._path = value.path;
       this._patchRange = {
         patchNum: value.patchNum,
         basePatchNum: value.basePatchNum || PARENT,
       };
-      this._path = value.path;
 
       // NOTE: This may be called before attachment (e.g. while parentElement is
       // null). Fire title-change in an async so that, if attachment to the DOM
@@ -679,6 +685,11 @@
     },
 
     _setReviewedObserver(_loggedIn, paramsRecord, _prefs) {
+      // Polymer 2: check for undefined
+      if ([_loggedIn, paramsRecord, _prefs].some(arg => arg === undefined)) {
+        return;
+      }
+
       const params = paramsRecord.base || {};
       if (!_loggedIn) { return; }
 
@@ -783,6 +794,15 @@
     },
 
     _formatFilesForDropdown(fileList, patchNum, changeComments) {
+      // Polymer 2: check for undefined
+      if ([
+        fileList,
+        patchNum,
+        changeComments,
+      ].some(arg => arg === undefined)) {
+        return;
+      }
+
       if (!fileList) { return; }
       const dropdownContent = [];
       for (const path of fileList) {
@@ -920,6 +940,15 @@
     },
 
     _computeCommentSkips(commentMap, fileList, path) {
+      // Polymer 2: check for undefined
+      if ([
+        commentMap,
+        fileList,
+        path,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const skips = {previous: null, next: null};
       if (!fileList.length) { return skips; }
       const pathIndex = fileList.indexOf(path);
@@ -1000,6 +1029,11 @@
     },
 
     _computeFileNum(file, files) {
+      // Polymer 2: check for undefined
+      if ([file, files].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       return files.findIndex(({value}) => value === file) + 1;
     },
 
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 1fde41c..24e5983 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
@@ -1147,5 +1147,38 @@
         1,
       ]);
     });
+
+    test('File change should trigger navigateToDiff once', () => {
+      element._fileList = ['file1', 'file2', 'file3'];
+      sandbox.stub(element, '_getLineOfInterest');
+      sandbox.stub(element, '_initCursor');
+      sandbox.stub(Gerrit.Nav, 'navigateToDiff');
+
+      // Load file1
+      element._paramsChanged({
+        view: Gerrit.Nav.View.DIFF,
+        patchNum: 1,
+        changeNum: 101,
+        project: 'test-project',
+        path: 'file1',
+      });
+      assert.isTrue(Gerrit.Nav.navigateToDiff.notCalled);
+
+      // Switch to file2
+      element.$.dropdown.value = 'file2';
+      assert.isTrue(Gerrit.Nav.navigateToDiff.calledOnce);
+
+      // This is to mock the param change triggered by above navigate
+      element._paramsChanged({
+        view: Gerrit.Nav.View.DIFF,
+        patchNum: 1,
+        changeNum: 101,
+        project: 'test-project',
+        path: 'file2',
+      });
+
+      // No extra call
+      assert.isTrue(Gerrit.Nav.navigateToDiff.calledOnce);
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
index 48bb6e0..b64385d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
@@ -30,9 +30,14 @@
 
     /** @type {number|string} */
     this.beforeNumber = opt_beforeLine || 0;
+
     /** @type {number|string} */
     this.afterNumber = opt_afterLine || 0;
 
+    /** @type {boolean} */
+    this.hasIntralineInfo = false;
+
+    /** @type Array<GrDiffLine.Highlights> */
     this.highlights = [];
 
     /** @type {?Array<Object>} ?Array<!GrDiffGroup> */
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 06f8a4f..2d16a4b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -130,8 +131,8 @@
         width: var(--content-width, 80ch);
       }
       .content.add .intraline,
-      /* If there are no intraline changes, consider everything changed */
-      .content.add.no-highlights,
+      /* If there are no intraline info, consider everything changed */
+      .content.add.no-intraline-info,
       .delta.total .content.add {
         background-color: var(--dark-add-highlight-color);
       }
@@ -139,8 +140,8 @@
         background-color: var(--light-add-highlight-color);
       }
       .content.remove .intraline,
-      /* If there are no intraline changes, consider everything changed */
-      .content.remove.no-highlights,
+      /* If there are no intraline info, consider everything changed */
+      .content.remove.no-intraline-info,
       .delta.total .content.remove {
         background-color: var(--dark-remove-highlight-color);
       }
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 633e081..c69b313 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -274,6 +274,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.PatchSetBehavior,
     ],
 
@@ -296,6 +297,11 @@
     },
 
     _enableSelectionObserver(loggedIn, isAttached) {
+      // Polymer 2: check for undefined
+      if ([loggedIn, isAttached].some(arg => arg === undefined)) {
+        return;
+      }
+
       if (loggedIn && isAttached) {
         this.listen(document, 'selectionchange', '_handleSelectionChange');
         this.listen(document, 'mouseup', '_handleMouseUp');
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 ea8aa47..a9fde2b 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
@@ -64,6 +64,17 @@
 
     _computeBaseDropdownContent(availablePatches, patchNum, _sortedRevisions,
         changeComments, revisionInfo) {
+      // Polymer 2: check for undefined
+      if ([
+        availablePatches,
+        patchNum,
+        _sortedRevisions,
+        changeComments,
+        revisionInfo,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const parentCounts = revisionInfo.getParentCountMap();
       const currentParentCount = parentCounts.hasOwnProperty(patchNum) ?
           parentCounts[patchNum] : 1;
@@ -107,6 +118,16 @@
 
     _computePatchDropdownContent(availablePatches, basePatchNum,
         _sortedRevisions, changeComments) {
+      // Polymer 2: check for undefined
+      if ([
+        availablePatches,
+        basePatchNum,
+        _sortedRevisions,
+        changeComments,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       const dropdownContent = [];
       for (const patch of availablePatches) {
         const patchNum = patch.num;
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
index c2983f7..aa4d2e1 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-tooltip/gr-tooltip.html">
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 26bf738..44bf6fb 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
@@ -49,6 +49,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html
index 6c2cbb5..2d8bf53 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
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 ec6f110..2ba6da6 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
@@ -74,6 +74,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.PathListBehavior,
@@ -209,6 +210,15 @@
     },
 
     _computeSaveDisabled(content, newContent, saving) {
+      // Polymer 2: check for undefined
+      if ([
+        content,
+        newContent,
+        saving,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (saving) {
         return true;
       }
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
index 717d52b..15db861 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 
 <dom-module id="gr-event-helper">
   <script src="gr-event-helper.js"></script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
index 08c1df9..896238f 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
@@ -33,12 +33,17 @@
     Polymer({
       is: 'some-element',
       _legacyUndefinedCheck: true,
+
       properties: {
         fooBar: {
           type: Object,
           notify: true,
         },
       },
+
+      behaviors: [
+        Gerrit.FireBehavior,
+      ],
     });
   </script>
 </dom-element>
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
index 3403d0d..6b75850 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-avatar/gr-avatar.html">
 <link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
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 feeb7df..246662d 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
@@ -69,6 +69,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     observers: [
       '_nameChanged(_account.name)',
       '_statusChanged(_account.status)',
@@ -145,6 +149,14 @@
     },
 
     _computeUsernameMutable(config, username) {
+      // Polymer 2: check for undefined
+      if ([
+        config,
+        username,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       // Username may not be changed once it is set.
       return config.auth.editable_account_fields.includes('USER_NAME') &&
           !username;
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
index 2d5fd26..7d876da 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
@@ -18,6 +18,7 @@
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
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 62bec12..11a0f1a 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
@@ -37,6 +37,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
     ],
 
     attached() {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
index cdc0825..b22f6f4 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
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 6b4ee18..3ce226f 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
@@ -60,6 +60,10 @@
       _serverConfig: Object,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     hostAttributes: {
       role: 'dialog',
     },
@@ -120,6 +124,14 @@
     },
 
     _computeUsernameMutable(config, username) {
+      // Polymer 2: check for undefined
+      if ([
+        config,
+        username,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       return config.auth.editable_account_fields.includes('USER_NAME') &&
           !username;
     },
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
index d193041..fea9e63 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
@@ -20,6 +20,7 @@
 
 <link rel="import" href="../../../behaviors/docs-url-behavior/docs-url-behavior.html">
 <link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
+
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../../styles/gr-menu-page-styles.html">
 <link rel="import" href="../../../styles/gr-page-nav-styles.html">
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 041dc64..7f0a754 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
@@ -149,6 +149,7 @@
     behaviors: [
       Gerrit.DocsUrlBehavior,
       Gerrit.ChangeTableBehavior,
+      Gerrit.FireBehavior,
     ],
 
     observers: [
@@ -158,6 +159,9 @@
     ],
 
     attached() {
+      // Polymer 2: anchor tag won't work on shadow DOM
+      // we need to manually calling scrollIntoView when hash changed
+      this.listen(window, 'location-change', '_handleLocationChange');
       this.fire('title-change', {title: 'Settings'});
 
       this._isDark = !!window.localStorage.getItem('dark-theme');
@@ -214,9 +218,28 @@
 
       this._loadingPromise = Promise.all(promises).then(() => {
         this._loading = false;
+
+        // Handle anchor tag for initial load
+        this._handleLocationChange();
       });
     },
 
+    detached() {
+      this.unlisten(window, 'location-change', '_handleLocationChange');
+    },
+
+    _handleLocationChange() {
+      // Handle anchor tag after dom attached
+      const urlHash = window.location.hash;
+      if (urlHash) {
+        // Use shadowRoot for Polymer 2
+        const elem = (this.shadowRoot || document).querySelector(urlHash);
+        if (elem) {
+          elem.scrollIntoView();
+        }
+      }
+    },
+
     reloadAccountDetail() {
       Promise.all([
         this.$.accountInfo.loadData(),
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
index 6de10a3..2f2c7ba 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../gr-account-link/gr-account-link.html">
 <link rel="import" href="../gr-button/gr-button.html">
 <link rel="import" href="../gr-icons/gr-icons.html">
@@ -39,14 +40,8 @@
       :host([show-avatar]) .container {
         padding-left: 0;
       }
-      gr-button.remove:hover,
-      gr-button.remove:focus {
-        --gr-button: {
-          color: #333;
-        }
-      }
       gr-button.remove {
-        --gr-button: {
+        --gr-remove-button-style: {
           border: 0;
           color: var(--deemphasized-text-color);
           font-size: 1.7rem;
@@ -58,6 +53,19 @@
           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;
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 827e33b..0e85599 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
@@ -57,6 +57,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     ready() {
       this._getHasAvatars().then(hasAvatars => {
         this.showAvatar = hasAvatars;
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 7983fad..0f914fb 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
@@ -67,6 +67,14 @@
     },
 
     _computeAccountTitle(account, tooltip) {
+      // Polymer 2: check for undefined
+      if ([
+        account,
+        tooltip,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (!account) { return; }
       let result = '';
       if (this._computeName(account, this._serverConfig)) {
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 d3f29dc..513b67a 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
@@ -68,31 +68,34 @@
           {
             name: 'Andrew Bonventre',
             email: 'andybons+gerrit@gmail.com',
-          }),
+          }, /* additionalText= */ ''),
           'Andrew Bonventre <andybons+gerrit@gmail.com>');
 
       assert.equal(element._computeAccountTitle(
-          {name: 'Andrew Bonventre'}),
+          {name: 'Andrew Bonventre'}, /* additionalText= */ ''),
           'Andrew Bonventre');
 
       assert.equal(element._computeAccountTitle(
           {
             email: 'andybons+gerrit@gmail.com',
-          }),
+          }, /* additionalText= */ ''),
           'Anonymous <andybons+gerrit@gmail.com>');
 
       assert.equal(element._computeShowEmailClass(
           {
             name: 'Andrew Bonventre',
             email: 'andybons+gerrit@gmail.com',
-          }), '');
+          }, /* additionalText= */ ''), '');
 
       assert.equal(element._computeShowEmailClass(
           {
             email: 'andybons+gerrit@gmail.com',
-          }), 'showEmail');
+          }, /* additionalText= */ ''), 'showEmail');
 
-      assert.equal(element._computeShowEmailClass({name: 'Andrew Bonventre'}),
+      assert.equal(element._computeShowEmailClass(
+          {name: 'Andrew Bonventre'},
+          /* additionalText= */ '',
+      ),
           '');
 
       assert.equal(element._computeShowEmailClass(undefined), '');
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
index 2b043c4..cc6453c 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
@@ -35,7 +35,6 @@
         box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
         color: var(--view-background-color);
         left: 1.25rem;
-        padding: 1em 1.5em;
         position: fixed;
         transform: translateY(5rem);
         transition: transform var(--gr-alert-transition-duration, 80ms) ease-out;
@@ -44,6 +43,17 @@
       :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
+       * may be affected by outside should be defined on it.
+       * In this case, `padding:0px` is defined in main.css for all elements
+       * with the universal selector: *.
+       */
+      .content-wrapper {
+        padding: 1em 1.5em;
+      }
       .text {
         color: var(--tooltip-text-color);
         display: inline-block;
@@ -62,12 +72,14 @@
         }
       }
     </style>
-    <span class="text">[[text]]</span>
-    <gr-button
-        link
-        class="action"
-        hidden$="[[_hideActionButton]]"
-        on-tap="_handleActionTap">[[actionText]]</gr-button>
+    <div class="content-wrapper">
+      <span class="text">[[text]]</span>
+      <gr-button
+          link
+          class="action"
+          hidden$="[[_hideActionButton]]"
+          on-tap="_handleActionTap">[[actionText]]</gr-button>
+    </div>
   </template>
   <script src="gr-alert.js"></script>
 </dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
index 64758ca..0d9f04c 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="/bower_components/iron-dropdown/iron-dropdown.html">
 <link rel="import" href="/bower_components/iron-fit-behavior/iron-fit-behavior.html">
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 1af629d2..af0b637 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
@@ -60,6 +60,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Polymer.IronFitBehavior,
     ],
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
index ac739c3..46877d7 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -16,6 +16,7 @@
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/paper-input/paper-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html">
 <link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
@@ -75,14 +76,31 @@
         on-focus="_onInputFocus"
         on-blur="_onInputBlur"
         autocomplete="off">
-      <!-- slot is for future use (2.x) while prefix attribute is for 1.x
-        (current) -->
-      <iron-icon
-          icon="gr-icons:search"
-          slot="prefix"
+
+      <template is="dom-if" if="[[_isPolymer2()]]">
+        <!-- Content uses vertical-align:baseline. If iron-icon is placed
+          directly in the slot and is not visible,  vertical-align doesn't
+          work, because display:none convert it from inline-block element to
+          block element. To fix this problem, iron-icon is wrapped in div
+          which never changes display type.
+          The problem doesn't exist in Polymer1 because DOM is different.
+          Applying the same fix to Polymer1 breaks vertical-align.
+        -->
+        <div slot="prefix">
+          <iron-icon
+            icon="gr-icons:search"
+            class$="searchIcon [[_computeShowSearchIconClass(showSearchIcon)]]">
+          </iron-icon>
+        </div>
+      </template>
+      <template is="dom-if" if="[[!_isPolymer2()]]">
+        <iron-icon
           prefix
+          icon="gr-icons:search"
           class$="searchIcon [[_computeShowSearchIconClass(showSearchIcon)]]">
-      </iron-icon>
+        </iron-icon>
+      </template>
+
     </paper-input>
     <gr-autocomplete-dropdown
         vertical-align="top"
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 76e14a7..af31473 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -168,6 +168,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
     ],
 
@@ -241,8 +242,13 @@
     },
 
     _updateSuggestions(text, threshold, noDebounce) {
+      // Polymer 2: check for undefined
+      if ([text, threshold, noDebounce].some(arg => arg === undefined)) {
+        return;
+      }
+
       if (this._disableSuggestions) { return; }
-      if (text === undefined || text.length < threshold) {
+      if (text.length < threshold) {
         this._suggestions = [];
         this.value = '';
         return;
@@ -419,5 +425,9 @@
     _computeShowSearchIconClass(showSearchIcon) {
       return showSearchIcon ? 'showSearchIcon' : '';
     },
+
+    _isPolymer2() {
+      return window.POLYMER2;
+    },
   });
 })();
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 217321f..cff35b4 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
@@ -320,12 +320,15 @@
       });
     });
 
-    test('search icon shows with showSearchIcon property', () => {
-      assert.equal(getComputedStyle(element.$$('iron-icon')).display,
-          'none');
-      element.showSearchIcon = true;
-      assert.notEqual(getComputedStyle(element.$$('iron-icon')).display,
-          'none');
+    test('search icon shows with showSearchIcon property', done => {
+      flush(() => {
+        assert.equal(getComputedStyle(element.$$('iron-icon')).display,
+            'none');
+        element.showSearchIcon = true;
+        assert.notEqual(getComputedStyle(element.$$('iron-icon')).display,
+            'none');
+        done();
+      });
     });
 
     test('vertical offset overridden by param if it exists', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index 754c3c0..340297f 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -39,6 +39,37 @@
         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: 3px;
+        -moz-user-select: none;
+        -ms-user-select: none;
+        -webkit-user-select: none;
+        user-select: none;
+        cursor: pointer;
+        z-index: 0;
+        padding: 0.7em 0.57em;
+
+        @apply --paper-font-common-base;
+        @apply --paper-button;
+        /* End of copy*/
+
         /* paper-button sets this to anti-aliased, which appears different than
           bold font elsewhere on macOS. */
         -webkit-font-smoothing: initial;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
index bee134b..b42c9c0 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
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 cf98b42c..615ce3b 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
@@ -127,6 +127,10 @@
     },
 
     behaviors: [
+      /**
+       * Not used in this element rather other elements tests
+       */
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.PathListBehavior,
     ],
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
index 1460035..f747719 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
@@ -18,6 +18,7 @@
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
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 bf7df71..cf2f57d 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -157,6 +157,7 @@
     ],
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
     ],
 
@@ -210,6 +211,11 @@
     },
 
     _calculateActionstoShow(showActions, isRobotComment) {
+      // Polymer 2: check for undefined
+      if ([showActions, isRobotComment].some(arg => arg === undefined)) {
+        return;
+      }
+
       this._showHumanActions = showActions && !isRobotComment;
       this._showRobotActions = showActions && isRobotComment;
     },
@@ -318,6 +324,11 @@
     },
 
     _editingChanged(editing, previousValue) {
+      // Polymer 2: observer fires when at least one property is defined.
+      // Do nothing to prevent comment.__editing being overwritten
+      // if previousValue is undefined
+      if (previousValue === undefined) return;
+
       this.$.container.classList.toggle('editing', editing);
       if (this.comment && this.comment.id) {
         this.$$('.cancel').hidden = !editing;
@@ -595,6 +606,11 @@
     },
 
     _loadLocalDraft(changeNum, patchNum, comment) {
+      // Polymer 2: check for undefined
+      if ([changeNum, patchNum, comment].some(arg => arg === undefined)) {
+        return;
+      }
+
       // Only apply local drafts to comments that haven't been saved
       // remotely, and haven't been given a default message already.
       //
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
index bf20429..eb3ae19 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
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 4ac059d..9f5f243 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
@@ -37,6 +37,10 @@
       message: String,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     resetFocus() {
       this.$.messageInput.textarea.focus();
     },
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 4d7f2bb..a39e170 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
@@ -173,6 +173,14 @@
     },
 
     _computeFullDateStr(dateStr, timeFormat) {
+      // Polymer 2: check for undefined
+      if ([
+        dateStr,
+        timeFormat,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       if (!dateStr) { return ''; }
       const date = moment(util.parseDate(dateStr));
       if (!date.isValid()) { return ''; }
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html
index 0321e58..bc08ad3 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../gr-button/gr-button.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
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 b8b2af4..065ee84 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
@@ -53,6 +53,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     hostAttributes: {
       role: 'dialog',
     },
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 bcf3729..b6d909d 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
@@ -104,6 +104,11 @@
     },
 
     _handleValueChange(value, items) {
+      // Polymer 2: check for undefined
+      if ([value, items].some(arg => arg === undefined)) {
+        return;
+      }
+
       if (!value) { return; }
       const selectedObj = items.find(item => {
         return item.value + '' === value + '';
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
index aa102e4..78fd0b7 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../gr-storage/gr-storage.html">
 
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 dc945a2..a0c4bae 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
@@ -71,6 +71,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     focusTextarea() {
       this.$$('iron-autogrow-textarea').textarea.focus();
     },
@@ -114,6 +118,15 @@
     },
 
     _computeSaveDisabled(disabled, content, newContent) {
+      // Polymer 2: check for undefined
+      if ([
+        disabled,
+        content,
+        newContent,
+      ].some(arg => arg === undefined)) {
+        return undefined;
+      }
+
       return disabled || (content === newContent);
     },
 
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
index 5faedd2..e1709dc 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
@@ -19,6 +19,7 @@
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="/bower_components/iron-dropdown/iron-dropdown.html">
 <link rel="import" href="/bower_components/paper-input/paper-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
 <dom-module id="gr-editable-label">
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 f23afea..e32f145 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
@@ -68,6 +68,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
     ],
 
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 44a8791..479dd1e 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
@@ -66,6 +66,11 @@
      * enabled.
      */
     _updateTitle(text, limit, tooltipLimit) {
+      // Polymer 2: check for undefined
+      if ([text, limit, tooltipLimit].some(arg => arg === undefined)) {
+        return;
+      }
+
       this.hasTooltip = !!limit && !!text && text.length > limit;
       if (this.hasTooltip) {
         this.setAttribute('title', text.substr(0, tooltipLimit));
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
index a3dd054..939d582 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
@@ -16,6 +16,7 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
 <link rel="import" href="../gr-button/gr-button.html">
 <link rel="import" href="../gr-icons/gr-icons.html">
@@ -36,14 +37,8 @@
         display: inline-flex;
         padding: 0 .5em;
       }
-      gr-button.remove:hover,
-      gr-button.remove:focus {
-        --gr-button: {
-          color: #333;
-        }
-      }
       gr-button.remove {
-        --gr-button: {
+        --gr-remove-button-style: {
           border: 0;
           color: var(--deemphasized-text-color);
           font-size: 1.7rem;
@@ -55,6 +50,19 @@
           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
+        }
+      }
       .transparentBackground,
       gr-button.transparentBackground {
         background-color: transparent;
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 8388a07..ee8653f 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
@@ -42,6 +42,10 @@
       limit: Number,
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     _getBackgroundClass(transparent) {
       return transparent ? 'transparentBackground' : '';
     },
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html
index 765345c..b72e1cc 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html
@@ -18,6 +18,7 @@
 <link rel="import" href="/bower_components/iron-input/iron-input.html">
 
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
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 2508196..d70d753 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
@@ -38,6 +38,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.FireBehavior,
       Gerrit.URLEncodingBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html
index 82c1169..2b4b982 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="/bower_components/iron-overlay-behavior/iron-overlay-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
 <dom-module id="gr-overlay">
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 c167b3b..bba5a46 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
@@ -45,6 +45,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Polymer.IronOverlayBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
index 607802f..87ea02b 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
@@ -17,6 +17,7 @@
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
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 c57a1eb..322cf5a 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
@@ -194,6 +194,7 @@
     _legacyUndefinedCheck: true,
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.PathListBehavior,
       Gerrit.PatchSetBehavior,
       Gerrit.RESTClientBehavior,
@@ -1094,6 +1095,8 @@
     getLoggedIn() {
       return this.getAccount().then(account => {
         return account != null;
+      }).catch(() => {
+        return false;
       });
     },
 
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 4c35151..748cb83 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
@@ -494,6 +494,15 @@
       });
     });
 
+    test('getLoggedIn returns false when network/auth failure', done => {
+      window.fetch.returns(
+          Promise.reject(new Error('Failed to fetch')));
+      element.getLoggedIn().then(isLoggedIn => {
+        assert.isFalse(isLoggedIn);
+        done();
+      });
+    });
+
     test('checkCredentials', done => {
       const responses = [
         {
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select.html b/polygerrit-ui/app/elements/shared/gr-select/gr-select.html
index 02afb38..f1ef86a 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.html
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.html
@@ -16,6 +16,8 @@
 -->
 
 <link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
+
 <dom-module id="gr-select">
   <slot></slot>
   <script src="gr-select.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 5e68c8b..357ab40 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
@@ -20,6 +20,7 @@
   Polymer({
     is: 'gr-select',
     _legacyUndefinedCheck: true,
+
     properties: {
       bindValue: {
         type: String,
@@ -28,6 +29,10 @@
       },
     },
 
+    behaviors: [
+      Gerrit.FireBehavior,
+    ],
+
     listeners: {
       'change': '_valueChanged',
       'dom-change': '_updateValue',
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
index fc532af..131dc79 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
@@ -16,6 +16,7 @@
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html">
 <link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
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 7929fbe..1f90211 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -101,6 +101,7 @@
     },
 
     behaviors: [
+      Gerrit.FireBehavior,
       Gerrit.KeyboardShortcutBehavior,
     ],
 
diff --git a/polygerrit-ui/app/styles/gr-voting-styles.html b/polygerrit-ui/app/styles/gr-voting-styles.html
index 3b1ee64..eec79be 100644
--- a/polygerrit-ui/app/styles/gr-voting-styles.html
+++ b/polygerrit-ui/app/styles/gr-voting-styles.html
@@ -23,6 +23,7 @@
           border: 1px solid rgba(0,0,0,.12);
           border-radius: 1em;
           box-shadow: none;
+          box-sizing: border-box;
           min-width: 3em;
         }
       }
diff --git a/polygerrit-ui/server.go b/polygerrit-ui/server.go
index e849469..305a6b8 100644
--- a/polygerrit-ui/server.go
+++ b/polygerrit-ui/server.go
@@ -103,7 +103,7 @@
 func handleIndex(writer http.ResponseWriter, originalRequest *http.Request) {
 	fakeRequest := &http.Request{
 		URL: &url.URL{
-			Path: "/",
+			Path:     "/",
 			RawQuery: originalRequest.URL.RawQuery,
 		},
 	}
@@ -170,7 +170,7 @@
 func patchResponse(req *http.Request, res *http.Response) io.Reader {
 	switch req.URL.EscapedPath() {
 	case "/":
-		return replaceCdn(res.Body)
+		return rewriteHostPage(res.Body)
 	case "/config/server/info":
 		return injectLocalPlugins(res.Body)
 	default:
@@ -178,13 +178,39 @@
 	}
 }
 
-func replaceCdn(reader io.Reader) io.Reader {
+func rewriteHostPage(reader io.Reader) io.Reader {
 	buf := new(bytes.Buffer)
 	buf.ReadFrom(reader)
 	original := buf.String()
 
+	// Simply remove all CDN references, so files are loaded from the local file system  or the proxy
+	// server instead.
 	replaced := cdnPattern.ReplaceAllString(original, "")
 
+	// Modify window.INITIAL_DATA so that it has the same effect as injectLocalPlugins. To achieve
+	// this let's add JavaScript lines at the end of the <script>...</script> snippet that also
+	// contains window.INITIAL_DATA=...
+	// Here we rely on the fact that the <script> snippet that we want to append to is the first one.
+	if len(*plugins) > 0 {
+		insertionPoint := strings.Index(replaced, "</script>")
+		builder := new(strings.Builder)
+		builder.WriteString(
+			"window.INITIAL_DATA['/config/server/info'].plugin.html_resource_paths = []; ")
+		builder.WriteString(
+			"window.INITIAL_DATA['/config/server/info'].plugin.js_resource_paths = []; ")
+		for _, p := range strings.Split(*plugins, ",") {
+			if filepath.Ext(p) == ".html" {
+				builder.WriteString(
+					"window.INITIAL_DATA['/config/server/info'].plugin.html_resource_paths.push('" + p + "'); ")
+			}
+			if filepath.Ext(p) == ".js" {
+				builder.WriteString(
+					"window.INITIAL_DATA['/config/server/info'].plugin.js_resource_paths.push('" + p + "'); ")
+			}
+		}
+		replaced = replaced[:insertionPoint] + builder.String() + replaced[insertionPoint:]
+	}
+
 	return strings.NewReader(replaced)
 }
 
@@ -209,11 +235,11 @@
 	jsResources := getJsonPropByPath(response, jsPluginsPath).([]interface{})
 
 	for _, p := range strings.Split(*plugins, ",") {
-		if strings.HasSuffix(p, ".html") {
+		if filepath.Ext(p) == ".html" {
 			htmlResources = append(htmlResources, p)
 		}
 
-		if strings.HasSuffix(p, ".js") {
+		if filepath.Ext(p) == ".js" {
 			jsResources = append(jsResources, p)
 		}
 	}
@@ -263,7 +289,7 @@
 
 // Any path prefixes that should resolve to index.html.
 var (
-	fePaths    = []string{"/q/", "/c/", "/p/", "/x/", "/dashboard/", "/admin/"}
+	fePaths    = []string{"/q/", "/c/", "/p/", "/x/", "/dashboard/", "/admin/", "/settings/"}
 	issueNumRE = regexp.MustCompile(`^\/\d+\/?$`)
 )