Merge "Merge branch 'stable-3.2'"
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index bd764b8..3b905d7 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -401,7 +401,7 @@
 
     Set<Account.Id> currentAccountUpdates =
         plannedAttentionSetUpdates.values().stream()
-            .map(u -> u.account())
+            .map(AttentionSetUpdate::account)
             .collect(Collectors.toSet());
     updates.stream()
         .filter(u -> !currentAccountUpdates.contains(u.account()))
@@ -754,7 +754,7 @@
             a ->
                 AttentionSetUpdate.createForWrite(
                     a.account(), AttentionSetUpdate.Operation.REMOVE, reason))
-        .forEach(a -> addToPlannedAttentionSetUpdates(a));
+        .forEach(this::addToPlannedAttentionSetUpdates);
   }
 
   private void applyReviewerUpdatesToAttentionSet() {
@@ -791,7 +791,7 @@
             r ->
                 AttentionSetUpdate.createForWrite(
                     r, AttentionSetUpdate.Operation.ADD, "Change was marked ready for review"))
-        .forEach(a -> addToPlannedAttentionSetUpdates(a));
+        .forEach(this::addToPlannedAttentionSetUpdates);
   }
 
   /**
diff --git a/polygerrit-ui/app/.eslintrc.js b/polygerrit-ui/app/.eslintrc.js
index e7506e0..ab9732a 100644
--- a/polygerrit-ui/app/.eslintrc.js
+++ b/polygerrit-ui/app/.eslintrc.js
@@ -219,7 +219,7 @@
       }
     },
     {
-      "files": ["samples/**/*.js", "**/test/plugin.html"],
+      "files": ["samples/**/*.js"],
       "globals": {
         // Settings for samples. You can add globals here if you want to use it
         "Gerrit": "readonly",
diff --git a/polygerrit-ui/app/elements/custom-dark-theme_test.js b/polygerrit-ui/app/elements/custom-dark-theme_test.js
index 31c78a1..cc955ce 100644
--- a/polygerrit-ui/app/elements/custom-dark-theme_test.js
+++ b/polygerrit-ui/app/elements/custom-dark-theme_test.js
@@ -16,32 +16,45 @@
  */
 
 import '../test/common-test-setup-karma.js';
+import {getComputedStyleValue} from '../utils/dom-util.js';
 import './shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import './gr-app.js';
 import {pluginLoader} from './shared/gr-js-api-interface/gr-plugin-loader.js';
+import {removeTheme} from '../styles/themes/dark-theme.js';
+
 const basicFixture = fixtureFromElement('gr-app');
+
 suite('gr-app custom dark theme tests', () => {
   let sandbox;
+  let element;
   setup(done => {
     window.localStorage.setItem('dark-theme', 'true');
     sandbox = sinon.sandbox.create();
-    basicFixture.instantiate();
+    element = basicFixture.instantiate();
     pluginLoader.loadPlugins([]);
     pluginLoader.awaitPluginsLoaded().then(() => flush(done));
   });
+
   teardown(() => {
     sandbox.restore();
     window.localStorage.removeItem('dark-theme');
-    const addedTheme = document
-        .querySelector('link[href="/styles/themes/dark-theme.html"]');
-    if (addedTheme) {
-      addedTheme.remove();
-    }
+    removeTheme();
   });
 
   test('should tried to load dark theme', () => {
     assert.isTrue(
-        !!document.querySelector('link[href="/styles/themes/dark-theme.html"]')
+        !!document.head.querySelector('#dark-theme')
     );
   });
+
+  test('applies the right theme', () => {
+    assert.equal(
+        getComputedStyleValue('--header-background-color', element)
+            .toLowerCase(),
+        '#3b3d3f');
+    assert.equal(
+        getComputedStyleValue('--footer-background-color', element)
+            .toLowerCase(),
+        '#3b3d3f');
+  });
 });
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/custom-light-theme_test.js b/polygerrit-ui/app/elements/custom-light-theme_test.js
index fa1f93f..a866bb3 100644
--- a/polygerrit-ui/app/elements/custom-light-theme_test.js
+++ b/polygerrit-ui/app/elements/custom-light-theme_test.js
@@ -20,6 +20,7 @@
 import './shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import './gr-app.js';
 import {pluginLoader} from './shared/gr-js-api-interface/gr-plugin-loader.js';
+
 const basicFixture = fixtureFromElement('gr-app');
 
 suite('gr-app custom light theme tests', () => {
@@ -45,17 +46,12 @@
   });
 
   test('should not load dark theme', () => {
-    assert.isFalse(
-        !!document.querySelector('link[href="/styles/themes/dark-theme.html"]')
-    );
+    assert.isFalse(!!document.head.querySelector('#dark-theme'));
+    assert.isTrue(!!document.head.querySelector('#light-theme'));
   });
 
   test('applies the right theme', () => {
     assert.equal(
-        getComputedStyleValue('--primary-text-color', element)
-            .toLowerCase(),
-        'black');
-    assert.equal(
         getComputedStyleValue('--header-background-color', element)
             .toLowerCase(),
         '#f1f3f4');
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
index 4915eda..91af97c 100644
--- a/polygerrit-ui/app/elements/gr-app-element.js
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -16,6 +16,7 @@
  */
 import '../styles/shared-styles.js';
 import '../styles/themes/app-theme.js';
+import {applyTheme as applyDarkTheme} from '../styles/themes/dark-theme.js';
 import './admin/gr-admin-view/gr-admin-view.js';
 import './documentation/gr-documentation-search/gr-documentation-search.js';
 import './change-list/gr-change-list-view/gr-change-list-view.js';
@@ -202,9 +203,7 @@
     });
 
     if (window.localStorage.getItem('dark-theme')) {
-      // No need to add the style module to element again as it's imported
-      // by importHref already
-      this.$.libLoader.getDarkTheme();
+      applyDarkTheme();
     }
 
     // Note: this is evaluated here to ensure that it only happens after the
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
index 95f7a2c..0e8c68a 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
@@ -20,6 +20,7 @@
 import '../../../styles/gr-menu-page-styles.js';
 import '../../../styles/gr-page-nav-styles.js';
 import '../../../styles/shared-styles.js';
+import {applyTheme as applyDarkTheme, removeTheme as removeDarkTheme} from '../../../styles/themes/dark-theme.js';
 import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.js';
 import '../gr-change-table-editor/gr-change-table-editor.js';
 import '../../shared/gr-button/gr-button.js';
@@ -68,8 +69,6 @@
 const ABSOLUTE_URL_PATTERN = /^https?:/;
 const TRAILING_SLASH_PATTERN = /\/$/;
 
-const RELOAD_MESSAGE = 'Reloading...';
-
 const HTTP_AUTH = [
   'HTTP',
   'HTTP_LDAP',
@@ -475,17 +474,19 @@
   _handleToggleDark() {
     if (this._isDark) {
       window.localStorage.removeItem('dark-theme');
+      removeDarkTheme();
     } else {
       window.localStorage.setItem('dark-theme', 'true');
+      applyDarkTheme();
     }
+    this._isDark = !!window.localStorage.getItem('dark-theme');
     this.dispatchEvent(new CustomEvent('show-alert', {
-      detail: {message: RELOAD_MESSAGE},
+      detail: {
+        message: `Theme changed to ${this._isDark ? 'dark' : 'light'}.`,
+      },
       bubbles: true,
       composed: true,
     }));
-    this.async(() => {
-      window.location.reload();
-    }, 1);
   }
 
   _showHttpAuth(config) {
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
similarity index 90%
rename from polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
rename to polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
index e430ecb..09ad0e3 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
@@ -1,46 +1,28 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2016 The Android Open Source Project
+/**
+ * @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.
+ */
 
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<meta charset="utf-8">
-<title>gr-settings-view</title>
-
-<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-
-<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
-<script src="/components/wct-browser-legacy/browser.js"></script>
-
-<test-fixture id="basic">
-  <template>
-    <gr-settings-view></gr-settings-view>
-  </template>
-</test-fixture>
-
-<test-fixture id="blank">
-  <template>
-    <div></div>
-  </template>
-</test-fixture>
-
-<script type="module">
-import '../../../test/common-test-setup.js';
+import '../../../test/common-test-setup-karma.js';
+import {getComputedStyleValue} from '../../../utils/dom-util.js';
 import './gr-settings-view.js';
 import {flush, dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
+
+const basicFixture = fixtureFromElement('gr-settings-view');
+const blankFixture = fixtureFromElement('div');
+
 suite('gr-settings-view tests', () => {
   let element;
   let account;
@@ -112,7 +94,7 @@
       getConfig() { return Promise.resolve(config); },
       getAccountGroups() { return Promise.resolve([]); },
     });
-    element = fixture('basic');
+    element = basicFixture.instantiate();
 
     // Allow the element to render.
     element._loadingPromise.then(done);
@@ -122,6 +104,20 @@
     sandbox.restore();
   });
 
+  test('theme changing', () => {
+    window.localStorage.removeItem('dark-theme');
+    assert.isFalse(window.localStorage.getItem('dark-theme') === 'true');
+    const themeToggle = element.shadowRoot
+        .querySelector('.darkToggle paper-toggle-button');
+    MockInteractions.tap(themeToggle);
+    assert.isTrue(window.localStorage.getItem('dark-theme') === 'true');
+    assert.equal(
+        getComputedStyleValue('--primary-text-color', document.body), '#e8eaed'
+    );
+    MockInteractions.tap(themeToggle);
+    assert.isFalse(window.localStorage.getItem('dark-theme') === 'true');
+  });
+
   test('calls the title-change event', () => {
     const titleChangedStub = sandbox.stub();
 
@@ -129,8 +125,7 @@
     const newElement = document.createElement('gr-settings-view');
     newElement.addEventListener('title-change', titleChangedStub);
 
-    // Attach it to the fixture.
-    const blank = fixture('blank');
+    const blank = blankFixture.instantiate();
     blank.appendChild(newElement);
 
     flush();
@@ -528,5 +523,4 @@
       resolveConfirm('bar');
     });
   });
-});
-</script>
+});
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
index e9cd441..3c3fc6a 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 import '../gr-js-api-interface/gr-js-api-interface.js';
-import {importHref} from '../../../scripts/import-href.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
@@ -23,7 +22,6 @@
 import {htmlTemplate} from './gr-lib-loader_html.js';
 
 const HLJS_PATH = 'bower_components/highlightjs/highlight.min.js';
-const DARK_THEME_PATH = 'styles/themes/dark-theme.html';
 
 /** @extends PolymerElement */
 class GrLibLoader extends GestureEventListeners(
@@ -76,28 +74,6 @@
   }
 
   /**
-   * Loads the dark theme document. Returns a promise that resolves with a
-   * custom-style DOM element.
-   *
-   * @return {!Promise<Element>}
-   * @suppress {checkTypes}
-   */
-  getDarkTheme() {
-    return new Promise((resolve, reject) => {
-      importHref(
-          this._getLibRoot() + DARK_THEME_PATH, () => {
-            const module = document.createElement('style');
-            module.setAttribute('include', 'dark-theme');
-            const cs = document.createElement('custom-style');
-            cs.appendChild(module);
-
-            resolve(cs);
-          },
-          reject);
-    });
-  }
-
-  /**
    * Execute callbacks awaiting the HLJS lib load.
    */
   _onHLJSLibLoaded() {
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.js
similarity index 68%
rename from polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
rename to polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.js
index f2e5e3d..6208252 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.js
@@ -1,39 +1,25 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2016 The Android Open Source Project
+/**
+ * @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.
+ */
 
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<meta charset="utf-8">
-<title>gr-lib-loader</title>
-
-<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-
-<script src="/node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js"></script>
-<script src="/components/wct-browser-legacy/browser.js"></script>
-
-<test-fixture id="basic">
-  <template>
-    <gr-lib-loader></gr-lib-loader>
-  </template>
-</test-fixture>
-
-<script type="module">
-import '../../../test/common-test-setup.js';
+import '../../../test/common-test-setup-karma.js';
 import './gr-lib-loader.js';
+
+const basicFixture = fixtureFromElement('gr-lib-loader');
+
 suite('gr-lib-loader tests', () => {
   let sandbox;
   let element;
@@ -42,7 +28,7 @@
 
   setup(() => {
     sandbox = sinon.sandbox.create();
-    element = fixture('basic');
+    element = basicFixture.instantiate();
 
     loadStub = sandbox.stub(element, '_loadScript', () =>
       new Promise(resolve => resolveLoad = resolve)
@@ -144,5 +130,4 @@
       });
     });
   });
-});
-</script>
+});
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/test/plugin.html b/polygerrit-ui/app/elements/test/plugin.html
deleted file mode 100644
index ecd9007..0000000
--- a/polygerrit-ui/app/elements/test/plugin.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<dom-module id="my-plugin">
-  <script>
-    Gerrit.install(plugin => {
-      plugin.registerStyleModule('app-theme', 'myplugin-app-theme');
-      plugin.registerStyleModule('app-theme-light', 'myplugin-app-theme-light');
-      plugin.registerStyleModule('app-theme-dark', 'myplugin-app-theme-dark');
-    });
-  </script>
-</dom-module>
-
-<dom-module id="myplugin-app-theme">
-  <template>
-    <style>
-      html {
-        --primary-text-color: #F00BAA;
-      }
-    </style>
-  </template>
-</dom-module>
-
-<dom-module id="myplugin-app-theme-light">
-  <template>
-    <style>
-      html {
-        --header-background-color: #F01BAA;
-        --header-title-content: "MyGerrit";
-        --footer-background-color: #F02BAA;
-      }
-    </style>
-  </template>
-</dom-module>
-
-<dom-module id="myplugin-app-theme-dark">
-  <template>
-    <style>
-      html {
-        --primary-text-color: red;
-        --header-background-color: black;
-        --header-title-content: "MyGerrit Dark";
-        --footer-background-color: yellow;
-      }
-    </style>
-  </template>
-</dom-module>
diff --git a/polygerrit-ui/app/embed/README.md b/polygerrit-ui/app/embed/README.md
index 8860878..4e1677de 100644
--- a/polygerrit-ui/app/embed/README.md
+++ b/polygerrit-ui/app/embed/README.md
@@ -10,4 +10,4 @@
 
 All supported attributes defined in `polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js`, you can pass them by just assigning them to the `gr-app` element.
 
-To customize the style of the diff, you can use `css variables`, all supported variables defined in `polygerrit-ui/app/styles/themes/app-theme.html` and `polygerrit-ui/app/styles/themes/dark-theme.html`.
+To customize the style of the diff, you can use `css variables`, all supported variables defined in `polygerrit-ui/app/styles/themes/app-theme.js` and `polygerrit-ui/app/styles/themes/dark-theme.js`.
diff --git a/polygerrit-ui/app/rules.bzl b/polygerrit-ui/app/rules.bzl
index bc2830d..5ccf92d 100644
--- a/polygerrit-ui/app/rules.bzl
+++ b/polygerrit-ui/app/rules.bzl
@@ -46,15 +46,6 @@
     )
 
     native.filegroup(
-        name = name + "_theme_sources",
-        srcs = native.glob(
-            ["styles/themes/*.html"],
-            # app-theme.html already included via an import in gr-app.html.
-            exclude = ["styles/themes/app-theme.html"],
-        ),
-    )
-
-    native.filegroup(
         name = name + "_top_sources",
         srcs = [
             "favicon.ico",
@@ -68,7 +59,6 @@
         srcs = [
             name + "_app_sources",
             name + "_css_sources",
-            name + "_theme_sources",
             name + "_top_sources",
             "//lib/fonts:robotofonts",
             "//lib/js:highlightjs__files",
@@ -84,7 +74,6 @@
             "cp $(locations //lib/fonts:robotofonts) $$TMP/polygerrit_ui/fonts/",
             "for f in $(locations " + name + "_top_sources); do cp $$f $$TMP/polygerrit_ui/; done",
             "for f in $(locations " + name + "_css_sources); do cp $$f $$TMP/polygerrit_ui/styles; done",
-            "for f in $(locations " + name + "_theme_sources); do cp $$f $$TMP/polygerrit_ui/styles/themes; done",
             "for f in $(locations //lib/js:highlightjs__files); do cp $$f $$TMP/polygerrit_ui/bower_components/highlightjs/ ; done",
             "cp $(location @ui_npm//:node_modules/@webcomponents/webcomponentsjs/webcomponents-lite.js) $$TMP/polygerrit_ui/bower_components/webcomponentsjs/webcomponents-lite.js",
             "cp $$FONT_DIR/roboto/*.ttf $$TMP/polygerrit_ui/fonts/roboto/",
diff --git a/polygerrit-ui/app/styles/themes/app-theme.js b/polygerrit-ui/app/styles/themes/app-theme.js
index 718d6a5..1a8296f 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.js
+++ b/polygerrit-ui/app/styles/themes/app-theme.js
@@ -16,217 +16,211 @@
  */
 const $_documentContainer = document.createElement('template');
 
-$_documentContainer.innerHTML = `<custom-style><style is="custom-style">
-html {
-  /**
-   * When adding a new color variable make sure to also add it to the other
-   * theme files in the same directory.
-   *
-   * For colors prefer lower case hex colors.
-   *
-   * Note that plugins might be using these variables, so removing a variable
-   * can be a breaking change that should go into the release notes.
-   */
-
-  /* text colors */
-  --primary-text-color: black;
-  --link-color: #2a66d9;
-  --comment-text-color: black;
-  --deemphasized-text-color: #5F6368;
-  --default-button-text-color: #2a66d9;
-  --error-text-color: red;
-  --primary-button-text-color: white;
-    /* Used on text color for change list that doesn't need user's attention. */
-  --reviewed-text-color: black;
-  --vote-text-color: black;
-  --status-text-color: white;
-  --tooltip-text-color: white;
-  --negative-red-text-color: #d93025;
-  --positive-green-text-color: #188038;
-  
-  /* background colors */
-  /* primary background colors */
-  --background-color-primary: #ffffff;
-  --background-color-secondary: #f8f9fa;
-  --background-color-tertiary: #f1f3f4;
-  /* directly derived from primary background colors */
-  --chip-background-color: var(--background-color-tertiary);
-  --default-button-background-color: var(--background-color-primary);
-  --dialog-background-color: var(--background-color-primary);
-  --dropdown-background-color: var(--background-color-primary);
-  --expanded-background-color: var(--background-color-tertiary);
-  --select-background-color: var(--background-color-secondary);
-  --shell-command-background-color: var(--background-color-secondary);
-  --shell-command-decoration-background-color: var(--background-color-tertiary);
-  --table-header-background-color: var(--background-color-secondary);
-  --table-subheader-background-color: var(--background-color-tertiary);
-  --view-background-color: var(--background-color-primary);
-  /* unique background colors */
-  --assignee-highlight-color: #fcfad6;
-  --edit-mode-background-color: #ebf5fb;
-  --emphasis-color: #fff9c4;
-  --hover-background-color: rgba(161, 194, 250, 0.2);
-  --disabled-button-background-color: #e8eaed;
-  --primary-button-background-color: #2a66d9;
-  --selection-background-color: rgba(161, 194, 250, 0.1);
-  --tooltip-background-color: #333;
-  /* comment background colors */
-  --comment-background-color: #e8eaed;
-  --robot-comment-background-color: #e8f0fe;
-  --unresolved-comment-background-color: #fef7e0;
-  /* vote background colors */
-  --vote-color-approved: #9fcc6b;
-  --vote-color-disliked: #f7c4cb;
-  --vote-color-neutral: #ebf5fb;
-  --vote-color-recommended: #c9dfaf;
-  --vote-color-rejected: #f7a1ad;
-
-  /* misc colors */
-  --border-color: #e8e8e8;
-  --comment-separator-color: #dadce0;
-
-  /* status colors */
-  --status-merged: #188038;
-  --status-abandoned: #5f6368;
-  --status-wip: #795548;
-  --status-private: #a142f4;
-  --status-conflict: #d93025;
-  --status-active: #1976d2;
-  --status-ready: #b80672;
-  --status-custom: #681da8;
-
-  /* fonts */
-  --font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-  --header-font-family: 'Open Sans', 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-  --monospace-font-family: 'Roboto Mono', 'SF Mono', 'Lucida Console', Monaco, monospace;
-  --font-size-code: 12px;     /* 12px mono */
-  --font-size-mono: .929rem;  /* 13px mono */
-  --font-size-small: .857rem; /* 12px */
-  --font-size-normal: 1rem;   /* 14px */
-  --font-size-h3: 1.143rem;   /* 16px */
-  --font-size-h2: 1.429rem;   /* 20px */
-  --font-size-h1: 1.714rem;   /* 24px */
-  --line-height-code: 1.334;      /* 16px */
-  --line-height-mono: 1.286rem;   /* 18px */
-  --line-height-small: 1.143rem;  /* 16px */
-  --line-height-normal: 1.429rem; /* 20px */
-  --line-height-h3: 1.714rem;     /* 24px */
-  --line-height-h2: 2rem;         /* 28px */
-  --line-height-h1: 2.286rem;     /* 32px */
-  --font-weight-normal: 400; /* 400 is the same as 'normal' */
-  --font-weight-bold: 500;
-  --font-weight-h1: 400;
-  --font-weight-h2: 400;
-  --font-weight-h3: 400;
-
-  /* spacing */
-  --spacing-xxs: 1px;
-  --spacing-xs: 2px;
-  --spacing-s: 4px;
-  --spacing-m: 8px;
-  --spacing-l: 12px;
-  --spacing-xl: 16px;
-  --spacing-xxl: 24px;
-
-  /* header and footer */
-  --footer-background-color: transparent;
-  --footer-border-top: none;
-  --header-background-color: var(--background-color-tertiary);
-  --header-border-bottom: 1px solid var(--border-color);
-  --header-border-image: '';
-  --header-box-shadow: none;
-  --header-padding: 0 var(--spacing-l);
-  --header-icon-size: 0em;
-  --header-icon: none;
-  --header-text-color: black;
-  --header-title-content: 'Gerrit';
-  --header-title-font-size: 1.75rem;
-
-  /* diff colors */
-  --dark-add-highlight-color: #aaf2aa;
-  --dark-rebased-add-highlight-color: #d7d7f9;
-  --dark-rebased-remove-highlight-color: #f7e8b7;
-  --dark-remove-highlight-color: #ffcdd2;
-  --diff-blank-background-color: var(--background-color-secondary);
-  --diff-context-control-background-color: #fff7d4;
-  --diff-context-control-border-color: #f6e6a5;
-  --diff-context-control-color: var(--deemphasized-text-color);
-  --diff-highlight-range-color: rgba(255, 213, 0, 0.5);
-  --diff-highlight-range-hover-color: rgba(255, 255, 0, 0.5);
-  --diff-selection-background-color: #c7dbf9;
-  --diff-tab-indicator-color: var(--deemphasized-text-color);
-  --diff-trailing-whitespace-indicator: #ff9ad2;
-  --light-add-highlight-color: #d8fed8;
-  --light-rebased-add-highlight-color: #eef;
-  --light-remove-add-highlight-color: #fff8dc;
-  --light-remove-highlight-color: #ffebee;
-  --coverage-covered: #e0f2f1;
-  --coverage-not-covered: #ffd1a4;
-
-  /* syntax colors */
-  --syntax-attr-color: #219;
-  --syntax-attribute-color: var(--primary-text-color);
-  --syntax-built_in-color: #30a;
-  --syntax-comment-color: #3f7f5f;
-  --syntax-default-color: var(--primary-text-color);
-  --syntax-doctag-weight: bold;
-  --syntax-function-color: var(--primary-text-color);
-  --syntax-keyword-color: #9e0069;
-  --syntax-link-color: #219;
-  --syntax-literal-color: #219;
-  --syntax-meta-color: #ff1717;
-  --syntax-meta-keyword-color: #219;
-  --syntax-number-color: #164;
-  --syntax-params-color: var(--primary-text-color);
-  --syntax-regexp-color: #fa8602;
-  --syntax-selector-attr-color: #fa8602;
-  --syntax-selector-class-color: #164;
-  --syntax-selector-id-color: #2a00ff;
-  --syntax-selector-pseudo-color: #fa8602;
-  --syntax-string-color: #2a00ff;
-  --syntax-tag-color: #170;
-  --syntax-template-tag-color: #fa8602;
-  --syntax-template-variable-color: #0000c0;
-  --syntax-title-color: #0000c0;
-  --syntax-type-color: #2a66d9;
-  --syntax-variable-color: var(--primary-text-color);
-
-  /* elevation */
-  --elevation-level-1: 0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 1px 3px 1px rgba(60, 64, 67, .15);
-  --elevation-level-2: 0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);
-  --elevation-level-3: 0px 1px 3px 0px rgba(60, 64, 67, .30), 0px 4px 8px 3px rgba(60, 64, 67, .15);
-  --elevation-level-4: 0px 2px 3px 0px rgba(60, 64, 67, .30), 0px 6px 10px 4px rgba(60, 64, 67, .15);
-  --elevation-level-5: 0px 4px 4px 0px rgba(60, 64, 67, .30), 0px 8px 12px 6px rgba(60, 64, 67, .15);
-
-  /* misc */
-  --border-radius: 4px;
-  --reply-overlay-z-index: 1000;
-
-  /* paper and iron component overrides */
-  --iron-overlay-backdrop-background-color: black;
-  --iron-overlay-backdrop-opacity: 0.32;
-  --iron-overlay-backdrop: {
-    transition: none;
-  };
-}
-@media screen and (max-width: 50em) {
+$_documentContainer.innerHTML = `
+<custom-style id="light-theme"><style is="custom-style">
   html {
+    /**
+     * When adding a new color variable make sure to also add it to the other
+     * theme files in the same directory.
+     *
+     * For colors prefer lower case hex colors.
+     *
+     * Note that plugins might be using these variables, so removing a variable
+     * can be a breaking change that should go into the release notes.
+     */
+  
+    /* text colors */
+    --primary-text-color: black;
+    --link-color: #2a66d9;
+    --comment-text-color: black;
+    --deemphasized-text-color: #5F6368;
+    --default-button-text-color: #2a66d9;
+    --error-text-color: red;
+    --primary-button-text-color: white;
+      /* Used on text color for change list that doesn't need user's attention. */
+    --reviewed-text-color: black;
+    --vote-text-color: black;
+    --status-text-color: white;
+    --tooltip-text-color: white;
+    --negative-red-text-color: #d93025;
+    --positive-green-text-color: #188038;
+    
+    /* background colors */
+    /* primary background colors */
+    --background-color-primary: #ffffff;
+    --background-color-secondary: #f8f9fa;
+    --background-color-tertiary: #f1f3f4;
+    /* directly derived from primary background colors */
+    --chip-background-color: var(--background-color-tertiary);
+    --default-button-background-color: var(--background-color-primary);
+    --dialog-background-color: var(--background-color-primary);
+    --dropdown-background-color: var(--background-color-primary);
+    --expanded-background-color: var(--background-color-tertiary);
+    --select-background-color: var(--background-color-secondary);
+    --shell-command-background-color: var(--background-color-secondary);
+    --shell-command-decoration-background-color: var(--background-color-tertiary);
+    --table-header-background-color: var(--background-color-secondary);
+    --table-subheader-background-color: var(--background-color-tertiary);
+    --view-background-color: var(--background-color-primary);
+    /* unique background colors */
+    --assignee-highlight-color: #fcfad6;
+    --edit-mode-background-color: #ebf5fb;
+    --emphasis-color: #fff9c4;
+    --hover-background-color: rgba(161, 194, 250, 0.2);
+    --disabled-button-background-color: #e8eaed;
+    --primary-button-background-color: #2a66d9;
+    --selection-background-color: rgba(161, 194, 250, 0.1);
+    --tooltip-background-color: #333;
+    /* comment background colors */
+    --comment-background-color: #e8eaed;
+    --robot-comment-background-color: #e8f0fe;
+    --unresolved-comment-background-color: #fef7e0;
+    /* vote background colors */
+    --vote-color-approved: #9fcc6b;
+    --vote-color-disliked: #f7c4cb;
+    --vote-color-neutral: #ebf5fb;
+    --vote-color-recommended: #c9dfaf;
+    --vote-color-rejected: #f7a1ad;
+  
+    /* misc colors */
+    --border-color: #e8e8e8;
+    --comment-separator-color: #dadce0;
+  
+    /* status colors */
+    --status-merged: #188038;
+    --status-abandoned: #5f6368;
+    --status-wip: #795548;
+    --status-private: #a142f4;
+    --status-conflict: #d93025;
+    --status-active: #1976d2;
+    --status-ready: #b80672;
+    --status-custom: #681da8;
+  
+    /* fonts */
+    --font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
+    --header-font-family: 'Open Sans', 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
+    --monospace-font-family: 'Roboto Mono', 'SF Mono', 'Lucida Console', Monaco, monospace;
+    --font-size-code: 12px;     /* 12px mono */
+    --font-size-mono: .929rem;  /* 13px mono */
+    --font-size-small: .857rem; /* 12px */
+    --font-size-normal: 1rem;   /* 14px */
+    --font-size-h3: 1.143rem;   /* 16px */
+    --font-size-h2: 1.429rem;   /* 20px */
+    --font-size-h1: 1.714rem;   /* 24px */
+    --line-height-code: 1.334;      /* 16px */
+    --line-height-mono: 1.286rem;   /* 18px */
+    --line-height-small: 1.143rem;  /* 16px */
+    --line-height-normal: 1.429rem; /* 20px */
+    --line-height-h3: 1.714rem;     /* 24px */
+    --line-height-h2: 2rem;         /* 28px */
+    --line-height-h1: 2.286rem;     /* 32px */
+    --font-weight-normal: 400; /* 400 is the same as 'normal' */
+    --font-weight-bold: 500;
+    --font-weight-h1: 400;
+    --font-weight-h2: 400;
+    --font-weight-h3: 400;
+  
+    /* spacing */
     --spacing-xxs: 1px;
-    --spacing-xs: 1px;
-    --spacing-s: 2px;
-    --spacing-m: 4px;
-    --spacing-l: 8px;
-    --spacing-xl: 12px;
-    --spacing-xxl: 16px;
+    --spacing-xs: 2px;
+    --spacing-s: 4px;
+    --spacing-m: 8px;
+    --spacing-l: 12px;
+    --spacing-xl: 16px;
+    --spacing-xxl: 24px;
+  
+    /* header and footer */
+    --footer-background-color: transparent;
+    --footer-border-top: none;
+    --header-background-color: var(--background-color-tertiary);
+    --header-border-bottom: 1px solid var(--border-color);
+    --header-border-image: '';
+    --header-box-shadow: none;
+    --header-padding: 0 var(--spacing-l);
+    --header-icon-size: 0em;
+    --header-icon: none;
+    --header-text-color: black;
+    --header-title-content: 'Gerrit';
+    --header-title-font-size: 1.75rem;
+  
+    /* diff colors */
+    --dark-add-highlight-color: #aaf2aa;
+    --dark-rebased-add-highlight-color: #d7d7f9;
+    --dark-rebased-remove-highlight-color: #f7e8b7;
+    --dark-remove-highlight-color: #ffcdd2;
+    --diff-blank-background-color: var(--background-color-secondary);
+    --diff-context-control-background-color: #fff7d4;
+    --diff-context-control-border-color: #f6e6a5;
+    --diff-context-control-color: var(--deemphasized-text-color);
+    --diff-highlight-range-color: rgba(255, 213, 0, 0.5);
+    --diff-highlight-range-hover-color: rgba(255, 255, 0, 0.5);
+    --diff-selection-background-color: #c7dbf9;
+    --diff-tab-indicator-color: var(--deemphasized-text-color);
+    --diff-trailing-whitespace-indicator: #ff9ad2;
+    --light-add-highlight-color: #d8fed8;
+    --light-rebased-add-highlight-color: #eef;
+    --light-remove-add-highlight-color: #fff8dc;
+    --light-remove-highlight-color: #ffebee;
+    --coverage-covered: #e0f2f1;
+    --coverage-not-covered: #ffd1a4;
+  
+    /* syntax colors */
+    --syntax-attr-color: #219;
+    --syntax-attribute-color: var(--primary-text-color);
+    --syntax-built_in-color: #30a;
+    --syntax-comment-color: #3f7f5f;
+    --syntax-default-color: var(--primary-text-color);
+    --syntax-doctag-weight: bold;
+    --syntax-function-color: var(--primary-text-color);
+    --syntax-keyword-color: #9e0069;
+    --syntax-link-color: #219;
+    --syntax-literal-color: #219;
+    --syntax-meta-color: #ff1717;
+    --syntax-meta-keyword-color: #219;
+    --syntax-number-color: #164;
+    --syntax-params-color: var(--primary-text-color);
+    --syntax-regexp-color: #fa8602;
+    --syntax-selector-attr-color: #fa8602;
+    --syntax-selector-class-color: #164;
+    --syntax-selector-id-color: #2a00ff;
+    --syntax-selector-pseudo-color: #fa8602;
+    --syntax-string-color: #2a00ff;
+    --syntax-tag-color: #170;
+    --syntax-template-tag-color: #fa8602;
+    --syntax-template-variable-color: #0000c0;
+    --syntax-title-color: #0000c0;
+    --syntax-type-color: #2a66d9;
+    --syntax-variable-color: var(--primary-text-color);
+  
+    /* elevation */
+    --elevation-level-1: 0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 1px 3px 1px rgba(60, 64, 67, .15);
+    --elevation-level-2: 0px 1px 2px 0px rgba(60, 64, 67, .30), 0px 2px 6px 2px rgba(60, 64, 67, .15);
+    --elevation-level-3: 0px 1px 3px 0px rgba(60, 64, 67, .30), 0px 4px 8px 3px rgba(60, 64, 67, .15);
+    --elevation-level-4: 0px 2px 3px 0px rgba(60, 64, 67, .30), 0px 6px 10px 4px rgba(60, 64, 67, .15);
+    --elevation-level-5: 0px 4px 4px 0px rgba(60, 64, 67, .30), 0px 8px 12px 6px rgba(60, 64, 67, .15);
+  
+    /* misc */
+    --border-radius: 4px;
+    --reply-overlay-z-index: 1000;
+  
+    /* paper and iron component overrides */
+    --iron-overlay-backdrop-background-color: black;
+    --iron-overlay-backdrop-opacity: 0.32;
+    --iron-overlay-backdrop: {
+      transition: none;
+    };
   }
-}
+  @media screen and (max-width: 50em) {
+    html {
+      --spacing-xxs: 1px;
+      --spacing-xs: 1px;
+      --spacing-s: 2px;
+      --spacing-m: 4px;
+      --spacing-l: 8px;
+      --spacing-xl: 12px;
+      --spacing-xxl: 16px;
+    }
+  }
 </style></custom-style>`;
 
-document.head.appendChild($_documentContainer.content);
-
-/*
-  FIXME(polymer-modulizer): the above comments were extracted
-  from HTML and may be out of place here. Review them and
-  then delete this comment!
-*/
-
+document.head.appendChild($_documentContainer.content);
\ No newline at end of file
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.js
similarity index 82%
rename from polygerrit-ui/app/styles/themes/dark-theme.html
rename to polygerrit-ui/app/styles/themes/dark-theme.js
index 18b2fd6..684f2fe 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.js
@@ -1,24 +1,27 @@
-<!--
-@license
-Copyright (C) 2019 The Android Open Source Project
+/**
+ * @license
+ * Copyright (C) 2015 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.
+ */
 
-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.
--->
-<dom-module id="dark-theme">
-  <custom-style><style is="custom-style">
+function getStyleEl() {
+  const $_documentContainer = document.createElement('template');
+  $_documentContainer.innerHTML = `
+  <custom-style id="dark-theme"><style is="custom-style">
     html {
       /**
-       * Sections and variables must stay consistent with app-theme.html.
+       * Sections and variables must stay consistent with app-theme.js.
        *
        * Only modify color variables in this theme file. dark-theme extends
        * app-theme, so there is no need to repeat all variables, but for colors
@@ -153,5 +156,18 @@
       /* rules applied to <html> */
       background-color: var(--view-background-color);
     }
-  </style></custom-style>
-</dom-module>
+  </style></custom-style>`;
+
+  return $_documentContainer;
+}
+
+export function applyTheme() {
+  document.head.appendChild(getStyleEl().content);
+}
+
+export function removeTheme() {
+  const darkThemeEls = document.head.querySelectorAll('#dark-theme');
+  if (darkThemeEls.length) {
+    darkThemeEls.forEach(darkThemeEl => darkThemeEl.remove());
+  }
+}
diff --git a/polygerrit-ui/app/test/tests.js b/polygerrit-ui/app/test/tests.js
index df6a667..15cf17e 100644
--- a/polygerrit-ui/app/test/tests.js
+++ b/polygerrit-ui/app/test/tests.js
@@ -138,7 +138,6 @@
   'settings/gr-identities/gr-identities_test.html',
   'settings/gr-menu-editor/gr-menu-editor_test.html',
   'settings/gr-registration-dialog/gr-registration-dialog_test.html',
-  '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',
@@ -179,7 +178,6 @@
   'shared/gr-fixed-panel/gr-fixed-panel_test.html',
   'shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html',
   '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',