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. Store proxy settings in LocalState instead of Profile, so that proxy is used for SimpleURLLoaders as well. --- chrome/android/java/res/values/values.xml | 3 + .../java/res/xml/privacy_preferences.xml | 4 + .../privacy/settings/PrivacySettings.java | 1 + .../chrome_autocomplete_provider_client.cc | 2 + chrome/browser/browser_resources.grd | 6 + chrome/browser/net/proxy_service_factory.cc | 24 +- chrome/browser/net/proxy_service_factory.h | 3 + chrome/browser/prefs/browser_prefs.cc | 4 + .../prefs/chrome_command_line_pref_store.cc | 2 +- chrome/browser/resources/proxy_config.css | 61 +++ chrome/browser/resources/proxy_config.html | 79 ++++ chrome/browser/resources/proxy_config.js | 266 +++++++++++ chrome/browser/ui/BUILD.gn | 2 + .../webui/chrome_web_ui_controller_factory.cc | 3 + chrome/browser/ui/webui/proxy_config_ui.cc | 418 ++++++++++++++++++ chrome/browser/ui/webui/proxy_config_ui.h | 31 ++ chrome/common/webui_url_constants.cc | 4 + chrome/common/webui_url_constants.h | 2 + .../pref_proxy_config_tracker_impl.cc | 1 + .../proxy_config/proxy_config_dictionary.cc | 30 +- .../proxy_config/proxy_config_dictionary.h | 7 +- .../proxy_config/proxy_policy_handler.cc | 2 +- net/proxy_resolution/proxy_config.cc | 52 ++- net/proxy_resolution/proxy_config.h | 3 + 24 files changed, 996 insertions(+), 14 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 @@ -25,6 +25,9 @@ true + Proxy configuration + chrome://proxy + 1200 200 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 @@ -6,6 +6,10 @@ + + + + + + + diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc --- a/chrome/browser/net/proxy_service_factory.cc +++ b/chrome/browser/net/proxy_service_factory.cc @@ -6,6 +6,7 @@ #include +#include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -14,6 +15,9 @@ #include "content/public/browser/browser_thread.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/proxy_resolution/proxy_config_service.h" +#include "components/proxy_config/proxy_config_pref_names.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_registry_simple.h" #if BUILDFLAG(IS_CHROMEOS_ASH) #include "chromeos/network/proxy/proxy_config_service_impl.h" @@ -72,7 +76,20 @@ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile( return std::make_unique( profile_prefs, local_state_prefs, nullptr); #else - return std::make_unique(profile_prefs, nullptr); + // Migrate from profile_prefs to local_state_prefs + if (local_state_prefs->GetBoolean("proxy_migrated") == false) { + const base::Value* dict = + profile_prefs->GetDictionary(proxy_config::prefs::kProxy); + + LOG(INFO) << "CreatePrefProxyConfigTrackerOfProfile: Migration from profile to local state"; + + const base::Value /*ProxyConfigDictionary*/ proxy_dict(dict->Clone()); + local_state_prefs->Set(proxy_config::prefs::kProxy, proxy_dict); + + local_state_prefs->SetBoolean("proxy_migrated", true); + local_state_prefs->CommitPendingWrite(); + } + return std::make_unique(local_state_prefs, nullptr); #endif // BUILDFLAG(IS_CHROMEOS_ASH) } @@ -88,3 +105,8 @@ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState( nullptr); #endif // BUILDFLAG(IS_CHROMEOS_ASH) } + +// static +void ProxyServiceFactory::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref("proxy_migrated", false); +} diff --git a/chrome/browser/net/proxy_service_factory.h b/chrome/browser/net/proxy_service_factory.h --- a/chrome/browser/net/proxy_service_factory.h +++ b/chrome/browser/net/proxy_service_factory.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_NET_PROXY_SERVICE_FACTORY_H_ #include +#include "components/prefs/pref_registry_simple.h" class PrefProxyConfigTracker; class PrefService; @@ -35,6 +36,8 @@ class ProxyServiceFactory { CreatePrefProxyConfigTrackerOfProfile(PrefService* profile_prefs, PrefService* local_state_prefs); + static void RegisterPrefs(PrefRegistrySimple* registry); + // Creates a PrefProxyConfigTracker that tracks local state only. This tracker // should be used for the system request context and the signin screen // (ChromeOS only). diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -161,6 +161,8 @@ #include "printing/buildflags/buildflags.h" #include "rlz/buildflags/buildflags.h" +#include "chrome/browser/net/proxy_service_factory.h" + #if BUILDFLAG(ENABLE_BACKGROUND_MODE) #include "chrome/browser/background/background_mode_manager.h" #endif @@ -994,6 +996,8 @@ void RegisterLocalState(PrefRegistrySimple* registry) { chrome::enterprise_util::RegisterLocalStatePrefs(registry); component_updater::RegisterPrefs(registry); embedder_support::OriginTrialPrefs::RegisterPrefs(registry); + ProxyServiceFactory::RegisterPrefs(registry); + enterprise_reporting::RegisterLocalStatePrefs(registry); ExternalProtocolHandler::RegisterPrefs(registry); flags_ui::PrefServiceFlagsStorage::RegisterPrefs(registry); 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 @@ -157,7 +157,7 @@ void ChromeCommandLinePrefStore::ApplyProxyMode() { SetValue( proxy_config::prefs::kProxy, std::make_unique(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,79 @@ + + + + + + + + + + + + + + + + + + +Proxy configuration + + +
+ +
+

Proxy configuration

+ Loading... +
+ + + + +
+ + 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 @@ -281,6 +281,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 @@ -56,6 +56,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/profiles/profile_internals_ui.h" #include "chrome/browser/ui/webui/segmentation_internals/segmentation_internals_ui.h" @@ -778,6 +779,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 @@ -48,6 +48,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"; @@ -421,6 +423,7 @@ bool IsSystemWebUIHost(base::StringPiece host) { kChromeUIMobileSetupHost, kChromeUIMultiDeviceSetupHost, kChromeUINetworkHost, + kChromeUIProxyConfigHost, kChromeUIOobeHost, kChromeUIOSCreditsHost, kChromeUIOSSettingsHost, @@ -672,6 +675,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 @@ -132,6 +132,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