+ Reset will update the displayed configuration to match the one currently in use.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ Clicking on Clear will remove any proxy configuration preference currently in effect.
+
+
+
+
+
+
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,266 @@
+/*
+ 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 .
+*/
+
+import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+
+/**
+ * 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 kIdModeDirect = 'direct';
+ 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');
+ }
+
+ addSingletonGetter(ProxyConfigView);
+ window.ProxyConfigView = 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": "none"
+ }
+ };
+ },
+
+ /**
+ * Serialize the user-selected configuration in an object.
+ */
+ serializeConfig_: function() {
+ if ($(kIdModeEmpty).checked) {
+ return {
+ "auto_detect": false,
+ "rules": {
+ "type": "none"
+ }
+ };
+ } else if ($(kIdModeDirect).checked) {
+ return {
+ "auto_detect": false,
+ "rules": {
+ "type": "direct"
+ }
+ };
+ } 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,
+ "rules": {}
+ };
+ } 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 == "none") {
+ $(kIdModeEmpty).checked = true;
+ } else if (this.currentConfig.rules.type == "direct") {
+ $(kIdModeDirect).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
@@ -274,6 +274,8 @@ 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
@@ -55,6 +55,7 @@
#include "chrome/browser/ui/webui/ntp_tiles_internals_ui.h"
#include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
#include "chrome/browser/ui/webui/policy/policy_ui.h"
+#include "chrome/browser/ui/webui/proxy_config_ui.h"
#include "chrome/browser/ui/webui/predictors/predictors_ui.h"
#include "chrome/browser/ui/webui/segmentation_internals/segmentation_internals_ui.h"
#include "chrome/browser/ui/webui/signin_internals_ui.h"
@@ -763,6 +764,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
return &NewWebUI;
if (url.host_piece() == chrome::kChromeUINetExportHost)
return &NewWebUI;
+ if (url.host_piece() == chrome::kChromeUIProxyConfigHost)
+ return &NewWebUI;
if (url.host_piece() == chrome::kChromeUINetInternalsHost)
return &NewWebUI;
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,418 @@
+/*
+ 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 .
+*/
+
+#include "chrome/browser/ui/webui/proxy_config_ui.h"
+
+#include
+
+#include
+#include
+#include
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.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/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->UseStringsJs();
+ source->AddResourcePath("proxy_config.js", IDR_PROXY_CONFIG_JS);
+ source->SetDefaultResource(IDR_PROXY_CONFIG_HTML);
+ 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,
+ public net::ProxyConfigService::Observer {
+ public:
+ ProxyConfigMessageHandler(const ProxyConfigMessageHandler&) = delete;
+ ProxyConfigMessageHandler& operator=(const ProxyConfigMessageHandler&) = delete;
+ // 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::Value::List& args);
+ void OnApply(const base::Value::List& args);
+ void OnClear(const base::Value::List& args);
+
+ // 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.
+ PrefService *pref_service_;
+ std::unique_ptr proxy_config_service_;
+ // Monitors global and Profile prefs related to proxy configuration.
+ std::unique_ptr pref_proxy_config_tracker_;
+ bool is_observing_;
+
+ void encodeConfig(const net::ProxyConfig& config, base::DictionaryValue& state);
+
+ void apply(const net::ProxyConfig& config);
+
+ base::WeakPtrFactory weak_ptr_factory_;
+};
+
+ProxyConfigMessageHandler::ProxyConfigMessageHandler(Profile *profile)
+ :
+ weak_ptr_factory_(this) {
+
+ // used to set new configuration preferences
+ pref_service_ = g_browser_process->local_state();
+ // 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(), nullptr);
+}
+
+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:
+ state.SetPath({"config", "rules", "type"}, base::Value("none"));
+ 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()));
+
+ auto rules = config.proxy_rules();
+ 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()));
+ state.SetPath({"config", "rules", "type"}, base::Value("none"));
+ state.SetPath({"config", "rules", "bypass_rules"}, base::Value(rules.bypass_rules.ToString()));
+ state.SetPath({"config", "rules", "reverse_bypass"}, base::Value(rules.reverse_bypass));
+ return;
+ }
+
+ const char *type;
+ switch (rules.type) {
+ case net::ProxyConfig::ProxyRules::Type::EMPTY:
+ type = "direct";
+ 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::Value::List& 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);
+
+ const base::Value* dict =
+ pref_service_->GetDictionary(proxy_config::prefs::kProxy);
+ ProxyConfigDictionary proxy_dict(dict->Clone());
+ ProxyPrefs::ProxyMode mode;
+ if (!proxy_dict.GetMode(&mode) || mode == ProxyPrefs::MODE_SYSTEM) {
+ availability = net::ProxyConfigService::CONFIG_UNSET;
+ }
+
+ OnProxyConfigChanged(config, availability);
+}
+
+void ProxyConfigMessageHandler::OnClear(const base::Value::List& list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ const base::Value cfg = ProxyConfigDictionary::CreateSystem();
+ pref_service_->Set(proxy_config::prefs::kProxy, cfg);
+ pref_service_->CommitPendingWrite();
+ OnEnableNotifyUIWithState(list);
+}
+
+void ProxyConfigMessageHandler::OnApply(const base::Value::List& list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if ((list.size() != 1) || !list[0].is_dict()) {
+ return;
+ }
+
+ const base::DictionaryValue* config = nullptr;
+ if (!list[0].GetAsDictionary(&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 == "direct") {
+ proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::EMPTY;
+ } else if (t == "none") {
+ base::Value::List empty;
+ OnClear(empty);
+ return;
+ } 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();
+ pref_service_->Set(proxy_config::prefs::kProxy, cfg);
+ } else if (proxyConfig.has_pac_url()) {
+ const base::Value cfg = ProxyConfigDictionary::CreatePacScript(proxyConfig.pac_url().spec(), proxyConfig.pac_mandatory());
+ pref_service_->Set(proxy_config::prefs::kProxy, cfg);
+ } else if (proxyConfig.proxy_rules().type == net::ProxyConfig::ProxyRules::Type::EMPTY) {
+ const base::Value cfg = ProxyConfigDictionary::CreateDirect();
+ pref_service_->Set(proxy_config::prefs::kProxy, cfg);
+ } else {
+ 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);
+ pref_service_->Set(proxy_config::prefs::kProxy, cfg);
+ }
+ pref_service_->CommitPendingWrite();
+
+ base::Value::List empty;
+ OnEnableNotifyUIWithState(empty);
+}
+
+} // namespace
+
+ProxyConfigUI::ProxyConfigUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+
+ web_ui->AddMessageHandler(std::make_unique(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,31 @@
+/*
+ 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 .
+*/
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://proxy/.
+class ProxyConfigUI : public content::WebUIController {
+ public:
+ ProxyConfigUI(const ProxyConfigUI&) = delete;
+ ProxyConfigUI& operator=(const ProxyConfigUI&) = delete;
+ explicit ProxyConfigUI(content::WebUI* web_ui);
+};
+
+#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
@@ -39,6 +39,8 @@ const char kChromeUICertificateViewerHost[] = "view-cert";
const char kChromeUICertificateViewerURL[] = "chrome://view-cert/";
const char kChromeUIChromeSigninHost[] = "chrome-signin";
const char kChromeUIChromeSigninURL[] = "chrome://chrome-signin/";
+const char kChromeUIProxyConfigHost[] = "proxy";
+const char kChromeUIProxyConfigURL[] = "chrome://proxy/";
const char kChromeUIChromeURLsHost[] = "chrome-urls";
const char kChromeUIChromeURLsURL[] = "chrome://chrome-urls/";
const char kChromeUIComponentsHost[] = "components";
@@ -409,6 +411,7 @@ bool IsSystemWebUIHost(base::StringPiece host) {
kChromeUIMobileSetupHost,
kChromeUIMultiDeviceSetupHost,
kChromeUINetworkHost,
+ kChromeUIProxyConfigHost,
kChromeUIOobeHost,
kChromeUIOSCreditsHost,
kChromeUIOSSettingsHost,
@@ -651,6 +654,7 @@ const char* const kChromeHostURLs[] = {
#if !BUILDFLAG(IS_ANDROID)
#if !BUILDFLAG(IS_CHROMEOS_ASH)
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
@@ -129,6 +129,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/proxy_config/pref_proxy_config_tracker_impl.cc b/components/proxy_config/pref_proxy_config_tracker_impl.cc
--- a/components/proxy_config/pref_proxy_config_tracker_impl.cc
+++ b/components/proxy_config/pref_proxy_config_tracker_impl.cc
@@ -381,6 +381,7 @@ bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
if (proxy_dict.GetBypassList(&proxy_bypass)) {
proxy_config.proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
}
+ proxy_config.proxy_rules().reverse_bypass = proxy_dict.HasReverseBypass();
*config = net::ProxyConfigWithAnnotation(
proxy_config, kSettingsProxyConfigTrafficAnnotation);
return true;
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
@@ -30,6 +30,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
@@ -78,6 +80,14 @@ bool ProxyConfigDictionary::HasBypassList() const {
return dict_.FindKey(kProxyBypassList);
}
+bool ProxyConfigDictionary::HasReverseBypass() const {
+ const base::Value* value = dict_.FindKey(kProxyReverseBypass);
+ if (!value || !value->is_bool()) {
+ return false;
+ }
+ return value->GetBool();
+}
+
const base::Value& ProxyConfigDictionary::GetDictionary() const {
return dict_;
}
@@ -85,29 +95,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();
}
@@ -116,7 +127,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
@@ -125,7 +136,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()) {
@@ -134,8 +146,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
@@ -42,6 +42,7 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary {
bool GetProxyServer(std::string* out) const;
bool GetBypassList(std::string* out) const;
bool HasBypassList() const;
+ bool HasReverseBypass() const;
const base::Value& GetDictionary() const;
@@ -50,7 +51,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 "=://".
@@ -66,7 +68,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/components/proxy_config/proxy_policy_handler.cc b/components/proxy_config/proxy_policy_handler.cc
--- a/components/proxy_config/proxy_policy_handler.cc
+++ b/components/proxy_config/proxy_policy_handler.cc
@@ -258,7 +258,7 @@ void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
ProxyConfigDictionary::CreateFixedServers(
server->GetString(), bypass_list && bypass_list->is_string()
? bypass_list->GetString()
- : std::string()));
+ : std::string(), false));
}
break;
}
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
@@ -112,7 +112,7 @@ void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
&single_proxies,
ProxyServer::SCHEME_HTTP);
type = Type::PROXY_LIST;
- return;
+ continue;
}
// Trim whitespace off the url scheme.
@@ -143,6 +143,56 @@ 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 servers list specified
+ if (type == Type::PROXY_LIST) {
+ std::string proxy_list;
+ for (const ProxyServer& proxy_server :
+ single_proxies.GetAll()) {
+ proxy_list += ProxyServerToProxyUri(proxy_server) + ";";
+ }
+ // remove last semicolon
+ if (proxy_list.length() != 0 ) {
+ proxy_list.pop_back();
+ }
+ return proxy_list;
+ }
+
+ 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=" + ProxyServerToProxyUri(proxy_server) + ";";
+ }
+ for (const ProxyServer& proxy_server :
+ proxies_for_https.GetAll()) {
+ list += "https=" + ProxyServerToProxyUri(proxy_server) + ";";
+ }
+ for (const ProxyServer& proxy_server :
+ proxies_for_ftp.GetAll()) {
+ list += "ftp=" + ProxyServerToProxyUri(proxy_server) + ";";
+ }
+ for (const ProxyServer& proxy_server :
+ fallback_proxies.GetAll()) {
+ list += "socks=" + ProxyServerToProxyUri(proxy_server) + ";";
+ }
+ 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(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
@@ -103,6 +103,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.25.1