Remove chrome_style & browser_style manifest keys

The chrome_style CSS stylesheet applied by Chrome to the options page is
now included directly in the extension's bundle, and injected into the
options page.

This change is done in preparation for Manifest Version 3, which doesn't
allow setting chrome_style to true:
https://source.chromium.org/chromium/chromium/src/+/master:extensions/common/api/extensions_manifest_types.json;l=94;drc=149f67d78acea93b3d4f28bcd8e594301e1fdb08

This change also moves the options resources to their own folder.

Change-Id: I88d74989b38053d2e137ff166ddcdc4e00489428
diff --git a/src/options/chrome_style/chrome_style.css b/src/options/chrome_style/chrome_style.css
new file mode 100644
index 0000000..367b9ee
--- /dev/null
+++ b/src/options/chrome_style/chrome_style.css
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This stylesheet is used to apply Chrome styles to extension pages that opt in
+ * to using them.
+ *
+ * These styles have been copied from ui/webui/resources/css/chrome_shared.css
+ * and ui/webui/resources/css/widgets.css *with CSS class logic removed*, so
+ * that it's as close to a user-agent stylesheet as possible.
+ *
+ * For example, extensions shouldn't be able to set a .link-button class and
+ * have it do anything.
+ *
+ * Other than that, keep this file and chrome_shared.css/widgets.cc in sync as
+ * much as possible.
+ */
+
+body {
+  color: #333;
+  cursor: default;
+  /* Note that the correct font-family and font-size are set in
+   * extension_fonts.css. */
+  /* This top margin of 14px matches the top padding on the h1 element on
+   * overlays (see the ".overlay .page h1" selector in overlay.css), which
+   * every dialogue has.
+   *
+   * Similarly, the bottom 14px margin matches the bottom padding of the area
+   * which hosts the buttons (see the ".overlay .page * .action-area" selector
+   * in overlay.css).
+   *
+   * Both have a padding left/right of 17px.
+   *
+   * Note that we're putting this here in the Extension content, rather than
+   * the WebUI element which contains the content, so that scrollbars in the
+   * Extension content don't get a 6px margin, which looks quite odd.
+   */
+  margin: 14px 17px;
+}
+
+p {
+  line-height: 1.8em;
+}
+
+h1,
+h2,
+h3 {
+  -webkit-user-select: none;
+  font-weight: normal;
+  /* Makes the vertical size of the text the same for all fonts. */
+  line-height: 1;
+}
+
+h1 {
+  font-size: 1.5em;
+}
+
+h2 {
+  font-size: 1.3em;
+  margin-bottom: 0.4em;
+}
+
+h3 {
+  color: black;
+  font-size: 1.2em;
+  margin-bottom: 0.8em;
+}
+
+a {
+  color: rgb(17, 85, 204);
+  text-decoration: underline;
+}
+
+a:active {
+  color: rgb(5, 37, 119);
+}
+
+/* Default state **************************************************************/
+
+:-webkit-any(button,
+             input[type='button'],
+             input[type='submit']),
+select,
+input[type='checkbox'],
+input[type='radio'] {
+  -webkit-appearance: none;
+  -webkit-user-select: none;
+  background-image: linear-gradient(#ededed, #ededed 38%, #dedede);
+  border: 1px solid rgba(0, 0, 0, 0.25);
+  border-radius: 2px;
+  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08),
+      inset 0 1px 2px rgba(255, 255, 255, 0.75);
+  color: #444;
+  font: inherit;
+  margin: 0 1px 0 0;
+  outline: none;
+  text-shadow: 0 1px 0 rgb(240, 240, 240);
+}
+
+:-webkit-any(button,
+             input[type='button'],
+             input[type='submit']),
+select {
+  min-height: 2em;
+  min-width: 4em;
+<if expr="is_win">
+  /* The following platform-specific rule is necessary to get adjacent
+   * buttons, text inputs, and so forth to align on their borders while also
+   * aligning on the text's baselines. */
+  padding-bottom: 1px;
+</if>
+}
+
+:-webkit-any(button,
+             input[type='button'],
+             input[type='submit']) {
+  -webkit-padding-end: 10px;
+  -webkit-padding-start: 10px;
+}
+
+select {
+  -webkit-appearance: none;
+  -webkit-padding-end: 20px;
+  -webkit-padding-start: 6px;
+  /* OVERRIDE */
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAAUUlEQVR4AWP4TxREZkYxpKHAKKzKEhOZvyG4zN8SE7Eq+6+wYCHbTwiT7eeChf8VsFsKVQhTxIDDbVCFfF8ginApgyp82wRShEcZVJIVzoJDAGqrgIJGRl20AAAAAElFTkSuQmCC),
+      linear-gradient(#ededed, #ededed 38%, #dedede);
+  background-position: right center;
+  background-repeat: no-repeat;
+}
+
+html[dir='rtl'] select {
+  background-position: center left;
+}
+
+input[type='checkbox'] {
+  height: 13px;
+  position: relative;
+  vertical-align: middle;
+  width: 13px;
+}
+
+input[type='radio'] {
+  /* OVERRIDE */
+  border-radius: 100%;
+  height: 15px;
+  position: relative;
+  vertical-align: middle;
+  width: 15px;
+}
+
+/* TODO(estade): add more types here? */
+input[type='number'],
+input[type='password'],
+input[type='search'],
+input[type='text'],
+input[type='url'],
+input:not([type]),
+textarea {
+  border: 1px solid #bfbfbf;
+  border-radius: 2px;
+  box-sizing: border-box;
+  color: #444;
+  font: inherit;
+  margin: 0;
+  /* Use min-height to accommodate addditional padding for touch as needed. */
+  min-height: 2em;
+  padding: 3px;
+  outline: none;
+<if expr="is_win or is_macosx or is_ios">
+  /* For better alignment between adjacent buttons and inputs. */
+  padding-bottom: 4px;
+</if>
+}
+
+input[type='search'] {
+  -webkit-appearance: textfield;
+  /* NOTE: Keep a relatively high min-width for this so we don't obscure the end
+   * of the default text in relatively spacious languages (i.e. German). */
+  min-width: 160px;
+}
+
+/* Checked ********************************************************************/
+
+input[type='checkbox']:checked::before {
+  -webkit-user-select: none;
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAQAAAADpb+tAAAAZ0lEQVR4AWNAA2xAiAXEM8xiMEAXVGJYz7AZCFEkmBi6wYKtEC4/gxqY9gILrmYQhwiXMWxkiAVyVoOFfSCCpkAmCK4Fk+1QA4GqekECUAMkka0KY9gIFvZDd5oawwyGBqACdIDqOwAQzBnTWnnU+gAAAABJRU5ErkJggg==);
+  background-size: 100% 100%;
+  content: '';
+  display: block;
+  height: 100%;
+  width: 100%;
+}
+
+input[type='radio']:checked::before {
+  background-color: #666;
+  border-radius: 100%;
+  bottom: 3px;
+  content: '';
+  display: block;
+  left: 3px;
+  position: absolute;
+  right: 3px;
+  top: 3px;
+}
+
+/* Hover **********************************************************************/
+
+:enabled:hover:-webkit-any(
+    select,
+    input[type='checkbox'],
+    input[type='radio'],
+    :-webkit-any(
+        button,
+        input[type='button'],
+        input[type='submit'])) {
+  background-image: linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
+  border-color: rgba(0, 0, 0, 0.3);
+  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.12),
+      inset 0 1px 2px rgba(255, 255, 255, 0.95);
+  color: black;
+}
+
+:enabled:hover:-webkit-any(select) {
+  /* OVERRIDE */
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAAUUlEQVR4AWP4TxREZkYxpKHAKKzKEhOZvyG4zN8SE7Eq+6+wYCHbTwiT7eeChf8VsFsKVQhTxIDDbVCFfF8ginApgyp82wRShEcZVJIVzoJDAGqrgIJGRl20AAAAAElFTkSuQmCC),
+      linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
+}
+
+/* Active *********************************************************************/
+
+:enabled:active:-webkit-any(
+    select,
+    input[type='checkbox'],
+    input[type='radio'],
+    :-webkit-any(
+        button,
+        input[type='button'],
+        input[type='submit'])) {
+  background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
+  box-shadow: none;
+  text-shadow: none;
+}
+
+:enabled:active:-webkit-any(select) {
+  /* OVERRIDE */
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAAUUlEQVR4AWP4TxREZkYxpKHAKKzKEhOZvyG4zN8SE7Eq+6+wYCHbTwiT7eeChf8VsFsKVQhTxIDDbVCFfF8ginApgyp82wRShEcZVJIVzoJDAGqrgIJGRl20AAAAAElFTkSuQmCC),
+      linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
+}
+
+/* Disabled *******************************************************************/
+
+:disabled:-webkit-any(
+    button,
+    input[type='button'],
+    input[type='submit']),
+select:disabled {
+  background-image: linear-gradient(#f1f1f1, #f1f1f1 38%, #e6e6e6);
+  border-color: rgba(80, 80, 80, 0.2);
+  box-shadow: 0 1px 0 rgba(80, 80, 80, 0.08),
+      inset 0 1px 2px rgba(255, 255, 255, 0.75);
+  color: #aaa;
+}
+
+select:disabled {
+  /* OVERRIDE */
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAASUlEQVR4AWP4TxREZkYxpKHAKKzKEhMb/iPDxESsyv4rLFiIULRg4X8F7JaCFSIUMeBwG1QhTBEuZVCFb5tAivAog0qywllwCAAavoiLhz+UlAAAAABJRU5ErkJggg==),
+      linear-gradient(#f1f1f1, #f1f1f1 38%, #e6e6e6);
+}
+
+input:disabled:-webkit-any([type='checkbox'],
+                           [type='radio']) {
+  opacity: .75;
+}
+
+input:disabled:-webkit-any([type='password'],
+                           [type='search'],
+                           [type='text'],
+                           [type='url'],
+                           :not([type])) {
+  color: #999;
+}
+
+/* Focus **********************************************************************/
+
+:enabled:focus:-webkit-any(
+    select,
+    input[type='checkbox'],
+    input[type='number'],
+    input[type='password'],
+    input[type='radio'],
+    input[type='search'],
+    input[type='text'],
+    input[type='url'],
+    input:not([type]),
+    :-webkit-any(
+         button,
+         input[type='button'],
+         input[type='submit'])) {
+  /* OVERRIDE */
+  -webkit-transition: border-color 200ms;
+  /* We use border color because it follows the border radius (unlike outline).
+   * This is particularly noticeable on mac. */
+  border-color: rgb(77, 144, 254);
+  outline: none;
+}
+
+/* Checkbox/radio helpers ******************************************************
+ *
+ * .checkbox and .radio classes wrap labels. Checkboxes and radios should use
+ * these classes with the markup structure:
+ *
+ *   <div class="checkbox">
+ *     <label>
+ *       <input type="checkbox">
+ *       <span>
+ *     </label>
+ *   </div>
+ */
+
+:-webkit-any(.checkbox, .radio) label {
+  /* Don't expand horizontally: <http://crbug.com/112091>. */
+  align-items: center;
+  display: inline-flex;
+  padding-bottom: 7px;
+  padding-top: 7px;
+}
+
+:-webkit-any(.checkbox, .radio) label input {
+  flex-shrink: 0;
+}
+
+:-webkit-any(.checkbox, .radio) label input ~ span {
+  -webkit-margin-start: 0.6em;
+  /* Make sure long spans wrap at the same horizontal position they start. */
+  display: block;
+}
+
+:-webkit-any(.checkbox, .radio) label:hover {
+  color: black;
+}
+
+label > input:disabled:-webkit-any([type='checkbox'], [type='radio']) ~ span {
+  color: #999;
+}
diff --git a/src/options/options.css b/src/options/options.css
new file mode 100644
index 0000000..06b8c40
--- /dev/null
+++ b/src/options/options.css
@@ -0,0 +1,42 @@
+body {
+  padding-top: 16px;
+}
+
+.features-link {
+  position: absolute;
+  top: 8px;
+  right: 8px;
+  width: 24px;
+}
+
+.option {
+  margin: 4px 0;
+  line-height: 1.8em;
+}
+
+.help {
+  cursor: help;
+  border-bottom: dashed 1px gray;
+}
+
+.experimental-label {
+  color: gray;
+}
+
+.actions {
+  text-align: center;
+}
+
+#profileindicatoralt_months {
+  width: 3em;
+}
+
+#save-indicator {
+  text-align: center;
+  margin-bottom: 16px;
+  color: green;
+}
+
+[hidden] {
+  display: none;
+}
diff --git a/src/options/options.html b/src/options/options.html
new file mode 100644
index 0000000..dec47e2
--- /dev/null
+++ b/src/options/options.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Options</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet" href="options.css">
+    <link rel="stylesheet" href="chrome_style/chrome_style.css">
+  </head>
+  <body>
+    <a href="https://gerrit.avm99963.com/plugins/gitiles/infinitegforums/+/master/docs/features.md" target="_blank" class="features-link">
+      <!--
+        Material Design Icon - action/help_outline
+         - LICENSE: Apache License Version 2.0
+         - Source: https://github.com/google/material-design-icons/
+         - Author: Google LLC
+      -->
+      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-4h2v2h-2zm1.61-9.96c-2.06-.3-3.88.97-4.43 2.79-.18.58.26 1.17.87 1.17h.2c.41 0 .74-.29.88-.67.32-.89 1.27-1.5 2.3-1.28.95.2 1.65 1.13 1.57 2.1-.1 1.34-1.62 1.63-2.45 2.88 0 .01-.01.01-.01.02-.01.02-.02.03-.03.05-.09.15-.18.32-.25.5-.01.03-.03.05-.04.08-.01.02-.01.04-.02.07-.12.34-.2.75-.2 1.25h2c0-.42.11-.77.28-1.07.02-.03.03-.06.05-.09.08-.14.18-.27.28-.39.01-.01.02-.03.03-.04.1-.12.21-.23.33-.34.96-.91 2.26-1.65 1.99-3.56-.24-1.74-1.61-3.21-3.35-3.47z"/></svg>
+    </a>
+    <form>
+      <div class="option"><input type="checkbox" id="list"> <label for="list" data-i18n="list"></label></div>
+      <div class="option"><input type="checkbox" id="thread"> <label for="thread" data-i18n="thread"></label></div>
+      <div class="option"><input type="checkbox" id="threadall"> <label for="threadall" data-i18n="threadall"></label></div>
+      <h4 data-i18n="enhancements"></h4>
+      <div class="option"><input type="checkbox" id="fixedtoolbar"> <label for="fixedtoolbar" data-i18n="fixedtoolbar"></label></div>
+      <div class="option"><input type="checkbox" id="redirect"> <label for="redirect" data-i18n="redirect"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
+      <div class="option"><input type="checkbox" id="history"> <label for="history" data-i18n="history"></label></div>
+      <div class="option"><input type="checkbox" id="loaddrafts"> <label for="loaddrafts" data-i18n="loaddrafts"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
+      <div class="option"><input type="checkbox" id="increasecontrast"> <label for="increasecontrast" data-i18n="increasecontrast"></label></div>
+      <div class="option"><input type="checkbox" id="stickysidebarheaders"> <label for="stickysidebarheaders" data-i18n="stickysidebarheaders"></label></div>
+      <div class="option"><input type="checkbox" id="ccdarktheme"> <label for="ccdarktheme" data-i18n="ccdarktheme"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
+      <div class="option"><input type="checkbox" id="ccforcehidedrawer"> <label for="ccforcehidedrawer" data-i18n="ccforcehidedrawer"></label></div>
+      <div id="dragndrop-wrapper" class="option" hidden><input type="checkbox" id="ccdragndropfix"> <label for="ccdragndropfix" data-i18n="ccdragndropfix"></label></div>
+      <div class="option"><input type="checkbox" id="batchlock"> <label for="batchlock" data-i18n="batchlock"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
+      <h4 data-i18n="profileindicator_header"></h4>
+      <div class="option"><input type="checkbox" id="profileindicator"> <label for="profileindicator" data-i18n="profileindicator"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
+      <div class="option"><input type="checkbox" id="profileindicatoralt"> <label for="profileindicatoralt" data-i18n="profileindicatoralt"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
+      <div class="option"><a href="https://gerrit.avm99963.com/plugins/gitiles/infinitegforums/+/refs/heads/master/docs/op_indicator.md" target="_blank" rel="noreferrer noopener" data-i18n="profileindicator_moreinfo"></a></div>
+      <div class="actions"><button id="save" data-i18n="save"></button></div>
+    </form>
+    <div id="save-indicator"></div>
+    <script src="common/common.js"></script>
+    <script src="options.js"></script>
+  </body>
+</html>
diff --git a/src/options/options.js b/src/options/options.js
new file mode 100644
index 0000000..46913d4
--- /dev/null
+++ b/src/options/options.js
@@ -0,0 +1,153 @@
+var savedSuccessfullyTimeout = null;
+
+const exclusiveOptions = [['thread', 'threadall']];
+
+function save(e) {
+  var options = defaultOptions;
+
+  // Validation checks before saving
+  var months = document.getElementById('profileindicatoralt_months');
+  if (!months.checkValidity()) {
+    console.warn(months.validationMessage);
+    return;
+  }
+
+  e.preventDefault();
+
+  // Save
+  Object.keys(options).forEach(function(opt) {
+    if (deprecatedOptions.includes(opt)) return;
+
+    if (specialOptions.includes(opt)) {
+      switch (opt) {
+        case 'profileindicatoralt_months':
+          options[opt] = document.getElementById(opt).value || 12;
+          break;
+
+        case 'ccdarktheme_mode':
+          options[opt] = document.getElementById(opt).value || 'switch';
+          break;
+
+        // This option is controlled directly in the Community Console.
+        case 'ccdarktheme_switch_enabled':
+          break;
+
+        case 'ccdragndropfix':
+          options[opt] = document.getElementById(opt).checked || false;
+          break;
+
+        default:
+          console.warn('Unrecognized option: ' + opt);
+          break;
+      }
+      return;
+    }
+
+    options[opt] = document.getElementById(opt).checked || false;
+  });
+
+  chrome.storage.sync.set(options, function() {
+    window.close();
+
+    // In browsers like Firefox window.close is not supported:
+    if (savedSuccessfullyTimeout !== null)
+      window.clearTimeout(savedSuccessfullyTimeout);
+
+    document.getElementById('save-indicator').innerText =
+        '✓ ' + chrome.i18n.getMessage('options_saved');
+    savedSuccessfullyTimeout = window.setTimeout(_ => {
+      document.getElementById('save-indicator').innerText = '';
+    }, 3699);
+  });
+}
+
+function i18n() {
+  document.querySelectorAll('[data-i18n]')
+      .forEach(
+          el => el.innerHTML = chrome.i18n.getMessage(
+              'options_' + el.getAttribute('data-i18n')));
+}
+
+window.addEventListener('load', function() {
+  i18n();
+
+  chrome.storage.sync.get(null, function(items) {
+    items = cleanUpOptions(items);
+
+    Object.keys(defaultOptions).forEach(function(opt) {
+      if (deprecatedOptions.includes(opt)) return;
+
+      if (specialOptions.includes(opt)) {
+        switch (opt) {
+          case 'profileindicatoralt_months':
+            var input = document.createElement('input');
+            input.type = 'number';
+            input.id = 'profileindicatoralt_months';
+            input.max = '12';
+            input.min = '1';
+            input.value = items[opt];
+            input.required = true;
+            document.getElementById('profileindicatoralt_months--container')
+                .appendChild(input);
+            break;
+
+          case 'ccdarktheme_mode':
+            var select = document.createElement('select');
+            select.id = 'ccdarktheme_mode';
+
+            const modes = ['switch', 'system'];
+            for (const mode of modes) {
+              var modeOption = document.createElement('option');
+              modeOption.value = mode;
+              modeOption.textContent =
+                  chrome.i18n.getMessage('options_ccdarktheme_mode_' + mode);
+              if (items.ccdarktheme_mode == mode) modeOption.selected = true;
+              select.appendChild(modeOption);
+            }
+
+            document.getElementById('ccdarktheme_mode--container')
+                .appendChild(select);
+            break;
+
+          // This option is controlled directly in the Community Console.
+          case 'ccdarktheme_switch_enabled':
+            break;
+
+          // Firefox doesn't support drag and dropping bookmarks into the text
+          // editor while preserving the bookmark title.
+          case 'ccdragndropfix':
+            var showOption = !isFirefox();
+            if (showOption) {
+              document.getElementById('dragndrop-wrapper')
+                  .removeAttribute('hidden');
+
+              if (items[opt] === true)
+                document.getElementById(opt).checked = true;
+            }
+            break;
+
+          default:
+            console.warn('Unrecognized option: ' + opt);
+            break;
+        }
+        return;
+      }
+
+      if (items[opt] === true) document.getElementById(opt).checked = true;
+    });
+
+    exclusiveOptions.forEach(exclusive => {
+      exclusive.forEach(
+          el => document.getElementById(el).addEventListener('change', e => {
+            if (document.getElementById(exclusive[0]).checked &&
+                document.getElementById(exclusive[1]).checked) {
+              document
+                  .getElementById(
+                      exclusive[(e.currentTarget.id == exclusive[0] ? 1 : 0)])
+                  .checked = false;
+            }
+          }));
+    });
+    document.querySelector('#save').addEventListener('click', save);
+  });
+});