From: csagan5 <32685696+csagan5@users.noreply.github.com>
Date: Thu, 29 Mar 2018 00:43:32 +0200
Subject: Add a proxy configuration page

Accessible from proxy settings and chrome://proxy
Allows to use a PAC script URL, automatic configuration and explicit proxy
settings.
Offer auto-complete for the proxy page URL.
---
 chrome/android/java/res/values/values.xml          |   3 +
 .../android/java/res/xml/privacy_preferences.xml   |   5 +
 .../chrome_autocomplete_provider_client.cc         |   2 +
 chrome/browser/browser_resources.grd               |   2 +
 .../prefs/chrome_command_line_pref_store.cc        |   2 +-
 chrome/browser/resources/proxy_config.css          |  61 ++++
 chrome/browser/resources/proxy_config.html         |  78 ++++
 chrome/browser/resources/proxy_config.js           | 252 +++++++++++++
 chrome/browser/ui/BUILD.gn                         |   2 +
 .../ui/webui/chrome_web_ui_controller_factory.cc   |   3 +
 chrome/browser/ui/webui/proxy_config_ui.cc         | 399 +++++++++++++++++++++
 chrome/browser/ui/webui/proxy_config_ui.h          |  33 ++
 chrome/common/webui_url_constants.cc               |   4 +
 chrome/common/webui_url_constants.h                |   2 +
 .../policy/core/browser/proxy_policy_handler.cc    |   2 +-
 components/proxy_config/proxy_config_dictionary.cc |  22 +-
 components/proxy_config/proxy_config_dictionary.h  |   6 +-
 net/proxy_resolution/proxy_config.cc               |  45 +++
 net/proxy_resolution/proxy_config.h                |   3 +
 19 files changed, 914 insertions(+), 12 deletions(-)
 create mode 100644 chrome/browser/resources/proxy_config.css
 create mode 100644 chrome/browser/resources/proxy_config.html
 create mode 100644 chrome/browser/resources/proxy_config.js
 create mode 100644 chrome/browser/ui/webui/proxy_config_ui.cc
 create mode 100644 chrome/browser/ui/webui/proxy_config_ui.h

diff --git a/chrome/android/java/res/values/values.xml b/chrome/android/java/res/values/values.xml
--- a/chrome/android/java/res/values/values.xml
+++ b/chrome/android/java/res/values/values.xml
@@ -81,6 +81,9 @@
     <integer name="list_item_level_selected">1</integer>
     <integer name="list_item_level_incognito">2</integer>
 
+    <string name="proxy_title">Proxy configuration</string>
+    <string name="proxy_url">chrome://proxy</string>
+
     <!-- Download InfoBar animation. -->
     <integer name="download_infobar_sweep_up_delay">500</integer>
     <integer name="download_infobar_sweep_down_delay">800</integer>
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
--- a/chrome/android/java/res/xml/privacy_preferences.xml
+++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -5,8 +5,13 @@
 
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools">
 
+    <org.chromium.chrome.browser.preferences.HyperlinkPreference
+        android:key="proxy"
+        android:title="@string/proxy_title"
+        app:url="@string/proxy_url" />
     <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
         android:key="navigation_error"
         android:title="@string/navigation_error_title"
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -236,6 +236,8 @@ ChromeAutocompleteProviderClient::GetBuiltinsToProvideAsUserTypes() {
   builtins_to_provide.push_back(
       base::ASCIIToUTF16(chrome::kChromeUIFlagsURL));
   builtins_to_provide.push_back(
+      base::ASCIIToUTF16(chrome::kChromeUIProxyConfigURL));
+  builtins_to_provide.push_back(
       base::ASCIIToUTF16(chrome::kChromeUIChromeURLsURL));
 #if !defined(OS_ANDROID)
   builtins_to_provide.push_back(
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -213,6 +213,8 @@
         <include name="IDR_SNIPPETS_INTERNALS_CSS" file="resources\snippets_internals\snippets_internals.css" compress="gzip" type="BINDATA" />
         <include name="IDR_SNIPPETS_INTERNALS_JS" file="resources\snippets_internals\snippets_internals.js" compress="gzip" type="BINDATA" />
         <include name="IDR_SNIPPETS_INTERNALS_MOJO_JS" file="${root_gen_dir}\chrome\browser\ui\webui\snippets_internals\snippets_internals.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+        <include name="IDR_PROXY_CONFIG_HTML" file="resources\proxy_config.html" flattenhtml="true" type="BINDATA" compress="gzip" />
+        <include name="IDR_PROXY_CONFIG_JS" file="resources\proxy_config.js" type="BINDATA" compress="gzip" />
       </if>
       <include name="IDR_SUPERVISED_USER_INTERNALS_HTML" file="resources\supervised_user_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" />
       <include name="IDR_SUPERVISED_USER_INTERNALS_CSS" file="resources\supervised_user_internals.css" compress="gzip" type="BINDATA" />
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store.cc b/chrome/browser/prefs/chrome_command_line_pref_store.cc
--- a/chrome/browser/prefs/chrome_command_line_pref_store.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store.cc
@@ -161,7 +161,7 @@ void ChromeCommandLinePrefStore::ApplyProxyMode() {
     SetValue(
         proxy_config::prefs::kProxy,
         std::make_unique<base::Value>(ProxyConfigDictionary::CreateFixedServers(
-            proxy_server, bypass_list)),
+            proxy_server, bypass_list, false)),
         WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
   }
 }
diff --git a/chrome/browser/resources/proxy_config.css b/chrome/browser/resources/proxy_config.css
new file mode 100644
--- /dev/null
+++ b/chrome/browser/resources/proxy_config.css
@@ -0,0 +1,61 @@
+/* Copyright (c) 2013 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.
+ */
+
+body {
+  font-size: 80%;
+  margin: 1em;
+}
+
+#main-container {
+  max-width: 60em;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+button {
+  display: block;
+  font-size: 110%;
+  font-weight: bold;
+  margin: 10px auto;
+  padding: 1em;
+  width: 15em;
+}
+
+h2 {
+  color: #546E7A;
+  font-weight: normal;
+  font-size: 170%;
+  margin-bottom: 1.5em;
+}
+
+.radio-button-div {
+  margin: 7px auto;
+}
+
+.warning {
+  color: red;
+  font-size: 90%;
+}
+
+.section-container {
+  margin-top: 2em;
+}
+
+#file-path-logging,
+#file-path-stopped {
+  font-family: monospace;
+}
+
+.outline-box {
+  margin-top: 2em;
+  border: 1px solid #ababab;
+  padding: 0.5em;
+  line-height: 1.5em;
+}
+
+textarea {
+  width: 95%;
+  height: 4em;
+}
diff --git a/chrome/browser/resources/proxy_config.html b/chrome/browser/resources/proxy_config.html
new file mode 100644
--- /dev/null
+++ b/chrome/browser/resources/proxy_config.html
@@ -0,0 +1,78 @@
+﻿<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<if expr="is_android">
+<meta name="viewport" content="width=device-width">
+</if>
+
+<if expr="is_ios">
+<!-- TODO(crbug.com/487000): Remove this once injected by web. -->
+<script src="chrome://resources/js/ios/web_ui.js"></script>
+</if>
+
+<script src="chrome://resources/js/util.js"></script>
+<script src="chrome://resources/js/cr.js"></script>
+<script src="chrome://proxy/proxy_config.js"></script>
+<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+<link rel="stylesheet" href="proxy_config.css">
+<title>Proxy configuration</title>
+</head>
+<body>
+  <div id="main-container">
+    <!--
+      =========================================================================
+      View for "pending" state.
+       * Only visible briefly, if at all
+      =========================================================================
+    -->
+    <div id="state-pending">
+      <h2>Proxy configuration</h2>
+      Loading...
+    </div>
+
+    <!--
+      =========================================================================
+      View for "available" and "unset" states.
+       * Has controls to change or reset proxy configuration.
+      =========================================================================
+    -->
+    <div id="state-main" hidden>
+      <h2>Proxy configuration</h2>
+      <button id="reset">Reset</button>
+      <div class="section-container">
+        Reset will update the displayed configuration to match the one currently in use.
+      </div>
+      <div class="section-container">
+          <input type="radio" id="empty" name="mode" value="empty" checked><label for="empty">None</label><br/>
+          <input type="radio" id="auto-detect" name="mode" value="auto-detect"><label for="auto-detect">Auto-detect (WPAD DHCP/DNS)</label><br/>
+          <input type="radio" id="use-pac-url" name="mode" value="use-pac-url"><label for="use-pac-url">Use PAC URL: <input id='pac-url' value="" size="40" /></label>
+          <p><input type="checkbox" id="pac-mandatory" name="pac-mandatory"><label for="pac-mandatory">Do not allow fallback to direct connection in case PAC script fails</label></p>
+          <input type="radio" id="use-single-list" name="mode" value="use-single-list"><label for="use-single-list">Use a single proxy list for all schemes (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file#Description">PAC format</a>):
+              <textarea id="single-proxies"></textarea>
+            </label><br/>
+          <input type="radio" id="use-list-per-scheme" name="mode" value="use-list-per-scheme"><label for="use-list-per-scheme">Use a proxy list per scheme:</label><br/>
+              <label for="use-list-per-scheme">HTTP (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file#Description">PAC format</a>):<br/>
+              <textarea id="http-proxies"></textarea></label><br/>
+              <label for="use-list-per-scheme">HTTPS (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file#Description">PAC format</a>):<br/>
+              <textarea id="https-proxies"></textarea></label><br/>
+              <label for="use-list-per-scheme">FTP (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file#Description">PAC format</a>):<br/>
+              <textarea id="ftp-proxies"></textarea></label><br/>
+              <label for="use-list-per-scheme">Fallback (used when the URL does not match any of the standard schemes, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file#Description">PAC format</a>):<br/>
+              <textarea id="fallback-proxies"></textarea></label>
+          <div class="outline-box">
+            Bypass rules (a list of matching expressions for the hostname separated by comma or semicolon, can use asterisk; matches against port numbers and IPv4/IPv6); in use only with single or per-scheme proxy lists.<br/>
+            <textarea id="bypass-rules"></textarea><br/>
+            <input type="checkbox" id="reverse-bypass" name="reverse-bypass"><label for="reverse-bypass">Reverse the meaning of bypass rules</label>
+          </div>
+      </div>
+      <button id="apply">Apply</button>
+      <button id="clear">Clear</button>
+      <div class="section-container">
+        Clicking on Clear will remove any proxy configuration preference currently in effect.
+      </div>
+    </div>
+
+  </div>
+</body>
+</html>
diff --git a/chrome/browser/resources/proxy_config.js b/chrome/browser/resources/proxy_config.js
new file mode 100644
--- /dev/null
+++ b/chrome/browser/resources/proxy_config.js
@@ -0,0 +1,252 @@
+/*
+    This file is part of Bromite.
+
+    Bromite is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Bromite is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Bromite. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/**
+ * Main entry point called once the page has loaded.
+ */
+function onLoad() {
+  ProxyConfigView.getInstance();
+}
+
+document.addEventListener('DOMContentLoaded', onLoad);
+
+/**
+ * This class handles the presentation of the proxy-config view. Used as a
+ * singleton.
+ */
+var ProxyConfigView = (function() {
+  'use strict';
+
+  // --------------------------------------------------------------------------
+
+  var kIdStateDivUninitialized = 'state-pending';
+  var kIdStateDivMain = 'state-main';
+  var kIdApplyButton = 'apply';
+  var kIdResetButton = 'reset';
+  var kIdClearButton = 'clear';
+
+  var kIdModeEmpty = 'empty';
+  var kIdModeAutoDetect = 'auto-detect';
+  var kIdModeUsePacURL = 'use-pac-url';
+
+  var kIdModeUseSingleList = 'use-single-list';
+  var kIdModeUseListPerScheme = 'use-list-per-scheme';
+
+  var kIdPacURL = 'pac-url';
+  var kIdPacMandatory = 'pac-mandatory';
+  var kIdBypassRules = 'bypass-rules';
+  var kIdReverseBypass = 'reverse-bypass';
+  var kIdSingleProxies = 'single-proxies';
+  var kIdHttpProxies = 'http-proxies';
+  var kIdHttpsProxies = 'https-proxies';
+  var kIdFtpProxies = 'ftp-proxies';
+  var kIdFallbackProxies = 'fallback-proxies';
+
+  /**
+   * @constructor
+   */
+  function ProxyConfigView() {
+    this.currentConfig = null;
+
+    $(kIdResetButton).onclick = this.onReset_.bind(this);
+    $(kIdApplyButton).onclick = this.onApply_.bind(this);
+    $(kIdClearButton).onclick = this.onClear_.bind(this);
+
+    // Tell ProxyConfigMessageHandler to notify the UI of future state changes
+    // from this point on.
+    chrome.send('enableNotifyUIWithState');
+  }
+
+  cr.addSingletonGetter(ProxyConfigView);
+
+  ProxyConfigView.prototype = {
+    /**
+     * Updates the UI to reflect the current state. The state transitions are
+     * sent by the browser controller (ProxyConfigMessageHandler):
+     *
+     *   * PENDING - This is the initial state when proxy configuration is opened
+     *         for the first time, or there was an error during initialization.
+     *         This state is short-lived and likely not observed; will
+     *         immediately transition to AVAILABLE).
+     *
+     *   * AVAILABLE - The reported proxy configuration is active; this state is entered
+     *         on first page load (or right after PENDING if configuration was not
+     *         available on page load) and every time some configuration change was applied.
+     *         It can transition to either AVAILABLE or UNSET.
+     *
+     *   * UNSET - Proxy configuration is reported to be currently not set.
+     *
+     */
+    onProxyConfigChanged: function(state) {
+      // may happen only on first load; leave the loading page as another update is expected
+      // when proxy configuration has finished loading
+      if (state.pending) {
+        $(kIdStateDivMain).hidden = true;
+        $(kIdStateDivUninitialized).hidden = false;
+        return;
+      }
+
+      if (!state.hasOwnProperty('config')) {
+        // configuration has been unset, use an empty one
+        this.eraseCurrentConfig_();
+      } else {
+       // save the configuration as current and reset all controls to it
+        this.currentConfig = state.config;
+      }
+
+      this.renderConfig_();
+
+      this.toggleButtons_(false);
+      $(kIdStateDivUninitialized).hidden = true;
+      $(kIdStateDivMain).hidden = false;
+    },
+
+    /**
+     * Set current configuration to an empty (default) one.
+     */
+    eraseCurrentConfig_: function() {
+        this.currentConfig = {
+          "auto_detect": false,
+          "pending": false,
+          "rules": {
+            "bypass_rules": "",
+            "reverse_bypass": false,
+            "type": "empty"
+          }
+        };
+    },
+
+    /**
+     * Serialize the user-selected configuration in an object.
+     */
+    serializeConfig_: function() {
+      if ($(kIdModeEmpty).checked) {
+        return {
+          "auto_detect": false,
+          "rules": {
+            "type": "empty"
+          }
+        };
+      } else if ($(kIdModeAutoDetect).checked) {
+        return {
+          "auto_detect": true
+        };
+      } else if ($(kIdModeUsePacURL).checked) {
+        return {
+          "auto_detect": false,
+          "pac_url": $(kIdPacURL).value.trim(),
+          "pac_mandatory": $(kIdPacMandatory).checked
+        };
+      } else if ($(kIdModeUseListPerScheme).checked || $(kIdModeUseSingleList).checked) {
+        var config = {
+          "auto_detect": false,
+          "rules": {
+            "bypass_rules": $(kIdBypassRules).value.trim(),
+            "reverse_bypass": $(kIdReverseBypass).checked,
+            "type": "list"
+          }
+        };
+
+        if ($(kIdModeUseListPerScheme).checked) {
+          config.rules.type = "list_per_scheme";
+
+          config.rules.proxies_for_http = $(kIdHttpProxies).value.trim();
+          config.rules.proxies_for_https = $(kIdHttpsProxies).value.trim();
+          config.rules.proxies_for_ftp = $(kIdFtpProxies).value.trim();
+          config.rules.fallback_proxies = $(kIdFallbackProxies).value.trim();
+        } else {
+          config.rules.single_proxies = $(kIdSingleProxies).value.trim();
+        }
+
+        return config;
+      }
+
+      throw new Error('unexpected mode');
+    },
+
+    /**
+     * Updates the UI to display the current proxy configuration.
+     */
+    renderConfig_: function() {
+      if (this.currentConfig.auto_detect)
+        $(kIdModeAutoDetect).checked = true;
+      else if (this.currentConfig.hasOwnProperty('pac_url')) {
+        $(kIdPacURL).value = this.currentConfig.pac_url;
+        $(kIdPacMandatory).checked = this.currentConfig.pac_mandatory;
+        $(kIdModeUsePacURL).checked = true;
+      } else if (this.currentConfig.rules.type == "empty")
+        $(kIdModeEmpty).checked = true;
+      else {
+        $(kIdBypassRules).value = this.currentConfig.rules.bypass_rules;
+        $(kIdReverseBypass).checked = this.currentConfig.rules.reverse_bypass;
+
+        switch (this.currentConfig.rules.type) {
+          case "list":
+            $(kIdModeUseSingleList).checked = true;
+            $(kIdSingleProxies).value = this.currentConfig.rules.single_proxies;
+          break;
+          case "list_per_scheme":
+            $(kIdModeUseListPerScheme).checked = true;
+            $(kIdHttpProxies).value = this.currentConfig.rules.proxies_for_http;
+            $(kIdHttpsProxies).value = this.currentConfig.rules.proxies_for_https;
+            $(kIdFtpProxies).value = this.currentConfig.rules.proxies_for_ftp;
+            $(kIdFallbackProxies).value = this.currentConfig.rules.fallback_proxies;
+          break;
+        }
+      }
+    },
+
+    /**
+     * Apply the configuration currently displayed.
+     */
+    onApply_: function() {
+      var config = this.serializeConfig_();
+
+      // disable buttons; will be enabled back when UI receives a state update
+      this.toggleButtons_(true);
+      chrome.send('apply', [config]);
+    },
+
+    /**
+     * Apply the configuration currently displayed.
+     */
+    onClear_: function() {
+      // disable buttons; will be enabled back when UI receives a state update
+      this.toggleButtons_(true);
+      this.eraseCurrentConfig_();
+      chrome.send('clear', []);
+    },
+
+    /**
+     * Toggle the disabled status of the action buttons.
+     */
+    toggleButtons_: function(disabled) {
+      $(kIdApplyButton).disabled = disabled;
+      $(kIdResetButton).disabled = disabled;
+      $(kIdClearButton).disabled = disabled;
+    },
+
+    /**
+     * Reset currently displayed configuration to the last known configuration in use.
+     */
+    onReset_: function() {
+      this.renderConfig_();
+    }
+  };
+
+  return ProxyConfigView;
+})();
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -271,6 +271,8 @@ jumbo_split_static_library("ui") {
     "webui/metrics_handler.h",
     "webui/net_export_ui.cc",
     "webui/net_export_ui.h",
+    "webui/proxy_config_ui.cc",
+    "webui/proxy_config_ui.h",
     "webui/net_internals/net_internals_ui.cc",
     "webui/net_internals/net_internals_ui.h",
     "webui/ntp_tiles_internals_ui.cc",
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -51,6 +51,7 @@
 #include "chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h"
 #include "chrome/browser/ui/webui/policy_ui.h"
 #include "chrome/browser/ui/webui/predictors/predictors_ui.h"
+#include "chrome/browser/ui/webui/proxy_config_ui.h"
 #include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h"
 #include "chrome/browser/ui/webui/settings/md_settings_ui.h"
 #include "chrome/browser/ui/webui/settings_utils.h"
@@ -386,6 +387,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
     return &NewWebUI<MemoryInternalsUI>;
   if (url.host_piece() == chrome::kChromeUINetExportHost)
     return &NewWebUI<NetExportUI>;
+  if (url.host_piece() == chrome::kChromeUIProxyConfigHost)
+    return &NewWebUI<ProxyConfigUI>;
   if (url.host_piece() == chrome::kChromeUINetInternalsHost)
     return &NewWebUI<NetInternalsUI>;
   if (url.host_piece() == chrome::kChromeUINTPTilesInternalsHost)
diff --git a/chrome/browser/ui/webui/proxy_config_ui.cc b/chrome/browser/ui/webui/proxy_config_ui.cc
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/proxy_config_ui.cc
@@ -0,0 +1,399 @@
+/*
+    This file is part of Bromite.
+
+    Bromite is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Bromite is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Bromite. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "chrome/browser/ui/webui/proxy_config_ui.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/scoped_observer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/net/proxy_service_factory.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "components/grit/components_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+#include "url/gurl.h"
+
+using content::BrowserThread;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateProxyConfigHTMLSource() {
+  content::WebUIDataSource* source =
+      content::WebUIDataSource::Create(chrome::kChromeUIProxyConfigHost);
+
+  source->SetJsonPath("strings.js");
+  source->AddResourcePath("proxy_config.js", IDR_PROXY_CONFIG_JS);
+  source->SetDefaultResource(IDR_PROXY_CONFIG_HTML);
+  source->UseGzip();
+  return source;
+}
+
+// This class receives javascript messages from the renderer.
+// Note that the WebUI infrastructure runs on the UI thread, therefore all of
+// this class's public methods are expected to run on the UI thread.
+class ProxyConfigMessageHandler
+    : public WebUIMessageHandler,
+      public base::SupportsWeakPtr<ProxyConfigMessageHandler>,
+      public net::ProxyConfigService::Observer {
+ public:
+  // Creates a ProxyConfigMessageHandler that handles message exchanges with the Javascript
+  // side of the UI and gets proxy settings from the Web UI associated profile to watch for changes.
+  // The created ProxyConfigMessageHandler must be destroyed before |profile|.
+  ProxyConfigMessageHandler(Profile *profile);
+  ~ProxyConfigMessageHandler() override;
+
+  // WebUIMessageHandler implementation.
+  void RegisterMessages() override;
+
+  // Messages
+  void OnEnableNotifyUIWithState(const base::ListValue* list);
+  void OnApply(const base::ListValue* config);
+  void OnClear(const base::ListValue* config);
+
+  // net::ProxyConfigService::Observer implementation:
+  // Calls ProxyConfigView.onProxyConfigChanged JavaScript function in the
+  // renderer.
+  void OnProxyConfigChanged(
+    const net::ProxyConfigWithAnnotation& config,
+    net::ProxyConfigService::ConfigAvailability availability) override;
+
+ private:
+  // Not owned.
+  Profile *profile_;
+  std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
+  // Monitors global and Profile prefs related to proxy configuration.
+  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
+  bool is_observing_;
+
+  void encodeConfig(const net::ProxyConfig& config, base::DictionaryValue& state);
+
+  void apply(const net::ProxyConfig& config);
+
+  base::WeakPtrFactory<ProxyConfigMessageHandler> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyConfigMessageHandler);
+};
+
+ProxyConfigMessageHandler::ProxyConfigMessageHandler(Profile *profile)
+    :
+      weak_ptr_factory_(this) {
+
+  // used to set new configuration preferences
+  profile_ = profile;
+  // observer is explicitly added only later in enableNotifyUIWithState
+  is_observing_ = false;
+
+// If this is the ChromeOS sign-in profile, just create the tracker from global
+// state.
+#if defined(OS_CHROMEOS)
+  if (chromeos::ProfileHelper::IsSigninProfile(profile)) {
+    pref_proxy_config_tracker_.reset(
+        ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
+            g_browser_process->local_state()));
+  }
+#endif  // defined(OS_CHROMEOS)
+
+  if (!pref_proxy_config_tracker_) {
+    pref_proxy_config_tracker_ =
+        ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(
+            profile->GetPrefs(), g_browser_process->local_state());
+  }
+
+  proxy_config_service_ = ProxyServiceFactory::CreateProxyConfigService(
+      pref_proxy_config_tracker_.get());
+}
+
+void ProxyConfigMessageHandler::OnProxyConfigChanged(
+    const net::ProxyConfigWithAnnotation& config,
+    net::ProxyConfigService::ConfigAvailability availability) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
+
+  base::DictionaryValue state;
+  bool pending = false;
+  switch (availability) {
+    case net::ProxyConfigService::CONFIG_VALID:
+      encodeConfig(config.value(), state);
+      break;
+    case net::ProxyConfigService::CONFIG_UNSET:
+      // nothing will be populated
+      break;
+    case net::ProxyConfigService::CONFIG_PENDING:
+      //NOTE: this can only happen when triggered manually first time
+      pending = true;
+      break;
+  }
+  state.SetKey("pending", base::Value(pending));
+
+  // call Javascript function
+  web_ui()->CallJavascriptFunctionUnsafe("ProxyConfigView.getInstance().onProxyConfigChanged",
+                                         *state.CreateDeepCopy());
+}
+
+const std::string omitDirect(const std::string pacString) {
+  if (pacString == "DIRECT") {
+    return "";
+  }
+  return pacString;
+}
+
+void ProxyConfigMessageHandler::encodeConfig(const net::ProxyConfig& config, base::DictionaryValue& state) {
+  // when automatic settings are enabled they take precedence over manual settings
+  // automatic settings are either the "auto-detect" flag or the existance of a PAC URL
+
+  state.SetPath({"config", "auto_detect"}, base::Value(config.auto_detect()));
+
+  if (config.has_pac_url()) {
+    state.SetPath({"config", "pac_url"}, base::Value(config.pac_url().spec()));
+    state.SetPath({"config", "pac_mandatory"}, base::Value(config.pac_mandatory()));
+  }
+
+  auto rules = config.proxy_rules();
+  const char *type;
+  switch (rules.type) {
+    case net::ProxyConfig::ProxyRules::Type::EMPTY:
+      type = "empty";
+      break;
+    case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
+      type = "list";
+
+      state.SetPath({"config", "rules", "single_proxies"}, base::Value(omitDirect(rules.single_proxies.ToPacString())));
+      break;
+    case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
+      type = "list_per_scheme";
+
+      state.SetPath({"config", "rules", "proxies_for_http"}, base::Value(omitDirect(rules.proxies_for_http.ToPacString())));
+      state.SetPath({"config", "rules", "proxies_for_https"}, base::Value(omitDirect(rules.proxies_for_https.ToPacString())));
+      state.SetPath({"config", "rules", "proxies_for_ftp"}, base::Value(omitDirect(rules.proxies_for_ftp.ToPacString())));
+      state.SetPath({"config", "rules", "fallback_proxies"}, base::Value(omitDirect(rules.fallback_proxies.ToPacString())));
+      break;
+    default:
+     NOTREACHED();
+     break;
+  }
+  state.SetPath({"config", "rules", "type"}, base::Value(type));
+  state.SetPath({"config", "rules", "bypass_rules"}, base::Value(rules.bypass_rules.ToString()));
+  state.SetPath({"config", "rules", "reverse_bypass"}, base::Value(rules.reverse_bypass));
+}
+
+ProxyConfigMessageHandler::~ProxyConfigMessageHandler() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
+  if (is_observing_) {
+    proxy_config_service_->RemoveObserver(this);
+  }
+  pref_proxy_config_tracker_->DetachFromPrefService();
+}
+
+void ProxyConfigMessageHandler::RegisterMessages() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  web_ui()->RegisterMessageCallback(
+      "enableNotifyUIWithState",
+      base::BindRepeating(&ProxyConfigMessageHandler::OnEnableNotifyUIWithState,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "apply",
+      base::BindRepeating(&ProxyConfigMessageHandler::OnApply,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "clear",
+      base::BindRepeating(&ProxyConfigMessageHandler::OnClear,
+                          base::Unretained(this)));
+}
+
+// The proxy configuration UI is not notified of state changes until this function runs.
+// After this function, OnProxyConfigChanged() will be called on all proxy state changes.
+void ProxyConfigMessageHandler::OnEnableNotifyUIWithState(
+    const base::ListValue* list) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (!is_observing_) {
+    is_observing_ = true;
+    proxy_config_service_->AddObserver(this);
+  }
+
+  net::ProxyConfigWithAnnotation config;
+  auto availability = proxy_config_service_->GetLatestProxyConfig(&config);
+  OnProxyConfigChanged(config, availability);
+}
+
+void ProxyConfigMessageHandler::OnClear(const base::ListValue* list) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  profile_->GetPrefs()->ClearPref(proxy_config::prefs::kProxy);
+}
+
+void ProxyConfigMessageHandler::OnApply(const base::ListValue* list) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  const base::Value::ListStorage& params = list->GetList();
+
+  if ((params.size() != 1) || !params[0].is_dict()) {
+    return;
+  }
+
+  const base::DictionaryValue* config = nullptr;
+  if (!list->GetDictionary(0, &config))
+    return;
+
+  const base::Value *autoDetect = config->FindKeyOfType("auto_detect", base::Value::Type::BOOLEAN);
+  if (autoDetect == nullptr)
+    return;
+
+  if (autoDetect->GetBool()) {
+    apply(net::ProxyConfig::CreateAutoDetect());
+    return;
+  }
+
+  const base::Value *pacURL = config->FindKeyOfType("pac_url", base::Value::Type::STRING);
+  if (pacURL != nullptr) {
+    const base::Value *pacMandatory = config->FindKeyOfType("pac_mandatory", base::Value::Type::BOOLEAN);
+    if (pacMandatory == nullptr)
+      return;
+    auto proxyConfig = net::ProxyConfig::CreateFromCustomPacURL(GURL(pacURL->GetString()));
+    proxyConfig.set_pac_mandatory(pacMandatory->GetBool());
+
+    apply(proxyConfig);
+    return;
+  }
+
+  const base::Value *rules = config->FindKeyOfType("rules", base::Value::Type::DICTIONARY);
+  if (rules == nullptr)
+    return;
+
+  const base::Value *type = rules->FindKeyOfType("type", base::Value::Type::STRING);
+  if (type == nullptr)
+    return;
+
+  net::ProxyConfig proxyConfig;
+
+  bool readBypass = false;
+
+  auto t = type->GetString();
+  if (t == "list") {
+      const base::Value *single_proxies = rules->FindKeyOfType("single_proxies", base::Value::Type::STRING);
+      if (single_proxies == nullptr)
+        return;
+      proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
+      proxyConfig.proxy_rules().single_proxies.SetFromPacString(single_proxies->GetString());
+      readBypass = true;
+  } else if (t == "list_per_scheme") {
+      const base::Value *http = rules->FindKeyOfType("proxies_for_http", base::Value::Type::STRING);
+      if (http == nullptr)
+        return;
+
+      const base::Value *https = rules->FindKeyOfType("proxies_for_https", base::Value::Type::STRING);
+      if (https == nullptr)
+        return;
+
+      const base::Value *ftp = rules->FindKeyOfType("proxies_for_ftp", base::Value::Type::STRING);
+      if (ftp == nullptr)
+        return;
+
+      const base::Value *fallback = rules->FindKeyOfType("fallback_proxies", base::Value::Type::STRING);
+      if (fallback == nullptr)
+        return;
+
+      proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
+      proxyConfig.proxy_rules().proxies_for_http.SetFromPacString(http->GetString());
+      proxyConfig.proxy_rules().proxies_for_https.SetFromPacString(https->GetString());
+      proxyConfig.proxy_rules().proxies_for_ftp.SetFromPacString(ftp->GetString());
+      proxyConfig.proxy_rules().fallback_proxies.SetFromPacString(fallback->GetString());
+      readBypass = true;
+  } else if (t == "empty") {
+      proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::EMPTY;
+  } else {
+     // invalid type
+     LOG(WARNING) << "invalid proxy configuration type";
+     return;
+  }
+
+  // bypass rules and reverse flag are common to both list types of proxy rules
+  if (readBypass) {
+    const base::Value *bypass_rules = rules->FindKeyOfType("bypass_rules", base::Value::Type::STRING);
+    if (bypass_rules == nullptr)
+      return;
+
+    const base::Value *reverse_bypass = rules->FindKeyOfType("reverse_bypass", base::Value::Type::BOOLEAN);
+    if (reverse_bypass == nullptr)
+      return;
+
+    proxyConfig.proxy_rules().bypass_rules.ParseFromString(bypass_rules->GetString());
+    proxyConfig.proxy_rules().reverse_bypass = reverse_bypass->GetBool();
+  }
+
+  apply(proxyConfig);
+}
+
+void ProxyConfigMessageHandler::apply(const net::ProxyConfig& proxyConfig) {
+  if (proxyConfig.auto_detect()) {
+    const base::Value cfg = ProxyConfigDictionary::CreateAutoDetect();
+    profile_->GetPrefs()->Set(proxy_config::prefs::kProxy, cfg);
+    return;
+  } else if (proxyConfig.has_pac_url()) {
+    const base::Value cfg = ProxyConfigDictionary::CreatePacScript(proxyConfig.pac_url().spec(), proxyConfig.pac_mandatory());
+    profile_->GetPrefs()->Set(proxy_config::prefs::kProxy, cfg);
+    return;
+  }
+
+  auto proxyRulesAsString = proxyConfig.proxy_rules().ToString();
+  auto bypassRulesAsString = proxyConfig.proxy_rules().bypass_rules.ToString();
+
+  // fixed servers
+  const base::Value cfg = ProxyConfigDictionary::CreateFixedServers(proxyRulesAsString,
+                          bypassRulesAsString, proxyConfig.proxy_rules().reverse_bypass);
+  profile_->GetPrefs()->Set(proxy_config::prefs::kProxy, cfg);
+}
+
+}  // namespace
+
+ProxyConfigUI::ProxyConfigUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+  Profile* profile = Profile::FromWebUI(web_ui);
+
+  web_ui->AddMessageHandler(std::make_unique<ProxyConfigMessageHandler>(profile));
+
+  // Set up the chrome://proxy/ source.
+  content::WebUIDataSource::Add(profile, CreateProxyConfigHTMLSource());
+}
diff --git a/chrome/browser/ui/webui/proxy_config_ui.h b/chrome/browser/ui/webui/proxy_config_ui.h
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/proxy_config_ui.h
@@ -0,0 +1,33 @@
+/*
+    This file is part of Bromite.
+
+    Bromite is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Bromite is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Bromite. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://proxy/.
+class ProxyConfigUI : public content::WebUIController {
+ public:
+  explicit ProxyConfigUI(content::WebUI* web_ui);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProxyConfigUI);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -177,6 +177,8 @@ const char kChromeUINativePhysicalWebDiagnosticsURL[] =
 const char kChromeUINativeScheme[] = "chrome-native";
 const char kChromeUIOfflineInternalsHost[] = "offline-internals";
 const char kChromeUIPhysicalWebDiagnosticsHost[] = "physical-web-diagnostics";
+const char kChromeUIProxyConfigHost[] = "proxy";
+const char kChromeUIProxyConfigURL[] = "chrome://proxy/";
 const char kChromeUISnippetsInternalsHost[] = "snippets-internals";
 const char kChromeUIWebApksHost[] = "webapks";
 #endif
@@ -354,6 +356,7 @@ const char* const kChromeHostURLs[] = {
     kChromeUIPasswordManagerInternalsHost,
     kChromeUIPolicyHost,
     kChromeUIPredictorsHost,
+    kChromeUIProxyConfigHost,
     kChromeUIQuotaInternalsHost,
     kChromeUISignInInternalsHost,
     kChromeUISiteEngagementHost,
@@ -388,6 +391,7 @@ const char* const kChromeHostURLs[] = {
 #if !defined(OS_ANDROID)
 #if !defined(OS_CHROMEOS)
     kChromeUIAppLauncherPageHost,
+    kChromeUIProxyConfigHost,
 #endif
     kChromeUIBookmarksHost,
     kChromeUIDownloadsHost,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -102,6 +102,8 @@ extern const char kChromeUIMemoryInternalsHost[];
 extern const char kChromeUINTPTilesInternalsHost[];
 extern const char kChromeUINaClHost[];
 extern const char kChromeUINetExportHost[];
+extern const char kChromeUIProxyConfigHost[];
+extern const char kChromeUIProxyConfigURL[];
 extern const char kChromeUINetInternalsHost[];
 extern const char kChromeUINetInternalsURL[];
 extern const char kChromeUINewTabHost[];
diff --git a/components/policy/core/browser/proxy_policy_handler.cc b/components/policy/core/browser/proxy_policy_handler.cc
--- a/components/policy/core/browser/proxy_policy_handler.cc
+++ b/components/policy/core/browser/proxy_policy_handler.cc
@@ -199,7 +199,7 @@ void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
           bypass_list->GetAsString(&bypass_list_string);
         prefs->SetValue(proxy_config::prefs::kProxy,
                         ProxyConfigDictionary::CreateFixedServers(
-                            proxy_server, bypass_list_string));
+                            proxy_server, bypass_list_string, false));
       }
       break;
     }
diff --git a/components/proxy_config/proxy_config_dictionary.cc b/components/proxy_config/proxy_config_dictionary.cc
--- a/components/proxy_config/proxy_config_dictionary.cc
+++ b/components/proxy_config/proxy_config_dictionary.cc
@@ -28,6 +28,8 @@ const char kProxyPacMandatory[] = "pac_mandatory";
 // String containing proxy bypass rules. For a specification of the
 // expected syntax see net::ProxyBypassRules::ParseFromString().
 const char kProxyBypassList[] = "bypass_list";
+// Boolean telling whether to reverse the meaning of the bypass list.
+const char kProxyReverseBypass[] = "reverse_bypass";
 
 }  // namespace
 
@@ -79,29 +81,30 @@ const base::Value& ProxyConfigDictionary::GetDictionary() const {
 // static
 base::Value ProxyConfigDictionary::CreateDirect() {
   return CreateDictionary(ProxyPrefs::MODE_DIRECT, std::string(), false,
-                          std::string(), std::string());
+                          std::string(), std::string(), false);
 }
 
 // static
 base::Value ProxyConfigDictionary::CreateAutoDetect() {
   return CreateDictionary(ProxyPrefs::MODE_AUTO_DETECT, std::string(), false,
-                          std::string(), std::string());
+                          std::string(), std::string(), false);
 }
 
 // static
 base::Value ProxyConfigDictionary::CreatePacScript(const std::string& pac_url,
                                                    bool pac_mandatory) {
   return CreateDictionary(ProxyPrefs::MODE_PAC_SCRIPT, pac_url, pac_mandatory,
-                          std::string(), std::string());
+                          std::string(), std::string(), false);
 }
 
 // static
 base::Value ProxyConfigDictionary::CreateFixedServers(
     const std::string& proxy_server,
-    const std::string& bypass_list) {
+    const std::string& bypass_list,
+    bool reverse_bypass) {
   if (!proxy_server.empty()) {
     return CreateDictionary(ProxyPrefs::MODE_FIXED_SERVERS, std::string(),
-                            false, proxy_server, bypass_list);
+                            false, proxy_server, bypass_list, reverse_bypass);
   } else {
     return CreateDirect();
   }
@@ -110,7 +113,7 @@ base::Value ProxyConfigDictionary::CreateFixedServers(
 // static
 base::Value ProxyConfigDictionary::CreateSystem() {
   return CreateDictionary(ProxyPrefs::MODE_SYSTEM, std::string(), false,
-                          std::string(), std::string());
+                          std::string(), std::string(), false);
 }
 
 // static
@@ -119,7 +122,8 @@ base::Value ProxyConfigDictionary::CreateDictionary(
     const std::string& pac_url,
     bool pac_mandatory,
     const std::string& proxy_server,
-    const std::string& bypass_list) {
+    const std::string& bypass_list,
+    bool reverse_bypass) {
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetKey(kProxyMode, base::Value(ProxyModeToString(mode)));
   if (!pac_url.empty()) {
@@ -128,8 +132,10 @@ base::Value ProxyConfigDictionary::CreateDictionary(
   }
   if (!proxy_server.empty())
     dict.SetKey(kProxyServer, base::Value(proxy_server));
-  if (!bypass_list.empty())
+  if (!bypass_list.empty()) {
     dict.SetKey(kProxyBypassList, base::Value(bypass_list));
+    dict.SetKey(kProxyReverseBypass, base::Value(reverse_bypass));
+  }
   return dict;
 }
 
diff --git a/components/proxy_config/proxy_config_dictionary.h b/components/proxy_config/proxy_config_dictionary.h
--- a/components/proxy_config/proxy_config_dictionary.h
+++ b/components/proxy_config/proxy_config_dictionary.h
@@ -46,7 +46,8 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary {
   static base::Value CreatePacScript(const std::string& pac_url,
                                      bool pac_mandatory);
   static base::Value CreateFixedServers(const std::string& proxy_server,
-                                        const std::string& bypass_list);
+                                        const std::string& bypass_list,
+                                        bool reverse_bypass);
   static base::Value CreateSystem();
 
   // Encodes the proxy server as "<url-scheme>=<proxy-scheme>://<proxy>".
@@ -62,7 +63,8 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary {
                                       const std::string& pac_url,
                                       bool pac_mandatory,
                                       const std::string& proxy_server,
-                                      const std::string& bypass_list);
+                                      const std::string& bypass_list,
+                                      bool reverse_bypass);
 
   base::Value dict_;
 
diff --git a/net/proxy_resolution/proxy_config.cc b/net/proxy_resolution/proxy_config.cc
--- a/net/proxy_resolution/proxy_config.cc
+++ b/net/proxy_resolution/proxy_config.cc
@@ -140,6 +140,51 @@ void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
   }
 }
 
+std::string ProxyConfig::ProxyRules::ToString() const {
+  if (type == Type::EMPTY) {
+    return "";
+  }
+
+  // special case: a single proxy server specified
+  if (type == Type::PROXY_LIST) {
+    if (single_proxies.size() == 1) {
+      return single_proxies.Get().ToURI();
+    }
+    // more than 1 proxies or 0 proxies is unexpected
+    return "";
+  }
+
+  if (type != Type::PROXY_LIST_PER_SCHEME) {
+    NOTREACHED();
+    // Unexpected LIST with fallback, or other type values
+    return "";
+  }
+
+  // start to build a per-scheme list
+  std::string list;
+  for (const ProxyServer& proxy_server :
+       proxies_for_http.GetAll()) {
+    list += "http=" + proxy_server.ToURI() + ";";
+  }
+  for (const ProxyServer& proxy_server :
+       proxies_for_https.GetAll()) {
+    list += "https=" + proxy_server.ToURI() + ";";
+  }
+  for (const ProxyServer& proxy_server :
+       proxies_for_ftp.GetAll()) {
+    list += "ftp=" + proxy_server.ToURI() + ";";
+  }
+  for (const ProxyServer& proxy_server :
+       fallback_proxies.GetAll()) {
+    list += "socks=" + proxy_server.ToURI() + ";";
+  }
+  if (list.length() != 0 ) {
+    // remove last semicolon
+    list.pop_back();
+  }
+  return list;
+}
+
 const ProxyList* ProxyConfig::ProxyRules::MapUrlSchemeToProxyList(
     const std::string& url_scheme) const {
   const ProxyList* proxy_server_list = const_cast<ProxyRules*>(this)->
diff --git a/net/proxy_resolution/proxy_config.h b/net/proxy_resolution/proxy_config.h
--- a/net/proxy_resolution/proxy_config.h
+++ b/net/proxy_resolution/proxy_config.h
@@ -104,6 +104,9 @@ class NET_EXPORT ProxyConfig {
     //                                  and use socks4://foopy2 for all other
     //                                  URLs.
     void ParseFromString(const std::string& proxy_rules);
+    // Returns the proxy rules in a format that can be parsed by ParseFromString;
+    // all information except bypass rules is used.
+    std::string ToString() const;
 
     // Returns one of {&proxies_for_http, &proxies_for_https, &proxies_for_ftp,
     // &fallback_proxies}, or NULL if there is no proxy to use.
-- 
2.11.0

