Add-a-proxy-configuration-page.patch 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398
  1. From: csagan5 <32685696+csagan5@users.noreply.github.com>
  2. Date: Thu, 29 Mar 2018 00:43:32 +0200
  3. Subject: Add a proxy configuration page
  4. Accessible from proxy settings and chrome://proxy
  5. Allows to use a PAC script URL, automatic configuration and explicit proxy
  6. settings.
  7. Offer auto-complete for the proxy page URL.
  8. Store proxy settings in LocalState instead of Profile, so that proxy is used
  9. for SimpleURLLoaders as well.
  10. ---
  11. chrome/android/java/res/values/values.xml | 3 +
  12. .../java/res/xml/privacy_preferences.xml | 4 +
  13. .../privacy/settings/PrivacySettings.java | 5 +-
  14. .../chrome_autocomplete_provider_client.cc | 2 +
  15. chrome/browser/browser_resources.grd | 6 +
  16. chrome/browser/net/proxy_service_factory.cc | 23 +-
  17. chrome/browser/net/proxy_service_factory.h | 3 +
  18. chrome/browser/prefs/browser_prefs.cc | 4 +
  19. .../prefs/chrome_command_line_pref_store.cc | 2 +-
  20. chrome/browser/resources/proxy_config.css | 61 +++
  21. chrome/browser/resources/proxy_config.html | 80 ++++
  22. chrome/browser/resources/proxy_config.js | 263 +++++++++++
  23. chrome/browser/ui/BUILD.gn | 2 +
  24. .../webui/chrome_web_ui_controller_factory.cc | 3 +
  25. chrome/browser/ui/webui/proxy_config_ui.cc | 417 ++++++++++++++++++
  26. chrome/browser/ui/webui/proxy_config_ui.h | 33 ++
  27. chrome/common/webui_url_constants.cc | 4 +
  28. chrome/common/webui_url_constants.h | 2 +
  29. .../core/browser/proxy_policy_handler.cc | 2 +-
  30. .../pref_proxy_config_tracker_impl.cc | 1 +
  31. .../proxy_config/proxy_config_dictionary.cc | 30 +-
  32. .../proxy_config/proxy_config_dictionary.h | 7 +-
  33. net/proxy_resolution/proxy_config.cc | 52 ++-
  34. net/proxy_resolution/proxy_config.h | 3 +
  35. 24 files changed, 997 insertions(+), 15 deletions(-)
  36. create mode 100644 chrome/browser/resources/proxy_config.css
  37. create mode 100644 chrome/browser/resources/proxy_config.html
  38. create mode 100644 chrome/browser/resources/proxy_config.js
  39. create mode 100644 chrome/browser/ui/webui/proxy_config_ui.cc
  40. create mode 100644 chrome/browser/ui/webui/proxy_config_ui.h
  41. diff --git a/chrome/android/java/res/values/values.xml b/chrome/android/java/res/values/values.xml
  42. --- a/chrome/android/java/res/values/values.xml
  43. +++ b/chrome/android/java/res/values/values.xml
  44. @@ -32,6 +32,9 @@
  45. <integer name="reload_button_level_reload">0</integer>
  46. <integer name="reload_button_level_stop">1</integer>
  47. + <string name="proxy_title">Proxy configuration</string>
  48. + <string name="proxy_url">chrome://proxy</string>
  49. +
  50. <!-- Download InfoBar animation. -->
  51. <integer name="download_infobar_sweep_up_delay">500</integer>
  52. <integer name="download_infobar_sweep_down_delay">800</integer>
  53. diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
  54. --- a/chrome/android/java/res/xml/privacy_preferences.xml
  55. +++ b/chrome/android/java/res/xml/privacy_preferences.xml
  56. @@ -7,6 +7,10 @@
  57. xmlns:app="http://schemas.android.com/apk/res-auto"
  58. android:orderingFromXml="false">
  59. + <org.chromium.chrome.browser.about_settings.HyperlinkPreference
  60. + android:key="proxy"
  61. + android:title="@string/proxy_title"
  62. + app:url="@string/proxy_url" />
  63. <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
  64. android:key="can_make_payment"
  65. android:title="@string/can_make_payment_title"
  66. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
  67. --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
  68. +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
  69. @@ -54,12 +54,15 @@ public class PrivacySettings
  70. private static final String PREF_PRIVACY_SANDBOX = "privacy_sandbox";
  71. public static final String PREF_ALLOW_CUSTOM_TAB_INTENTS = "allow_custom_tab_intents";
  72. private static final String PREF_CLOSE_TABS_ON_EXIT = "close_tabs_on_exit";
  73. + private static final String PREF_PROXY_OPTIONS = "proxy";
  74. private static final String[] NEW_PRIVACY_PREFERENCE_ORDER = {PREF_CLEAR_BROWSING_DATA,
  75. PREF_ALWAYS_INCOGNITO,
  76. PREF_CAN_MAKE_PAYMENT, PREF_NETWORK_PREDICTIONS,
  77. PREF_CLOSE_TABS_ON_EXIT,
  78. PREF_SECURE_DNS, PREF_DO_NOT_TRACK, PREF_PRIVACY_SANDBOX,
  79. - PREF_ALLOW_CUSTOM_TAB_INTENTS};
  80. + PREF_ALLOW_CUSTOM_TAB_INTENTS,
  81. + PREF_PROXY_OPTIONS
  82. + };
  83. private ManagedPreferenceDelegate mManagedPreferenceDelegate;
  84. diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
  85. --- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
  86. +++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
  87. @@ -332,6 +332,8 @@ ChromeAutocompleteProviderClient::GetBuiltinsToProvideAsUserTypes() {
  88. std::vector<base::string16> builtins_to_provide;
  89. builtins_to_provide.push_back(
  90. base::ASCIIToUTF16(chrome::kChromeUIFlagsURL));
  91. + builtins_to_provide.push_back(
  92. + base::ASCIIToUTF16(chrome::kChromeUIProxyConfigURL));
  93. builtins_to_provide.push_back(
  94. base::ASCIIToUTF16(chrome::kChromeUIChromeURLsURL));
  95. #if !defined(OS_ANDROID)
  96. diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
  97. --- a/chrome/browser/browser_resources.grd
  98. +++ b/chrome/browser/browser_resources.grd
  99. @@ -162,6 +162,12 @@
  100. <include name="IDR_VIDEO_PLAYER_JS" file="resources\video_tutorials\video_player.js" type="BINDATA" />
  101. </if>
  102. + <!-- Bromite Proxy Configuration UI -->
  103. + <if expr="is_android">
  104. + <include name="IDR_PROXY_CONFIG_HTML" file="resources\proxy_config.html" flattenhtml="true" type="BINDATA" compress="gzip" />
  105. + <include name="IDR_PROXY_CONFIG_JS" file="resources\proxy_config.js" type="BINDATA" compress="gzip" />
  106. + </if>
  107. +
  108. <if expr="not is_android">
  109. <!-- New Tab Page -->
  110. <part file="resources/local_ntp/icons.grdp" />
  111. diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc
  112. --- a/chrome/browser/net/proxy_service_factory.cc
  113. +++ b/chrome/browser/net/proxy_service_factory.cc
  114. @@ -15,6 +15,9 @@
  115. #include "content/public/browser/browser_thread.h"
  116. #include "net/proxy_resolution/configured_proxy_resolution_service.h"
  117. #include "net/proxy_resolution/proxy_config_service.h"
  118. +#include "components/proxy_config/proxy_config_pref_names.h"
  119. +#include "components/prefs/pref_service.h"
  120. +#include "components/prefs/pref_registry_simple.h"
  121. #if BUILDFLAG(IS_CHROMEOS_ASH)
  122. #include "chromeos/network/proxy/proxy_config_service_impl.h"
  123. @@ -59,7 +62,20 @@ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(
  124. return std::make_unique<chromeos::ProxyConfigServiceImpl>(
  125. profile_prefs, local_state_prefs, nullptr);
  126. #else
  127. - return std::make_unique<PrefProxyConfigTrackerImpl>(profile_prefs, nullptr);
  128. + // Migrate from profile_prefs to local_state_prefs
  129. + if (local_state_prefs->GetBoolean("proxy_migrated") == false) {
  130. + const base::DictionaryValue* dict =
  131. + profile_prefs->GetDictionary(proxy_config::prefs::kProxy);
  132. +
  133. + LOG(INFO) << "CreatePrefProxyConfigTrackerOfProfile: Migration from profile to local state";
  134. +
  135. + const base::Value /*ProxyConfigDictionary*/ proxy_dict(dict->Clone());
  136. + local_state_prefs->Set(proxy_config::prefs::kProxy, proxy_dict);
  137. +
  138. + local_state_prefs->SetBoolean("proxy_migrated", true);
  139. + local_state_prefs->CommitPendingWrite();
  140. + }
  141. + return std::make_unique<PrefProxyConfigTrackerImpl>(local_state_prefs, nullptr);
  142. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  143. }
  144. @@ -75,3 +91,8 @@ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
  145. nullptr);
  146. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  147. }
  148. +
  149. +// static
  150. +void ProxyServiceFactory::RegisterPrefs(PrefRegistrySimple* registry) {
  151. + registry->RegisterBooleanPref("proxy_migrated", false);
  152. +}
  153. diff --git a/chrome/browser/net/proxy_service_factory.h b/chrome/browser/net/proxy_service_factory.h
  154. --- a/chrome/browser/net/proxy_service_factory.h
  155. +++ b/chrome/browser/net/proxy_service_factory.h
  156. @@ -8,6 +8,7 @@
  157. #include <memory>
  158. #include "base/macros.h"
  159. +#include "components/prefs/pref_registry_simple.h"
  160. class PrefProxyConfigTracker;
  161. class PrefService;
  162. @@ -37,6 +38,8 @@ class ProxyServiceFactory {
  163. static std::unique_ptr<PrefProxyConfigTracker>
  164. CreatePrefProxyConfigTrackerOfLocalState(PrefService* local_state_prefs);
  165. + static void RegisterPrefs(PrefRegistrySimple* registry);
  166. +
  167. private:
  168. DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyServiceFactory);
  169. };
  170. diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
  171. --- a/chrome/browser/prefs/browser_prefs.cc
  172. +++ b/chrome/browser/prefs/browser_prefs.cc
  173. @@ -156,6 +156,8 @@
  174. #include "printing/buildflags/buildflags.h"
  175. #include "rlz/buildflags/buildflags.h"
  176. +#include "chrome/browser/net/proxy_service_factory.h"
  177. +
  178. #if BUILDFLAG(ENABLE_BACKGROUND_MODE)
  179. #include "chrome/browser/background/background_mode_manager.h"
  180. #endif
  181. @@ -746,6 +748,8 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
  182. PluginsResourceService::RegisterPrefs(registry);
  183. #endif
  184. + ProxyServiceFactory::RegisterPrefs(registry);
  185. +
  186. #if defined(OS_ANDROID)
  187. ::android::RegisterPrefs(registry);
  188. diff --git a/chrome/browser/prefs/chrome_command_line_pref_store.cc b/chrome/browser/prefs/chrome_command_line_pref_store.cc
  189. --- a/chrome/browser/prefs/chrome_command_line_pref_store.cc
  190. +++ b/chrome/browser/prefs/chrome_command_line_pref_store.cc
  191. @@ -156,7 +156,7 @@ void ChromeCommandLinePrefStore::ApplyProxyMode() {
  192. SetValue(
  193. proxy_config::prefs::kProxy,
  194. std::make_unique<base::Value>(ProxyConfigDictionary::CreateFixedServers(
  195. - proxy_server, bypass_list)),
  196. + proxy_server, bypass_list, false)),
  197. WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
  198. }
  199. }
  200. diff --git a/chrome/browser/resources/proxy_config.css b/chrome/browser/resources/proxy_config.css
  201. new file mode 100644
  202. --- /dev/null
  203. +++ b/chrome/browser/resources/proxy_config.css
  204. @@ -0,0 +1,61 @@
  205. +/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
  206. + * Use of this source code is governed by a BSD-style license that can be
  207. + * found in the LICENSE file.
  208. + */
  209. +
  210. +body {
  211. + font-size: 80%;
  212. + margin: 1em;
  213. +}
  214. +
  215. +#main-container {
  216. + max-width: 60em;
  217. + margin-left: auto;
  218. + margin-right: auto;
  219. +}
  220. +
  221. +button {
  222. + display: block;
  223. + font-size: 110%;
  224. + font-weight: bold;
  225. + margin: 10px auto;
  226. + padding: 1em;
  227. + width: 15em;
  228. +}
  229. +
  230. +h2 {
  231. + color: #546E7A;
  232. + font-weight: normal;
  233. + font-size: 170%;
  234. + margin-bottom: 1.5em;
  235. +}
  236. +
  237. +.radio-button-div {
  238. + margin: 7px auto;
  239. +}
  240. +
  241. +.warning {
  242. + color: red;
  243. + font-size: 90%;
  244. +}
  245. +
  246. +.section-container {
  247. + margin-top: 2em;
  248. +}
  249. +
  250. +#file-path-logging,
  251. +#file-path-stopped {
  252. + font-family: monospace;
  253. +}
  254. +
  255. +.outline-box {
  256. + margin-top: 2em;
  257. + border: 1px solid #ababab;
  258. + padding: 0.5em;
  259. + line-height: 1.5em;
  260. +}
  261. +
  262. +textarea {
  263. + width: 95%;
  264. + height: 4em;
  265. +}
  266. diff --git a/chrome/browser/resources/proxy_config.html b/chrome/browser/resources/proxy_config.html
  267. new file mode 100644
  268. --- /dev/null
  269. +++ b/chrome/browser/resources/proxy_config.html
  270. @@ -0,0 +1,80 @@
  271. +<!doctype html>
  272. +<html>
  273. +<head>
  274. +<meta charset="utf-8">
  275. +<if expr="is_android">
  276. +<meta name="viewport" content="width=device-width">
  277. +</if>
  278. +
  279. +<if expr="is_ios">
  280. +<!-- TODO(crbug.com/487000): Remove this once injected by web. -->
  281. +<script src="chrome://resources/js/ios/web_ui.js"></script>
  282. +</if>
  283. +
  284. +<script src="chrome://resources/js/assert.js"></script>
  285. +<script src="chrome://resources/js/util.js"></script>
  286. +<script src="chrome://resources/js/cr.js"></script>
  287. +<script src="chrome://proxy/proxy_config.js"></script>
  288. +<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
  289. +<link rel="stylesheet" href="proxy_config.css">
  290. +<title>Proxy configuration</title>
  291. +</head>
  292. +<body>
  293. + <div id="main-container">
  294. + <!--
  295. + =========================================================================
  296. + View for "pending" state.
  297. + * Only visible briefly, if at all
  298. + =========================================================================
  299. + -->
  300. + <div id="state-pending">
  301. + <h2>Proxy configuration</h2>
  302. + Loading...
  303. + </div>
  304. +
  305. + <!--
  306. + =========================================================================
  307. + View for "available" and "unset" states.
  308. + * Has controls to change or reset proxy configuration.
  309. + =========================================================================
  310. + -->
  311. + <div id="state-main" hidden>
  312. + <h2>Proxy configuration</h2>
  313. + <button id="reset">Reset</button>
  314. + <div class="section-container">
  315. + Reset will update the displayed configuration to match the one currently in use.
  316. + </div>
  317. + <div class="section-container">
  318. + <input type="radio" id="empty" name="mode" value="empty"><label for="empty">System Default</label><br/>
  319. + <input type="radio" id="direct" name="mode" value="direct"><label for="direct">Direct</label><br/>
  320. + <input type="radio" id="auto-detect" name="mode" value="auto-detect"><label for="auto-detect">Auto-detect (WPAD DHCP/DNS)</label><br/>
  321. + <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>
  322. + <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>
  323. + <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>):
  324. + <textarea id="single-proxies"></textarea>
  325. + </label><br/>
  326. + <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/>
  327. + <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/>
  328. + <textarea id="http-proxies"></textarea></label><br/>
  329. + <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/>
  330. + <textarea id="https-proxies"></textarea></label><br/>
  331. + <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/>
  332. + <textarea id="ftp-proxies"></textarea></label><br/>
  333. + <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/>
  334. + <textarea id="fallback-proxies"></textarea></label>
  335. + <div class="outline-box">
  336. + 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/>
  337. + <textarea id="bypass-rules"></textarea><br/>
  338. + <input type="checkbox" id="reverse-bypass" name="reverse-bypass"><label for="reverse-bypass">Reverse the meaning of bypass rules</label>
  339. + </div>
  340. + </div>
  341. + <button id="apply">Apply</button>
  342. + <button id="clear">Clear</button>
  343. + <div class="section-container">
  344. + Clicking on Clear will remove any proxy configuration preference currently in effect.
  345. + </div>
  346. + </div>
  347. +
  348. + </div>
  349. +</body>
  350. +</html>
  351. diff --git a/chrome/browser/resources/proxy_config.js b/chrome/browser/resources/proxy_config.js
  352. new file mode 100644
  353. --- /dev/null
  354. +++ b/chrome/browser/resources/proxy_config.js
  355. @@ -0,0 +1,263 @@
  356. +/*
  357. + This file is part of Bromite.
  358. +
  359. + Bromite is free software: you can redistribute it and/or modify
  360. + it under the terms of the GNU General Public License as published by
  361. + the Free Software Foundation, either version 3 of the License, or
  362. + (at your option) any later version.
  363. +
  364. + Bromite is distributed in the hope that it will be useful,
  365. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  366. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  367. + GNU General Public License for more details.
  368. +
  369. + You should have received a copy of the GNU General Public License
  370. + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
  371. +*/
  372. +
  373. +/**
  374. + * Main entry point called once the page has loaded.
  375. + */
  376. +function onLoad() {
  377. + ProxyConfigView.getInstance();
  378. +}
  379. +
  380. +document.addEventListener('DOMContentLoaded', onLoad);
  381. +
  382. +/**
  383. + * This class handles the presentation of the proxy-config view. Used as a
  384. + * singleton.
  385. + */
  386. +var ProxyConfigView = (function() {
  387. + 'use strict';
  388. +
  389. + // --------------------------------------------------------------------------
  390. +
  391. + var kIdStateDivUninitialized = 'state-pending';
  392. + var kIdStateDivMain = 'state-main';
  393. + var kIdApplyButton = 'apply';
  394. + var kIdResetButton = 'reset';
  395. + var kIdClearButton = 'clear';
  396. +
  397. + var kIdModeEmpty = 'empty';
  398. + var kIdModeDirect = 'direct';
  399. + var kIdModeAutoDetect = 'auto-detect';
  400. + var kIdModeUsePacURL = 'use-pac-url';
  401. +
  402. + var kIdModeUseSingleList = 'use-single-list';
  403. + var kIdModeUseListPerScheme = 'use-list-per-scheme';
  404. +
  405. + var kIdPacURL = 'pac-url';
  406. + var kIdPacMandatory = 'pac-mandatory';
  407. + var kIdBypassRules = 'bypass-rules';
  408. + var kIdReverseBypass = 'reverse-bypass';
  409. + var kIdSingleProxies = 'single-proxies';
  410. + var kIdHttpProxies = 'http-proxies';
  411. + var kIdHttpsProxies = 'https-proxies';
  412. + var kIdFtpProxies = 'ftp-proxies';
  413. + var kIdFallbackProxies = 'fallback-proxies';
  414. +
  415. + /**
  416. + * @constructor
  417. + */
  418. + function ProxyConfigView() {
  419. + this.currentConfig = null;
  420. +
  421. + $(kIdResetButton).onclick = this.onReset_.bind(this);
  422. + $(kIdApplyButton).onclick = this.onApply_.bind(this);
  423. + $(kIdClearButton).onclick = this.onClear_.bind(this);
  424. +
  425. + // Tell ProxyConfigMessageHandler to notify the UI of future state changes
  426. + // from this point on.
  427. + chrome.send('enableNotifyUIWithState');
  428. + }
  429. +
  430. + cr.addSingletonGetter(ProxyConfigView);
  431. +
  432. + ProxyConfigView.prototype = {
  433. + /**
  434. + * Updates the UI to reflect the current state. The state transitions are
  435. + * sent by the browser controller (ProxyConfigMessageHandler):
  436. + *
  437. + * * PENDING - This is the initial state when proxy configuration is opened
  438. + * for the first time, or there was an error during initialization.
  439. + * This state is short-lived and likely not observed; will
  440. + * immediately transition to AVAILABLE).
  441. + *
  442. + * * AVAILABLE - The reported proxy configuration is active; this state is entered
  443. + * on first page load (or right after PENDING if configuration was not
  444. + * available on page load) and every time some configuration change was applied.
  445. + * It can transition to either AVAILABLE or UNSET.
  446. + *
  447. + * * UNSET - Proxy configuration is reported to be currently not set.
  448. + *
  449. + */
  450. + onProxyConfigChanged: function(state) {
  451. + // may happen only on first load; leave the loading page as another update is expected
  452. + // when proxy configuration has finished loading
  453. + if (state.pending) {
  454. + $(kIdStateDivMain).hidden = true;
  455. + $(kIdStateDivUninitialized).hidden = false;
  456. + return;
  457. + }
  458. +
  459. + if (!state.hasOwnProperty('config')) {
  460. + // configuration has been unset, use an empty one
  461. + this.eraseCurrentConfig_();
  462. + } else {
  463. + // save the configuration as current and reset all controls to it
  464. + this.currentConfig = state.config;
  465. + }
  466. +
  467. + this.renderConfig_();
  468. +
  469. + this.toggleButtons_(false);
  470. + $(kIdStateDivUninitialized).hidden = true;
  471. + $(kIdStateDivMain).hidden = false;
  472. + },
  473. +
  474. + /**
  475. + * Set current configuration to an empty (default) one.
  476. + */
  477. + eraseCurrentConfig_: function() {
  478. + this.currentConfig = {
  479. + "auto_detect": false,
  480. + "pending": false,
  481. + "rules": {
  482. + "bypass_rules": "",
  483. + "reverse_bypass": false,
  484. + "type": "none"
  485. + }
  486. + };
  487. + },
  488. +
  489. + /**
  490. + * Serialize the user-selected configuration in an object.
  491. + */
  492. + serializeConfig_: function() {
  493. + if ($(kIdModeEmpty).checked) {
  494. + return {
  495. + "auto_detect": false,
  496. + "rules": {
  497. + "type": "none"
  498. + }
  499. + };
  500. + } else if ($(kIdModeDirect).checked) {
  501. + return {
  502. + "auto_detect": false,
  503. + "rules": {
  504. + "type": "direct"
  505. + }
  506. + };
  507. + } else if ($(kIdModeAutoDetect).checked) {
  508. + return {
  509. + "auto_detect": true
  510. + };
  511. + } else if ($(kIdModeUsePacURL).checked) {
  512. + return {
  513. + "auto_detect": false,
  514. + "pac_url": $(kIdPacURL).value.trim(),
  515. + "pac_mandatory": $(kIdPacMandatory).checked,
  516. + "rules": {}
  517. + };
  518. + } else if ($(kIdModeUseListPerScheme).checked || $(kIdModeUseSingleList).checked) {
  519. + var config = {
  520. + "auto_detect": false,
  521. + "rules": {
  522. + "bypass_rules": $(kIdBypassRules).value.trim(),
  523. + "reverse_bypass": $(kIdReverseBypass).checked,
  524. + "type": "list"
  525. + }
  526. + };
  527. +
  528. + if ($(kIdModeUseListPerScheme).checked) {
  529. + config.rules.type = "list_per_scheme";
  530. +
  531. + config.rules.proxies_for_http = $(kIdHttpProxies).value.trim();
  532. + config.rules.proxies_for_https = $(kIdHttpsProxies).value.trim();
  533. + config.rules.proxies_for_ftp = $(kIdFtpProxies).value.trim();
  534. + config.rules.fallback_proxies = $(kIdFallbackProxies).value.trim();
  535. + } else {
  536. + config.rules.single_proxies = $(kIdSingleProxies).value.trim();
  537. + }
  538. +
  539. + return config;
  540. + }
  541. +
  542. + throw new Error('unexpected mode');
  543. + },
  544. +
  545. + /**
  546. + * Updates the UI to display the current proxy configuration.
  547. + */
  548. + renderConfig_: function() {
  549. + if (this.currentConfig.auto_detect) {
  550. + $(kIdModeAutoDetect).checked = true;
  551. + } else if (this.currentConfig.rules.type == "none") {
  552. + $(kIdModeEmpty).checked = true;
  553. + } else if (this.currentConfig.rules.type == "direct") {
  554. + $(kIdModeDirect).checked = true;
  555. + } else if (this.currentConfig.hasOwnProperty('pac_url')) {
  556. + $(kIdPacURL).value = this.currentConfig.pac_url;
  557. + $(kIdPacMandatory).checked = this.currentConfig.pac_mandatory;
  558. + $(kIdModeUsePacURL).checked = true;
  559. + } else {
  560. + $(kIdBypassRules).value = this.currentConfig.rules.bypass_rules;
  561. + $(kIdReverseBypass).checked = this.currentConfig.rules.reverse_bypass;
  562. +
  563. + switch (this.currentConfig.rules.type) {
  564. + case "list":
  565. + $(kIdModeUseSingleList).checked = true;
  566. + $(kIdSingleProxies).value = this.currentConfig.rules.single_proxies;
  567. + break;
  568. + case "list_per_scheme":
  569. + $(kIdModeUseListPerScheme).checked = true;
  570. + $(kIdHttpProxies).value = this.currentConfig.rules.proxies_for_http;
  571. + $(kIdHttpsProxies).value = this.currentConfig.rules.proxies_for_https;
  572. + $(kIdFtpProxies).value = this.currentConfig.rules.proxies_for_ftp;
  573. + $(kIdFallbackProxies).value = this.currentConfig.rules.fallback_proxies;
  574. + break;
  575. + }
  576. + }
  577. + },
  578. +
  579. + /**
  580. + * Apply the configuration currently displayed.
  581. + */
  582. + onApply_: function() {
  583. + var config = this.serializeConfig_();
  584. +
  585. + // disable buttons; will be enabled back when UI receives a state update
  586. + this.toggleButtons_(true);
  587. + chrome.send('apply', [config]);
  588. + },
  589. +
  590. + /**
  591. + * Apply the configuration currently displayed.
  592. + */
  593. + onClear_: function() {
  594. + // disable buttons; will be enabled back when UI receives a state update
  595. + this.toggleButtons_(true);
  596. + this.eraseCurrentConfig_();
  597. + chrome.send('clear', []);
  598. + },
  599. +
  600. + /**
  601. + * Toggle the disabled status of the action buttons.
  602. + */
  603. + toggleButtons_: function(disabled) {
  604. + $(kIdApplyButton).disabled = disabled;
  605. + $(kIdResetButton).disabled = disabled;
  606. + $(kIdClearButton).disabled = disabled;
  607. + },
  608. +
  609. + /**
  610. + * Reset currently displayed configuration to the last known configuration in use.
  611. + */
  612. + onReset_: function() {
  613. + this.renderConfig_();
  614. + }
  615. + };
  616. +
  617. + return ProxyConfigView;
  618. +})();
  619. diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
  620. --- a/chrome/browser/ui/BUILD.gn
  621. +++ b/chrome/browser/ui/BUILD.gn
  622. @@ -256,6 +256,8 @@ static_library("ui") {
  623. "webui/metrics_handler.h",
  624. "webui/net_export_ui.cc",
  625. "webui/net_export_ui.h",
  626. + "webui/proxy_config_ui.cc",
  627. + "webui/proxy_config_ui.h",
  628. "webui/net_internals/net_internals_ui.cc",
  629. "webui/net_internals/net_internals_ui.h",
  630. "webui/ntp_tiles_internals_ui.cc",
  631. diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
  632. --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
  633. +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
  634. @@ -53,6 +53,7 @@
  635. #include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
  636. #include "chrome/browser/ui/webui/policy/policy_ui.h"
  637. #include "chrome/browser/ui/webui/predictors/predictors_ui.h"
  638. +#include "chrome/browser/ui/webui/proxy_config_ui.h"
  639. #include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h"
  640. #include "chrome/browser/ui/webui/signin_internals_ui.h"
  641. #include "chrome/browser/ui/webui/sync_internals/sync_internals_ui.h"
  642. @@ -558,6 +559,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
  643. return &NewWebUI<MemoryInternalsUI>;
  644. if (url.host_piece() == chrome::kChromeUINetExportHost)
  645. return &NewWebUI<NetExportUI>;
  646. + if (url.host_piece() == chrome::kChromeUIProxyConfigHost)
  647. + return &NewWebUI<ProxyConfigUI>;
  648. if (url.host_piece() == chrome::kChromeUINetInternalsHost)
  649. return &NewWebUI<NetInternalsUI>;
  650. if (url.host_piece() == chrome::kChromeUINTPTilesInternalsHost)
  651. diff --git a/chrome/browser/ui/webui/proxy_config_ui.cc b/chrome/browser/ui/webui/proxy_config_ui.cc
  652. new file mode 100644
  653. --- /dev/null
  654. +++ b/chrome/browser/ui/webui/proxy_config_ui.cc
  655. @@ -0,0 +1,417 @@
  656. +/*
  657. + This file is part of Bromite.
  658. +
  659. + Bromite is free software: you can redistribute it and/or modify
  660. + it under the terms of the GNU General Public License as published by
  661. + the Free Software Foundation, either version 3 of the License, or
  662. + (at your option) any later version.
  663. +
  664. + Bromite is distributed in the hope that it will be useful,
  665. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  666. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  667. + GNU General Public License for more details.
  668. +
  669. + You should have received a copy of the GNU General Public License
  670. + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
  671. +*/
  672. +
  673. +#include "chrome/browser/ui/webui/proxy_config_ui.h"
  674. +
  675. +#include <stdint.h>
  676. +
  677. +#include <memory>
  678. +#include <string>
  679. +#include <vector>
  680. +
  681. +#include "base/bind.h"
  682. +#include "base/command_line.h"
  683. +#include "base/lazy_instance.h"
  684. +#include "base/macros.h"
  685. +#include "base/memory/ref_counted.h"
  686. +#include "base/scoped_observer.h"
  687. +#include "base/strings/string_util.h"
  688. +#include "base/strings/utf_string_conversions.h"
  689. +#include "base/values.h"
  690. +#include "chrome/browser/browser_process.h"
  691. +#include "chrome/browser/net/proxy_service_factory.h"
  692. +#include "chrome/browser/platform_util.h"
  693. +#include "chrome/browser/profiles/profile.h"
  694. +#include "chrome/common/url_constants.h"
  695. +#include "chrome/grit/browser_resources.h"
  696. +#include "components/prefs/pref_service.h"
  697. +#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
  698. +#include "components/proxy_config/proxy_config_pref_names.h"
  699. +#include "components/grit/components_resources.h"
  700. +#include "content/public/browser/browser_thread.h"
  701. +#include "content/public/browser/url_data_source.h"
  702. +#include "content/public/browser/web_contents.h"
  703. +#include "content/public/browser/web_ui.h"
  704. +#include "content/public/browser/web_ui_data_source.h"
  705. +#include "content/public/browser/web_ui_message_handler.h"
  706. +
  707. +#include "url/gurl.h"
  708. +
  709. +using content::BrowserThread;
  710. +using content::WebContents;
  711. +using content::WebUIMessageHandler;
  712. +
  713. +namespace {
  714. +
  715. +content::WebUIDataSource* CreateProxyConfigHTMLSource() {
  716. + content::WebUIDataSource* source =
  717. + content::WebUIDataSource::Create(chrome::kChromeUIProxyConfigHost);
  718. +
  719. + source->UseStringsJs();
  720. + source->AddResourcePath("proxy_config.js", IDR_PROXY_CONFIG_JS);
  721. + source->SetDefaultResource(IDR_PROXY_CONFIG_HTML);
  722. + return source;
  723. +}
  724. +
  725. +// This class receives javascript messages from the renderer.
  726. +// Note that the WebUI infrastructure runs on the UI thread, therefore all of
  727. +// this class's public methods are expected to run on the UI thread.
  728. +class ProxyConfigMessageHandler
  729. + : public WebUIMessageHandler,
  730. + public base::SupportsWeakPtr<ProxyConfigMessageHandler>,
  731. + public net::ProxyConfigService::Observer {
  732. + public:
  733. + // Creates a ProxyConfigMessageHandler that handles message exchanges with the Javascript
  734. + // side of the UI and gets proxy settings from the Web UI associated profile to watch for changes.
  735. + // The created ProxyConfigMessageHandler must be destroyed before |profile|.
  736. + ProxyConfigMessageHandler(Profile *profile);
  737. + ~ProxyConfigMessageHandler() override;
  738. +
  739. + // WebUIMessageHandler implementation.
  740. + void RegisterMessages() override;
  741. +
  742. + // Messages
  743. + void OnEnableNotifyUIWithState(const base::ListValue* list);
  744. + void OnApply(const base::ListValue* config);
  745. + void OnClear(const base::ListValue* config);
  746. +
  747. + // net::ProxyConfigService::Observer implementation:
  748. + // Calls ProxyConfigView.onProxyConfigChanged JavaScript function in the
  749. + // renderer.
  750. + void OnProxyConfigChanged(
  751. + const net::ProxyConfigWithAnnotation& config,
  752. + net::ProxyConfigService::ConfigAvailability availability) override;
  753. +
  754. + private:
  755. + // Not owned.
  756. + PrefService *pref_service_;
  757. + std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
  758. + // Monitors global and Profile prefs related to proxy configuration.
  759. + std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
  760. + bool is_observing_;
  761. +
  762. + void encodeConfig(const net::ProxyConfig& config, base::DictionaryValue& state);
  763. +
  764. + void apply(const net::ProxyConfig& config);
  765. +
  766. + base::WeakPtrFactory<ProxyConfigMessageHandler> weak_ptr_factory_;
  767. +
  768. + DISALLOW_COPY_AND_ASSIGN(ProxyConfigMessageHandler);
  769. +};
  770. +
  771. +ProxyConfigMessageHandler::ProxyConfigMessageHandler(Profile *profile)
  772. + :
  773. + weak_ptr_factory_(this) {
  774. +
  775. + // used to set new configuration preferences
  776. + pref_service_ = g_browser_process->local_state();
  777. + // observer is explicitly added only later in enableNotifyUIWithState
  778. + is_observing_ = false;
  779. +
  780. +// If this is the ChromeOS sign-in profile, just create the tracker from global
  781. +// state.
  782. +#if defined(OS_CHROMEOS)
  783. + if (chromeos::ProfileHelper::IsSigninProfile(profile)) {
  784. + pref_proxy_config_tracker_.reset(
  785. + ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
  786. + g_browser_process->local_state()));
  787. + }
  788. +#endif // defined(OS_CHROMEOS)
  789. +
  790. + if (!pref_proxy_config_tracker_) {
  791. + pref_proxy_config_tracker_ =
  792. + ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(
  793. + profile->GetPrefs(), g_browser_process->local_state());
  794. + }
  795. +
  796. + proxy_config_service_ = ProxyServiceFactory::CreateProxyConfigService(
  797. + pref_proxy_config_tracker_.get());
  798. +}
  799. +
  800. +void ProxyConfigMessageHandler::OnProxyConfigChanged(
  801. + const net::ProxyConfigWithAnnotation& config,
  802. + net::ProxyConfigService::ConfigAvailability availability) {
  803. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
  804. + !BrowserThread::IsThreadInitialized(BrowserThread::UI));
  805. +
  806. + base::DictionaryValue state;
  807. + bool pending = false;
  808. + switch (availability) {
  809. + case net::ProxyConfigService::CONFIG_VALID:
  810. + encodeConfig(config.value(), state);
  811. + break;
  812. + case net::ProxyConfigService::CONFIG_UNSET:
  813. + state.SetPath({"config", "rules", "type"}, base::Value("none"));
  814. + break;
  815. + case net::ProxyConfigService::CONFIG_PENDING:
  816. + //NOTE: this can only happen when triggered manually first time
  817. + pending = true;
  818. + break;
  819. + }
  820. + state.SetKey("pending", base::Value(pending));
  821. +
  822. + // call Javascript function
  823. + web_ui()->CallJavascriptFunctionUnsafe("ProxyConfigView.getInstance().onProxyConfigChanged",
  824. + *state.CreateDeepCopy());
  825. +}
  826. +
  827. +const std::string omitDirect(const std::string pacString) {
  828. + if (pacString == "DIRECT") {
  829. + return "";
  830. + }
  831. + return pacString;
  832. +}
  833. +
  834. +void ProxyConfigMessageHandler::encodeConfig(const net::ProxyConfig& config, base::DictionaryValue& state) {
  835. + // when automatic settings are enabled they take precedence over manual settings
  836. + // automatic settings are either the "auto-detect" flag or the existance of a PAC URL
  837. +
  838. + state.SetPath({"config", "auto_detect"}, base::Value(config.auto_detect()));
  839. +
  840. + auto rules = config.proxy_rules();
  841. + if (config.has_pac_url()) {
  842. + state.SetPath({"config", "pac_url"}, base::Value(config.pac_url().spec()));
  843. + state.SetPath({"config", "pac_mandatory"}, base::Value(config.pac_mandatory()));
  844. + state.SetPath({"config", "rules", "type"}, base::Value("none"));
  845. + state.SetPath({"config", "rules", "bypass_rules"}, base::Value(rules.bypass_rules.ToString()));
  846. + state.SetPath({"config", "rules", "reverse_bypass"}, base::Value(rules.reverse_bypass));
  847. + return;
  848. + }
  849. +
  850. + const char *type;
  851. + switch (rules.type) {
  852. + case net::ProxyConfig::ProxyRules::Type::EMPTY:
  853. + type = "direct";
  854. + break;
  855. + case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
  856. + type = "list";
  857. +
  858. + state.SetPath({"config", "rules", "single_proxies"}, base::Value(omitDirect(rules.single_proxies.ToPacString())));
  859. + break;
  860. + case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
  861. + type = "list_per_scheme";
  862. +
  863. + state.SetPath({"config", "rules", "proxies_for_http"}, base::Value(omitDirect(rules.proxies_for_http.ToPacString())));
  864. + state.SetPath({"config", "rules", "proxies_for_https"}, base::Value(omitDirect(rules.proxies_for_https.ToPacString())));
  865. + state.SetPath({"config", "rules", "proxies_for_ftp"}, base::Value(omitDirect(rules.proxies_for_ftp.ToPacString())));
  866. + state.SetPath({"config", "rules", "fallback_proxies"}, base::Value(omitDirect(rules.fallback_proxies.ToPacString())));
  867. + break;
  868. + default:
  869. + NOTREACHED();
  870. + break;
  871. + }
  872. + state.SetPath({"config", "rules", "type"}, base::Value(type));
  873. + state.SetPath({"config", "rules", "bypass_rules"}, base::Value(rules.bypass_rules.ToString()));
  874. + state.SetPath({"config", "rules", "reverse_bypass"}, base::Value(rules.reverse_bypass));
  875. +}
  876. +
  877. +ProxyConfigMessageHandler::~ProxyConfigMessageHandler() {
  878. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
  879. + !BrowserThread::IsThreadInitialized(BrowserThread::UI));
  880. + if (is_observing_) {
  881. + proxy_config_service_->RemoveObserver(this);
  882. + }
  883. + pref_proxy_config_tracker_->DetachFromPrefService();
  884. +}
  885. +
  886. +void ProxyConfigMessageHandler::RegisterMessages() {
  887. + DCHECK_CURRENTLY_ON(BrowserThread::UI);
  888. +
  889. + web_ui()->RegisterMessageCallback(
  890. + "enableNotifyUIWithState",
  891. + base::BindRepeating(&ProxyConfigMessageHandler::OnEnableNotifyUIWithState,
  892. + base::Unretained(this)));
  893. + web_ui()->RegisterMessageCallback(
  894. + "apply",
  895. + base::BindRepeating(&ProxyConfigMessageHandler::OnApply,
  896. + base::Unretained(this)));
  897. + web_ui()->RegisterMessageCallback(
  898. + "clear",
  899. + base::BindRepeating(&ProxyConfigMessageHandler::OnClear,
  900. + base::Unretained(this)));
  901. +}
  902. +
  903. +// The proxy configuration UI is not notified of state changes until this function runs.
  904. +// After this function, OnProxyConfigChanged() will be called on all proxy state changes.
  905. +void ProxyConfigMessageHandler::OnEnableNotifyUIWithState(
  906. + const base::ListValue* list) {
  907. + DCHECK_CURRENTLY_ON(BrowserThread::UI);
  908. +
  909. + if (!is_observing_) {
  910. + is_observing_ = true;
  911. + proxy_config_service_->AddObserver(this);
  912. + }
  913. +
  914. + net::ProxyConfigWithAnnotation config;
  915. + auto availability = proxy_config_service_->GetLatestProxyConfig(&config);
  916. +
  917. + const base::DictionaryValue* dict =
  918. + pref_service_->GetDictionary(proxy_config::prefs::kProxy);
  919. + ProxyConfigDictionary proxy_dict(dict->Clone());
  920. + ProxyPrefs::ProxyMode mode;
  921. + if (!proxy_dict.GetMode(&mode) || mode == ProxyPrefs::MODE_SYSTEM) {
  922. + availability = net::ProxyConfigService::CONFIG_UNSET;
  923. + }
  924. +
  925. + OnProxyConfigChanged(config, availability);
  926. +}
  927. +
  928. +void ProxyConfigMessageHandler::OnClear(const base::ListValue* list) {
  929. + DCHECK_CURRENTLY_ON(BrowserThread::UI);
  930. +
  931. + const base::Value cfg = ProxyConfigDictionary::CreateSystem();
  932. + pref_service_->Set(proxy_config::prefs::kProxy, cfg);
  933. + pref_service_->CommitPendingWrite();
  934. + OnEnableNotifyUIWithState(nullptr);
  935. +}
  936. +
  937. +void ProxyConfigMessageHandler::OnApply(const base::ListValue* list) {
  938. + DCHECK_CURRENTLY_ON(BrowserThread::UI);
  939. +
  940. + if ((list->GetList().size() != 1) || !list->GetList()[0].is_dict()) {
  941. + return;
  942. + }
  943. +
  944. + const base::DictionaryValue* config = nullptr;
  945. + if (!list->GetDictionary(0, &config))
  946. + return;
  947. +
  948. + const base::Value *autoDetect = config->FindKeyOfType("auto_detect", base::Value::Type::BOOLEAN);
  949. + if (autoDetect == nullptr)
  950. + return;
  951. +
  952. + if (autoDetect->GetBool()) {
  953. + apply(net::ProxyConfig::CreateAutoDetect());
  954. + return;
  955. + }
  956. +
  957. + const base::Value *pacURL = config->FindKeyOfType("pac_url", base::Value::Type::STRING);
  958. + if (pacURL != nullptr) {
  959. + const base::Value *pacMandatory = config->FindKeyOfType("pac_mandatory", base::Value::Type::BOOLEAN);
  960. + if (pacMandatory == nullptr)
  961. + return;
  962. + auto proxyConfig = net::ProxyConfig::CreateFromCustomPacURL(GURL(pacURL->GetString()));
  963. + proxyConfig.set_pac_mandatory(pacMandatory->GetBool());
  964. +
  965. + apply(proxyConfig);
  966. + return;
  967. + }
  968. +
  969. + const base::Value *rules = config->FindKeyOfType("rules", base::Value::Type::DICTIONARY);
  970. + if (rules == nullptr)
  971. + return;
  972. +
  973. + const base::Value *type = rules->FindKeyOfType("type", base::Value::Type::STRING);
  974. + if (type == nullptr)
  975. + return;
  976. +
  977. + net::ProxyConfig proxyConfig;
  978. +
  979. + bool readBypass = false;
  980. +
  981. + auto t = type->GetString();
  982. + if (t == "list") {
  983. + const base::Value *single_proxies = rules->FindKeyOfType("single_proxies", base::Value::Type::STRING);
  984. + if (single_proxies == nullptr)
  985. + return;
  986. + proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
  987. + proxyConfig.proxy_rules().single_proxies.SetFromPacString(single_proxies->GetString());
  988. + readBypass = true;
  989. + } else if (t == "list_per_scheme") {
  990. + const base::Value *http = rules->FindKeyOfType("proxies_for_http", base::Value::Type::STRING);
  991. + if (http == nullptr)
  992. + return;
  993. +
  994. + const base::Value *https = rules->FindKeyOfType("proxies_for_https", base::Value::Type::STRING);
  995. + if (https == nullptr)
  996. + return;
  997. +
  998. + const base::Value *ftp = rules->FindKeyOfType("proxies_for_ftp", base::Value::Type::STRING);
  999. + if (ftp == nullptr)
  1000. + return;
  1001. +
  1002. + const base::Value *fallback = rules->FindKeyOfType("fallback_proxies", base::Value::Type::STRING);
  1003. + if (fallback == nullptr)
  1004. + return;
  1005. +
  1006. + proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
  1007. + proxyConfig.proxy_rules().proxies_for_http.SetFromPacString(http->GetString());
  1008. + proxyConfig.proxy_rules().proxies_for_https.SetFromPacString(https->GetString());
  1009. + proxyConfig.proxy_rules().proxies_for_ftp.SetFromPacString(ftp->GetString());
  1010. + proxyConfig.proxy_rules().fallback_proxies.SetFromPacString(fallback->GetString());
  1011. + readBypass = true;
  1012. + } else if (t == "direct") {
  1013. + proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::EMPTY;
  1014. + } else if (t == "none") {
  1015. + OnClear(nullptr);
  1016. + return;
  1017. + } else {
  1018. + // invalid type
  1019. + LOG(WARNING) << "invalid proxy configuration type";
  1020. + return;
  1021. + }
  1022. +
  1023. + // bypass rules and reverse flag are common to both list types of proxy rules
  1024. + if (readBypass) {
  1025. + const base::Value *bypass_rules = rules->FindKeyOfType("bypass_rules", base::Value::Type::STRING);
  1026. + if (bypass_rules == nullptr)
  1027. + return;
  1028. +
  1029. + const base::Value *reverse_bypass = rules->FindKeyOfType("reverse_bypass", base::Value::Type::BOOLEAN);
  1030. + if (reverse_bypass == nullptr)
  1031. + return;
  1032. +
  1033. + proxyConfig.proxy_rules().bypass_rules.ParseFromString(bypass_rules->GetString());
  1034. + proxyConfig.proxy_rules().reverse_bypass = reverse_bypass->GetBool();
  1035. + }
  1036. +
  1037. + apply(proxyConfig);
  1038. +}
  1039. +
  1040. +void ProxyConfigMessageHandler::apply(const net::ProxyConfig& proxyConfig) {
  1041. + if (proxyConfig.auto_detect()) {
  1042. + const base::Value cfg = ProxyConfigDictionary::CreateAutoDetect();
  1043. + pref_service_->Set(proxy_config::prefs::kProxy, cfg);
  1044. + } else if (proxyConfig.has_pac_url()) {
  1045. + const base::Value cfg = ProxyConfigDictionary::CreatePacScript(proxyConfig.pac_url().spec(), proxyConfig.pac_mandatory());
  1046. + pref_service_->Set(proxy_config::prefs::kProxy, cfg);
  1047. + } else if (proxyConfig.proxy_rules().type == net::ProxyConfig::ProxyRules::Type::EMPTY) {
  1048. + const base::Value cfg = ProxyConfigDictionary::CreateDirect();
  1049. + pref_service_->Set(proxy_config::prefs::kProxy, cfg);
  1050. + } else {
  1051. + auto proxyRulesAsString = proxyConfig.proxy_rules().ToString();
  1052. + auto bypassRulesAsString = proxyConfig.proxy_rules().bypass_rules.ToString();
  1053. +
  1054. + // fixed servers
  1055. + const base::Value cfg = ProxyConfigDictionary::CreateFixedServers(proxyRulesAsString,
  1056. + bypassRulesAsString, proxyConfig.proxy_rules().reverse_bypass);
  1057. + pref_service_->Set(proxy_config::prefs::kProxy, cfg);
  1058. + }
  1059. + pref_service_->CommitPendingWrite();
  1060. + OnEnableNotifyUIWithState(nullptr);
  1061. +}
  1062. +
  1063. +} // namespace
  1064. +
  1065. +ProxyConfigUI::ProxyConfigUI(content::WebUI* web_ui) : WebUIController(web_ui) {
  1066. + Profile* profile = Profile::FromWebUI(web_ui);
  1067. +
  1068. + web_ui->AddMessageHandler(std::make_unique<ProxyConfigMessageHandler>(profile));
  1069. +
  1070. + // Set up the chrome://proxy/ source.
  1071. + content::WebUIDataSource::Add(profile, CreateProxyConfigHTMLSource());
  1072. +}
  1073. diff --git a/chrome/browser/ui/webui/proxy_config_ui.h b/chrome/browser/ui/webui/proxy_config_ui.h
  1074. new file mode 100644
  1075. --- /dev/null
  1076. +++ b/chrome/browser/ui/webui/proxy_config_ui.h
  1077. @@ -0,0 +1,33 @@
  1078. +/*
  1079. + This file is part of Bromite.
  1080. +
  1081. + Bromite is free software: you can redistribute it and/or modify
  1082. + it under the terms of the GNU General Public License as published by
  1083. + the Free Software Foundation, either version 3 of the License, or
  1084. + (at your option) any later version.
  1085. +
  1086. + Bromite is distributed in the hope that it will be useful,
  1087. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  1088. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1089. + GNU General Public License for more details.
  1090. +
  1091. + You should have received a copy of the GNU General Public License
  1092. + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
  1093. +*/
  1094. +
  1095. +#ifndef CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
  1096. +#define CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
  1097. +
  1098. +#include "base/macros.h"
  1099. +#include "content/public/browser/web_ui_controller.h"
  1100. +
  1101. +// The WebUI for chrome://proxy/.
  1102. +class ProxyConfigUI : public content::WebUIController {
  1103. + public:
  1104. + explicit ProxyConfigUI(content::WebUI* web_ui);
  1105. +
  1106. + private:
  1107. + DISALLOW_COPY_AND_ASSIGN(ProxyConfigUI);
  1108. +};
  1109. +
  1110. +#endif // CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_
  1111. diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
  1112. --- a/chrome/common/webui_url_constants.cc
  1113. +++ b/chrome/common/webui_url_constants.cc
  1114. @@ -33,6 +33,8 @@ const char kChromeUICertificateViewerHost[] = "view-cert";
  1115. const char kChromeUICertificateViewerURL[] = "chrome://view-cert/";
  1116. const char kChromeUIChromeSigninHost[] = "chrome-signin";
  1117. const char kChromeUIChromeSigninURL[] = "chrome://chrome-signin/";
  1118. +const char kChromeUIProxyConfigHost[] = "proxy";
  1119. +const char kChromeUIProxyConfigURL[] = "chrome://proxy/";
  1120. const char kChromeUIChromeURLsHost[] = "chrome-urls";
  1121. const char kChromeUIChromeURLsURL[] = "chrome://chrome-urls/";
  1122. const char kChromeUIComponentsHost[] = "components";
  1123. @@ -327,6 +329,7 @@ bool IsSystemWebUIHost(base::StringPiece host) {
  1124. kChromeUIMobileSetupHost,
  1125. kChromeUIMultiDeviceSetupHost,
  1126. kChromeUINetworkHost,
  1127. + kChromeUIProxyConfigHost,
  1128. kChromeUIOobeHost,
  1129. kChromeUIOSCreditsHost,
  1130. kChromeUIOSSettingsHost,
  1131. @@ -539,6 +542,7 @@ const char* const kChromeHostURLs[] = {
  1132. #if !defined(OS_ANDROID)
  1133. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  1134. kChromeUIAppLauncherPageHost,
  1135. + kChromeUIProxyConfigHost,
  1136. #endif
  1137. kChromeUIBookmarksHost,
  1138. kChromeUIDownloadsHost,
  1139. diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
  1140. --- a/chrome/common/webui_url_constants.h
  1141. +++ b/chrome/common/webui_url_constants.h
  1142. @@ -120,6 +120,8 @@ extern const char kChromeUIMemoryInternalsHost[];
  1143. extern const char kChromeUINTPTilesInternalsHost[];
  1144. extern const char kChromeUINaClHost[];
  1145. extern const char kChromeUINetExportHost[];
  1146. +extern const char kChromeUIProxyConfigHost[];
  1147. +extern const char kChromeUIProxyConfigURL[];
  1148. extern const char kChromeUINetInternalsHost[];
  1149. extern const char kChromeUINetInternalsURL[];
  1150. extern const char kChromeUINewTabHost[];
  1151. diff --git a/components/policy/core/browser/proxy_policy_handler.cc b/components/policy/core/browser/proxy_policy_handler.cc
  1152. --- a/components/policy/core/browser/proxy_policy_handler.cc
  1153. +++ b/components/policy/core/browser/proxy_policy_handler.cc
  1154. @@ -200,7 +200,7 @@ void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
  1155. bypass_list->GetAsString(&bypass_list_string);
  1156. prefs->SetValue(proxy_config::prefs::kProxy,
  1157. ProxyConfigDictionary::CreateFixedServers(
  1158. - proxy_server, bypass_list_string));
  1159. + proxy_server, bypass_list_string, false));
  1160. }
  1161. break;
  1162. }
  1163. diff --git a/components/proxy_config/pref_proxy_config_tracker_impl.cc b/components/proxy_config/pref_proxy_config_tracker_impl.cc
  1164. --- a/components/proxy_config/pref_proxy_config_tracker_impl.cc
  1165. +++ b/components/proxy_config/pref_proxy_config_tracker_impl.cc
  1166. @@ -380,6 +380,7 @@ bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
  1167. if (proxy_dict.GetBypassList(&proxy_bypass)) {
  1168. proxy_config.proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
  1169. }
  1170. + proxy_config.proxy_rules().reverse_bypass = proxy_dict.HasReverseBypass();
  1171. *config = net::ProxyConfigWithAnnotation(
  1172. proxy_config, kSettingsProxyConfigTrafficAnnotation);
  1173. return true;
  1174. diff --git a/components/proxy_config/proxy_config_dictionary.cc b/components/proxy_config/proxy_config_dictionary.cc
  1175. --- a/components/proxy_config/proxy_config_dictionary.cc
  1176. +++ b/components/proxy_config/proxy_config_dictionary.cc
  1177. @@ -28,6 +28,8 @@ const char kProxyPacMandatory[] = "pac_mandatory";
  1178. // String containing proxy bypass rules. For a specification of the
  1179. // expected syntax see net::ProxyBypassRules::ParseFromString().
  1180. const char kProxyBypassList[] = "bypass_list";
  1181. +// Boolean telling whether to reverse the meaning of the bypass list.
  1182. +const char kProxyReverseBypass[] = "reverse_bypass";
  1183. } // namespace
  1184. @@ -72,6 +74,14 @@ bool ProxyConfigDictionary::HasBypassList() const {
  1185. return dict_.FindKey(kProxyBypassList);
  1186. }
  1187. +bool ProxyConfigDictionary::HasReverseBypass() const {
  1188. + const base::Value* value = dict_.FindKey(kProxyReverseBypass);
  1189. + if (!value || !value->is_bool()) {
  1190. + return false;
  1191. + }
  1192. + return value->GetBool();
  1193. +}
  1194. +
  1195. const base::Value& ProxyConfigDictionary::GetDictionary() const {
  1196. return dict_;
  1197. }
  1198. @@ -79,29 +89,30 @@ const base::Value& ProxyConfigDictionary::GetDictionary() const {
  1199. // static
  1200. base::Value ProxyConfigDictionary::CreateDirect() {
  1201. return CreateDictionary(ProxyPrefs::MODE_DIRECT, std::string(), false,
  1202. - std::string(), std::string());
  1203. + std::string(), std::string(), false);
  1204. }
  1205. // static
  1206. base::Value ProxyConfigDictionary::CreateAutoDetect() {
  1207. return CreateDictionary(ProxyPrefs::MODE_AUTO_DETECT, std::string(), false,
  1208. - std::string(), std::string());
  1209. + std::string(), std::string(), false);
  1210. }
  1211. // static
  1212. base::Value ProxyConfigDictionary::CreatePacScript(const std::string& pac_url,
  1213. bool pac_mandatory) {
  1214. return CreateDictionary(ProxyPrefs::MODE_PAC_SCRIPT, pac_url, pac_mandatory,
  1215. - std::string(), std::string());
  1216. + std::string(), std::string(), false);
  1217. }
  1218. // static
  1219. base::Value ProxyConfigDictionary::CreateFixedServers(
  1220. const std::string& proxy_server,
  1221. - const std::string& bypass_list) {
  1222. + const std::string& bypass_list,
  1223. + bool reverse_bypass) {
  1224. if (!proxy_server.empty()) {
  1225. return CreateDictionary(ProxyPrefs::MODE_FIXED_SERVERS, std::string(),
  1226. - false, proxy_server, bypass_list);
  1227. + false, proxy_server, bypass_list, reverse_bypass);
  1228. } else {
  1229. return CreateDirect();
  1230. }
  1231. @@ -110,7 +121,7 @@ base::Value ProxyConfigDictionary::CreateFixedServers(
  1232. // static
  1233. base::Value ProxyConfigDictionary::CreateSystem() {
  1234. return CreateDictionary(ProxyPrefs::MODE_SYSTEM, std::string(), false,
  1235. - std::string(), std::string());
  1236. + std::string(), std::string(), false);
  1237. }
  1238. // static
  1239. @@ -119,7 +130,8 @@ base::Value ProxyConfigDictionary::CreateDictionary(
  1240. const std::string& pac_url,
  1241. bool pac_mandatory,
  1242. const std::string& proxy_server,
  1243. - const std::string& bypass_list) {
  1244. + const std::string& bypass_list,
  1245. + bool reverse_bypass) {
  1246. base::Value dict(base::Value::Type::DICTIONARY);
  1247. dict.SetKey(kProxyMode, base::Value(ProxyModeToString(mode)));
  1248. if (!pac_url.empty()) {
  1249. @@ -128,8 +140,10 @@ base::Value ProxyConfigDictionary::CreateDictionary(
  1250. }
  1251. if (!proxy_server.empty())
  1252. dict.SetKey(kProxyServer, base::Value(proxy_server));
  1253. - if (!bypass_list.empty())
  1254. + if (!bypass_list.empty()) {
  1255. dict.SetKey(kProxyBypassList, base::Value(bypass_list));
  1256. + dict.SetKey(kProxyReverseBypass, base::Value(reverse_bypass));
  1257. + }
  1258. return dict;
  1259. }
  1260. diff --git a/components/proxy_config/proxy_config_dictionary.h b/components/proxy_config/proxy_config_dictionary.h
  1261. --- a/components/proxy_config/proxy_config_dictionary.h
  1262. +++ b/components/proxy_config/proxy_config_dictionary.h
  1263. @@ -38,6 +38,7 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary {
  1264. bool GetProxyServer(std::string* out) const;
  1265. bool GetBypassList(std::string* out) const;
  1266. bool HasBypassList() const;
  1267. + bool HasReverseBypass() const;
  1268. const base::Value& GetDictionary() const;
  1269. @@ -46,7 +47,8 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary {
  1270. static base::Value CreatePacScript(const std::string& pac_url,
  1271. bool pac_mandatory);
  1272. static base::Value CreateFixedServers(const std::string& proxy_server,
  1273. - const std::string& bypass_list);
  1274. + const std::string& bypass_list,
  1275. + bool reverse_bypass);
  1276. static base::Value CreateSystem();
  1277. // Encodes the proxy server as "<url-scheme>=<proxy-scheme>://<proxy>".
  1278. @@ -62,7 +64,8 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary {
  1279. const std::string& pac_url,
  1280. bool pac_mandatory,
  1281. const std::string& proxy_server,
  1282. - const std::string& bypass_list);
  1283. + const std::string& bypass_list,
  1284. + bool reverse_bypass);
  1285. base::Value dict_;
  1286. diff --git a/net/proxy_resolution/proxy_config.cc b/net/proxy_resolution/proxy_config.cc
  1287. --- a/net/proxy_resolution/proxy_config.cc
  1288. +++ b/net/proxy_resolution/proxy_config.cc
  1289. @@ -110,7 +110,7 @@ void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
  1290. &single_proxies,
  1291. ProxyServer::SCHEME_HTTP);
  1292. type = Type::PROXY_LIST;
  1293. - return;
  1294. + continue;
  1295. }
  1296. // Trim whitespace off the url scheme.
  1297. @@ -141,6 +141,56 @@ void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
  1298. }
  1299. }
  1300. +std::string ProxyConfig::ProxyRules::ToString() const {
  1301. + if (type == Type::EMPTY) {
  1302. + return "";
  1303. + }
  1304. +
  1305. + // special case: a single proxy servers list specified
  1306. + if (type == Type::PROXY_LIST) {
  1307. + std::string proxy_list;
  1308. + for (const ProxyServer& proxy_server :
  1309. + single_proxies.GetAll()) {
  1310. + proxy_list += proxy_server.ToURI() + ";";
  1311. + }
  1312. + // remove last semicolon
  1313. + if (proxy_list.length() != 0 ) {
  1314. + proxy_list.pop_back();
  1315. + }
  1316. + return proxy_list;
  1317. + }
  1318. +
  1319. + if (type != Type::PROXY_LIST_PER_SCHEME) {
  1320. + NOTREACHED();
  1321. + // Unexpected LIST with fallback, or other type values
  1322. + return "";
  1323. + }
  1324. +
  1325. + // start to build a per-scheme list
  1326. + std::string list;
  1327. + for (const ProxyServer& proxy_server :
  1328. + proxies_for_http.GetAll()) {
  1329. + list += "http=" + proxy_server.ToURI() + ";";
  1330. + }
  1331. + for (const ProxyServer& proxy_server :
  1332. + proxies_for_https.GetAll()) {
  1333. + list += "https=" + proxy_server.ToURI() + ";";
  1334. + }
  1335. + for (const ProxyServer& proxy_server :
  1336. + proxies_for_ftp.GetAll()) {
  1337. + list += "ftp=" + proxy_server.ToURI() + ";";
  1338. + }
  1339. + for (const ProxyServer& proxy_server :
  1340. + fallback_proxies.GetAll()) {
  1341. + list += "socks=" + proxy_server.ToURI() + ";";
  1342. + }
  1343. + if (list.length() != 0 ) {
  1344. + // remove last semicolon
  1345. + list.pop_back();
  1346. + }
  1347. + return list;
  1348. +}
  1349. +
  1350. const ProxyList* ProxyConfig::ProxyRules::MapUrlSchemeToProxyList(
  1351. const std::string& url_scheme) const {
  1352. const ProxyList* proxy_server_list = const_cast<ProxyRules*>(this)->
  1353. diff --git a/net/proxy_resolution/proxy_config.h b/net/proxy_resolution/proxy_config.h
  1354. --- a/net/proxy_resolution/proxy_config.h
  1355. +++ b/net/proxy_resolution/proxy_config.h
  1356. @@ -103,6 +103,9 @@ class NET_EXPORT ProxyConfig {
  1357. // and use socks4://foopy2 for all other
  1358. // URLs.
  1359. void ParseFromString(const std::string& proxy_rules);
  1360. + // Returns the proxy rules in a format that can be parsed by ParseFromString;
  1361. + // all information except bypass rules is used.
  1362. + std::string ToString() const;
  1363. // Returns one of {&proxies_for_http, &proxies_for_https, &proxies_for_ftp,
  1364. // &fallback_proxies}, or NULL if there is no proxy to use.
  1365. --
  1366. 2.17.1