Merge "gr-diff-builder: Changing single-directional context expansion to bi-directional ('+10' button)"
diff --git a/.bazelversion b/.bazelversion
index 8862dba..25939d3 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-1.0.0rc2
+0.29.1
diff --git a/Documentation/dev-processes.txt b/Documentation/dev-processes.txt
index c19a579..ecea54b 100644
--- a/Documentation/dev-processes.txt
+++ b/Documentation/dev-processes.txt
@@ -28,7 +28,7 @@
 * 2 non-Google maintainers, elected by non-Google maintainers for the
   period of 1 year (see link:#steering-committee-election[below])
 
-Refer to the project homepage for the link:https://www.gerritcodereview.com/esc.html[
+Refer to the project homepage for the link:https://www.gerritcodereview.com/members.html#engineering-steering-committee[
 list of current committee members].
 
 The steering committee should act in the interest of the Gerrit project
diff --git a/Documentation/dev-roles.txt b/Documentation/dev-roles.txt
index 93a58c6..d8f3d38 100644
--- a/Documentation/dev-roles.txt
+++ b/Documentation/dev-roles.txt
@@ -117,7 +117,7 @@
 
 It's highly appreciated if contributors engage in code reviews,
 link:dev-design-docs.html#review[design reviews] and mailing list
-discussions. If wanted, contributors can also serve as link#mentor[
+discussions. If wanted, contributors can also serve as link:#mentor[
 mentors] to support other contributors with getting their features
 done.
 
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 145af0e..6fb9220 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -57,8 +57,8 @@
 
 [[details]]
 --
-* `DETAILS`: Includes full name, preferred email, username and avatars
-for each account.
+* `DETAILS`: Includes full name, preferred email, username, avatars,
+status and state for each account.
 --
 
 [[all-emails]]
@@ -2200,7 +2200,7 @@
 account.
 
 `AccountDetailInfo` has the same fields as link:#account-info[
-AccountInfo]. In addition `AccountDetailInfo` has the following fields:
+AccountInfo]. In addition `AccountDetailInfo` has the following field:
 
 [options="header",cols="1,^1,5"]
 |=================================
@@ -2208,8 +2208,6 @@
 |`registered_on`     ||
 The link:rest-api.html#timestamp[timestamp] of when the account was
 registered.
-|`inactive`          |not set if `false`|
-Whether the account is inactive.
 |=================================
 
 [[account-external-id-info]]
@@ -2260,9 +2258,14 @@
 See option link:rest-api-changes.html#detailed-accounts[
 DETAILED_ACCOUNTS] for change queries +
 and option link:#details[DETAILS] for account queries.
+|`avatars`         |optional|List of link:#avatar-info[AvatarInfo] +
+entities that provide information about avatar images of the account.
 |`_more_accounts`  |optional, not set if `false`|
 Whether the query would deliver more results if not limited. +
 Only set on the last account that is returned.
+|`status`          |optional|Status message of the account.
+|`inactive`        |not set if `false`|
+Whether the account is inactive.
 |===============================
 
 [[account-input]]
@@ -2308,6 +2311,19 @@
 If not set or if set to an empty string, the account status is deleted.
 |=============================
 
+[[avatar-info]]
+=== AvatarInfo
+The `AccountInfo` entity contains information about an avatar image of
+an account.
+
+[options="header",cols="1,6"]
+|======================
+|Field Name|Description
+|`url`     |The URL to the avatar image.
+|`height`  |The height of the avatar image in pixels.
+|`width`   |The width of the avatar image in pixels.
+|======================
+
 [[capability-info]]
 === CapabilityInfo
 The `CapabilityInfo` entity contains information about the global
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index d2f4d97..de0d9e7 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6370,8 +6370,8 @@
 the skip length is the number of characters between the end of the previous edit
 and the start of this edit, and the edit length is the number of edited characters
 following the skip. The start of the edits is from the beginning of the related
-diff content lines. If the list is empty, the entire DiffContent should be
-considered edited.
+diff content lines. If the list is empty, the entire DiffContent should be considered
+as unedited.
 
 Note that the implied newline character at the end of each line is included in
 the length calculation, and thus it is possible for the edits to span newlines.
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 52c393f..2b10e33 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1367,6 +1367,28 @@
   }
 ----
 
+[[index.changes]]
+=== Index a set of changes
+
+This endpoint allows Gerrit admins to index a set of changes with one request
+by providing a link:#index-changes-input[IndexChangesInput] entity.
+
+Using this endpoint Gerrit admins can also index change(s) which are not visible to them.
+
+.Request
+----
+  POST /config/server/index.changes HTTP/1.0
+  Content-Type: application/json; charset=UTF-8
+
+  {changes: ["foo~101", "bar~202"]}
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+----
+
 
 [[ids]]
 == IDs
@@ -1814,6 +1836,17 @@
 Only set for disk caches.
 |==================================
 
+[[index-changes-input]]
+=== IndexChangesInput
+The `IndexChangesInput` contains a list of numerical changes IDs to index.
+
+[options="header",cols="1,^2,4"]
+|================================
+|Field Name         ||Description
+|`changes`   ||
+List of link:rest-api-changes.html#change-id[change-ids]
+|================================
+
 [[jvm-summary-info]]
 === JvmSummaryInfo
 The `JvmSummaryInfo` entity contains information about the JVM.
diff --git a/WORKSPACE b/WORKSPACE
index 83ee604..a05e3f0 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -942,7 +942,6 @@
     sha1 = "7e060dd5b19431e6d198e91ff670644372f60fbd",
 )
 
-# When bumping the easymock version number, make sure to also move powermock to a compatible version
 maven_jar(
     name = "easymock",
     artifact = "org.easymock:easymock:3.1",
@@ -955,50 +954,6 @@
     sha1 = "92bf48723d277d6efd1150b2f7e9e1e92cb56caf",
 )
 
-POWERM_VERS = "1.6.1"
-
-maven_jar(
-    name = "powermock-module-junit4",
-    artifact = "org.powermock:powermock-module-junit4:" + POWERM_VERS,
-    sha1 = "ea8530b2848542624f110a393513af397b37b9cf",
-)
-
-maven_jar(
-    name = "powermock-module-junit4-common",
-    artifact = "org.powermock:powermock-module-junit4-common:" + POWERM_VERS,
-    sha1 = "7222ced54dabc310895d02e45c5428ca05193cda",
-)
-
-maven_jar(
-    name = "powermock-reflect",
-    artifact = "org.powermock:powermock-reflect:" + POWERM_VERS,
-    sha1 = "97d25eda8275c11161bcddda6ef8beabd534c878",
-)
-
-maven_jar(
-    name = "powermock-api-easymock",
-    artifact = "org.powermock:powermock-api-easymock:" + POWERM_VERS,
-    sha1 = "aa740ecf89a2f64d410b3d93ef8cd6833009ef00",
-)
-
-maven_jar(
-    name = "powermock-api-support",
-    artifact = "org.powermock:powermock-api-support:" + POWERM_VERS,
-    sha1 = "592ee6d929c324109d3469501222e0c76ccf0869",
-)
-
-maven_jar(
-    name = "powermock-core",
-    artifact = "org.powermock:powermock-core:" + POWERM_VERS,
-    sha1 = "5afc1efce8d44ed76b30af939657bd598e45d962",
-)
-
-maven_jar(
-    name = "javassist",
-    artifact = "org.javassist:javassist:3.22.0-GA",
-    sha1 = "3e83394258ae2089be7219b971ec21a8288528ad",
-)
-
 JETTY_VERS = "9.4.18.v20190429"
 
 maven_jar(
@@ -1090,8 +1045,8 @@
 # and httpasyncclient as necessary.
 maven_jar(
     name = "elasticsearch-rest-client",
-    artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.3.1",
-    sha1 = "f5793c89b50a159cbb3e15e17bb981ff854cbe51",
+    artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.3.2",
+    sha1 = "38721e908cad8a30fa3f8e659c0571150a60cab3",
 )
 
 maven_jar(
@@ -1139,6 +1094,12 @@
 )
 
 maven_jar(
+    name = "javax-annotation",
+    artifact = "javax.annotation:javax.annotation-api:1.3.2",
+    sha1 = "934c04d3cfef185a8008e7bf34331b79730a9d43",
+)
+
+maven_jar(
     name = "mockito",
     artifact = "org.mockito:mockito-core:2.24.0",
     sha1 = "969a7bcb6f16e076904336ebc7ca171d412cc1f9",
diff --git a/contrib/themes/diffy/etc/GerritSite.css b/contrib/themes/diffy/etc/GerritSite.css
deleted file mode 100644
index 76c595a..0000000
--- a/contrib/themes/diffy/etc/GerritSite.css
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (C) 2013 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.
- */
-#gerrit_topmenu {
-  left: 60px;
-  margin-right: 60px;
-  position: relative;
-  margin-bottom: 5px;
-}
-
-#diffy_logo {
-  display: block !important;
-  margin-bottom: -55px;
-  padding-left: 10px;
-  position: relative;
-  top: -55px;
-  width: 60px;
-}
diff --git a/contrib/themes/diffy/etc/GerritSiteHeader.html b/contrib/themes/diffy/etc/GerritSiteHeader.html
deleted file mode 100644
index 89b4db5..0000000
--- a/contrib/themes/diffy/etc/GerritSiteHeader.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div>
-  <div id="diffy_logo">
-    <img width="50" height="46" src="static/logo.png"/>
-  </div>
-</div>
diff --git a/contrib/themes/diffy/static/diffy.svg b/contrib/themes/diffy/static/diffy.svg
deleted file mode 100644
index 3e6e6c2..0000000
--- a/contrib/themes/diffy/static/diffy.svg
+++ /dev/null
@@ -1,326 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="600.06732"
-   height="558.20709"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.3.1 r9886"
-   sodipodi:docname="diffy.svg"
-   inkscape:export-filename="/home/sarah/art/diffy.png"
-   inkscape:export-xdpi="500"
-   inkscape:export-ydpi="500">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.66618777"
-     inkscape:cx="300.03365"
-     inkscape:cy="258.35521"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:window-width="1366"
-     inkscape:window-height="744"
-     inkscape:window-x="0"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-3069.8852,2251.5084)">
-    <text
-       xml:space="preserve"
-       style="font-size:51.65934372px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans Bold"
-       x="475.83173"
-       y="900.47076"
-       id="text4257"
-       sodipodi:linespacing="125%"
-       transform="scale(1.1411753,0.87628955)"><tspan
-         sodipodi:role="line"
-         x="475.83173"
-         y="900.47076"
-         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;text-anchor:end;font-family:Acme;-inkscape-font-specification:Acme Bold"
-         id="tspan3008" /></text>
-    <flowRoot
-       xml:space="preserve"
-       id="flowRoot4263"
-       style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
-         id="flowRegion4265"><rect
-           id="rect4267"
-           width="313.14728"
-           height="343.45187"
-           x="-335.37064"
-           y="205.85435" /></flowRegion><flowPara
-         id="flowPara4269" /></flowRoot>    <g
-       id="g10119"
-       transform="matrix(0.99201906,-0.12608805,0.12608805,0.99201906,273.85438,408.58042)">
-      <path
-         sodipodi:nodetypes="csccscac"
-         inkscape:connector-curvature="0"
-         id="path4130"
-         d="m 3378.9965,-2206.2972 c 0,0 13.4039,-8.8398 27.0022,-17.1718 13.5984,-8.3319 22.2322,-21.276 22.2322,-21.276 l 34.2799,28.1467 c 0,0 -16.0641,15.553 -24.7742,21.6893 -3.709,2.6129 -10.9463,9.1577 -10.9463,9.1577 0,0 -14.3617,-10.0769 -22.3623,-13.5225 -8.0773,-3.4786 -25.4315,-7.0234 -25.4315,-7.0234 z"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="cac"
-         inkscape:connector-curvature="0"
-         d="m 3414.7387,-2204.9828 c 0,0 8.2233,-5.8571 11.9326,-9.2622 3.8224,-3.5089 10.5596,-11.437 10.5596,-11.437"
-         style="fill:#ffffff;stroke:#ff0000;stroke-width:8.97858429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         id="path4185" />
-      <path
-         sodipodi:nodetypes="cascacsc"
-         inkscape:connector-curvature="0"
-         id="path3307"
-         d="m 3247.6516,-2171.4037 c 0,0 4.3348,-19.2835 10.7071,-26.2701 7.4284,-8.1446 19.072,-14.1542 29.3495,-15.24 27.8177,-2.9386 63.2371,6.1908 63.2371,6.1908 0,0 -30.8042,0.7262 -45.6022,4.5134 -5.1922,1.3289 -14.7941,6.2977 -14.7941,6.2977 0,0 -16.1166,0.8374 -29.8971,12.0927 -13.7804,11.2552 -3.0344,9.7831 -3.0344,9.7831"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="ccsccscscccccscc"
-         inkscape:connector-curvature="0"
-         id="path3011"
-         d="m 3332.9985,-2012.4352 -20.0742,24.9886 c 0,0 -16.3479,-5.082 -40.3255,4.9725 -23.9776,10.0546 -39.2193,26.4042 -39.2193,26.4042 l 7.8897,-0.7975 c 0,0 20.4818,-14.4371 31.9614,-17.1294 11.4796,-2.6923 23.6197,-2.8388 23.6197,-2.8388 0,0 -25.492,10.0264 -30.7497,12.3759 -6.2584,2.7968 -15.0874,9.199 -15.0874,9.199 l 10.4761,-0.2965 c 17.6867,-7.2528 41.3221,-16.0187 59.9685,-15.047 15.5919,1.1274 33.9405,16.7543 33.9405,16.7543 l -3.8165,-9.9895 c 0,0 -10.8307,-8.1932 -16.3866,-13.7491 -5.5558,-5.5558 17.5848,-25.8193 17.5848,-25.8193 -14.3098,-2.8351 -2.1172,-7.9424 -19.7815,-9.0274 z"
-         style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         d="m 3397.9929,-1984.0461 -15.5774,33.0846 c 0,0 -14.4499,-2.2165 -39.5097,4.7139 -25.0597,6.9303 -44.9575,20.0476 -44.9575,20.0476 l 10.5706,1.822 c 0,0 19.7276,-11.1182 30.516,-14.1955 7.747,-2.2097 23.9934,-2.9008 23.9934,-2.9008 0,0 -20.2687,6.6869 -30.057,10.9033 -5.4067,2.329 -15.7817,7.9273 -15.7817,7.9273 l 10.8391,1.5269 c 0,0 31.056,-11.8625 47.4047,-13.3504 11.1103,-1.0112 11.8331,-2.962 33.324,3.1079 10.0509,2.8388 16.3928,6.252 16.3928,6.252 l -3.3122,-5.8696 c 0,0 -5.3909,-4.5711 -8.2712,-6.6111 -3.5982,-2.5484 -11.2202,-7.0059 -11.2202,-7.0059 l 15.4484,-30.858 c -13.3255,-5.9362 -2.8177,-3.6211 -19.8021,-8.5942 z"
-         id="path3013"
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="ccsccacaccasccaccc" />
-      <path
-         sodipodi:nodetypes="aaczsccsssca"
-         inkscape:connector-curvature="0"
-         id="path3014"
-         d="m 3138.8926,-2124.2277 c 9.8375,-20.5358 27.6337,-38.9533 48.4705,-48.136 22.7321,-10.018 51.1582,-10.0879 74.4382,-3.5941 7.6944,6.5 10.3115,-17.1804 46.9059,-27.4208 36.5944,-10.2403 91.6429,-6.509 142.1505,22.7854 54.0494,31.3487 -23.7386,41.9213 -8.0812,94.4492 32.3844,113.578 -63.1345,101.5204 -63.1345,101.5204 0,0 -25.2539,-16.1625 -35.3554,-17.1726 -10.1015,-1.0102 -46.6429,-25.1251 -68.8662,-28.6607 -22.2234,-3.5355 -36.9003,18.6222 -107.991,-9.9282 -24.3178,-9.7661 -33.8401,-33.335 -33.8401,-33.335 0,0 -2.0102,-35.2405 5.3033,-50.5076 z"
-         style="fill:#0000ff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="cssscscsac"
-         inkscape:connector-curvature="0"
-         id="path3028"
-         d="m 3179.7636,-2035.4822 c 0,0 33.988,-7.9036 57.2215,-14.9746 23.2335,-7.0711 36.1637,-43.7474 71.519,-48.7981 35.3554,-5.0508 68.528,22.1624 80.6498,47.4162 12.1218,25.2539 50.6701,55.6193 50.6701,55.6193 0,0 1.0101,11.1117 -14.1422,18.1828 -15.1522,7.0711 -10.4989,7.6628 -10.4989,7.6628 0,0 -43.1103,-11.4139 -62.396,-21.4139 -19.2857,-10 -61.236,-23.044 -92.9377,-30.7963 -26.2653,-6.4228 -35.0334,2.5255 -80.0856,-12.8982 z"
-         style="fill:#ff6600;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="cscccccscc"
-         inkscape:connector-curvature="0"
-         id="path3009"
-         d="m 3470.1209,-2002.8128 c 0,0 13.8937,99.0002 33.6829,157.8517 19.7893,58.8514 78.4464,180.4742 78.4464,180.4742 l 28.1031,-31.8521 -52.5625,-109.7203 60.5152,95.9016 26.0124,-35.7271 c 0,0 -70.663,-88.4878 -93.8954,-140.4597 -23.2324,-51.9718 -37.4945,-152.9082 -37.4945,-152.9082 z"
-         style="fill:#0000ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="csscsccccccccccsc"
-         inkscape:connector-curvature="0"
-         id="path3016"
-         d="m 3359.2154,-2191.502 c 0,0 -40.7142,5 -57.1428,42.1428 -16.4286,37.1429 50.7143,115.7143 60.7143,132.1429 10,16.4286 24.2857,38.5714 24.2857,38.5714 0,0 47.6766,32.0335 82.9062,53.8444 32.6567,20.2179 65.2266,33.1109 65.2266,33.1109 0,0 1.1687,-0.4937 7.0265,-19.1963 -12.1218,-21.7183 -38.3216,-53.2543 -54.5036,-69.192 24.2792,14.1167 43.346,28.1254 60.1041,56.5482 0,0 4.9588,-21.3916 6.5659,-30.7894 -3.0357,-9.5763 -47.4771,-56.5685 -47.4771,-56.5685 20.5517,8.7613 33.7508,23.0255 50.0892,44.7732 0,0 3.3263,-21.5598 3.8366,-32.9472 -2.5253,-6.566 -33.7228,-35.0595 -33.7228,-35.0595 13.1654,6.6374 19.714,11.0661 34.4127,24.0476 0,0 -3.0357,-60.7142 -66.6072,-130.7142 -63.5714,-70 -135.7143,-50.7143 -135.7143,-50.7143 z"
-         style="fill:#000080;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <g
-         transform="translate(2935.644,-2513.1499)"
-         id="g3138">
-        <path
-           style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-           d="m 337.66259,394.0585 c 0,0 -28.86328,-30.61257 -60.78781,-25.80202 -31.92454,4.81054 -41.10831,33.67382 -39.79634,39.79633 1.31197,6.12252 13.557,11.80771 22.30345,15.74361 8.74644,3.9359 32.79917,-0.43732 45.48152,-6.55984 12.68235,-6.12251 32.79918,-23.17808 32.79918,-23.17808 z"
-           id="path3022"
-           inkscape:connector-curvature="0" />
-        <path
-           style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-miterlimit:4"
-           d="m 286.65383,372.49887 c 12.50235,2.18615 20.86528,14.09354 18.67912,26.59589 -2.18615,12.50234 -14.09354,20.86527 -26.59589,18.67911 -12.50235,-2.18616 -20.86528,-14.09354 -18.67912,-26.59588 z"
-           id="path3024"
-           inkscape:connector-curvature="0"
-           sodipodi:nodetypes="csscc" />
-        <path
-           sodipodi:type="arc"
-           style="fill:#000000;fill-opacity:1;stroke:none"
-           id="path3026"
-           sodipodi:cx="229.64285"
-           sodipodi:cy="403.79074"
-           sodipodi:rx="8.2142859"
-           sodipodi:ry="8.2142859"
-           d="m 237.85714,403.79074 c 0,4.53663 -3.67766,8.21429 -8.21429,8.21429 -4.53662,0 -8.21428,-3.67766 -8.21428,-8.21429 0,-4.53662 3.67766,-8.21428 8.21428,-8.21428 4.53663,0 8.21429,3.67766 8.21429,8.21428 z"
-           transform="matrix(1.5694327,0,0,1.5694327,-85.566867,-233.30737)" />
-      </g>
-      <path
-         sodipodi:nodetypes="cssssssc"
-         style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         d="m 3110.2692,-2103.8575 c -32.3249,20.7081 -31.4522,56.0845 -27.4116,60.6301 4.0406,4.5457 8.0812,1.0102 15.6574,-1.5152 7.5761,-2.5254 26.2639,-10.1015 42.4264,-11.1117 16.1624,-1.0101 21.2132,3.0305 23.7386,-0.505 2.5254,-3.5356 4.0406,-15.1523 3.0304,-25.7589 -1.0101,-10.6066 -1.0101,-23.2335 -7.071,-25.2538 -6.061,-2.0203 -38.2484,-5.0718 -50.3702,3.5145 z"
-         id="path3296"
-         inkscape:connector-curvature="0" />
-      <path
-         inkscape:connector-curvature="0"
-         id="path3305"
-         d="m 3251.1404,-2178.2729 c 0,0 12.1428,21.0714 67.1428,32.5 55,11.4286 125.7143,-38.2143 125.7143,-38.2143 l 38.7857,38.6429 c 0,0 -113.0714,25.6428 -150.2143,21 -37.1428,-4.6429 -81.4285,-50.3572 -81.4285,-50.3572"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="ccccc"
-         inkscape:connector-curvature="0"
-         id="path4099"
-         d="m 3453.1169,-2166.1191 -30.2186,14.3667 6.8378,5.5184 29.9689,-14.0733 z"
-         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
-      <path
-         sodipodi:nodetypes="cacscacsc"
-         inkscape:connector-curvature="0"
-         id="path3303"
-         d="m 3154.8351,-2149.0637 c 0,0 -6.1891,7.2925 -8.7928,11.2888 -2.8625,4.3936 -7.4472,13.8569 -7.4472,13.8569 0,0 49.8667,-24.0831 67.6065,-30.2972 18.2787,-6.4028 54.803,-21.7873 54.803,-21.7873 0,0 -3.8451,-1.8876 -5.9103,-2.3872 -3.1018,-0.7503 -9.5343,-0.868 -9.5343,-0.868 0,0 -25.7837,-0.1235 -42.8032,6.1343 -17.0196,6.2579 -47.9217,24.0597 -47.9217,24.0597 z"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
-         d="m 3433.0379,-2169.2212 24.4739,22.8165 -8.1844,3.1976 -24.3243,-22.4616 z"
-         id="path27241"
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="ccccc" />
-    </g>
-    <g
-       id="g10229"
-       transform="translate(31.403024,6.4439689)">
-      <g
-         id="text4253"
-         style="font-size:140.50161743px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans Bold"
-         transform="scale(1.066563,0.93759113)">
-        <path
-           id="path10219"
-           style="font-family:Acme;-inkscape-font-specification:Acme Bold"
-           d="m 2917.8887,-1930.8568 c -6.6505,0 -16.2514,-0.5152 -28.8029,-1.5455 l 1.6861,-34.1419 -1.6861,-62.8043 37.514,0 c 12.645,10e-5 22.527,3.7937 29.6458,11.3807 7.1187,7.5871 10.678,18.1247 10.6781,31.6128 -10e-5,17.0476 -4.4024,30.5826 -13.2071,40.605 -8.8049,9.9288 -20.7475,14.8932 -35.8279,14.8932 m 3.653,-83.3175 c -6.1821,10e-5 -10.5376,0.047 -13.0666,0.1405 l -1.4051,45.101 0.9836,22.7613 c 7.868,0.562 13.1602,0.843 15.8766,0.843 7.3997,0 13.2071,-2.9973 17.4222,-8.9921 4.3087,-5.9947 6.463,-14.7058 6.4631,-26.1333 -1e-4,-11.4274 -2.1076,-19.9043 -6.3226,-25.4308 -4.1214,-5.5263 -10.7718,-8.2895 -19.9512,-8.2896"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10221"
-           style="font-family:Acme;-inkscape-font-specification:Acme Bold"
-           d="m 3002.3389,-2030.051 -1.967,61.1182 1.686,36.5305 -19.6702,0 1.686,-34.1419 -1.686,-59.9942 19.9512,-3.5126"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10223"
-           style="font-family:Acme;-inkscape-font-specification:Acme Bold"
-           d="m 3074.2384,-2014.3148 -29.9268,0 -0.843,25.8523 26.8358,0 -1.5455,14.8932 -25.7118,0 -0.1405,4.6365 1.686,36.5305 -19.6703,0 1.6861,-34.1419 -1.6861,-62.8043 50.8616,0 -1.5455,15.0337"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10225"
-           style="font-family:Acme;-inkscape-font-specification:Acme Bold"
-           d="m 3136.2567,-2014.3148 -29.9268,0 -0.843,25.8523 26.8358,0 -1.5456,14.8932 -25.7117,0 -0.1405,4.6365 1.686,36.5305 -19.6703,0 1.6861,-34.1419 -1.6861,-62.8043 50.8616,0 -1.5455,15.0337"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10227"
-           style="font-family:Acme;-inkscape-font-specification:Acme Bold"
-           d="m 3177.4808,-1987.9005 1.686,0 10.3971,-19.1082 10.5376,-23.1828 17.1412,2.5291 -12.9261,23.4637 -18.2652,35.6874 1.5455,36.109 -19.6702,0 1.405,-33.7204 -17.7032,-35.9684 -13.4882,-24.5878 19.8107,-3.5126 9.1326,21.3563 10.3972,20.9347"
-           inkscape:connector-curvature="0" />
-      </g>
-      <g
-         id="text3010"
-         style="font-size:62.54807663px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Acme;-inkscape-font-specification:Acme"
-         transform="scale(1.1647576,0.85854772)">
-        <path
-           id="path10175"
-           d="m 2645.5222,-2090.4097 0.688,-7.1931 29.7104,0 -0.688,7.1931 -10.8834,-0.9382 -0.2502,20.6408 0.7506,16.2625 -8.7568,0 0.7506,-15.1992 -0.2502,-21.6416 -11.071,0.8757"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10177"
-           d="m 2697.8329,-2070.2692 c 0,-2.8772 -0.2502,-4.7954 -0.7505,-5.7545 -0.5005,-0.959 -1.668,-1.4385 -3.5027,-1.4386 -1.8348,1e-4 -3.9197,0.7298 -6.2548,2.1892 l 0,4.566 0.688,14.6988 -8.444,1.5637 0.7506,-15.1992 -0.7506,-31.6493 8.5691,-1.5637 -0.8131,16.2 0,7.3181 c 0.6254,-0.834 1.3343,-1.6471 2.1266,-2.4394 0.7923,-0.7922 2.0224,-1.6888 3.6903,-2.6896 1.668,-1.0007 3.4401,-1.5011 5.3166,-1.5011 1.9181,0 3.5235,0.7506 4.8162,2.2517 1.2926,1.4595 1.939,3.461 1.939,6.0046 l -0.3753,7.0054 0.688,15.0116 -8.4439,1.5637 0.7505,-16.1374"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10179"
-           d="m 2723.6897,-2060.8245 c 1.2093,0 2.4811,-0.3127 3.8155,-0.9382 1.3343,-0.6255 2.3768,-1.251 3.1274,-1.8764 l 1.1258,-0.9383 2.8772,3.5027 c -0.417,0.7089 -1.0633,1.522 -1.939,2.4394 -0.8757,0.9174 -1.7722,1.7096 -2.6895,2.3768 -0.8757,0.6255 -2.0641,1.2093 -3.5653,1.7514 -1.4594,0.5004 -2.9814,0.7505 -4.566,0.7505 -3.3776,0 -6.1297,-1.2718 -8.2563,-3.8154 -2.1267,-2.5853 -3.19,-5.9003 -3.19,-9.9451 0,-5.0873 1.4803,-9.549 4.4409,-13.3853 2.9606,-3.8363 6.4216,-5.7544 10.383,-5.7544 3.044,0 5.4,0.8548 7.068,2.5644 1.7096,1.7097 2.5644,4.1074 2.5644,7.1931 0,1.8347 -0.3127,3.9197 -0.9382,6.2548 l -1.251,1.3135 -15.637,1.4386 c 0.7089,4.7119 2.9189,7.0679 6.6301,7.0679 m 0,-19.3899 c -1.8347,0 -3.3776,0.7506 -4.6285,2.2517 -1.251,1.4595 -1.9807,3.336 -2.1892,5.6294 l 10.6332,-1.3135 c 0.125,-0.9591 0.1876,-1.7514 0.1876,-2.3769 0,-2.7938 -1.3344,-4.1907 -4.0031,-4.1907"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10181"
-           d="m 2760.1934,-2097.9155 -0.6881,20.3907 15.9498,-20.2656 6.3799,4.2533 -14.3861,16.7003 15.2618,18.0764 -8.0062,5.0038 -15.2617,-21.579 -0.1251,4.6285 0.7506,16.2625 -8.7568,0 0.7506,-15.1992 -0.7506,-26.708 8.8819,-1.5637"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10183"
-           d="m 2793.6018,-2054.4446 c -2.2517,0 -4.0864,-0.7297 -5.5042,-2.1892 -1.376,-1.5011 -2.0641,-3.461 -2.0641,-5.8795 l 0.3753,-7.193 -0.688,-15.0115 8.3814,-1.5638 -0.8131,19.0147 c 0,1.793 0.3127,3.1065 0.9382,3.9405 0.6672,0.834 1.7097,1.251 3.1274,1.251 1.4178,0 3.3776,-0.7089 5.8796,-2.1267 l 0,-3.7529 -0.6881,-16.4501 8.444,-1.5637 -0.7506,15.637 0.063,5.129 c 0,2.6687 0.688,5.3582 2.0641,8.0687 l -6.505,2.8772 c -1.3761,-2.7104 -2.2101,-4.8788 -2.502,-6.505 -3.7529,4.2115 -7.0053,6.3173 -9.7575,6.3173"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10185"
-           d="m 2836.1199,-2070.2692 c 0,-2.8772 -0.2502,-4.7954 -0.7506,-5.7545 -0.5004,-0.959 -1.668,-1.4385 -3.5027,-1.4386 -1.8347,1e-4 -3.9197,0.7298 -6.2548,2.1892 l 0,4.566 0.688,14.6988 -8.3814,1.5637 0.7506,-15.1992 c -0.1668,-5.5876 -0.6255,-10.5914 -1.3761,-15.0115 l 7.5683,-1.6263 c 0.2919,2.4186 0.5004,4.7746 0.6255,7.068 0.6255,-0.834 1.3344,-1.6471 2.1266,-2.4394 0.7923,-0.8339 2.0433,-1.7513 3.7529,-2.7521 1.7097,-1.0424 3.5027,-1.5637 5.3792,-1.5637 1.9181,0 3.5235,0.7506 4.8162,2.2517 1.2926,1.4595 1.9389,3.461 1.939,6.0046 l -0.3753,7.0054 0.688,15.0116 -8.444,1.5637 0.7506,-16.1374"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10187"
-           d="m 2848.5318,-2048.2523 c 0,-3.6695 3.4193,-6.5467 10.2579,-8.6317 -1.6263,-0.8339 -2.4394,-1.9181 -2.4394,-3.2525 0,-0.542 0.2085,-1.0841 0.6255,-1.6262 0.4169,-0.5421 1.0216,-1.105 1.8139,-1.6888 l 0,-0.5004 c -3.0024,0 -5.4,-0.834 -7.1931,-2.5019 -1.7513,-1.6679 -2.627,-3.8988 -2.627,-6.6927 0,-3.6277 1.4803,-6.776 4.4409,-9.4447 2.9606,-2.7104 6.4633,-4.0656 10.5081,-4.0656 1.0425,0 2.5645,0.2085 4.566,0.6254 2.0015,0.4171 3.9614,0.6256 5.8795,0.6255 l 4.8162,0 -0.6254,5.4417 -4.6912,-0.5629 c 0.417,1.2093 0.6255,2.5853 0.6255,4.1281 0,1.5429 -0.3753,3.0858 -1.1258,4.6286 -0.7089,1.5012 -1.5846,2.7104 -2.6271,3.6278 -1.0007,0.9174 -2.0224,1.7305 -3.0648,2.4394 -1.0008,0.6672 -1.8765,1.2718 -2.627,1.8139 -0.7089,0.5004 -1.0634,0.959 -1.0633,1.376 -1e-4,0.7923 0.8339,1.4803 2.5019,2.0641 4.42,1.7931 7.4015,3.3151 8.9443,4.566 1.5846,1.251 2.3768,2.7938 2.3769,4.6286 -10e-5,3.0857 -1.5638,5.6084 -4.6911,7.5683 -3.0858,2.0015 -6.7761,3.0023 -11.071,3.0023 -4.295,0 -7.6309,-0.688 -10.0077,-2.0641 -2.3352,-1.3761 -3.5027,-3.2108 -3.5027,-5.5042 m 18.8895,-26.2702 c 0,-2.0432 -0.4587,-3.5444 -1.3761,-4.5035 -0.9174,-0.959 -2.356,-1.4386 -4.3158,-1.4386 -3.7946,0 -5.6919,1.8765 -5.6919,5.6293 0,3.7112 2.0224,5.5668 6.0672,5.5668 3.5444,0 5.3166,-1.7513 5.3166,-5.254 m 2.2517,24.2061 c 0,-0.7506 -0.3961,-1.4595 -1.1884,-2.1266 -0.7923,-0.6255 -2.21,-1.3761 -4.2533,-2.2518 -3.044,0.834 -5.1498,1.6054 -6.3173,2.3143 -1.1259,0.7089 -1.6888,1.5846 -1.6888,2.627 0,1.0425 0.5629,1.8765 1.6888,2.5019 1.1675,0.6672 2.7729,1.0008 4.8162,1.0008 2.0432,0 3.7112,-0.3753 5.0038,-1.1258 1.2927,-0.7506 1.939,-1.7305 1.939,-2.9398"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10189"
-           d="m 2915.4651,-2090.9101 -13.3228,0 -0.3753,11.5088 11.9467,0 -0.688,6.6301 -11.4463,0 -0.063,2.0641 0.7506,16.2625 -8.7567,0 0.7506,-15.1992 -0.7506,-27.959 22.6424,0 -0.688,6.6927"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10191"
-           d="m 2927.2495,-2054.4446 c -2.2517,0 -4.0865,-0.7297 -5.5042,-2.1892 -1.3761,-1.5011 -2.0641,-3.461 -2.0641,-5.8795 l 0.3753,-7.193 -0.6881,-15.0115 8.3815,-1.5638 -0.8131,19.0147 c -10e-5,1.793 0.3127,3.1065 0.9382,3.9405 0.6671,0.834 1.7096,1.251 3.1274,1.251 1.4177,0 3.3776,-0.7089 5.8795,-2.1267 l 0,-3.7529 -0.688,-16.4501 8.444,-1.5637 -0.7506,15.637 0.063,5.129 c 0,2.6687 0.688,5.3582 2.0641,8.0687 l -6.505,2.8772 c -1.3761,-2.7104 -2.21,-4.8788 -2.5019,-6.505 -3.7529,4.2115 -7.0054,6.3173 -9.7575,6.3173"
-           inkscape:connector-curvature="0" />
-      </g>
-      <g
-         id="text3016"
-         style="font-size:55.5345993px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Acme;-inkscape-font-specification:Acme"
-         transform="scale(1.0640965,0.93976441)">
-        <path
-           id="path10194"
-           d="m 2910.1662,-1832.5692 -3.1655,0 0,0.3332 0.6664,14.439 -7.4416,0 0.6664,-13.4949 -0.6664,-24.8239 12.051,0 c 3.5542,0 6.3864,0.9626 8.4968,2.8878 2.1103,1.9252 3.1654,4.4798 3.1655,7.6637 -10e-5,2.4066 -0.6295,4.6094 -1.8882,6.6087 -1.2218,1.9622 -2.8878,3.5172 -4.9981,4.6649 l 9.663,13.6615 -6.7197,3.4431 -8.6634,-15.4386 c -0.2592,0.037 -0.6479,0.056 -1.1662,0.056 m 0.111,-17.9376 -2.7212,0 -0.3887,12.5508 3.7764,0 c 1.8511,0 3.2765,-0.6109 4.2761,-1.8327 1.0366,-1.2217 1.555,-2.9618 1.555,-5.2202 0,-3.6653 -2.1659,-5.4979 -6.4976,-5.4979"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10196"
-           d="m 2941.1059,-1823.4615 c 1.0736,0 2.2028,-0.2777 3.3876,-0.833 1.1847,-0.5553 2.1103,-1.1107 2.7767,-1.666 l 0.9997,-0.8331 2.5545,3.11 c -0.3702,0.6294 -0.9441,1.3513 -1.7215,2.1658 -0.7775,0.8145 -1.5735,1.518 -2.388,2.1103 -0.7775,0.5554 -1.8327,1.0737 -3.1655,1.555 -1.2958,0.4443 -2.6471,0.6664 -4.054,0.6664 -2.9989,0 -5.4424,-1.1292 -7.3306,-3.3876 -1.8882,-2.2954 -2.8322,-5.2387 -2.8322,-8.83 0,-4.5168 1.3143,-8.4783 3.9429,-11.8844 2.6286,-3.4061 5.7016,-5.1092 9.2188,-5.1092 2.7026,0 4.7944,0.759 6.2754,2.2769 1.5179,1.518 2.2769,3.6468 2.2769,6.3865 0,1.6291 -0.2777,3.4802 -0.833,5.5535 l -1.1107,1.1662 -13.8837,1.2773 c 0.6294,4.1836 2.5916,6.2754 5.8867,6.2754 m 0,-17.2157 c -1.629,0 -2.9989,0.6664 -4.1096,1.9992 -1.1107,1.2959 -1.7586,2.9619 -1.9437,4.9981 l 9.4409,-1.1662 c 0.1111,-0.8515 0.1666,-1.5549 0.1666,-2.1103 0,-2.4805 -1.1847,-3.7208 -3.5542,-3.7208"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10198"
-           d="m 2965.2808,-1824.7388 0.7219,0 1.8327,-7.6082 2.9989,-13.7171 6.8862,0.833 -4.165,13.7726 -3.4987,12.9951 -9.4964,0.833 -3.2766,-13.4949 -3.9985,-13.4949 7.5527,-1.3883 2.388,12.2176 2.0548,9.0521"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10200"
-           d="m 2988.8795,-1846.0641 -0.6664,13.8281 0.6109,13.0507 -7.4416,1.3883 0.6664,-13.4949 -0.6109,-13.3838 7.4416,-1.3884 m -7.8303,-8.108 c 0,-1.2588 0.4812,-2.3695 1.4439,-3.3321 0.9625,-0.9996 2.0732,-1.4994 3.332,-1.4994 1.2588,0 2.2214,0.3517 2.8878,1.0551 0.6664,0.6665 0.9996,1.6846 0.9997,3.0544 -10e-5,1.3699 -0.4814,2.5547 -1.4439,3.5542 -0.9256,0.9627 -2.0178,1.444 -3.2766,1.4439 -1.2218,10e-5 -2.1844,-0.4072 -2.8878,-1.2217 -0.7034,-0.8145 -1.0551,-1.8326 -1.0551,-3.0544"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10202"
-           d="m 3004.9924,-1823.4615 c 1.0736,0 2.2028,-0.2777 3.3876,-0.833 1.1847,-0.5553 2.1103,-1.1107 2.7767,-1.666 l 0.9996,-0.8331 2.5546,3.11 c -0.3702,0.6294 -0.9441,1.3513 -1.7215,2.1658 -0.7775,0.8145 -1.5735,1.518 -2.388,2.1103 -0.7775,0.5554 -1.8327,1.0737 -3.1655,1.555 -1.2958,0.4443 -2.6472,0.6664 -4.054,0.6664 -2.9989,0 -5.4424,-1.1292 -7.3306,-3.3876 -1.8882,-2.2954 -2.8323,-5.2387 -2.8323,-8.83 0,-4.5168 1.3144,-8.4783 3.943,-11.8844 2.6286,-3.4061 5.7015,-5.1092 9.2187,-5.1092 2.7027,0 4.7945,0.759 6.2754,2.2769 1.518,1.518 2.2769,3.6468 2.277,6.3865 -1e-4,1.6291 -0.2777,3.4802 -0.8331,5.5535 l -1.1106,1.1662 -13.8837,1.2773 c 0.6294,4.1836 2.5916,6.2754 5.8867,6.2754 m 0,-17.2157 c -1.6291,0 -2.9989,0.6664 -4.1096,1.9992 -1.1107,1.2959 -1.7586,2.9619 -1.9437,4.9981 l 9.4409,-1.1662 c 0.111,-0.8515 0.1666,-1.5549 0.1666,-2.1103 0,-2.4805 -1.1848,-3.7208 -3.5542,-3.7208"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10204"
-           d="m 3044.4948,-1824.7388 0.722,0 1.7771,-7.6082 2.8878,-13.7171 6.7752,0.833 -4.054,13.7726 -3.3876,12.9951 -9.1077,0.833 -3.0544,-13.4949 -0.2777,-0.8885 -3.7208,13.5504 -8.83,0.833 -3.221,-13.4949 -3.8319,-13.4949 7.275,-1.3883 2.277,12.2176 1.9992,9.0521 0.7219,0 4.776,-15.8273 -1.1662,-4.0541 7.164,-1.3883 2.3324,12.2176 1.9437,9.0521"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10206"
-           d="m 3081.9121,-1817.1861 c -4.1095,0 -7.5712,-1.7216 -10.3849,-5.1647 -2.8138,-3.4431 -4.2207,-7.6638 -4.2207,-12.6619 0,-6.1088 1.6846,-11.255 5.0537,-15.4386 3.4061,-4.1836 7.5712,-6.2754 12.4953,-6.2754 1.9251,0 3.7022,0.2037 5.3313,0.6109 1.666,0.3702 2.8507,0.759 3.5542,1.1662 l 0.9996,0.6109 -3.7763,7.7193 c -0.8516,-0.7404 -2.0548,-1.4254 -3.6098,-2.0548 -1.555,-0.6664 -2.9618,-0.9996 -4.2206,-0.9996 -2.5546,0 -4.6094,1.0552 -6.1644,3.1655 -1.5179,2.1103 -2.2769,5.1277 -2.2769,9.0521 0,3.8874 0.796,7.0899 2.388,9.6075 1.592,2.4805 3.7578,3.7208 6.4976,3.7208 1.7771,0 3.3876,-0.5553 4.8315,-1.666 1.4438,-1.1107 2.462,-2.5916 3.0544,-4.4428 l 4.6093,2.6656 c -1.2217,3.2581 -3.0914,5.8127 -5.6089,7.6638 -2.5176,1.8142 -5.3684,2.7212 -8.5524,2.7212"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10208"
-           d="m 3106.1131,-1817.797 c -1.9993,0 -3.6283,-0.6479 -4.8871,-1.9437 -1.2217,-1.3328 -1.8326,-3.0729 -1.8326,-5.2202 l 0.3332,-6.3865 -0.6109,-13.3283 7.4416,-1.3884 -0.7219,16.8825 c 0,1.592 0.2777,2.7583 0.833,3.4987 0.5924,0.7405 1.5179,1.1107 2.7767,1.1107 1.2588,0 2.9989,-0.6294 5.2203,-1.8882 l 0,-3.332 -0.6109,-14.6056 7.4972,-1.3884 -0.6664,13.8837 0.055,4.5538 c 0,2.3695 0.6109,4.7575 1.8326,7.164 l -5.7756,2.5545 c -1.2217,-2.4065 -1.9622,-4.3317 -2.2213,-5.7756 -3.3321,3.7394 -6.2199,5.609 -8.6634,5.609"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10210"
-           d="m 3137.3105,-1839.1778 c -1.629,0 -2.8878,0.5924 -3.7764,1.7771 -0.8885,1.1478 -1.3328,2.9249 -1.3328,5.3313 0,2.4066 0.4443,4.3503 1.3328,5.8312 0.9256,1.4439 2.2584,2.1658 3.9985,2.1658 0.8886,0 1.7586,-0.2776 2.6102,-0.833 0.8515,-0.5553 1.4994,-1.1107 1.9437,-1.666 l 0.6109,-0.8886 2.7767,2.8878 c -0.1111,0.1851 -0.2592,0.4443 -0.4443,0.7775 -0.1481,0.3332 -0.5554,0.9256 -1.2218,1.7771 -0.6294,0.8515 -1.3143,1.6105 -2.0547,2.2769 -0.7035,0.6294 -1.6291,1.2218 -2.7768,1.7771 -1.1477,0.5184 -2.3509,0.7775 -3.6097,0.7775 -2.7768,0 -5.0352,-1.1292 -6.7752,-3.3876 -1.7401,-2.2584 -2.6102,-5.2017 -2.6102,-8.83 0,-4.5168 1.3144,-8.4783 3.943,-11.8844 2.6286,-3.4061 5.7015,-5.1092 9.2187,-5.1092 1.0737,0 2.1474,0.1667 3.221,0.4998 1.0737,0.2962 2.1659,0.7775 3.2766,1.4439 l -3.8874,6.8308 c -1.3329,-1.0366 -2.8138,-1.555 -4.4428,-1.555"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10212"
-           d="m 3166.4532,-1817.2416 -10.4406,-15.3831 0,0.3887 0.6109,13.0507 -7.4971,1.3883 0.6664,-13.4949 -0.6664,-28.1005 7.6082,-1.3883 -0.722,26.8232 11.107,-12.1621 5.2202,3.5542 -9.7185,9.1077 10.1628,11.9955 -6.3309,4.2206"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10214"
-           d="m 3174.2514,-1829.4037 c 0,-4.7389 1.2773,-8.7559 3.8319,-12.051 2.5546,-3.295 5.5349,-4.9426 8.9411,-4.9426 3.4431,0 6.1088,1.1107 7.997,3.3321 1.9251,2.2214 2.8877,5.1647 2.8877,8.83 0,4.8871 -1.2402,8.9596 -3.7208,12.2176 -2.4435,3.221 -5.5349,4.8315 -9.2742,4.8315 -3.073,0 -5.6276,-1.1477 -7.6638,-3.4431 -1.9993,-2.3325 -2.9989,-5.2573 -2.9989,-8.7745 m 17.4379,-1.9437 c 0,-5.3683 -1.8327,-8.0525 -5.498,-8.0525 -3.8133,0 -5.72,2.5176 -5.72,7.5527 0,2.6286 0.5368,4.6834 1.6105,6.1643 1.0736,1.481 2.4805,2.2214 4.2206,2.2214 1.7771,0 3.1099,-0.6664 3.9985,-1.9992 0.9256,-1.3699 1.3884,-3.3321 1.3884,-5.8867"
-           inkscape:connector-curvature="0" />
-        <path
-           id="path10216"
-           d="m 3201.0425,-1829.4037 c 0,-4.7389 1.2773,-8.7559 3.8319,-12.051 2.5546,-3.295 5.5349,-4.9426 8.9411,-4.9426 3.4431,0 6.1088,1.1107 7.997,3.3321 1.9251,2.2214 2.8877,5.1647 2.8878,8.83 -10e-5,4.8871 -1.2403,8.9596 -3.7209,12.2176 -2.4435,3.221 -5.5349,4.8315 -9.2742,4.8315 -3.073,0 -5.6276,-1.1477 -7.6638,-3.4431 -1.9993,-2.3325 -2.9989,-5.2573 -2.9989,-8.7745 m 17.4379,-1.9437 c 0,-5.3683 -1.8327,-8.0525 -5.4979,-8.0525 -3.8134,0 -5.7201,2.5176 -5.7201,7.5527 0,2.6286 0.5368,4.6834 1.6105,6.1643 1.0737,1.481 2.4805,2.2214 4.2206,2.2214 1.7771,0 3.1099,-0.6664 3.9985,-1.9992 0.9256,-1.3699 1.3884,-3.3321 1.3884,-5.8867"
-           inkscape:connector-curvature="0" />
-      </g>
-    </g>
-  </g>
-</svg>
diff --git a/contrib/themes/diffy/static/diffymute.svg b/contrib/themes/diffy/static/diffymute.svg
deleted file mode 100644
index 6833ba6..0000000
--- a/contrib/themes/diffy/static/diffymute.svg
+++ /dev/null
@@ -1,198 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="600.06732"
-   height="558.20709"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.3.1 r9886"
-   sodipodi:docname="diffy.svg"
-   inkscape:export-filename="/home/sarah/art/diffy.png"
-   inkscape:export-xdpi="500"
-   inkscape:export-ydpi="500">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.66618777"
-     inkscape:cx="300.03365"
-     inkscape:cy="258.35521"
-     inkscape:document-units="px"
-     inkscape:current-layer="g10119"
-     showgrid="false"
-     inkscape:window-width="1366"
-     inkscape:window-height="744"
-     inkscape:window-x="0"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-3069.8852,2251.5084)">
-    <text
-       xml:space="preserve"
-       style="font-size:51.65934372px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans Bold"
-       x="475.83173"
-       y="900.47076"
-       id="text4257"
-       sodipodi:linespacing="125%"
-       transform="scale(1.1411753,0.87628955)"><tspan
-         sodipodi:role="line"
-         x="475.83173"
-         y="900.47076"
-         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;text-anchor:end;font-family:Acme;-inkscape-font-specification:Acme Bold"
-         id="tspan3008" /></text>
-    <flowRoot
-       xml:space="preserve"
-       id="flowRoot4263"
-       style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
-         id="flowRegion4265"><rect
-           id="rect4267"
-           width="313.14728"
-           height="343.45187"
-           x="-335.37064"
-           y="205.85435" /></flowRegion><flowPara
-         id="flowPara4269" /></flowRoot>    <g
-       id="g10119"
-       transform="matrix(0.99201906,-0.12608805,0.12608805,0.99201906,273.85438,408.58042)">
-      <path
-         sodipodi:nodetypes="csccscac"
-         inkscape:connector-curvature="0"
-         id="path4130"
-         d="m 3378.9965,-2206.2972 c 0,0 13.4039,-8.8398 27.0022,-17.1718 13.5984,-8.3319 22.2322,-21.276 22.2322,-21.276 l 34.2799,28.1467 c 0,0 -16.0641,15.553 -24.7742,21.6893 -3.709,2.6129 -10.9463,9.1577 -10.9463,9.1577 0,0 -14.3617,-10.0769 -22.3623,-13.5225 -8.0773,-3.4786 -25.4315,-7.0234 -25.4315,-7.0234 z"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="cac"
-         inkscape:connector-curvature="0"
-         d="m 3414.7387,-2204.9828 c 0,0 8.2233,-5.8571 11.9326,-9.2622 3.8224,-3.5089 10.5596,-11.437 10.5596,-11.437"
-         style="fill:#ffffff;stroke:#ff0000;stroke-width:8.97858429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         id="path4185" />
-      <path
-         sodipodi:nodetypes="cascacsc"
-         inkscape:connector-curvature="0"
-         id="path3307"
-         d="m 3247.6516,-2171.4037 c 0,0 4.3348,-19.2835 10.7071,-26.2701 7.4284,-8.1446 19.072,-14.1542 29.3495,-15.24 27.8177,-2.9386 63.2371,6.1908 63.2371,6.1908 0,0 -30.8042,0.7262 -45.6022,4.5134 -5.1922,1.3289 -14.7941,6.2977 -14.7941,6.2977 0,0 -16.1166,0.8374 -29.8971,12.0927 -13.7804,11.2552 -3.0344,9.7831 -3.0344,9.7831"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="ccsccscscccccscc"
-         inkscape:connector-curvature="0"
-         id="path3011"
-         d="m 3332.9985,-2012.4352 -20.0742,24.9886 c 0,0 -16.3479,-5.082 -40.3255,4.9725 -23.9776,10.0546 -39.2193,26.4042 -39.2193,26.4042 l 7.8897,-0.7975 c 0,0 20.4818,-14.4371 31.9614,-17.1294 11.4796,-2.6923 23.6197,-2.8388 23.6197,-2.8388 0,0 -25.492,10.0264 -30.7497,12.3759 -6.2584,2.7968 -15.0874,9.199 -15.0874,9.199 l 10.4761,-0.2965 c 17.6867,-7.2528 41.3221,-16.0187 59.9685,-15.047 15.5919,1.1274 33.9405,16.7543 33.9405,16.7543 l -3.8165,-9.9895 c 0,0 -10.8307,-8.1932 -16.3866,-13.7491 -5.5558,-5.5558 17.5848,-25.8193 17.5848,-25.8193 -14.3098,-2.8351 -2.1172,-7.9424 -19.7815,-9.0274 z"
-         style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         d="m 3397.9929,-1984.0461 -15.5774,33.0846 c 0,0 -14.4499,-2.2165 -39.5097,4.7139 -25.0597,6.9303 -44.9575,20.0476 -44.9575,20.0476 l 10.5706,1.822 c 0,0 19.7276,-11.1182 30.516,-14.1955 7.747,-2.2097 23.9934,-2.9008 23.9934,-2.9008 0,0 -20.2687,6.6869 -30.057,10.9033 -5.4067,2.329 -15.7817,7.9273 -15.7817,7.9273 l 10.8391,1.5269 c 0,0 31.056,-11.8625 47.4047,-13.3504 11.1103,-1.0112 11.8331,-2.962 33.324,3.1079 10.0509,2.8388 16.3928,6.252 16.3928,6.252 l -3.3122,-5.8696 c 0,0 -5.3909,-4.5711 -8.2712,-6.6111 -3.5982,-2.5484 -11.2202,-7.0059 -11.2202,-7.0059 l 15.4484,-30.858 c -13.3255,-5.9362 -2.8177,-3.6211 -19.8021,-8.5942 z"
-         id="path3013"
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="ccsccacaccasccaccc" />
-      <path
-         sodipodi:nodetypes="aaczsccsssca"
-         inkscape:connector-curvature="0"
-         id="path3014"
-         d="m 3138.8926,-2124.2277 c 9.8375,-20.5358 27.6337,-38.9533 48.4705,-48.136 22.7321,-10.018 51.1582,-10.0879 74.4382,-3.5941 7.6944,6.5 10.3115,-17.1804 46.9059,-27.4208 36.5944,-10.2403 91.6429,-6.509 142.1505,22.7854 54.0494,31.3487 -23.7386,41.9213 -8.0812,94.4492 32.3844,113.578 -63.1345,101.5204 -63.1345,101.5204 0,0 -25.2539,-16.1625 -35.3554,-17.1726 -10.1015,-1.0102 -46.6429,-25.1251 -68.8662,-28.6607 -22.2234,-3.5355 -36.9003,18.6222 -107.991,-9.9282 -24.3178,-9.7661 -33.8401,-33.335 -33.8401,-33.335 0,0 -2.0102,-35.2405 5.3033,-50.5076 z"
-         style="fill:#0000ff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="cssscscsac"
-         inkscape:connector-curvature="0"
-         id="path3028"
-         d="m 3179.7636,-2035.4822 c 0,0 33.988,-7.9036 57.2215,-14.9746 23.2335,-7.0711 36.1637,-43.7474 71.519,-48.7981 35.3554,-5.0508 68.528,22.1624 80.6498,47.4162 12.1218,25.2539 50.6701,55.6193 50.6701,55.6193 0,0 1.0101,11.1117 -14.1422,18.1828 -15.1522,7.0711 -10.4989,7.6628 -10.4989,7.6628 0,0 -43.1103,-11.4139 -62.396,-21.4139 -19.2857,-10 -61.236,-23.044 -92.9377,-30.7963 -26.2653,-6.4228 -35.0334,2.5255 -80.0856,-12.8982 z"
-         style="fill:#ff6600;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="cscccccscc"
-         inkscape:connector-curvature="0"
-         id="path3009"
-         d="m 3470.1209,-2002.8128 c 0,0 13.8937,99.0002 33.6829,157.8517 19.7893,58.8514 78.4464,180.4742 78.4464,180.4742 l 28.1031,-31.8521 -52.5625,-109.7203 60.5152,95.9016 26.0124,-35.7271 c 0,0 -70.663,-88.4878 -93.8954,-140.4597 -23.2324,-51.9718 -37.4945,-152.9082 -37.4945,-152.9082 z"
-         style="fill:#0000ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="csscsccccccccccsc"
-         inkscape:connector-curvature="0"
-         id="path3016"
-         d="m 3359.2154,-2191.502 c 0,0 -40.7142,5 -57.1428,42.1428 -16.4286,37.1429 50.7143,115.7143 60.7143,132.1429 10,16.4286 24.2857,38.5714 24.2857,38.5714 0,0 47.6766,32.0335 82.9062,53.8444 32.6567,20.2179 65.2266,33.1109 65.2266,33.1109 0,0 1.1687,-0.4937 7.0265,-19.1963 -12.1218,-21.7183 -38.3216,-53.2543 -54.5036,-69.192 24.2792,14.1167 43.346,28.1254 60.1041,56.5482 0,0 4.9588,-21.3916 6.5659,-30.7894 -3.0357,-9.5763 -47.4771,-56.5685 -47.4771,-56.5685 20.5517,8.7613 33.7508,23.0255 50.0892,44.7732 0,0 3.3263,-21.5598 3.8366,-32.9472 -2.5253,-6.566 -33.7228,-35.0595 -33.7228,-35.0595 13.1654,6.6374 19.714,11.0661 34.4127,24.0476 0,0 -3.0357,-60.7142 -66.6072,-130.7142 -63.5714,-70 -135.7143,-50.7143 -135.7143,-50.7143 z"
-         style="fill:#000080;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <g
-         transform="translate(2935.644,-2513.1499)"
-         id="g3138">
-        <path
-           style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-           d="m 337.66259,394.0585 c 0,0 -28.86328,-30.61257 -60.78781,-25.80202 -31.92454,4.81054 -41.10831,33.67382 -39.79634,39.79633 1.31197,6.12252 13.557,11.80771 22.30345,15.74361 8.74644,3.9359 32.79917,-0.43732 45.48152,-6.55984 12.68235,-6.12251 32.79918,-23.17808 32.79918,-23.17808 z"
-           id="path3022"
-           inkscape:connector-curvature="0" />
-        <path
-           style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-miterlimit:4"
-           d="m 286.65383,372.49887 c 12.50235,2.18615 20.86528,14.09354 18.67912,26.59589 -2.18615,12.50234 -14.09354,20.86527 -26.59589,18.67911 -12.50235,-2.18616 -20.86528,-14.09354 -18.67912,-26.59588 z"
-           id="path3024"
-           inkscape:connector-curvature="0"
-           sodipodi:nodetypes="csscc" />
-        <path
-           sodipodi:type="arc"
-           style="fill:#000000;fill-opacity:1;stroke:none"
-           id="path3026"
-           sodipodi:cx="229.64285"
-           sodipodi:cy="403.79074"
-           sodipodi:rx="8.2142859"
-           sodipodi:ry="8.2142859"
-           d="m 237.85714,403.79074 c 0,4.53663 -3.67766,8.21429 -8.21429,8.21429 -4.53662,0 -8.21428,-3.67766 -8.21428,-8.21429 0,-4.53662 3.67766,-8.21428 8.21428,-8.21428 4.53663,0 8.21429,3.67766 8.21429,8.21428 z"
-           transform="matrix(1.5694327,0,0,1.5694327,-85.566867,-233.30737)" />
-      </g>
-      <path
-         sodipodi:nodetypes="cssssssc"
-         style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-         d="m 3110.2692,-2103.8575 c -32.3249,20.7081 -31.4522,56.0845 -27.4116,60.6301 4.0406,4.5457 8.0812,1.0102 15.6574,-1.5152 7.5761,-2.5254 26.2639,-10.1015 42.4264,-11.1117 16.1624,-1.0101 21.2132,3.0305 23.7386,-0.505 2.5254,-3.5356 4.0406,-15.1523 3.0304,-25.7589 -1.0101,-10.6066 -1.0101,-23.2335 -7.071,-25.2538 -6.061,-2.0203 -38.2484,-5.0718 -50.3702,3.5145 z"
-         id="path3296"
-         inkscape:connector-curvature="0" />
-      <path
-         inkscape:connector-curvature="0"
-         id="path3305"
-         d="m 3251.1404,-2178.2729 c 0,0 12.1428,21.0714 67.1428,32.5 55,11.4286 125.7143,-38.2143 125.7143,-38.2143 l 38.7857,38.6429 c 0,0 -113.0714,25.6428 -150.2143,21 -37.1428,-4.6429 -81.4285,-50.3572 -81.4285,-50.3572"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         sodipodi:nodetypes="ccccc"
-         inkscape:connector-curvature="0"
-         id="path4099"
-         d="m 3453.1169,-2166.1191 -30.2186,14.3667 6.8378,5.5184 29.9689,-14.0733 z"
-         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
-      <path
-         sodipodi:nodetypes="cacscacsc"
-         inkscape:connector-curvature="0"
-         id="path3303"
-         d="m 3154.8351,-2149.0637 c 0,0 -6.1891,7.2925 -8.7928,11.2888 -2.8625,4.3936 -7.4472,13.8569 -7.4472,13.8569 0,0 49.8667,-24.0831 67.6065,-30.2972 18.2787,-6.4028 54.803,-21.7873 54.803,-21.7873 0,0 -3.8451,-1.8876 -5.9103,-2.3872 -3.1018,-0.7503 -9.5343,-0.868 -9.5343,-0.868 0,0 -25.7837,-0.1235 -42.8032,6.1343 -17.0196,6.2579 -47.9217,24.0597 -47.9217,24.0597 z"
-         style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-      <path
-         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
-         d="m 3433.0379,-2169.2212 24.4739,22.8165 -8.1844,3.1976 -24.3243,-22.4616 z"
-         id="path27241"
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="ccccc" />
-    </g>
-  </g>
-</svg>
diff --git a/contrib/themes/diffy/static/logo.png b/contrib/themes/diffy/static/logo.png
deleted file mode 100644
index 989c303..0000000
--- a/contrib/themes/diffy/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/contrib/themes/spotify/etc/GerritSite.css b/contrib/themes/spotify/etc/GerritSite.css
deleted file mode 100644
index 489452a..0000000
--- a/contrib/themes/spotify/etc/GerritSite.css
+++ /dev/null
@@ -1,113 +0,0 @@
-#gerrit_topmenu {
-	font-size: 9pt !important;
-	padding-top: 5px !important;
-	padding-left: 15px !important;
-	padding-right: 15px !important;
-	background: url(static/background-spotigreen.jpg) !important;
-	float: left !important;
-	margin-left: 250px !important;
-	margin-right: 150px !important;
-	border-radius: 0 0 10px 10px !important;
-	padding-bottom: 10px !important;
-	border-bottom: 1px solid #abc506 !important;
-	border-right: 1px solid #abc506 !important;
-	border-left: 1px solid #abc506 !important;
-}
-
-body, .gwt-DialogBox .dialogMiddleCenter {
-	background: #FFF url(static/background-gradient.png) no-repeat !important;
-}
-
-#logo-spotify {
-	background: url(static/logo.png) no-repeat;
-	height: 98px;
-	width: 228px;
-	margin-left: 2px;
-}
-
-#gerrit_header {
-	background: #FCFEEF;
-	padding-bottom: 10px;
-	border-bottom: dashed 1px rgba(0, 0, 0, 0.05);
-}
-
-#gerrit_topmenu .gwt-TextBox {
-	margin-top: 15px;
-	width: 240px;
-}
-
-#gerrit_topmenu > table {
-    float: left;
-}
-
-#gerrit_topmenu > table > tbody > tr > td:nth-child(3) a {
-	margin-left: 5px;
-	background-color: #FFC;
-	border-radius: 3px;
-	padding: 5px 5px;
-	border-right: none !important;
-}
-
-.gwt-TabPanelBottom {
-	background: #FFC !important;
-	border-radius: 0 0 3px 3px !important;
-	padding-bottom: 6px !important;
-	padding-top: 6px !important;
-}
-
-.gwt-TabBar .gwt-TabBarItem, .gwt-TabBar .gwt-TabBarRest, .gwt-TabBar .gwt-TabPanelBottom {
-	background: transparent !important;
-}
-
-.gwt-TabBarItem-wrapper {
-	background: rgba(255, 255, 255, 0.2) !important;
-}
-
-#gerrit_topmenu .gwt-TabBar .gwt-TabBarItem-selected {
-	background: #FFC !important;
-	border-radius: 3px 3px 0 0 !important;
-}
-
-#gerrit_topmenu .gwt-TabBar .gwt-TabBarItem-selected:hover {
-	background: #FFC !important;
-}
-
-#gerrit_topmenu .gwt-TabBar .gwt-TabBarItem-selected:focus {
-	outline: none !important;
-}
-
-#gerrit_topmenu > table > tbody > tr > td > table {
-	border: none !important;
-}
-
-.gwt-TabBar {
-	border-bottom: 1px solid #E2E2AD !important;
-}
-
-.gwt-TabBarItem {
-	border-right: 1px solid rgba(79, 58, 0, 0.3) !important;
-	background: rgba(255, 255, 255, 0.2) !important;
-}
-
-.gwt-TabBarItem:hover {
-	background: rgba(255, 255, 255, 0.1) !important;
-}
-
-a, a:visited {
-	text-decoration: none !important;
-	color: #3F4D00 !important;
-}
-
-a:hover {
-	color: #5d7200 !important;
-	text-decoration: underline !important;
-}
-
-.gwt-Label {
-height: 30px !important;
-line-height: 30px !important;
-}
-
-#gerrit_btmmenu > div {
-	color: rgba(0, 0, 0, 0.3) !important;
-}
\ No newline at end of file
diff --git a/contrib/themes/spotify/etc/GerritSiteHeader.html b/contrib/themes/spotify/etc/GerritSiteHeader.html
deleted file mode 100644
index 8af84be..0000000
--- a/contrib/themes/spotify/etc/GerritSiteHeader.html
+++ /dev/null
@@ -1 +0,0 @@
-<div id="logo-spotify"></div>
\ No newline at end of file
diff --git a/contrib/themes/spotify/static/background-gradient.png b/contrib/themes/spotify/static/background-gradient.png
deleted file mode 100644
index 3b9422e..0000000
--- a/contrib/themes/spotify/static/background-gradient.png
+++ /dev/null
Binary files differ
diff --git a/contrib/themes/spotify/static/background-spotigreen.jpg b/contrib/themes/spotify/static/background-spotigreen.jpg
deleted file mode 100644
index fcc7983..0000000
--- a/contrib/themes/spotify/static/background-spotigreen.jpg
+++ /dev/null
Binary files differ
diff --git a/contrib/themes/spotify/static/logo.png b/contrib/themes/spotify/static/logo.png
deleted file mode 100644
index c231031..0000000
--- a/contrib/themes/spotify/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
index e04a643..1ff7d0e 100644
--- a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
+++ b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
@@ -37,6 +37,10 @@
     countsByChange.clear();
   }
 
+  public long getCount(ChangeInfo info) {
+    return countsByChange.get(info._number);
+  }
+
   public void assertReindexOf(ChangeInfo info) {
     assertReindexOf(info, 1);
   }
diff --git a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
index a498ab0..3ffa97a 100644
--- a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
@@ -18,7 +18,6 @@
 
 public class AccountDetailInfo extends AccountInfo {
   public Timestamp registeredOn;
-  public Boolean inactive;
 
   public AccountDetailInfo(Integer id) {
     super(id);
diff --git a/java/com/google/gerrit/extensions/common/AccountInfo.java b/java/com/google/gerrit/extensions/common/AccountInfo.java
index f20509b..e949f07 100644
--- a/java/com/google/gerrit/extensions/common/AccountInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountInfo.java
@@ -26,6 +26,7 @@
   public List<AvatarInfo> avatars;
   public Boolean _moreAccounts;
   public String status;
+  public Boolean inactive;
 
   public AccountInfo(Integer id) {
     this._accountId = id;
diff --git a/java/com/google/gerrit/extensions/restapi/Response.java b/java/com/google/gerrit/extensions/restapi/Response.java
index 6a1020a..f5b9145 100644
--- a/java/com/google/gerrit/extensions/restapi/Response.java
+++ b/java/com/google/gerrit/extensions/restapi/Response.java
@@ -14,6 +14,10 @@
 
 package com.google.gerrit.extensions.restapi;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.gerrit.common.Nullable;
+import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
 /** Special return value to mean specific HTTP status codes in a REST API. */
@@ -51,6 +55,19 @@
     return new Redirect(location);
   }
 
+  /**
+   * HTTP 500 Internal Server Error: failure due to an unexpected exception.
+   *
+   * <p>Can be returned from REST endpoints, instead of throwing the exception, if additional
+   * properties (e.g. a traceId) should be set on the response.
+   *
+   * @param cause the exception that caused the request to fail, must not be a {@link
+   *     RestApiException} because such an exception would result in a 4XX response code
+   */
+  public static <T> InternalServerError<T> internalServerError(Exception cause) {
+    return new InternalServerError<>(cause);
+  }
+
   /** Arbitrary status code with wrapped result. */
   public static <T> Response<T> withStatusCode(int statusCode, T value) {
     return new Impl<>(statusCode, value);
@@ -64,6 +81,17 @@
     return obj;
   }
 
+  private String traceId;
+
+  public Response<T> traceId(@Nullable String traceId) {
+    this.traceId = traceId;
+    return this;
+  }
+
+  public Optional<String> traceId() {
+    return Optional.ofNullable(traceId);
+  }
+
   public abstract boolean isNone();
 
   public abstract int statusCode();
@@ -258,4 +286,57 @@
       return String.format("[202 Accepted] %s", location);
     }
   }
+
+  public static final class InternalServerError<T> extends Response<T> {
+    private final Exception cause;
+
+    private InternalServerError(Exception cause) {
+      checkArgument(!(cause instanceof RestApiException), "cause must not be a RestApiException");
+      this.cause = cause;
+    }
+
+    @Override
+    public boolean isNone() {
+      return false;
+    }
+
+    @Override
+    public int statusCode() {
+      return 500;
+    }
+
+    @Override
+    public T value() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CacheControl caching() {
+      return CacheControl.NONE;
+    }
+
+    @Override
+    public Response<T> caching(CacheControl c) {
+      throw new UnsupportedOperationException();
+    }
+
+    public Exception cause() {
+      return cause;
+    }
+
+    @Override
+    public int hashCode() {
+      return cause.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof InternalServerError && ((InternalServerError<?>) o).cause.equals(cause);
+    }
+
+    @Override
+    public String toString() {
+      return String.format("[500 Internal Server Error] %s", cause.getClass());
+    }
+  }
 }
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 66768ec..250b665 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -552,6 +552,8 @@
             throw new ResourceNotFoundException();
           }
 
+          response.traceId().ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId));
+
           if (response instanceof Response.Redirect) {
             CacheHeaders.setNotCacheable(res);
             String location = ((Response.Redirect) response).location();
@@ -564,6 +566,12 @@
             res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted) response).location());
             logger.atFinest().log("REST call succeeded: %d", response.statusCode());
             return;
+          } else if (response instanceof Response.InternalServerError) {
+            // Rethrow the exception to have exactly the same error handling as if the REST endpoint
+            // would have thrown the exception directly, instead of returning
+            // Response.InternalServerError.
+            Exception cause = ((Response.InternalServerError<?>) response).cause();
+            throw cause;
           }
 
           status = response.statusCode();
diff --git a/java/com/google/gerrit/server/account/AccountDirectory.java b/java/com/google/gerrit/server/account/AccountDirectory.java
index ee9265f..60c1678 100644
--- a/java/com/google/gerrit/server/account/AccountDirectory.java
+++ b/java/com/google/gerrit/server/account/AccountDirectory.java
@@ -45,7 +45,10 @@
     ID,
 
     /** The user-settable status of this account (e.g. busy, OOO, available) */
-    STATUS
+    STATUS,
+
+    /** The state of the account (e.g. active or inactive) */
+    STATE
   }
 
   public abstract void fillAccountInfo(Iterable<? extends AccountInfo> in, Set<FillOptions> options)
diff --git a/java/com/google/gerrit/server/account/AccountLoader.java b/java/com/google/gerrit/server/account/AccountLoader.java
index 09b9ac3..5d0059e 100644
--- a/java/com/google/gerrit/server/account/AccountLoader.java
+++ b/java/com/google/gerrit/server/account/AccountLoader.java
@@ -42,6 +42,7 @@
               FillOptions.EMAIL,
               FillOptions.USERNAME,
               FillOptions.STATUS,
+              FillOptions.STATE,
               FillOptions.AVATARS));
 
   public interface Factory {
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index 244bb9e..d07765c 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -516,7 +516,12 @@
    * @throws IOException if an error occurs.
    */
   public Result resolve(String input) throws ConfigInvalidException, IOException {
-    return searchImpl(input, searchers, visibilitySupplier());
+    return searchImpl(input, searchers, visibilitySupplier(), accountActivityPredicate());
+  }
+
+  public Result resolve(String input, Predicate<AccountState> accountActivityPredicate)
+      throws ConfigInvalidException, IOException {
+    return searchImpl(input, searchers, visibilitySupplier(), accountActivityPredicate);
   }
 
   /**
@@ -544,18 +549,24 @@
    */
   @Deprecated
   public Result resolveByNameOrEmail(String input) throws ConfigInvalidException, IOException {
-    return searchImpl(input, nameOrEmailSearchers, visibilitySupplier());
+    return searchImpl(
+        input, nameOrEmailSearchers, visibilitySupplier(), accountActivityPredicate());
   }
 
   private Supplier<Predicate<AccountState>> visibilitySupplier() {
     return () -> accountControlFactory.get()::canSee;
   }
 
+  private Predicate<AccountState> accountActivityPredicate() {
+    return (AccountState accountState) -> accountState.getAccount().isActive();
+  }
+
   @VisibleForTesting
   Result searchImpl(
       String input,
       ImmutableList<Searcher<?>> searchers,
-      Supplier<Predicate<AccountState>> visibilitySupplier)
+      Supplier<Predicate<AccountState>> visibilitySupplier,
+      Predicate<AccountState> accountActivityPredicate)
       throws ConfigInvalidException, IOException {
     visibilitySupplier = Suppliers.memoize(visibilitySupplier::get);
     List<AccountState> inactive = new ArrayList<>();
@@ -576,7 +587,7 @@
         // Keep track of all inactive candidates discovered by any searchers. If we end up short-
         // circuiting, the inactive list will be discarded.
         List<AccountState> active = new ArrayList<>();
-        results.forEach(a -> (a.getAccount().isActive() ? active : inactive).add(a));
+        results.forEach(a -> (accountActivityPredicate.test(a) ? active : inactive).add(a));
         list = active;
       } else {
         list = results.collect(toImmutableList());
diff --git a/java/com/google/gerrit/server/account/InternalAccountDirectory.java b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
index 8062eaf..dde8f25 100644
--- a/java/com/google/gerrit/server/account/InternalAccountDirectory.java
+++ b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
@@ -147,6 +147,10 @@
       info.status = account.status();
     }
 
+    if (options.contains(FillOptions.STATE)) {
+      info.inactive = account.inactive() ? true : null;
+    }
+
     if (options.contains(FillOptions.AVATARS)) {
       AvatarProvider ap = avatar.get();
       if (ap != null) {
diff --git a/java/com/google/gerrit/server/git/MultiProgressMonitor.java b/java/com/google/gerrit/server/git/MultiProgressMonitor.java
index b72ea92..bfc5135 100644
--- a/java/com/google/gerrit/server/git/MultiProgressMonitor.java
+++ b/java/com/google/gerrit/server/git/MultiProgressMonitor.java
@@ -346,6 +346,8 @@
         out.write(Constants.encode(s.toString()));
         out.flush();
       } catch (IOException e) {
+        logger.atWarning().withCause(e).log(
+            "Sending progress to client failed. Stop sending updates for task %s", taskName);
         write = false;
       }
     }
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 599c604..1eed4d2 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -237,7 +237,6 @@
     Set<String> r = new HashSet<>();
     for (String path : paths) {
       StringBuilder directory = new StringBuilder();
-      directory.append("");
       r.add(directory.toString());
       String nextPart = null;
       for (String part : s.split(path)) {
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 2f722d3..fd0140a 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -54,6 +54,7 @@
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
+import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.account.GroupMembers;
@@ -1000,7 +1001,7 @@
   @Operator
   public Predicate<ChangeData> owner(String who)
       throws QueryParseException, IOException, ConfigInvalidException {
-    return owner(parseAccount(who));
+    return owner(parseAccount(who, (AccountState s) -> true));
   }
 
   private Predicate<ChangeData> owner(Set<Account.Id> who) {
@@ -1023,7 +1024,7 @@
   @Operator
   public Predicate<ChangeData> assignee(String who)
       throws QueryParseException, IOException, ConfigInvalidException {
-    return assignee(parseAccount(who));
+    return assignee(parseAccount(who, (AccountState s) -> true));
   }
 
   private Predicate<ChangeData> assignee(Set<Account.Id> who) {
@@ -1349,6 +1350,19 @@
     }
   }
 
+  private Set<Account.Id> parseAccount(
+      String who, java.util.function.Predicate<AccountState> activityFilter)
+      throws QueryParseException, IOException, ConfigInvalidException {
+    try {
+      return args.accountResolver.resolve(who, activityFilter).asNonEmptyIdSet();
+    } catch (UnresolvableAccountException e) {
+      if (e.isSelf()) {
+        throw new QueryRequiresAuthException(e.getMessage(), e);
+      }
+      throw new QueryParseException(e.getMessage(), e);
+    }
+  }
+
   private GroupReference parseGroup(String group) throws QueryParseException {
     GroupReference g = GroupBackends.findBestSuggestion(args.groupBackend, group);
     if (g == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 4e89306..445279f 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -160,6 +160,9 @@
       BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
       throws IOException, InvalidChangeOperationException, RestApiException, UpdateException,
           PermissionBackendException, ConfigInvalidException {
+    if (!user.get().isIdentifiedUser()) {
+      throw new AuthException("Authentication required");
+    }
     IdentifiedUser me = user.get().asIdentifiedUser();
     checkAndSanitizeChangeInput(input, me);
 
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 910bc0c..4755e85 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -257,10 +257,11 @@
       checkLabels(revision, labelTypes, input.labels);
     }
     if (input.comments != null) {
-      cleanUpComments(input.comments);
+      input.comments = cleanUpComments(input.comments);
       checkComments(revision, input.comments);
     }
     if (input.robotComments != null) {
+      input.robotComments = cleanUpComments(input.robotComments);
       checkRobotComments(revision, input.robotComments);
     }
 
@@ -544,37 +545,30 @@
     }
   }
 
-  private static <T extends CommentInput> void cleanUpComments(
+  private static <T extends CommentInput> Map<String, List<T>> cleanUpComments(
       Map<String, List<T>> commentsPerPath) {
-    Iterator<List<T>> mapValueIterator = commentsPerPath.values().iterator();
-    while (mapValueIterator.hasNext()) {
-      List<T> comments = mapValueIterator.next();
+    Map<String, List<T>> cleanedUpCommentMap = new HashMap<>();
+    for (Map.Entry<String, List<T>> e : commentsPerPath.entrySet()) {
+      String path = e.getKey();
+      List<T> comments = e.getValue();
+
       if (comments == null) {
-        mapValueIterator.remove();
         continue;
       }
 
-      cleanUpComments(comments);
-      if (comments.isEmpty()) {
-        mapValueIterator.remove();
+      List<T> cleanedUpComments = cleanUpComments(comments);
+      if (!cleanedUpComments.isEmpty()) {
+        cleanedUpCommentMap.put(path, cleanedUpComments);
       }
     }
+    return cleanedUpCommentMap;
   }
 
-  private static <T extends CommentInput> void cleanUpComments(List<T> comments) {
-    Iterator<T> commentsIterator = comments.iterator();
-    while (commentsIterator.hasNext()) {
-      T comment = commentsIterator.next();
-      if (comment == null) {
-        commentsIterator.remove();
-        continue;
-      }
-
-      comment.message = Strings.nullToEmpty(comment.message).trim();
-      if (comment.message.isEmpty()) {
-        commentsIterator.remove();
-      }
-    }
+  private static <T extends CommentInput> List<T> cleanUpComments(List<T> comments) {
+    return comments.stream()
+        .filter(Objects::nonNull)
+        .filter(comment -> !Strings.nullToEmpty(comment.message).trim().isEmpty())
+        .collect(toList());
   }
 
   private <T extends CommentInput> void checkComments(
@@ -632,7 +626,6 @@
   private void checkRobotComments(
       RevisionResource revision, Map<String, List<RobotCommentInput>> in)
       throws BadRequestException, PatchListNotAvailableException {
-    cleanUpComments(in);
     for (Map.Entry<String, List<RobotCommentInput>> e : in.entrySet()) {
       String commentPath = e.getKey();
       for (RobotCommentInput c : e.getValue()) {
@@ -1150,9 +1143,6 @@
     }
 
     private boolean isReviewer(ChangeContext ctx) {
-      if (ctx.getAccountId().equals(ctx.getChange().getOwner())) {
-        return true;
-      }
       ChangeData cd = changeDataFactory.create(ctx.getNotes());
       ReviewerSet reviewers = cd.reviewers();
       return reviewers.byState(REVIEWER).contains(ctx.getAccountId());
diff --git a/java/com/google/gerrit/server/restapi/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
index 21e2e4f..dd84624 100644
--- a/java/com/google/gerrit/server/restapi/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -25,7 +25,9 @@
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.change.ChangeResource;
@@ -54,6 +56,7 @@
   private final ReviewerAdder reviewerAdder;
   private final AccountLoader.Factory accountLoaderFactory;
   private final PermissionBackend permissionBackend;
+  private final ApprovalsUtil approvalsUtil;
 
   @Inject
   PutAssignee(
@@ -62,13 +65,15 @@
       RetryHelper retryHelper,
       ReviewerAdder reviewerAdder,
       AccountLoader.Factory accountLoaderFactory,
-      PermissionBackend permissionBackend) {
+      PermissionBackend permissionBackend,
+      ApprovalsUtil approvalsUtil) {
     super(retryHelper);
     this.accountResolver = accountResolver;
     this.assigneeFactory = assigneeFactory;
     this.reviewerAdder = reviewerAdder;
     this.accountLoaderFactory = accountLoaderFactory;
     this.permissionBackend = permissionBackend;
+    this.approvalsUtil = approvalsUtil;
   }
 
   @Override
@@ -98,9 +103,12 @@
       SetAssigneeOp op = assigneeFactory.create(assignee);
       bu.addOp(rsrc.getId(), op);
 
-      ReviewerAddition reviewersAddition = addAssigneeAsCC(rsrc, input.assignee);
-      reviewersAddition.op.suppressEmail();
-      bu.addOp(rsrc.getId(), reviewersAddition.op);
+      ReviewerSet currentReviewers = approvalsUtil.getReviewers(rsrc.getNotes());
+      if (!currentReviewers.all().contains(assignee.getAccountId())) {
+        ReviewerAddition reviewersAddition = addAssigneeAsCC(rsrc, input.assignee);
+        reviewersAddition.op.suppressEmail();
+        bu.addOp(rsrc.getId(), reviewersAddition.op);
+      }
 
       bu.execute();
       return Response.ok(accountLoaderFactory.create(true).fillOne(assignee.getAccountId()));
diff --git a/java/com/google/gerrit/server/restapi/config/IndexChanges.java b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
new file mode 100644
index 0000000..904c44f
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
@@ -0,0 +1,68 @@
+// 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.restapi.config;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.change.ChangeFinder;
+import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.config.IndexChanges.Input;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Set;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+@Singleton
+public class IndexChanges implements RestModifyView<ConfigResource, Input> {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  public static class Input {
+    public Set<String> changes;
+  }
+
+  private final ChangeFinder changeFinder;
+  private final ChangeData.Factory changeDataFactory;
+  private final ChangeIndexer indexer;
+
+  @Inject
+  IndexChanges(
+      ChangeFinder changeFinder, ChangeData.Factory changeDataFactory, ChangeIndexer indexer) {
+    this.changeFinder = changeFinder;
+    this.changeDataFactory = changeDataFactory;
+    this.indexer = indexer;
+  }
+
+  @Override
+  public Response<String> apply(ConfigResource resource, Input input) {
+    if (input == null || input.changes == null) {
+      return Response.ok("Nothing to index");
+    }
+
+    for (String id : input.changes) {
+      for (ChangeNotes n : changeFinder.find(id)) {
+        indexer.index(changeDataFactory.create(n));
+        logger.atFine().log("Indexed change %s", id);
+      }
+    }
+
+    return Response.ok("Indexed changes " + input.changes);
+  }
+}
diff --git a/java/com/google/gerrit/server/restapi/config/Module.java b/java/com/google/gerrit/server/restapi/config/Module.java
index c4a6f56..78a7f48 100644
--- a/java/com/google/gerrit/server/restapi/config/Module.java
+++ b/java/com/google/gerrit/server/restapi/config/Module.java
@@ -37,6 +37,7 @@
     get(CONFIG_KIND, "version").to(GetVersion.class);
     get(CONFIG_KIND, "info").to(GetServerInfo.class);
     post(CONFIG_KIND, "check.consistency").to(CheckConsistency.class);
+    post(CONFIG_KIND, "index.changes").to(IndexChanges.class);
     post(CONFIG_KIND, "reload").to(ReloadConfig.class);
     get(CONFIG_KIND, "preferences").to(GetPreferences.class);
     put(CONFIG_KIND, "preferences").to(SetPreferences.class);
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index d24832b..b120379 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -102,6 +102,8 @@
 
     abstract Optional<Predicate<Throwable>> retryWithTrace();
 
+    abstract Optional<Consumer<String>> onAutoTrace();
+
     @AutoValue.Builder
     public abstract static class Builder {
       public abstract Builder listener(RetryListener listener);
@@ -112,6 +114,8 @@
 
       public abstract Builder retryWithTrace(Predicate<Throwable> exceptionPredicate);
 
+      public abstract Builder onAutoTrace(Consumer<String> traceIdConsumer);
+
       public abstract Options build();
     }
   }
@@ -311,9 +315,9 @@
                     && 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();
+                    String traceId = "retry-on-failure-" + new RequestId();
+                    traceContext.addTag(RequestId.Type.TRACE_ID, traceId).forceLogging();
+                    opts.onAutoTrace().ifPresent(c -> c.accept(traceId));
                     logger.atFine().withCause(t).log(
                         "AutoRetry: %s failed, retry with tracing enabled", caller);
                     metrics.autoRetryCount.increment(actionType, caller);
diff --git a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
index 9ed5a67..96c2ed3 100644
--- a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.update;
 
+import com.google.common.base.Throwables;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -21,6 +22,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
 import com.google.gerrit.extensions.restapi.RestResource;
+import java.util.concurrent.atomic.AtomicReference;
 
 public abstract class RetryingRestCollectionModifyView<
         P extends RestResource, C extends RestResource, I, O>
@@ -34,13 +36,21 @@
   @Override
   public final Response<O> apply(P parentResource, I input)
       throws AuthException, BadRequestException, ResourceConflictException, Exception {
-    RetryHelper.Options retryOptions =
-        RetryHelper.options()
-            .caller(getClass())
-            .retryWithTrace(t -> !(t instanceof RestApiException))
-            .build();
-    return retryHelper.execute(
-        (updateFactory) -> applyImpl(updateFactory, parentResource, input), retryOptions);
+    AtomicReference<String> traceId = new AtomicReference<>(null);
+    try {
+      RetryHelper.Options retryOptions =
+          RetryHelper.options()
+              .caller(getClass())
+              .retryWithTrace(t -> !(t instanceof RestApiException))
+              .onAutoTrace(traceId::set)
+              .build();
+      return retryHelper
+          .execute((updateFactory) -> applyImpl(updateFactory, parentResource, input), retryOptions)
+          .traceId(traceId.get());
+    } catch (Exception e) {
+      Throwables.throwIfInstanceOf(e, RestApiException.class);
+      return Response.<O>internalServerError(e).traceId(traceId.get());
+    }
   }
 
   protected abstract Response<O> applyImpl(
diff --git a/java/com/google/gerrit/server/update/RetryingRestModifyView.java b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
index 20cf0c9..275dc55 100644
--- a/java/com/google/gerrit/server/update/RetryingRestModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
@@ -14,10 +14,12 @@
 
 package com.google.gerrit.server.update;
 
+import com.google.common.base.Throwables;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestResource;
+import java.util.concurrent.atomic.AtomicReference;
 
 public abstract class RetryingRestModifyView<R extends RestResource, I, O>
     implements RestModifyView<R, I> {
@@ -28,14 +30,22 @@
   }
 
   @Override
-  public final Response<O> apply(R resource, I input) throws Exception {
-    RetryHelper.Options retryOptions =
-        RetryHelper.options()
-            .caller(getClass())
-            .retryWithTrace(t -> !(t instanceof RestApiException))
-            .build();
-    return retryHelper.execute(
-        (updateFactory) -> applyImpl(updateFactory, resource, input), retryOptions);
+  public final Response<O> apply(R resource, I input) throws RestApiException {
+    AtomicReference<String> traceId = new AtomicReference<>(null);
+    try {
+      RetryHelper.Options retryOptions =
+          RetryHelper.options()
+              .caller(getClass())
+              .retryWithTrace(t -> !(t instanceof RestApiException))
+              .onAutoTrace(traceId::set)
+              .build();
+      return retryHelper
+          .execute((updateFactory) -> applyImpl(updateFactory, resource, input), retryOptions)
+          .traceId(traceId.get());
+    } catch (Exception e) {
+      Throwables.throwIfInstanceOf(e, RestApiException.class);
+      return Response.<O>internalServerError(e).traceId(traceId.get());
+    }
   }
 
   protected abstract Response<O> applyImpl(BatchUpdate.Factory updateFactory, R resource, I input)
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index f5298ea..2ab91ae 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -9,12 +9,8 @@
     ),
     visibility = ["//visibility:public"],
     exports = [
+        "//lib:junit",
         "//lib/easymock",
-        "//lib/powermock:powermock-api-easymock",
-        "//lib/powermock:powermock-api-support",
-        "//lib/powermock:powermock-core",
-        "//lib/powermock:powermock-module-junit4",
-        "//lib/powermock:powermock-module-junit4-common",
     ],
     deps = [
         "//java/com/google/gerrit/acceptance/testsuite/project",
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 48c9995..9fb6d26 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -569,6 +569,24 @@
   }
 
   @Test
+  public void shouldAllowQueryByEmailForInactiveUser() throws Exception {
+    Account.Id activatableAccountId =
+        accountOperations.newAccount().inactive().preferredEmail("foo@activatable.com").create();
+    accountIndexedCounter.assertReindexOf(activatableAccountId, 1);
+
+    gApi.changes().query("owner:foo@activatable.com").get();
+  }
+
+  @Test
+  public void shouldAllowQueryByUserNameForInactiveUser() throws Exception {
+    Account.Id activatableAccountId =
+        accountOperations.newAccount().inactive().username("foo").create();
+    accountIndexedCounter.assertReindexOf(activatableAccountId, 1);
+
+    gApi.changes().query("owner:foo").get();
+  }
+
+  @Test
   public void validateAccountActivation() throws Exception {
     Account.Id activatableAccountId =
         accountOperations.newAccount().inactive().preferredEmail("foo@activatable.com").create();
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 9fefbe9..9dc79f6 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -508,8 +508,7 @@
     Function<Collection<AccountInfo>, Collection<String>> toEmails =
         ais -> ais.stream().map(ai -> ai.email).collect(toSet());
     assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
-        .containsExactly(
-            admin.email(), email1, email2, "byemail1@example.com", "byemail2@example.com");
+        .containsExactly(email1, email2, "byemail1@example.com", "byemail2@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
         .containsExactly(email3, email4, "byemail3@example.com", "byemail4@example.com");
     assertThat(info.pendingReviewers.get(REMOVED)).isNull();
@@ -521,7 +520,7 @@
     gApi.changes().id(changeId).reviewer("byemail3@example.com").remove();
     info = gApi.changes().id(changeId).get();
     assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
-        .containsExactly(admin.email(), email2, "byemail2@example.com");
+        .containsExactly(email2, "byemail2@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
         .containsExactly(email4, "byemail4@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -532,7 +531,7 @@
     gApi.changes().id(changeId).revision("current").review(in);
     info = gApi.changes().id(changeId).get();
     assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
-        .containsExactly(admin.email(), email1, email2, "byemail2@example.com");
+        .containsExactly(email1, email2, "byemail2@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
         .containsExactly(email4, "byemail4@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -543,7 +542,7 @@
     info = gApi.changes().id(changeId).get();
     assertThat(info.pendingReviewers).isEmpty();
     assertThat(toEmails.apply(info.reviewers.get(REVIEWER)))
-        .containsExactly(admin.email(), email1, email2, "byemail2@example.com");
+        .containsExactly(email1, email2, "byemail2@example.com");
     assertThat(toEmails.apply(info.reviewers.get(CC)))
         .containsExactly(email4, "byemail4@example.com");
     assertThat(info.reviewers.get(REMOVED)).isNull();
@@ -1898,15 +1897,34 @@
 
   @Test
   public void addReviewer() throws Exception {
+    testAddReviewerViaPostReview(
+        (changeId, reviewer) -> {
+          AddReviewerInput in = new AddReviewerInput();
+          in.reviewer = reviewer;
+          gApi.changes().id(changeId).addReviewer(in);
+        });
+  }
+
+  @Test
+  public void addReviewerViaPostReview() throws Exception {
+    testAddReviewerViaPostReview(
+        (changeId, reviewer) -> {
+          AddReviewerInput addReviewerInput = new AddReviewerInput();
+          addReviewerInput.reviewer = reviewer;
+          ReviewInput reviewInput = new ReviewInput();
+          reviewInput.reviewers = ImmutableList.of(addReviewerInput);
+          gApi.changes().id(changeId).current().review(reviewInput);
+        });
+  }
+
+  private void testAddReviewerViaPostReview(AddReviewerCaller addReviewer) throws Exception {
     TestTimeUtil.resetWithClockStep(1, SECONDS);
     PushOneCommit.Result r = createChange();
     ChangeResource rsrc = parseResource(r);
     String oldETag = rsrc.getETag();
     Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
 
-    AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email();
-    gApi.changes().id(r.getChangeId()).addReviewer(in);
+    addReviewer.call(r.getChangeId(), user.email());
 
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
@@ -1924,6 +1942,9 @@
     assertThat(reviewers).hasSize(1);
     assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
 
+    // Nobody was added as CC.
+    assertThat(c.reviewers.get(CC)).isNull();
+
     // Ensure ETag and lastUpdatedOn are updated.
     rsrc = parseResource(r);
     assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
@@ -1937,6 +1958,19 @@
   }
 
   @Test
+  public void postingMessageOnOwnChangeDoesntAddCallerAsReviewer() throws Exception {
+    PushOneCommit.Result r = createChange();
+
+    ReviewInput reviewInput = new ReviewInput();
+    reviewInput.message = "Foo Bar";
+    gApi.changes().id(r.getChangeId()).current().review(reviewInput);
+
+    ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+    assertThat(c.reviewers.get(REVIEWER)).isNull();
+    assertThat(c.reviewers.get(CC)).isNull();
+  }
+
+  @Test
   public void listReviewers() throws Exception {
     PushOneCommit.Result r = createChange();
     AddReviewerInput in = new AddReviewerInput();
@@ -3778,9 +3812,9 @@
     ReviewInput input = new ReviewInput().label("Code-Review", 3);
     gApi.changes().id(changeId).current().review(input);
 
-    Map<String, Short> votes =
-        gApi.changes().id(changeId).current().reviewer(admin.email()).votes();
-    assertThat(votes).isEmpty();
+    assertThrows(
+        ResourceNotFoundException.class,
+        () -> gApi.changes().id(changeId).current().reviewer(admin.email()));
   }
 
   @Test
@@ -4703,4 +4737,9 @@
     }
     throw new AssertionError("expected BadRequestException");
   }
+
+  @FunctionalInterface
+  private interface AddReviewerCaller {
+    void call(String changeId, String reviewer) throws RestApiException;
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
index ebed15c..216a2ec 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
@@ -138,6 +138,26 @@
   }
 
   @Test
+  public void validateCommentsInInput_commentCleanedUp() throws Exception {
+    EasyMock.replay(mockCommentValidator);
+
+    PushOneCommit.Result r = createChange();
+    assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+
+    // posting a comment which is empty after trim is a no-op, as the empty comment is dropped
+    // during comment cleanup
+    ReviewInput input = new ReviewInput();
+    CommentInput comment =
+        TestCommentHelper.populate(
+            new CommentInput(), r.getChange().currentFilePaths().get(0), " ");
+    comment.updated = new Timestamp(0);
+    input.comments = ImmutableMap.of(comment.path, ImmutableList.of(comment));
+    gApi.changes().id(r.getChangeId()).current().review(input);
+
+    assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+  }
+
+  @Test
   public void validateDrafts_draftOK() throws Exception {
     EasyMock.expect(
             mockCommentValidator.validateComments(
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 070b98c..df4b6d6 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -47,9 +47,11 @@
 import com.google.gerrit.extensions.api.changes.PublishChangeEditInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.client.ChangeEditDetailOption;
+import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ApprovalInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.ChangeInput;
 import com.google.gerrit.extensions.common.DiffInfo;
 import com.google.gerrit.extensions.common.EditInfo;
 import com.google.gerrit.extensions.common.FileInfo;
@@ -734,6 +736,27 @@
         .contains(String.format("change %s is abandoned", change._number));
   }
 
+  @Test
+  public void sha1sOfTwoChangesWithSameContentAfterEditDiffer() throws Exception {
+    ChangeInput changeInput = new ChangeInput();
+    changeInput.project = project.get();
+    changeInput.branch = "master";
+    changeInput.subject = "Empty change";
+    changeInput.status = ChangeStatus.NEW;
+
+    ChangeInfo info1 = gApi.changes().create(changeInput).get();
+    gApi.changes().id(info1._number).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
+    gApi.changes().id(info1._number).edit().publish(new PublishChangeEditInput());
+    info1 = gApi.changes().id(info1._number).get();
+
+    ChangeInfo info2 = gApi.changes().create(changeInput).get();
+    gApi.changes().id(info2._number).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
+    gApi.changes().id(info2._number).edit().publish(new PublishChangeEditInput());
+    info2 = gApi.changes().id(info2._number).get();
+
+    assertThat(info1.currentRevision).isNotEqualTo(info2.currentRevision);
+  }
+
   private void createArbitraryEditFor(String changeId) throws Exception {
     createEmptyEditFor(changeId);
     arbitrarilyModifyEditOf(changeId);
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 8a4d5ff..27cc241 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -2619,6 +2619,76 @@
             + "\n");
   }
 
+  @Test
+  public void cannotPushTheSameCommitTwiceForReviewToTheSameBranch() throws Exception {
+    testCannotPushTheSameCommitTwiceForReviewToTheSameBranch();
+  }
+
+  @Test
+  public void cannotPushTheSameCommitTwiceForReviewToTheSameBranchCreateNewChangeForAllNotInTarget()
+      throws Exception {
+    enableCreateNewChangeForAllNotInTarget();
+    testCannotPushTheSameCommitTwiceForReviewToTheSameBranch();
+  }
+
+  private void testCannotPushTheSameCommitTwiceForReviewToTheSameBranch() throws Exception {
+    setRequireChangeId(InheritableBoolean.FALSE);
+
+    // create a commit without Change-Id
+    testRepo
+        .branch("HEAD")
+        .commit()
+        .author(user.newIdent())
+        .committer(user.newIdent())
+        .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
+        .message(PushOneCommit.SUBJECT)
+        .create();
+
+    // push the commit for review to create a change
+    PushResult r = pushHead(testRepo, "refs/for/master");
+    assertPushOk(r, "refs/for/master");
+
+    // try to push the same commit for review again to create another change on the same branch,
+    // it's expected that this is rejected with "no new changes"
+    r = pushHead(testRepo, "refs/for/master");
+    assertPushRejected(r, "refs/for/master", "no new changes");
+  }
+
+  @Test
+  public void pushTheSameCommitTwiceForReviewToDifferentBranches() throws Exception {
+    setRequireChangeId(InheritableBoolean.FALSE);
+
+    // create a commit without Change-Id
+    testRepo
+        .branch("HEAD")
+        .commit()
+        .author(user.newIdent())
+        .committer(user.newIdent())
+        .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
+        .message(PushOneCommit.SUBJECT)
+        .create();
+
+    // push the commit for review to create a change
+    PushResult r = pushHead(testRepo, "refs/for/master");
+    assertPushOk(r, "refs/for/master");
+
+    // create another branch
+    gApi.projects().name(project.get()).branch("otherBranch").create(new BranchInput());
+
+    // try to push the same commit for review again to create a change on another branch,
+    // it's expected that this is rejected with "no new changes" since
+    // CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET is false
+    r = pushHead(testRepo, "refs/for/otherBranch");
+    assertPushRejected(r, "refs/for/otherBranch", "no new changes");
+
+    enableCreateNewChangeForAllNotInTarget();
+
+    // try to push the same commit for review again to create a change on another branch,
+    // now it should succeed since CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET is true
+    r = pushHead(testRepo, "refs/for/otherBranch");
+    assertPushOk(r, "refs/for/otherBranch");
+  }
+
   private DraftInput newDraft(String path, int line, String message) {
     DraftInput d = new DraftInput();
     d.path = path;
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index ac37530..af4a22e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -596,7 +596,7 @@
     try {
       RestResponse response = adminRestSession.post("/changes/" + changeId + "/submit");
       assertThat(response.getStatusCode()).isEqualTo(SC_OK);
-      assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+      assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).startsWith("retry-on-failure-");
       assertThat(traceSubmitRule.traceId).startsWith("retry-on-failure-");
       assertThat(traceSubmitRule.isLoggingForced).isTrue();
     } finally {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
index 63e9ebf..c4e8c31 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
@@ -23,13 +23,25 @@
 import java.util.List;
 
 public class AccountAssert {
-
-  public static void assertAccountInfo(TestAccount a, AccountInfo ai) {
-    assertThat(a.id().get()).isEqualTo(ai._accountId);
-    assertThat(a.fullName()).isEqualTo(ai.name);
-    assertThat(a.email()).isEqualTo(ai.email);
+  /**
+   * Asserts an AccountInfo for an active account.
+   *
+   * @param testAccount the TestAccount which the provided AccountInfo is expected to match
+   * @param accountInfo the AccountInfo that should be asserted
+   */
+  public static void assertAccountInfo(TestAccount testAccount, AccountInfo accountInfo) {
+    assertThat(accountInfo._accountId).isEqualTo(testAccount.id().get());
+    assertThat(accountInfo.name).isEqualTo(testAccount.fullName());
+    assertThat(accountInfo.email).isEqualTo(testAccount.email());
+    assertThat(accountInfo.inactive).isNull();
   }
 
+  /**
+   * Asserts an AccountInfos for active accounts.
+   *
+   * @param expected the TestAccounts which the provided AccountInfos are expected to match
+   * @param actual the AccountInfos that should be asserted
+   */
   public static void assertAccountInfos(List<TestAccount> expected, List<AccountInfo> actual) {
     Iterable<Account.Id> expectedIds = TestAccount.ids(expected);
     Iterable<Account.Id> actualIds = Iterables.transform(actual, a -> Account.id(a._accountId));
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
index 931dace..782638a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
@@ -14,17 +14,23 @@
 
 package com.google.gerrit.acceptance.rest.account;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfo;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.inject.Inject;
 import org.junit.Test;
 
 @NoHttpd
 public class GetAccountIT extends AbstractDaemonTest {
+  @Inject private AccountOperations accountOperations;
+
   @Test
   public void getNonExistingAccount_NotFound() throws Exception {
     assertThrows(ResourceNotFoundException.class, () -> gApi.accounts().id("non-existing").get());
@@ -51,6 +57,16 @@
     testGetAccount("self", admin);
   }
 
+  @Test
+  public void getInactiveAccount() throws Exception {
+    accountOperations.account(user.id()).forUpdate().inactive().update();
+    AccountInfo accountInfo = gApi.accounts().id(user.id().get()).get();
+    assertThat(accountInfo._accountId).isEqualTo(user.id().get());
+    assertThat(accountInfo.name).isEqualTo(user.fullName());
+    assertThat(accountInfo.email).isEqualTo(user.email());
+    assertThat(accountInfo.inactive).isTrue();
+  }
+
   private void testGetAccount(String id, TestAccount expectedAccount) throws Exception {
     assertAccountInfo(expectedAccount, gApi.accounts().id(id).get());
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
index cef599f..00dcb4f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
@@ -62,7 +62,8 @@
           RestCall.get("/config/server/capabilities"),
           RestCall.get("/config/server/caches"),
           RestCall.post("/config/server/caches"),
-          RestCall.get("/config/server/tasks"));
+          RestCall.get("/config/server/tasks"),
+          RestCall.post("/config/server/index.changes"));
 
   /**
    * Cache REST endpoints to be tested, the URLs contain a placeholder for the cache identifier.
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
index fec0d4b..39eb5cd 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
@@ -96,16 +96,32 @@
   }
 
   @Test
-  public void assigneeAddedAsReviewer() throws Exception {
-    ReviewerState state = ReviewerState.CC;
+  public void assigneeAddedAsCc() throws Exception {
     PushOneCommit.Result r = createChange();
-    Iterable<AccountInfo> reviewers = getReviewers(r, state);
+    Iterable<AccountInfo> reviewers = getReviewers(r, ReviewerState.CC);
     assertThat(reviewers).isNull();
+
     assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
-    reviewers = getReviewers(r, state);
+    reviewers = getReviewers(r, ReviewerState.CC);
     assertThat(reviewers).hasSize(1);
-    AccountInfo reviewer = Iterables.getFirst(reviewers, null);
-    assertThat(reviewer._accountId).isEqualTo(user.id().get());
+    assertThat(Iterables.getFirst(reviewers, null)._accountId).isEqualTo(user.id().get());
+    assertThat(getReviewers(r, ReviewerState.REVIEWER)).isNull();
+  }
+
+  @Test
+  public void assigneeStaysReviewer() throws Exception {
+    PushOneCommit.Result r = createChange();
+    gApi.changes().id(r.getChangeId()).addReviewer(user.email());
+    Iterable<AccountInfo> reviewers = getReviewers(r, ReviewerState.REVIEWER);
+    assertThat(reviewers).hasSize(1);
+    assertThat(Iterables.getFirst(reviewers, null)._accountId).isEqualTo(user.id().get());
+    assertThat(getReviewers(r, ReviewerState.CC)).isNull();
+
+    assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
+    reviewers = getReviewers(r, ReviewerState.REVIEWER);
+    assertThat(reviewers).hasSize(1);
+    assertThat(Iterables.getFirst(reviewers, null)._accountId).isEqualTo(user.id().get());
+    assertThat(getReviewers(r, ReviewerState.CC)).isNull();
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index 96b96eb..b620bf7 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -337,20 +337,18 @@
     assertThat(result.reviewers).isNotNull();
     assertThat(result.reviewers).hasSize(1);
 
-    // Verify reviewer state. Both admin and user should be REVIEWERs now,
-    // because admin gets forced into REVIEWER state by virtue of being owner.
+    // Verify reviewer state.
     c = gApi.changes().id(r.getChangeId()).get();
-    assertReviewers(c, REVIEWER, admin, user);
+    assertReviewers(c, REVIEWER, user);
     assertReviewers(c, CC);
     label = c.labels.get("Code-Review");
     assertThat(label).isNotNull();
     assertThat(label.all).isNotNull();
-    assertThat(label.all).hasSize(2);
+    assertThat(label.all).hasSize(1);
     Map<Integer, Integer> approvals = new HashMap<>();
     for (ApprovalInfo approval : label.all) {
       approvals.put(approval._accountId, approval.value);
     }
-    assertThat(approvals).containsEntry(admin.id().get(), 0);
     assertThat(approvals).containsEntry(user.id().get(), 0);
 
     // Comment as user without voting. This should delete the approval and
@@ -365,17 +363,16 @@
 
     // Verify reviewer state.
     c = gApi.changes().id(r.getChangeId()).get();
-    assertReviewers(c, REVIEWER, admin, user);
+    assertReviewers(c, REVIEWER, user);
     assertReviewers(c, CC);
     label = c.labels.get("Code-Review");
     assertThat(label).isNotNull();
     assertThat(label.all).isNotNull();
-    assertThat(label.all).hasSize(2);
+    assertThat(label.all).hasSize(1);
     approvals.clear();
     for (ApprovalInfo approval : label.all) {
       approvals.put(approval._accountId, approval.value);
     }
-    assertThat(approvals).containsEntry(admin.id().get(), 0);
     assertThat(approvals).containsEntry(user.id().get(), 0);
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index b5f3d06..54a50ce 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -40,6 +40,7 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.common.ChangeInput;
 import com.google.gerrit.extensions.common.MergeInput;
+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.ResourceNotFoundException;
@@ -125,6 +126,13 @@
   }
 
   @Test
+  public void createNewChange_RequiresAuthentication() throws Exception {
+    requestScopeOperations.setApiUserAnonymous();
+    assertCreateFails(
+        newChangeInput(ChangeStatus.NEW), AuthException.class, "Authentication required");
+  }
+
+  @Test
   public void createNewChange() throws Exception {
     ChangeInfo info = assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
     assertThat(info.revisions.get(info.currentRevision).commit.message)
@@ -436,6 +444,14 @@
     assertCreateFails(input, ResourceNotFoundException.class, "ref refs/heads/foo not found");
   }
 
+  @Test
+  public void sha1sOfTwoNewChangesDiffer() throws Exception {
+    ChangeInput changeInput = newChangeInput(ChangeStatus.NEW);
+    ChangeInfo info1 = assertCreateSucceeds(changeInput);
+    ChangeInfo info2 = assertCreateSucceeds(changeInput);
+    assertThat(info1.currentRevision).isNotEqualTo(info2.currentRevision);
+  }
+
   private ChangeInput newChangeInput(ChangeStatus status) {
     ChangeInput in = new ChangeInput();
     in.project = project.get();
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
new file mode 100644
index 0000000..7590532
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
@@ -0,0 +1,107 @@
+// 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.acceptance.rest.config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ChangeIndexedCounter;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.events.ChangeIndexedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.restapi.config.IndexChanges;
+import com.google.inject.Inject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class IndexChangesIT extends AbstractDaemonTest {
+
+  @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
+  @Inject private ProjectOperations projectOperations;
+
+  private ChangeIndexedCounter changeIndexedCounter;
+  private RegistrationHandle changeIndexedCounterHandle;
+
+  @Before
+  public void addChangeIndexedCounter() {
+    changeIndexedCounter = new ChangeIndexedCounter();
+    changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter);
+  }
+
+  @After
+  public void removeChangeIndexedCounter() {
+    if (changeIndexedCounterHandle != null) {
+      changeIndexedCounterHandle.remove();
+    }
+  }
+
+  @Test
+  public void indexRequestFromNonAdminRejected() throws Exception {
+    String changeId = createChange().getChangeId();
+    IndexChanges.Input in = new IndexChanges.Input();
+    in.changes = ImmutableSet.of(changeId);
+    changeIndexedCounter.clear();
+    userRestSession.post("/config/server/index.changes", in).assertForbidden();
+    assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(0);
+  }
+
+  @Test
+  public void indexVisibleChange() throws Exception {
+    String changeId = createChange().getChangeId();
+    IndexChanges.Input in = new IndexChanges.Input();
+    in.changes = ImmutableSet.of(changeId);
+    changeIndexedCounter.clear();
+    adminRestSession.post("/config/server/index.changes", in).assertOK();
+    assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+  }
+
+  @Test
+  public void indexNonVisibleChange() throws Exception {
+    String changeId = createChange().getChangeId();
+    ChangeInfo changeInfo = info(changeId);
+    projectOperations
+        .project(project)
+        .forUpdate()
+        .add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+        .update();
+    IndexChanges.Input in = new IndexChanges.Input();
+    changeIndexedCounter.clear();
+    in.changes = ImmutableSet.of(changeId);
+    adminRestSession.post("/config/server/index.changes", in).assertOK();
+    assertThat(changeIndexedCounter.getCount(changeInfo)).isEqualTo(1);
+  }
+
+  @Test
+  public void indexMultipleChanges() throws Exception {
+    ImmutableSet.Builder<String> changeIds = ImmutableSet.builder();
+    for (int i = 0; i < 10; i++) {
+      changeIds.add(createChange().getChangeId());
+    }
+    IndexChanges.Input in = new IndexChanges.Input();
+    in.changes = changeIds.build();
+    changeIndexedCounter.clear();
+    adminRestSession.post("/config/server/index.changes", in).assertOK();
+    for (String changeId : in.changes) {
+      assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+    }
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
index f80f86b..c91bfcb 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -181,7 +181,7 @@
     revision(r).review(input);
     ChangeInfo c = getWithLabels(r);
     LabelInfo q = c.labels.get(P.getName());
-    assertThat(q.all).hasSize(2);
+    assertThat(q.all).hasSize(1);
     assertThat(q.approved).isNull();
     assertThat(q.recommended).isNull();
     assertThat(q.disliked).isNull();
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
index d214ae0..27ac839 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -59,7 +59,7 @@
       case V7_2:
         return "blacktop/elasticsearch:7.2.1";
       case V7_3:
-        return "blacktop/elasticsearch:7.3.1";
+        return "blacktop/elasticsearch:7.3.2";
     }
     throw new IllegalStateException("No tests for version: " + version.name());
   }
diff --git a/javatests/com/google/gerrit/server/account/AccountResolverTest.java b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
index 14e6876..e9e5e54 100644
--- a/javatests/com/google/gerrit/server/account/AccountResolverTest.java
+++ b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
@@ -161,6 +161,23 @@
   }
 
   @Test
+  public void shouldUseCustomAccountActivityPredicate() throws Exception {
+    TestSearcher searcher1 = new TestSearcher("foo", false, newInactiveAccount(1));
+    searcher1.setCallerShouldFilterOutInactiveCandidates();
+    TestSearcher searcher2 = new TestSearcher("f.*", false, newInactiveAccount(2));
+    searcher2.setCallerShouldFilterOutInactiveCandidates();
+    ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher1, searcher2);
+
+    Result result = search("foo", searchers, allVisible(), (a) -> true);
+    // Searchers always short-circuit when finding a non-empty result list,
+    // and this one didn't filter out inactive results,
+    // so the second searcher never ran.
+    assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1));
+    assertThat(getOnlyElement(result.asList()).getAccount().isActive()).isFalse();
+    assertThat(filteredInactiveIds(result)).isEmpty();
+  }
+
+  @Test
   public void filterInactiveEventuallyFindingResults() throws Exception {
     TestSearcher searcher1 = new TestSearcher("foo", false, newInactiveAccount(1));
     searcher1.setCallerShouldFilterOutInactiveCandidates();
@@ -320,7 +337,16 @@
       ImmutableList<Searcher<?>> searchers,
       Supplier<Predicate<AccountState>> visibilitySupplier)
       throws Exception {
-    return newAccountResolver().searchImpl(input, searchers, visibilitySupplier);
+    return search(input, searchers, visibilitySupplier, activityPrediate());
+  }
+
+  private Result search(
+      String input,
+      ImmutableList<Searcher<?>> searchers,
+      Supplier<Predicate<AccountState>> visibilitySupplier,
+      Predicate<AccountState> activityPredicate)
+      throws Exception {
+    return newAccountResolver().searchImpl(input, searchers, visibilitySupplier, activityPredicate);
   }
 
   private static AccountResolver newAccountResolver() {
@@ -348,6 +374,10 @@
     return () -> a -> true;
   }
 
+  private Predicate<AccountState> activityPrediate() {
+    return (AccountState accountState) -> accountState.getAccount().isActive();
+  }
+
   private static Supplier<Predicate<AccountState>> only(int... ids) {
     ImmutableSet<Account.Id> idSet =
         Arrays.stream(ids).mapToObj(Account::id).collect(toImmutableSet());
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 4d7ba94..41230f1 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -575,8 +575,7 @@
         changeInfos.get(0).pendingReviewers;
     assertThat(pendingReviewers).isNotNull();
 
-    assertReviewers(
-        pendingReviewers.get(ReviewerState.REVIEWER), userId.toString(), user1.toString(), email1);
+    assertReviewers(pendingReviewers.get(ReviewerState.REVIEWER), user1.toString(), email1);
     assertReviewers(pendingReviewers.get(ReviewerState.CC), user2.toString(), email2);
     assertReviewers(pendingReviewers.get(ReviewerState.REMOVED));
 
diff --git a/lib/BUILD b/lib/BUILD
index ab2bad9..c30d9d9 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -419,13 +419,6 @@
 )
 
 java_library(
-    name = "javassist",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = ["@javassist//jar"],
-)
-
-java_library(
     name = "soy",
     data = ["//lib:LICENSE-Apache2.0"],
     visibility = ["//visibility:public"],
@@ -462,6 +455,18 @@
     exports = ["@icu4j//jar"],
 )
 
+java_library(
+    name = "javax-annotation",
+    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
+    visibility = [
+        "//java/com/google/gerrit/acceptance:__pkg__",
+        "//java/com/google/gerrit/extensions:__pkg__",
+        "//java/com/google/gerrit/server:__pkg__",
+        "//plugins:__pkg__",
+    ],
+    exports = ["@javax-annotation//jar"],
+)
+
 sh_test(
     name = "nongoogle_test",
     srcs = ["nongoogle_test.sh"],
diff --git a/lib/LICENSE-clippy b/lib/LICENSE-clippy
deleted file mode 100644
index b0feeae..0000000
--- a/lib/LICENSE-clippy
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2008 Tom Preston-Werner
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/LICENSE-diffy b/lib/LICENSE-diffy
deleted file mode 100644
index e837a1a..0000000
--- a/lib/LICENSE-diffy
+++ /dev/null
@@ -1 +0,0 @@
-link:http://creativecommons.org/licenses/by/3.0/[CC-BY 3.0] (c) Sara Owens, inkylabs.com
diff --git a/lib/LICENSE-mockito b/lib/LICENSE-mockito
new file mode 100644
index 0000000..5a311f7c
--- /dev/null
+++ b/lib/LICENSE-mockito
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2007 Mockito contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lib/LICENSE-silk_icons b/lib/LICENSE-silk_icons
deleted file mode 100644
index b7417f6..0000000
--- a/lib/LICENSE-silk_icons
+++ /dev/null
@@ -1,338 +0,0 @@
-link:http://creativecommons.org/licenses/by/3.0/[CC-BY 3.0] (c) Mark James, link:http://famfamfam.com/lab/icons/silk/[SILK ICONS]
-
-As an author, I would appreciate a reference to my authorship of the Silk
-icon set contents within a readme file or equivalent documentation for
-the software which includes the set or a subset of the icons contained
-within.
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
-CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE").  THE WORK IS
-PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  ANY USE OF THE
-WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
-PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
-AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.  TO THE EXTENT THIS
-LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU
-THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH
-TERMS AND CONDITIONS.
-
-1.  Definitions
-
-  a.  "Adaptation" means a work based upon the Work, or upon the Work
-      and other pre-existing works, such as a translation, adaptation,
-      derivative work, arrangement of music or other alterations of a
-      literary or artistic work, or phonogram or performance and
-      includes cinematographic adaptations or any other form in which
-      the Work may be recast, transformed, or adapted including in any
-      form recognizably derived from the original, except that a work
-      that constitutes a Collection will not be considered an
-      Adaptation for the purpose of this License.  For the avoidance
-      of doubt, where the Work is a musical work, performance or
-      phonogram, the synchronization of the Work in timed-relation
-      with a moving image ("synching") will be considered an
-      Adaptation for the purpose of this License.
-
-  b.  "Collection" means a collection of literary or artistic works,
-      such as encyclopedias and anthologies, or performances,
-      phonograms or broadcasts, or other works or subject matter other
-      than works listed in Section 1(f) below, which, by reason of the
-      selection and arrangement of their contents, constitute
-      intellectual creations, in which the Work is included in its
-      entirety in unmodified form along with one or more other
-      contributions, each constituting separate and independent works
-      in themselves, which together are assembled into a collective
-      whole.  A work that constitutes a Collection will not be
-      considered an Adaptation (as defined above) for the purposes of
-      this License.
-
-  c.  "Distribute" means to make available to the public the original
-      and copies of the Work or Adaptation, as appropriate, through
-      sale or other transfer of ownership.
-
-  d.  "Licensor" means the individual, individuals, entity or entities
-      that offer(s) the Work under the terms of this License.
-
-  e.  "Original Author" means, in the case of a literary or artistic
-      work, the individual, individuals, entity or entities who
-      created the Work or if no individual or entity can be
-      identified, the publisher; and in addition (i) in the case of a
-      performance the actors, singers, musicians, dancers, and other
-      persons who act, sing, deliver, declaim, play in, interpret or
-      otherwise perform literary or artistic works or expressions of
-      folklore; (ii) in the case of a phonogram the producer being the
-      person or legal entity who first fixes the sounds of a
-      performance or other sounds; and, (iii) in the case of
-      broadcasts, the organization that transmits the broadcast.
-
-  f.  "Work" means the literary and/or artistic work offered under the
-      terms of this License including without limitation any
-      production in the literary, scientific and artistic domain,
-      whatever may be the mode or form of its expression including
-      digital form, such as a book, pamphlet and other writing; a
-      lecture, address, sermon or other work of the same nature; a
-      dramatic or dramatico-musical work; a choreographic work or
-      entertainment in dumb show; a musical composition with or
-      without words; a cinematographic work to which are assimilated
-      works expressed by a process analogous to cinematography; a work
-      of drawing, painting, architecture, sculpture, engraving or
-      lithography; a photographic work to which are assimilated works
-      expressed by a process analogous to photography; a work of
-      applied art; an illustration, map, plan, sketch or
-      three-dimensional work relative to geography, topography,
-      architecture or science; a performance; a broadcast; a
-      phonogram; a compilation of data to the extent it is protected
-      as a copyrightable work; or a work performed by a variety or
-      circus performer to the extent it is not otherwise considered a
-      literary or artistic work.
-
-  g.  "You" means an individual or entity exercising rights under this
-      License who has not previously violated the terms of this
-      License with respect to the Work, or who has received express
-      permission from the Licensor to exercise rights under this
-      License despite a previous violation.
-
-  h.  "Publicly Perform" means to perform public recitations of the
-      Work and to communicate to the public those public recitations,
-      by any means or process, including by wire or wireless means or
-      public digital performances; to make available to the public
-      Works in such a way that members of the public may access these
-      Works from a place and at a place individually chosen by them;
-      to perform the Work to the public by any means or process and
-      the communication to the public of the performances of the Work,
-      including by public digital performance; to broadcast and
-      rebroadcast the Work by any means including signs, sounds or
-      images.
-
-  i.  "Reproduce" means to make copies of the Work by any means
-      including without limitation by sound or visual recordings and
-      the right of fixation and reproducing fixations of the Work,
-      including storage of a protected performance or phonogram in
-      digital form or other electronic medium.
-
-2.  Fair Dealing Rights.  Nothing in this License is intended to
-    reduce, limit, or restrict any uses free from copyright or rights
-    arising from limitations or exceptions that are provided for in
-    connection with the copyright protection under copyright law or
-    other applicable laws.
-
-3.  License Grant.  Subject to the terms and conditions of this
-    License, Licensor hereby grants You a worldwide, royalty-free,
-    non-exclusive, perpetual (for the duration of the applicable
-    copyright) license to exercise the rights in the Work as stated
-    below:
-
-  a.  to Reproduce the Work, to incorporate the Work into one or more
-      Collections, and to Reproduce the Work as incorporated in the
-      Collections;
-
-  b.  to create and Reproduce Adaptations provided that any such
-      Adaptation, including any translation in any medium, takes
-      reasonable steps to clearly label, demarcate or otherwise
-      identify that changes were made to the original Work.  For
-      example, a translation could be marked "The original work was
-      translated from English to Spanish," or a modification could
-      indicate "The original work has been modified.";
-
-  c.  to Distribute and Publicly Perform the Work including as
-      incorporated in Collections; and,
-
-  d.  to Distribute and Publicly Perform Adaptations.
-
-  e.  For the avoidance of doubt:
-
-    i.   Non-waivable Compulsory License Schemes.  In those
-	     jurisdictions in which the right to collect royalties
-	     through any statutory or compulsory licensing scheme
-	     cannot be waived, the Licensor reserves the exclusive
-	     right to collect such royalties for any exercise by You
-	     of the rights granted under this License;
-
-    ii.  Waivable Compulsory License Schemes.  In those jurisdictions
-	     in which the right to collect royalties through any
-	     statutory or compulsory licensing scheme can be waived,
-	     the Licensor waives the exclusive right to collect such
-	     royalties for any exercise by You of the rights granted
-	     under this License; and,
-
-    iii. Voluntary License Schemes.  The Licensor waives the right to
-	     collect royalties, whether individually or, in the event
-	     that the Licensor is a member of a collecting society
-	     that administers voluntary licensing schemes, via that
-	     society, from any exercise by You of the rights granted
-	     under this License.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised.  The above rights include the right to
-make such modifications as are technically necessary to exercise the
-rights in other media and formats.  Subject to Section 8(f), all
-rights not expressly granted by Licensor are hereby reserved.
-
-4.  Restrictions.  The license granted in Section 3 above is expressly
-    made subject to and limited by the following restrictions:
-
-  a.  You may Distribute or Publicly Perform the Work only under the
-      terms of this License.  You must include a copy of, or the
-      Uniform Resource Identifier (URI) for, this License with every
-      copy of the Work You Distribute or Publicly Perform.  You may
-      not offer or impose any terms on the Work that restrict the
-      terms of this License or the ability of the recipient of the
-      Work to exercise the rights granted to that recipient under the
-      terms of the License.  You may not sublicense the Work.  You
-      must keep intact all notices that refer to this License and to
-      the disclaimer of warranties with every copy of the Work You
-      Distribute or Publicly Perform.  When You Distribute or Publicly
-      Perform the Work, You may not impose any effective technological
-      measures on the Work that restrict the ability of a recipient of
-      the Work from You to exercise the rights granted to that
-      recipient under the terms of the License.  This Section 4(a)
-      applies to the Work as incorporated in a Collection, but this
-      does not require the Collection apart from the Work itself to be
-      made subject to the terms of this License.  If You create a
-      Collection, upon notice from any Licensor You must, to the
-      extent practicable, remove from the Collection any credit as
-      required by Section 4(b), as requested.  If You create an
-      Adaptation, upon notice from any Licensor You must, to the
-      extent practicable, remove from the Adaptation any credit as
-      required by Section 4(b), as requested.
-
-  b.  If You Distribute, or Publicly Perform the Work or any
-      Adaptations or Collections, You must, unless a request has been
-      made pursuant to Section 4(a), keep intact all copyright notices
-      for the Work and provide, reasonable to the medium or means You
-      are utilizing: (i) the name of the Original Author (or
-      pseudonym, if applicable) if supplied, and/or if the Original
-      Author and/or Licensor designate another party or parties (e.g.,
-      a sponsor institute, publishing entity, journal) for attribution
-      ("Attribution Parties") in Licensor's copyright notice, terms of
-      service or by other reasonable means, the name of such party or
-      parties; (ii) the title of the Work if supplied; (iii) to the
-      extent reasonably practicable, the URI, if any, that Licensor
-      specifies to be associated with the Work, unless such URI does
-      not refer to the copyright notice or licensing information for
-      the Work; and (iv) , consistent with Section 3(b), in the case
-      of an Adaptation, a credit identifying the use of the Work in
-      the Adaptation (e.g., "French translation of the Work by
-      Original Author," or "Screenplay based on original Work by
-      Original Author").  The credit required by this Section 4 (b)
-      may be implemented in any reasonable manner; provided, however,
-      that in the case of a Adaptation or Collection, at a minimum
-      such credit will appear, if a credit for all contributing
-      authors of the Adaptation or Collection appears, then as part of
-      these credits and in a manner at least as prominent as the
-      credits for the other contributing authors.  For the avoidance
-      of doubt, You may only use the credit required by this Section
-      for the purpose of attribution in the manner set out above and,
-      by exercising Your rights under this License, You may not
-      implicitly or explicitly assert or imply any connection with,
-      sponsorship or endorsement by the Original Author, Licensor
-      and/or Attribution Parties, as appropriate, of You or Your use
-      of the Work, without the separate, express prior written
-      permission of the Original Author, Licensor and/or Attribution
-      Parties.
-
-  c.  Except as otherwise agreed in writing by the Licensor or as may
-      be otherwise permitted by applicable law, if You Reproduce,
-      Distribute or Publicly Perform the Work either by itself or as
-      part of any Adaptations or Collections, You must not distort,
-      mutilate, modify or take other derogatory action in relation to
-      the Work which would be prejudicial to the Original Author's
-      honor or reputation.  Licensor agrees that in those
-      jurisdictions (e.g.  Japan), in which any exercise of the right
-      granted in Section 3(b) of this License (the right to make
-      Adaptations) would be deemed to be a distortion, mutilation,
-      modification or other derogatory action prejudicial to the
-      Original Author's honor and reputation, the Licensor will waive
-      or not assert, as appropriate, this Section, to the fullest
-      extent permitted by the applicable national law, to enable You
-      to reasonably exercise Your right under Section 3(b) of this
-      License (right to make Adaptations) but not otherwise.
-
-5.  Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING,
-LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR
-WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED,
-STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF
-TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
-NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY,
-OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
-SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES,
-SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6.  Limitation on Liability.  EXCEPT TO THE EXTENT REQUIRED BY
-    APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
-    LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
-    OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE
-    WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-    DAMAGES.
-
-7.  Termination
-
-  a.  This License and the rights granted hereunder will terminate
-      automatically upon any breach by You of the terms of this
-      License.  Individuals or entities who have received Adaptations
-      or Collections from You under this License, however, will not
-      have their licenses terminated provided such individuals or
-      entities remain in full compliance with those licenses.
-      Sections 1, 2, 5, 6, 7, and 8 will survive any termination of
-      this License.
-
-  b.  Subject to the above terms and conditions, the license granted
-      here is perpetual (for the duration of the applicable copyright
-      in the Work).  Notwithstanding the above, Licensor reserves the
-      right to release the Work under different license terms or to
-      stop distributing the Work at any time; provided, however that
-      any such election will not serve to withdraw this License (or
-      any other license that has been, or is required to be, granted
-      under the terms of this License), and this License will continue
-      in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
-  a.  Each time You Distribute or Publicly Perform the Work or a
-      Collection, the Licensor offers to the recipient a license to
-      the Work on the same terms and conditions as the license granted
-      to You under this License.
-
-  b.  Each time You Distribute or Publicly Perform an Adaptation,
-      Licensor offers to the recipient a license to the original Work
-      on the same terms and conditions as the license granted to You
-      under this License.
-
-  c.  If any provision of this License is invalid or unenforceable
-      under applicable law, it shall not affect the validity or
-      enforceability of the remainder of the terms of this License,
-      and without further action by the parties to this agreement,
-      such provision shall be reformed to the minimum extent necessary
-      to make such provision valid and enforceable.
-
-  d.  No term or provision of this License shall be deemed waived and
-      no breach consented to unless such waiver or consent shall be in
-      writing and signed by the party to be charged with such waiver
-      or consent.
-
-  e.  This License constitutes the entire agreement between the
-      parties with respect to the Work licensed here.  There are no
-      understandings, agreements or representations with respect to
-      the Work not specified here.  Licensor shall not be bound by any
-      additional provisions that may appear in any communication from
-      You.  This License may not be modified without the mutual
-      written agreement of the Licensor and You.
-
-  f.  The rights granted under, and the subject matter referenced, in
-      this License were drafted utilizing the terminology of the Berne
-      Convention for the Protection of Literary and Artistic Works (as
-      amended on September 28, 1979), the Rome Convention of 1961, the
-      WIPO Copyright Treaty of 1996, the WIPO Performances and
-      Phonograms Treaty of 1996 and the Universal Copyright Convention
-      (as revised on July 24, 1971).  These rights and subject matter
-      take effect in the relevant jurisdiction in which the License
-      terms are sought to be enforced according to the corresponding
-      provisions of the implementation of those treaty provisions in
-      the applicable national law.  If the standard suite of rights
-      granted under applicable copyright law includes additional
-      rights not granted under this License, such additional rights
-      are deemed to be included in the License; this License is not
-      intended to restrict the license of any rights under applicable
-      law.
diff --git a/lib/mockito/BUILD b/lib/mockito/BUILD
index 7af9669..2aaf56d 100644
--- a/lib/mockito/BUILD
+++ b/lib/mockito/BUILD
@@ -7,7 +7,7 @@
 
 java_library(
     name = "mockito",
-    data = ["//lib:LICENSE-Apache2.0"],
+    data = ["//lib:LICENSE-mockito"],
     visibility = ["//visibility:public"],
     exports = ["@mockito//jar"],
     runtime_deps = [
diff --git a/lib/powermock/BUILD b/lib/powermock/BUILD
deleted file mode 100644
index 39df164..0000000
--- a/lib/powermock/BUILD
+++ /dev/null
@@ -1,69 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-java_library(
-    name = "powermock-module-junit4",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = [
-        ":powermock-module-junit4-common",
-        "//lib:junit",
-        "@powermock-module-junit4//jar",
-    ],
-)
-
-java_library(
-    name = "powermock-module-junit4-common",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = [
-        ":powermock-reflect",
-        "//lib:junit",
-        "@powermock-module-junit4-common//jar",
-    ],
-)
-
-java_library(
-    name = "powermock-reflect",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = [
-        "//lib:junit",
-        "//lib/easymock:objenesis",
-        "@powermock-reflect//jar",
-    ],
-)
-
-java_library(
-    name = "powermock-api-easymock",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = [
-        ":powermock-api-support",
-        "//lib/easymock",
-        "@powermock-api-easymock//jar",
-    ],
-)
-
-java_library(
-    name = "powermock-api-support",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = [
-        ":powermock-core",
-        ":powermock-reflect",
-        "//lib:junit",
-        "@powermock-api-support//jar",
-    ],
-)
-
-java_library(
-    name = "powermock-core",
-    data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
-    visibility = ["//visibility:public"],
-    exports = [
-        ":powermock-reflect",
-        "//lib:javassist",
-        "//lib:junit",
-        "@powermock-core//jar",
-    ],
-)
diff --git a/plugins/replication b/plugins/replication
index 485027c..c65db46 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 485027c2d63dda7b588fa5c07f91d2a65db4d787
+Subproject commit c65db46ef4114df13accacd20fea61788eec01f1
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index db53979..731af7e 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit db5397931ed05e9aedd385d3ec1511b4a64d3ddc
+Subproject commit 731af7e23c5a235c5ce087aaeb44dc8a12070bcb
diff --git a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
index 499970e..0c75c44 100644
--- a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
@@ -52,9 +52,9 @@
             id: 'delete',
             name: 'Delete Reference',
           },
-          deleteDrafts: {
-            id: 'deleteDrafts',
-            name: 'Delete Drafts',
+          deleteChanges: {
+            id: 'deleteChanges',
+            name: 'Delete Changes',
           },
           deleteOwnChanges: {
             id: 'deleteOwnChanges',
@@ -124,10 +124,6 @@
             id: 'toggleWipState',
             name: 'Toggle Work In Progress State',
           },
-          viewDrafts: {
-            id: 'viewDrafts',
-            name: 'View Drafts',
-          },
           viewPrivateChanges: {
             id: 'viewPrivateChanges',
             name: 'View Private Changes',
diff --git a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior.html
similarity index 60%
rename from polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html
rename to polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior.html
index 40379e4..3106fc8 100644
--- a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior.html
@@ -15,33 +15,28 @@
 limitations under the License.
 -->
 
+<script src="../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
+
 <script>
 (function(window) {
   'use strict';
 
-  const ANONYMOUS_NAME = 'Anonymous';
-
   window.Gerrit = window.Gerrit || {};
 
-  /** @polymerBehavior Gerrit.AnonymousNameBehavior */
-  Gerrit.AnonymousNameBehavior = {
+  /** @polymerBehavior Gerrit.DisplayNameBehavior */
+  Gerrit.DisplayNameBehavior = {
+    // TODO(dmfilippov) replace DisplayNameBehavior with GrDisplayNameUtils
+
     /**
      * enableEmail when true enables to fallback to using email if
      * the account name is not avilable.
      */
     getUserName(config, account, enableEmail) {
-      if (account && account.name) {
-        return account.name;
-      } else if (account && account.username) {
-        return account.username;
-      } else if (enableEmail && account && account.email) {
-        return account.email;
-      } else if (config && config.user &&
-          config.user.anonymous_coward_name !== 'Anonymous Coward') {
-        return config.user.anonymous_coward_name;
-      }
+      return GrDisplayNameUtils.getUserName(config, account, enableEmail);
+    },
 
-      return ANONYMOUS_NAME;
+    getGroupDisplayName(group) {
+      return GrDisplayNameUtils.getGroupDisplayName(group);
     },
   };
 })(window);
diff --git a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
similarity index 79%
rename from polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
rename to polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
index 64f0b3a..4c5c899 100644
--- a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
@@ -17,14 +17,14 @@
 -->
 
 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-anonymous-name-behavior</title>
+<title>gr-display-name-behavior</title>
 <script src="/test/common-test-setup.js"></script>
 <script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
 
 <script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
 <script src="/bower_components/web-component-tester/browser.js"></script>
 <link rel="import" href="../../test/common-test-setup.html"/>
-<link rel="import" href="gr-anonymous-name-behavior.html">
+<link rel="import" href="gr-display-name-behavior.html">
 
 <test-fixture id="basic">
   <template>
@@ -33,7 +33,7 @@
 </test-fixture>
 
 <script>
-  suite('gr-anonymous-name-behavior tests', () => {
+  suite('gr-display-name-behavior tests', () => {
     let element;
     // eslint-disable-next-line no-unused-vars
     const config = {
@@ -48,7 +48,7 @@
         is: 'test-element-anon',
         _legacyUndefinedCheck: true,
         behaviors: [
-          Gerrit.AnonymousNameBehavior,
+          Gerrit.DisplayNameBehavior,
         ],
       });
     });
@@ -57,21 +57,21 @@
       element = fixture('basic');
     });
 
-    test('test for it to return name', () => {
+    test('getUserName name only', () => {
       const account = {
         name: 'test-name',
       };
       assert.deepEqual(element.getUserName(config, account, true), 'test-name');
     });
 
-    test('test for it to return username', () => {
+    test('getUserName username only', () => {
       const account = {
         username: 'test-user',
       };
       assert.deepEqual(element.getUserName(config, account, true), 'test-user');
     });
 
-    test('test for it to return email', () => {
+    test('getUserName email only', () => {
       const account = {
         email: 'test-user@test-url.com',
       };
@@ -79,11 +79,11 @@
           'test-user@test-url.com');
     });
 
-    test('test for it not to Anonymous Coward as the anon name', () => {
+    test('getUserName returns not Anonymous Coward as the anon name', () => {
       assert.deepEqual(element.getUserName(config, null, true), 'Anonymous');
     });
 
-    test('test for the config returning the anon name', () => {
+    test('getUserName for the config returning the anon name', () => {
       const config = {
         user: {
           anonymous_coward_name: 'Test Anon',
@@ -91,5 +91,10 @@
       };
       assert.deepEqual(element.getUserName(config, null, true), 'Test Anon');
     });
+
+    test('getGroupDisplayName', () => {
+      assert.equal(element.getGroupDisplayName({name: 'Some user name'}),
+          'Some user name (group)');
+    });
   });
 </script>
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
deleted file mode 100644
index 1cb1ca5..0000000
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
+++ /dev/null
@@ -1,186 +0,0 @@
-/**
- * @license
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-(function() {
-  'use strict';
-
-  Polymer({
-    is: 'gr-account-entry',
-    _legacyUndefinedCheck: true,
-
-    /**
-     * Fired when an account is entered.
-     *
-     * @event add
-     */
-
-    /**
-     * When allowAnyInput is true, account-text-changed is fired when input text
-     * changed. This is needed so that the reply dialog's save button can be
-     * enabled for arbitrary cc's, which don't need a 'commit'.
-     *
-     * @event account-text-changed
-     */
-    properties: {
-      allowAnyInput: Boolean,
-      borderless: Boolean,
-      change: Object,
-      filter: Function,
-      placeholder: String,
-      /**
-       * When true, account-entry uses the account suggest API endpoint, which
-       * suggests any account in that Gerrit instance (and does not suggest
-       * groups).
-       *
-       * When false/undefined, account-entry uses the suggest_reviewers API
-       * endpoint, which suggests any account or group in that Gerrit instance
-       * that is not already a reviewer (or is not CCed) on that change.
-       */
-      allowAnyUser: Boolean,
-
-      // suggestFrom = 0 to enable default suggestions.
-      suggestFrom: {
-        type: Number,
-        value: 0,
-      },
-
-      query: {
-        type: Function,
-        value() {
-          return this._getReviewerSuggestions.bind(this);
-        },
-      },
-
-      _config: Object,
-      /** The value of the autocomplete entry. */
-      _inputText: {
-        type: String,
-        observer: '_inputTextChanged',
-      },
-
-      _loggedIn: Boolean,
-    },
-
-    behaviors: [
-      Gerrit.AnonymousNameBehavior,
-      Gerrit.FireBehavior,
-    ],
-
-    attached() {
-      this.$.restAPI.getConfig().then(cfg => {
-        this._config = cfg;
-      });
-      this.$.restAPI.getLoggedIn().then(loggedIn => {
-        this._loggedIn = loggedIn;
-      });
-    },
-
-    get focusStart() {
-      return this.$.input.focusStart;
-    },
-
-    focus() {
-      this.$.input.focus();
-    },
-
-    clear() {
-      this.$.input.clear();
-    },
-
-    setText(text) {
-      this.$.input.setText(text);
-    },
-
-    getText() {
-      return this.$.input.text;
-    },
-
-    _handleInputCommit(e) {
-      this.fire('add', {value: e.detail.value});
-      this.$.input.focus();
-    },
-
-    _accountOrAnon(reviewer) {
-      return this.getUserName(this._config, reviewer, false);
-    },
-
-    _inputTextChanged(text) {
-      if (text.length && this.allowAnyInput) {
-        this.dispatchEvent(new CustomEvent(
-            'account-text-changed', {bubbles: true, composed: true}));
-      }
-    },
-
-    _makeSuggestion(reviewer) {
-      let name;
-      let value;
-      const generateStatusStr = function(account) {
-        return account.status ? '(' + account.status + ')' : '';
-      };
-      if (reviewer.account) {
-        // Reviewer is an account suggestion from getChangeSuggestedReviewers.
-        const reviewerName = this._accountOrAnon(reviewer.account);
-        const reviewerEmail = this._reviewerEmail(reviewer.account.email);
-        const reviewerStatus = generateStatusStr(reviewer.account);
-        name = [reviewerName, reviewerEmail, reviewerStatus]
-            .filter(p => p.length > 0).join(' ');
-        value = reviewer;
-      } else if (reviewer.group) {
-        // Reviewer is a group suggestion from getChangeSuggestedReviewers.
-        name = reviewer.group.name + ' (group)';
-        value = reviewer;
-      } else if (reviewer._account_id) {
-        // Reviewer is an account suggestion from getSuggestedAccounts.
-        const reviewerName = this._accountOrAnon(reviewer);
-        const reviewerEmail = this._reviewerEmail(reviewer.email);
-        const reviewerStatus = generateStatusStr(reviewer);
-        name = [reviewerName, reviewerEmail, reviewerStatus]
-            .filter(p => p.length > 0).join(' ');
-        value = {account: reviewer, count: 1};
-      }
-      return {name, value};
-    },
-
-    _getReviewerSuggestions(input) {
-      if (!this.change || !this.change._number || !this._loggedIn) {
-        return Promise.resolve([]);
-      }
-
-      const api = this.$.restAPI;
-      const xhr = this.allowAnyUser ?
-          api.getSuggestedAccounts(`cansee:${this.change._number} ${input}`) :
-          api.getChangeSuggestedReviewers(this.change._number, input);
-
-      return xhr.then(reviewers => {
-        if (!reviewers) { return []; }
-        if (!this.filter) {
-          return reviewers.map(this._makeSuggestion.bind(this));
-        }
-        return reviewers
-            .filter(this.filter)
-            .map(this._makeSuggestion.bind(this));
-      });
-    },
-
-    _reviewerEmail(email) {
-      if (typeof email !== 'undefined') {
-        return '<' + email + '>';
-      }
-
-      return '';
-    },
-  });
-})();
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html
deleted file mode 100644
index 57bdd1d..0000000
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html
+++ /dev/null
@@ -1,276 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2016 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-account-entry</title>
-<script src="/test/common-test-setup.js"></script>
-<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-
-<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
-<script src="/bower_components/web-component-tester/browser.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../scripts/util.js"></script>
-
-<link rel="import" href="gr-account-entry.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
-  <template>
-    <gr-account-entry></gr-account-entry>
-  </template>
-</test-fixture>
-
-<script>
-  suite('gr-account-entry tests', () => {
-    let sandbox;
-    let _nextAccountId = 0;
-    const makeAccount = function(opt_status) {
-      const accountId = ++_nextAccountId;
-      return {
-        _account_id: accountId,
-        name: 'name ' + accountId,
-        email: 'email ' + accountId,
-        status: opt_status,
-      };
-    };
-    let _nextAccountId2 = 0;
-    const makeAccount2 = function(opt_status) {
-      const accountId2 = ++_nextAccountId2;
-      return {
-        _account_id: accountId2,
-        email: 'email ' + accountId2,
-        status: opt_status,
-      };
-    };
-    let _nextAccountId3 = 0;
-    const makeAccount3 = function(opt_status) {
-      const accountId3 = ++_nextAccountId3;
-      return {
-        _account_id: accountId3,
-        name: 'name ' + accountId3,
-        status: opt_status,
-      };
-    };
-
-    let owner;
-    let existingReviewer1;
-    let existingReviewer2;
-    let suggestion1;
-    let suggestion2;
-    let suggestion3;
-    let element;
-
-    setup(done => {
-      owner = makeAccount();
-      existingReviewer1 = makeAccount();
-      existingReviewer2 = makeAccount();
-      suggestion1 = {account: makeAccount()};
-      suggestion2 = {account: makeAccount()};
-      suggestion3 = {
-        group: {
-          id: 'suggested group id',
-          name: 'suggested group',
-        },
-      };
-
-      stub('gr-rest-api-interface', {
-        getLoggedIn() { return Promise.resolve(true); },
-      });
-
-      element = fixture('basic');
-      element.change = {
-        _number: 42,
-        owner,
-        reviewers: {
-          CC: [existingReviewer1],
-          REVIEWER: [existingReviewer2],
-        },
-      };
-      sandbox = sinon.sandbox.create();
-      return flush(done);
-    });
-
-    teardown(() => {
-      sandbox.restore();
-    });
-
-    suite('stubbed values for _getReviewerSuggestions', () => {
-      setup(() => {
-        stub('gr-rest-api-interface', {
-          getChangeSuggestedReviewers() {
-            const redundantSuggestion1 = {account: existingReviewer1};
-            const redundantSuggestion2 = {account: existingReviewer2};
-            const redundantSuggestion3 = {account: owner};
-            return Promise.resolve([redundantSuggestion1, redundantSuggestion2,
-              redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
-          },
-        });
-      });
-
-      test('_makeSuggestion formats account or group accordingly', () => {
-        let account = makeAccount();
-        const account2 = makeAccount2();
-        const account3 = makeAccount3();
-        let suggestion = element._makeSuggestion({account});
-        assert.deepEqual(suggestion, {
-          name: account.name + ' <' + account.email + '>',
-          value: {account},
-        });
-
-        const group = {name: 'test'};
-        suggestion = element._makeSuggestion({group});
-        assert.deepEqual(suggestion, {
-          name: group.name + ' (group)',
-          value: {group},
-        });
-
-        suggestion = element._makeSuggestion(account);
-        assert.deepEqual(suggestion, {
-          name: account.name + ' <' + account.email + '>',
-          value: {account, count: 1},
-        });
-
-        element._config = {
-          user: {
-            anonymous_coward_name: 'Anonymous Coward',
-          },
-        };
-        assert.deepEqual(element._accountOrAnon(account2), 'Anonymous');
-
-        account = makeAccount('OOO');
-
-        suggestion = element._makeSuggestion({account});
-        assert.deepEqual(suggestion, {
-          name: account.name + ' <' + account.email + '> (OOO)',
-          value: {account},
-        });
-
-        suggestion = element._makeSuggestion(account);
-        assert.deepEqual(suggestion, {
-          name: account.name + ' <' + account.email + '> (OOO)',
-          value: {account, count: 1},
-        });
-
-        sandbox.stub(element, '_reviewerEmail',
-            () => { return ''; });
-
-        suggestion = element._makeSuggestion(account3);
-        assert.deepEqual(suggestion, {
-          name: account3.name,
-          value: {account: account3, count: 1},
-        });
-      });
-
-      test('_reviewerEmail', () => {
-        assert.equal(
-            element._reviewerEmail('email@gerritreview.com'),
-            '<email@gerritreview.com>');
-        assert.equal(element._reviewerEmail(undefined), '');
-      });
-
-      test('_getReviewerSuggestions excludes owner+reviewers', done => {
-        element._getReviewerSuggestions().then(reviewers => {
-          // Default is no filtering.
-          assert.equal(reviewers.length, 6);
-
-          // Set up filter that only accepts suggestion1.
-          const accountId = suggestion1.account._account_id;
-          element.filter = function(suggestion) {
-            return suggestion.account &&
-                suggestion.account._account_id === accountId;
-          };
-
-          element._getReviewerSuggestions().then(reviewers => {
-            assert.deepEqual(reviewers, [element._makeSuggestion(suggestion1)]);
-          }).then(done);
-        });
-      });
-
-      test('_getReviewerSuggestions short circuits when logged out', () => {
-        // API call is already stubbed.
-        const xhrSpy = element.$.restAPI.getChangeSuggestedReviewers;
-        element._loggedIn = false;
-        return element._getReviewerSuggestions('').then(() => {
-          assert.isFalse(xhrSpy.called);
-          element._loggedIn = true;
-          return element._getReviewerSuggestions('').then(() => {
-            assert.isTrue(xhrSpy.called);
-          });
-        });
-      });
-    });
-
-    test('allowAnyUser', done => {
-      const suggestReviewerStub =
-          sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
-          .returns(Promise.resolve([]));
-      const suggestAccountStub =
-          sandbox.stub(element.$.restAPI, 'getSuggestedAccounts')
-          .returns(Promise.resolve([]));
-
-      element._getReviewerSuggestions('').then(() => {
-        assert.isTrue(suggestReviewerStub.calledOnce);
-        assert.isTrue(suggestReviewerStub.calledWith(42, ''));
-        assert.isFalse(suggestAccountStub.called);
-        element.allowAnyUser = true;
-
-        element._getReviewerSuggestions('').then(() => {
-          assert.isTrue(suggestReviewerStub.calledOnce);
-          assert.isTrue(suggestAccountStub.calledOnce);
-          assert.isTrue(suggestAccountStub.calledWith('cansee:42 '));
-          done();
-        });
-      });
-    });
-    test('account-text-changed fired when input text changed and allowAnyInput',
-        () => {
-          // Spy on query, as that is called when _updateSuggestions proceeds.
-          const changeStub = sandbox.stub();
-          element.allowAnyInput = true;
-          sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
-              .returns(Promise.resolve([]));
-          element.addEventListener('account-text-changed', changeStub);
-          element.$.input.text = 'a';
-          assert.isTrue(changeStub.calledOnce);
-          element.$.input.text = 'ab';
-          assert.isTrue(changeStub.calledTwice);
-        });
-
-    test('account-text-changed not fired when input text changed without ' +
-        'allowAnyUser', () => {
-          // Spy on query, as that is called when _updateSuggestions proceeds.
-      const changeStub = sandbox.stub();
-      sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
-          .returns(Promise.resolve([]));
-      element.addEventListener('account-text-changed', changeStub);
-      element.$.input.text = 'a';
-      assert.isFalse(changeStub.called);
-    });
-
-    test('setText', () => {
-      // Spy on query, as that is called when _updateSuggestions proceeds.
-      const suggestSpy = sandbox.spy(element.$.input, 'query');
-      element.setText('test text');
-      flushAsynchronousOperations();
-
-      assert.equal(element.$.input.$.input.value, 'test text');
-      assert.isFalse(suggestSpy.called);
-    });
-  });
-</script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index 15892c9..7c86143 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -36,6 +36,8 @@
 <link rel="import" href="../gr-change-requirements/gr-change-requirements.html">
 <link rel="import" href="../gr-commit-info/gr-commit-info.html">
 <link rel="import" href="../gr-reviewer-list/gr-reviewer-list.html">
+<script src="../../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js"></script>
 
 <dom-module id="gr-change-metadata">
   <template>
@@ -114,6 +116,9 @@
         margin-top: .5em;
         padding: .5em 0;
       }
+      .topic gr-linked-chip {
+        --linked-chip-text-color: var(--link-color);
+      }
     </style>
     <gr-external-style id="externalStyle" name="change-metadata">
       <section>
@@ -172,9 +177,9 @@
               id="assigneeValue"
               placeholder="Set assignee..."
               accounts="{{_assignee}}"
-              change="[[change]]"
               readonly="[[_computeAssigneeReadOnly(_mutable, change)]]"
-              allow-any-user></gr-account-list>
+              suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
+          </gr-account-list>
         </span>
       </section>
       <section>
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 6ee5a24..dbb41f6 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
@@ -471,5 +471,12 @@
       // dom-if.
       this.$$('.topicEditableLabel').open();
     },
+
+    _getReviewerSuggestionsProvider(change) {
+      const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
+          change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY);
+      provider.init();
+      return provider;
+    },
   });
 })();
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 30a0807..ee1ae11 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
@@ -139,9 +139,9 @@
       }
       .editCommitMessage {
         margin-top: 1em;
+
         --gr-button: {
-          padding-left: 0;
-          padding-right: 0;
+          padding: 5px 0px;
         }
       }
       .changeStatuses,
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 44ccda3..301bdbd 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
@@ -890,19 +890,22 @@
     _changeChanged(change) {
       if (!change || !this._patchRange || !this._allPatchSets) { return; }
 
+      // We get the parent first so we keep the original value for basePatchNum
+      // and not the updated value.
       const parent = this._getBasePatchNum(change, this._patchRange);
 
-      this.set('_patchRange.basePatchNum', parent);
       this.set('_patchRange.patchNum', this._patchRange.patchNum ||
               this.computeLatestPatchNum(this._allPatchSets));
 
+      this.set('_patchRange.basePatchNum', parent);
+
       const title = change.subject + ' (' + change.change_id.substr(0, 9) + ')';
       this.fire('title-change', {title});
     },
 
     /**
      * Gets base patch number, if it is a parent try and decide from
-     * preference weather to default to `auto merge`, `Parent 1` or `PARENT`.
+     * preference whether to default to `auto merge`, `Parent 1` or `PARENT`.
      * @param {Object} change
      * @param {Object} patchRange
      * @return {number|string}
@@ -1244,7 +1247,7 @@
       if (!this._patchRange.patchNum &&
           change.current_revision === edit.base_revision) {
         change.current_revision = edit.commit.commit;
-        this._patchRange.patchNum = this.EDIT_NAME;
+        this.set('_patchRange.patchNum', this.EDIT_NAME);
         // Because edits are fibbed as revisions and added to the revisions
         // array, and revision actions are always derived from the 'latest'
         // patch set, we must copy over actions from the patch set base.
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 08a3ec3..12698fc 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
@@ -32,9 +32,11 @@
 <link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../../shared/gr-storage/gr-storage.html">
-<link rel="import" href="../gr-account-list/gr-account-list.html">
+<link rel="import" href="../../shared/gr-account-list/gr-account-list.html">
 <link rel="import" href="../gr-label-scores/gr-label-scores.html">
 <link rel="import" href="../../../styles/shared-styles.html">
+<script src="../../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js"></script>
 
 <dom-module id="gr-reply-dialog">
   <template>
@@ -165,11 +167,11 @@
               id="reviewers"
               accounts="{{_reviewers}}"
               removable-values="[[change.removable_reviewers]]"
-              change="[[change]]"
               filter="[[filterReviewerSuggestion]]"
               pending-confirmation="{{_reviewerPendingConfirmation}}"
               placeholder="Add reviewer..."
-              on-account-text-changed="_handleAccountTextEntry">
+              on-account-text-changed="_handleAccountTextEntry"
+              suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
           </gr-account-list>
         </div>
         <div class="peopleList">
@@ -177,12 +179,12 @@
           <gr-account-list
               id="ccs"
               accounts="{{_ccs}}"
-              change="[[change]]"
               filter="[[filterCCSuggestion]]"
               pending-confirmation="{{_ccPendingConfirmation}}"
               allow-any-input
               placeholder="Add CC..."
-              on-account-text-changed="_handleAccountTextEntry">
+              on-account-text-changed="_handleAccountTextEntry"
+              suggestions-provider="[[_getCcSuggestionsProvider(change)]]">
           </gr-account-list>
         </div>
         <gr-overlay
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 3b97439..73b12f7 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
@@ -315,31 +315,37 @@
     },
 
     _ccsChanged(splices) {
-      if (splices && splices.indexSplices) {
-        this._reviewersMutated = true;
-        this._processReviewerChange(splices.indexSplices, ReviewerTypes.CC);
-      }
+      this._reviewerTypeChanged(splices, ReviewerTypes.CC);
     },
 
     _reviewersChanged(splices) {
+      this._reviewerTypeChanged(splices, ReviewerTypes.REVIEWER);
+    },
+
+    _reviewerTypeChanged(splices, reviewerType) {
       if (splices && splices.indexSplices) {
         this._reviewersMutated = true;
         this._processReviewerChange(splices.indexSplices,
-            ReviewerTypes.REVIEWER);
+            reviewerType);
         let key;
         let index;
         let account;
-        // Remove any accounts that already exist as a CC.
+        // Remove any accounts that already exist as a CC for reviewer
+        // or vice versa.
+        const isReviewer = ReviewerTypes.REVIEWER === reviewerType;
         for (const splice of splices.indexSplices) {
           for (let i = 0; i < splice.addedCount; i++) {
             account = splice.object[splice.index + i];
             key = this._accountOrGroupKey(account);
-            index = this._ccs.findIndex(
+            const array = isReviewer ? this._ccs : this._reviewers;
+            index = array.findIndex(
                 account => this._accountOrGroupKey(account) === key);
             if (index >= 0) {
-              this.splice('_ccs', index, 1);
+              this.splice(isReviewer ? '_ccs' : '_reviewers', index, 1);
+              const moveFrom = isReviewer ? 'CC' : 'reviewer';
+              const moveTo = isReviewer ? 'reviewer' : 'CC';
               const message = (account.name || account.email || key) +
-                  ' moved from CC to reviewer.';
+                  ` moved from ${moveFrom} to ${moveTo}.`;
               this.fire('show-alert', {message});
             }
           }
@@ -895,5 +901,19 @@
     _sendDisabledChanged(sendDisabled) {
       this.dispatchEvent(new CustomEvent('send-disabled-changed'));
     },
+
+    _getReviewerSuggestionsProvider(change) {
+      const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
+          change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.REVIEWER);
+      provider.init();
+      return provider;
+    },
+
+    _getCcSuggestionsProvider(change) {
+      const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
+          change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.CC);
+      provider.init();
+      return provider;
+    },
   });
 })();
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 665ca63..01f1f4d 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
@@ -733,6 +733,40 @@
       assert.deepEqual(element._reviewersPendingRemove.CC, [cc1, cc4, cc3]);
     });
 
+    test('moving from reviewer to cc', () => {
+      element._reviewersPendingRemove = {
+        CC: [],
+        REVIEWER: [],
+      };
+      flushAsynchronousOperations();
+
+      const reviewer1 = makeAccount();
+      const reviewer2 = makeAccount();
+      const reviewer3 = makeAccount();
+      const cc1 = makeAccount();
+      const cc2 = makeAccount();
+      const cc3 = makeAccount();
+      const cc4 = makeAccount();
+      element._reviewers = [reviewer1, reviewer2, reviewer3];
+      element._ccs = [cc1, cc2, cc3, cc4];
+      element.push('_ccs', reviewer1);
+      flushAsynchronousOperations();
+
+      assert.deepEqual(element._reviewers,
+          [reviewer2, reviewer3]);
+      assert.deepEqual(element._ccs, [cc1, cc2, cc3, cc4, reviewer1]);
+      assert.deepEqual(element._reviewersPendingRemove.REVIEWER, [reviewer1]);
+
+      element.push('_ccs', reviewer3, reviewer2);
+      flushAsynchronousOperations();
+
+      assert.deepEqual(element._reviewers, []);
+      assert.deepEqual(element._ccs,
+          [cc1, cc2, cc3, cc4, reviewer1, reviewer3, reviewer2]);
+      assert.deepEqual(element._reviewersPendingRemove.REVIEWER,
+          [reviewer1, reviewer3, reviewer2]);
+    });
+
     test('migrate reviewers between states', done => {
       element._reviewersPendingRemove = {
         CC: [],
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 6e2686e..c1031301 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
@@ -51,8 +51,7 @@
       }
       gr-button {
         --gr-button: {
-          padding-left: 0;
-          padding-right: 0;
+          padding: 5px 0px;
         }
       }
       @media screen and (max-width: 50em), screen and (min-width: 75em) {
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
index 7949002..dc18f8a 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
@@ -15,7 +15,7 @@
 limitations under the License.
 -->
 
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
+<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
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 2ade782..f8288c6 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
@@ -58,7 +58,7 @@
     },
 
     behaviors: [
-      Gerrit.AnonymousNameBehavior,
+      Gerrit.DisplayNameBehavior,
     ],
 
     detached() {
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 08bb700..92c0fb8 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
@@ -40,7 +40,7 @@
       }
       .bigTitle {
         color: var(--header-text-color);
-        font-size: 1.75rem;
+        font-size: var(--header-title-font-size);
         text-decoration: none;
       }
       .bigTitle:hover {
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
index 06e354c..c4ae41b 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
@@ -16,7 +16,7 @@
 -->
 <link rel="import" href="/bower_components/polymer/polymer.html">
 
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
+<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.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">
 <link rel="import" href="../gr-search-bar/gr-search-bar.html">
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
index fed02d6..017310d 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
@@ -49,7 +49,7 @@
     },
 
     behaviors: [
-      Gerrit.AnonymousNameBehavior,
+      Gerrit.DisplayNameBehavior,
     ],
 
     attached() {
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
index 2d66bc2..2f3e9b1e 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
@@ -52,6 +52,9 @@
       gr-dialog .main {
         width: 100%;
       }
+      gr-dialog .main > iron-input{
+        width: 100%;
+      }
       gr-autocomplete {
         --gr-autocomplete: {
           border: 1px solid var(--border-color);
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
index 2658bd2..ece15bc 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -188,28 +188,33 @@
     },
 
     _handleDeleteConfirm(e) {
+      // Get the dialog before the api call as the event will change during bubbling
+      // which will make Polymer.dom(e).path an emtpy array in polymer 2
+      const dialog = this._getDialogFromEvent(e);
       this.$.restAPI.deleteFileInChangeEdit(this.change._number, this._path)
           .then(res => {
             if (!res.ok) { return; }
-            this._closeDialog(this._getDialogFromEvent(e), true);
+            this._closeDialog(dialog, true);
             Gerrit.Nav.navigateToChange(this.change);
           });
     },
 
     _handleRestoreConfirm(e) {
+      const dialog = this._getDialogFromEvent(e);
       this.$.restAPI.restoreFileInChangeEdit(this.change._number, this._path)
           .then(res => {
             if (!res.ok) { return; }
-            this._closeDialog(this._getDialogFromEvent(e), true);
+            this._closeDialog(dialog, true);
             Gerrit.Nav.navigateToChange(this.change);
           });
     },
 
     _handleRenameConfirm(e) {
+      const dialog = this._getDialogFromEvent(e);
       return this.$.restAPI.renameFileInChangeEdit(this.change._number,
           this._path, this._newPath).then(res => {
             if (!res.ok) { return; }
-            this._closeDialog(this._getDialogFromEvent(e), true);
+            this._closeDialog(dialog, true);
             Gerrit.Nav.navigateToChange(this.change);
           });
     },
diff --git a/polygerrit-ui/app/elements/gr-app-element.html b/polygerrit-ui/app/elements/gr-app-element.html
index a42622e..5c297e7 100644
--- a/polygerrit-ui/app/elements/gr-app-element.html
+++ b/polygerrit-ui/app/elements/gr-app-element.html
@@ -68,13 +68,17 @@
         color: var(--primary-text-color);
       }
       gr-main-header {
-        background-color: var(--header-background-color);
+        background: var(--header-background, var(--header-background-color, #eee));
         padding: 0 var(--default-horizontal-margin);
-        border-bottom: 1px solid var(--border-color);
+        border-bottom: var(--header-border-bottom);
+        border-image: var(--header-border-image);
+        border-right: 0;
+        border-left: 0;
+        border-top: 0;
       }
       footer {
-        background-color: var(--footer-background-color);
-        border-top: 1px solid var(--border-color);
+        background: var(--footer-background, var(--footer-background-color, #eee));
+        border-top: var(--footer-border-top);
         display: flex;
         justify-content: space-between;
         padding: .5rem var(--default-horizontal-margin);
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.html
similarity index 79%
rename from polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
rename to polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.html
index 03fc606..ae656fd 100644
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.html
@@ -15,12 +15,11 @@
 limitations under the License.
 -->
 
-<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">
+<link rel="import" href="../gr-autocomplete/gr-autocomplete.html">
+<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
 
 <dom-module id="gr-account-entry">
   <template>
@@ -36,14 +35,13 @@
         borderless="[[borderless]]"
         placeholder="[[placeholder]]"
         threshold="[[suggestFrom]]"
-        query="[[query]]"
+        query="[[querySuggestions]]"
         allow-non-suggested-values="[[allowAnyInput]]"
         on-commit="_handleInputCommit"
         clear-on-commit
         warn-uncommitted
         text="{{_inputText}}">
     </gr-autocomplete>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
   <script src="gr-account-entry.js"></script>
 </dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js
new file mode 100644
index 0000000..63bd252
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js
@@ -0,0 +1,102 @@
+/**
+ * @license
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function() {
+  'use strict';
+
+  /**
+   * gr-account-entry is an element for entering account
+   * and/or group with autocomplete support.
+   */
+  Polymer({
+    is: 'gr-account-entry',
+    _legacyUndefinedCheck: true,
+
+    /**
+     * Fired when an account is entered.
+     *
+     * @event add
+     */
+
+    /**
+     * When allowAnyInput is true, account-text-changed is fired when input text
+     * changed. This is needed so that the reply dialog's save button can be
+     * enabled for arbitrary cc's, which don't need a 'commit'.
+     *
+     * @event account-text-changed
+     */
+    properties: {
+      allowAnyInput: Boolean,
+      borderless: Boolean,
+      placeholder: String,
+
+      // suggestFrom = 0 to enable default suggestions.
+      suggestFrom: {
+        type: Number,
+        value: 0,
+      },
+
+      /** @type {!function(string): !Promise<Array<{name, value}>>} */
+      querySuggestions: {
+        type: Function,
+        notify: true,
+        value() {
+          return input => Promise.resolve([]);
+        },
+      },
+
+      _config: Object,
+      /** The value of the autocomplete entry. */
+      _inputText: {
+        type: String,
+        observer: '_inputTextChanged',
+      },
+
+    },
+
+    get focusStart() {
+      return this.$.input.focusStart;
+    },
+
+    focus() {
+      this.$.input.focus();
+    },
+
+    clear() {
+      this.$.input.clear();
+    },
+
+    setText(text) {
+      this.$.input.setText(text);
+    },
+
+    getText() {
+      return this.$.input.text;
+    },
+
+    _handleInputCommit(e) {
+      this.fire('add', {value: e.detail.value});
+      this.$.input.focus();
+    },
+
+    _inputTextChanged(text) {
+      if (text.length && this.allowAnyInput) {
+        this.dispatchEvent(new CustomEvent(
+                'account-text-changed', {bubbles: true, composed: true}));
+      }
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html
new file mode 100644
index 0000000..59792a7
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-account-entry</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<script src="../../../scripts/util.js"></script>
+
+<link rel="import" href="gr-account-entry.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-account-entry></gr-account-entry>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-account-entry tests', () => {
+    let sandbox;
+
+    const suggestion1 = {
+      email: 'email1@example.com',
+      _account_id: 1,
+      some_property: 'value',
+    };
+    const suggestion2 = {
+      email: 'email2@example.com',
+      _account_id: 2,
+    };
+    const suggestion3 = {
+      email: 'email25@example.com',
+      _account_id: 25,
+      some_other_property: 'other value',
+    };
+
+    setup(done => {
+      element = fixture('basic');
+      sandbox = sinon.sandbox.create();
+      return flush(done);
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    suite('stubbed values for querySuggestions', () => {
+      setup(() => {
+        element.querySuggestions = input => {
+          return Promise.resolve([
+            suggestion1,
+            suggestion2,
+            suggestion3,
+          ]);
+        };
+      });
+    });
+
+    test('account-text-changed fired when input text changed and allowAnyInput',
+        () => {
+          // Spy on query, as that is called when _updateSuggestions proceeds.
+          const changeStub = sandbox.stub();
+          element.allowAnyInput = true;
+          element.querySuggestions = input => Promise.resolve([]);
+          element.addEventListener('account-text-changed', changeStub);
+          element.$.input.text = 'a';
+          assert.isTrue(changeStub.calledOnce);
+          element.$.input.text = 'ab';
+          assert.isTrue(changeStub.calledTwice);
+        });
+
+    test('account-text-changed not fired when input text changed without ' +
+        'allowAnyInput', () => {
+          // Spy on query, as that is called when _updateSuggestions proceeds.
+      const changeStub = sandbox.stub();
+      element.querySuggestions = input => Promise.resolve([]);
+      element.addEventListener('account-text-changed', changeStub);
+      element.$.input.text = 'a';
+      assert.isFalse(changeStub.called);
+    });
+
+    test('setText', () => {
+      // Spy on query, as that is called when _updateSuggestions proceeds.
+      const suggestSpy = sandbox.spy(element.$.input, 'query');
+      element.setText('test text');
+      flushAsynchronousOperations();
+
+      assert.equal(element.$.input.$.input.value, 'test text');
+      assert.isFalse(suggestSpy.called);
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html
index 9d0782f..f807a74 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html
@@ -15,7 +15,7 @@
 limitations under the License.
 -->
 
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
+<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
 <link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
 <link rel="import" href="/bower_components/polymer/polymer.html">
 <link rel="import" href="../../../styles/shared-styles.html">
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 88df33b..a778e8b 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
@@ -52,7 +52,7 @@
     },
 
     behaviors: [
-      Gerrit.AnonymousNameBehavior,
+      Gerrit.DisplayNameBehavior,
       Gerrit.TooltipBehavior,
     ],
 
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.html
similarity index 91%
rename from polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
rename to polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.html
index 31c1be5..c793c07 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.html
@@ -17,7 +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-account-chip/gr-account-chip.html">
+<link rel="import" href="../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">
 
@@ -56,7 +56,7 @@
             account="[[account]]"
             class$="[[_computeChipClass(account)]]"
             data-account-id$="[[account._account_id]]"
-            removable="[[_computeRemovable(account)]]"
+            removable="[[_computeRemovable(account, readonly)]]"
             on-keydown="_handleChipKeydown"
             tabindex="-1">
         </gr-account-chip>
@@ -67,13 +67,13 @@
         hidden$="[[_computeEntryHidden(maxCount, accounts.*, readonly)]]"
         id="entry"
         change="[[change]]"
-        filter="[[filter]]"
         placeholder="[[placeholder]]"
         on-add="_handleAdd"
         on-input-keydown="_handleInputKeydown"
         allow-any-input="[[allowAnyInput]]"
-        allow-any-user="[[allowAnyUser]]">
+        query-suggestions="[[_querySuggestions]]">
     </gr-account-entry>
+    <slot></slot>
   </template>
   <script src="gr-account-list.js"></script>
 </dom-module>
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
similarity index 79%
rename from polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
rename to polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
index 479fee2..9edc9c8 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
@@ -19,6 +19,24 @@
 
   const VALID_EMAIL_ALERT = 'Please input a valid email.';
 
+  const Defs = {};
+
+  /**
+   * @typedef {{
+   *   name: string,
+   *   value: Object,
+   * }}
+   */
+  Defs.GrSuggestionItem;
+
+  /**
+   * @typedef {{
+   *    getSuggestions: function(string): Promise<Array<Object>>,
+   *    makeSuggestionItem: function(Object): Defs.GrSuggestionItem,
+   * }}
+   */
+  Defs.GrSuggestionsProvider;
+
   Polymer({
     is: 'gr-account-list',
     _legacyUndefinedCheck: true,
@@ -38,6 +56,19 @@
       change: Object,
       filter: Function,
       placeholder: String,
+      disabled: {
+        type: Function,
+        value: false,
+      },
+
+      /**
+       * Returns suggestions and convert them to list item
+       * @type {Defs.GrSuggestionsProvider}
+       */
+      suggestionsProvider: {
+        type: Object,
+      },
+
       /**
        * Needed for template checking since value is initially set to null.
        * @type {?Object} */
@@ -50,21 +81,6 @@
         type: Boolean,
         value: false,
       },
-
-      /**
-       * When true, the account-entry autocomplete uses the account suggest API
-       * endpoint, which suggests any account in that Gerrit instance (and does
-       * not suggest groups).
-       *
-       * When false/undefined, account-entry uses the suggest_reviewers API
-       * endpoint, which suggests any account or group in that Gerrit instance
-       * that is not already a reviewer (or is not CCed) on that change.
-       */
-      allowAnyUser: {
-        type: Boolean,
-        value: false,
-      },
-
       /**
        * When true, allows for non-suggested inputs to be added.
        */
@@ -82,6 +98,16 @@
         type: Number,
         value: 0,
       },
+
+      /** Returns suggestion items
+      * @type {!function(string): Promise<Array<Defs.GrSuggestionItem>>}
+      */
+      _querySuggestions: {
+        type: Function,
+        value() {
+          return this._getSuggestions.bind(this);
+        },
+      },
     },
 
     behaviors: [
@@ -103,31 +129,46 @@
       return this.$.entry.focusStart;
     },
 
-    _handleAdd(e) {
-      this._addReviewer(e.detail.value);
+    _getSuggestions(input) {
+      const provider = this.suggestionsProvider;
+      if (!provider) {
+        return Promise.resolve([]);
+      }
+      return provider.getSuggestions(input).then(suggestions => {
+        if (!suggestions) { return []; }
+        if (this.filter) {
+          suggestions = suggestions.filter(this.filter);
+        }
+        return suggestions.map(suggestion =>
+            provider.makeSuggestionItem(suggestion));
+      });
     },
 
-    _addReviewer(reviewer) {
+    _handleAdd(e) {
+      this._addAccountItem(e.detail.value);
+    },
+
+    _addAccountItem(item) {
       // Append new account or group to the accounts property. We add our own
       // internal properties to the account/group here, so we clone the object
       // to avoid cluttering up the shared change object.
-      if (reviewer.account) {
+      if (item.account) {
         const account =
-            Object.assign({}, reviewer.account, {_pendingAdd: true});
+            Object.assign({}, item.account, {_pendingAdd: true});
         this.push('accounts', account);
-      } else if (reviewer.group) {
-        if (reviewer.confirm) {
-          this.pendingConfirmation = reviewer;
+      } else if (item.group) {
+        if (item.confirm) {
+          this.pendingConfirmation = item;
           return;
         }
-        const group = Object.assign({}, reviewer.group,
+        const group = Object.assign({}, item.group,
             {_pendingAdd: true, _group: true});
         this.push('accounts', group);
       } else if (this.allowAnyInput) {
-        if (!reviewer.includes('@')) {
+        if (!item.includes('@')) {
           // Repopulate the input with what the user tried to enter and have
           // a toast tell them why they can't enter it.
-          this.$.entry.setText(reviewer);
+          this.$.entry.setText(item);
           this.dispatchEvent(new CustomEvent('show-alert', {
             detail: {message: VALID_EMAIL_ALERT},
             bubbles: true,
@@ -135,7 +176,7 @@
           }));
           return false;
         } else {
-          const account = {email: reviewer, _pendingAdd: true};
+          const account = {email: item, _pendingAdd: true};
           this.push('accounts', account);
         }
       }
@@ -173,8 +214,8 @@
       return a === b;
     },
 
-    _computeRemovable(account) {
-      if (this.readonly) { return false; }
+    _computeRemovable(account, readonly) {
+      if (readonly) { return false; }
       if (this.removableValues) {
         for (let i = 0; i < this.removableValues.length; i++) {
           if (this._accountMatches(this.removableValues[i], account)) {
@@ -193,7 +234,9 @@
     },
 
     _removeAccount(toRemove) {
-      if (!toRemove || !this._computeRemovable(toRemove)) { return; }
+      if (!toRemove || !this._computeRemovable(toRemove, this.readonly)) {
+        return;
+      }
       for (let i = 0; i < this.accounts.length; i++) {
         let matches;
         const account = this.accounts[i];
@@ -277,7 +320,7 @@
     submitEntryText() {
       const text = this.$.entry.getText();
       if (!text.length) { return true; }
-      const wasSubmitted = this._addReviewer(text);
+      const wasSubmitted = this._addAccountItem(text);
       if (wasSubmitted) { this.$.entry.clear(); }
       return wasSubmitted;
     },
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
similarity index 77%
rename from polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html
rename to polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
index d32c269..22e3a3d 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
@@ -35,6 +35,15 @@
 </test-fixture>
 
 <script>
+  class MockSuggestionsProvider {
+    getSuggestions(input) {
+      return Promise.resolve([]);
+    }
+
+    makeSuggestionItem(item) {
+      return item;
+    }
+  }
   suite('gr-account-list tests', () => {
     let _nextAccountId = 0;
     const makeAccount = function() {
@@ -51,10 +60,11 @@
       };
     };
 
-    let existingReviewer1;
-    let existingReviewer2;
+    let existingAccount1;
+    let existingAccount2;
     let sandbox;
     let element;
+    let suggestionsProvider;
 
     function getChips() {
       return Polymer.dom(element.root).querySelectorAll('gr-account-chip');
@@ -62,14 +72,16 @@
 
     setup(() => {
       sandbox = sinon.sandbox.create();
-      existingReviewer1 = makeAccount();
-      existingReviewer2 = makeAccount();
+      existingAccount1 = makeAccount();
+      existingAccount2 = makeAccount();
 
       stub('gr-rest-api-interface', {
         getConfig() { return Promise.resolve({}); },
       });
       element = fixture('basic');
-      element.accounts = [existingReviewer1, existingReviewer2];
+      element.accounts = [existingAccount1, existingAccount2];
+      suggestionsProvider = new MockSuggestionsProvider();
+      element.suggestionsProvider = suggestionsProvider;
     });
 
     teardown(() => {
@@ -109,7 +121,7 @@
       assert.isTrue(chips[2].classList.contains('pendingAdd'));
 
       // Removed accounts are taken out of the list.
-      element.fire('remove', {account: existingReviewer1});
+      element.fire('remove', {account: existingAccount1});
       flushAsynchronousOperations();
       chips = getChips();
       assert.equal(chips.length, 2);
@@ -117,7 +129,7 @@
       assert.isTrue(chips[1].classList.contains('pendingAdd'));
 
       // Invalid remove is ignored.
-      element.fire('remove', {account: existingReviewer1});
+      element.fire('remove', {account: existingAccount1});
       element.fire('remove', {account: newAccount});
       flushAsynchronousOperations();
       chips = getChips();
@@ -147,6 +159,52 @@
       assert.isFalse(chips[0].classList.contains('pendingAdd'));
     });
 
+    test('_getSuggestions uses filter correctly', done => {
+      const originalSuggestions = [
+        {
+          email: 'abc@example.com',
+          text: 'abcd',
+          _account_id: 3,
+        },
+        {
+          email: 'qwe@example.com',
+          text: 'qwer',
+          _account_id: 1,
+        },
+        {
+          email: 'xyz@example.com',
+          text: 'aaaaa',
+          _account_id: 25,
+        },
+      ];
+      sandbox.stub(suggestionsProvider, 'getSuggestions')
+          .returns(Promise.resolve(originalSuggestions));
+      sandbox.stub(suggestionsProvider, 'makeSuggestionItem', suggestion => {
+        return {
+          name: suggestion.email,
+          value: suggestion._account_id,
+        };
+      });
+
+
+      element._getSuggestions().then(suggestions => {
+        // Default is no filtering.
+        assert.equal(suggestions.length, 3);
+
+        // Set up filter that only accepts suggestion1.
+        const accountId = originalSuggestions[0]._account_id;
+        element.filter = function(suggestion) {
+          return suggestion._account_id === accountId;
+        };
+
+        element._getSuggestions().then(suggestions => {
+          assert.deepEqual(suggestions,
+              [{name: originalSuggestions[0].email,
+                value: originalSuggestions[0]._account_id}]);
+        }).then(done);
+      });
+    });
+
     test('_computeChipClass', () => {
       const account = makeAccount();
       assert.equal(element._computeChipClass(account), '');
@@ -163,18 +221,18 @@
       newAccount._pendingAdd = true;
       element.readonly = false;
       element.removableValues = [];
-      assert.isFalse(element._computeRemovable(existingReviewer1));
-      assert.isTrue(element._computeRemovable(newAccount));
+      assert.isFalse(element._computeRemovable(existingAccount1, false));
+      assert.isTrue(element._computeRemovable(newAccount, false));
 
 
-      element.removableValues = [existingReviewer1];
-      assert.isTrue(element._computeRemovable(existingReviewer1));
-      assert.isTrue(element._computeRemovable(newAccount));
-      assert.isFalse(element._computeRemovable(existingReviewer2));
+      element.removableValues = [existingAccount1];
+      assert.isTrue(element._computeRemovable(existingAccount1, false));
+      assert.isTrue(element._computeRemovable(newAccount, false));
+      assert.isFalse(element._computeRemovable(existingAccount2, false));
 
       element.readonly = true;
-      assert.isFalse(element._computeRemovable(existingReviewer1));
-      assert.isFalse(element._computeRemovable(newAccount));
+      assert.isFalse(element._computeRemovable(existingAccount1, true));
+      assert.isFalse(element._computeRemovable(newAccount, true));
     });
 
     test('submitEntryText', () => {
@@ -293,13 +351,40 @@
       assert.isTrue(element.$.entry.hasAttribute('hidden'));
     });
 
-    suite('allowAnyInput', () => {
-      let entry;
+    test('enter text calls suggestions provider', done => {
+      const suggestions = [
+        {
+          email: 'abc@example.com',
+          text: 'abcd',
+        },
+        {
+          email: 'qwe@example.com',
+          text: 'qwer',
+        },
+      ];
+      const getSuggestionsStub =
+          sandbox.stub(suggestionsProvider, 'getSuggestions')
+              .returns(Promise.resolve(suggestions));
 
+      const makeSuggestionItemStub =
+          sandbox.stub(suggestionsProvider, 'makeSuggestionItem', item => item);
+
+      const input = element.$.entry.$.input;
+
+      input.text = 'newTest';
+      MockInteractions.focus(input.$.input);
+      input.noDebounce = true;
+      flushAsynchronousOperations();
+      flush(() => {
+        assert.isTrue(getSuggestionsStub.calledOnce);
+        assert.equal(getSuggestionsStub.lastCall.args[0], 'newTest');
+        assert.equal(makeSuggestionItemStub.getCalls().length, 2);
+        done();
+      });
+    });
+
+    suite('allowAnyInput', () => {
       setup(() => {
-        entry = element.$.entry;
-        sandbox.stub(entry, '_getReviewerSuggestions');
-        sandbox.stub(entry.$.input, '_updateSuggestions');
         element.allowAnyInput = true;
       });
 
@@ -334,7 +419,6 @@
     suite('keyboard interactions', () => {
       test('backspace at text input start removes last account', () => {
         const input = element.$.entry.$.input;
-        sandbox.stub(element.$.entry, '_getReviewerSuggestions');
         sandbox.stub(input, '_updateSuggestions');
         sandbox.stub(element, '_computeRemovable').returns(true);
         // Next line is a workaround for Firefix not moving cursor
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 340297f..76ff442 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -122,6 +122,8 @@
       }
       :host([disabled][link]) {
         --background-color: transparent;
+        --text-color: var(--deemphasized-text-color);
+        cursor: default;
       }
 
       /* Styles for the optional down arrow */
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 e32f145..f21605d 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
@@ -95,15 +95,15 @@
     _showDropdown() {
       if (this.readOnly || this.editing) { return; }
       return this._open().then(() => {
-        this.$.input.$.input.focus();
+        this._nativeInput.focus();
         if (!this.$.input.value) { return; }
-        this.$.input.$.input.setSelectionRange(0, this.$.input.value.length);
+        this._nativeInput.setSelectionRange(0, this.$.input.value.length);
       });
     },
 
     open() {
       return this._open().then(() => {
-        this.$.input.$.input.focus();
+        this._nativeInput.focus();
       });
     },
 
@@ -155,6 +155,12 @@
       this._inputText = this.value;
     },
 
+    get _nativeInput() {
+      // In Polymer 2, the namespace of nativeInput
+      // changed from input to nativeInput
+      return this.$.input.$.nativeInput || this.$.input.$.input;
+    },
+
     /**
      * @suppress {checkTypes}
      * Closure doesn't think 'e' is an Event.
@@ -163,7 +169,7 @@
     _handleEnter(e) {
       e = this.getKeyboardEvent(e);
       const target = Polymer.dom(e).rootTarget;
-      if (target === this.$.input.$.input) {
+      if (target === this._nativeInput) {
         e.preventDefault();
         this._save();
       }
@@ -177,7 +183,7 @@
     _handleEsc(e) {
       e = this.getKeyboardEvent(e);
       const target = Polymer.dom(e).rootTarget;
-      if (target === this.$.input.$.input) {
+      if (target === this._nativeInput) {
         e.preventDefault();
         this._cancel();
       }
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
index f494325..984b870 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
@@ -50,18 +50,6 @@
     return getRestApi().getAccountCapabilities(capabilities);
   };
 
-  GrPluginRestApi.prototype.fetchJSON = function(req) {
-    // TODO(dhruvsri): find better implementation for fetchJSON
-    const api = getRestApi();
-    let fetchJSON;
-    if (api._fetchJSON) {
-      fetchJSON = api._fetchJSON.bind(api);
-    } else {
-      fetchJSON = api._restApiHelper.fetchJSON.bind(api._restApiHelper);
-    }
-    return fetchJSON(req);
-  };
-
   GrPluginRestApi.prototype.getRepos =
     function(filter, reposPerPage, opt_offset) {
       return getRestApi().getRepos(filter, reposPerPage, opt_offset);
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 417fa23..b63acda 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
@@ -1293,9 +1293,24 @@
      * @param {function(?Response, string=)=} opt_errFn
      */
     getChangeSuggestedReviewers(changeNum, inputVal, opt_errFn) {
+      return this._getChangeSuggestedGroup('REVIEWER', changeNum, inputVal,
+          opt_errFn);
+    },
+
+    /**
+     * @param {number|string} changeNum
+     * @param {string} inputVal
+     * @param {function(?Response, string=)=} opt_errFn
+     */
+    getChangeSuggestedCCs(changeNum, inputVal, opt_errFn) {
+      return this._getChangeSuggestedGroup('CC', changeNum, inputVal,
+          opt_errFn);
+    },
+
+    _getChangeSuggestedGroup(reviewerState, changeNum, inputVal, opt_errFn) {
       // More suggestions may obscure content underneath in the reply dialog,
       // see issue 10793.
-      const params = {n: 6};
+      const params = {'n': 6, 'reviewer-state': reviewerState};
       if (inputVal) { params.q = inputVal; }
       return this._getChangeURLAndFetch({
         changeNum,
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
index 6146e0f..0690950 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -76,14 +76,6 @@
       this._storage.removeItem(this._getEditableContentKey(key));
     },
 
-    getPreferences() {
-      return this._getObject('localPrefs');
-    },
-
-    savePreferences(localPrefs) {
-      this._setObject('localPrefs', localPrefs || null);
-    },
-
     _getDraftKey(location) {
       const range = location.range ?
           `${location.range.start_line}-${location.range.start_character}` +
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 131dc79..fa54dba 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
@@ -55,6 +55,9 @@
       iron-autogrow-textarea {
         padding: 2px;
         position: relative;
+
+        /** This is needed for firefox */
+        --iron-autogrow-textarea_-_white-space: pre-wrap;
       }
       #textarea.noBorder {
         border: none;
diff --git a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js
new file mode 100644
index 0000000..6e503c7
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js
@@ -0,0 +1,71 @@
+/**
+ * @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.
+ */
+(function(window) {
+  'use strict';
+
+  if (window.GrDisplayNameUtils) {
+    return;
+  }
+
+  const ANONYMOUS_NAME = 'Anonymous';
+
+  class GrDisplayNameUtils {
+    /**
+     * enableEmail when true enables to fallback to using email if
+     * the account name is not avilable.
+     */
+    static getUserName(config, account, enableEmail) {
+      if (account && account.name) {
+        return account.name;
+      } else if (account && account.username) {
+        return account.username;
+      } else if (enableEmail && account && account.email) {
+        return account.email;
+      } else if (config && config.user &&
+          config.user.anonymous_coward_name !== 'Anonymous Coward') {
+        return config.user.anonymous_coward_name;
+      }
+
+      return ANONYMOUS_NAME;
+    }
+
+    static getAccountDisplayName(config, account, enableEmail) {
+      const reviewerName = this._accountOrAnon(config, account, enableEmail);
+      const reviewerEmail = this._accountEmail(account.email);
+      const reviewerStatus = account.status ? '(' + account.status + ')' : '';
+      return [reviewerName, reviewerEmail, reviewerStatus]
+          .filter(p => p.length > 0).join(' ');
+    }
+
+    static _accountOrAnon(config, reviewer, enableEmail) {
+      return this.getUserName(config, reviewer, !!enableEmail);
+    }
+
+    static _accountEmail(email) {
+      if (typeof email !== 'undefined') {
+        return '<' + email + '>';
+      }
+      return '';
+    }
+
+    static getGroupDisplayName(group) {
+      return group.name + ' (group)';
+    }
+  }
+
+  window.GrDisplayNameUtils = GrDisplayNameUtils;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html
new file mode 100644
index 0000000..25ca4c5
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<!--
+@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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-display-name-utils</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
+<script src="gr-display-name-utils.js"></script>
+
+<script>
+  suite('gr-display-name-utils tests', () => {
+    // eslint-disable-next-line no-unused-vars
+    const config = {
+      user: {
+        anonymous_coward_name: 'Anonymous Coward',
+      },
+    };
+
+
+    test('getUserName name only', () => {
+      const account = {
+        name: 'test-name',
+      };
+      assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
+          'test-name');
+    });
+
+    test('getUserName username only', () => {
+      const account = {
+        username: 'test-user',
+      };
+      assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
+          'test-user');
+    });
+
+    test('getUserName email only', () => {
+      const account = {
+        email: 'test-user@test-url.com',
+      };
+      assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
+          'test-user@test-url.com');
+    });
+
+    test('getUserName returns not Anonymous Coward as the anon name', () => {
+      assert.deepEqual(GrDisplayNameUtils.getUserName(config, null, true),
+          'Anonymous');
+    });
+
+    test('getUserName for the config returning the anon name', () => {
+      const config = {
+        user: {
+          anonymous_coward_name: 'Test Anon',
+        },
+      };
+      assert.deepEqual(GrDisplayNameUtils.getUserName(config, null, true),
+          'Test Anon');
+    });
+
+    test('getAccountDisplayName - account with name only', () => {
+      assert.equal(
+          GrDisplayNameUtils.getAccountDisplayName(config,
+              {name: 'Some user name'}),
+          'Some user name');
+    });
+
+    test('getAccountDisplayName - account with email only', () => {
+      assert.equal(
+          GrDisplayNameUtils.getAccountDisplayName(config,
+              {email: 'my@example.com'}),
+          'Anonymous <my@example.com>');
+    });
+
+    test('getAccountDisplayName - account with email only - allowEmail', () => {
+      assert.equal(
+          GrDisplayNameUtils.getAccountDisplayName(config,
+              {email: 'my@example.com'}, true),
+          'my@example.com <my@example.com>');
+    });
+
+    test('getAccountDisplayName - account with name and status', () => {
+      assert.equal(
+          GrDisplayNameUtils.getAccountDisplayName(config, {
+            name: 'Some name',
+            status: 'OOO',
+          }),
+          'Some name (OOO)');
+    });
+
+    test('getAccountDisplayName - account with name and email', () => {
+      assert.equal(
+          GrDisplayNameUtils.getAccountDisplayName(config, {
+            name: 'Some name',
+            email: 'my@example.com',
+          }),
+          'Some name <my@example.com>');
+    });
+
+    test('getAccountDisplayName - account with name, email and status', () => {
+      assert.equal(
+          GrDisplayNameUtils.getAccountDisplayName(config, {
+            name: 'Some name',
+            email: 'my@example.com',
+            status: 'OOO',
+          }),
+          'Some name <my@example.com> (OOO)');
+    });
+
+    test('getGroupDisplayName', () => {
+      assert.equal(
+          GrDisplayNameUtils.getGroupDisplayName({name: 'Some user name'}),
+          'Some user name (group)');
+    });
+
+    test('_accountEmail', () => {
+      assert.equal(
+          GrDisplayNameUtils._accountEmail('email@gerritreview.com'),
+          '<email@gerritreview.com>');
+      assert.equal(GrDisplayNameUtils._accountEmail(undefined), '');
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider.js b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider.js
new file mode 100644
index 0000000..67001d2
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider.js
@@ -0,0 +1,46 @@
+/**
+ * @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.
+ */
+(function(window) {
+  'use strict';
+
+  if (window.GrEmailSuggestionsProvider) {
+    return;
+  }
+
+  class GrEmailSuggestionsProvider {
+    constructor(restAPI) {
+      this._restAPI = restAPI;
+    }
+
+    getSuggestions(input) {
+      return this._restAPI.getSuggestedAccounts(`${input}`)
+          .then(accounts => {
+            if (!accounts) { return []; }
+            return accounts;
+          });
+    }
+
+    makeSuggestionItem(account) {
+      return {
+        name: GrDisplayNameUtils.getAccountDisplayName(null, account, true),
+        value: {account, count: 1},
+      };
+    }
+  }
+
+  window.GrEmailSuggestionsProvider = GrEmailSuggestionsProvider;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
new file mode 100644
index 0000000..fb6b5d4
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<!--
+@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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-email-suggestions-provider</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
+<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="gr-email-suggestions-provider.js"></script>
+
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </template>
+</test-fixture>
+
+<script>
+  suite('GrEmailSuggestionsProvider tests', () => {
+    let sandbox;
+    let restAPI;
+    let provider;
+    const account1 = {
+      name: 'Some name',
+      email: 'some@example.com',
+    };
+    const account2 = {
+      email: 'other@example.com',
+      _account_id: 3,
+    };
+
+    setup(() => {
+      sandbox = sinon.sandbox.create();
+
+      stub('gr-rest-api-interface', {
+        getConfig() { return Promise.resolve({}); },
+      });
+      restAPI = fixture('basic');
+      provider = new GrEmailSuggestionsProvider(restAPI);
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    test('getSuggestions', done => {
+      const getSuggestedAccountsStub =
+          sandbox.stub(restAPI, 'getSuggestedAccounts')
+            .returns(Promise.resolve([account1, account2]));
+
+      provider.getSuggestions('Some input').then(res => {
+        assert.deepEqual(res, [account1, account2]);
+        assert.isTrue(getSuggestedAccountsStub.calledOnce);
+        assert.equal(getSuggestedAccountsStub.lastCall.args[0], 'Some input');
+        done();
+      });
+    });
+
+    test('makeSuggestionItem', () => {
+      assert.deepEqual(provider.makeSuggestionItem(account1), {
+        name: 'Some name <some@example.com>',
+        value: {
+          account: account1,
+          count: 1,
+        },
+      });
+
+      assert.deepEqual(provider.makeSuggestionItem(account2), {
+        name: 'other@example.com <other@example.com>',
+        value: {
+          account: account2,
+          count: 1,
+        },
+      });
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js
new file mode 100644
index 0000000..a95670b
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js
@@ -0,0 +1,47 @@
+/**
+ * @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.
+ */
+(function(window) {
+  'use strict';
+
+  if (window.GrGroupSuggestionsProvider) {
+    return;
+  }
+
+  class GrGroupSuggestionsProvider {
+    constructor(restAPI) {
+      this._restAPI = restAPI;
+    }
+
+    getSuggestions(input) {
+      return this._restAPI.getSuggestedGroups(`${input}`)
+          .then(groups => {
+            if (!groups) { return []; }
+            const keys = Object.keys(groups);
+            return keys.map(key => {
+              return Object.assign({}, groups[key], {name: key});
+            });
+          });
+    }
+
+    makeSuggestionItem(suggestion) {
+      return {name: suggestion.name,
+        value: {group: {name: suggestion.name, id: suggestion.id}}};
+    }
+  }
+
+  window.GrGroupSuggestionsProvider = GrGroupSuggestionsProvider;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
new file mode 100644
index 0000000..b60aaa9
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<!--
+@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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-group-suggestions-provider</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
+<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="gr-group-suggestions-provider.js"></script>
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </template>
+</test-fixture>
+
+<script>
+  suite('GrGroupSuggestionsProvider tests', () => {
+    let sandbox;
+    let restAPI;
+    let provider;
+    const group1 = {
+      name: 'Some name',
+      id: 1,
+    };
+    const group2 = {
+      name: 'Other name',
+      id: 3,
+      url: 'abcd',
+    };
+
+    setup(() => {
+      sandbox = sinon.sandbox.create();
+
+      stub('gr-rest-api-interface', {
+        getConfig() { return Promise.resolve({}); },
+      });
+      restAPI = fixture('basic');
+      provider = new GrGroupSuggestionsProvider(restAPI);
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    test('getSuggestions', done => {
+      const getSuggestedAccountsStub =
+          sandbox.stub(restAPI, 'getSuggestedGroups')
+              .returns(Promise.resolve({
+                'Some name': {id: 1},
+                'Other name': {id: 3, url: 'abcd'},
+              }));
+
+      provider.getSuggestions('Some input').then(res => {
+        assert.deepEqual(res, [group1, group2]);
+        assert.isTrue(getSuggestedAccountsStub.calledOnce);
+        assert.equal(getSuggestedAccountsStub.lastCall.args[0], 'Some input');
+        done();
+      });
+    });
+
+    test('makeSuggestionItem', () => {
+      assert.deepEqual(provider.makeSuggestionItem(group1), {
+        name: 'Some name',
+        value: {
+          group: {
+            name: 'Some name',
+            id: 1,
+          },
+        },
+      });
+
+      assert.deepEqual(provider.makeSuggestionItem(group2), {
+        name: 'Other name',
+        value: {
+          group: {
+            name: 'Other name',
+            id: 3,
+          },
+        },
+      });
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js
new file mode 100644
index 0000000..c83e5a2
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js
@@ -0,0 +1,113 @@
+/**
+ * @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.
+ */
+(function(window) {
+  'use strict';
+
+  if (window.GrReviewerSuggestionsProvider) {
+    return;
+  }
+
+  /**
+   * @enum {string}
+   */
+  Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES = {
+    REVIEWER: 'reviewers',
+    CC: 'ccs',
+    ANY: 'any',
+  };
+
+  class GrReviewerSuggestionsProvider {
+    static create(restApi, changeNumber, usersType) {
+      switch (usersType) {
+        case Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.REVIEWER:
+          return new GrReviewerSuggestionsProvider(restApi, changeNumber,
+            input => restApi.getChangeSuggestedReviewers(changeNumber, input));
+        case Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.CC:
+          return new GrReviewerSuggestionsProvider(restApi, changeNumber,
+            input => restApi.getChangeSuggestedCCs(changeNumber, input));
+        case Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY:
+          return new GrReviewerSuggestionsProvider(restApi, changeNumber,
+            input => restApi.getSuggestedAccounts(
+                `cansee:${changeNumber} ${input}`));
+        default:
+          throw new Error(`Unknown users type: ${usersType}`);
+      }
+    }
+
+    constructor(restAPI, changeNumber, apiCall) {
+      this._changeNumber = changeNumber;
+      this._apiCall = apiCall;
+      this._restAPI = restAPI;
+    }
+
+    init() {
+      if (this._initPromise) {
+        return this._initPromise;
+      }
+      const getConfigPromise = this._restAPI.getConfig().then(cfg => {
+        this._config = cfg;
+      });
+      const getLoggedInPromise = this._restAPI.getLoggedIn().then(loggedIn => {
+        this._loggedIn = loggedIn;
+      });
+      this._initPromise = Promise.all([getConfigPromise, getLoggedInPromise])
+        .then(() => {
+          this._initialized = true;
+        });
+      return this._initPromise;
+    }
+
+    getSuggestions(input) {
+      if (!this._initialized || !this._loggedIn) {
+        return Promise.resolve([]);
+      }
+
+      return this._apiCall(input)
+          .then(reviewers => (reviewers || []));
+    }
+
+    makeSuggestionItem(suggestion) {
+      if (suggestion.account) {
+        // Reviewer is an account suggestion from getChangeSuggestedReviewers.
+        return {
+          name: GrDisplayNameUtils.getAccountDisplayName(this._config,
+              suggestion.account, false),
+          value: suggestion,
+        };
+      }
+
+      if (suggestion.group) {
+        // Reviewer is a group suggestion from getChangeSuggestedReviewers.
+        return {
+          name: GrDisplayNameUtils.getGroupDisplayName(suggestion.group),
+          value: suggestion,
+        };
+      }
+
+      if (suggestion._account_id) {
+        // Reviewer is an account suggestion from getSuggestedAccounts.
+        return {
+          name: GrDisplayNameUtils.getAccountDisplayName(this._config,
+              suggestion, false),
+          value: {account: suggestion, count: 1},
+        };
+      }
+    }
+  }
+
+  window.GrReviewerSuggestionsProvider = GrReviewerSuggestionsProvider;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
new file mode 100644
index 0000000..ca3c277
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
@@ -0,0 +1,260 @@
+<!DOCTYPE html>
+<!--
+@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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-reviewer-suggestions-provider</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
+<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="gr-reviewer-suggestions-provider.js"></script>
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </template>
+</test-fixture>
+
+<script>
+  suite('GrReviewerSuggestionsProvider tests', () => {
+    let sandbox;
+    let _nextAccountId = 0;
+    const makeAccount = function(opt_status) {
+      const accountId = ++_nextAccountId;
+      return {
+        _account_id: accountId,
+        name: 'name ' + accountId,
+        email: 'email ' + accountId,
+        status: opt_status,
+      };
+    };
+    let _nextAccountId2 = 0;
+    const makeAccount2 = function(opt_status) {
+      const accountId2 = ++_nextAccountId2;
+      return {
+        _account_id: accountId2,
+        name: 'name ' + accountId2,
+        status: opt_status,
+      };
+    };
+
+    let owner;
+    let existingReviewer1;
+    let existingReviewer2;
+    let suggestion1;
+    let suggestion2;
+    let suggestion3;
+    let restAPI;
+    let provider;
+
+    let redundantSuggestion1;
+    let redundantSuggestion2;
+    let redundantSuggestion3;
+    let change;
+
+    setup(done => {
+      owner = makeAccount();
+      existingReviewer1 = makeAccount();
+      existingReviewer2 = makeAccount();
+      suggestion1 = {account: makeAccount()};
+      suggestion2 = {account: makeAccount()};
+      suggestion3 = {
+        group: {
+          id: 'suggested group id',
+          name: 'suggested group',
+        },
+      };
+
+      stub('gr-rest-api-interface', {
+        getLoggedIn() { return Promise.resolve(true); },
+        getConfig() { return Promise.resolve({}); },
+      });
+
+      restAPI = fixture('basic');
+      change = {
+        _number: 42,
+        owner,
+        reviewers: {
+          CC: [existingReviewer1],
+          REVIEWER: [existingReviewer2],
+        },
+      };
+      sandbox = sinon.sandbox.create();
+      return flush(done);
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+    suite('allowAnyUser set to false', () => {
+      setup(done => {
+        provider = GrReviewerSuggestionsProvider.create(restAPI, change._number,
+            Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.REVIEWER);
+        provider.init().then(done);
+      });
+      suite('stubbed values for _getReviewerSuggestions', () => {
+        setup(() => {
+          stub('gr-rest-api-interface', {
+            getChangeSuggestedReviewers() {
+              redundantSuggestion1 = {account: existingReviewer1};
+              redundantSuggestion2 = {account: existingReviewer2};
+              redundantSuggestion3 = {account: owner};
+              return Promise.resolve([redundantSuggestion1, redundantSuggestion2,
+                redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
+            },
+          });
+        });
+
+        test('makeSuggestionItem formats account or group accordingly', () => {
+          let account = makeAccount();
+          const account3 = makeAccount2();
+          let suggestion = provider.makeSuggestionItem({account});
+          assert.deepEqual(suggestion, {
+            name: account.name + ' <' + account.email + '>',
+            value: {account},
+          });
+
+          const group = {name: 'test'};
+          suggestion = provider.makeSuggestionItem({group});
+          assert.deepEqual(suggestion, {
+            name: group.name + ' (group)',
+            value: {group},
+          });
+
+          suggestion = provider.makeSuggestionItem(account);
+          assert.deepEqual(suggestion, {
+            name: account.name + ' <' + account.email + '>',
+            value: {account, count: 1},
+          });
+
+          suggestion = provider.makeSuggestionItem({account: {}});
+          assert.deepEqual(suggestion, {
+            name: 'Anonymous',
+            value: {account: {}},
+          });
+
+          provider._config = {
+            user: {
+              anonymous_coward_name: 'Anonymous Coward Name',
+            },
+          };
+
+          suggestion = provider.makeSuggestionItem({account: {}});
+          assert.deepEqual(suggestion, {
+            name: 'Anonymous Coward Name',
+            value: {account: {}},
+          });
+
+          account = makeAccount('OOO');
+
+          suggestion = provider.makeSuggestionItem({account});
+          assert.deepEqual(suggestion, {
+            name: account.name + ' <' + account.email + '> (OOO)',
+            value: {account},
+          });
+
+          suggestion = provider.makeSuggestionItem(account);
+          assert.deepEqual(suggestion, {
+            name: account.name + ' <' + account.email + '> (OOO)',
+            value: {account, count: 1},
+          });
+
+          sandbox.stub(GrDisplayNameUtils, '_accountEmail',
+              () => {
+                return '';
+              });
+
+          suggestion = provider.makeSuggestionItem(account3);
+          assert.deepEqual(suggestion, {
+            name: account3.name,
+            value: {account: account3, count: 1},
+          });
+        });
+
+        test('getSuggestions', done => {
+          provider.getSuggestions().then(reviewers => {
+            // Default is no filtering.
+            assert.equal(reviewers.length, 6);
+            assert.deepEqual(reviewers,
+                [redundantSuggestion1, redundantSuggestion2,
+                  redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
+          }).then(done);
+        });
+
+        test('getSuggestions short circuits when logged out', () => {
+          // API call is already stubbed.
+          const xhrSpy = restAPI.getChangeSuggestedReviewers;
+          provider._loggedIn = false;
+          return provider.getSuggestions('').then(() => {
+            assert.isFalse(xhrSpy.called);
+            provider._loggedIn = true;
+            return provider.getSuggestions('').then(() => {
+              assert.isTrue(xhrSpy.called);
+            });
+          });
+        });
+      });
+
+      test('getChangeSuggestedReviewers is used', done => {
+        const suggestReviewerStub =
+            sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
+                .returns(Promise.resolve([]));
+        const suggestAccountStub =
+            sandbox.stub(restAPI, 'getSuggestedAccounts')
+                .returns(Promise.resolve([]));
+
+        provider.getSuggestions('').then(() => {
+          assert.isTrue(suggestReviewerStub.calledOnce);
+          assert.isTrue(suggestReviewerStub.calledWith(42, ''));
+          assert.isFalse(suggestAccountStub.called);
+          done();
+        });
+      });
+    });
+
+    suite('allowAnyUser set to true', () => {
+      setup(done => {
+        provider = GrReviewerSuggestionsProvider.create(restAPI, change._number,
+            Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY);
+        provider.init().then(done);
+      });
+
+      test('getSuggestedAccounts is used', done => {
+        const suggestReviewerStub =
+            sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
+                .returns(Promise.resolve([]));
+        const suggestAccountStub =
+            sandbox.stub(restAPI, 'getSuggestedAccounts')
+                .returns(Promise.resolve([]));
+
+        provider.getSuggestions('').then(() => {
+          assert.isFalse(suggestReviewerStub.called);
+          assert.isTrue(suggestAccountStub.calledOnce);
+          assert.isTrue(suggestAccountStub.calledWith('cansee:42 '));
+          done();
+        });
+      });
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/styles/shared-styles.html b/polygerrit-ui/app/styles/shared-styles.html
index 78abe3a..14fee4f 100644
--- a/polygerrit-ui/app/styles/shared-styles.html
+++ b/polygerrit-ui/app/styles/shared-styles.html
@@ -33,6 +33,10 @@
         padding: 0;
         vertical-align: baseline;
       }
+      *::after,
+      *::before {
+        box-sizing: border-box;
+      }
       input,
       iron-autogrow-textarea {
         background-color: inherit;
diff --git a/polygerrit-ui/app/styles/themes/app-theme.html b/polygerrit-ui/app/styles/themes/app-theme.html
index 142dd82..a0cb3d8 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.html
+++ b/polygerrit-ui/app/styles/themes/app-theme.html
@@ -18,13 +18,19 @@
 html {
   /* Following vars have LTS for plugin API. */
   --primary-text-color: #000;
+  /* For backwords compatibility we keep this as --header-background-color. */
   --header-background-color: #eee;
   --header-title-content: 'Gerrit';
   --header-icon: none;
   --header-icon-size: 0em;
   --header-text-color: #000;
-  --footer-background-color: var(--header-background-color);
+  --header-title-font-size: 1.75rem;
+  --footer-background-color: #eee;
   --border-color: #ddd;
+  /* This allows to add a border in custom themes */
+  --header-border-bottom: 1px solid var(--border-color);
+  --header-border-image: '';
+  --footer-border-top: 1px solid var(--border-color);
 
   /* Following are not part of plugin API. */
   --selection-background-color: rgba(161, 194, 250, 0.1);
@@ -32,6 +38,7 @@
   --expanded-background-color: #eee;
   --view-background-color: #fff;
   --default-horizontal-margin: 1rem;
+
   --deemphasized-text-color: #757575;
   /* Used on text color for change list that doesn't need user's attention. */
   --reviewed-text-color: var(--primary-text-color);
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index 8cf1b13..2324fd5 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -20,6 +20,9 @@
       --primary-text-color: #e8eaed;
       --view-background-color: #131416;
       --border-color: #5f6368;
+      --header-border-bottom: 1px solid var(--border-color);
+      --header-border-image: '';
+      --footer-border-bottom: 1px solid var(--border-color);
       --table-header-background-color: #131416;
       --table-subheader-background-color: #3c4043;
       --header-background-color: #3c4043;
@@ -41,6 +44,7 @@
       --dropdown-background-color: var(--table-header-background-color);
       --dialog-background-color: var(--view-background-color);
       --chip-background-color: var(--table-header-background-color);
+      --header-title-font-size: 1.75rem;
 
       --select-background-color: var(--table-subheader-background-color);
 
diff --git a/polygerrit-ui/app/template_test_srcs/template_test.js b/polygerrit-ui/app/template_test_srcs/template_test.js
index ac0dbeb..ec3b7d5 100644
--- a/polygerrit-ui/app/template_test_srcs/template_test.js
+++ b/polygerrit-ui/app/template_test_srcs/template_test.js
@@ -38,6 +38,8 @@
   'SiteBasedCache',
   'FetchPromisesCache',
   'GrRestApiHelper',
+  'GrDisplayNameUtils',
+  'GrReviewerSuggestionsProvider',
   'moment',
   'page',
   'util',
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 9c06113..6cdac42 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -23,6 +23,7 @@
 <script src="/bower_components/web-component-tester/browser.js"></script>
 <script>
   const testFiles = [];
+  const scriptsPath = '../scripts/';
   const elementsPath = '../elements/';
   const behaviorsPath = '../behaviors/';
 
@@ -61,9 +62,9 @@
     'change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html',
     'change-list/gr-create-change-help/gr-create-change-help_test.html',
     'change-list/gr-dashboard-view/gr-dashboard-view_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'change-list/gr-repo-header/gr-repo-header_test.html',
     'change-list/gr-user-header/gr-user-header_test.html',
-    'change/gr-account-entry/gr-account-entry_test.html',
-    'change/gr-account-list/gr-account-list_test.html',
     'change/gr-change-actions/gr-change-actions_test.html',
     'change/gr-change-metadata/gr-change-metadata-it_test.html',
     'change/gr-change-metadata/gr-change-metadata_test.html',
@@ -105,10 +106,13 @@
     'core/gr-search-bar/gr-search-bar_test.html',
     'core/gr-smart-search/gr-smart-search_test.html',
     'diff/gr-comment-api/gr-comment-api_test.html',
+    'diff/gr-coverage-layer/gr-coverage-layer_test.html',
     'diff/gr-diff-builder/gr-diff-builder_test.html',
     'diff/gr-diff-cursor/gr-diff-cursor_test.html',
     'diff/gr-diff-highlight/gr-annotation_test.html',
     'diff/gr-diff-highlight/gr-diff-highlight_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'diff/gr-diff-host/gr-diff-host_test.html',
     'diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html',
     'diff/gr-diff-processor/gr-diff-processor_test.html',
     'diff/gr-diff-selection/gr-diff-selection_test.html',
@@ -127,6 +131,8 @@
     'plugins/gr-admin-api/gr-admin-api_test.html',
     'plugins/gr-styles-api/gr-styles-api_test.html',
     'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'plugins/gr-dom-hooks/gr-dom-hooks_test.html',
     'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html',
     'plugins/gr-event-helper/gr-event-helper_test.html',
     'plugins/gr-external-style/gr-external-style_test.html',
@@ -135,7 +141,10 @@
     'plugins/gr-popup-interface/gr-popup-interface_test.html',
     'plugins/gr-repo-api/gr-repo-api_test.html',
     'plugins/gr-settings-api/gr-settings-api_test.html',
+    'plugins/gr-theme-api/gr-theme-api_test.html',
     'settings/gr-account-info/gr-account-info_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'settings/gr-agreements-list/gr-agreements-list_test.html',
     'settings/gr-change-table-editor/gr-change-table-editor_test.html',
     'settings/gr-cla-view/gr-cla-view_test.html',
     'settings/gr-edit-preferences/gr-edit-preferences_test.html',
@@ -149,7 +158,9 @@
     'settings/gr-settings-view/gr-settings-view_test.html',
     'settings/gr-ssh-editor/gr-ssh-editor_test.html',
     'settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html',
+    'shared/gr-account-entry/gr-account-entry_test.html',
     'shared/gr-account-label/gr-account-label_test.html',
+    'shared/gr-account-list/gr-account-list_test.html',
     'shared/gr-account-link/gr-account-link_test.html',
     'shared/gr-alert/gr-alert_test.html',
     'shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html',
@@ -161,34 +172,49 @@
     'shared/gr-comment-thread/gr-comment-thread_test.html',
     'shared/gr-comment/gr-comment_test.html',
     'shared/gr-copy-clipboard/gr-copy-clipboard_test.html',
+    'shared/gr-count-string-formatter/gr-count-string-formatter_test.html',
     'shared/gr-cursor-manager/gr-cursor-manager_test.html',
     'shared/gr-date-formatter/gr-date-formatter_test.html',
     'shared/gr-dialog/gr-dialog_test.html',
     'shared/gr-diff-preferences/gr-diff-preferences_test.html',
     'shared/gr-download-commands/gr-download-commands_test.html',
+    'shared/gr-dropdown/gr-dropdown_test.html',
     'shared/gr-dropdown-list/gr-dropdown-list_test.html',
     'shared/gr-editable-content/gr-editable-content_test.html',
     'shared/gr-editable-label/gr-editable-label_test.html',
     'shared/gr-formatted-text/gr-formatted-text_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'shared/gr-hovercard/gr-hovercard_test.html',
+    'shared/gr-js-api-interface/gr-annotation-actions-context_test.html',
+    'shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html',
     'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
     'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
     'shared/gr-js-api-interface/gr-js-api-interface_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'shared/gr-js-api-interface/gr-plugin-action-context_test.html',
     'shared/gr-js-api-interface/gr-plugin-endpoints_test.html',
     'shared/gr-js-api-interface/gr-plugin-rest-api_test.html',
     'shared/gr-fixed-panel/gr-fixed-panel_test.html',
     'shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'shared/gr-label-info/gr-label-info_test.html',
     'shared/gr-lib-loader/gr-lib-loader_test.html',
     'shared/gr-limited-text/gr-limited-text_test.html',
     'shared/gr-linked-chip/gr-linked-chip_test.html',
     'shared/gr-linked-text/gr-linked-text_test.html',
     'shared/gr-list-view/gr-list-view_test.html',
+    'shared/gr-overlay/gr-overlay_test.html',
     'shared/gr-page-nav/gr-page-nav_test.html',
     'shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html',
     'shared/gr-rest-api-interface/gr-auth_test.html',
+    'shared/gr-rest-api-interface/gr-etag-decorator_test.html',
     'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
     'shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html',
     'shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'shared/gr-rest-api-interface/mock-diff-response_test.html',
     'shared/gr-select/gr-select_test.html',
+    'shared/gr-shell-command/gr-shell-command_test.html',
     'shared/gr-storage/gr-storage_test.html',
     'shared/gr-textarea/gr-textarea_test.html',
     'shared/gr-tooltip-content/gr-tooltip-content_test.html',
@@ -212,8 +238,10 @@
     'rest-client-behavior/rest-client-behavior_test.html',
     'gr-access-behavior/gr-access-behavior_test.html',
     'gr-admin-nav-behavior/gr-admin-nav-behavior_test.html',
-    'gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html',
     'gr-change-table-behavior/gr-change-table-behavior_test.html',
+    // TODO: uncomment file & fix tests. The file was missed in this list for a long time.
+    // 'gr-list-view-behavior/gr-list-view-behavior_test.html',
+    'gr-display-name-behavior/gr-display-name-behavior_test.html',
     'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
     'gr-path-list-behavior/gr-path-list-behavior_test.html',
     'gr-tooltip-behavior/gr-tooltip-behavior_test.html',
@@ -227,5 +255,17 @@
     testFiles.push(file);
   }
 
+  const scripts = [
+    'gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html',
+    'gr-group-suggestions-provider/gr-group-suggestions-provider_test.html',
+    'gr-display-name-utils/gr-display-name-utils_test.html',
+    'gr-email-suggestions-provider/gr-email-suggestions-provider_test.html',
+  ];
+  /* eslint-enable max-len */
+  for (let file of scripts) {
+    file = scriptsPath + file;
+    testFiles.push(file);
+  }
+
   WCT.loadSuites(testFiles);
 </script>
diff --git a/prologtests/examples/BUILD b/prologtests/examples/BUILD
index 4048bc7..7244f1e 100644
--- a/prologtests/examples/BUILD
+++ b/prologtests/examples/BUILD
@@ -1,7 +1,15 @@
 package(default_visibility = ["//visibility:public"])
 
+DUMMY = ["dummy.sh"]
+
+# Enable prologtests on newer Java versions again, when this Bazel bug is fixed:
+# https://github.com/bazelbuild/bazel/issues/9391
 sh_test(
     name = "test_examples",
-    srcs = ["run.sh"],
+    srcs = select({
+        "//:java9": DUMMY,
+        "//:java_next": DUMMY,
+        "//conditions:default": ["run.sh"],
+    }),
     data = glob(["*.pl"]) + ["//:gerrit.war"],
 )
diff --git a/prologtests/examples/dummy.sh b/prologtests/examples/dummy.sh
new file mode 100755
index 0000000..2e0cca3
--- /dev/null
+++ b/prologtests/examples/dummy.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Skip all prolog tests for newer Java versions.
+# See https://github.com/bazelbuild/bazel/issues/9391
+# for more details why we cannot support running tests
+# on newer Java versions for now.
diff --git a/tools/BUILD b/tools/BUILD
index 29626d9..5531c3e 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -29,6 +29,9 @@
 # enabled. This warnings list is originally based on:
 # https://github.com/bazelbuild/BUILD_file_generator/blob/master/tools/bazel_defs/java.bzl
 # However, feel free to add any additional errors. Thus far they have all been pretty useful.
+# TODO(davido): Enable ImmutableAnnotationChecker again when these issues are fixed:
+# https://github.com/google/error-prone/issues/1348
+# https://github.com/bazelbuild/bazel/issues/9378
 java_package_configuration(
     name = "error_prone",
     javacopts = [
@@ -55,7 +58,7 @@
         "-Xep:FunctionalInterfaceClash:ERROR",
         "-Xep:FutureReturnValueIgnored:ERROR",
         "-Xep:GetClassOnEnum:ERROR",
-        "-Xep:ImmutableAnnotationChecker:ERROR",
+        "-Xep:ImmutableAnnotationChecker:OFF",
         "-Xep:ImmutableEnumChecker:ERROR",
         "-Xep:IncompatibleModifiers:ERROR",
         "-Xep:InjectOnConstructorOfAbstractClass:ERROR",
diff --git a/tools/bzl/javadoc.bzl b/tools/bzl/javadoc.bzl
index 77c2d4a..521cebe 100644
--- a/tools/bzl/javadoc.bzl
+++ b/tools/bzl/javadoc.bzl
@@ -61,14 +61,14 @@
         command = " && ".join(cmd),
     )
 
-java_doc = rule(
+_java_doc = rule(
     attrs = {
         "external_docs": attr.string_list(),
         "libs": attr.label_list(allow_files = False),
         "pkgs": attr.string_list(),
         "title": attr.string(),
         "_jdk": attr.label(
-            default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
+            default = Label("@bazel_tools//tools/jdk:current_host_java_runtime"),
             allow_files = True,
             providers = [java_common.JavaRuntimeInfo],
         ),
@@ -76,3 +76,16 @@
     outputs = {"zip": "%{name}.zip"},
     implementation = _impl,
 )
+
+def java_doc(**kwargs):
+    libs = kwargs.get("libs", [])
+    libs = libs + select({
+        "//:java9": [],
+        "//:java_next": [],
+        # TODO(davido): Remove this dependency, when Java 8 support is removed.
+        # auto-value generates @javax.annotation.Generated annotation on generated
+        # classes when Java 8 source compatibility level is used, but Java 11 and
+        # later don't have this class any more.
+        "//conditions:default": ["//lib:javax-annotation"],
+    })
+    _java_doc(**dict(kwargs, libs = libs))