Merge "Fix mobile display on gr-create-change-dialog" into stable-2.16
diff --git a/.mailmap b/.mailmap
index c863847..b5c119c 100644
--- a/.mailmap
+++ b/.mailmap
@@ -6,6 +6,7 @@
 Alex Ryazantsev <alex.ryazantsev@gmail.com>                                                 alex.ryazantsev <alex.ryazantsev@gmail.com>
 Andrew Bonventre <andybons@chromium.org>                                                    <andybons@google.com>
 Becky Siegel <beckysiegel@google.com>                                                       beckysiegel <beckysiegel@google.com>
+Ben Rohlfs <brohlfs@google.com>                                                             brohlfs <brohlfs@google.com>
 Brad Larson <bklarson@gmail.com>                                                            <brad.larson@garmin.com>
 Bruce Zu <bruce.zu.run10@gmail.com>                                                         <bruce.zu@sonyericsson.com>
 Bruce Zu <bruce.zu.run10@gmail.com>                                                         <bruce.zu@sonymobile.com>
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 7c904f5..48d331b 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -216,7 +216,8 @@
 [[hashtag]]
 hashtag:'HASHTAG'::
 +
-Changes whose link:intro-user.html#hashtags[hashtag] matches 'HASHTAG' exactly.
+Changes whose link:intro-user.html#hashtags[hashtag] matches 'HASHTAG'.
+The match is case-insensitive.
 
 [[ref]]
 ref:'REF'::
diff --git a/WORKSPACE b/WORKSPACE
index aa4e9f5..47ead15d 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -48,11 +48,11 @@
 # Golang support for PolyGerrit local dev server.
 http_archive(
     name = "io_bazel_rules_go",
-    sha256 = "ee5fe78fe417c685ecb77a0a725dc9f6040ae5beb44a0ba4ddb55453aad23a8a",
-    url = "https://github.com/bazelbuild/rules_go/releases/download/0.16.0/rules_go-0.16.0.tar.gz",
+    sha256 = "6776d68ebb897625dead17ae510eac3d5f6342367327875210df44dbe2aeeb19",
+    url = "https://github.com/bazelbuild/rules_go/releases/download/0.17.1/rules_go-0.17.1.tar.gz",
 )
 
-load("@io_bazel_rules_go//go:def.bzl", "go_register_toolchains", "go_rules_dependencies")
+load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
 
 go_rules_dependencies()
 
@@ -60,8 +60,8 @@
 
 http_archive(
     name = "bazel_gazelle",
-    sha256 = "c0a5739d12c6d05b6c1ad56f2200cb0b57c5a70e03ebd2f7b87ce88cabf09c7b",
-    urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/0.14.0/bazel-gazelle-0.14.0.tar.gz"],
+    sha256 = "3c681998538231a2d24d0c07ed5a7658cb72bfb5fd4bf9911157c0e9ac6a2687",
+    urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/0.17.0/bazel-gazelle-0.17.0.tar.gz"],
 )
 
 load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
index cb3e9f0..e74ed71 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
@@ -138,6 +138,7 @@
     suggestions.add("is:ignored");
     suggestions.add("is:wip");
     suggestions.add("is:assigned");
+    suggestions.add("is:submittable");
 
     suggestions.add("status:");
     suggestions.add("status:open");
diff --git a/java/com/google/gerrit/acceptance/LogThreshold.java b/java/com/google/gerrit/acceptance/LogThreshold.java
index da1fcc5..36831f3 100644
--- a/java/com/google/gerrit/acceptance/LogThreshold.java
+++ b/java/com/google/gerrit/acceptance/LogThreshold.java
@@ -17,11 +17,13 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 @Target({TYPE, METHOD})
 @Retention(RUNTIME)
+@Inherited
 public @interface LogThreshold {
   String level() default "DEBUG";
 }
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 4f5bf94..07573d3 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
@@ -24,7 +24,6 @@
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../diff/gr-comment-api/gr-comment-api.html">
-<link rel="import" href="../../diff/gr-diff-preferences/gr-diff-preferences.html">
 <link rel="import" href="../../edit/gr-edit-constants.html">
 <link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
 <link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
@@ -40,6 +39,7 @@
 <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-tooltip-content/gr-tooltip-content.html">
+<link rel="import" href="../../shared/revision-info/revision-info.html">
 <link rel="import" href="../gr-change-actions/gr-change-actions.html">
 <link rel="import" href="../gr-change-metadata/gr-change-metadata.html">
 <link rel="import" href="../gr-commit-info/gr-commit-info.html">
@@ -525,6 +525,7 @@
             all-patch-sets="[[_allPatchSets]]"
             change="[[_change]]"
             change-num="[[_changeNum]]"
+            revision-info="[[_revisionInfo]]"
             change-comments="[[_changeComments]]"
             commit-info="[[_commitInfo]]"
             change-url="[[_computeChangeUrl(_change)]]"
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 909a1ee..470f859 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
@@ -122,6 +122,7 @@
         type: Object,
         value: {},
       },
+      _prefs: Object,
       /** @type {?} */
       _changeComments: Object,
       _canStartReview: {
@@ -134,6 +135,10 @@
         type: Object,
         observer: '_changeChanged',
       },
+      _revisionInfo: {
+        type: Object,
+        computed: '_getRevisionInfo(_change)',
+      },
       /** @type {?} */
       _commitInfo: Object,
       _currentRevision: {
@@ -262,6 +267,7 @@
       'fullscreen-overlay-closed': '_handleShowBackgroundContent',
       'diff-comments-modified': '_handleReloadCommentThreads',
     },
+
     observers: [
       '_labelsChanged(_change.labels.*)',
       '_paramsAndChangeChanged(params, _change)',
@@ -335,7 +341,7 @@
     _setDiffViewMode(opt_reset) {
       if (!opt_reset && this.viewState.diffViewMode) { return; }
 
-      return this.$.restAPI.getPreferences().then( prefs => {
+      return this._getPreferences().then( prefs => {
         if (!this.viewState.diffMode) {
           this.set('viewState.diffMode', prefs.default_diff_view);
         }
@@ -790,10 +796,11 @@
 
     _changeChanged(change) {
       if (!change || !this._patchRange || !this._allPatchSets) { return; }
-      this.set('_patchRange.basePatchNum',
-          this._patchRange.basePatchNum || 'PARENT');
-      this.set('_patchRange.patchNum',
-          this._patchRange.patchNum ||
+
+      const parent = this._getBasePatchNum(change, this._patchRange);
+
+      this.set('_patchRange.basePatchNum', parent);
+      this.set('_patchRange.patchNum', this._patchRange.patchNum ||
               this.computeLatestPatchNum(this._allPatchSets));
 
       // Reset the related changes toggle in the event it was previously
@@ -804,6 +811,35 @@
       this.fire('title-change', {title});
     },
 
+    /**
+     * Gets base patch number, if is a parent try and
+     * decide from preference weather to default to `auto merge`
+     * or `Parent 1`.
+     * @param {Object} change
+     * @param {Object} patchRange
+     * @return {number|string}
+     */
+    _getBasePatchNum(change, patchRange) {
+      if (patchRange.basePatchNum &&
+          patchRange.basePatchNum !== 'PARENT') {
+        return patchRange.basePatchNum;
+      }
+
+      const revisionInfo = this._getRevisionInfo(change);
+      if (!revisionInfo) return 'PARENT';
+
+      const parentCounts = revisionInfo.getParentCountMap();
+      // check that there is at least 2 parents otherwise fall back to 1,
+      // which means there is only one parent.
+      const parentCount = parentCounts.hasOwnProperty(1) ?
+          parentCounts[1] : 1;
+
+      const preferFirst = this._prefs &&
+          this._prefs.default_base_for_merges === 'FIRST_PARENT';
+
+      return parentCount > 1 && preferFirst ? -1 : 'PARENT';
+    },
+
     _computeChangeUrl(change) {
       return Gerrit.Nav.getUrlForChange(change);
     },
@@ -1057,6 +1093,10 @@
           });
     },
 
+    _getPreferences() {
+      return this.$.restAPI.getPreferences();
+    },
+
     _updateRebaseAction(revisionActions) {
       if (revisionActions && revisionActions.rebase) {
         revisionActions.rebase.rebaseOnCurrent =
@@ -1110,9 +1150,12 @@
       const detailCompletes = this.$.restAPI.getChangeDetail(
           this._changeNum, this._handleGetChangeDetailError.bind(this));
       const editCompletes = this._getEdit();
+      const prefCompletes = this._getPreferences();
 
-      return Promise.all([detailCompletes, editCompletes])
-          .then(([change, edit]) => {
+      return Promise.all([detailCompletes, editCompletes, prefCompletes])
+          .then(([change, edit, prefs]) => {
+            this._prefs = prefs;
+
             if (!change) {
               return '';
             }
@@ -1661,6 +1704,10 @@
           e.detail.starred);
     },
 
+    _getRevisionInfo(change) {
+      return new Gerrit.RevisionInfo(change);
+    },
+
     _computeCurrentRevision(currentRevision, revisions) {
       return revisions && revisions[currentRevision];
     },
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index f6acef6..419cf01 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -283,7 +283,8 @@
       });
 
       test(', should open diff preferences', () => {
-        const stub = sandbox.stub(element.$.fileList.$.diffPreferences, 'open');
+        const stub = sandbox.stub(
+            element.$.fileList.$.diffPreferencesDialog, 'open');
         MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
         assert.isTrue(stub.called);
       });
@@ -1580,8 +1581,8 @@
     });
 
     test('_selectedRevision updates when patchNum is changed', () => {
-      const revision1 = {_number: 1, commit: {}};
-      const revision2 = {_number: 2, commit: {}};
+      const revision1 = {_number: 1, commit: {parents: []}};
+      const revision2 = {_number: 2, commit: {parents: []}};
       sandbox.stub(element.$.restAPI, 'getChangeDetail').returns(
           Promise.resolve({
             revisions: {
@@ -1594,6 +1595,7 @@
             change_id: 'loremipsumdolorsitamet',
           }));
       sandbox.stub(element, '_getEdit').returns(Promise.resolve());
+      sandbox.stub(element, '_getPreferences').returns(Promise.resolve({}));
       element._patchRange = {patchNum: '2'};
       return element._getChangeDetail().then(() => {
         assert.strictEqual(element._selectedRevision, revision2);
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
index 5e82cda..28d25d2 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
@@ -19,8 +19,8 @@
 
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../shared/gr-download-commands/gr-download-commands.html">
 <link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-download-commands/gr-download-commands.html">
 
 <dom-module id="gr-download-dialog">
   <template>
@@ -87,7 +87,7 @@
           selected-scheme="{{_selectedScheme}}"></gr-download-commands>
     </section>
     <section class="flexContainer">
-      <div class="patchFiles">
+      <div class="patchFiles" hidden="[[_computeHidePatchFile(change, patchNum)]]" hidden>
         <label>Patch file</label>
         <div>
           <a
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
index 5fc81e8..4fe6d8f 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
@@ -138,6 +138,17 @@
       return shortRev + '.diff.' + (opt_zip ? 'zip' : 'base64');
     },
 
+    _computeHidePatchFile(change, patchNum) {
+      for (const rev of Object.values(change.revisions || {})) {
+        if (this.patchNumEquals(rev._number, patchNum)) {
+          const parentLength = rev.commit && rev.commit.parents ?
+                rev.commit.parents.length : 0;
+          return parentLength == 0;
+        }
+      }
+      return false;
+    },
+
     _computeArchiveDownloadLink(change, patchNum, format) {
       return this.changeBaseURL(change.project, change._number, patchNum) +
           '/archive?format=' + format;
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
index 2915e29..ee284b9 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
@@ -45,6 +45,9 @@
       revisions: {
         '34685798fe548b6d17d1e8e5edc43a26d055cc72': {
           _number: 1,
+          commit: {
+            parents: [],
+          },
           fetch: {
             repo: {
               commands: {
@@ -105,6 +108,9 @@
       revisions: {
         '34685798fe548b6d17d1e8e5edc43a26d055cc72': {
           _number: 1,
+          commit: {
+            parents: [],
+          },
           fetch: {},
         },
       },
@@ -188,5 +194,25 @@
       assert.equal(element._computeShowDownloadCommands([]), 'hidden');
       assert.equal(element._computeShowDownloadCommands(['test']), '');
     });
+
+    test('_computeHidePatchFile', () => {
+      const patchNum = '1';
+
+      const change1 = {
+        revisions: {
+          r1: {_number: 1, commit: {parents: []}},
+        },
+      };
+      assert.isTrue(element._computeHidePatchFile(change1, patchNum));
+
+      const change2 = {
+        revisions: {
+          r1: {_number: 1, commit: {parents: [
+            {commit: 'p1'},
+          ]}},
+        },
+      };
+      assert.isFalse(element._computeHidePatchFile(change2, patchNum));
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
index 142e706..22c476c 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
@@ -28,7 +28,6 @@
 <link rel="import" href="../../shared/gr-select/gr-select.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-icons/gr-icons.html">
-<link rel="import" href="../../shared/revision-info/revision-info.html">
 <link rel="import" href="../gr-file-list-constants.html">
 
 <dom-module id="gr-file-list-header">
@@ -164,7 +163,7 @@
               base-patch-num="[[basePatchNum]]"
               available-patches="[[allPatchSets]]"
               revisions="[[change.revisions]]"
-              revision-info="[[_revisionInfo]]"
+              revision-info="[[revisionInfo]]"
               on-patch-range-change="_handlePatchChange">
           </gr-patch-range-select>
           <span class="separator"></span>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
index 665472b..c6e369c 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
@@ -84,10 +84,7 @@
         type: Boolean,
         computed: '_computeDescriptionReadOnly(loggedIn, change, account)',
       },
-      _revisionInfo: {
-        type: Object,
-        computed: '_getRevisionInfo(change)',
-      },
+      revisionInfo: Object,
     },
 
     behaviors: [
@@ -230,10 +227,6 @@
       return 'patchInfoOldPatchSet';
     },
 
-    _getRevisionInfo(change) {
-      return new Gerrit.RevisionInfo(change);
-    },
-
     _hideIncludedIn(change) {
       return change && change.status === MERGED_STATUS ? '' : 'hide';
     },
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index 4852cb3..84de48f 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -23,8 +23,9 @@
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
-<link rel="import" href="../../diff/gr-diff-host/gr-diff-host.html">
 <link rel="import" href="../../diff/gr-diff-cursor/gr-diff-cursor.html">
+<link rel="import" href="../../diff/gr-diff-host/gr-diff-host.html">
+<link rel="import" href="../../diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html">
 <link rel="import" href="../../edit/gr-edit-file-controls/gr-edit-file-controls.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
@@ -465,10 +466,11 @@
         </gr-button><!--
   --></gr-tooltip-content>
     </div>
-    <gr-diff-preferences
-        id="diffPreferences"
-        prefs="{{diffPrefs}}"
-        local-prefs="{{_localPrefs}}"></gr-diff-preferences>
+    <gr-diff-preferences-dialog
+        id="diffPreferencesDialog"
+        diff-prefs="{{diffPrefs}}"
+        on-reload-diff-preference="_handleReloadingDiffPreference">
+    </gr-diff-preferences-dialog>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
     <gr-storage id="storage"></gr-storage>
     <gr-diff-cursor id="diffCursor"></gr-diff-cursor>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index b3e4a70..fba7b1b 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -125,7 +125,6 @@
       },
       /** @type {?} */
       _userPrefs: Object,
-      _localPrefs: Object,
       _showInlineDiffs: Boolean,
       numFilesShown: {
         type: Number,
@@ -269,7 +268,6 @@
         });
       }));
 
-      this._localPrefs = this.$.storage.getPreferences();
       promises.push(this._getDiffPreferences().then(prefs => {
         this.diffPrefs = prefs;
       }));
@@ -297,7 +295,7 @@
     },
 
     openDiffPrefs() {
-      this.$.diffPreferences.open();
+      this.$.diffPreferencesDialog.open();
     },
 
     _calculatePatchChange(files) {
@@ -1255,5 +1253,11 @@
 
       return 'Mark as reviewed (shortcut: r)';
     },
+
+    _handleReloadingDiffPreference() {
+      this._getDiffPreferences().then(prefs => {
+        this.diffPrefs = prefs;
+      });
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index a81526c..91c16c3 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -60,6 +60,7 @@
     'is:reviewed',
     'is:reviewer',
     'is:starred',
+    'is:submittable',
     'is:watched',
     'is:wip',
     'label:',
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
new file mode 100644
index 0000000..5cff316
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
@@ -0,0 +1,80 @@
+<!--
+@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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-diff-preferences/gr-preferences.html">
+<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
+
+<dom-module id="gr-diff-preferences-dialog">
+  <template>
+    <style include="shared-styles">
+      .diffHeader,
+      .diffActions {
+        padding: 1em 1.5em;
+      }
+      .diffHeader,
+      .diffActions {
+        background-color: var(--dialog-background-color);
+      }
+      .diffHeader {
+        border-bottom: 1px solid var(--border-color);
+        font-weight: var(--font-weight-bold);
+      }
+      .diffActions {
+        border-top: 1px solid var(--border-color);
+        display: flex;
+        justify-content: flex-end;
+      }
+      .diffPrefsOverlay gr-button {
+        margin-left: 1em;
+      }
+      div.edited:after {
+        color: var(--deemphasized-text-color);
+        content: ' *';
+      }
+      #diffPreferences {
+        display: flex;
+        padding: .35em 1.5em;
+      }
+    </style>
+    <gr-overlay id="diffPrefsOverlay" with-backdrop>
+      <div class$="diffHeader [[_computeHeaderClass(_diffPrefsChanged)]]">Diff Preferences</div>
+      <gr-diff-preferences
+          id="diffPreferences"
+          diff-prefs="{{diffPrefs}}"
+          has-unsaved-changes="{{_diffPrefsChanged}}"></gr-diff-preferences>
+      <div class="diffActions">
+        <gr-button
+            id="cancelButton"
+            link
+            on-tap="_handleCancelDiff">
+            Cancel
+        </gr-button>
+        <gr-button
+            id="saveButton"
+            link primary
+            on-tap="_handleSaveDiffPreferences"
+            disabled$="[[!_diffPrefsChanged]]">
+            Save
+        </gr-button>
+      </div>
+    </gr-overlay>
+  </template>
+  <script src="gr-diff-preferences-dialog.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
new file mode 100644
index 0000000..b50ef69
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
@@ -0,0 +1,66 @@
+/**
+ * @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() {
+  'use strict';
+
+  Polymer({
+    is: 'gr-diff-preferences-dialog',
+
+    properties: {
+      /** @type {?} */
+      diffPrefs: Object,
+
+      _diffPrefsChanged: Boolean,
+    },
+
+    getFocusStops() {
+      return {
+        start: this.$.contextSelect,
+        end: this.$.saveButton,
+      };
+    },
+
+    resetFocus() {
+      this.$.contextSelect.focus();
+    },
+
+    _computeHeaderClass(changed) {
+      return changed ? 'edited' : '';
+    },
+
+    _handleCancelDiff(e) {
+      e.stopPropagation();
+      this.$.diffPrefsOverlay.close();
+    },
+
+    open() {
+      this.$.diffPrefsOverlay.open().then(() => {
+        const focusStops = this.getFocusStops();
+        this.$.diffPrefsOverlay.setFocusStops(focusStops);
+        this.resetFocus();
+      });
+    },
+
+    _handleSaveDiffPreferences() {
+      this.$.diffPreferences.save().then(() => {
+        this.fire('reload-diff-preference', null, {bubbles: false});
+
+        this.$.diffPrefsOverlay.close();
+      });
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
deleted file mode 100644
index a22f689..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
+++ /dev/null
@@ -1,173 +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.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../shared/gr-button/gr-button.html">
-<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="../../../styles/shared-styles.html">
-
-<dom-module id="gr-diff-preferences">
-  <template>
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      input,
-      select {
-        font: inherit;
-      }
-      input[type="number"] {
-        width: 4em;
-      }
-      .header,
-      .actions {
-        padding: 1em 1.5em;
-      }
-      .header,
-      .mainContainer,
-      .actions {
-        background-color: var(--dialog-background-color);
-      }
-      .header {
-        border-bottom: 1px solid var(--border-color);
-        font-weight: var(--font-weight-bold);
-      }
-      .mainContainer {
-        padding: 1em 0;
-      }
-      .pref {
-        align-items: center;
-        display: flex;
-        padding: .35em 1.5em;
-        width: 25em;
-      }
-      .pref:hover {
-        background-color: var(--hover-background-color);
-      }
-      .pref label {
-        cursor: pointer;
-        flex: 1;
-      }
-      .actions {
-        border-top: 1px solid var(--border-color);
-        display: flex;
-        justify-content: flex-end;
-      }
-      gr-button {
-        margin-left: 1em;
-      }
-    </style>
-    <gr-overlay id="prefsOverlay" with-backdrop>
-      <div class="header">
-        Diff View Preferences
-      </div>
-      <div class="mainContainer">
-        <div class="pref">
-          <label for="contextSelect">Context</label>
-          <select id="contextSelect" on-change="_handleContextSelectChange">
-            <option value="3">3 lines</option>
-            <option value="10">10 lines</option>
-            <option value="25">25 lines</option>
-            <option value="50">50 lines</option>
-            <option value="75">75 lines</option>
-            <option value="100">100 lines</option>
-            <option value="-1">Whole file</option>
-          </select>
-        </div>
-        <div class="pref">
-          <label for="lineWrappingInput">Fit to screen</label>
-          <input
-              is="iron-input"
-              type="checkbox"
-              id="lineWrappingInput"
-              on-tap="_handlelineWrappingTap">
-        </div>
-        <div class="pref" id="columnsPref">
-          <label for="columnsInput">Diff width</label>
-          <input is="iron-input" type="number" id="columnsInput"
-              prevent-invalid-input
-              allowed-pattern="[0-9]"
-              bind-value="{{_newPrefs.line_length}}">
-        </div>
-        <div class="pref">
-          <label for="tabSizeInput">Tab width</label>
-          <input is="iron-input" type="number" id="tabSizeInput"
-              prevent-invalid-input
-              allowed-pattern="[0-9]"
-              bind-value="{{_newPrefs.tab_size}}">
-        </div>
-        <div class="pref" hidden$="[[!_newPrefs.font_size]]">
-          <label for="fontSizeInput">Font size</label>
-          <input is="iron-input" type="number" id="fontSizeInput"
-                prevent-invalid-input
-                allowed-pattern="[0-9]"
-                bind-value="{{_newPrefs.font_size}}">
-        </div>
-        <div class="pref">
-          <label for="showTabsInput">Show tabs</label>
-          <input is="iron-input" type="checkbox" id="showTabsInput"
-              on-tap="_handleShowTabsTap">
-        </div>
-        <div class="pref">
-          <label for="showTrailingWhitespaceInput">
-            Show trailing whitespace</label>
-          <input is="iron-input" type="checkbox"
-              id="showTrailingWhitespaceInput"
-              on-tap="_handleShowTrailingWhitespaceTap">
-        </div>
-        <div class="pref">
-          <label for="syntaxHighlightInput">Syntax highlighting</label>
-          <input is="iron-input" type="checkbox" id="syntaxHighlightInput"
-              on-tap="_handleSyntaxHighlightTap">
-        </div>
-        <div class="pref">
-          <label for="automaticReviewInput">Automatically mark viewed files reviewed</label>
-          <input
-              is="iron-input"
-              id="automaticReviewInput"
-              type="checkbox"
-              on-tap="_handleAutomaticReviewTap">
-        </div>
-        <div class="pref">
-          <label for="ignoreWhitespace">Ignore Whitespace</label>
-          <select id="ignoreWhitespace" on-change="_handleIgnoreWhitespaceChange">
-            <option value="IGNORE_NONE">None</option>
-            <option value="IGNORE_TRAILING">Trailing</option>
-            <option value="IGNORE_LEADING_AND_TRAILING">Leading & trailing</option>
-            <option value="IGNORE_ALL">All</option>
-          </select>
-        </div>
-      </div>
-      <div class="actions">
-        <gr-button id="cancelButton" link on-tap="_handleCancel">
-            Cancel</gr-button>
-        <gr-button id="saveButton" link primary on-tap="_handleSave">
-            Save</gr-button>
-      </div>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
-  </template>
-  <script src="gr-diff-preferences.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.js
deleted file mode 100644
index 8fc90b9..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.js
+++ /dev/null
@@ -1,144 +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-diff-preferences',
-
-    properties: {
-      prefs: {
-        type: Object,
-        notify: true,
-      },
-      localPrefs: {
-        type: Object,
-        notify: true,
-      },
-      disabled: {
-        type: Boolean,
-        value: false,
-        reflectToAttribute: true,
-      },
-
-      /** @type {?} */
-      _newPrefs: Object,
-      _newLocalPrefs: Object,
-    },
-
-    observers: [
-      '_prefsChanged(prefs.*)',
-      '_localPrefsChanged(localPrefs.*)',
-    ],
-
-    getFocusStops() {
-      return {
-        start: this.$.contextSelect,
-        end: this.$.saveButton,
-      };
-    },
-
-    resetFocus() {
-      this.$.contextSelect.focus();
-    },
-
-    _prefsChanged(changeRecord) {
-      const prefs = changeRecord.base;
-      // NOTE: Object.assign is NOT automatically a deep copy. If prefs adds
-      // an object as a value, it must be marked enumerable.
-      this._newPrefs = Object.assign({}, prefs);
-      this.$.contextSelect.value = prefs.context;
-      this.$.showTabsInput.checked = prefs.show_tabs;
-      this.$.showTrailingWhitespaceInput.checked = prefs.show_whitespace_errors;
-      this.$.lineWrappingInput.checked = prefs.line_wrapping;
-      this.$.syntaxHighlightInput.checked = prefs.syntax_highlighting;
-      this.$.automaticReviewInput.checked = !prefs.manual_review;
-      this.$.ignoreWhitespace.value = prefs.ignore_whitespace;
-    },
-
-    _localPrefsChanged(changeRecord) {
-      const localPrefs = changeRecord.base || {};
-      this._newLocalPrefs = Object.assign({}, localPrefs);
-    },
-
-    _handleContextSelectChange(e) {
-      const selectEl = Polymer.dom(e).rootTarget;
-      this.set('_newPrefs.context', parseInt(selectEl.value, 10));
-    },
-
-    _handleIgnoreWhitespaceChange(e) {
-      const selectEl = Polymer.dom(e).rootTarget;
-      this.set('_newPrefs.ignore_whitespace', selectEl.value);
-    },
-
-    _handleShowTabsTap(e) {
-      this.set('_newPrefs.show_tabs', Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleShowTrailingWhitespaceTap(e) {
-      this.set('_newPrefs.show_whitespace_errors',
-          Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleSyntaxHighlightTap(e) {
-      this.set('_newPrefs.syntax_highlighting',
-          Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handlelineWrappingTap(e) {
-      this.set('_newPrefs.line_wrapping', Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleAutomaticReviewTap(e) {
-      this.set('_newPrefs.manual_review', !Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleSave(e) {
-      e.stopPropagation();
-      this.prefs = this._newPrefs;
-      this.localPrefs = this._newLocalPrefs;
-      const el = Polymer.dom(e).rootTarget;
-      el.disabled = true;
-      this.$.storage.savePreferences(this._localPrefs);
-      this._saveDiffPreferences().then(response => {
-        el.disabled = false;
-        if (!response.ok) { return response; }
-
-        this.$.prefsOverlay.close();
-      }).catch(err => {
-        el.disabled = false;
-      });
-    },
-
-    _handleCancel(e) {
-      e.stopPropagation();
-      this.$.prefsOverlay.close();
-    },
-
-    open() {
-      this.$.prefsOverlay.open().then(() => {
-        const focusStops = this.getFocusStops();
-        this.$.prefsOverlay.setFocusStops(focusStops);
-        this.resetFocus();
-      });
-    },
-
-    _saveDiffPreferences() {
-      return this.$.restAPI.saveDiffPreferences(this.prefs);
-    },
-  });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html
deleted file mode 100644
index d9e14c0..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html
+++ /dev/null
@@ -1,110 +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-diff-preferences</title>
-
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.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-diff-preferences.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
-  <template>
-    <gr-diff-preferences></gr-diff-preferences>
-  </template>
-</test-fixture>
-
-<script>
-  suite('gr-diff-preferences tests', () => {
-    let element;
-    let sandbox;
-
-    setup(() => {
-      sandbox = sinon.sandbox.create();
-      element = fixture('basic');
-    });
-
-    teardown(() => {
-      sandbox.restore();
-    });
-
-    test('model changes', () => {
-      element.prefs = {
-        context: 10,
-        font_size: 12,
-        line_length: 100,
-        show_tabs: true,
-        tab_size: 8,
-        show_whitespace_errors: true,
-        syntax_highlighting: true,
-      };
-      assert.deepEqual(element.prefs, element._newPrefs);
-
-      element.$.contextSelect.value = '50';
-      element.fire('change', {}, {node: element.$.contextSelect});
-      element.$.columnsInput.bindValue = 80;
-      element.$.fontSizeInput.bindValue = 10;
-      element.$.tabSizeInput.bindValue = 4;
-      MockInteractions.tap(element.$.showTabsInput);
-      MockInteractions.tap(element.$.showTrailingWhitespaceInput);
-      MockInteractions.tap(element.$.syntaxHighlightInput);
-      MockInteractions.tap(element.$.lineWrappingInput);
-
-      assert.equal(element._newPrefs.context, 50);
-      assert.equal(element._newPrefs.font_size, 10);
-      assert.equal(element._newPrefs.line_length, 80);
-      assert.equal(element._newPrefs.tab_size, 4);
-      assert.isFalse(element._newPrefs.show_tabs);
-      assert.isFalse(element._newPrefs.show_whitespace_errors);
-      assert.isTrue(element._newPrefs.line_wrapping);
-      assert.isFalse(element._newPrefs.syntax_highlighting);
-    });
-
-    test('clicking save button calls _handleSave function', () => {
-      const savePrefs = sinon.stub(element, '_handleSave');
-      MockInteractions.tap(element.$.saveButton);
-      flushAsynchronousOperations();
-      assert(savePrefs.calledOnce);
-      savePrefs.restore();
-    });
-
-    test('save button', () => {
-      element.prefs = {
-        font_size: '11',
-      };
-      element._newPrefs = {
-        font_size: '12',
-      };
-      const saveStub = sandbox.stub(element.$.restAPI, 'saveDiffPreferences',
-          () => { return Promise.resolve(); });
-
-      MockInteractions.tap(element.$$('gr-button[primary]'));
-      assert.deepEqual(element.prefs, element._newPrefs);
-      assert.deepEqual(saveStub.lastCall.args[0], element._newPrefs);
-    });
-
-    test('cancel button', () => {
-      const closeStub = sandbox.stub(element.$.prefsOverlay, 'close');
-      MockInteractions.tap(element.$$('gr-button:not([primary])'));
-      assert.isTrue(closeStub.called);
-    });
-  });
-</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
index 0866849..06658da 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -34,9 +34,9 @@
 <link rel="import" href="../../shared/revision-info/revision-info.html">
 <link rel="import" href="../gr-comment-api/gr-comment-api.html">
 <link rel="import" href="../gr-diff-cursor/gr-diff-cursor.html">
-<link rel="import" href="../gr-diff-mode-selector/gr-diff-mode-selector.html">
-<link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html">
 <link rel="import" href="../gr-diff-host/gr-diff-host.html">
+<link rel="import" href="../gr-diff-mode-selector/gr-diff-mode-selector.html">
+<link rel="import" href="../gr-diff-preferences-dialog/gr-diff-preferences-dialog.html">
 <link rel="import" href="../gr-patch-range-select/gr-patch-range-select.html">
 
 <dom-module id="gr-diff-view">
@@ -338,10 +338,11 @@
         is-blame-loaded="{{_isBlameLoaded}}"
         on-line-selected="_onLineSelected">
     </gr-diff-host>
-    <gr-diff-preferences
-        id="diffPreferences"
-        prefs="{{_prefs}}"
-        local-prefs="{{_localPrefs}}"></gr-diff-preferences>
+    <gr-diff-preferences-dialog
+        id="diffPreferencesDialog"
+        diff-prefs="{{_prefs}}"
+        on-reload-diff-preference="_handleReloadingDiffPreference">
+    </gr-diff-preferences-dialog>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
     <gr-storage id="storage"></gr-storage>
     <gr-diff-cursor id="cursor"></gr-diff-cursor>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index 095dc72..89ad2d2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -258,7 +258,9 @@
     },
 
     _getDiffPreferences() {
-      return this.$.restAPI.getDiffPreferences();
+      return this.$.restAPI.getDiffPreferences().then(prefs => {
+        this._prefs = prefs;
+      });
     },
 
     _getPreferences() {
@@ -457,7 +459,7 @@
           this.modifierPressed(e)) { return; }
 
       e.preventDefault();
-      this.$.diffPreferences.open();
+      this.$.diffPreferencesDialog.open();
     },
 
     _handleToggleDiffMode(e) {
@@ -608,10 +610,7 @@
 
       const promises = [];
 
-      this._localPrefs = this.$.storage.getPreferences();
-      promises.push(this._getDiffPreferences().then(prefs => {
-        this._prefs = prefs;
-      }));
+      promises.push(this._getDiffPreferences());
 
       promises.push(this._getPreferences().then(prefs => {
         this._userPrefs = prefs;
@@ -837,22 +836,7 @@
 
     _handlePrefsTap(e) {
       e.preventDefault();
-      this.$.diffPreferences.open();
-    },
-
-    _handlePrefsSave(e) {
-      e.stopPropagation();
-      const el = Polymer.dom(e).rootTarget;
-      el.disabled = true;
-      this.$.storage.savePreferences(this._localPrefs);
-      this._saveDiffPreferences().then(response => {
-        el.disabled = false;
-        if (!response.ok) { return response; }
-
-        this.$.prefsOverlay.close();
-      }).catch(err => {
-        el.disabled = false;
-      });
+      this.$.diffPreferencesDialog.open();
     },
 
     /**
@@ -1036,5 +1020,9 @@
           (file === this._path || !this._reviewedFiles.has(file)));
       this._navToFile(this._path, unreviewedFiles, 1);
     },
+
+    _handleReloadingDiffPreference() {
+      this._getDiffPreferences();
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
index 958acdb..354bfef 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
@@ -172,7 +172,7 @@
       assert.isTrue(element._loading);
 
       const showPrefsStub =
-          sandbox.stub(element.$.diffPreferences.$.prefsOverlay, 'open',
+          sandbox.stub(element.$.diffPreferencesDialog, 'open',
               () => Promise.resolve());
 
       MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
@@ -365,7 +365,7 @@
 
     test('prefsButton opens gr-diff-preferences', () => {
       const handlePrefsTapSpy = sandbox.spy(element, '_handlePrefsTap');
-      const overlayOpenStub = sandbox.stub(element.$.diffPreferences,
+      const overlayOpenStub = sandbox.stub(element.$.diffPreferencesDialog,
           'open');
       const prefsButton =
           Polymer.dom(element.root).querySelector('.prefsButton');
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
index 70eed78..fe82f2b 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
@@ -112,7 +112,7 @@
     },
 
     _initModule({moduleName, plugin, type, domHook}) {
-      if (this._initializedPlugins.get(plugin.name)) {
+      if (this._initializedPlugins.get(plugin.getPluginName())) {
         return;
       }
       let initPromise;
@@ -128,7 +128,7 @@
         console.warn('Unable to initialize module' +
             `${moduleName} from ${plugin.getPluginName()}`);
       }
-      this._initializedPlugins.set(plugin.name, true);
+      this._initializedPlugins.set(plugin.getPluginName(), true);
       initPromise.then(el => {
         domHook.handleInstanceAttached(el);
         this._domHooks.set(el, domHook);
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
index 799cf59..3c4270f 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
@@ -27,6 +27,7 @@
 <link rel="import" href="../../settings/gr-change-table-editor/gr-change-table-editor.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
+<link rel="import" href="../../shared/gr-diff-preferences/gr-diff-preferences.html">
 <link rel="import" href="../../shared/gr-page-nav/gr-page-nav.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../../shared/gr-select/gr-select.html">
@@ -194,6 +195,18 @@
               </gr-select>
             </span>
           </section>
+          <section hidden$="[[!_localPrefs.default_base_for_merges]]">
+            <span class="title">Default Base For Merges</span>
+            <span class="value">
+              <gr-select
+                  bind-value="{{_localPrefs.default_base_for_merges}}">
+                <select>
+                  <option value="AUTO_MERGE">Auto Merge</option>
+                  <option value="FIRST_PARENT">First Parent</option>
+                </select>
+              </gr-select>
+            </span>
+          </section>
           <section>
             <span class="title">Diff view</span>
             <span class="value">
@@ -259,110 +272,9 @@
           Diff Preferences
         </h2>
         <fieldset id="diffPreferences">
-          <section>
-            <span class="title">Context</span>
-            <span class="value">
-              <gr-select bind-value="{{_diffPrefs.context}}">
-                <select>
-                  <option value="3">3 lines</option>
-                  <option value="10">10 lines</option>
-                  <option value="25">25 lines</option>
-                  <option value="50">50 lines</option>
-                  <option value="75">75 lines</option>
-                  <option value="100">100 lines</option>
-                  <option value="-1">Whole file</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section>
-            <span class="title">Fit to screen</span>
-            <span class="value">
-              <input
-                  id="diffLineWrapping"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.line_wrapping]]"
-                  on-change="_handleDiffLineWrappingChanged">
-            </span>
-          </section>
-          <section id="columnsPref" hidden$="[[_diffPrefs.line_wrapping]]">
-            <span class="title">Diff width</span>
-            <span class="value">
-              <input
-                  is="iron-input"
-                  type="number"
-                  prevent-invalid-input
-                  allowed-pattern="[0-9]"
-                  bind-value="{{_diffPrefs.line_length}}">
-            </span>
-          </section>
-          <section>
-            <span class="title">Tab width</span>
-            <span class="value">
-              <input
-                  is="iron-input"
-                  type="number"
-                  prevent-invalid-input
-                  allowed-pattern="[0-9]"
-                  bind-value="{{_diffPrefs.tab_size}}">
-            </span>
-          </section>
-          <section hidden$="[[!_diffPrefs.font_size]]">
-            <span class="title">Font size</span>
-            <span class="value">
-              <input
-                  is="iron-input"
-                  type="number"
-                  prevent-invalid-input
-                  allowed-pattern="[0-9]"
-                  bind-value="{{_diffPrefs.font_size}}">
-            </span>
-          </section>
-          <section>
-            <span class="title">Show tabs</span>
-            <span class="value">
-              <input
-                  id="diffShowTabs"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.show_tabs]]"
-                  on-change="_handleDiffShowTabsChanged">
-            </span>
-          </section>
-          <section>
-            <span class="title">Show trailing whitespace</span>
-            <span class="value">
-              <input
-                  id="showTrailingWhitespace"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.show_whitespace_errors]]"
-                  on-change="_handleShowTrailingWhitespaceChanged">
-            </span>
-          </section>
-          <section>
-            <span class="title">Syntax highlighting</span>
-            <span class="value">
-              <input
-                  id="diffSyntaxHighlighting"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.syntax_highlighting]]"
-                  on-change="_handleDiffSyntaxHighlightingChanged">
-            </span>
-          </section>
-          <section>
-            <div class="pref">
-              <span class="title">Ignore Whitespace</span>
-              <span class="value">
-                <gr-select bind-value="{{_diffPrefs.ignore_whitespace}}">
-                  <select>
-                    <option value="IGNORE_NONE">None</option>
-                    <option value="IGNORE_TRAILING">Trailing</option>
-                    <option value="IGNORE_LEADING_AND_TRAILING">Leading & trailing</option>
-                    <option value="IGNORE_ALL">All</option>
-                  </select>
-                </gr-select>
-              </span>
-            </div>
-          </section>
+          <gr-diff-preferences
+              id="diffPrefs"
+              has-unsaved-changes="{{_diffPrefsChanged}}"></gr-diff-preferences>
           <gr-button
               id="saveDiffPrefs"
               on-tap="_handleSaveDiffPreferences"
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 0ce8ce0..916f97f 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
@@ -25,6 +25,7 @@
     'diff_view',
     'publish_comments_on_push',
     'work_in_progress_by_default',
+    'default_base_for_merges',
     'signed_off_by',
     'email_format',
     'size_bar_in_change_table',
@@ -64,8 +65,6 @@
       },
       _accountNameMutable: Boolean,
       _accountInfoChanged: Boolean,
-      /** @type {?} */
-      _diffPrefs: Object,
       _changeTableColumnsNotDisplayed: Array,
       /** @type {?} */
       _localPrefs: {
@@ -92,10 +91,8 @@
         type: Boolean,
         value: false,
       },
-      _diffPrefsChanged: {
-        type: Boolean,
-        value: false,
-      },
+      /** @type {?} */
+      _diffPrefsChanged: Boolean,
       /** @type {?} */
       _editPrefsChanged: Boolean,
       _menuChanged: {
@@ -149,7 +146,6 @@
 
     observers: [
       '_handlePrefsChanged(_localPrefs.*)',
-      '_handleDiffPrefsChanged(_diffPrefs.*)',
       '_handleMenuChanged(_localMenu.splices)',
       '_handleChangeTableChanged(_localChangeTableColumns, _showNumber)',
     ],
@@ -166,6 +162,7 @@
         this.$.httpPass.loadData(),
         this.$.identities.loadData(),
         this.$.editPrefs.loadData(),
+        this.$.diffPrefs.loadData(),
       ];
 
       promises.push(this.$.restAPI.getPreferences().then(prefs => {
@@ -176,10 +173,6 @@
         this._cloneChangeTableColumns();
       }));
 
-      promises.push(this.$.restAPI.getDiffPreferences().then(prefs => {
-        this._diffPrefs = prefs;
-      }));
-
       promises.push(this.$.restAPI.getConfig().then(config => {
         this._serverConfig = config;
         const configPromises = [];
@@ -277,11 +270,6 @@
       this._prefsChanged = true;
     },
 
-    _handleDiffPrefsChanged() {
-      if (this._isLoading()) { return; }
-      this._diffPrefsChanged = true;
-    },
-
     _handleShowSizeBarsInFileListChanged() {
       this.set('_localPrefs.size_bar_in_change_table',
           this.$.showSizeBarsInFileList.checked);
@@ -318,24 +306,6 @@
       });
     },
 
-    _handleDiffLineWrappingChanged() {
-      this.set('_diffPrefs.line_wrapping', this.$.diffLineWrapping.checked);
-    },
-
-    _handleDiffShowTabsChanged() {
-      this.set('_diffPrefs.show_tabs', this.$.diffShowTabs.checked);
-    },
-
-    _handleShowTrailingWhitespaceChanged() {
-      this.set('_diffPrefs.show_whitespace_errors',
-          this.$.showTrailingWhitespace.checked);
-    },
-
-    _handleDiffSyntaxHighlightingChanged() {
-      this.set('_diffPrefs.syntax_highlighting',
-          this.$.diffSyntaxHighlighting.checked);
-    },
-
     _handleSaveChangeTable() {
       this.set('prefs.change_table', this._localChangeTableColumns);
       this.set('prefs.legacycid_in_change_table', this._showNumber);
@@ -346,10 +316,7 @@
     },
 
     _handleSaveDiffPreferences() {
-      return this.$.restAPI.saveDiffPreferences(this._diffPrefs)
-          .then(() => {
-            this._diffPrefsChanged = false;
-          });
+      this.$.diffPrefs.save();
     },
 
     _handleSaveEditPreferences() {
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
index f47816f..b0ef704 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
@@ -43,7 +43,6 @@
     let element;
     let account;
     let preferences;
-    let diffPreferences;
     let config;
     let sandbox;
 
@@ -88,6 +87,7 @@
         diff_view: 'UNIFIED_DIFF',
         email_strategy: 'ENABLED',
         email_format: 'HTML_PLAINTEXT',
+        default_base_for_merges: 'FIRST_PARENT',
         size_bar_in_change_table: true,
 
         my: [
@@ -96,31 +96,12 @@
         ],
         change_table: [],
       };
-      diffPreferences = {
-        context: 10,
-        tab_size: 8,
-        font_size: 12,
-        line_length: 100,
-        cursor_blink_rate: 0,
-        line_wrapping: false,
-        intraline_difference: true,
-        show_line_endings: true,
-        show_tabs: true,
-        show_whitespace_errors: true,
-        syntax_highlighting: true,
-        auto_hide_diff_table_header: true,
-        theme: 'DEFAULT',
-        ignore_whitespace: 'IGNORE_NONE',
-      };
       config = {auth: {editable_account_fields: []}};
 
       stub('gr-rest-api-interface', {
         getLoggedIn() { return Promise.resolve(true); },
         getAccount() { return Promise.resolve(account); },
         getPreferences() { return Promise.resolve(preferences); },
-        getDiffPreferences() {
-          return Promise.resolve(diffPreferences);
-        },
         getWatchedProjects() {
           return Promise.resolve([]);
         },
@@ -168,6 +149,8 @@
           .firstElementChild.bindValue, preferences.email_strategy);
       assert.equal(valueOf('Email format', 'preferences')
           .firstElementChild.bindValue, preferences.email_format);
+      assert.equal(valueOf('Default Base For Merges', 'preferences')
+          .firstElementChild.bindValue, preferences.default_base_for_merges);
       assert.equal(valueOf('Diff view', 'preferences')
           .firstElementChild.bindValue, preferences.diff_view);
       assert.equal(valueOf('Show size bars in file list', 'preferences')
@@ -261,56 +244,6 @@
       });
     });
 
-    test('diff preferences', done => {
-      // Rendered with the expected preferences selected.
-      assert.equal(valueOf('Context', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.context);
-      assert.equal(valueOf('Diff width', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.line_length);
-      assert.equal(valueOf('Tab width', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.tab_size);
-      assert.equal(valueOf('Font size', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.font_size);
-      assert.equal(valueOf('Show tabs', 'diffPreferences')
-          .firstElementChild.checked, diffPreferences.show_tabs);
-      assert.equal(valueOf('Show trailing whitespace', 'diffPreferences')
-          .firstElementChild.checked, diffPreferences.show_whitespace_errors);
-      assert.equal(valueOf('Fit to screen', 'diffPreferences')
-          .firstElementChild.checked, diffPreferences.line_wrapping);
-
-      assert.isFalse(element._diffPrefsChanged);
-
-      const showTabsCheckbox = valueOf('Show tabs', 'diffPreferences')
-          .firstElementChild;
-      showTabsCheckbox.checked = false;
-      element._handleDiffShowTabsChanged();
-
-      assert.isTrue(element._diffPrefsChanged);
-
-      stub('gr-rest-api-interface', {
-        saveDiffPreferences(prefs) {
-          assert.equal(prefs.show_tabs, false);
-          return Promise.resolve();
-        },
-      });
-
-      // Save the change.
-      element._handleSaveDiffPreferences().then(() => {
-        assert.isFalse(element._diffPrefsChanged);
-        done();
-      });
-    });
-
-    test('columns input is hidden with fit to scsreen is selected', () => {
-      assert.isFalse(element.$.columnsPref.hidden);
-
-      MockInteractions.tap(element.$.diffLineWrapping);
-      assert.isTrue(element.$.columnsPref.hidden);
-
-      MockInteractions.tap(element.$.diffLineWrapping);
-      assert.isFalse(element.$.columnsPref.hidden);
-    });
-
     test('menu', done => {
       assert.isFalse(element._menuChanged);
       assert.isFalse(element._prefsChanged);
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
index f32e940b..bf56382 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -41,25 +41,30 @@
 
     attached() {
       Promise.all([
-        this.$.restAPI.getConfig(),
+        this._getConfig(),
         Gerrit.awaitPluginsLoaded(),
       ]).then(([cfg]) => {
         this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
-        if (this._hasAvatars && this.account) {
-          // src needs to be set if avatar becomes visible
-          this._updateAvatarURL();
-        } else {
-          this.hidden = true;
-        }
+
+        this._updateAvatarURL();
       });
     },
 
+    _getConfig() {
+      return this.$.restAPI.getConfig();
+    },
+
     _accountChanged(account) {
       this._updateAvatarURL();
     },
 
     _updateAvatarURL() {
-      if (this.hidden || !this._hasAvatars) { return; }
+      if (!this._hasAvatars || !this.account) {
+        this.hidden = true;
+        return;
+      }
+      this.hidden = false;
+
       const url = this._buildAvatarURL(this.account);
       if (url) {
         this.style.backgroundImage = 'url("' + url + '")';
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
index f137c7f..5ce17c0 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
@@ -35,14 +35,17 @@
 <script>
   suite('gr-avatar tests', () => {
     let element;
+    let sandbox;
 
     setup(() => {
-      stub('gr-rest-api-interface', {
-        getConfig() { return Promise.resolve({plugin: {has_avatars: true}}); },
-      });
+      sandbox = sinon.sandbox.create();
       element = fixture('basic');
     });
 
+    teardown(() => {
+      sandbox.restore();
+    });
+
     test('methods', () => {
       assert.equal(element._buildAvatarURL(
           {
@@ -94,22 +97,32 @@
             ],
           }),
           '/accounts/123/avatar?s=16');
+      assert.equal(element._buildAvatarURL(undefined), '');
     });
 
     test('dom for existing account', () => {
       assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({plugin: {has_avatars: true}});
+      });
+
       element.imageSize = 64;
       element.account = {
         _account_id: 123,
       };
+
       assert.strictEqual(element.style.backgroundImage, '');
+
       // Emulate plugins loaded.
       Gerrit._setPluginsPending([]);
-      return Promise.all([
+
+      Promise.all([
         element.$.restAPI.getConfig(),
         Gerrit.awaitPluginsLoaded(),
       ]).then(() => {
         assert.isFalse(element.hasAttribute('hidden'));
+
         assert.isTrue(
             element.style.backgroundImage.includes('/accounts/123/avatar?s=64'));
       });
@@ -117,10 +130,57 @@
 
     test('dom for non available account', () => {
       assert.isFalse(element.hasAttribute('hidden'));
-      element.account = null;
-      assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({plugin: {has_avatars: true}});
+      });
+
       // Emulate plugins loaded.
       Gerrit._setPluginsPending([]);
+
+      return Promise.all([
+        element.$.restAPI.getConfig(),
+        Gerrit.awaitPluginsLoaded(),
+      ]).then(() => {
+        assert.isTrue(element.hasAttribute('hidden'));
+
+        assert.strictEqual(element.style.backgroundImage, '');
+      });
+    });
+
+    test('avatar config not set and account not set', () => {
+      assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({});
+      });
+
+      // Emulate plugins loaded.
+      Gerrit._setPluginsPending([]);
+
+      return Promise.all([
+        element.$.restAPI.getConfig(),
+        Gerrit.awaitPluginsLoaded(),
+      ]).then(() => {
+        assert.isTrue(element.hasAttribute('hidden'));
+      });
+    });
+
+    test('avatar config not set and account set', () => {
+      assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({});
+      });
+
+      element.imageSize = 64;
+      element.account = {
+        _account_id: 123,
+      };
+
+      // Emulate plugins loaded.
+      Gerrit._setPluginsPending([]);
+
       return Promise.all([
         element.$.restAPI.getConfig(),
         Gerrit.awaitPluginsLoaded(),
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html
new file mode 100644
index 0000000..d7bc704
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html
@@ -0,0 +1,163 @@
+<!--
+@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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../gr-button/gr-button.html">
+<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../gr-select/gr-select.html">
+
+<dom-module id="gr-diff-preferences">
+  <template>
+    <style include="shared-styles"></style>
+    <style include="gr-form-styles"></style>
+    <div id="diffPreferences" class="gr-form-styles">
+      <section>
+        <span class="title">Context</span>
+        <span class="value">
+          <gr-select
+              id="contextSelect"
+              bind-value="{{diffPrefs.context}}">
+            <select
+                on-keypress="_handleDiffPrefsChanged"
+                on-change="_handleDiffPrefsChanged">
+              <option value="3">3 lines</option>
+              <option value="10">10 lines</option>
+              <option value="25">25 lines</option>
+              <option value="50">50 lines</option>
+              <option value="75">75 lines</option>
+              <option value="100">100 lines</option>
+              <option value="-1">Whole file</option>
+            </select>
+          </gr-select>
+        </span>
+      </section>
+      <section>
+        <span class="title">Fit to screen</span>
+        <span class="value">
+          <input
+              id="lineWrappingInput"
+              type="checkbox"
+              checked$="[[diffPrefs.line_wrapping]]"
+              on-change="_handleLineWrappingTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Diff width</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              type="number"
+              id="columnsInput"
+              prevent-invalid-input
+              allowed-pattern="[0-9]"
+              bind-value="{{diffPrefs.line_length}}"
+              on-keypress="_handleDiffPrefsChanged"
+              on-change="_handleDiffPrefsChanged">
+        </span>
+      </section>
+      <section>
+        <span class="title">Tab width</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              type="number"
+              id="tabSizeInput"
+              prevent-invalid-input
+              allowed-pattern="[0-9]"
+              bind-value="{{diffPrefs.tab_size}}"
+              on-keypress="_handleDiffPrefsChanged"
+              on-change="_handleDiffPrefsChanged">
+        </span>
+      </section>
+      <section hidden$="[[!diffPrefs.font_size]]">
+        <span class="title">Font size</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              type="number"
+              id="fontSizeInput"
+              prevent-invalid-input
+              allowed-pattern="[0-9]"
+              bind-value="{{diffPrefs.font_size}}"
+              on-keypress="_handleDiffPrefsChanged"
+              on-change="_handleDiffPrefsChanged">
+        </span>
+      </section>
+      <section>
+        <span class="title">Show tabs</span>
+        <span class="value">
+          <input
+              id="showTabsInput"
+              type="checkbox"
+              checked$="[[diffPrefs.show_tabs]]"
+              on-change="_handleShowTabsTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Show trailing whitespace</span>
+        <span class="value">
+          <input
+              id="showTrailingWhitespaceInput"
+              type="checkbox"
+              checked$="[[diffPrefs.show_whitespace_errors]]"
+              on-change="_handleShowTrailingWhitespaceTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Syntax highlighting</span>
+        <span class="value">
+          <input
+              id="syntaxHighlightInput"
+              type="checkbox"
+              checked$="[[diffPrefs.syntax_highlighting]]"
+              on-change="_handleSyntaxHighlightTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Automatically mark viewed files reviewed</span>
+        <span class="value">
+          <input
+              id="automaticReviewInput"
+              type="checkbox"
+              checked$="[[diffPrefs.manual_review]]"
+              on-change="_handleAutomaticReviewTap">
+        </span>
+      </section>
+      <section>
+        <div class="pref">
+          <span class="title">Ignore Whitespace</span>
+          <span class="value">
+            <gr-select bind-value="{{diffPrefs.ignore_whitespace}}">
+              <select
+                  on-keypress="_handleDiffPrefsChanged"
+                  on-change="_handleDiffPrefsChanged">
+                <option value="IGNORE_NONE">None</option>
+                <option value="IGNORE_TRAILING">Trailing</option>
+                <option value="IGNORE_LEADING_AND_TRAILING">Leading & trailing</option>
+                <option value="IGNORE_ALL">All</option>
+              </select>
+            </gr-select>
+          </span>
+        </div>
+      </section>
+    </div>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </template>
+  <script src="gr-diff-preferences.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
new file mode 100644
index 0000000..e2278c2
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
@@ -0,0 +1,78 @@
+/**
+ * @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-diff-preferences',
+
+    properties: {
+      hasUnsavedChanges: {
+        type: Boolean,
+        notify: true,
+        value: false,
+      },
+
+      /** @type {?} */
+      diffPrefs: Object,
+    },
+
+    loadData() {
+      return this.$.restAPI.getDiffPreferences().then(prefs => {
+        this.diffPrefs = prefs;
+      });
+    },
+
+    _handleDiffPrefsChanged() {
+      this.hasUnsavedChanges = true;
+    },
+
+    _handleLineWrappingTap() {
+      this.set('diffPrefs.line_wrapping', this.$.lineWrappingInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleShowTabsTap() {
+      this.set('diffPrefs.show_tabs', this.$.showTabsInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleShowTrailingWhitespaceTap() {
+      this.set('diffPrefs.show_whitespace_errors',
+          this.$.showTrailingWhitespaceInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleSyntaxHighlightTap() {
+      this.set('diffPrefs.syntax_highlighting',
+          this.$.syntaxHighlightInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleAutomaticReviewTap() {
+      this.set('diffPrefs.manual_review',
+          this.$.automaticReviewInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    save() {
+      return this.$.restAPI.saveDiffPreferences(this.diffPrefs).then(res => {
+        this.hasUnsavedChanges = false;
+      });
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
new file mode 100644
index 0000000..511737f
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
@@ -0,0 +1,123 @@
+<!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-diff-preferences</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.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-diff-preferences.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-diff-preferences></gr-diff-preferences>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-diff-preferences tests', () => {
+    let element;
+    let sandbox;
+    let diffPreferences;
+
+    function valueOf(title, fieldsetid) {
+      const sections = element.$[fieldsetid].querySelectorAll('section');
+      let titleEl;
+      for (let i = 0; i < sections.length; i++) {
+        titleEl = sections[i].querySelector('.title');
+        if (titleEl.textContent.trim() === title) {
+          return sections[i].querySelector('.value');
+        }
+      }
+    }
+
+    setup(() => {
+      diffPreferences = {
+        context: 10,
+        line_wrapping: false,
+        line_length: 100,
+        tab_size: 8,
+        font_size: 12,
+        show_tabs: true,
+        show_whitespace_errors: true,
+        syntax_highlighting: true,
+        manual_review: false,
+        ignore_whitespace: 'IGNORE_NONE',
+      };
+
+      stub('gr-rest-api-interface', {
+        getDiffPreferences() {
+          return Promise.resolve(diffPreferences);
+        },
+      });
+
+      element = fixture('basic');
+      sandbox = sinon.sandbox.create();
+      return element.loadData();
+    });
+
+    teardown(() => { sandbox.restore(); });
+
+    test('renders', () => {
+      // Rendered with the expected preferences selected.
+      assert.equal(valueOf('Context', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.context);
+      assert.equal(valueOf('Fit to screen', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.line_wrapping);
+      assert.equal(valueOf('Diff width', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.line_length);
+      assert.equal(valueOf('Tab width', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.tab_size);
+      assert.equal(valueOf('Font size', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.font_size);
+      assert.equal(valueOf('Show tabs', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.show_tabs);
+      assert.equal(valueOf('Show trailing whitespace', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.show_whitespace_errors);
+      assert.equal(valueOf('Syntax highlighting', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.syntax_highlighting);
+      assert.equal(
+          valueOf('Automatically mark viewed files reviewed', 'diffPreferences')
+              .firstElementChild.checked, diffPreferences.manual_review);
+      assert.equal(valueOf('Ignore Whitespace', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.ignore_whitespace);
+
+      assert.isFalse(element.hasUnsavedChanges);
+    });
+
+    test('save changes', () => {
+      sandbox.stub(element.$.restAPI, 'saveDiffPreferences')
+          .returns(Promise.resolve());
+      const showTrailingWhitespaceCheckbox =
+          valueOf('Show trailing whitespace', 'diffPreferences')
+          .firstElementChild;
+      showTrailingWhitespaceCheckbox.checked = false;
+      element._handleShowTrailingWhitespaceTap();
+
+      assert.isTrue(element.hasUnsavedChanges);
+
+      // Save the change.
+      return element.save().then(() => {
+        assert.isFalse(element.hasUnsavedChanges);
+      });
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 10d3e0d..94a48d3 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -110,7 +110,6 @@
     'diff/gr-diff-highlight/gr-annotation_test.html',
     'diff/gr-diff-highlight/gr-diff-highlight_test.html',
     'diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html',
-    'diff/gr-diff-preferences/gr-diff-preferences_test.html',
     'diff/gr-diff-processor/gr-diff-processor_test.html',
     'diff/gr-diff-selection/gr-diff-selection_test.html',
     'diff/gr-diff-view/gr-diff-view_test.html',
@@ -162,6 +161,7 @@
     '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-list/gr-dropdown-list_test.html',
     'shared/gr-editable-content/gr-editable-content_test.html',