diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-10-24 11:30:15 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-10-30 12:56:19 +0000 |
commit | 6036726eb981b6c4b42047513b9d3f4ac865daac (patch) | |
tree | 673593e70678e7789766d1f732eb51f613a2703b /chromium/chrome/browser/resources/settings | |
parent | 466052c4e7c052268fd931888cd58961da94c586 (diff) |
BASELINE: Update Chromium to 70.0.3538.78
Change-Id: Ie634710bf039e26c1957f4ae45e101bd4c434ae7
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/chrome/browser/resources/settings')
193 files changed, 5143 insertions, 2576 deletions
diff --git a/chromium/chrome/browser/resources/settings/BUILD.gn b/chromium/chrome/browser/resources/settings/BUILD.gn index a21d2781afe..94da713a1ce 100644 --- a/chromium/chrome/browser/resources/settings/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/BUILD.gn @@ -22,6 +22,10 @@ if (optimize_webui) { "vulcanized.html", "lazy_load.vulcanized.html", ] + html_out_files_polymer2 = [ + "vulcanized.p2.html", + "lazy_load.vulcanized.p2.html", + ] insert_in_head = "<base href=\"chrome://settings\">" input = rebase_path("$target_gen_dir/$unpak_folder", root_build_dir) js_out_files = [ @@ -124,9 +128,9 @@ js_library("extension_control_browser_proxy") { js_library("find_shortcut_behavior") { deps = [ - "//third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior:iron-a11y-keys-behavior-extracted", "//ui/webui/resources/js:assert", "//ui/webui/resources/js:cr", + "//ui/webui/resources/js/cr/ui:command", ] } diff --git a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html index 0d071105c46..e9e35f21160 100644 --- a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html +++ b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html @@ -15,21 +15,21 @@ <template> <style include="settings-shared"> h2 { - -webkit-padding-start: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-padding); } .sub-item { - -webkit-margin-start: var(--settings-indent-width); + margin-inline-start: var(--settings-indent-width); } h2 ~ .settings-box, h2 ~ settings-toggle-button, iron-collapse .settings-box, iron-collapse settings-toggle-button { - -webkit-margin-end: var(--settings-box-row-padding); - -webkit-margin-start: var(--settings-box-row-indent); - -webkit-padding-end: 0; - -webkit-padding-start: 0; + margin-inline-end: var(--settings-box-row-padding); + margin-inline-start: var(--settings-box-row-indent); + padding-inline-end: 0; + padding-inline-start: 0; } </style> <div class="settings-box row first"> @@ -244,6 +244,11 @@ pref="{{prefs.settings.a11y.mono_audio}}" label="$i18n{monoAudioLabel}"> </settings-toggle-button> + <settings-toggle-button id="startupSoundEnabled" class="first" + pref=" " + on-change="toggleStartupSoundEnabled_" + label="$i18n{startupSoundLabel}"> + </settings-toggle-button> <template is="dom-if" if="[[!isGuest_]]"> <a class="settings-box two-line inherit-color no-outline" tabindex="-1" diff --git a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js index 2466b990bbe..d8acc989fc0 100644 --- a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js +++ b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js @@ -114,6 +114,14 @@ Polymer({ chrome.send('initializeKeyboardWatcher'); }, + /** @override */ + ready: function() { + this.addWebUIListener( + 'startup-sound-enabled-updated', + this.updateStartupSoundEnabled_.bind(this)); + chrome.send('getStartupSoundEnabled'); + }, + /** * Updates the Select-to-Speak description text based on: * 1. Whether Select-to-Speak is enabled. @@ -133,6 +141,23 @@ Polymer({ hasKeyboard ? keyboardString : noKeyboardString; }, + /** + * @param {!CustomEvent} e + * @private + */ + toggleStartupSoundEnabled_: function(e) { + let checked = /** @type {boolean} */ (e.detail); + chrome.send('setStartupSoundEnabled', [checked]); + }, + + /** + * @param {boolean} enabled + * @private + */ + updateStartupSoundEnabled_: function(enabled) { + this.$.startupSoundEnabled.checked = enabled; + }, + /** @private */ onManageTtsSettingsTap_: function() { settings.navigateTo(settings.routes.MANAGE_TTS_SETTINGS); diff --git a/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.html b/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.html index 565e2243c62..47e2e5abf5b 100644 --- a/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.html +++ b/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.html @@ -16,14 +16,14 @@ <template> <style include="settings-shared md-select"> h2 { - -webkit-padding-start: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-padding); } .settings-box { - -webkit-margin-end: var(--settings-box-row-padding); - -webkit-margin-start: var(--settings-box-row-indent); - -webkit-padding-end: 0; - -webkit-padding-start: 0; + margin-inline-end: var(--settings-box-row-padding); + margin-inline-start: var(--settings-box-row-indent); + padding-inline-end: 0; + padding-inline-start: 0; } #previewInput { @@ -32,7 +32,7 @@ } #previewInput paper-button { - -webkit-margin-start: 8px; + margin-inline-start: 8px; } </style> @@ -147,15 +147,11 @@ <div id="extension_name_[[index]]" class="start"> [[extension.name]] </div> - <a href="[[extension.optionsPage]]" - tabindex=-1 - target="_blank" + <paper-button on-click="onEngineSettingsTap_" aria-describedby$="extension_name_[[index]]" hidden$="[[!extension.optionsPage]]"> - <paper-button> - $i18n{settings} - </paper-button> - </a> + $i18n{settings} + </paper-button> </div> </template> </template> diff --git a/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.js b/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.js index f6e16daae7f..0e27875f62b 100644 --- a/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.js +++ b/chromium/chrome/browser/resources/settings/a11y_page/tts_subpage.js @@ -360,4 +360,13 @@ Polymer({ chrome.metricsPrivate.recordSparseHashable( 'TextToSpeech.Settings.DefaultVoicePicked', newDefault); }, + + /** + * @param {{model:Object}} event + * @private + */ + onEngineSettingsTap_: function(event) { + chrome.send('wakeTtsEngine'); + window.open(event.model.extension.optionsPage); + }, }); diff --git a/chromium/chrome/browser/resources/settings/about_page/about_page.html b/chromium/chrome/browser/resources/settings/about_page/about_page.html index 720a636c74c..4bc8e37370d 100644 --- a/chromium/chrome/browser/resources/settings/about_page/about_page.html +++ b/chromium/chrome/browser/resources/settings/about_page/about_page.html @@ -53,11 +53,11 @@ } img { - -webkit-margin-end: var(--about-page-image-space); + margin-inline-end: var(--about-page-image-space); } iron-icon { - -webkit-margin-end: var(--about-page-image-space); + margin-inline-end: var(--about-page-image-space); min-width: 32px; /* The width of the product-logo img. */ } diff --git a/chromium/chrome/browser/resources/settings/about_page/about_page.js b/chromium/chrome/browser/resources/settings/about_page/about_page.js index 0274720ac5a..e03fda011c3 100644 --- a/chromium/chrome/browser/resources/settings/about_page/about_page.js +++ b/chromium/chrome/browser/resources/settings/about_page/about_page.js @@ -41,7 +41,10 @@ Polymer({ regulatoryInfo_: Object, /** @private */ - hasEndOfLife_: Boolean, + hasEndOfLife_: { + type: Boolean, + value: false, + }, /** @private */ showCrostini: Boolean, @@ -72,18 +75,25 @@ Polymer({ // </if> /** @private */ - showUpdateStatus_: Boolean, + showUpdateStatus_: { + type: Boolean, + value: false, + }, /** @private */ showButtonContainer_: Boolean, /** @private */ - showRelaunch_: Boolean, + showRelaunch_: { + type: Boolean, + value: false, + }, // <if expr="chromeos"> /** @private */ showRelaunchAndPowerwash_: { type: Boolean, + value: false, computed: 'computeShowRelaunchAndPowerwash_(' + 'currentUpdateStatusEvent_, targetChannel_, currentChannel_)', }, diff --git a/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html b/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html index e0c6761e7be..c604d6dd566 100644 --- a/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html +++ b/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html @@ -14,7 +14,7 @@ <template> <style include="settings-shared"> cr-policy-indicator { - -webkit-margin-start: var(--cr-controlled-by-spacing); + margin-inline-start: var(--cr-controlled-by-spacing); } /* The command line string can contain very long substrings that diff --git a/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html index e3e305c9e18..720d25d126a 100644 --- a/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html +++ b/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html @@ -23,7 +23,7 @@ <template> <style include="settings-shared md-select iron-flex"> .secondary-button ~ .secondary-button { - -webkit-margin-start: 12px; + margin-inline-start: 12px; } /* Lines up with cr-input. */ @@ -40,11 +40,11 @@ * grit expressions and dom-if templates. That leads to a tricky thing * to style correctly, these are a workaround. */ #themeRow paper-button { - -webkit-margin-end: 20px; + margin-inline-end: 20px; } #themeRow .separator { - -webkit-margin-start: 0; + margin-inline-start: 0; } </style> <settings-animated-pages id="pages" section="appearance" diff --git a/chromium/chrome/browser/resources/settings/appearance_page/home_url_input.js b/chromium/chrome/browser/resources/settings/appearance_page/home_url_input.js index a9939ce9517..870e6c10fa7 100644 --- a/chromium/chrome/browser/resources/settings/appearance_page/home_url_input.js +++ b/chromium/chrome/browser/resources/settings/appearance_page/home_url_input.js @@ -123,7 +123,7 @@ Polymer({ /** * This function prevents unwanted change of selection of the containing * paper-radio-group, when the user traverses the input with arrow keys. - * @param {!Event} event + * @param {!Event} e * @private */ stopKeyEventPropagation_: function(e) { diff --git a/chromium/chrome/browser/resources/settings/basic_page/basic_page.html b/chromium/chrome/browser/resources/settings/basic_page/basic_page.html index e3369e5ef3b..ec867412506 100644 --- a/chromium/chrome/browser/resources/settings/basic_page/basic_page.html +++ b/chromium/chrome/browser/resources/settings/basic_page/basic_page.html @@ -85,7 +85,7 @@ } iron-icon { - -webkit-margin-start: 16px; + margin-inline-start: 16px; } </style> <template is="dom-if" if="[[showBasicPage_( @@ -110,18 +110,6 @@ </settings-internet-page> </settings-section> </template> - <template is="dom-if" - if="[[canShowMultideviceSection_(showMultidevice, pageVisibility)]]" - restamp> - <settings-section page-title="$i18n{multidevicePageTitle}" - section="multidevice" - hidden$="[[!doesAccountSupportMultiDeviceSection_]]"> - <settings-multidevice-page-container prefs="{{prefs}}" - does-potential-connected-phone-exist= - "{{doesAccountSupportMultiDeviceSection_}}"> - </settings-multidevice-page-container> - </settings-section> - </template> <template is="dom-if" if="[[showPage_(pageVisibility.bluetooth)]]" restamp> <settings-section page-title="$i18n{bluetoothPageTitle}" @@ -130,6 +118,18 @@ </settings-bluetooth-page> </settings-section> </template> + <template is="dom-if" + if="[[canShowMultideviceSection_(showMultidevice, pageVisibility)]]" + restamp> + <settings-section page-title="$i18n{multidevicePageTitle}" + section="multidevice" + hidden$="[[!doesChromebookSupportMultiDeviceSection_]]"> + <settings-multidevice-page-container + does-chromebook-support-multi-device-features= + "{{doesChromebookSupportMultiDeviceSection_}}"> + </settings-multidevice-page-container> + </settings-section> + </template> </if> <template is="dom-if" if="[[showChangePassword]]" restamp> <settings-section section="changePassword"> diff --git a/chromium/chrome/browser/resources/settings/basic_page/basic_page.js b/chromium/chrome/browser/resources/settings/basic_page/basic_page.js index b0e03709417..0a99b639d16 100644 --- a/chromium/chrome/browser/resources/settings/basic_page/basic_page.js +++ b/chromium/chrome/browser/resources/settings/basic_page/basic_page.js @@ -95,7 +95,7 @@ Polymer({ * section. * @private {boolean} */ - doesAccountSupportMultiDeviceSection_: { + doesChromebookSupportMultiDeviceSection_: { type: Boolean, value: false, }, diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html index 36872d0208c..cb249bebe13 100644 --- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html +++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html @@ -3,6 +3,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> @@ -21,36 +22,47 @@ <settings-animated-pages id="pages" section="bluetooth" focus-config="[[focusConfig_]]"> <neon-animatable route-path="default"> - <div id="bluetoothDevices" - class="settings-box two-line" actionable on-click="onTap_"> - <iron-icon icon="[[getIcon_(bluetoothToggleState_)]]"></iron-icon> - <div class="middle"> - $i18n{bluetoothPageTitle} - <div class="secondary" id="bluetoothSecondary"> - [[getOnOffString_(bluetoothToggleState_, - '$i18nPolymer{deviceOn}', '$i18nPolymer{deviceOff}')]] + <template is="dom-if" if="[[!isSecondaryUser_]]"> + <div id="bluetoothDevices" + class="settings-box two-line" actionable on-click="onTap_"> + <iron-icon icon="[[getIcon_(bluetoothToggleState_)]]"></iron-icon> + <div class="middle"> + $i18n{bluetoothPageTitle} + <div class="secondary" id="bluetoothSecondary"> + [[getOnOffString_(bluetoothToggleState_, + '$i18nPolymer{deviceOn}', '$i18nPolymer{deviceOff}')]] + </div> </div> + <cr-policy-pref-indicator + icon-aria-label="$i18n{bluetoothPageTitle}" + pref="[[prefs.cros.device.allow_bluetooth]]" + hidden="[[prefs.cros.device.allow_bluetooth.value]]"> + </cr-policy-pref-indicator> + <template is="dom-if" if="[[bluetoothToggleState_]]"> + <paper-icon-button-light class="subpage-arrow"> + <button on-click="onSubpageArrowTap_" + aria-label="$i18n{bluetoothPageTitle}" + aria-describedby="bluetoothSecondary"> + </button> + </paper-icon-button-light> + </template> + <div class="separator"></div> + <cr-toggle id="enableBluetooth" + checked="{{bluetoothToggleState_}}" + disabled$="[[!isToggleEnabled_( + adapterState_, stateChangeInProgress_)]]" + aria-label="$i18n{bluetoothToggleA11yLabel}"> + </cr-toggle> </div> - <cr-policy-pref-indicator icon-aria-label="$i18n{bluetoothPageTitle}" - pref="[[prefs.cros.device.allow_bluetooth]]" - hidden="[[prefs.cros.device.allow_bluetooth.value]]"> - </cr-policy-pref-indicator> - <template is="dom-if" if="[[bluetoothToggleState_]]"> - <paper-icon-button-light class="subpage-arrow"> - <button on-click="onSubpageArrowTap_" - aria-label="$i18n{bluetoothPageTitle}" - aria-describedby="bluetoothSecondary"> - </button> - </paper-icon-button-light> - </template> - <div class="separator"></div> - <cr-toggle id="enableBluetooth" - checked="{{bluetoothToggleState_}}" - disabled$= - "[[!isToggleEnabled_(adapterState_, stateChangeInProgress_)]]" - aria-label="$i18n{bluetoothToggleA11yLabel}"> - </cr-toggle> - </div> + </template> + <template is="dom-if" if="[[isSecondaryUser_]]"> + <div id="bluetoothDevices" class="settings-box two-line"> + <iron-icon class="policy" icon="cr:group"></iron-icon> + <div class="middle"> + [[i18n('bluetoothPrimaryUserControlled', primaryUserEmail_)]] + </div> + </div> + </template> </neon-animatable> <template is="dom-if" route-path="/bluetoothDevices"> diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js index ec86e09ad87..f532340394a 100644 --- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js +++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.js @@ -25,7 +25,7 @@ const bluetoothApis = window['bluetoothApis'] || { Polymer({ is: 'settings-bluetooth-page', - behaviors: [PrefsBehavior], + behaviors: [I18nBehavior, PrefsBehavior], properties: { /** Preferences state. */ @@ -99,6 +99,30 @@ Polymer({ type: Object, value: chrome.bluetoothPrivate, }, + + /** + * Whether the user is a secondary user. + * @private + */ + isSecondaryUser_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('isSecondaryUser'); + }, + readOnly: true, + }, + + /** + * Email address for the primary user. + * @private + */ + primaryUserEmail_: { + type: String, + value: function() { + return loadTimeData.getString('primaryUserEmail'); + }, + readOnly: true, + }, }, observers: ['deviceListChanged_(deviceList_.*)'], diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js index 476a45012b2..a29bb4d3afa 100644 --- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js +++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.js @@ -271,7 +271,7 @@ Polymer({ * @private */ updateDeviceList_: function() { - if (!this.bluetoothToggleState) { + if (!this.adapterState || !this.adapterState.powered) { this.deviceList_ = []; return; } diff --git a/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html b/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html index dc7ebec5125..8bb78adddca 100644 --- a/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html +++ b/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html @@ -11,7 +11,7 @@ <template> <style include="settings-shared"> .icon-container { - -webkit-padding-end: var(--settings-box-row-padding); + padding-inline-end: var(--settings-box-row-padding); } .change-password-icon { diff --git a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html index 2a309a7a43d..dbab0d8557f 100644 --- a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html +++ b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html @@ -1,11 +1,9 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> -<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> @@ -13,7 +11,6 @@ <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="../controls/controlled_button.html"> <link rel="import" href="../controls/settings_toggle_button.html"> -<link rel="import" href="../icons.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="chrome_cleanup_proxy.html"> <link rel="import" href="items_to_remove_list.html"> @@ -22,6 +19,7 @@ <template> <style include="settings-shared"> #waiting-spinner { + flex-shrink: 0; height: 20px; width: 20px; } @@ -43,28 +41,6 @@ height: 22px; } - #status-icon { - height: 20px; - vertical-align: top; - width: 20px; - } - - .status-icon-container { - -webkit-padding-end: var(--settings-box-row-padding); - } - - .status-icon-remove { - --iron-icon-fill-color: var(--google-grey-refresh-700); - } - - .status-icon-done { - --iron-icon-fill-color: var(--google-blue-500); - } - - .status-icon-warning { - --iron-icon-fill-color: var(--google-red-700); - } - .top-aligned-settings-box { align-items: center; /* override settings-box min-height since we use vertical padding */ @@ -74,14 +50,6 @@ </style> <div class$="settings-box first [[getTopSettingsBoxClass_(showExplanation_)]]"> - <div class="status-icon-container"> - <paper-spinner-lite id="waiting-spinner" - hidden="[[!isWaitingForResult_]]" active="[[isWaitingForResult_]]"> - </paper-spinner-lite> - <iron-icon id="status-icon" hidden="[[isWaitingForResult_]]" - icon="[[statusIcon_]]" class$="[[statusIconClassName_]]"> - </iron-icon> - </div> <div class="start"> <div id="status-title" role="status" inner-h-t-m-l="[[title_]]"></div> <div hidden="[[!showExplanation_]]"> @@ -91,6 +59,9 @@ <cr-policy-pref-indicator pref="[[prefs.software_reporter.enabled]]"> </cr-policy-pref-indicator> + <paper-spinner-lite id="waiting-spinner" + hidden="[[!isWaitingForResult_]]" active="[[isWaitingForResult_]]"> + </paper-spinner-lite> <template is="dom-if" if="[[showActionButton_]]"> <div class="separator"></div> <paper-button id="action-button" class="action-button" diff --git a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js index 8c6329255bf..e61a4d7aa0a 100644 --- a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js +++ b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js @@ -60,14 +60,6 @@ settings.ChromeCleanupOngoingAction = { /** * @typedef {{ - * statusIcon: string, - * statusIconClassName: string, - * }} - */ -settings.ChromeCleanupCardIcon; - -/** - * @typedef {{ * label: string, * doAction: !function(), * }} @@ -78,7 +70,6 @@ settings.ChromeCleanupCardActionButton; * @typedef {{ * title: ?string, * explanation: ?string, - * icon: ?settings.ChromeCleanupCardIcon, * actionButton: ?settings.ChromeCleanupCardActionButton, * flags: number, * }} @@ -214,18 +205,6 @@ Polymer({ computed: 'computeHasExtensionsToShow_(scannerResults_)', }, - /** @private */ - statusIcon_: { - type: String, - value: '', - }, - - /** @private */ - statusIconClassName_: { - type: String, - value: '', - }, - /** @private {chrome.settingsPrivate.PrefObject} */ logsUploadPref_: { type: Object, @@ -521,28 +500,11 @@ Polymer({ this.title_ = components.title || ''; this.explanation_ = components.explanation || ''; - this.updateIcon_(components.icon); this.updateActionButton_(components.actionButton); this.updateCardFlags_(components.flags); }, /** - * Updates the icon on the cleanup card to show the current state. - * @param {?settings.ChromeCleanupCardIcon} icon The icon to - * render, or null if no icon should be shown. - * @private - */ - updateIcon_: function(icon) { - if (!icon) { - this.statusIcon_ = ''; - this.statusIconClassName_ = ''; - } else { - this.statusIcon_ = icon.statusIcon; - this.statusIconClassName_ = icon.statusIconClassName; - } - }, - - /** * Updates the action button on the cleanup card as the action expected for * the current state. * @param {?settings.ChromeCleanupCardActionButton} actionButton @@ -663,30 +625,6 @@ Polymer({ */ buildCardStateToComponentsMap_: function() { /** - * The icons to show on the card. - * @enum {settings.ChromeCleanupCardIcon} - */ - const icons = { - // Card's icon indicates a cleanup offer. - SYSTEM: { - statusIcon: 'cr:security', - statusIconClassName: 'status-icon-remove', - }, - - // Card's icon indicates a warning (in case of failure). - WARNING: { - statusIcon: 'cr:error', - statusIconClassName: 'status-icon-warning', - }, - - // Card's icon indicates completion or reboot required. - DONE: { - statusIcon: 'settings:check-circle', - statusIconClassName: 'status-icon-done', - }, - }; - - /** * The action buttons to show on the card. * @enum {settings.ChromeCleanupCardActionButton} */ @@ -719,7 +657,6 @@ Polymer({ settings.ChromeCleanerCardState.CLEANUP_OFFERED, { title: this.i18n('chromeCleanupTitleRemove'), explanation: this.i18n('chromeCleanupExplanationRemove'), - icon: icons.SYSTEM, actionButton: actionButtons.REMOVE, flags: settings.ChromeCleanupCardFlags.SHOW_LOGS_PERMISSIONS | settings.ChromeCleanupCardFlags.SHOW_ITEMS_TO_REMOVE, @@ -729,7 +666,6 @@ Polymer({ settings.ChromeCleanerCardState.CLEANING, { title: this.i18n('chromeCleanupTitleRemoving'), explanation: this.i18n('chromeCleanupExplanationRemoving'), - icon: null, actionButton: null, flags: settings.ChromeCleanupCardFlags.WAITING_FOR_RESULT | settings.ChromeCleanupCardFlags.SHOW_ITEMS_TO_REMOVE, @@ -739,7 +675,6 @@ Polymer({ settings.ChromeCleanerCardState.REBOOT_REQUIRED, { title: this.i18n('chromeCleanupTitleRestart'), explanation: null, - icon: icons.DONE, actionButton: actionButtons.RESTART_COMPUTER, flags: settings.ChromeCleanupCardFlags.NONE, } @@ -748,7 +683,6 @@ Polymer({ settings.ChromeCleanerCardState.CLEANUP_SUCCEEDED, { title: this.i18nAdvanced('chromeCleanupTitleRemoved', {tags: ['a']}), explanation: null, - icon: icons.DONE, actionButton: null, flags: settings.ChromeCleanupCardFlags.NONE, } @@ -757,7 +691,6 @@ Polymer({ settings.ChromeCleanerCardState.CLEANING_FAILED, { title: this.i18n('chromeCleanupTitleErrorCantRemove'), explanation: this.i18n('chromeCleanupExplanationCleanupError'), - icon: icons.WARNING, actionButton: null, flags: settings.ChromeCleanupCardFlags.NONE, } @@ -766,7 +699,6 @@ Polymer({ settings.ChromeCleanerCardState.SCANNING_OFFERED, { title: this.i18n('chromeCleanupTitleFindAndRemove'), explanation: this.i18n('chromeCleanupExplanationFindAndRemove'), - icon: icons.SYSTEM, actionButton: actionButtons.FIND, flags: settings.ChromeCleanupCardFlags.SHOW_LOGS_PERMISSIONS, } @@ -775,7 +707,6 @@ Polymer({ settings.ChromeCleanerCardState.SCANNING, { title: this.i18n('chromeCleanupTitleScanning'), explanation: null, - icon: null, actionButton: null, flags: settings.ChromeCleanupCardFlags.WAITING_FOR_RESULT, } @@ -785,7 +716,6 @@ Polymer({ settings.ChromeCleanerCardState.SCANNING_FOUND_NOTHING, { title: this.i18n('chromeCleanupTitleNothingFound'), explanation: null, - icon: icons.DONE, actionButton: null, flags: settings.ChromeCleanupCardFlags.NONE, } @@ -794,7 +724,6 @@ Polymer({ settings.ChromeCleanerCardState.SCANNING_FAILED, { title: this.i18n('chromeCleanupTitleScanningFailed'), explanation: this.i18n('chromeCleanupExplanationScanError'), - icon: icons.WARNING, actionButton: null, flags: settings.ChromeCleanupCardFlags.NONE, } @@ -806,7 +735,6 @@ Polymer({ // connectivity and cleanups being disabled by the server. title: this.i18n('chromeCleanupTitleCleanupUnavailable'), explanation: this.i18n('chromeCleanupExplanationCleanupUnavailable'), - icon: icons.WARNING, actionButton: actionButtons.TRY_SCAN_AGAIN, flags: settings.ChromeCleanupCardFlags.NONE, }, diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn index cf04a888e8d..272d36e080d 100644 --- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn @@ -22,6 +22,7 @@ js_library("clear_browsing_data_dialog") { deps = [ ":clear_browsing_data_browser_proxy", "../device_page:keyboard", + "../people_page:sync_browser_proxy", "//third_party/polymer/v1_0/components-chromium/iron-pages:iron-pages-extracted", "//third_party/polymer/v1_0/components-chromium/iron-resizable-behavior:iron-resizable-behavior-extracted", "//ui/webui/resources/js:cr", diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html index 48df5d399b4..ba93834005d 100644 --- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html +++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html @@ -2,6 +2,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/cr_elements/paper_tabs_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> @@ -46,6 +47,12 @@ padding-top: 8px; } + #clearBrowsingDataDialog [slot=footer] { + background: var(--paper-grey-50); + border-top: none; + padding: 0; + } + .row { align-items: center; display: flex; @@ -53,8 +60,8 @@ } paper-spinner-lite { - -webkit-margin-end: 16px; margin-bottom: auto; + margin-inline-end: 16px; margin-top: auto; } @@ -81,13 +88,23 @@ } .time-range-select { - -webkit-margin-start: 12px; + margin-inline-start: 12px; } [slot=title] .secondary { font-size: calc(13 / 15 * 100%); padding-top: 8px; } + + .divider { + border-top: var(--settings-separator-line); + margin: 0 16px; + } + + #footer-description { + color: var(--cr-secondary-text-color); + padding: 16px; + } </style> <cr-dialog id="clearBrowsingDataDialog" @@ -127,7 +144,7 @@ pref="{{prefs.browser.clear_data.browsing_history_basic}}" label="$i18n{clearBrowsingHistory}" sub-label-html="[[browsingCheckboxLabel_( - isSignedIn_, isSyncingHistory_, + isSignedIn_, isSyncingHistory_, syncStatus.hasError, '$i18nPolymer{clearBrowsingHistorySummary}', '$i18nPolymer{clearBrowsingHistorySummarySignedIn}', '$i18nPolymer{clearBrowsingHistorySummarySynced}')]]" @@ -239,6 +256,37 @@ $i18n{clearData} </paper-button> </div> + <div slot="footer" + hidden="[[!shouldShowFooter_(syncStatus.signedIn, diceEnabled_)]]"> + <settings-sync-account-control sync-status="[[syncStatus]]" + hide-buttons> + </settings-sync-account-control> + <div class="divider"></div> + <div id="footer-description" on-tap="onSyncDescriptionLinkClicked_"> + <template is="dom-if" if="[[!syncStatus.hasError]]"> + <span id="sync-info"> + $i18nRaw{clearBrowsingDataWithSync} + </span> + </template> + <template is="dom-if" if="[[syncStatus.hasError]]"> + <template is="dom-if" if="[[isSyncPaused_]]"> + <span id="sync-paused-info"> + $i18nRaw{clearBrowsingDataWithSyncPaused} + </span> + </template> + <template is="dom-if" if="[[hasPassphraseError_]]"> + <span id="sync-passphrase-error-info"> + $i18nRaw{clearBrowsingDataWithSyncPassphraseError} + </span> + </template> + <template is="dom-if" if="[[hasOtherSyncError_]]"> + <span id="sync-other-error-info"> + $i18nRaw{clearBrowsingDataWithSyncError} + </span> + </template> + </template> + </div> + </div> </cr-dialog> <template is="dom-if" if="[[showHistoryDeletionDialog_]]" restamp> diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js index fd323fc5f5a..a3e35cf4a9e 100644 --- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js +++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js @@ -21,6 +21,12 @@ Polymer({ }, /** + * The current sync status, supplied by SyncBrowserProxy. + * @type {?settings.SyncStatus} + */ + syncStatus: Object, + + /** * Results of browsing data counters, keyed by the suffix of * the corresponding data type deletion preference, as reported * by the C++ side. @@ -94,6 +100,43 @@ Polymer({ value: false, }, + /** @private */ + isSyncPaused_: { + type: Boolean, + value: false, + computed: 'computeIsSyncPaused_(syncStatus)', + }, + + /** @private */ + hasPassphraseError_: { + type: Boolean, + value: false, + computed: 'computeHasPassphraseError_(syncStatus)', + }, + + /** @private */ + hasOtherSyncError_: { + type: Boolean, + value: false, + computed: + 'computeHasOtherError_(syncStatus, isSyncPaused_, hasPassphraseError_)', + }, + + /** + * This flag is used to conditionally show the footer for the dialog. + * @private + */ + diceEnabled_: { + type: Boolean, + value: function() { + let diceEnabled = false; + // <if expr="not chromeos"> + diceEnabled = loadTimeData.getBoolean('diceEnabled'); + // </if> + return diceEnabled; + }, + }, + /** * Time in ms, when the dialog was opened. * @private @@ -109,8 +152,17 @@ Polymer({ /** @private {settings.ClearBrowsingDataBrowserProxy} */ browserProxy_: null, + /** @private {?settings.SyncBrowserProxy} */ + syncBrowserProxy_: null, + /** @override */ ready: function() { + this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance(); + this.syncBrowserProxy_.getSyncStatus().then( + this.handleSyncStatus_.bind(this)); + this.addWebUIListener( + 'sync-status-changed', this.handleSyncStatus_.bind(this)); + this.addWebUIListener( 'update-sync-state', this.updateSyncState_.bind(this)); this.addWebUIListener( @@ -128,6 +180,15 @@ Polymer({ }, /** + * Handler for when the sync state is pushed from the browser. + * @param {?settings.SyncStatus} syncStatus + * @private + */ + handleSyncStatus_: function(syncStatus) { + this.syncStatus = syncStatus; + }, + + /** * Returns true if either clearing is in progress or no data type is selected. * @param {boolean} clearingInProgress * @param {boolean} clearButtonDisabled @@ -193,9 +254,9 @@ Polymer({ * @private */ browsingCheckboxLabel_: function( - isSignedIn, isSyncingHistory, historySummary, historySummarySignedIn, - historySummarySynced) { - if (isSyncingHistory) { + isSignedIn, isSyncingHistory, hasSyncError, historySummary, + historySummarySignedIn, historySummarySynced) { + if (isSyncingHistory && !hasSyncError) { return historySummarySynced; } else if (isSignedIn) { return historySummarySignedIn; @@ -318,4 +379,61 @@ Polymer({ 'ClearBrowsingData_SwitchTo_AdvancedTab'); } }, + + /** + * Called when the user clicks the link in the footer. + * @param {!Event} e + * @private + */ + onSyncDescriptionLinkClicked_: function(e) { + if (e.target.tagName === 'A') { + e.preventDefault(); + if (!this.syncStatus.hasError) { + this.syncBrowserProxy_.pauseSync(); + return; + } + + if (this.isSyncPaused_) { + this.syncBrowserProxy_.startSignIn(); + } else { + // In any other error case, navigate to the sync page. + settings.navigateTo(settings.routes.SYNC); + } + } + }, + + /** + * @return {boolean} + * @private + */ + computeIsSyncPaused_: function() { + return !!this.syncStatus.hasError && + this.syncStatus.statusAction === settings.StatusAction.REAUTHENTICATE; + }, + + /** + * @return {boolean} + * @private + */ + computeHasPassphraseError_: function() { + return !!this.syncStatus.hasError && + this.syncStatus.statusAction === settings.StatusAction.ENTER_PASSPHRASE; + }, + + /** + * @return {boolean} + * @private + */ + computeHasOtherError_: function() { + return !!this.syncStatus.hasError && !this.isSyncPaused_ && + !this.hasPassphraseError_; + }, + + /** + * @return {boolean} + * @private + */ + shouldShowFooter_: function() { + return this.diceEnabled_ && !!this.syncStatus.signedIn; + }, }); diff --git a/chromium/chrome/browser/resources/settings/controls/BUILD.gn b/chromium/chrome/browser/resources/settings/controls/BUILD.gn index 8e938f25335..7baf0b551a4 100644 --- a/chromium/chrome/browser/resources/settings/controls/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/controls/BUILD.gn @@ -9,6 +9,7 @@ js_type_check("closure_compile") { ":controlled_button", ":controlled_radio_button", ":extension_controlled_indicator", + ":password_prompt_dialog", ":pref_control_behavior", ":settings_boolean_control_behavior", ":settings_checkbox", @@ -47,6 +48,16 @@ js_library("extension_controlled_indicator") { ] } +js_library("password_prompt_dialog") { + deps = [ + "..:route", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/cr_elements/cr_input:cr_input", + ] + externs_list = [ "$externs_path/quick_unlock_private.js" ] + extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ] +} + js_library("pref_control_behavior") { deps = [ "../prefs:prefs_types", diff --git a/chromium/chrome/browser/resources/settings/controls/controlled_button.html b/chromium/chrome/browser/resources/settings/controls/controlled_button.html index f1d81142250..e76e3584ea7 100644 --- a/chromium/chrome/browser/resources/settings/controls/controlled_button.html +++ b/chromium/chrome/browser/resources/settings/controls/controlled_button.html @@ -31,14 +31,14 @@ } :host(:not([end-justified])) cr-policy-pref-indicator { - -webkit-margin-end: calc( + margin-inline-end: calc( var(--cr-controlled-by-spacing) - var(--justify-margin)); - -webkit-margin-start: var(--cr-controlled-by-spacing); + margin-inline-start: var(--cr-controlled-by-spacing); } :host([end-justified]) cr-policy-pref-indicator { - -webkit-margin-end: var(--cr-controlled-by-spacing); - -webkit-margin-start: calc( + margin-inline-end: var(--cr-controlled-by-spacing); + margin-inline-start: calc( var(--cr-controlled-by-spacing) - var(--justify-margin)); order: -1; } diff --git a/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html b/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html index a8260c24312..e3b26b4205b 100644 --- a/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html +++ b/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html @@ -22,7 +22,7 @@ } cr-policy-pref-indicator { - -webkit-margin-start: var(--settings-controlled-by-spacing); + margin-inline-start: var(--settings-controlled-by-spacing); /* Enable pointer events for the indicator so :hover works. Disable * clicks/taps via onIndicatorTap_ so outer on-tap doesn't trigger. */ pointer-events: all; diff --git a/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html b/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html index 50e3313c33c..15be87a78f0 100644 --- a/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html +++ b/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html @@ -10,23 +10,23 @@ <template> <style include="settings-shared"> :host { - -webkit-margin-start: 36px; align-items: center; display: flex; + margin-inline-start: 36px; min-height: var(--settings-row-min-height); } img { @apply --cr-icon-height-width; - -webkit-margin-end: 16px; + margin-inline-end: 16px; } /* Using ">" operator to ensure that this CSS rule will not accidentally * be applied to a search highlight span (which is inserted dynamically if * when search "hit" occurs within this element. */ :host > span { - -webkit-margin-end: 8px; flex: 1; + margin-inline-end: 8px; } </style> <img role="presentation" src="chrome://extension-icon/[[extensionId]]/40/1"> diff --git a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.html b/chromium/chrome/browser/resources/settings/controls/password_prompt_dialog.html index 3425c33c812..b8afbd0e4a8 100644 --- a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.html +++ b/chromium/chrome/browser/resources/settings/controls/password_prompt_dialog.html @@ -3,8 +3,6 @@ <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="lock_screen_constants.html"> -<link rel="import" href="lock_state_behavior.html"> <link rel="import" href="../settings_shared_css.html"> <dom-module id="settings-password-prompt-dialog"> @@ -23,7 +21,7 @@ <div slot="title">$i18n{passwordPromptTitle}</div> <div slot="body"> <div class="settings-box first"> - [[selectPasswordPromptEnterPasswordString(hasPinLogin)]] + [[passwordPromptText]] </div> <cr-input id="passwordInput" type="password" placeholder="$i18n{passwordPromptPasswordLabel}" diff --git a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.js b/chromium/chrome/browser/resources/settings/controls/password_prompt_dialog.js index a633c83a40b..2ed8e8b55dd 100644 --- a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.js +++ b/chromium/chrome/browser/resources/settings/controls/password_prompt_dialog.js @@ -8,20 +8,15 @@ * 'settings-password-prompt-dialog' shows a dialog which asks for the user to * enter their password. It validates the password is correct. Once the user has * entered their account password, the page fires an 'authenticated' event and - * updates the setModes binding. - * - * The setModes binding is a wrapper around chrome.quickUnlockPrivate.setModes - * which has a prebound account password. The account password by itself is not - * available for other elements to access. + * updates the authToken binding. * * Example: * * <settings-password-prompt-dialog * id="passwordPrompt" - * set-modes="[[setModes]]"> + * password-prompt-text="{{passwordPromptText}}" + * auth-token="{{authToken}}"> * </settings-password-prompt-dialog> - * - * this.$.passwordPrompt.open() */ (function() { @@ -30,27 +25,21 @@ Polymer({ is: 'settings-password-prompt-dialog', - behaviors: [ - LockStateBehavior, - ], - properties: { /** - * A wrapper around chrome.quickUnlockPrivate.setModes with the account - * password already supplied. If this is null, the authentication screen - * needs to be redisplayed. This property will be cleared after the timeout - * returned by quickUnlockPrivate.getAuthToken. - * @type {?Object} + * The subtext to be displayed above the password input field. Embedders + * may choose to change this value for their specific use case. + * @type {string} */ - setModes: { - type: Object, + passwordPromptText: { + type: String, notify: true, }, /** - * Authentication token used when calling setModes, returned by - * quickUnlockPrivate.getAuthToken. Reflected to lock-screen. - * @private + * Authentication token returned by quickUnlockPrivate.getAuthToken(). + * Should be passed to API calls which require authentication. + * @type {string} */ authToken: { type: String, @@ -58,17 +47,17 @@ Polymer({ }, /** - * @private + * @private {string} */ inputValue_: { - type: Boolean, + type: String, value: '', observer: 'onInputValueChange_', }, /** * Helper property which marks password as valid/invalid. - * @private + * @private {boolean} */ passwordInvalid_: { type: Boolean, @@ -76,18 +65,11 @@ Polymer({ }, /** - * writeUma_ is a function that handles writing uma stats. It may be - * overridden for tests. - * - * @type {Function} - * @private + * Interface for chrome.quickUnlockPrivate calls. May be overridden by + * tests. + * @type {QuickUnlockPrivate} */ - writeUma_: { - type: Object, - value: function() { - return settings.recordLockScreenProgress; - } - }, + quickUnlockPrivate: {type: Object, value: chrome.quickUnlockPrivate}, }, /** @return {!CrInputElement} */ @@ -97,13 +79,14 @@ Polymer({ /** @override */ attached: function() { - this.writeUma_(LockScreenProgress.START_SCREEN_LOCK); this.$.dialog.showModal(); // This needs to occur at the next paint otherwise the password input will // not receive focus. this.async(() => { + // TODO(crbug.com/876377): This is unusual; the 'autofocus' attribute on + // the cr-input element should work. Investigate. this.passwordInput.focus(); - }, 1); + }, 1 /* waitTime */); }, /** @private */ @@ -113,11 +96,18 @@ Polymer({ }, /** + * The timeout ID to pass to clearTimeout() to cancel auth token + * invalidation. + * @private {number|undefined} + */ + clearAccountPasswordTimeoutId_: undefined, + + /** * Run the account password check. * @private */ submitPassword_: function() { - clearTimeout(this.clearAccountPasswordTimeout_); + clearTimeout(this.clearAccountPasswordTimeoutId_); const password = this.passwordInput.value; // The user might have started entering a password and then deleted it all. @@ -138,35 +128,19 @@ Polymer({ this.authToken = tokenInfo.token; this.passwordInvalid_ = false; - // Create the |this.setModes| closure and automatically clear it after - // tokenInfo.lifetimeSeconds. - this.setModes = (modes, credentials, onComplete) => { - this.quickUnlockPrivate.setModes( - tokenInfo.token, modes, credentials, () => { - let result = true; - if (chrome.runtime.lastError) { - console.error( - 'setModes failed: ' + chrome.runtime.lastError.message); - result = false; - } - onComplete(result); - }); - }; - - // Subtract time from the exiration time to account for IPC delays. + // Clear |this.authToken| after tokenInfo.lifetimeSeconds. + // Subtract time from the expiration time to account for IPC delays. // Treat values less than the minimum as 0 for testing. const IPC_SECONDS = 2; const lifetimeMs = tokenInfo.lifetimeSeconds > IPC_SECONDS ? (tokenInfo.lifetimeSeconds - IPC_SECONDS) * 1000 : 0; - this.clearAccountPasswordTimeout_ = setTimeout(() => { - this.setModes = null; + this.clearAccountPasswordTimeoutId_ = setTimeout(() => { + this.authToken = ''; }, lifetimeMs); if (this.$.dialog.open) this.$.dialog.close(); - - this.writeUma_(LockScreenProgress.ENTER_PASSWORD_CORRECTLY); }); }, @@ -179,16 +153,5 @@ Polymer({ isConfirmEnabled_: function() { return !this.passwordInvalid_ && this.inputValue_; }, - - /** - * Looks up the translation id, which depends on PIN login support. - * @param {boolean} hasPinLogin - * @private - */ - selectPasswordPromptEnterPasswordString(hasPinLogin) { - if (hasPinLogin) - return this.i18n('passwordPromptEnterPasswordLoginLock'); - return this.i18n('passwordPromptEnterPasswordLock'); - }, }); })(); diff --git a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html index ec8f7cfed6e..d8856d5402e 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html +++ b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html @@ -27,7 +27,7 @@ } cr-policy-pref-indicator { - -webkit-margin-start: var(--settings-controlled-by-spacing); + margin-inline-start: var(--settings-controlled-by-spacing); } </style> <div id="outerRow" noSubLabel$="[[!hasSubLabel_(subLabel, subLabelHtml)]]"> diff --git a/chromium/chrome/browser/resources/settings/controls/settings_slider.html b/chromium/chrome/browser/resources/settings/controls/settings_slider.html index c1015b40593..f58423c9724 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_slider.html +++ b/chromium/chrome/browser/resources/settings/controls/settings_slider.html @@ -9,15 +9,15 @@ <style> :host { --calculated-paper-slider-height: var(--paper-slider-height, 2px); + display: inline-flex; /* Counteract the margin on #sliderContainer in paper-slider.html. */ - -webkit-margin-end: + margin-inline-end: calc(-15px - var(--calculated-paper-slider-height) / 2); - display: inline-flex; } cr-policy-pref-indicator { - -webkit-margin-start: var(--settings-controlled-by-spacing); align-self: center; + margin-inline-start: var(--settings-controlled-by-spacing); } #labels[disabled] { @@ -44,7 +44,7 @@ } #label-begin { - -webkit-margin-end: 4px; + margin-inline-end: 4px; } #label-end { diff --git a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html index 0965fc374fa..dcf511439de 100644 --- a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html +++ b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html @@ -46,11 +46,11 @@ #labelWrapper, ::slotted([slot='more-actions']) { - -webkit-margin-end: var(--settings-control-label-spacing); + margin-inline-end: var(--settings-control-label-spacing); } cr-policy-pref-indicator { - -webkit-margin-end: var(--settings-controlled-by-spacing); + margin-inline-end: var(--settings-controlled-by-spacing); } </style> <div id="outerRow" noSubLabel$="[[!subLabel]]"> diff --git a/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.html b/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.html index e2953defea0..2ae2ba3226e 100644 --- a/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.html +++ b/chromium/chrome/browser/resources/settings/date_time_page/timezone_subpage.html @@ -20,7 +20,7 @@ } #timeZoneResolveMethodDropdown, #timezoneSelector { - -webkit-padding-start: 28px; + padding-inline-start: 28px; } </style> <div class="settings-box block first"> diff --git a/chromium/chrome/browser/resources/settings/device_page/device_page.html b/chromium/chrome/browser/resources/settings/device_page/device_page.html index 7d8d94f311d..d9a55adb6d6 100644 --- a/chromium/chrome/browser/resources/settings/device_page/device_page.html +++ b/chromium/chrome/browser/resources/settings/device_page/device_page.html @@ -57,8 +57,8 @@ <button aria-label="$i18n{displayTitle}"></button> </paper-icon-button-light> </div> - <div id="storageRow" class="settings-box" on-click="onStorageTap_" - actionable> + <div id="storageRow" hidden="[[hideStorageInfo_]]" class="settings-box" + on-click="onStorageTap_" actionable> <div class="start">$i18n{storageTitle}</div> <paper-icon-button-light class="subpage-arrow"> <button aria-label="$i18n{storageTitle}"></button> diff --git a/chromium/chrome/browser/resources/settings/device_page/device_page.js b/chromium/chrome/browser/resources/settings/device_page/device_page.js index 2dac5e6137f..46c32cde32e 100644 --- a/chromium/chrome/browser/resources/settings/device_page/device_page.js +++ b/chromium/chrome/browser/resources/settings/device_page/device_page.js @@ -54,6 +54,19 @@ Polymer({ readOnly: true, }, + /** + * Whether storage management info should be hidden. + * @private + */ + hideStorageInfo_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('hideStorageInfo') && + loadTimeData.getBoolean('hideStorageInfo'); + }, + readOnly: true, + }, + /** @private {!Map<string, string>} */ focusConfig_: { type: Object, diff --git a/chromium/chrome/browser/resources/settings/device_page/display.html b/chromium/chrome/browser/resources/settings/device_page/display.html index 577ec094628..f271dcdc49b 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display.html +++ b/chromium/chrome/browser/resources/settings/device_page/display.html @@ -26,8 +26,8 @@ <template> <style include="settings-shared md-select iron-flex iron-flex-alignment paper-tabs-style"> .indented { - -webkit-margin-start: var(--cr-section-indent-padding); align-self: stretch; + margin-inline-start: var(--cr-section-indent-padding); padding: 0; } @@ -52,7 +52,7 @@ } .settings-box > paper-button:first-child { - -webkit-padding-start: 0 + padding-inline-start: 0 } #controlsDiv > .settings-box:first-of-type { @@ -137,57 +137,39 @@ </div> </template> - <!-- Slider for selecting resolution when display zoom is disabled --> - <template is="dom-if" if="[[!showDisplayZoomSetting_]]"> - <div class="settings-box indented two-line"> - <div class="start text-area layout vertical"> - <div>$i18n{displayResolutionTitle}</div> - <div class="secondary self-start"> - [[getResolutionText_(selectedDisplay, selectedModePref_.value)]] - </div> - </div> - <settings-slider disabled="[[!enableSetResolution_(selectedDisplay)]]" - tick-values="[[modeValues_]]" pref="{{selectedModePref_}}" - on-change="onSelectedModeSliderChange_"> - </settings-slider> - </div> - </template> - <!-- Display zoom selection slider --> - <template is="dom-if" if="[[showDisplayZoomSetting_]]"> - <div class="settings-box indented two-line"> - <div class="start text-area layout vertical"> - <div>$i18n{displayZoomTitle}</div> - <div class="secondary self-start">$i18n{displayZoomSublabel}</div> - <div class="secondary self-start" - hidden$="[[!logicalResolutionText_]]"> - [[logicalResolutionText_]] - </div> + <div class="settings-box indented two-line"> + <div class="start text-area layout vertical"> + <div>$i18n{displayZoomTitle}</div> + <div class="secondary self-start">$i18n{displayZoomSublabel}</div> + <div class="secondary self-start" + hidden$="[[!logicalResolutionText_]]"> + [[logicalResolutionText_]] </div> - <display-size-slider id="displaySizeSlider" - ticks="[[zoomValues_]]" pref="{{selectedZoomPref_}}" - min-label="$i18n{displaySizeSliderMinLabel}" - max-label="$i18n{displaySizeSliderMaxLabel}" - on-immediate-value-changed="onDisplaySizeSliderDrag_"> - </display-size-slider> </div> + <display-size-slider id="displaySizeSlider" + ticks="[[zoomValues_]]" pref="{{selectedZoomPref_}}" + min-label="$i18n{displaySizeSliderMinLabel}" + max-label="$i18n{displaySizeSliderMaxLabel}" + on-immediate-value-changed="onDisplaySizeSliderDrag_"> + </display-size-slider> + </div> - <!-- Drop down select menu for resolution --> - <div class="settings-box indented two-line" - hidden$="[[!showDropDownResolutionSetting_(selectedDisplay)]]"> - <div class="start text-area layout vertical"> - <div>$i18n{displayResolutionTitle}</div> - <div class="secondary self-start"> - $i18n{displayResolutionSublabel} - </div> + <!-- Drop down select menu for resolution --> + <div class="settings-box indented two-line" + hidden$="[[!showDropDownResolutionSetting_(selectedDisplay)]]"> + <div class="start text-area layout vertical"> + <div>$i18n{displayResolutionTitle}</div> + <div class="secondary self-start"> + $i18n{displayResolutionSublabel} </div> - <settings-dropdown-menu id="displayModeSelector" - pref="{{selectedModePref_}}" - label="Display Mode Menu" - menu-options="[[displayModeList_]]"> - </settings-dropdown-menu> </div> - </template> + <settings-dropdown-menu id="displayModeSelector" + pref="{{selectedModePref_}}" + label="Display Mode Menu" + menu-options="[[displayModeList_]]"> + </settings-dropdown-menu> + </div> <template is="dom-if" if="[[!unifiedDesktopMode_]]" restamp> <div class="settings-box indented"> <div id="displayOrientation" class="start text-area"> diff --git a/chromium/chrome/browser/resources/settings/device_page/display.js b/chromium/chrome/browser/resources/settings/device_page/display.js index 3443c71cd57..cde11aeae41 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display.js +++ b/chromium/chrome/browser/resources/settings/device_page/display.js @@ -170,12 +170,6 @@ Polymer({ }, /** @private */ - showDisplayZoomSetting_: { - type: Boolean, - value: loadTimeData.getBoolean('enableDisplayZoomSetting'), - }, - - /** @private */ nightLightScheduleSubLabel_: String, /** @private */ @@ -370,14 +364,12 @@ Polymer({ const numModes = selectedDisplay.modes.length; this.modeValues_ = numModes == 0 ? [] : Array.from(Array(numModes).keys()); - if (this.showDisplayZoomSetting_) { - // Note that the display zoom values has the same number of ticks for all - // displays, so the above problem doesn't apply here. - this.zoomValues_ = this.getZoomValues_(selectedDisplay); - this.set( - 'selectedZoomPref_.value', - this.getSelectedDisplayZoom_(selectedDisplay)); - } + // Note that the display zoom values has the same number of ticks for all + // displays, so the above problem doesn't apply here. + this.zoomValues_ = this.getZoomValues_(selectedDisplay); + this.set( + 'selectedZoomPref_.value', + this.getSelectedDisplayZoom_(selectedDisplay)); this.displayModeList_ = this.getDisplayModeOptionList_(selectedDisplay); // Set |selectedDisplay| first since only the resolution slider depends @@ -401,8 +393,7 @@ Polymer({ * @private */ showDropDownResolutionSetting_: function(display) { - return !display.isInternal && - loadTimeData.getBoolean('enableDisplayZoomSetting'); + return !display.isInternal; }, /** @@ -600,7 +591,7 @@ Polymer({ * @private */ updateLogicalResolutionText_: function(zoomFactor) { - if (!this.showDisplayZoomSetting_ || !this.selectedDisplay.isInternal) { + if (!this.selectedDisplay.isInternal) { this.logicalResolutionText_ = ''; return; } @@ -718,11 +709,7 @@ Polymer({ */ onSelectedModeChange_: function(newModeIndex) { // We want to ignore all value changes to the pref due to the slider being - // dragged. Since this can only happen when the slider is present which is - // when display zoom is disabled, we can use this check. - // See http://crbug/845712 for more info. - if (!this.showDisplayZoomSetting_) - return; + // dragged. See http://crbug/845712 for more info. if (this.currentSelectedModeIndex_ == newModeIndex) return; this.onSelectedModeSliderChange_(); diff --git a/chromium/chrome/browser/resources/settings/device_page/display_size_slider.html b/chromium/chrome/browser/resources/settings/device_page/display_size_slider.html index 631200d711c..93757c4ef6f 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display_size_slider.html +++ b/chromium/chrome/browser/resources/settings/device_page/display_size_slider.html @@ -14,13 +14,14 @@ <template> <style> :host { - /* Counteract the margin on #sliderContainer and match the margin from - settings-slider.html */ - -webkit-margin-end: -16px; - cursor: default; display: inline-flex; font-weight: 500; + + /* Counteract the margin on #sliderContainer and match the margin from + settings-slider.html */ + margin-inline-end: -16px; + min-width: 200px; text-align: center; user-select: none; @@ -142,14 +143,12 @@ .slider-knob-inner { background-color: var(--google-blue-600); - border: 2px solid var(--google-blue-600); + border: 0; border-radius: 50%; - box-sizing: border-box; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4); + box-sizing: content-box; height: 10px; margin: 11px; - transition-duration: 180ms; - transition-property: transform, background-color, border; - transition-timing-function: ease; width: 10px; } @@ -192,6 +191,8 @@ .slider-knob-inner[disabled] { background-color: var(--google-grey-600); border: 2px solid white; + box-shadow: unset; + margin: 9px; } </style> <div id="sliderContainer" class$="[[getClassNames_(disabled, dragging)]]"> diff --git a/chromium/chrome/browser/resources/settings/device_page/display_size_slider.js b/chromium/chrome/browser/resources/settings/device_page/display_size_slider.js index 924c9bfd5ff..7dd8f904aaa 100644 --- a/chromium/chrome/browser/resources/settings/device_page/display_size_slider.js +++ b/chromium/chrome/browser/resources/settings/device_page/display_size_slider.js @@ -128,6 +128,16 @@ Polymer({ 'pageup end': 'resetToMaxIndex_', }, + /** @override */ + ready: function() { + chrome.settingsPrivate.onPrefsChanged.addListener((prefs) => { + prefs.forEach((pref) => { + if (pref.key == this.pref.key && this.pref.value != pref.value) + this.pref.value = pref.value; + }); + }); + }, + /** @private {boolean} */ usedMouse_: false, @@ -469,18 +479,20 @@ Polymer({ updateIndex_: function() { if (!this.ticks || this.ticks.length == 0) return; - if (!this.pref) + if (!this.pref || typeof(this.pref.value) != 'number') return; + let resolvedTick = this.ticks.length - 1; for (let i = 0; i < this.ticks.length; i++) { - if (this.ticks[i].value == this.pref.value) { - this._setIndex(i); - this.setAttribute( - 'aria-valuenow', - this.getAriaValueForIndex_(this.ticks, this.index)); - this.setAttribute( - 'aria-valuetext', this.getLabelForIndex_(this.ticks, this.index)); + if (this.ticks[i].value >= this.pref.value) { + resolvedTick = i; + break; } } + this._setIndex(resolvedTick); + this.setAttribute( + 'aria-valuenow', this.getAriaValueForIndex_(this.ticks, this.index)); + this.setAttribute( + 'aria-valuetext', this.getLabelForIndex_(this.ticks, this.index)); }, /** diff --git a/chromium/chrome/browser/resources/settings/device_page/keyboard.html b/chromium/chrome/browser/resources/settings/device_page/keyboard.html index 6f88254da9c..415d4d62a62 100644 --- a/chromium/chrome/browser/resources/settings/device_page/keyboard.html +++ b/chromium/chrome/browser/resources/settings/device_page/keyboard.html @@ -34,15 +34,6 @@ menu-options="[[keyMapTargets_]]"> </settings-dropdown-menu> </div> - <template is="dom-if" if="[[showCapsLock_]]"> - <div class="settings-box" id="capsLockKey"> - <div class="start">$i18n{keyboardKeyCapsLock}</div> - <settings-dropdown-menu label="$i18n{keyboardKeyCapsLock}" - pref="{{prefs.settings.language.remap_caps_lock_key_to}}" - menu-options="[[keyMapTargets_]]"> - </settings-dropdown-menu> - </div> - </template> <template is="dom-if" if="[[showDiamondKey_]]"> <div class="settings-box" id="diamondKey"> <div class="start">$i18n{keyboardKeyDiamond}</div> @@ -66,6 +57,34 @@ menu-options="[[keyMapTargets_]]"> </settings-dropdown-menu> </div> + <template is="dom-if" if="[[showCapsLock_]]"> + <div class="settings-box" id="capsLockKey"> + <div class="start">$i18n{keyboardKeyCapsLock}</div> + <settings-dropdown-menu label="$i18n{keyboardKeyCapsLock}" + pref="{{prefs.settings.language.remap_caps_lock_key_to}}" + menu-options="[[keyMapTargets_]]"> + </settings-dropdown-menu> + </div> + </template> + <template is="dom-if" if="[[showExternalMetaKey_]]"> + <div class="settings-box" id="externalMetaKey"> + <div class="start">$i18n{keyboardKeyExternalMeta}</div> + <settings-dropdown-menu label="$i18n{keyboardKeyExternalMeta}" + pref="{{prefs.settings.language.remap_external_meta_key_to}}" + menu-options="[[keyMapTargets_]]"> + </settings-dropdown-menu> + </div> + </template> + <template is="dom-if" if="[[showAppleCommandKey_]]"> + <div class="settings-box" id="externalCommandKey"> + <div class="start">$i18n{keyboardKeyExternalCommand}</div> + <settings-dropdown-menu + label="$i18n{keyboardKeyExternalCommand}" + pref="{{prefs.settings.language.remap_external_command_key_to}}" + menu-options="[[keyMapTargets_]]"> + </settings-dropdown-menu> + </div> + </template> <settings-toggle-button pref="{{prefs.settings.language.send_function_keys}}" label="$i18n{keyboardSendFunctionKeys}" diff --git a/chromium/chrome/browser/resources/settings/device_page/keyboard.js b/chromium/chrome/browser/resources/settings/device_page/keyboard.js index e10766d145b..204013c2fd2 100644 --- a/chromium/chrome/browser/resources/settings/device_page/keyboard.js +++ b/chromium/chrome/browser/resources/settings/device_page/keyboard.js @@ -39,6 +39,21 @@ Polymer({ /** @private Whether to show diamond key options. */ showDiamondKey_: Boolean, + /** + * Whether to show a remapping option for external keyboard's Meta key + * (Search/Windows keys). This is true only when there's an external + * keyboard connected that is a non-Apple keyboard. + * @private + */ + showExternalMetaKey_: Boolean, + + /** + * Whether to show a remapping option for the Command key. This is true when + * one of the connected keyboards is an Apple keyboard. + * @private + */ + showAppleCommandKey_: Boolean, + /** @private {!DropdownMenuOptionList} Menu items for key mapping. */ keyMapTargets_: Object, @@ -114,13 +129,14 @@ Polymer({ /** * Handler for updating which keys to show. - * @param {boolean} showCapsLock - * @param {boolean} showDiamondKey + * @param {Object} keyboardParams * @private */ - onShowKeysChange_: function(showCapsLock, showDiamondKey) { - this.showCapsLock_ = showCapsLock; - this.showDiamondKey_ = showDiamondKey; + onShowKeysChange_: function(keyboardParams) { + this.showCapsLock_ = keyboardParams['showCapsLock']; + this.showDiamondKey_ = keyboardParams['showDiamondKey']; + this.showExternalMetaKey_ = keyboardParams['showExternalMetaKey']; + this.showAppleCommandKey_ = keyboardParams['showAppleCommandKey']; }, onShowKeyboardShortcutsOverlayTap_: function() { diff --git a/chromium/chrome/browser/resources/settings/device_page/pointers.html b/chromium/chrome/browser/resources/settings/device_page/pointers.html index 0595aa2bf6e..0ae8891c9cc 100644 --- a/chromium/chrome/browser/resources/settings/device_page/pointers.html +++ b/chromium/chrome/browser/resources/settings/device_page/pointers.html @@ -11,12 +11,12 @@ <template> <style include="settings-shared"> h2 { - -webkit-padding-start: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-padding); } .subsection { - -webkit-padding-end: var(--settings-box-row-padding); - -webkit-padding-start: var(--settings-box-row-indent); + padding-inline-end: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-indent); } .subsection > settings-toggle-button, diff --git a/chromium/chrome/browser/resources/settings/device_page/storage.html b/chromium/chrome/browser/resources/settings/device_page/storage.html index 27bef12bff6..7b7c8d91bc8 100644 --- a/chromium/chrome/browser/resources/settings/device_page/storage.html +++ b/chromium/chrome/browser/resources/settings/device_page/storage.html @@ -55,20 +55,20 @@ } .message-area { - -webkit-padding-end: 48px; - -webkit-padding-start: 16px; background-color: var(--google-grey-100); border-radius: 2px; display: flex; margin: 14px 0 16px; padding-bottom: 12px; + padding-inline-end: 48px; + padding-inline-start: 16px; padding-top: 16px; width: 100%; } .message-area > iron-icon { - -webkit-padding-end: 16px; flex: none; + padding-inline-end: 16px; } .message-title { diff --git a/chromium/chrome/browser/resources/settings/downloads_page/BUILD.gn b/chromium/chrome/browser/resources/settings/downloads_page/BUILD.gn index 77ad172d70c..a4c1ddd2ed5 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/downloads_page/BUILD.gn @@ -24,6 +24,7 @@ js_library("downloads_browser_proxy") { js_library("smb_shares_page") { deps = [ ":add_smb_share_dialog", + "..:route", "//ui/webui/resources/js:web_ui_listener_behavior", ] } @@ -32,6 +33,7 @@ js_library("add_smb_share_dialog") { deps = [ ":smb_browser_proxy", "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js:web_ui_listener_behavior", ] } @@ -47,6 +49,7 @@ js_library("downloads_page") { ":smb_browser_proxy", "..:page_visibility", "..:route", + "../prefs:prefs_behavior", "//ui/webui/resources/js:cr", "//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:util", diff --git a/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.html b/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.html index 155c13f9f75..5dab706aaa2 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.html +++ b/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.html @@ -2,7 +2,9 @@ <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> +<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="../settings_vars_css.html"> @@ -11,12 +13,21 @@ <dom-module id="settings-add-smb-share-dialog"> <template> <style include="settings-shared"> + #dialog [slot=body] { + height: 350px; + } + cr-input { --cr-input-error-display: none; - width: var(--settings-input-max-width); } - cr-input:not(:last-child) { + cr-searchable-drop-down { + display: block; + --cr-searchable-drop-down-width: 472px; + } + + cr-input:not(:last-child), + cr-searchable-drop-down { margin-bottom: var(--cr-form-field-bottom-spacing); } </style> @@ -24,9 +35,10 @@ <cr-dialog id="dialog"> <div slot="title">$i18n{addSmbShare}</div> <div slot="body" spellcheck="false"> - <cr-input id="address" label="$i18n{smbShareUrl}" value="{{mountUrl_}}" - autofocus> - </cr-input> + <cr-searchable-drop-down id="address" label="$i18n{smbShareUrl}" + value="{{mountUrl_}}" items="[[discoveredShares_]]" + update-value-on-input autofocus> + </cr-searchable-drop-down> <cr-input id="name" label="$i18n{smbShareName}" value="{{mountName_}}" maxlength="64"> </cr-input> diff --git a/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.js b/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.js index 0156d6df0fc..67273f283d3 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.js +++ b/chromium/chrome/browser/resources/settings/downloads_page/add_smb_share_dialog.js @@ -9,18 +9,39 @@ Polymer({ is: 'settings-add-smb-share-dialog', + behaviors: [WebUIListenerBehavior], + properties: { /** @private {string} */ - mountUrl_: String, + mountUrl_: { + type: String, + value: '', + }, /** @private {string} */ - mountName_: String, + mountName_: { + type: String, + value: '', + }, /** @private {string} */ - username_: String, + username_: { + type: String, + value: '', + }, /** @private {string} */ - password_: String, + password_: { + type: String, + value: '', + }, + /** @private {!Array<string>}*/ + discoveredShares_: { + type: Array, + value: function() { + return []; + }, + }, }, /** @private {?settings.SmbBrowserProxy} */ @@ -35,6 +56,8 @@ Polymer({ attached: function() { this.browserProxy_.startDiscovery(); this.$.dialog.showModal(); + + this.addWebUIListener('on-shares-found', this.onSharesFound_.bind(this)); }, /** @private */ @@ -56,4 +79,13 @@ Polymer({ canAddShare_: function() { return !!this.mountUrl_; }, + + /** + * @param {!Array<string>} shares + * @private + */ + onSharesFound_: function(shares) { + this.discoveredShares_ = this.discoveredShares_.concat(shares); + }, + }); diff --git a/chromium/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js b/chromium/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js index 88c41dbafbc..0ce7ea25259 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js @@ -8,6 +8,13 @@ cr.define('settings', function() { initializeDownloads() {} selectDownloadLocation() {} resetAutoOpenFileTypes() {} + // <if expr="chromeos"> + /** + * @param {string} path path to sanitze. + * @return {!Promise<string>} string to display in UI. + */ + getDownloadLocationText(path) {} + // </if> } /** @@ -28,6 +35,13 @@ cr.define('settings', function() { resetAutoOpenFileTypes() { chrome.send('resetAutoOpenFileTypes'); } + + // <if expr="chromeos"> + /** @override */ + getDownloadLocationText(path) { + return cr.sendWithPromise('getDownloadLocationText', path); + } + // </if> } cr.addSingletonGetter(DownloadsBrowserProxyImpl); diff --git a/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html index 90e4ca90487..69dbf529286 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html +++ b/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html @@ -4,6 +4,7 @@ <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="../controls/controlled_button.html"> <link rel="import" href="../controls/settings_toggle_button.html"> +<link rel="import" href="../prefs/prefs_behavior.html"> <link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="downloads_browser_proxy.html"> @@ -24,12 +25,12 @@ <div class="settings-box first two-line"> <div class="start"> <div>$i18n{downloadLocation}</div> - <div class="secondary"> + <div class="secondary" id="defaultDownloadPath"> <if expr="not chromeos"> [[prefs.download.default_directory.value]] </if> <if expr="chromeos"> - [[getDownloadLocation_(prefs.download.default_directory.value)]] + [[downloadLocation_]] </if> </div> </div> @@ -80,7 +81,7 @@ <settings-subpage associated-control="[[$$('#smbShares')]]" page-title="$i18n{smbSharesTitle}"> - <settings-smb-shares-page> + <settings-smb-shares-page prefs="[[prefs]]"> </settings-smb-shares-page> </settings-subpage> </template> diff --git a/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.js b/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.js index 11a50db0904..11c89e7c8c4 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.js +++ b/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.js @@ -18,7 +18,7 @@ Polymer({ is: 'settings-downloads-page', - behaviors: [WebUIListenerBehavior], + behaviors: [WebUIListenerBehavior, PrefsBehavior], properties: { /** @@ -53,6 +53,11 @@ Polymer({ }, readOnly: true, }, + + /** + * The download location string that is suitable to display in the UI. + */ + downloadLocation_: String, // </if> /** @private {!Map<string, string>} */ @@ -73,13 +78,22 @@ Polymer({ }, + // <if expr="chromeos"> + observers: [ + 'handleDownloadLocationChanged_(prefs.download.default_directory.value)' + ], + // </if> + /** @private {?settings.DownloadsBrowserProxy} */ browserProxy_: null, /** @override */ - attached: function() { + created: function() { this.browserProxy_ = settings.DownloadsBrowserProxyImpl.getInstance(); + }, + /** @override */ + ready: function() { this.addWebUIListener('auto-open-downloads-changed', autoOpen => { this.autoOpenDownloads_ = autoOpen; }); @@ -99,25 +113,17 @@ Polymer({ onTapSmbShares_: function() { settings.navigateTo(settings.routes.SMB_SHARES); }, - // </if> - - // <if expr="chromeos"> /** - * @param {string} path - * @return {string} The download location string that is suitable to display - * in the UI. * @private */ - getDownloadLocation_: function(path) { - // Replace /special/drive-<hash>/root with "Google Drive" for remote files, - // /home/chronos/user/Downloads or /home/chronos/u-<hash>/Downloads with - // "Downloads" for local paths, and '/' with ' \u203a ' (angled quote sign) - // everywhere. It is used only for display purpose. - path = path.replace(/^\/special\/drive[^\/]*\/root/, 'Google Drive'); - path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, ''); - path = path.replace(/\//g, ' \u203a '); - return path; + handleDownloadLocationChanged_: function() { + this.browserProxy_ + .getDownloadLocationText(/** @type {string} */ ( + this.getPref('download.default_directory').value)) + .then(text => { + this.downloadLocation_ = text; + }); }, // </if> diff --git a/chromium/chrome/browser/resources/settings/downloads_page/smb_browser_proxy.js b/chromium/chrome/browser/resources/settings/downloads_page/smb_browser_proxy.js index e32cc6e4a62..5085ce6b042 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/smb_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/downloads_page/smb_browser_proxy.js @@ -19,6 +19,7 @@ const SmbMountResult = { NOT_FOUND: 3, UNSUPPORTED_DEVICE: 4, MOUNT_EXISTS: 5, + INVALID_URL: 6, }; cr.define('settings', function() { diff --git a/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.html b/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.html index 83406eaf117..584488ae30d 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.html +++ b/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.html @@ -7,6 +7,7 @@ <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/html/md_select_css.html"> +<link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="../settings_vars_css.html"> <link rel="import" href="add_smb_share_dialog.html"> @@ -17,15 +18,23 @@ <div class="settings-box first"> <div class="start"> <span>$i18n{smbSharesLearnMoreLabel}</span> - <a href="http://support.google.com" target="_blank"> + <a href="$i18n{smbSharesLearnMoreURL}" target="_blank"> $i18n{learnMore} </a> <div class="secondary" hidden="false"> $i18n{requireNetworkMessage} </div> </div> + <template is="dom-if" if="[[!prefs.network_file_shares.allowed.value]]" + restamp> + <cr-policy-pref-indicator + pref="[[prefs.network_file_shares.allowed]]" + icon-aria-label="$i18n{smbSharesTitle}"> + </cr-policy-pref-indicator> + </template> <paper-button class="action-button" id="addShare" - on-click="onAddShareTap_"> + on-click="onAddShareTap_" + disabled="[[!prefs.network_file_shares.allowed.value]]"> $i18n{addSmbShare} </paper-button> </div> diff --git a/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.js b/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.js index 04829640903..f9a8ecc35d1 100644 --- a/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.js +++ b/chromium/chrome/browser/resources/settings/downloads_page/smb_shares_page.js @@ -5,9 +5,20 @@ Polymer({ is: 'settings-smb-shares-page', - behaviors: [WebUIListenerBehavior], + behaviors: [ + WebUIListenerBehavior, + settings.RouteObserverBehavior, + ], properties: { + /** + * Preferences state. + */ + prefs: { + type: Object, + notify: true, + }, + /** @private */ showAddSmbDialog_: Boolean, @@ -15,6 +26,18 @@ Polymer({ addShareResultText_: String, }, + /** + * Overridden from settings.RouteObserverBehavior. + * @param {!settings.Route} route + * @protected + */ + currentRouteChanged: function(route) { + if (route == settings.routes.SMB_SHARES) { + this.showAddSmbDialog_ = + settings.getQueryParameters().get('showAddShare') == 'true'; + } + }, + /** @override */ attached: function() { this.addWebUIListener('on-add-smb-share', this.onAddShare_.bind(this)); @@ -56,6 +79,10 @@ Polymer({ this.addShareResultText_ = loadTimeData.getString('smbShareAddedMountExistsMessage'); break; + case SmbMountResult.INVALID_URL: + this.addShareResultText_ = + loadTimeData.getString('smbShareAddedInvalidURLMessage'); + break; default: this.addShareResultText_ = loadTimeData.getString('smbShareAddedErrorMessage'); diff --git a/chromium/chrome/browser/resources/settings/find_shortcut_behavior.html b/chromium/chrome/browser/resources/settings/find_shortcut_behavior.html index 4fedd0e11f1..50b4f350f43 100644 --- a/chromium/chrome/browser/resources/settings/find_shortcut_behavior.html +++ b/chromium/chrome/browser/resources/settings/find_shortcut_behavior.html @@ -1,4 +1,4 @@ <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html"> +<link rel="import" href="chrome://resources/html/cr/ui/command.html"> <script src="find_shortcut_behavior.js"></script> diff --git a/chromium/chrome/browser/resources/settings/find_shortcut_behavior.js b/chromium/chrome/browser/resources/settings/find_shortcut_behavior.js index 51103e5b683..0beae62528a 100644 --- a/chromium/chrome/browser/resources/settings/find_shortcut_behavior.js +++ b/chromium/chrome/browser/resources/settings/find_shortcut_behavior.js @@ -3,49 +3,80 @@ // found in the LICENSE file. /** - * @fileoverview Listens for a find keyboard shortcut (i.e. Ctrl/Cmd+f) wherever - * this behavior is applied and invokes canHandleFindShortcut(). If - * canHandleFindShortcut() returns true, handleFindShortcut() will be called. - * Override these methods in your element in order to use this behavior. + * @fileoverview Listens for a find keyboard shortcut (i.e. Ctrl/Cmd+f) + * and keeps track of an stack of potential listeners. Only the listener at the + * top of the stack will be notified that a find shortcut has been invoked. */ -cr.exportPath('settings'); - -/** @polymerBehavior */ -settings.FindShortcutBehaviorImpl = { - keyBindings: { - // <if expr="is_macosx"> - 'meta+f': 'onFindShortcut_', - // </if> - // <if expr="not is_macosx"> - 'ctrl+f': 'onFindShortcut_', - // </if> - }, - - /** @private */ - onFindShortcut_: function(e) { - if (!e.defaultPrevented && this.canHandleFindShortcut()) { - this.handleFindShortcut(); - e.preventDefault(); +cr.define('settings', function() { + /** + * Stack of listeners. Only the top listener will handle the shortcut. + * @type {!Array<!HTMLElement>} + */ + const listeners = []; + + /** + * Tracks if any modal context is open in settings. This assumes only one + * modal can be open at a time. The modals that are being tracked include + * cr-dialog and cr-drawer. + * @type {boolean} + */ + let modalContextOpen = false; + + const shortcut = + new cr.ui.KeyboardShortcutList(cr.isMac ? 'meta|f' : 'ctrl|f'); + + window.addEventListener('keydown', e => { + if (e.defaultPrevented || listeners.length == 0) + return; + + if (shortcut.matchesEvent(e)) { + const listener = /** @type {!{handleFindShortcut: function(boolean)}} */ ( + listeners[listeners.length - 1]); + if (listener.handleFindShortcut(modalContextOpen)) + e.preventDefault(); } - }, + }); + + window.addEventListener('cr-dialog-open', () => { + modalContextOpen = true; + }); + + window.addEventListener('cr-drawer-opened', () => { + modalContextOpen = true; + }); + + window.addEventListener('close', e => { + if (['CR-DIALOG', 'CR-DRAWER'].includes(e.composedPath()[0].nodeName)) + modalContextOpen = false; + }); /** - * @return {boolean} - * @protected + * Used to determine how to handle find shortcut invocations. + * @polymerBehavior */ - canHandleFindShortcut: function() { - assertNotReached(); - }, - - /** @protected */ - handleFindShortcut: function() { - assertNotReached(); - }, -}; - -/** @polymerBehavior */ -settings.FindShortcutBehavior = [ - Polymer.IronA11yKeysBehavior, - settings.FindShortcutBehaviorImpl, -]; + const FindShortcutBehavior = { + /** + * If handled, return true. + * @param {boolean} modalContextOpen + * @return {boolean} + * @protected + */ + handleFindShortcut(modalContextOpen) { + assertNotReached(); + }, + + becomeActiveFindShortcutListener() { + assert(listeners.length == 0 || listeners[listeners.length - 1] != this); + listeners.push(this); + }, + + removeSelfAsFindShortcutListener() { + assert(listeners.pop() == this); + }, + }; + + return { + FindShortcutBehavior, + }; +}); diff --git a/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html b/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html index fdfd106a425..e87f2d0f2f0 100644 --- a/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html +++ b/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html @@ -37,6 +37,14 @@ sub-label="$i18n{googleAssistantEnableHotwordDescription}"> </settings-toggle-button> </template> + <template is="dom-if" if="[[assistantFeatureEnabled_]]"> + <settings-toggle-button id="googleAssistantNotificationEnable" + class="continuation indented" + pref="{{prefs.settings.voice_interaction.notification.enabled}}" + label="$i18n{googleAssistantEnableNotification}" + sub-label="$i18n{googleAssistantEnableNotificationDescription}"> + </settings-toggle-button> + </template> <div id="googleAssistantSettings" class="settings-box" on-click="onGoogleAssistantSettingsTapped_" actionable> <div class="start"> diff --git a/chromium/chrome/browser/resources/settings/icons.html b/chromium/chrome/browser/resources/settings/icons.html index 0f361f4f1cc..ae8db8c7760 100644 --- a/chromium/chrome/browser/resources/settings/icons.html +++ b/chromium/chrome/browser/resources/settings/icons.html @@ -49,8 +49,9 @@ List icons here rather than importing large sets of (e.g. Polymer) icons. </g> <!-- Icons for MultiDevice Settings UI --> - <g id="smart-lock" fill="#9AA0A6" fill-rule="nonzero"><path d="M18,9 L17,9 L17,7 C17,4.24 14.76,2 12,2 C9.24,2 7,4.24 7,7 L7,9 L6,9 C4.9,9 4,9.9 4,11 L4,21 C4,22.1 4.9,23 6,23 L18,23 C19.1,23 20,22.1 20,21 L20,11 C20,9.9 19.1,9 18,9 Z M9,7 C9,5.34 10.34,4 12,4 C13.66,4 15,5.34 15,7 L15,9 L9,9 L9,7 Z M18,21 L6,21 L6,11 L18,11 L18,21 Z M12,18 C13.1,18 14,17.1 14,16 C14,14.9 13.1,14 12,14 C10.9,14 10,14.9 10,16 C10,17.1 10.9,18 12,18 Z"></path></g> - + <g id="multidevice-better-together-suite" fill="#9AA0A6" fill-rule="nonzero"><path d="M17,1.01 L7,1 C5.9,1 5,1.9 5,3 L5,21 C5,22.1 5.9,23 7,23 L17,23 C18.1,23 19,22.1 19,21 L19,3 C19,1.9 18.1,1.01 17,1.01 Z M17,21 L7,21 L7,20 L17,20 L17,21 Z M17,18 L7,18 L7,6 L17,6 L17,18 Z M7,4 L7,3 L17,3 L17,4 L7,4 Z"></path></g> + <g id="multidevice-smart-lock" fill="#9AA0A6" fill-rule="nonzero"><path d="M18,9 L17,9 L17,7 C17,4.24 14.76,2 12,2 C9.24,2 7,4.24 7,7 L7,9 L6,9 C4.9,9 4,9.9 4,11 L4,21 C4,22.1 4.9,23 6,23 L18,23 C19.1,23 20,22.1 20,21 L20,11 C20,9.9 19.1,9 18,9 Z M9,7 C9,5.34 10.34,4 12,4 C13.66,4 15,5.34 15,7 L15,9 L9,9 L9,7 Z M18,21 L6,21 L6,11 L18,11 L18,21 Z M12,18 C13.1,18 14,17.1 14,16 C14,14.9 13.1,14 12,14 C10.9,14 10,14.9 10,16 C10,17.1 10.9,18 12,18 Z"></path></g> + <g id="multidevice-messages" fill="#9AA0A6" fill-rule="nonzero"><path d="M19.9593506,2 C21.0593506,2 22,2.89658203 22,3.99658203 L22,16.0036621 C22,17.1036621 21.0593506,18.0036621 19.9593506,18.0036621 L6.00878906,18.0036621 L2.00878906,22.4 L2.01878906,3.99658203 C2.01878906,2.89658203 2.90878906,2 4.00878906,2 L19.9593506,2 Z M8,8.56896973 L4.5,12 L8,15.6103516 L8,13 L14,13 L14,11 L8,11 L8,8.56896973 Z M20.5688477,7 L17,3.57207031 L17,6 L11,6 L11,8 L17,8 L17,10.5720703 L20.5688477,7 Z"></path></g> </if> <!-- Protected Content SVG --> diff --git a/chromium/chrome/browser/resources/settings/internet_page/BUILD.gn b/chromium/chrome/browser/resources/settings/internet_page/BUILD.gn index 19d20ec040e..02cd35af1eb 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/internet_page/BUILD.gn @@ -12,7 +12,6 @@ js_type_check("closure_compile") { ":internet_page", ":internet_page_browser_proxy", ":internet_subpage", - ":network_listener_behavior", ":network_proxy_section", ":network_summary", ":network_summary_item", @@ -24,9 +23,9 @@ js_library("internet_page") { deps = [ ":internet_config", ":internet_page_browser_proxy", - ":network_listener_behavior", "..:route", "../settings_page:settings_animated_pages", + "//ui/webui/resources/cr_elements/chromeos/network:cr_network_listener_behavior", "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", "//ui/webui/resources/js:assert", "//ui/webui/resources/js:i18n_behavior", @@ -101,14 +100,6 @@ js_library("internet_subpage") { extra_sources = [ "$interfaces_path/networking_private_interface.js" ] } -js_library("network_listener_behavior") { - deps = [ - "//ui/webui/resources/js:assert", - ] - externs_list = [ "$externs_path/networking_private.js" ] - extra_sources = [ "$interfaces_path/networking_private_interface.js" ] -} - js_library("network_proxy_section") { deps = [ "..:route", diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html index a7c1e4f2f18..5eaadd83c65 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html @@ -38,16 +38,16 @@ } iron-icon { - -webkit-margin-end: 10px; + margin-inline-end: 10px; } cr-toggle { - -webkit-margin-start: var(--settings-control-label-spacing); + margin-inline-start: var(--settings-control-label-spacing); } cr-policy-network-indicator, cr-policy-indicator { - -webkit-margin-start: var(--settings-controlled-by-spacing); + margin-inline-start: var(--settings-controlled-by-spacing); } .settings-box:first-of-type { @@ -93,13 +93,16 @@ $i18n{networkButtonActivate} </paper-button> <paper-button on-click="onConfigureTap_" - hidden$="[[!showConfigure_(networkProperties, globalPolicy)]]"> + hidden$="[[!showConfigure_(networkProperties, globalPolicy, + managedNetworkAvailable)]]"> $i18n{networkButtonConfigure} </paper-button> <paper-button class="action-button" on-click="onConnectTap_" - hidden$="[[!showConnect_(networkProperties, globalPolicy)]]" + hidden$="[[!showConnect_(networkProperties, globalPolicy, + managedNetworkAvailable)]]" disabled="[[!enableConnect_(networkProperties, defaultNetwork, - networkPropertiesReceived_, outOfRange_, globalPolicy)]]"> + networkPropertiesReceived_, outOfRange_, globalPolicy, + managedNetworkAvailable)]]"> $i18n{networkButtonConnect} </paper-button> <paper-button class="action-button" on-click="onDisconnectTap_" @@ -110,12 +113,14 @@ <!-- Disabled by policy / Shared messages. --> <div class="settings-box continuation" - hidden$="[[!isBlockedByPolicy_(networkProperties, globalPolicy)]]"> + hidden$="[[!isBlockedByPolicy_(networkProperties, globalPolicy, + managedNetworkAvailable)]]"> <iron-icon class="policy" icon="cr20:domain"></iron-icon> <div>$i18n{networkConnectNotAllowed}</div> </div> <div class="settings-box continuation" - hidden$="[[!showShared_(networkProperties, globalPolicy)]]"> + hidden$="[[!showShared_(networkProperties, globalPolicy, + managedNetworkAvailable)]]"> $i18n{networkShared} </div> @@ -132,7 +137,8 @@ <template is="dom-if" if="[[!isSecondaryUser_]]"> <!-- Prefer this network. --> <template is="dom-if" - if="[[showPreferNetwork_(networkProperties, globalPolicy)]]"> + if="[[showPreferNetwork_(networkProperties, globalPolicy, + managedNetworkAvailable)]]"> <div class="settings-box"> <div id="preferNetworkToggleLabel"class="start"> $i18n{networkPrefer} @@ -148,7 +154,8 @@ </template> <!-- Autoconnect. --> <template is="dom-if" - if="[[showAutoConnect_(networkProperties, globalPolicy)]]"> + if="[[showAutoConnect_(networkProperties, globalPolicy, + managedNetworkAvailable)]]"> <div class="settings-box"> <div id="autoConnectToggleLabel" class="start"> $i18n{networkAutoConnect} @@ -227,8 +234,8 @@ </iron-collapse> </template> - <template is="dom-if" - if="[[hasNetworkSection_(networkProperties, globalPolicy)]]"> + <template is="dom-if" if="[[hasNetworkSection_(networkProperties, + globalPolicy, managedNetworkAvailable)]]"> <!-- Network toggle --> <div class="settings-box" actionable on-click="toggleNetworkExpanded_"> <div class="start">$i18n{networkSectionNetwork}</div> @@ -270,8 +277,8 @@ </iron-collapse> </template> - <template is="dom-if" - if="[[hasProxySection_(networkProperties, globalPolicy)]]"> + <template is="dom-if" if="[[hasProxySection_(networkProperties, + globalPolicy, managedNetworkAvailable)]]"> <!-- Proxy toggle --> <div class="settings-box" actionable on-click="toggleProxyExpanded_"> <div class="start">$i18n{networkSectionProxy}</div> diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js index 0c6b717312d..399bbfd6179 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js @@ -90,6 +90,14 @@ Polymer({ value: null, }, + /** Whether a managed network is available in the visible network list. + * @private {boolean} + */ + managedNetworkAvailable: { + type: Boolean, + value: false, + }, + /** * Interface for networkingPrivate calls, passed from internet_page. * @type {NetworkingPrivate} @@ -137,7 +145,7 @@ Polymer({ listeners: { 'network-list-changed': 'checkNetworkExists_', - 'networks-changed': 'updateNetworkDetails_', + 'networks-changed': 'updateNetworkDetails_' }, /** @private {boolean} */ @@ -468,30 +476,36 @@ Polymer({ /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} * @private */ - isBlockedByPolicy_: function(networkProperties, globalPolicy) { + isBlockedByPolicy_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { if (networkProperties.Type != CrOnc.Type.WI_FI || - this.isPolicySource(networkProperties.Source)) { + this.isPolicySource(networkProperties.Source) || !globalPolicy) { return false; } - return !!globalPolicy && - (!!globalPolicy.AllowOnlyPolicyNetworksToConnect || - (!!networkProperties.WiFi && !!networkProperties.WiFi.HexSSID && - !!globalPolicy.BlacklistedHexSSIDs && - globalPolicy.BlacklistedHexSSIDs.includes( - CrOnc.getStateOrActiveString(networkProperties.WiFi.HexSSID)))); + return !!globalPolicy.AllowOnlyPolicyNetworksToConnect || + (!!globalPolicy.AllowOnlyPolicyNetworksToConnectIfAvailable && + !!managedNetworkAvailable) || + (!!networkProperties.WiFi && !!networkProperties.WiFi.HexSSID && + !!globalPolicy.BlacklistedHexSSIDs && + globalPolicy.BlacklistedHexSSIDs.includes( + CrOnc.getStateOrActiveString(networkProperties.WiFi.HexSSID))); }, /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} * @private */ - showConnect_: function(networkProperties, globalPolicy) { - if (this.isBlockedByPolicy_(networkProperties, globalPolicy)) + showConnect_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { + if (this.isBlockedByPolicy_( + networkProperties, globalPolicy, managedNetworkAvailable)) return false; // TODO(lgcheng@) support connect Arc VPN from UI once Android support API // to initiate a VPN session. @@ -549,13 +563,16 @@ Polymer({ /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} * @private */ - showConfigure_: function(networkProperties, globalPolicy) { + showConfigure_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { if (this.isSecondaryUser_) return false; - if (this.isBlockedByPolicy_(networkProperties, globalPolicy)) + if (this.isBlockedByPolicy_( + networkProperties, globalPolicy, managedNetworkAvailable)) return false; const type = networkProperties.Type; if (type == CrOnc.Type.CELLULAR || type == CrOnc.Type.TETHER) @@ -621,13 +638,15 @@ Polymer({ * @param {boolean} networkPropertiesReceived * @param {boolean} outOfRange * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} Whether or not to enable the network connect button. * @private */ enableConnect_: function( networkProperties, defaultNetwork, networkPropertiesReceived, outOfRange, - globalPolicy) { - if (!this.showConnect_(networkProperties, globalPolicy)) + globalPolicy, managedNetworkAvailable) { + if (!this.showConnect_( + networkProperties, globalPolicy, managedNetworkAvailable)) return false; if (!networkPropertiesReceived || outOfRange) return false; @@ -880,26 +899,32 @@ Polymer({ /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} True if the shared message should be shown. * @private */ - showShared_: function(networkProperties, globalPolicy) { + showShared_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { return (networkProperties.Source == 'Device' || networkProperties.Source == 'DevicePolicy') && - !this.isBlockedByPolicy_(networkProperties, globalPolicy); + !this.isBlockedByPolicy_( + networkProperties, globalPolicy, managedNetworkAvailable); }, /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} True if the AutoConnect checkbox should be shown. * @private */ - showAutoConnect_: function(networkProperties, globalPolicy) { + showAutoConnect_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { return networkProperties.Type != CrOnc.Type.ETHERNET && this.isRemembered_(networkProperties) && !this.isArcVpn_(networkProperties) && - !this.isBlockedByPolicy_(networkProperties, globalPolicy); + !this.isBlockedByPolicy_( + networkProperties, globalPolicy, managedNetworkAvailable); }, /** @@ -930,15 +955,18 @@ Polymer({ /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} True if the prefer network checkbox should be shown. * @private */ - showPreferNetwork_: function(networkProperties, globalPolicy) { + showPreferNetwork_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { // TODO(stevenjb): Resolve whether or not we want to allow "preferred" for // networkProperties.Type == CrOnc.Type.ETHERNET. return this.isRemembered_(networkProperties) && !this.isArcVpn_(networkProperties) && - !this.isBlockedByPolicy_(networkProperties, globalPolicy); + !this.isBlockedByPolicy_( + networkProperties, globalPolicy, managedNetworkAvailable); }, /** @@ -1109,16 +1137,19 @@ Polymer({ /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} * @private */ - hasNetworkSection_: function(networkProperties, globalPolicy) { + hasNetworkSection_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { if (networkProperties.Type == CrOnc.Type.TETHER) { // These settings apply to the underlying WiFi network, not the Tether // network. return false; } - if (this.isBlockedByPolicy_(networkProperties, globalPolicy)) + if (this.isBlockedByPolicy_( + networkProperties, globalPolicy, managedNetworkAvailable)) return false; if (networkProperties.Type == CrOnc.Type.CELLULAR) return true; @@ -1128,16 +1159,19 @@ Polymer({ /** * @param {!CrOnc.NetworkProperties} networkProperties * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} * @private */ - hasProxySection_: function(networkProperties, globalPolicy) { + hasProxySection_: function( + networkProperties, globalPolicy, managedNetworkAvailable) { if (networkProperties.Type == CrOnc.Type.TETHER) { // Proxy settings apply to the underlying WiFi network, not the Tether // network. return false; } - if (this.isBlockedByPolicy_(networkProperties, globalPolicy)) + if (this.isBlockedByPolicy_( + networkProperties, globalPolicy, managedNetworkAvailable)) return false; return this.isRememberedOrConnected_(networkProperties); }, diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html index 35babb87473..82af6eeacb9 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html @@ -10,7 +10,7 @@ <template> <style include="internet-shared iron-flex"> cr-policy-indicator { - -webkit-margin-start: var(--settings-controlled-by-spacing); + margin-inline-start: var(--settings-controlled-by-spacing); } </style> diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_page.html b/chromium/chrome/browser/resources/settings/internet_page/internet_page.html index 41701b4c97a..5544462d263 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_listener_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> @@ -17,16 +18,15 @@ <link rel="import" href="internet_detail_page.html"> <link rel="import" href="internet_known_networks_page.html"> <link rel="import" href="internet_subpage.html"> -<link rel="import" href="network_listener_behavior.html"> <link rel="import" href="network_summary.html"> <dom-module id="settings-internet-page"> <template> <style include="settings-shared"> iron-icon.policy { - -webkit-margin-end: 12px; - -webkit-margin-start: 4px; height: 24px; + margin-inline-end: 12px; + margin-inline-start: 4px; width: 24px; } </style> @@ -37,7 +37,8 @@ device-states="{{deviceStates}}" networking-private="[[networkingPrivate]]"> </network-summary> - <template is="dom-if" if="[[allowAddConnection_(globalPolicy_)]]"> + <template is="dom-if" if="[[allowAddConnection_(globalPolicy_, + managedNetworkAvailable)]]"> <div actionable class="settings-box two-line" on-click="onExpandAddConnectionsTap_"> <div class="start layout horizontal center"> @@ -88,7 +89,8 @@ </div> </template> </template> - <template is="dom-if" if="[[!allowAddConnection_(globalPolicy_)]]"> + <template is="dom-if" if="[[!allowAddConnection_(globalPolicy_, + managedNetworkAvailable)]]"> <div class="settings-box"> <iron-icon class="policy" icon="cr20:domain"></iron-icon> <div>$i18n{internetAddConnectionNotAllowed}</div> @@ -101,7 +103,8 @@ <settings-internet-detail-page prefs="{{prefs}}" default-network="[[defaultNetwork]]" global-policy="[[globalPolicy_]]" - networking-private="[[networkingPrivate]]"> + networking-private="[[networkingPrivate]]" + managed-network-available="[[managedNetworkAvailable]]"> </settings-internet-detail-page> </settings-subpage> </template> diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_page.js b/chromium/chrome/browser/resources/settings/internet_page/internet_page.js index 3f01b7f2ea7..103624689c5 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.js @@ -12,7 +12,7 @@ Polymer({ behaviors: [ I18nBehavior, settings.RouteObserverBehavior, WebUIListenerBehavior, - NetworkListenerBehavior + CrNetworkListenerBehavior ], properties: { @@ -39,6 +39,7 @@ Polymer({ deviceStates: { type: Object, notify: true, + observer: 'onDeviceStatesChanged_', }, /** @@ -78,7 +79,19 @@ Polymer({ }, /** @private {!chrome.networkingPrivate.GlobalPolicy|undefined} */ - globalPolicy_: Object, + globalPolicy_: { + type: Object, + value: null, + }, + + /** + * Whether a managed network is available in the visible network list. + * @private {boolean} + */ + managedNetworkAvailable: { + type: Boolean, + value: false, + }, /** Overridden from NetworkListenerBehavior. */ networkListChangeSubscriberSelectors_: { @@ -125,7 +138,7 @@ Polymer({ } }, - /** @private {!Map<string, string>} */ + /** @private {!Map<string, Element>} */ focusConfig_: { type: Object, value: function() { @@ -239,18 +252,21 @@ Polymer({ return; // Focus the subpage arrow where appropriate. - let selector; + let element; if (route == settings.routes.INTERNET_NETWORKS) { // iron-list makes the correct timing to focus an item in the list // very complicated, and the item may not exist, so just focus the // entire list for now. - selector = '* /deep/ #networkList'; + let subPage = this.$$('settings-internet-subpage'); + if (subPage) + element = subPage.$$('#networkList'); } else if (this.detailType_) { - selector = - '* /deep/ #' + this.detailType_ + ' /deep/ .subpage-arrow button'; + element = this.$$('network-summary') + .$$(`#${this.detailType_}`) + .$$('.subpage-arrow button'); } - if (selector && this.querySelector(selector)) - this.focusConfig_.set(oldRoute.path, selector); + if (element) + this.focusConfig_.set(oldRoute.path, element); else this.focusConfig_.delete(oldRoute.path); }, @@ -274,10 +290,14 @@ Polymer({ */ onShowConfig_: function(event) { const properties = event.detail; - let configAndConnect = !properties.GUID; // New configuration - this.showConfig_( - configAndConnect, properties.Type, properties.GUID, - CrOnc.getNetworkName(properties)); + if (!properties.GUID) { + // New configuration + this.showConfig_(true /* configAndConnect */, properties.Type); + } else { + this.showConfig_( + false /* configAndConnect */, properties.Type, properties.GUID, + CrOnc.getNetworkName(properties)); + } }, /** @@ -354,6 +374,21 @@ Polymer({ }, /** + * @param {!CrOnc.DeviceStateProperties|undefined} newValue + * @param {!CrOnc.DeviceStateProperties|undefined} oldValue + * @private + */ + onDeviceStatesChanged_: function(newValue, oldValue) { + let wifiDeviceState = this.getDeviceState_(CrOnc.Type.WI_FI, newValue); + let managedNetworkAvailable = false; + if (!!wifiDeviceState) + managedNetworkAvailable = !!wifiDeviceState.ManagedNetworkAvailable; + + if (this.managedNetworkAvailable != managedNetworkAvailable) + this.managedNetworkAvailable = managedNetworkAvailable; + }, + + /** * @param {!{detail: {type: string}}} event * @private */ @@ -517,10 +552,16 @@ Polymer({ /** * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy + * @param {boolean} managedNetworkAvailable * @return {boolean} */ - allowAddConnection_: function(globalPolicy) { - return !globalPolicy.AllowOnlyPolicyNetworksToConnect; + allowAddConnection_: function(globalPolicy, managedNetworkAvailable) { + if (!globalPolicy) + return true; + + return !globalPolicy.AllowOnlyPolicyNetworksToConnect && + (!globalPolicy.AllowOnlyPolicyNetworksToConnectIfAvailable || + !managedNetworkAvailable); }, /** @@ -528,7 +569,8 @@ Polymer({ * @return {string} */ getAddThirdPartyVpnLabel_: function(provider) { - return this.i18n('internetAddThirdPartyVPN', provider.ProviderName); + return this.i18n( + 'internetAddThirdPartyVPN', provider.ProviderName || ''); }, /** diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_shared_css.html b/chromium/chrome/browser/resources/settings/internet_page/internet_shared_css.html index 39d86fdfa70..a3b39fb5c99 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_shared_css.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_shared_css.html @@ -6,15 +6,15 @@ <template> <style include="settings-shared"> cr-network-icon { - -webkit-padding-end: var(--settings-box-row-padding); + padding-inline-end: var(--settings-box-row-padding); } iron-icon.policy { - -webkit-margin-end: var(--cr-controlled-by-spacing); + margin-inline-end: var(--cr-controlled-by-spacing); } .indented { - -webkit-margin-start: var(--settings-box-row-padding); + margin-inline-start: var(--settings-box-row-padding); } .stretch { diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html index 2889a2e4f56..40ef55b8d7b 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html @@ -20,13 +20,13 @@ separator lines can fill the entire width of the page. */ #networkListDiv > * { /* cr-network-list is padded to the right to allow space for a ripple */ - -webkit-padding-end: calc(var(--settings-box-row-padding) - + padding-inline-end: calc(var(--settings-box-row-padding) - var(--cr-icon-ripple-padding)); - -webkit-padding-start: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-padding); } #addButton { - -webkit-margin-end: var(--settings-control-label-spacing); + margin-inline-end: var(--settings-control-label-spacing); } #onOff { @@ -38,9 +38,9 @@ } .vpn-header { - -webkit-margin-end: 12px; - -webkit-margin-start: 4px; margin-bottom: 8px; + margin-inline-end: 12px; + margin-inline-start: 4px; margin-top: 8px; } @@ -71,8 +71,8 @@ #gmscore-notifications-instructions { @apply --cr-secondary-text; - -webkit-padding-start: 15px; margin: 0; + padding-inline-start: 15px; } </style> diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js index 7f61083c696..003c91a13f6 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js +++ b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js @@ -363,7 +363,8 @@ Polymer({ * @private */ getAddThirdPartyVpnA11yString_: function(vpnState) { - return this.i18n('internetAddThirdPartyVPN', vpnState.ProviderName); + return this.i18n( + 'internetAddThirdPartyVPN', vpnState.ProviderName || ''); }, /** @@ -509,7 +510,7 @@ Polymer({ assert(this.defaultNetwork !== undefined); const state = e.detail; e.target.blur(); - if (this.canConnect_(state, this.defaultNetwork, this.globalPolicy)) { + if (this.canConnect_(state)) { this.fire('network-connect', {networkProperties: state}); return; } @@ -518,35 +519,35 @@ Polymer({ /** * @param {!CrOnc.NetworkStateProperties} state The network state. - * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy * @private */ - isBlockedByPolicy_: function(state, globalPolicy) { - if (state.Type != CrOnc.Type.WI_FI || this.isPolicySource(state.Source)) { + isBlockedByPolicy_: function(state) { + if (state.Type != CrOnc.Type.WI_FI || this.isPolicySource(state.Source) || + !this.globalPolicy) { return false; } - return !!globalPolicy && - (!!globalPolicy.AllowOnlyPolicyNetworksToConnect || - (!!state.WiFi && !!state.WiFi.HexSSID && - !!globalPolicy.BlacklistedHexSSIDs && - globalPolicy.BlacklistedHexSSIDs.includes(state.WiFi.HexSSID))); + return !!this.globalPolicy.AllowOnlyPolicyNetworksToConnect || + (!!this.globalPolicy.AllowOnlyPolicyNetworksToConnectIfAvailable && + !!this.deviceState && !!this.deviceState.ManagedNetworkAvailable) || + (!!state.WiFi && !!state.WiFi.HexSSID && + !!this.globalPolicy.BlacklistedHexSSIDs && + this.globalPolicy.BlacklistedHexSSIDs.includes(state.WiFi.HexSSID)); }, /** * Determines whether or not a network state can be connected to. * @param {!CrOnc.NetworkStateProperties} state The network state. - * @param {?CrOnc.NetworkStateProperties} defaultNetwork - * @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy * @private */ - canConnect_: function(state, defaultNetwork, globalPolicy) { + canConnect_: function(state) { if (state.ConnectionState != CrOnc.ConnectionState.NOT_CONNECTED) return false; - if (this.isBlockedByPolicy_(state, globalPolicy)) + if (this.isBlockedByPolicy_(state)) return false; if (state.Type == CrOnc.Type.VPN && - (!defaultNetwork || - defaultNetwork.ConnectionState != CrOnc.ConnectionState.CONNECTED)) { + (!this.defaultNetwork || + this.defaultNetwork.ConnectionState != + CrOnc.ConnectionState.CONNECTED)) { return false; } return true; diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_listener_behavior.html b/chromium/chrome/browser/resources/settings/internet_page/network_listener_behavior.html deleted file mode 100644 index f564a7461d6..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_listener_behavior.html +++ /dev/null @@ -1 +0,0 @@ -<script src="network_listener_behavior.js"></script> diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_listener_behavior.js b/chromium/chrome/browser/resources/settings/internet_page/network_listener_behavior.js deleted file mode 100644 index 665cc54069c..00000000000 --- a/chromium/chrome/browser/resources/settings/internet_page/network_listener_behavior.js +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018 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. - -/** - * @fileoverview Polymer behavior for alerting specified child elements of - * changes to the devices network data. - */ - -/** @polymerBehavior */ -const NetworkListenerBehavior = { - properties: { - /** - * Array of selectors specifying all children to alert of changes to the - * network list. - * @private {!Array<string>} - */ - networkListChangeSubscriberSelectors_: Array, - - /** - * Array of selectors specifying all children to alert of important changes - * to the specific networks. - * @private {!Array<string>} - */ - networksChangeSubscriberSelectors_: Array, - - /** @type {!NetworkingPrivate} */ - networkingPrivate: Object, - }, - - /** @private {?function(!Array<string>)} */ - networkListChangedListener_: null, - - /** @private {?function(!Array<string>)} */ - networksChangedListener_: null, - - /** @override */ - attached: function() { - this.networkListChangedListener_ = this.networkListChangedListener_ || - this.onNetworkListChanged_.bind(this); - this.networkingPrivate.onNetworkListChanged.addListener( - this.networkListChangedListener_); - - this.networksChangedListener_ = - this.networksChangedListener_ || this.onNetworksChanged_.bind(this); - this.networkingPrivate.onNetworksChanged.addListener( - this.networksChangedListener_); - }, - - /** @override */ - detached: function() { - this.networkingPrivate.onNetworkListChanged.removeListener( - assert(this.networkListChangedListener_)); - this.networkingPrivate.onNetworksChanged.removeListener( - assert(this.networksChangedListener_)); - }, - - /** - * This event is triggered when the list of networks changes. - * |networkIds| contains the ids for all visible or configured networks. - * networkingPrivate.onNetworkListChanged event callback. - * @param {!Array<string>} networkIds - * @private - */ - onNetworkListChanged_: function(networkIds) { - const event = new CustomEvent('network-list-changed', {detail: networkIds}); - this.networkListChangeSubscriberSelectors_.forEach( - selector => this.maybeDispatchEvent_(selector, event)); - }, - - /** - * This event is triggered when interesting properties of a network change. - * |networkIds| contains the ids for networks whose properties have changed. - * networkingPrivate.onNetworksChanged event callback. - * @param {!Array<string>} networkIds - * @private - */ - onNetworksChanged_: function(networkIds) { - const event = new CustomEvent('networks-changed', {detail: networkIds}); - this.networksChangeSubscriberSelectors_.forEach( - selector => this.maybeDispatchEvent_(selector, event)); - }, - - /** - * @param {!Event} event - * @private - */ - maybeDispatchEvent_: function(selectors, event) { - const element = this.$$(selectors); - if (!element) - return; - element.dispatchEvent(event); - }, -}; diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html b/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html index 146da4c1ceb..9d718a9df80 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html +++ b/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html @@ -22,11 +22,11 @@ <template> <style include="internet-shared cr-hidden-style iron-flex iron-flex-alignment"> cr-policy-network-indicator { - -webkit-margin-end: 10px; + margin-inline-end: 10px; } extension-controlled-indicator { - -webkit-margin-start: 0; + margin-inline-start: 0; width: 100%; } diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html index 930197efe7d..7edbb1cd5f2 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html +++ b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html @@ -15,7 +15,12 @@ <template> <style include="internet-shared iron-flex"> network-siminfo { - -webkit-padding-start: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-padding); + } + + #outerBox { + padding: 0 var(--settings-box-row-padding); + @apply(--network-summary-item-outer-box); } #details { @@ -24,9 +29,10 @@ flex: auto; } - #networkName { + #networkTitleText { color: #333; font-weight: 500; + @apply(--network-summary-item-title); } #networkState { @@ -35,7 +41,7 @@ font-weight: 400; } </style> - <div class="settings-box two-line"> + <div id="outerBox" class="settings-box two-line"> <div actionable class="flex layout horizontal center" on-click="onShowDetailsTap_"> <div id="details" no-flex$="[[showSimInfo_(deviceState)]]"> @@ -43,7 +49,9 @@ device-state="[[deviceState]]"> </cr-network-icon> <div class="flex"> - <div id="networkName">[[getNetworkName_(activeNetworkState)]]</div> + <div id="networkTitleText"> + [[getTitleText_(activeNetworkState)]] + </div> <div id="networkState"> [[getNetworkStateText_(activeNetworkState, deviceState)]] </div> diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js index ab3be60ec47..b4a8f8892ea 100644 --- a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js +++ b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js @@ -51,14 +51,14 @@ Polymer({ * @type {!NetworkingPrivate} */ networkingPrivate: Object, - }, - /** - * @return {string} - * @private - */ - getNetworkName_: function() { - return CrOncStrings['OncType' + this.activeNetworkState.Type]; + /** + * Title line describing the network type to appear in the row's top line. + * If it is undefined, the title text is a default from CrOncStrings (see + * this.getTitleText_() below). + * @type {string|undefined} + */ + networkTitleText: String, }, /** @@ -335,6 +335,15 @@ Polymer({ }, /** + * @return {string} + * @private + */ + getTitleText_: function() { + return this.networkTitleText || + CrOncStrings['OncType' + this.activeNetworkState.Type]; + }, + + /** * Make sure events in embedded components do not propagate to onDetailsTap_. * @param {!Event} event * @private diff --git a/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html b/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html index 9c0daa34a25..62c848ce669 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html +++ b/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html @@ -16,7 +16,7 @@ <style include="settings-shared"> cr-dialog { --cr-dialog-body: { - -webkit-padding-end: 0; + padding-inline-end: 0; display: flex; flex-direction: column; height: 350px; @@ -25,7 +25,7 @@ } settings-subpage-search { - -webkit-padding-end: 24px; + padding-inline-end: 24px; } iron-list { @@ -34,7 +34,7 @@ .ripple-padding { /* Create a little extra space for checkbox ink ripple to flow into. */ - -webkit-padding-start: 20px; + padding-inline-start: 20px; } cr-checkbox { diff --git a/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.js b/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.js index 4e38bc5f976..77c6b94a521 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.js +++ b/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.js @@ -48,16 +48,23 @@ Polymer({ /** @override */ attached: function() { this.$.dialog.showModal(); + this.becomeActiveFindShortcutListener(); }, - // Override settings.FindShortcutBehavior methods. - canHandleFindShortcut: function() { - return true; + /** @override */ + detached: function() { + this.removeSelfAsFindShortcutListener(); }, - handleFindShortcut: function() { - this.$.search.getSearchInput().scrollIntoViewIfNeeded(); - this.$.search.getSearchInput().focus(); + // Override settings.FindShortcutBehavior methods. + handleFindShortcut: function(modalContextOpen) { + // Assumes this is the only open modal. + const searchInput = this.$.search.getSearchInput(); + if (searchInput != this.$.search.shadowRoot.activeElement) { + searchInput.scrollIntoViewIfNeeded(); + searchInput.focus(); + } + return true; }, /** diff --git a/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html b/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html index d8957326506..cef2743fea8 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html +++ b/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html @@ -37,7 +37,8 @@ invalid="[[isWordInvalid_(newWordValue_)]]" error-message="[[isWordInvalid_(newWordValue_, '$i18nPolymer{addDictionaryWordDuplicateError}', - '$i18nPolymer{addDictionaryWordLengthError}')]]"> + '$i18nPolymer{addDictionaryWordLengthError}')]]" + spellcheck="false"> <paper-button class="secondary-button" on-click="onAddWordTap_" disabled="[[disableAddButton_(newWordValue_)]]" id="addWord" slot="suffix"> diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages.js b/chromium/chrome/browser/resources/settings/languages_page/languages.js index 6acc1a9601f..6c81d0d59f5 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/languages.js +++ b/chromium/chrome/browser/resources/settings/languages_page/languages.js @@ -174,6 +174,12 @@ Polymer({ // <if expr="chromeos"> /** @private {?InputMethodPrivate} */ inputMethodPrivate_: null, + + /** @private {?Function} */ + boundOnInputMethodAdded_: null, + + /** @private {?Function} */ + boundOnInputMethodRemoved_: null, // </if> /** @override */ @@ -251,6 +257,12 @@ Polymer({ this.boundOnInputMethodChanged_ = this.onInputMethodChanged_.bind(this); this.inputMethodPrivate_.onChanged.addListener( assert(this.boundOnInputMethodChanged_)); + this.boundOnInputMethodAdded_ = this.onInputMethodAdded_.bind(this); + this.languageSettingsPrivate_.onInputMethodAdded.addListener( + this.boundOnInputMethodAdded_); + this.boundOnInputMethodRemoved_ = this.onInputMethodRemoved_.bind(this); + this.languageSettingsPrivate_.onInputMethodRemoved.addListener( + this.boundOnInputMethodRemoved_); } }, @@ -260,6 +272,12 @@ Polymer({ this.inputMethodPrivate_.onChanged.removeListener( assert(this.boundOnInputMethodChanged_)); this.boundOnInputMethodChanged_ = null; + this.languageSettingsPrivate_.onInputMethodAdded.removeListener( + assert(this.boundOnInputMethodAdded_)); + this.boundOnInputMethodAdded_ = null; + this.languageSettingsPrivate_.onInputMethodRemoved.removeListener( + assert(this.boundOnInputMethodRemoved_)); + this.boundOnInputMethodRemoved_ = null; } // <if expr="not is_macosx"> @@ -414,25 +432,7 @@ Polymer({ } if (supportedInputMethods) { - // Populate the hash map of supported input methods. - for (let j = 0; j < supportedInputMethods.length; j++) { - const inputMethod = supportedInputMethods[j]; - inputMethod.enabled = !!inputMethod.enabled; - inputMethod.isProhibitedByPolicy = !!inputMethod.isProhibitedByPolicy; - // Add the input method to the map of IDs. - this.supportedInputMethodMap_.set(inputMethod.id, inputMethod); - // Add the input method to the list of input methods for each language - // it supports. - for (let k = 0; k < inputMethod.languageCodes.length; k++) { - const languageCode = inputMethod.languageCodes[k]; - if (!this.supportedLanguageMap_.has(languageCode)) - continue; - if (!this.languageInputMethods_.has(languageCode)) - this.languageInputMethods_.set(languageCode, [inputMethod]); - else - this.languageInputMethods_.get(languageCode).push(inputMethod); - } - } + this.createInputMethodModel_(supportedInputMethods); } let prospectiveUILanguage; @@ -475,6 +475,37 @@ Polymer({ }, /** + * Constructs the input method part of the languages model. + * @param {!Array<!chrome.languageSettingsPrivate.InputMethod>} + * supportedInputMethods Input methods. + * @private + */ + createInputMethodModel_: function(supportedInputMethods) { + assert(cr.isChromeOS); + // Populate the hash map of supported input methods. + this.supportedInputMethodMap_.clear(); + this.languageInputMethods_.clear(); + for (let j = 0; j < supportedInputMethods.length; j++) { + const inputMethod = supportedInputMethods[j]; + inputMethod.enabled = !!inputMethod.enabled; + inputMethod.isProhibitedByPolicy = !!inputMethod.isProhibitedByPolicy; + // Add the input method to the map of IDs. + this.supportedInputMethodMap_.set(inputMethod.id, inputMethod); + // Add the input method to the list of input methods for each language + // it supports. + for (let k = 0; k < inputMethod.languageCodes.length; k++) { + const languageCode = inputMethod.languageCodes[k]; + if (!this.supportedLanguageMap_.has(languageCode)) + continue; + if (!this.languageInputMethods_.has(languageCode)) + this.languageInputMethods_.set(languageCode, [inputMethod]); + else + this.languageInputMethods_.get(languageCode).push(inputMethod); + } + } + }, + + /** * Returns a list of LanguageStates for each enabled language in the supported * languages list. * @param {string} translateTarget Language code of the default translate @@ -589,6 +620,23 @@ Polymer({ }, /** @private */ + updateSupportedInputMethods_: function() { + assert(cr.isChromeOS); + const promise = new Promise(resolve => { + this.languageSettingsPrivate_.getInputMethodLists(function(lists) { + resolve( + lists.componentExtensionImes.concat(lists.thirdPartyExtensionImes)); + }); + }); + promise.then(result => { + const supportedInputMethods = result; + this.createInputMethodModel_(supportedInputMethods); + this.set('languages.inputMethods.supported', supportedInputMethods); + this.updateEnabledInputMethods_(); + }); + }, + + /** @private */ updateEnabledInputMethods_: function() { assert(cr.isChromeOS); const enabledInputMethods = this.getEnabledInputMethods_(); @@ -932,12 +980,12 @@ Polymer({ /** @param {string} id Added input method ID. */ onInputMethodAdded_: function(id) { - this.updateEnabledInputMethods_(); + this.updateSupportedInputMethods_(); }, /** @param {string} id Removed input method ID. */ onInputMethodRemoved_: function(id) { - this.updateEnabledInputMethods_(); + this.updateSupportedInputMethods_(); }, // </if> }); diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages_page.html b/chromium/chrome/browser/resources/settings/languages_page/languages_page.html index 75a6eb93028..a979b391e55 100644 --- a/chromium/chrome/browser/resources/settings/languages_page/languages_page.html +++ b/chromium/chrome/browser/resources/settings/languages_page/languages_page.html @@ -67,13 +67,13 @@ cr-checkbox.dropdown-item { --cr-action-menu-disabled-item-opacity: 0.38; - -webkit-margin-start: 0; + margin-inline-start: 0; } .icon-external { /* The negative margin messes up the outline border. These are in an indented list so this looks fine until moved: crbug.com/708286. */ - -webkit-margin-end: 0; + margin-inline-end: 0; } #uiLanguageItem:focus, @@ -102,7 +102,7 @@ iron-icon[icon='cr:error'] { @apply --cr-icon-height-width; --iron-icon-fill-color: var(--google-red-700); - -webkit-margin-end: 8px; + margin-inline-end: 8px; } .name-with-error-list[disabled] { @@ -110,7 +110,7 @@ } iron-icon.policy { - -webkit-margin-start: 10px; + margin-inline-start: 10px; } </style> <settings-languages languages="{{languages}}" prefs="{{prefs}}" diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/BUILD.gn b/chromium/chrome/browser/resources/settings/multidevice_page/BUILD.gn index ed7d9c960e0..46e84a108f6 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/multidevice_page/BUILD.gn @@ -8,15 +8,19 @@ js_type_check("closure_compile") { deps = [ ":multidevice_browser_proxy", ":multidevice_constants", + ":multidevice_feature_behavior", ":multidevice_feature_item", + ":multidevice_feature_toggle", ":multidevice_page", ":multidevice_page_container", ":multidevice_subpage", + ":multidevice_tether_item", ] } js_library("multidevice_browser_proxy") { deps = [ + ":multidevice_constants", "//ui/webui/resources/js:cr", ] } @@ -27,26 +31,45 @@ js_library("multidevice_constants") { ] } +js_library("multidevice_feature_behavior") { + deps = [ + ":multidevice_constants", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] +} + js_library("multidevice_feature_item") { deps = [ + ":multidevice_constants", + ":multidevice_feature_behavior", "..:route", "//ui/webui/resources/js:cr", ] } +js_library("multidevice_feature_toggle") { + deps = [ + ":multidevice_constants", + ":multidevice_feature_behavior", + ] +} + js_library("multidevice_page") { deps = [ ":multidevice_browser_proxy", ":multidevice_constants", + ":multidevice_feature_behavior", + "../controls:password_prompt_dialog", "//ui/webui/resources/js:cr", - "//ui/webui/resources/js:i18n_behavior", ] } js_library("multidevice_page_container") { deps = [ + ":multidevice_browser_proxy", ":multidevice_constants", - ":multidevice_page", + ":multidevice_feature_behavior", "//ui/webui/resources/js:cr", "//ui/webui/resources/js:web_ui_listener_behavior", ] @@ -54,7 +77,19 @@ js_library("multidevice_page_container") { js_library("multidevice_subpage") { deps = [ + ":multidevice_constants", + ":multidevice_feature_behavior", "..:route", - "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/cr_elements/chromeos/network:cr_network_listener_behavior", + ] + externs_list = [ "$externs_path/networking_private.js" ] + extra_sources = [ "$interfaces_path/networking_private_interface.js" ] +} + +js_library("multidevice_tether_item") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", ] + externs_list = [ "$externs_path/networking_private.js" ] + extra_sources = [ "$interfaces_path/networking_private_interface.js" ] } diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js index 594d6de3e80..bdc01201447 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js @@ -7,8 +7,29 @@ cr.define('settings', function() { class MultiDeviceBrowserProxy { showMultiDeviceSetupDialog() {} - /** @return Promise<!MultiDevicePageContentData> */ + /** @return {!Promise<!MultiDevicePageContentData>} */ getPageContentData() {} + + /** + * @param {!settings.MultiDeviceFeature} feature The feature whose state + * should be set. + * @param {boolean} enabled Whether the feature should be turned off or on. + * @param {string=} opt_authToken Proof that the user is authenticated. + * Needed to enable Smart Lock, and Better Together Suite if the Smart + * Lock user pref is enabled. + * @return {!Promise<boolean>} Whether the operation was successful. + */ + setFeatureEnabledState(feature, enabled, opt_authToken) {} + + removeHostDevice() {} + + retryPendingHostSetup() {} + + /** + * Called when the "Set Up" button is clicked to open the Android Messages + * PWA. + */ + setUpAndroidSms() {} } /** @@ -22,15 +43,28 @@ cr.define('settings', function() { /** @override */ getPageContentData() { - // TODO(jordynass): change method content to - // return cr.sendWithPromise('getPageContentData'); - // once handler is built. - return Promise.resolve({ - mode: settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED, - hostDevice: { - name: 'Pixel XL', - }, - }); + return cr.sendWithPromise('getPageContentData'); + } + + /** @override */ + setFeatureEnabledState(feature, enabled, opt_authToken) { + return cr.sendWithPromise( + 'setFeatureEnabledState', feature, enabled, opt_authToken); + } + + /** @override */ + removeHostDevice() { + chrome.send('removeHostDevice'); + } + + /** @override */ + retryPendingHostSetup() { + chrome.send('retryPendingHostSetup'); + } + + /** @override */ + setUpAndroidSms() { + chrome.send('setUpAndroidSms'); } } diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js index 585611f3ab4..b254dd07c2f 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_constants.js @@ -19,70 +19,62 @@ cr.define('settings', function() { }; /** - * MultiDevice software features. Note that this is copied from (and must - * include an analog of all values in) the enum of the same name in - * //components/cryptauth/proto/cryptauth_api.proto. + * Enum of MultiDevice features. Note that this is copied from (and must + * include an analog of all values in) the Feature enum in + * //chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom. * @enum {number} */ - MultiDeviceSoftwareFeature = { - UNKNOWN_FEATURE: 0, - BETTER_TOGETHER_HOST: 1, - BETTER_TOGETHER_CLIENT: 2, - EASY_UNLOCK_HOST: 3, - EASY_UNLOCK_CLIENT: 4, - MAGIC_TETHER_HOST: 5, - MAGIC_TETHER_CLIENT: 6, - SMS_CONNECT_HOST: 7, - SMS_CONNECT_CLIENT: 8, + MultiDeviceFeature = { + BETTER_TOGETHER_SUITE: 0, + INSTANT_TETHERING: 1, + MESSAGES: 2, + SMART_LOCK: 3, }; /** - * Possible states of MultiDevice software features. Note that this is based - * on (and must include an analog of all values in) the enum of the same name - * in //components/cryptauth/software_feature_state.h. + * Possible states of MultiDevice features. Note that this is copied from (and + * must include an analog of all values in) the FeatureState enum in + * //chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom. * @enum {number} */ - MultiDeviceSoftwareFeatureState = { - NOT_SUPPORTED: 0, - SUPPORTED: 1, - ENABLED: 2, + MultiDeviceFeatureState = { + PROHIBITED_BY_POLICY: 0, + DISABLED_BY_USER: 1, + ENABLED_BY_USER: 2, + NOT_SUPPORTED_BY_CHROMEBOOK: 3, + NOT_SUPPORTED_BY_PHONE: 4, + UNAVAILABLE_NO_VERIFIED_HOST: 5, + UNAVAILABLE_INSUFFICIENT_SECURITY: 6, + UNAVAILABLE_SUITE_DISABLED: 7, }; return { MultiDeviceSettingsMode: MultiDeviceSettingsMode, - MultiDeviceSoftwareFeature: MultiDeviceSoftwareFeature, - MultiDeviceSoftwareFeatureState: MultiDeviceSoftwareFeatureState, + MultiDeviceFeature: MultiDeviceFeature, + MultiDeviceFeatureState: MultiDeviceFeatureState, }; }); /** - * Represents a multidevice host, i.e. a phone set by the user to connect to - * their Chromebook(s). The type is a subset of the RemoteDevice structure - * defined by CryptAuth (components/cryptauth/remote_device.h). It contains the - * host device's name (e.g. Pixel, Nexus 5) and the map softwareFeatures - * sending each MultiDevice feature to the host device's state with regards to - * that feature. - * - * @typedef {{ - * name: string, - * softwareFeatures: - * !Object<settings.MultiDeviceSoftwareFeature, - * settings.MultiDeviceSoftwareFeatureState> - * }} - */ -let RemoteDevice; - -/** * Container for the initial data that the page requires in order to display * the correct content. It is also used for receiving status updates during - * use. Note that the host may be verified (enabled or disabled), awaiting - * verification, or it may have failed setup because it was not able to connect - * to the server. If the property is null or undefined, then no host has been - * set up, although there may be potential hosts on the account. + * use. Note that the host device may be verified (enabled or disabled), + * awaiting verification, or it may have failed setup because it was not able + * to connect to the server. + * + * For each MultiDevice feature (including the "suite" feature, which acts as a + * gatekeeper for the others), the corresponding *State property is an enum + * containing the data necessary to display it. Note that hostDeviceName should + * be undefined if and only if no host has been set up, regardless of whether + * there are potential hosts on the account. * * @typedef {{ * mode: !settings.MultiDeviceSettingsMode, - * hostDevice: (?RemoteDevice|undefined) + * hostDeviceName: (string|undefined), + * betterTogetherState: !settings.MultiDeviceFeatureState, + * instantTetheringState: !settings.MultiDeviceFeatureState, + * messagesState: !settings.MultiDeviceFeatureState, + * smartLockState: !settings.MultiDeviceFeatureState, * }} */ let MultiDevicePageContentData; diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_behavior.html b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_behavior.html new file mode 100644 index 00000000000..709413725c9 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_behavior.html @@ -0,0 +1,4 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="multidevice_constants.html"> +<script src="multidevice_feature_behavior.js"></script> diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_behavior.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_behavior.js new file mode 100644 index 00000000000..4179e9c94c7 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_behavior.js @@ -0,0 +1,181 @@ +// Copyright 2018 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. + +/** + * @fileoverview Polymer behavior for dealing with MultiDevice features. It is + * intended to facilitate passing data between elements in the MultiDevice page + * cleanly and concisely. It includes some constants and utility methods. + */ +cr.exportPath('settings'); + +/** @polymerBehavior */ +const MultiDeviceFeatureBehaviorImpl = { + properties: { + /** @type {MultiDevicePageContentData} */ + pageContentData: Object, + + /** + * Enum defined in multidevice_constants.js. + * @type {Object<string, number>} + */ + MultiDeviceFeature: { + type: Object, + value: settings.MultiDeviceFeature, + }, + }, + + /** + * Whether the gatekeeper pref for the whole Better Together feature suite is + * on. + * @return {boolean} + */ + isSuiteOn: function() { + return this.pageContentData.betterTogetherState === + settings.MultiDeviceFeatureState.ENABLED_BY_USER; + }, + + /** + * Whether the gatekeeper pref for the whole Better Together feature suite is + * allowed by policy. + * @return {boolean} + */ + isSuiteAllowedByPolicy: function() { + return this.pageContentData.betterTogetherState !== + settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY; + }, + + /** + * Whether an individual feature is allowed by policy. + * @param {!settings.MultiDeviceFeature} feature + * @return {boolean} + */ + isFeatureAllowedByPolicy: function(feature) { + return this.getFeatureState(feature) !== + settings.MultiDeviceFeatureState.PROHIBITED_BY_POLICY; + }, + + /** + * @param {!settings.MultiDeviceFeature} feature + * @return {boolean} + */ + isFeatureSupported: function(feature) { + return ![settings.MultiDeviceFeatureState.NOT_SUPPORTED_BY_CHROMEBOOK, + settings.MultiDeviceFeatureState.NOT_SUPPORTED_BY_PHONE, + ].includes(this.getFeatureState(feature)); + }, + + /** + * Whether the user is prevented from attempted to change a given feature. In + * the UI this corresponds to a disabled toggle. + * @param {!settings.MultiDeviceFeature} feature + * @return {boolean} + */ + isFeatureStateEditable: function(feature) { + // The suite is off and the toggle corresponds to an individual feature + // (as opposed to the full suite). + if (feature !== settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE && + !this.isSuiteOn()) { + return false; + } + + return [ + settings.MultiDeviceFeatureState.DISABLED_BY_USER, + settings.MultiDeviceFeatureState.ENABLED_BY_USER + ].includes(this.getFeatureState(feature)); + }, + + /** + * The localized string representing the name of the feature. + * @param {!settings.MultiDeviceFeature} feature + * @return {string} + */ + getFeatureName: function(feature) { + switch (feature) { + case settings.MultiDeviceFeature.INSTANT_TETHERING: + return this.i18n('multideviceInstantTetheringItemTitle'); + case settings.MultiDeviceFeature.MESSAGES: + return this.i18n('multideviceAndroidMessagesItemTitle'); + case settings.MultiDeviceFeature.SMART_LOCK: + return this.i18n('multideviceSmartLockItemTitle'); + default: + return ''; + } + }, + + /** + * The full icon name used provided by the containing iron-iconset-svg + * (i.e. [iron-iconset-svg name]:[SVG <g> tag id]) for a given feature. + * @param {!settings.MultiDeviceFeature} feature + * @return {string} + */ + getIconName: function(feature) { + switch (feature) { + case settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE: + return 'settings:multidevice-better-together-suite'; + case settings.MultiDeviceFeature.MESSAGES: + return 'settings:multidevice-messages'; + case settings.MultiDeviceFeature.SMART_LOCK: + return 'settings:multidevice-smart-lock'; + default: + return ''; + } + }, + + /** + * The localized string providing a description or useful status information + * concerning a given feature. + * @param {!settings.MultiDeviceFeature} feature + * @return {string} + */ + getFeatureSummaryHtml: function(feature) { + switch (feature) { + case settings.MultiDeviceFeature.SMART_LOCK: + return this.i18nAdvanced('multideviceSmartLockItemSummary'); + case settings.MultiDeviceFeature.MESSAGES: + return this.i18nAdvanced('multideviceAndroidMessagesItemSummary'); + default: + return ''; + } + }, + + /** + * Extracts the MultiDeviceFeatureState enum value describing the given + * feature from this.pageContentData. Returns null if the feature is not + * an accepted value (e.g. testing fake). + * @param {!settings.MultiDeviceFeature} feature + * @return {?settings.MultiDeviceFeatureState} + */ + getFeatureState: function(feature) { + switch (feature) { + case settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE: + return this.pageContentData.betterTogetherState; + case settings.MultiDeviceFeature.INSTANT_TETHERING: + return this.pageContentData.instantTetheringState; + case settings.MultiDeviceFeature.MESSAGES: + return this.pageContentData.messagesState; + case settings.MultiDeviceFeature.SMART_LOCK: + return this.pageContentData.smartLockState; + default: + return null; + } + }, + + /** + * Whether a host phone has been set by the user (not necessarily verified). + * @return {boolean} + */ + isHostSet: function() { + return [ + settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER, + settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION, + settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED, + ].includes(this.pageContentData.mode); + }, +}; + +/** @polymerBehavior */ +const MultiDeviceFeatureBehavior = [ + I18nBehavior, + MultiDeviceFeatureBehaviorImpl, +]; diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.html b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.html index e4a2b503647..2be742bad0e 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.html +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.html @@ -7,37 +7,64 @@ <link rel="import" href="../icons.html"> <link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="../settings_vars_css.html"> +<link rel="import" href="multidevice_constants.html"> +<link rel="import" href="multidevice_feature_behavior.html"> +<link rel="import" href="multidevice_feature_toggle.html"> <dom-module id="settings-multidevice-feature-item"> <template> <style include="settings-shared"> + #card { + border-top: var(--settings-separator-line); + border-top-style: var(--feature-item-border-top-style, solid); + padding: var(--feature-item-row-padding); + } + iron-icon { padding: 2px; } - #item-text-container { - -webkit-padding-start: 18px; + cr-policy-indicator { + padding: 0 var(--cr-controlled-by-spacing); } </style> - <div class="settings-box two-line first" + <div id="card" + class="settings-box two-line" on-click="handleItemClick_" - actionable$="[[hasSubpageClickHandler_(subpageRoute)]]"> - <iron-icon icon="[[iconName]]"></iron-icon> + actionable$="[[hasSubpageClickHandler_( + subpageRoute, pageContentData, feature)]]"> + <iron-icon icon="[[getIconName(feature)]]"></iron-icon> <div id="item-text-container" class="middle"> - [[featureName]] + [[getFeatureName(feature)]] <div class="secondary" id="featureSecondary" - inner-h-t-m-l="[[featureSummaryHtml]]"> + inner-h-t-m-l="[[getFeatureSummaryHtml(feature)]]"> </div> </div> - <template is="dom-if" if="[[hasSubpageClickHandler_(subpageRoute)]]"> + <template is="dom-if" + if="[[hasSubpageClickHandler_( + subpageRoute, pageContentData, feature)]]" + restamp> <paper-icon-button-light class="subpage-arrow"> - <button aria-label="[[featureName]]" - aria-describedby="featureSecondary"></button> + <button aria-label="[[getFeatureName(feature)]]" + aria-describedby="featureSecondary"></button> </paper-icon-button-light> <div class="separator"></div> </template> - <cr-toggle on-change="onChangeToggle_"></cr-toggle> + <template is="dom-if" + if="[[!isFeatureAllowedByPolicy(feature, pageContentData)]]" + restamp> + <cr-policy-indicator indicator-type="userPolicy"></cr-policy-indicator> + </template> + <slot name="feature-controller"> + <!-- This settings-multidevice-feature-toggle is the default controller. + If an element with slot="feature-controller" is attached, it will + replace this one. --> + <settings-multidevice-feature-toggle feature="[[feature]]" + page-content-data="[[pageContentData]]"> + </settings-multidevice-feature-toggle> + </slot> </div> </template> <script src="multidevice_feature_item.js"></script> diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.js index 6b71b219733..55783509adb 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.js +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_item.js @@ -15,30 +15,15 @@ cr.exportPath('settings'); Polymer({ is: 'settings-multidevice-feature-item', - properties: { - /** - * The localized string representing the name of the feature. - * @type {string} - */ - featureName: String, - - /** - * The localized string providing a description or useful status information - * concertning the feature. - * @type {string} - */ - featureSummaryHtml: String, + behaviors: [MultiDeviceFeatureBehavior], - /** - * The full icon name used provided by the containing iron-iconset-svg - * (i.e. [iron-iconset-svg name]:[SVG <g> tag id] - * @type {string} - */ - iconName: String, + properties: { + /** @type {!settings.MultiDeviceFeature} */ + feature: Number, /** - * If it is non-null, the item should be actionable and clicking on it - * should navigate there. If it is undefined, the item is simply not + * If it is truthy, the item should be actionable and clicking on it should + * navigate to the provided route. Otherwise, the item is simply not * actionable. * @type {settings.Route|undefined} */ @@ -50,18 +35,12 @@ Polymer({ * @private */ hasSubpageClickHandler_: function() { - return !!this.subpageRoute; - }, - - /** @private */ - onChangeToggle_: function() { - // TODO (jordynass): Trigger the correct workflow. - console.log('Toggle changed'); + return !!this.subpageRoute && this.isFeatureAllowedByPolicy(this.feature); }, /** @private */ handleItemClick_: function(event) { - if (!this.subpageRoute) + if (!this.hasSubpageClickHandler_()) return; // We do not navigate away if the click was on a link. diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.html b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.html new file mode 100644 index 00000000000..b52a7be4e64 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.html @@ -0,0 +1,17 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> +<link rel="import" href="multidevice_browser_proxy.html"> +<link rel="import" href="multidevice_constants.html"> +<link rel="import" href="multidevice_feature_behavior.html"> + +<dom-module id="settings-multidevice-feature-toggle"> + <template> + <cr-toggle id="toggle" + checked="{{checked_}}" + disabled="[[!isFeatureStateEditable(feature, pageContentData)]]" + on-change="onChange_"> + </cr-toggle> + </template> + <script src="multidevice_feature_toggle.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.js new file mode 100644 index 00000000000..83350e86cb8 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.js @@ -0,0 +1,76 @@ +// Copyright 2018 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. + +/** + * @fileoverview + * A toggle button specially suited for the MultiDevice Settings UI use-case. + * Instead of changing on clicks, it requests a pref change from the + * MultiDevice service and/or triggers a password check to grab an auth token + * for the user. It also receives real time updates on feature states and + * reflects them in the toggle status. + */ +Polymer({ + is: 'settings-multidevice-feature-toggle', + + behaviors: [MultiDeviceFeatureBehavior], + + properties: { + /** @type {!settings.MultiDeviceFeature} */ + feature: Number, + + /** @private {boolean} */ + checked_: Boolean, + }, + + listeners: { + 'click': 'onDisabledInnerToggleClick_', + }, + + // Note that, although this.feature does not change throughout the element's + // lifecycle, it must be listed as an observer dependency to ensure that + // this.feature is defined by the time of the observer's first call. + observers: ['resetChecked_(feature, pageContentData)'], + + /** + * Because MultiDevice prefs are only meant to be controlled via the + * MultiDevice mojo service, we need the cr-toggle to appear not to change + * when pressed. This method resets it before a change is visible to the + * user. + * @private + */ + resetChecked_: function() { + this.checked_ = this.getFeatureState(this.feature) === + settings.MultiDeviceFeatureState.ENABLED_BY_USER; + }, + + /** + * This handles the edge case in which the inner toggle (i.e., the cr-toggle) + * is disabled. For context, the cr-toggle element naturally stops clicks + * from propagating as long as its disabled attribute is false. However, if + * the cr-toggle's disabled attribute is set to true, its pointer-event CSS + * property is set to 'none' automatically. Thus, if the cr-toggle is clicked + * while it is disabled, the click event targets the parent element directly + * instead of propagating through the cr-toggle. This handler prevents such a + * click from unintentionally bubbling up the tree. + * @private + */ + onDisabledInnerToggleClick_: function(event) { + event.stopPropagation(); + }, + + /** + * Callback for clicking on the toggle. It attempts to toggle the feature's + * status if the user is allowed. + * @private + */ + onChange_: function() { + this.resetChecked_(); + + // Pass the negation of |this.checked_|: this indicates that if the toggle + // is checked, the intent is for it to be unchecked, and vice versa. + this.fire( + 'feature-toggle-clicked', + {feature: this.feature, enabled: !this.checked_}); + }, +}); diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.html b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.html index 1708c06a264..c9c8c666188 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.html +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.html @@ -1,66 +1,106 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> -<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> +<link rel="import" href="../icons.html"> <link rel="import" href="../route.html"> +<link rel="import" href="../controls/password_prompt_dialog.html"> <link rel="import" href="../settings_page/settings_animated_pages.html"> <link rel="import" href="../settings_page/settings_subpage.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="multidevice_browser_proxy.html"> <link rel="import" href="multidevice_constants.html"> +<link rel="import" href="multidevice_feature_behavior.html"> +<link rel="import" href="multidevice_feature_toggle.html"> <link rel="import" href="multidevice_subpage.html"> <dom-module id="settings-multidevice-page"> <template> - <style include="settings-shared"></style> + <style include="settings-shared"> + cr-policy-indicator { + padding: 0 var(--cr-controlled-by-spacing); + } + </style> <settings-animated-pages id="pages" section="multidevice" focus-config="[[focusConfig_]]"> <neon-animatable route-path="default"> <div id="multidevice-item" - class="settings-box two-line first" + class="settings-box two-line" on-click="handleItemClick_" actionable$="[[doesClickOpenSubpage_(pageContentData)]]"> - <div class="start"> + <template is="dom-if" if="[[isHostSet(pageContentData)]]" restamp> + <iron-icon icon= + "[[getIconName(MultiDeviceFeature.BETTER_TOGETHER_SUITE)]]"> + </iron-icon> + </template> + <div class$= + "[[getMultiDeviceItemLabelBlockCssClass_(pageContentData)]]"> <div id="multidevice-label">[[getLabelText_(pageContentData)]]</div> - <div class="secondary" id="mutltideviceSubLabel" inner-h-t-m-l= - "[[getSubLabelInnerHtml_(pageContentData, hostEnabled_)]]"> + <div id="mutltideviceSubLabel" class="secondary" + inner-h-t-m-l="[[getSubLabelInnerHtml_(pageContentData)]]"> </div> </div> <template is="dom-if" - if="[[doesClickOpenSubpage_(pageContentData)]]"> - <paper-icon-button-light class="subpage-arrow"> - <button aria-label="[[getLabelText_(pageContentData)]]" - aria-describedby="mutltideviceSubLabel"></button> - </paper-icon-button-light> + if="[[!isSuiteAllowedByPolicy(pageContentData)]]" + restamp> + <cr-policy-indicator indicator-type="userPolicy"> + </cr-policy-indicator> + <settings-multidevice-feature-toggle + feature="[[MultiDeviceFeature.BETTER_TOGETHER_SUITE]]" + page-content-data="[[pageContentData]]"> + </settings-multidevice-feature-toggle> + </template> + <template is="dom-if" + if="[[shouldShowSeparatorAndSubpageArrow_(pageContentData)]]" + restamp> + <template is="dom-if" + if="[[doesClickOpenSubpage_(pageContentData)]]" + restamp> + <paper-icon-button-light class="subpage-arrow"> + <button aria-label="[[getLabelText_(pageContentData)]]" + aria-describedby="mutltideviceSubLabel"></button> + </paper-icon-button-light> + </template> + <div class="separator"></div> </template> - <div class="separator"></div> - <template is="dom-if" if="[[showButton_(pageContentData)]]" restamp> + <template is="dom-if" + if="[[shouldShowButton_(pageContentData)]]" + restamp> <paper-button class="secondary-button" + disabled$="[[shouldDisableButton_(pageContentData)]]" on-click="handleButtonClick_"> [[getButtonText_(pageContentData)]] </paper-button> </template> - <template is="dom-if" if="[[showToggle_(pageContentData)]]" restamp> - <cr-toggle checked="{{hostEnabled_}}"></cr-toggle> + <template is="dom-if" + if="[[shouldShowToggle_(pageContentData)]]" + restamp> + <settings-multidevice-feature-toggle + feature="[[MultiDeviceFeature.BETTER_TOGETHER_SUITE]]" + page-content-data="[[pageContentData]]"> + </settings-multidevice-feature-toggle> </template> </div> </neon-animatable> - <template is="dom-if" route-path="/multidevice/features"> + <template is="dom-if" route-path="/multidevice/features" restamp> <settings-subpage associated-control="[[$$('#multidevice-item')]]" - page-title="[[pageContentData.hostDevice.name]]"> - <settings-multidevice-subpage prefs="{{prefs}}" - page-content-data="[[pageContentData]]" - host-enabled="{{hostEnabled_}}"> + page-title="[[pageContentData.hostDeviceName]]"> + <settings-multidevice-subpage + page-content-data="[[pageContentData]]"> </settings-multidevice-subpage> </settings-subpage> </template> </settings-animated-pages> + <template is="dom-if" if="[[showPasswordPromptDialog_]]" restamp> + <settings-password-prompt-dialog id="passwordPrompt"> + </settings-password-prompt-dialog> + </template> </template> <script src="multidevice_page.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.js index 9172aa548a2..f6dec83fc24 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.js +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page.js @@ -11,30 +11,9 @@ cr.exportPath('settings'); Polymer({ is: 'settings-multidevice-page', - behaviors: [I18nBehavior], + behaviors: [MultiDeviceFeatureBehavior], properties: { - /** SettingsPrefsElement 'prefs' Object reference. See prefs.js. */ - prefs: { - type: Object, - notify: true, - }, - - /** @type {MultiDevicePageContentData} */ - pageContentData: Object, - - // TODO(jordynass): Set this variable once the information is retrieved from - // prefs. - /** - * True if the multidevice setup is complete and the paired phone has been - * verified; otherwise, false. - * @private {boolean} - */ - hostEnabled_: { - type: Boolean, - value: true, - }, - /** * A Map specifying which element should be focused when exiting a subpage. * The key of the map holds a settings.Route path, and the value holds a @@ -52,6 +31,42 @@ Polymer({ return map; }, }, + + /** + * Authentication token provided by password-prompt-dialog. + * @private {string} + */ + authToken_: { + type: String, + value: '', + observer: 'authTokenChanged_', + }, + + /** + * Feature which the user has requested to be enabled but could not be + * enabled immediately because authentication (i.e., entering a password) is + * required. This value is initialized to null, is set when the password + * dialog is opened, and is reset to null again once the password dialog is + * closed. + * @private {?settings.MultiDeviceFeature} + */ + featureToBeEnabledOnceAuthenticated_: { + type: Number, + value: null, + }, + + /** @private {boolean} */ + showPasswordPromptDialog_: { + type: Boolean, + value: false, + }, + }, + + listeners: { + 'auth-token-changed': 'onAuthTokenChanged_', + 'close': 'onPasswordPromptDialogClose_', + 'feature-toggle-clicked': 'onFeatureToggleClicked_', + 'forget-device-requested': 'onForgetDeviceRequested_', }, /** @private {?settings.MultiDeviceBrowserProxy} */ @@ -63,12 +78,22 @@ Polymer({ }, /** + * CSS class for the <div> containing all the text in the multidevice-item + * <div>, i.e. the label and sublabel. If the host is set, the Better Together + * icon appears so before the text (i.e. text div is 'middle' class). + * @return {string} + * @private + */ + getMultiDeviceItemLabelBlockCssClass_: function() { + return this.isHostSet() ? 'middle' : 'start'; + }, + + /** * @return {string} Translated item label. * @private */ getLabelText_: function() { - return !!this.pageContentData.hostDevice ? - this.pageContentData.hostDevice.name : + return this.pageContentData.hostDeviceName || this.i18n('multideviceSetupItemHeading'); }, @@ -77,7 +102,11 @@ Polymer({ * @private */ getSubLabelInnerHtml_: function() { + if (!this.isSuiteAllowedByPolicy()) + return this.i18nAdvanced('multideviceSetupSummary'); switch (this.pageContentData.mode) { + case settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS: + return this.i18nAdvanced('multideviceNoHostText'); case settings.MultiDeviceSettingsMode.NO_HOST_SET: return this.i18nAdvanced('multideviceSetupSummary'); case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER: @@ -85,8 +114,8 @@ Polymer({ case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION: return this.i18nAdvanced('multideviceVerificationText'); default: - return this.hostEnabled_ ? this.i18n('multideviceEnabled') : - this.i18n('multideviceDisabled'); + return this.isSuiteOn() ? this.i18n('multideviceEnabled') : + this.i18n('multideviceDisabled'); } }, @@ -99,7 +128,7 @@ Polymer({ case settings.MultiDeviceSettingsMode.NO_HOST_SET: return this.i18n('multideviceSetupButton'); case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER: - return this.i18n('retry'); + return this.i18n('multideviceVerifyButton'); case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION: return this.i18n('multideviceVerifyButton'); default: @@ -111,36 +140,56 @@ Polymer({ * @return {boolean} * @private */ - showButton_: function() { - return this.pageContentData.mode != - settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED; + shouldShowButton_: function() { + return [ + settings.MultiDeviceSettingsMode.NO_HOST_SET, + settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER, + settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION, + ].includes(this.pageContentData.mode); }, /** * @return {boolean} * @private */ - showToggle_: function() { - return this.pageContentData.mode == + shouldDisableButton_: function() { + return this.pageContentData.mode === + settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER; + }, + + /** + * @return {boolean} + * @private + */ + shouldShowToggle_: function() { + return this.pageContentData.mode === settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED; }, /** + * Whether to show the separator bar and, if the state calls for a chevron + * (a.k.a. subpage arrow) routing to the subpage, the chevron. + * @return {boolean} + * @private + */ + shouldShowSeparatorAndSubpageArrow_: function() { + return this.pageContentData.mode !== + settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS; + }, + + /** * @return {boolean} * @private */ doesClickOpenSubpage_: function() { - return [ - settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER, - settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION, - settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED, - ].includes(this.pageContentData.mode); + return this.isHostSet(); }, /** @private */ handleItemClick_: function() { - if (!this.doesClickOpenSubpage_()) + if (!this.isHostSet()) return; + settings.navigateTo(settings.routes.MULTIDEVICE_FEATURES); }, @@ -152,13 +201,114 @@ Polymer({ this.browserProxy_.showMultiDeviceSetupDialog(); return; case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER: - // TODO(jordynass): Implement this when API is ready. - console.log('Trying to connect to server again.'); - return; + // Intentional fall-through. case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION: - // TODO(jordynass): Implement this when API is ready. - console.log('Trying to verify multidevice connection.'); - return; + // If this device is waiting for action on the server or the host + // device, clicking the button should trigger this action. + this.browserProxy_.retryPendingHostSetup(); + } + }, + + /** @private */ + openPasswordPromptDialog_: function() { + this.showPasswordPromptDialog_ = true; + }, + + /** @private */ + onPasswordPromptDialogClose_: function() { + // The password prompt should only be shown when there is a feature waiting + // to be enabled. + assert(this.featureToBeEnabledOnceAuthenticated_ !== null); + + // If |this.authToken_| is set when the dialog has been closed, this means + // that the user entered the correct password into the dialog. Thus, send + // all pending features to be enabled. + if (this.authToken_) { + this.browserProxy_.setFeatureEnabledState( + this.featureToBeEnabledOnceAuthenticated_, true /* enabled */, + this.authToken_); + + // Reset |this.authToken_| now that it has been used. This ensures that + // users cannot keep an old auth token and reuse it on an subsequent + // request. + this.authToken_ = ''; + } + + // Either the feature was enabled above or the user canceled the request by + // clicking "Cancel" on the password dialog. Thus, there is no longer a need + // to track any pending feature. + this.featureToBeEnabledOnceAuthenticated_ = null; + + // Remove the password prompt dialog from the DOM. + this.showPasswordPromptDialog_ = false; + }, + + /** + * @param {!{detail: !Object}} event + * @private + */ + onAuthTokenChanged_: function(event) { + this.authToken_ = event.detail.value; + }, + + /** + * Attempt to enable the provided feature. If not authenticated (i.e., + * |authToken_| is invalid), display the password prompt to begin the + * authentication process. + * + * @param {!{detail: !Object}} event + * @private + */ + onFeatureToggleClicked_: function(event) { + let feature = event.detail.feature; + let enabled = event.detail.enabled; + + // Disabling any feature does not require authentication, and enable some + // features does not require authentication. + if (!enabled || !this.isAuthenticationRequiredToEnable_(feature)) { + this.browserProxy_.setFeatureEnabledState(feature, enabled); + return; } + + // If the feature required authentication to be enabled, open the password + // prompt dialog. This is required every time the user enables a security- + // sensitive feature (i.e., use of stale auth tokens is not acceptable). + this.featureToBeEnabledOnceAuthenticated_ = feature; + this.openPasswordPromptDialog_(); + }, + + /** + * @param {!settings.MultiDeviceFeature} feature The feature to enable. + * @return {boolean} Whether authentication is required to enable the feature. + * @private + */ + isAuthenticationRequiredToEnable_: function(feature) { + // Enabling SmartLock always requires authentication. + if (feature == settings.MultiDeviceFeature.SMART_LOCK) + return true; + + // Enabling any feature besides SmartLock and the Better Together suite does + // not require authentication. + if (feature != settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE) + return false; + + const smartLockState = + this.getFeatureState(settings.MultiDeviceFeature.SMART_LOCK); + + // If the user is enabling the Better Together suite and this change would + // result in SmartLock being implicitly enabled, authentication is required. + // SmartLock is implicitly enabled if it is only currently not enabled due + // to the suite being disabled or due to the SmartLock host device not + // having a lock screen set. + return smartLockState == + settings.MultiDeviceFeatureState.UNAVAILABLE_SUITE_DISABLED || + smartLockState == + settings.MultiDeviceFeatureState.UNAVAILABLE_INSUFFICIENT_SECURITY; + }, + + /** @private */ + onForgetDeviceRequested_: function() { + this.browserProxy_.removeHostDevice(); + settings.navigateTo(settings.routes.MULTIDEVICE); }, }); diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.html b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.html index 98b70696a94..c804083150d 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.html +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.html @@ -2,15 +2,17 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> -<link rel="import" href="multidevice_constants.html"> <link rel="import" href="multidevice_browser_proxy.html"> +<link rel="import" href="multidevice_constants.html"> +<link rel="import" href="multidevice_feature_behavior.html"> <link rel="import" href="multidevice_page.html"> <dom-module id="settings-multidevice-page-container"> <template> - <template is="dom-if" if="[[doesPotentialConnectedPhoneExist]]" restamp> - <settings-multidevice-page prefs="[[prefs]]" - page-content-data="[[pageContentData_]]"> + <template is="dom-if" + if="[[doesChromebookSupportMultiDeviceFeatures]]" + restamp> + <settings-multidevice-page page-content-data="[[pageContentData]]"> </settings-multidevice-page> </template> </template> diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js index 87143142770..dd74a9271a4 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_page_container.js @@ -18,28 +18,19 @@ cr.exportPath('settings'); Polymer({ is: 'settings-multidevice-page-container', - behaviors: [WebUIListenerBehavior], + behaviors: [MultiDeviceFeatureBehavior, WebUIListenerBehavior], properties: { - /** SettingsPrefsElement 'prefs' Object reference. See prefs.js. */ - prefs: { - type: Object, - notify: true, - }, - /** - * Whether a phone was found on the account that is either connected to the - * Chromebook or has the potential to be. + * Whether the Chromebook is capable of enabling Better Together features. * @type {boolean} */ - doesPotentialConnectedPhoneExist: { + doesChromebookSupportMultiDeviceFeatures: { type: Boolean, - computed: 'computeDoesPotentialConnectedPhoneExist(pageContentData_)', + computed: + 'computeDoesChromebookSupportMultiDeviceFeatures(pageContentData)', notify: true, }, - - /** @private {MultiDevicePageContentData} */ - pageContentData_: Object, }, /** @private {?settings.MultiDeviceBrowserProxy} */ @@ -66,13 +57,13 @@ Polymer({ console.error('Invalid status change'); return; } - this.pageContentData_ = newData; + this.pageContentData = newData; }, /** * If the new mode corresponds to no eligible host or unset potential hosts - * (i.e. NO_ELIGIBLE_HOSTS or NO_HOST_SET), then newHostDevice should be null - * or undefined. Otherwise it should be defined and non-null. + * (i.e. NO_ELIGIBLE_HOSTS or NO_HOST_SET), then newHostDeviceName should be + * falsy. Otherwise it should be truthy. * @param {!MultiDevicePageContentData} newData * @private */ @@ -81,16 +72,16 @@ Polymer({ settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS, settings.MultiDeviceSettingsMode.NO_HOST_SET, ]; - return !newData.hostDevice == noHostModes.includes(newData.mode); + return !newData.hostDeviceName === noHostModes.includes(newData.mode); }, /** * @return {boolean} * @private */ - computeDoesPotentialConnectedPhoneExist: function() { - return !!this.pageContentData_ && - this.pageContentData_.mode != - settings.MultiDeviceSettingsMode.NO_ELIGIBLE_HOSTS; + computeDoesChromebookSupportMultiDeviceFeatures: function() { + return !!this.pageContentData && + this.isFeatureSupported( + settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE); }, }); diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html index 3f78863025d..946ab4a626d 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.html @@ -1,44 +1,136 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_listener_behavior.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="../settings_vars_css.html"> +<link rel="import" href="multidevice_constants.html"> +<link rel="import" href="multidevice_feature_behavior.html"> <link rel="import" href="multidevice_feature_item.html"> +<link rel="import" href="multidevice_feature_toggle.html"> +<link rel="import" href="multidevice_tether_item.html"> <dom-module id="settings-multidevice-subpage"> <template> <style include="settings-shared iron-flex"> + settings-multidevice-feature-item, + settings-multidevice-tether-item { + --feature-item-row-padding: 0; + } + + settings-multidevice-feature-item:first-of-type { + --feature-item-border-top-style: none; + } + #status-text-container[enabled] { color: var(--google-green-500); } - .feature-item-container { - -webkit-margin-end: var(--settings-box-row-padding); - -webkit-margin-start: var(--settings-box-row-indent); + #feature-items-container, + #forget-device-container { + @apply(--settings-list-frame-padding); + } + + #forget-device-container { + border-top: var(--settings-separator-line); + } + + #forget-device { + padding: 0; } </style> <div class="settings-box first"> - <div id="status-text-container" class="start" enabled$="[[hostEnabled]]"> - [[getStatusText_(hostEnabled)]] + <div id="status-text-container" + class="start" + enabled$="[[isSuiteOn(pageContentData)]]"> + [[getStatusText_(pageContentData)]] </div> - <cr-toggle id="enable-multidevice" checked="{{hostEnabled}}"> - </cr-toggle> + <settings-multidevice-feature-toggle + feature="[[MultiDeviceFeature.BETTER_TOGETHER_SUITE]]" + page-content-data="[[pageContentData]]"> + </settings-multidevice-feature-toggle> </div> <template is="dom-if" if="[[shouldShowIndividualFeatures_(pageContentData)]]" restamp> - <div class="feature-item-container"> - <settings-multidevice-feature-item icon-name="settings:smart-lock" - subpage-route="[[routes.LOCK_SCREEN]]" - feature-name="$i18n{multideviceSmartLockItemTitle}" - feature-summary-html="$i18n{multideviceSmartLockItemSummary}"> - </settings-multidevice-feature-item> + <div id="feature-items-container"> + <template is="dom-if" + if="[[isFeatureSupported( + MultiDeviceFeature.SMART_LOCK, pageContentData)]]" + restamp> + <settings-multidevice-feature-item id="smartLockItem" + feature="[[MultiDeviceFeature.SMART_LOCK]]" + page-content-data="[[pageContentData]]" + subpage-route="[[routes.LOCK_SCREEN]]"> + </settings-multidevice-feature-item> + </template> + <template is="dom-if" + if="[[isFeatureSupported( + MultiDeviceFeature.INSTANT_TETHERING, pageContentData)]]" + restamp> + <settings-multidevice-tether-item id="instantTetheringItem"> + </settings-multidevice-tether-item> + </template> + <template is="dom-if" + if="[[isFeatureSupported( + MultiDeviceFeature.MESSAGES, pageContentData)]]" + restamp> + <settings-multidevice-feature-item id="messagesItem" + feature="[[MultiDeviceFeature.MESSAGES]]" + page-content-data="[[pageContentData]]"> + <template is="dom-if" if="[[androidMessagesRequiresSetup_]]" + restamp> + <paper-button on-click="handleAndroidMessagesButtonClick_" + slot="feature-controller"> + $i18n{multideviceSetupButton} + </paper-button> + </template> + </settings-multidevice-feature-item> + </template> </div> </template> + <div id="forget-device-container"> + <div id="forget-device" + class="settings-box two-line first" + on-click="handleForgetDeviceClick_" + actionable> + <div class="start"> + $i18n{multideviceForgetDevice} + <div class="secondary" id="multideviceForgetDeviceSummary"> + $i18n{multideviceForgetDeviceSummary} + </div> + </div> + <paper-icon-button-light class="subpage-arrow"> + <button aria-label="$i18n{multideviceForgetDevice}" + aria-describedby="multideviceForgetDeviceSummary"></button> + </paper-icon-button-light> + </div> + </div> + <cr-dialog id="forgetDeviceDialog" show-close-button="false"> + <div slot="title">$i18n{multideviceForgetDeviceDialogHeading}</div> + <div slot="body"> + <div class="settings-box first"> + $i18n{multideviceForgetDeviceDialogMessage} + </div> + </div> + <div slot="button-container"> + <paper-button class="cancel-button" + on-click="onForgetDeviceDialogCancelClick_"> + $i18n{cancel} + </paper-button> + + <paper-button id="confirmButton" + class="action-button" + on-click="onForgetDeviceDialogConfirmClick_"> + $i18n{confirm} + </paper-button> + </div> + </cr-dialog> </template> <script src="multidevice_subpage.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js index 202a2b58d64..a8da00c237d 100644 --- a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_subpage.js @@ -12,34 +12,71 @@ cr.exportPath('settings'); Polymer({ is: 'settings-multidevice-subpage', - behaviors: [I18nBehavior], + behaviors: [ + MultiDeviceFeatureBehavior, + CrNetworkListenerBehavior, + ], properties: { - /** SettingsPrefsElement 'prefs' Object reference. See prefs.js. */ - prefs: { + /** @type {?SettingsRoutes} */ + routes: { type: Object, - notify: true, + value: settings.routes, + }, + + /** Overridden from NetworkListenerBehavior. */ + networkingPrivate: { + type: Object, + value: chrome.networkingPrivate, + }, + + /** Overridden from NetworkListenerBehavior. */ + networkListChangeSubscriberSelectors_: { + type: Array, + value: () => ['settings-multidevice-tether-item'], + }, + + /** Overridden from NetworkListenerBehavior. */ + networksChangeSubscriberSelectors_: { + type: Array, + value: () => ['settings-multidevice-tether-item'], }, - // TODO(jordynass): Set this based on data in this.prefs. + // TODO(jordynass): Once the service provides this data via pageContentData, + // replace this property with that path. /** - * If a host has been verified, this is true if that host is and enabled and - * false if it is disabled. Otherwise it is undefined. - * @type {boolean|undefined} + * If SMS Connect requires setup, it displays a paper button prompting the + * setup flow. If it is already set up, it displays a regular toggle for the + * feature. + * @private {boolean} */ - hostEnabled: { + androidMessagesRequiresSetup_: { type: Boolean, - notify: true, + value: true, }, + }, - /** @type {?SettingsRoutes} */ - routes: { - type: Object, - value: settings.routes, - }, + /** @private {?settings.MultiDeviceBrowserProxy} */ + browserProxy_: null, + + /** @override */ + created: function() { + this.browserProxy_ = settings.MultiDeviceBrowserProxyImpl.getInstance(); + }, + + /** @private */ + handleAndroidMessagesButtonClick_: function() { + this.browserProxy_.setUpAndroidSms(); + }, + + listeners: { + 'show-networks': 'onShowNetworks_', + }, - /** @type {MultiDevicePageContentData} */ - pageContentData: Object, + onShowNetworks_: function() { + settings.navigateTo( + settings.routes.INTERNET_NETWORKS, + new URLSearchParams('type=' + CrOnc.Type.TETHER)); }, /** @@ -51,12 +88,28 @@ Polymer({ settings.MultiDeviceSettingsMode.HOST_SET_VERIFIED; }, + /** @private */ + handleForgetDeviceClick_: function() { + this.$.forgetDeviceDialog.showModal(); + }, + + /** @private */ + onForgetDeviceDialogCancelClick_: function() { + this.$.forgetDeviceDialog.close(); + }, + + /** @private */ + onForgetDeviceDialogConfirmClick_: function() { + this.fire('forget-device-requested'); + this.$.forgetDeviceDialog.close(); + }, + /** * @return {string} * @private */ getStatusText_: function() { - return this.hostEnabled ? this.i18n('multideviceEnabled') : + return this.isSuiteOn() ? this.i18n('multideviceEnabled') : this.i18n('multideviceDisabled'); }, }); diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html new file mode 100644 index 00000000000..e831086d43e --- /dev/null +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html @@ -0,0 +1,34 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> +<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html"> +<link rel="import" href="../i18n_setup.html"> +<link rel="import" href="../internet_page/network_summary_item.html"> +<link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="../settings_vars_css.html"> + +<dom-module id="settings-multidevice-tether-item"> + <style include="settings-shared"> + network-summary-item { + --network-summary-item-outer-box: { + padding: var(--feature-item-row-padding); + }; + + --network-summary-item-title: { + color: var(--cr-primary-text-color); + font-weight: 400; + }; + } + </style> + <template> + <network-summary-item id="networkSummaryItem" + active-network-state="[[activeNetworkState_]]" + device-state="[[deviceState_]]" + network-state-list="[[getNetworkStateList_(activeNetworkState_)]]" + networking-private="[[networkingPrivate]]" + tether-device-state="[[deviceState_]]" + network-title-text="$i18n{multideviceInstantTetheringItemTitle}"> + </network-summary-item> + </template> + <script src="multidevice_tether_item.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js new file mode 100644 index 00000000000..468d045f1b6 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js @@ -0,0 +1,163 @@ +// Copyright 2018 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. + +/** + * @fileoverview + * This element provides a layer between the settings-multidevice-subpage + * element and the internet_page folder's network-summary-item. It is + * responsible for loading initial tethering network data from the + * chrome.networkingPrivate API as well as updating the data in real time. It + * serves a role comparable to the internet_page's network-summary element. + */ + +Polymer({ + is: 'settings-multidevice-tether-item', + + properties: { + /** + * Interface for networkingPrivate calls. + * @private {!NetworkingPrivate} + */ + networkingPrivate_: { + type: Object, + value: chrome.networkingPrivate, + }, + + /** + * The device state for tethering. + * @private {?CrOnc.DeviceStateProperties|undefined} + */ + deviceState_: Object, + + /** + * The network state for a potential tethering host phone. Note that there + * is at most one because only one MultiDevice host phone is allowed on an + * account at a given time. + * @private {?CrOnc.NetworkStateProperties|undefined} + */ + activeNetworkState_: Object, + }, + + listeners: { + 'device-enabled-toggled': 'onDeviceEnabledToggled_', + 'network-list-changed': 'updateTetherNetworkState_', + // network-changed is fired by the settings-multidevice-subpage element's + // CrNetworkListenerBehavior. + // TODO (jordynass): Refactor to allow this element to listen to network + // changes without requiring the settings-multidevice-subpage to communicate + // with the networkingPrivate API. + 'networks-changed': 'onNetworksChanged_', + }, + + /** + * Listener function for chrome.networkingPrivate.onDeviceStateListChanged + * event. + * @private {?function(!Array<string>)} + */ + deviceStateListChangedListener_: null, + + /** @override */ + attached: function() { + this.updateTetherDeviceState_(); + this.updateTetherNetworkState_(); + + this.deviceStateListChangedListener_ = + this.deviceStateListChangedListener_ || + this.updateTetherDeviceState_.bind(this); + this.networkingPrivate_.onDeviceStateListChanged.addListener( + this.deviceStateListChangedListener_); + }, + + /** @override */ + detached: function() { + this.networkingPrivate_.onDeviceStateListChanged.removeListener( + assert(this.deviceStateListChangedListener_)); + }, + + /** + * Callback for the a network changing state. Note that any change to leading + * to a new active network would fire the 'network-list-changed' event, + * triggering updateTetherNetworkState_ and rendering this callback + * redundant. As a result, we return early if the active network is not + * changed. + * @param {{detail: Array<string>}} event stores an array of the GUIDs of all + * networks that changed in its detail property. + * @private + */ + onNetworksChanged_: function(event) { + const id = this.activeNetworkState_.GUID; + if (!event.detail.includes(id)) + return; + this.networkingPrivate_.getState(id, newNetworkState => { + if (chrome.runtime.lastError) { + const message = chrome.runtime.lastError.message; + if (message != 'Error.NetworkUnavailable' && + message != 'Error.InvalidNetworkGuid') { + console.error( + 'Unexpected networkingPrivate.getState error: ' + message + + ' For: ' + id); + return; + } + } + this.activeNetworkState_ = newNetworkState; + }); + }, + + /** + * Event triggered by a device state enabled toggle. + * @param {!{detail: {enabled: boolean, type: CrOnc.Type}}} event + * @private + */ + onDeviceEnabledToggled_: function(event) { + if (event.detail.enabled) + this.networkingPrivate_.enableNetworkType(CrOnc.Type.TETHER); + else + this.networkingPrivate_.disableNetworkType(CrOnc.Type.TETHER); + }, + + /** + * Retrieves device states (CrOnc.DeviceStateProperties) and sets + * this.deviceState_ to the retrieved Instant Tethering state (or undefined if + * there is none) in its callback. Note that the function + * chrome.networkingPrivate.getDevicePolicy() retrieves at most one object per + * network type (CrOnc.Type) so, in particular there will be at most one state + * for Instant Tethering. + * @private + */ + updateTetherDeviceState_: function() { + this.networkingPrivate_.getDeviceStates(deviceStates => { + this.deviceState_ = + deviceStates.find( + deviceState => deviceState.Type == CrOnc.Type.TETHER) || + {Type: CrOnc.Type.TETHER, State: CrOnc.DeviceState.DISABLED}; + }); + }, + + /** + * Retrieves all Instant Tethering network states + * (CrOnc.NetworkStateProperties). Note that there is at most one because + * only one host is allowed on an account at a given time. Then it sets + * this.activeNetworkState_ to that network if there is one or a dummy object + * with an empty string for a GUID otherwise. + * @private + */ + updateTetherNetworkState_: function() { + this.networkingPrivate_.getNetworks( + {networkType: CrOnc.Type.TETHER}, networkStates => { + this.activeNetworkState_ = + networkStates[0] || {GUID: '', Type: CrOnc.Type.TETHER}; + }); + }, + + /** + * Returns an array containing the active network state if there is one + * (note that if there is not GUID will be falsy). Returns an empty array + * otherwise. + * @return {!Array<CrOnc.NetworkStateProperties>} + * @private + */ + getNetworkStateList_: function() { + return this.activeNetworkState_.GUID ? [this.activeNetworkState_] : []; + }, +}); diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/BUILD.gn b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/BUILD.gn index 4f4ba6a686b..a8f8b60e2b6 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/BUILD.gn @@ -14,6 +14,7 @@ js_type_check("closure_compile") { ":password_manager_proxy", ":passwords_and_forms_page", ":passwords_section", + ":payments_section", ":show_password_behavior", ] } @@ -22,6 +23,7 @@ js_library("passwords_and_forms_page") { deps = [ ":autofill_section", ":passwords_section", + ":payments_section", "..:route", "../prefs:prefs_behavior", "../settings_page:settings_animated_pages", @@ -37,10 +39,22 @@ js_library("passwords_and_forms_page") { js_library("autofill_section") { deps = [ ":address_edit_dialog", + "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:load_time_data", + "//ui/webui/resources/js/cr/ui:focus_without_ink", + ] + externs_list = [ "$externs_path/autofill_private.js" ] +} + +js_library("payments_section") { + deps = [ ":credit_card_edit_dialog", + "../people_page:sync_browser_proxy", "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu", "//ui/webui/resources/js:assert", - "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js:load_time_data", + "//ui/webui/resources/js:web_ui_listener_behavior", "//ui/webui/resources/js/cr/ui:focus_without_ink", ] externs_list = [ "$externs_path/autofill_private.js" ] diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html index 45beb855788..5ceba365497 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html @@ -18,7 +18,7 @@ } .address-column { - -webkit-margin-end: 16px; + margin-inline-end: 16px; width: calc((var(--settings-input-max-width) - 16px) / 2); } @@ -77,12 +77,13 @@ <template is="dom-if" if="[[item.isTextArea]]"> <settings-textarea label="[[item.component.fieldName]]" value="{{item.value}}" on-value-changed="updateCanSave_" - class$="address-column [[long_(item)]]" autofocus> + class$="address-column [[long_(item)]]" autofocus + spellcheck="false"> </settings-textarea> </template> <template is="dom-if" if="[[!item.isTextArea]]"> <cr-input type="text" label="[[item.component.fieldName]]" - autofocus value="{{item.value}}" + autofocus value="{{item.value}}" spellcheck="false" on-value-changed="updateCanSave_" class$="address-column [[long_(item)]]"> </cr-input> @@ -106,11 +107,11 @@ <div class="address-row"> <cr-input id="phoneInput" type="text" label="$i18n{addressPhone}" class="address-column last-row" on-value-changed="updateCanSave_" - value="{{phoneNumber_}}"> + value="{{phoneNumber_}}" spellcheck="false"> </cr-input> <cr-input id="emailInput" type="text" label="$i18n{addressEmail}" on-value-changed="updateCanSave_" value="{{email_}}" - class="address-column long last-row"> + class="address-column long last-row" spellcheck="false"> </cr-input> </div> </div> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.js index 1ec36ce4685..a184152c8af 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.js @@ -92,9 +92,23 @@ Polymer({ // Default to the last country used if no country code is provided. const countryCode = this.countryCode_ || this.countries_[0].countryCode; this.countryInfo.getAddressFormat(countryCode).then(format => { - this.addressWrapper_ = format.components.map( - component => component.row.map( - c => new settings.address.AddressComponentUI(this.address, c))); + let filteredComponent = + format.components.map(component => component.row.filter(c => { + return ( + loadTimeData.getBoolean('EnableCompanyName') || + c.field !== chrome.autofillPrivate.AddressField.COMPANY_NAME); + })); + this.addressWrapper_ = + filteredComponent + .filter(component => { + return (component && component.length > 0); + }) + .map(component => { + return component.map(c => { + return new settings.address.AddressComponentUI( + this.address, c); + }); + }); // Flush dom before resize and savability updates. Polymer.dom.flush(); diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html index 6883353a5c3..e9427a1e447 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html @@ -1,13 +1,8 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> -<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html"> -<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> -<link rel="import" href="chrome://resources/html/action_link.html"> -<link rel="import" href="chrome://resources/html/action_link_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> -<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../settings_shared_css.html"> @@ -15,32 +10,11 @@ <link rel="import" href="../controls/settings_toggle_button.html"> <link rel="import" href="../prefs/prefs.html"> <link rel="import" href="address_edit_dialog.html"> -<link rel="import" href="credit_card_edit_dialog.html"> <link rel="import" href="passwords_shared_css.html"> <dom-module id="settings-autofill-section"> <template> - <style include="settings-shared passwords-shared action-link"> - .type-column { - align-items: center; - flex: 2; - } - - .expiration-column { - align-items: center; - display: flex; - flex: 1; - } - - .expiration-date { - flex: 1; - } - - .payments-label { - -webkit-margin-start: 16px; - color: var(--cr-secondary-text-color); - } - + <style include="settings-shared passwords-shared"> #addressList .start { display: flex; overflow: hidden; @@ -51,34 +25,22 @@ flex: 1; overflow: hidden; } - - .ellipses { - flex: 1; - max-width: fit-content; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - cr-policy-indicator { - padding-right: 20px; - width: 20px; - } </style> - <settings-toggle-button id="autofillToggle" - class="first" + <settings-toggle-button id="autofillProfileToggle" + class="settings-box first" aria-label="$i18n{autofill}" no-extension-indicator - label="$i18n{autofillFormsLabel}" - pref="{{prefs.autofill.enabled}}"> + label="$i18n{enableProfilesLabel}" + sub-label="$i18n{enableProfilesSublabel}" + pref="{{prefs.autofill.profile_enabled}}"> </settings-toggle-button> - <template is="dom-if" if="[[prefs.autofill.enabled.extensionId]]"> + <template is="dom-if" if="[[prefs.autofill.profile_enabled.extensionId]]"> <div class="settings-box continuation"> <extension-controlled-indicator class="start" id="autofillExtensionIndicator" - extension-id="[[prefs.autofill.enabled.extensionId]]" - extension-name="[[prefs.autofill.enabled.controlledByName]]" + extension-id="[[prefs.autofill.profile_enabled.extensionId]]" + extension-name="[[prefs.autofill.profile_enabled.controlledByName]]" extension-can-be-disabled="[[ - prefs.autofill.enabled.extensionCanBeDisabled]]"> + prefs.autofill.profile_enabled.extensionCanBeDisabled]]"> </extension-controlled-indicator> </div> </template> @@ -86,7 +48,8 @@ <h2 class="start">$i18n{addresses}</h2> <paper-button id="addAddress" class="secondary-button header-aligned-button" - on-click="onAddAddressTap_"> + on-click="onAddAddressTap_" + disabled$="[[!prefs.autofill.profile_enabled.value]]"> $i18n{add} </paper-button> </div> @@ -103,9 +66,6 @@ [[item.metadata.summarySublabel]] </span> </span> - <span class="payments-label" hidden$="[[item.metadata.isLocal]]"> - $i18n{googlePayments} - </span> </div> <template is="dom-if" if="[[item.metadata.isLocal]]"> <paper-icon-button-light class="icon-more-vert"> @@ -135,81 +95,9 @@ </cr-action-menu> <template is="dom-if" if="[[showAddressDialog_]]" restamp> <settings-address-edit-dialog address="[[activeAddress]]" - on-close="onAddressDialogClosed_"> + on-close="onAddressDialogClose_"> </settings-address-edit-dialog> </template> - <div class="settings-box first"> - <h2 class="start">$i18n{creditCards}</h2> - <paper-button id="addCreditCard" - class="secondary-button header-aligned-button" - on-click="onAddCreditCardTap_"> - $i18n{add} - </paper-button> - </div> - <div class="list-frame"> - <div id="creditCardsHeading" class="list-item column-header" - hidden$="[[!hasSome_(creditCards)]]"> - <div class="type-column">$i18n{creditCardType}</div> - <div class="expiration-column">$i18n{creditCardExpiration}</div> - </div> - <div id="creditCardList" class="vertical-list list-with-header"> - <template is="dom-repeat" items="[[creditCards]]"> - <div class="list-item"> - <div class="type-column"> - <span id="creditCardLabel">[[item.metadata.summaryLabel]]</span> - <span class="payments-label" - hidden$="[[item.metadata.isLocal]]"> - <span hidden$="[[item.metadata.isCached]]"> - $i18n{googlePayments} - </span> - <span hidden$="[[!item.metadata.isCached]]"> - $i18n{googlePaymentsCached} - </span> - </span> - </div> - <div class="expiration-column"> - <div id="creditCardExpiration" - class="expiration-date">[[expiration_(item)]]</div> - <template is="dom-if" if="[[showDots_(item.metadata)]]"> - <paper-icon-button-light class="icon-more-vert"> - <button id="creditCardMenu" title="$i18n{moreActions}" - on-click="onCreditCardMenuTap_"> - </button> - </paper-icon-button-light> - </template> - <template is="dom-if" if="[[!showDots_(item.metadata)]]"> - <paper-icon-button-light actionable class="icon-external"> - <button id="remoteCreditCardLink" - on-click="onRemoteEditCreditCardTap_"> - </button> - </paper-icon-button-light> - </template> - </div> - </div> - </template> - </div> - <div id="noCreditCardsLabel" class="list-item" - hidden$="[[hasSome_(creditCards)]]"> - $i18n{noCreditCardsFound} - </div> - </div> - <cr-action-menu id="creditCardSharedMenu"> - <button id="menuEditCreditCard" slot="item" class="dropdown-item" - on-click="onMenuEditCreditCardTap_">$i18n{edit}</button> - <button id="menuRemoveCreditCard" slot="item" class="dropdown-item" - hidden$="[[!activeCreditCard.metadata.isLocal]]" - on-click="onMenuRemoveCreditCardTap_">$i18n{removeCreditCard}</button> - <button id="menuClearCreditCard" slot="item" class="dropdown-item" - on-click="onMenuClearCreditCardTap_" - hidden$="[[!activeCreditCard.metadata.isCached]]"> - $i18n{clearCreditCard} - </button> - </cr-action-menu> - <template is="dom-if" if="[[showCreditCardDialog_]]" restamp> - <settings-credit-card-edit-dialog credit-card="[[activeCreditCard]]" - on-close="onCreditCardDialogClosed_"> - </settings-credit-card-edit-dialog> - </template> </template> <script src="autofill_section.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js index ef0055304d9..25bd70563bf 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.js @@ -4,7 +4,7 @@ /** * @fileoverview 'settings-autofill-section' is the section containing saved - * addresses and credit cards for use in autofill. + * addresses for use in autofill and payments APIs. */ /** @@ -38,44 +38,11 @@ class AutofillManager { /** @param {string} guid The guid of the address to remove. */ removeAddress(guid) {} - - /** - * Add an observer to the list of credit cards. - * @param {function(!Array<!AutofillManager.CreditCardEntry>):void} listener - */ - addCreditCardListChangedListener(listener) {} - - /** - * Remove an observer from the list of credit cards. - * @param {function(!Array<!AutofillManager.CreditCardEntry>):void} listener - */ - removeCreditCardListChangedListener(listener) {} - - /** - * Request the list of credit cards. - * @param {function(!Array<!AutofillManager.CreditCardEntry>):void} callback - */ - getCreditCardList(callback) {} - - /** @param {string} guid The GUID of the credit card to remove. */ - removeCreditCard(guid) {} - - /** @param {string} guid The GUID to credit card to remove from the cache. */ - clearCachedCreditCard(guid) {} - - /** - * Saves the given credit card. - * @param {!AutofillManager.CreditCardEntry} creditCard - */ - saveCreditCard(creditCard) {} } /** @typedef {chrome.autofillPrivate.AddressEntry} */ AutofillManager.AddressEntry; -/** @typedef {chrome.autofillPrivate.CreditCardEntry} */ -AutofillManager.CreditCardEntry; - /** * Implementation that accesses the private API. * @implements {AutofillManager} @@ -105,36 +72,6 @@ class AutofillManagerImpl { removeAddress(guid) { chrome.autofillPrivate.removeEntry(assert(guid)); } - - /** @override */ - addCreditCardListChangedListener(listener) { - chrome.autofillPrivate.onCreditCardListChanged.addListener(listener); - } - - /** @override */ - removeCreditCardListChangedListener(listener) { - chrome.autofillPrivate.onCreditCardListChanged.removeListener(listener); - } - - /** @override */ - getCreditCardList(callback) { - chrome.autofillPrivate.getCreditCardList(callback); - } - - /** @override */ - removeCreditCard(guid) { - chrome.autofillPrivate.removeEntry(assert(guid)); - } - - /** @override */ - clearCachedCreditCard(guid) { - chrome.autofillPrivate.maskCreditCard(assert(guid)); - } - - /** @override */ - saveCreditCard(creditCard) { - chrome.autofillPrivate.saveCreditCard(creditCard); - } } cr.addSingletonGetter(AutofillManagerImpl); @@ -145,8 +82,6 @@ cr.addSingletonGetter(AutofillManagerImpl); Polymer({ is: 'settings-autofill-section', - behaviors: [I18nBehavior], - properties: { /** * An array of saved addresses. @@ -162,26 +97,10 @@ Polymer({ /** @private */ showAddressDialog_: Boolean, - - /** - * An array of saved credit cards. - * @type {!Array<!AutofillManager.CreditCardEntry>} - */ - creditCards: Array, - - /** - * The model for any credit card related action menus or dialogs. - * @private {?chrome.autofillPrivate.CreditCardEntry} - */ - activeCreditCard: Object, - - /** @private */ - showCreditCardDialog_: Boolean, }, listeners: { 'save-address': 'saveAddress_', - 'save-credit-card': 'saveCreditCard_', }, /** @@ -203,12 +122,6 @@ Polymer({ */ setAddressesListener_: null, - /** - * @type {?function(!Array<!AutofillManager.CreditCardEntry>)} - * @private - */ - setCreditCardsListener_: null, - /** @override */ attached: function() { // Create listener functions. @@ -217,26 +130,17 @@ Polymer({ this.addresses = list; }; - /** @type {function(!Array<!AutofillManager.CreditCardEntry>)} */ - const setCreditCardsListener = list => { - this.creditCards = list; - }; - // Remember the bound reference in order to detach. this.setAddressesListener_ = setAddressesListener; - this.setCreditCardsListener_ = setCreditCardsListener; // Set the managers. These can be overridden by tests. this.autofillManager_ = AutofillManagerImpl.getInstance(); // Request initial data. this.autofillManager_.getAddressList(setAddressesListener); - this.autofillManager_.getCreditCardList(setCreditCardsListener); // Listen for changes. this.autofillManager_.addAddressListChangedListener(setAddressesListener); - this.autofillManager_.addCreditCardListChangedListener( - setCreditCardsListener); }, /** @override */ @@ -244,19 +148,6 @@ Polymer({ this.autofillManager_.removeAddressListChangedListener( /** @type {function(!Array<!AutofillManager.AddressEntry>)} */ ( this.setAddressesListener_)); - this.autofillManager_.removeCreditCardListChangedListener( - /** @type {function(!Array<!AutofillManager.CreditCardEntry>)} */ ( - this.setCreditCardsListener_)); - }, - - /** - * Formats the expiration date so it's displayed as MM/YYYY. - * @param {!chrome.autofillPrivate.CreditCardEntry} item - * @return {string} - * @private - */ - expiration_: function(item) { - return item.expirationMonth + '/' + item.expirationYear; }, /** @@ -297,7 +188,7 @@ Polymer({ }, /** @private */ - onAddressDialogClosed_: function() { + onAddressDialogClose_: function() { this.showAddressDialog_ = false; cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); this.activeDialogAnchor_ = null; @@ -316,7 +207,7 @@ Polymer({ /** @private */ onRemoteEditAddressTap_: function() { - window.open(this.i18n('manageAddressesUrl')); + window.open(loadTimeData.getString('manageAddressesUrl')); }, /** @@ -330,107 +221,6 @@ Polymer({ }, /** - * Opens the credit card action menu. - * @param {!Event} e The polymer event. - * @private - */ - onCreditCardMenuTap_: function(e) { - const menuEvent = /** @type {!{model: !{item: !Object}}} */ (e); - - /* TODO(scottchen): drop the [dataHost][dataHost] once this bug is fixed: - https://github.com/Polymer/polymer/issues/2574 */ - // TODO(dpapad): The [dataHost][dataHost] workaround is only necessary for - // Polymer 1. Remove once migration to Polymer 2 has completed. - const item = Polymer.DomIf ? menuEvent.model.item : - menuEvent.model['dataHost']['dataHost'].item; - - // Copy item so dialog won't update model on cancel. - this.activeCreditCard = - /** @type {!chrome.autofillPrivate.CreditCardEntry} */ ( - Object.assign({}, item)); - - const dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); - /** @type {!CrActionMenuElement} */ (this.$.creditCardSharedMenu) - .showAt(dotsButton); - this.activeDialogAnchor_ = dotsButton; - }, - - /** - * Handles tapping on the "Add credit card" button. - * @param {!Event} e - * @private - */ - onAddCreditCardTap_: function(e) { - e.preventDefault(); - const date = new Date(); // Default to current month/year. - const expirationMonth = date.getMonth() + 1; // Months are 0 based. - this.activeCreditCard = { - expirationMonth: expirationMonth.toString(), - expirationYear: date.getFullYear().toString(), - }; - this.showCreditCardDialog_ = true; - this.activeDialogAnchor_ = this.$.addCreditCard; - }, - - /** @private */ - onCreditCardDialogClosed_: function() { - this.showCreditCardDialog_ = false; - cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); - this.activeDialogAnchor_ = null; - }, - - /** - * Handles tapping on the "Edit" credit card button. - * @param {!Event} e The polymer event. - * @private - */ - onMenuEditCreditCardTap_: function(e) { - e.preventDefault(); - - if (this.activeCreditCard.metadata.isLocal) - this.showCreditCardDialog_ = true; - else - this.onRemoteEditCreditCardTap_(); - - this.$.creditCardSharedMenu.close(); - }, - - /** @private */ - onRemoteEditCreditCardTap_: function() { - window.open(this.i18n('manageCreditCardsUrl')); - }, - - /** - * Handles tapping on the "Remove" credit card button. - * @private - */ - onMenuRemoveCreditCardTap_: function() { - this.autofillManager_.removeCreditCard( - /** @type {string} */ (this.activeCreditCard.guid)); - this.$.creditCardSharedMenu.close(); - }, - - /** - * Handles tapping on the "Clear copy" button for cached credit cards. - * @private - */ - onMenuClearCreditCardTap_: function() { - this.autofillManager_.clearCachedCreditCard( - /** @type {string} */ (this.activeCreditCard.guid)); - this.$.creditCardSharedMenu.close(); - }, - - /** - * The 3-dot menu should not be shown if the card is entirely remote. - * @param {!chrome.autofillPrivate.AutofillMetadata} metadata - * @return {boolean} - * @private - */ - showDots_: function(metadata) { - return !!(metadata.isLocal || metadata.isCached); - }, - - /** * Returns true if the list exists and has items. * @param {Array<Object>} list * @return {boolean} @@ -448,23 +238,5 @@ Polymer({ saveAddress_: function(event) { this.autofillManager_.saveAddress(event.detail); }, - - /** - * Listens for the save-credit-card event, and calls the private API. - * @param {!Event} event - * @private - */ - saveCreditCard_: function(event) { - this.autofillManager_.saveCreditCard(event.detail); - }, - - /** - * @private - * @param {boolean} toggleValue - * @return {string} - */ - getOnOffLabel_: function(toggleValue) { - return toggleValue ? this.i18n('toggleOn') : this.i18n('toggleOff'); - } }); })(); diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html index d7228196d7f..7138118d0c4 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html @@ -19,7 +19,7 @@ } .md-select + .md-select { - -webkit-margin-start: 8px; + margin-inline-start: 8px; } /* Prevent focus-outline from being chopped by the bottom of dialog body. @@ -54,7 +54,7 @@ <div slot="title">[[title_]]</div> <div slot="body"> <cr-input id="nameInput" label="$i18n{creditCardName}" - value="{{creditCard.name}}" autofocus + value="{{creditCard.name}}" autofocus spellcheck="false" on-input="onCreditCardNameOrNumberChanged_"> </cr-input> <cr-input id="numberInput" label="$i18n{creditCardNumber}" diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html index 84ead164afc..9b7f575fca7 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html @@ -35,8 +35,8 @@ } paper-icon-button-light { - -webkit-margin-start: 2px; background-size: 24px; /* Other buttons are sized by --cr-icon-size. */ + margin-inline-start: 2px; } </style> <cr-dialog id="dialog" close-text="$i18n{close}"> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js index 862c1dd4dc3..0c97de354b1 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.js @@ -10,14 +10,17 @@ Polymer({ is: 'password-list-item', - behaviors: [FocusRowBehavior, ShowPasswordBehavior], + behaviors: [ + FocusRowBehavior, + ShowPasswordBehavior, + ], /** * Selects the password on tap if revealed. * @private */ onReadonlyInputTap_: function() { - if (this.password) + if (this.item.password) this.$$('#password').select(); }, diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html index 8a2376f9935..e1282d3061e 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html @@ -8,6 +8,7 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="autofill_section.html"> <link rel="import" href="passwords_section.html"> +<link rel="import" href="payments_section.html"> <link rel="import" href="../controls/extension_controlled_indicator.html"> <link rel="import" href="../controls/settings_toggle_button.html"> <link rel="import" href="../prefs/prefs.html"> @@ -27,6 +28,10 @@ id="autofillManagerButton" label="$i18n{autofill}" sub-label="$i18n{autofillDetail}" on-click="onAutofillTap_"> </cr-link-row> + <cr-link-row icon-class="subpage-arrow" + id="paymentManagerButton" label="$i18n{creditCards}" + sub-label="$i18n{creditCardsDetail}" on-click="onPaymentsTap_"> + </cr-link-row> <div class="settings-box two-line"> <div class="start two-line" on-click="onPasswordsTap_" actionable id="passwordManagerButton"> @@ -51,6 +56,14 @@ </settings-autofill-section> </settings-subpage> </template> + <template is="dom-if" route-path="/payments"> + <settings-subpage + associated-control="[[$$('#paymentManagerButton')]]" + page-title="$i18n{creditCards}"> + <settings-payments-section id="paymentSection" prefs="{{prefs}}"> + </settings-payments-section> + </settings-subpage> + </template> <template is="dom-if" route-path="/passwords"> <settings-subpage associated-control="[[$$('#passwordManagerButton')]]" diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js index b5ec2f9ad89..eadb3769d0c 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js @@ -39,11 +39,18 @@ Polymer({ }, /** + * Shows the manage payment methods sub page. + * @private + */ + onPaymentsTap_: function() { + settings.navigateTo(settings.routes.PAYMENTS); + }, + + /** * Shows the manage passwords sub page. - * @param {!Event} event * @private */ - onPasswordsTap_: function(event) { + onPasswordsTap_: function() { settings.navigateTo(settings.routes.MANAGE_PASSWORDS); }, }); diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html index 33e3643e397..bb85a7e20b2 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html @@ -13,7 +13,7 @@ --paper-progress-active-color: var(--google-blue-500); } .action-button { - -webkit-margin-start: 8px; + margin-inline-start: 8px; } </style> <cr-dialog id="dialog_start" close-text="$i18n{close}"> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html index c9f66d0f5d9..eafabb70dbb 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html @@ -30,7 +30,7 @@ * columns. It is necessary due to the absence of the "more actions" * button in the heading. */ - -webkit-padding-end: calc( + padding-inline-end: calc( var(--cr-icon-ripple-size) + var(--cr-icon-button-margin-start)); } @@ -44,7 +44,7 @@ } #exportImportMenuButtonContainer { - -webkit-margin-end: 0; + margin-inline-end: 0; } </style> <settings-toggle-button id="passwordToggle" diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js index 243d81313d8..e5ec2b4ed72 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js @@ -150,25 +150,30 @@ Polymer({ /** @override */ attached: function() { + // The item uid is built from index, origin, and username for the + // following reasons: origin and username are enough to describe and + // uniquely identify an entry. It is impossible to have two entries + // that have the same origin and username, but different passwords, + // as the password update logic prevents these cases. The entry is + // required to force a refresh of entries, after a removal or undo of + // a removal has taken place. All entries before the point of + // modification are uneffected, but the ones following need to be + // refreshed. Including the index in the uid achieves this effect. + // See https://crbug.com/862119 how this could lead to bugs otherwise. + const getItemUid = + item => [item.entry.index, item.entry.loginPair.urls.origin, + item.entry.loginPair.username] + .join('_'); + // Create listener functions. - const setSavedPasswordsListener = list => - this.updateList('savedPasswords', item => { - // The item uid is built from index, origin, and username for the - // following reasons: origin and username are enough to describe and - // uniquely identify an entry. It is impossible to have two entries - // that have the same origin and username, but different passwords, - // as the password update logic prevents these cases. The entry is - // required to force a refresh of entries, after a removal or undo of - // a removal has taken place. All entries before the point of - // modification are uneffected, but the ones following need to be - // refreshed. Including the index in the uid achieves this effect. - // See https://crbug.com/862119 how this could lead to bugs otherwise. - return item.entry.index + '_' + item.entry.loginPair.urls.origin + - '_' + item.entry.loginPair.username; - }, list.map(entry => ({ - entry: entry, - password: '', - }))); + const setSavedPasswordsListener = list => { + const newList = list.map(entry => ({entry: entry, password: ''})); + this.updateList('savedPasswords', getItemUid, newList); + this.savedPasswords.forEach((item, index) => { + item.password = ''; + this.$.passwordList.notifyPath(`items.${index}.password`); + }); + }; const setPasswordExceptionsListener = list => { this.passwordExceptions = list; diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_shared_css.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_shared_css.html index 5bcd1e903f9..62693628167 100644 --- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_shared_css.html +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_shared_css.html @@ -34,6 +34,19 @@ height: 20px; width: 0; } + + .type-column { + align-items: center; + flex: 2; + } + + .ellipses { + flex: 1; + max-width: fit-content; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } </style> </template> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/payments_section.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/payments_section.html new file mode 100644 index 00000000000..c35d1f829df --- /dev/null +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/payments_section.html @@ -0,0 +1,154 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> +<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> +<link rel="import" href="../i18n_setup.html"> +<link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="../controls/settings_toggle_button.html"> +<link rel="import" href="../prefs/prefs.html"> +<link rel="import" href="../people_page/sync_browser_proxy.html"> +<link rel="import" href="credit_card_edit_dialog.html"> +<link rel="import" href="passwords_shared_css.html"> + +<dom-module id="settings-payments-section"> + <template> + <style include="settings-shared passwords-shared"> + .expiration-column { + align-items: center; + display: flex; + flex: 1; + } + + .expiration-date { + flex: 1; + } + + .payments-label { + color: var(--cr-secondary-text-color); + margin-inline-start: 16px; + } + + #migrateCreditCards { + border-bottom: var(--settings-separator-line); + border-top: none; + } + + #migrateCreditCardsButton { + margin: 0 auto; + } + </style> + <settings-toggle-button id="autofillCreditCardToggle" + class="settings-box first" + aria-label="$i18n{creditCards}" no-extension-indicator + label="$i18n{enableCreditCardsLabel}" + sub-label="$i18n{enableCreditCardsSublabel}" + pref="{{prefs.autofill.credit_card_enabled}}"> + </settings-toggle-button> + <template is="dom-if" + if="[[prefs.autofill.credit_card_enabled.extensionId]]"> + <div class="settings-box continuation"> + <extension-controlled-indicator class="start" + id="autofillExtensionIndicator" + extension-id="[[prefs.autofill.credit_card_enabled.extensionId]]" + extension-name="[[ + prefs.autofill.credit_card_enabled.controlledByName]]" + extension-can-be-disabled="[[ + prefs.autofill.credit_card_enabled.extensionCanBeDisabled]]"> + </extension-controlled-indicator> + </div> + </template> + <div class="settings-box continuation"> + <h2 class="start">$i18n{creditCards}</h2> + <paper-button id="addCreditCard" + class="secondary-button header-aligned-button" + on-click="onAddCreditCardTap_" + disabled$="[[!prefs.autofill.credit_card_enabled.value]]"> + $i18n{add} + </paper-button> + </div> + <div class="settings-box two-line" id="migrateCreditCards" + hidden$="[[!checkIfMigratable_(syncStatus, creditCards, + prefs.autofill.credit_card_enabled.value)]]" + on-click="onMigrateCreditCardsClick_" actionable> + <div class="start"> + [[migrateCreditCardsLabel_]] + <div class="secondary"> + [[migratableCreditCardsInfo_]] + </div> + </div> + <paper-icon-button-light id="migrateCreditCardsButton" + class="subpage-arrow"> + <button aria-label="[[migrateCreditCardsLabel_]]"></button> + </paper-icon-button-light> + </div> + <div class="list-frame"> + <div id="creditCardsHeading" class="list-item column-header" + hidden$="[[!hasSome_(creditCards)]]"> + <div class="type-column">$i18n{creditCardType}</div> + <div class="expiration-column">$i18n{creditCardExpiration}</div> + </div> + <div id="creditCardList" class="vertical-list list-with-header"> + <template is="dom-repeat" items="[[creditCards]]"> + <div class="list-item"> + <div class="type-column"> + <span id="creditCardLabel">[[item.metadata.summaryLabel]]</span> + <span class="payments-label" + hidden$="[[item.metadata.isLocal]]"> + <span hidden$="[[item.metadata.isCached]]"> + $i18n{googlePayments} + </span> + <span hidden$="[[!item.metadata.isCached]]"> + $i18n{googlePaymentsCached} + </span> + </span> + </div> + <div class="expiration-column"> + <div id="creditCardExpiration" + class="expiration-date">[[expiration_(item)]]</div> + <template is="dom-if" if="[[showDots_(item.metadata)]]"> + <paper-icon-button-light class="icon-more-vert"> + <button id="creditCardMenu" title="$i18n{moreActions}" + on-click="onCreditCardMenuTap_"> + </button> + </paper-icon-button-light> + </template> + <template is="dom-if" if="[[!showDots_(item.metadata)]]"> + <paper-icon-button-light actionable class="icon-external"> + <button id="remoteCreditCardLink" + on-click="onRemoteEditCreditCardTap_"> + </button> + </paper-icon-button-light> + </template> + </div> + </div> + </template> + </div> + <div id="noCreditCardsLabel" class="list-item" + hidden$="[[hasSome_(creditCards)]]"> + $i18n{noCreditCardsFound} + </div> + </div> + <cr-action-menu id="creditCardSharedMenu"> + <button id="menuEditCreditCard" slot="item" class="dropdown-item" + on-click="onMenuEditCreditCardTap_">$i18n{edit}</button> + <button id="menuRemoveCreditCard" slot="item" class="dropdown-item" + hidden$="[[!activeCreditCard.metadata.isLocal]]" + on-click="onMenuRemoveCreditCardTap_">$i18n{removeCreditCard}</button> + <button id="menuClearCreditCard" slot="item" class="dropdown-item" + on-click="onMenuClearCreditCardTap_" + hidden$="[[!activeCreditCard.metadata.isCached]]"> + $i18n{clearCreditCard} + </button> + </cr-action-menu> + <template is="dom-if" if="[[showCreditCardDialog_]]" restamp> + <settings-credit-card-edit-dialog credit-card="[[activeCreditCard]]" + on-close="onCreditCardDialogClose_"> + </settings-credit-card-edit-dialog> + </template> + </template> + <script src="payments_section.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/payments_section.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/payments_section.js new file mode 100644 index 00000000000..e3c141a86b1 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/payments_section.js @@ -0,0 +1,421 @@ +// Copyright 2018 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. + +/** + * @fileoverview 'settings-payments-section' is the section containing saved + * credit cards for use in autofill and payments APIs. + */ + +/** + * Interface for all callbacks to the payments autofill API. + * @interface + */ +class PaymentsManager { + /** + * Add an observer to the list of credit cards. + * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} listener + */ + addCreditCardListChangedListener(listener) {} + + /** + * Remove an observer from the list of credit cards. + * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} listener + */ + removeCreditCardListChangedListener(listener) {} + + /** + * Request the list of credit cards. + * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} callback + */ + getCreditCardList(callback) {} + + /** @param {string} guid The GUID of the credit card to remove. */ + removeCreditCard(guid) {} + + /** @param {string} guid The GUID to credit card to remove from the cache. */ + clearCachedCreditCard(guid) {} + + /** + * Saves the given credit card. + * @param {!PaymentsManager.CreditCardEntry} creditCard + */ + saveCreditCard(creditCard) {} + + /** + * Migrate the local credit cards. + */ + migrateCreditCards() {} +} + +/** @typedef {chrome.autofillPrivate.CreditCardEntry} */ +PaymentsManager.CreditCardEntry; + +/** + * Implementation that accesses the private API. + * @implements {PaymentsManager} + */ +class PaymentsManagerImpl { + /** @override */ + addCreditCardListChangedListener(listener) { + chrome.autofillPrivate.onCreditCardListChanged.addListener(listener); + } + + /** @override */ + removeCreditCardListChangedListener(listener) { + chrome.autofillPrivate.onCreditCardListChanged.removeListener(listener); + } + + /** @override */ + getCreditCardList(callback) { + chrome.autofillPrivate.getCreditCardList(callback); + } + + /** @override */ + removeCreditCard(guid) { + chrome.autofillPrivate.removeEntry(assert(guid)); + } + + /** @override */ + clearCachedCreditCard(guid) { + chrome.autofillPrivate.maskCreditCard(assert(guid)); + } + + /** @override */ + saveCreditCard(creditCard) { + chrome.autofillPrivate.saveCreditCard(creditCard); + } + + /** @override */ + migrateCreditCards() { + chrome.autofillPrivate.migrateCreditCards(); + } +} + +cr.addSingletonGetter(PaymentsManagerImpl); + +(function() { +'use strict'; + +Polymer({ + is: 'settings-payments-section', + + behaviors: [ + WebUIListenerBehavior, + I18nBehavior, + ], + + properties: { + /** + * An array of saved credit cards. + * @type {!Array<!PaymentsManager.CreditCardEntry>} + */ + creditCards: Array, + + /** + * The model for any credit card related action menus or dialogs. + * @private {?chrome.autofillPrivate.CreditCardEntry} + */ + activeCreditCard: Object, + + /** @private */ + showCreditCardDialog_: Boolean, + + /** @private */ + migrateCreditCardsLabel_: String, + + /** @private */ + migratableCreditCardsInfo_: String, + + /** + * The current sync status, supplied by SyncBrowserProxy. + * @type {?settings.SyncStatus} + */ + syncStatus: Object, + + /** + * Whether migration local card on settings page is enabled. + * @private + */ + migrationEnabled_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('migrationEnabled'); + }, + readOnly: true, + }, + + /** + * Whether user has a Google Payments account. + * @private + */ + hasGooglePaymentsAccount_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('hasGooglePaymentsAccount'); + }, + readOnly: true, + }, + }, + + listeners: { + 'save-credit-card': 'saveCreditCard_', + }, + + /** + * The element to return focus to, when the currently active dialog is + * closed. + * @private {?HTMLElement} + */ + activeDialogAnchor_: null, + + /** + * @type {PaymentsManager} + * @private + */ + PaymentsManager_: null, + + /** + * @type {?function(!Array<!PaymentsManager.CreditCardEntry>)} + * @private + */ + setCreditCardsListener_: null, + + /** @private {?settings.SyncBrowserProxy} */ + syncBrowserProxy_: null, + + /** @override */ + attached: function() { + // Create listener function. + /** @type {function(!Array<!PaymentsManager.CreditCardEntry>)} */ + const setCreditCardsListener = list => { + this.creditCards = list; + }; + + // Remember the bound reference in order to detach. + this.setCreditCardsListener_ = setCreditCardsListener; + + // Set the managers. These can be overridden by tests. + this.paymentsManager_ = PaymentsManagerImpl.getInstance(); + + // Request initial data. + this.paymentsManager_.getCreditCardList(setCreditCardsListener); + + // Listen for changes. + this.paymentsManager_.addCreditCardListChangedListener( + setCreditCardsListener); + + this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance(); + this.syncBrowserProxy_.getSyncStatus().then( + this.handleSyncStatus_.bind(this)); + this.addWebUIListener( + 'sync-status-changed', this.handleSyncStatus_.bind(this)); + }, + + /** @override */ + detached: function() { + this.paymentsManager_.removeCreditCardListChangedListener( + /** @type {function(!Array<!PaymentsManager.CreditCardEntry>)} */ ( + this.setCreditCardsListener_)); + }, + + /** + * Formats the expiration date so it's displayed as MM/YYYY. + * @param {!chrome.autofillPrivate.CreditCardEntry} item + * @return {string} + * @private + */ + expiration_: function(item) { + return item.expirationMonth + '/' + item.expirationYear; + }, + + /** + * Opens the credit card action menu. + * @param {!Event} e The polymer event. + * @private + */ + onCreditCardMenuTap_: function(e) { + const menuEvent = /** @type {!{model: !{item: !Object}}} */ (e); + + /* TODO(scottchen): drop the [dataHost][dataHost] once this bug is fixed: + https://github.com/Polymer/polymer/issues/2574 */ + // TODO(dpapad): The [dataHost][dataHost] workaround is only necessary for + // Polymer 1. Remove once migration to Polymer 2 has completed. + const item = Polymer.DomIf ? menuEvent.model.item : + menuEvent.model['dataHost']['dataHost'].item; + + // Copy item so dialog won't update model on cancel. + this.activeCreditCard = + /** @type {!chrome.autofillPrivate.CreditCardEntry} */ ( + Object.assign({}, item)); + + const dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); + /** @type {!CrActionMenuElement} */ (this.$.creditCardSharedMenu) + .showAt(dotsButton); + this.activeDialogAnchor_ = dotsButton; + }, + + /** + * Handles tapping on the "Add credit card" button. + * @param {!Event} e + * @private + */ + onAddCreditCardTap_: function(e) { + e.preventDefault(); + const date = new Date(); // Default to current month/year. + const expirationMonth = date.getMonth() + 1; // Months are 0 based. + this.activeCreditCard = { + expirationMonth: expirationMonth.toString(), + expirationYear: date.getFullYear().toString(), + }; + this.showCreditCardDialog_ = true; + this.activeDialogAnchor_ = this.$.addCreditCard; + }, + + /** @private */ + onCreditCardDialogClose_: function() { + this.showCreditCardDialog_ = false; + cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); + this.activeDialogAnchor_ = null; + this.activeCreditCard = null; + }, + + /** + * Handles tapping on the "Edit" credit card button. + * @param {!Event} e The polymer event. + * @private + */ + onMenuEditCreditCardTap_: function(e) { + e.preventDefault(); + + if (this.activeCreditCard.metadata.isLocal) + this.showCreditCardDialog_ = true; + else + this.onRemoteEditCreditCardTap_(); + + this.$.creditCardSharedMenu.close(); + }, + + /** @private */ + onRemoteEditCreditCardTap_: function() { + window.open(loadTimeData.getString('manageCreditCardsUrl')); + }, + + /** + * Handles tapping on the "Remove" credit card button. + * @private + */ + onMenuRemoveCreditCardTap_: function() { + this.paymentsManager_.removeCreditCard( + /** @type {string} */ (this.activeCreditCard.guid)); + this.$.creditCardSharedMenu.close(); + this.activeCreditCard = null; + }, + + /** + * Handles tapping on the "Clear copy" button for cached credit cards. + * @private + */ + onMenuClearCreditCardTap_: function() { + this.paymentsManager_.clearCachedCreditCard( + /** @type {string} */ (this.activeCreditCard.guid)); + this.$.creditCardSharedMenu.close(); + this.activeCreditCard = null; + }, + + /** + * Handles clicking on the "Migrate" button for migrate local credit cards. + * @private + */ + onMigrateCreditCardsClick_: function() { + this.paymentsManager_.migrateCreditCards(); + }, + + /** + * The 3-dot menu should not be shown if the card is entirely remote. + * @param {!chrome.autofillPrivate.AutofillMetadata} metadata + * @return {boolean} + * @private + */ + showDots_: function(metadata) { + return !!(metadata.isLocal || metadata.isCached); + }, + + /** + * Returns true if the list exists and has items. + * @param {Array<Object>} list + * @return {boolean} + * @private + */ + hasSome_: function(list) { + return !!(list && list.length); + }, + + /** + * Listens for the save-credit-card event, and calls the private API. + * @param {!Event} event + * @private + */ + saveCreditCard_: function(event) { + this.paymentsManager_.saveCreditCard(event.detail); + }, + + /** + * Handler for when the sync state is pushed from the browser. + * @param {?settings.SyncStatus} syncStatus + * @private + */ + handleSyncStatus_: function(syncStatus) { + this.syncStatus = syncStatus; + }, + + /** + * @param {!settings.SyncStatus} syncStatus + * @param {!Array<!PaymentsManager.CreditCardEntry>} creditCards + * @param {boolean} creditCardEnabled + * @return {boolean} Whether to show the migration button. True iff at least + * one valid local card, enable migration, signed-in & synced and credit card + * pref enabled. + * @private + */ + checkIfMigratable_: function(syncStatus, creditCards, creditCardEnabled) { + if (syncStatus == undefined) + return false; + + // If user not enable migration experimental flag, return false. + if (!this.migrationEnabled_) + return false; + + // If user does not have Google Payments Account, return false. + if (!this.hasGooglePaymentsAccount_) + return false; + + // If credit card enabled pref is false, return false. + if (!creditCardEnabled) + return false; + + // If user not signed-in and synced, return false. + if (!syncStatus.signedIn || !syncStatus.syncSystemEnabled) + return false; + + let numberOfMigratableCreditCard = + creditCards.filter(card => card.metadata.isMigratable).length; + // Check whether exist at least one local valid card for migration. + if (numberOfMigratableCreditCard == 0) + return false; + + // Update the display label depends on the number of migratable credit + // cards. + this.migrateCreditCardsLabel_ = numberOfMigratableCreditCard == 1 ? + this.i18n('migrateCreditCardsLabelSingle') : + this.i18n('migrateCreditCardsLabelMultiple'); + // Update the display text depends on the number of migratable credit cards. + this.migratableCreditCardsInfo_ = numberOfMigratableCreditCard == 1 ? + this.i18n('migratableCardsInfoSingle') : + this.i18n('migratableCardsInfoMultiple'); + + return true; + }, + +}); +})(); diff --git a/chromium/chrome/browser/resources/settings/people_page/BUILD.gn b/chromium/chrome/browser/resources/settings/people_page/BUILD.gn index 6426f36e0e8..c2f8cc39834 100644 --- a/chromium/chrome/browser/resources/settings/people_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/people_page/BUILD.gn @@ -14,19 +14,19 @@ js_type_check("closure_compile") { ":easy_unlock_turn_off_dialog", ":fingerprint_browser_proxy", ":fingerprint_list", - ":fingerprint_progress_arc", ":import_data_browser_proxy", ":import_data_dialog", ":lock_screen", ":lock_screen_constants", + ":lock_screen_password_prompt_dialog", ":lock_state_behavior", ":manage_profile", ":manage_profile_browser_proxy", - ":password_prompt_dialog", ":people_page", ":profile_info_browser_proxy", ":setup_fingerprint_dialog", ":setup_pin_dialog", + ":signout_dialog", ":sync_account_control", ":sync_browser_proxy", ":sync_page", @@ -110,12 +110,6 @@ js_library("fingerprint_list") { externs_list = [ "$externs_path/web_animations.js" ] } -js_library("fingerprint_progress_arc") { - deps = [ - "//ui/webui/resources/js:cr", - ] -} - js_library("import_data_browser_proxy") { deps = [ "//ui/webui/resources/js:cr", @@ -128,8 +122,8 @@ js_library("lock_screen") { ":easy_unlock_turn_off_dialog", ":fingerprint_browser_proxy", ":lock_screen_constants", + ":lock_screen_password_prompt_dialog", ":lock_state_behavior", - ":password_prompt_dialog", "..:route", "../controls:settings_dropdown_menu", "../controls:settings_toggle_button", @@ -176,14 +170,12 @@ js_library("manage_profile_browser_proxy") { ] } -js_library("password_prompt_dialog") { +js_library("lock_screen_password_prompt_dialog") { deps = [ ":lock_screen_constants", - "..:route", - "//ui/webui/resources/cr_elements/cr_input:cr_input", + ":lock_state_behavior", + "../controls:password_prompt_dialog", ] - externs_list = [ "$externs_path/quick_unlock_private.js" ] - extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ] } js_library("people_page") { @@ -192,6 +184,7 @@ js_library("people_page") { ":lock_screen_constants", ":lock_state_behavior", ":profile_info_browser_proxy", + ":signout_dialog", ":sync_browser_proxy", "..:page_visibility", "..:route", @@ -216,7 +209,7 @@ js_library("profile_info_browser_proxy") { js_library("setup_fingerprint_dialog") { deps = [ ":fingerprint_browser_proxy", - ":fingerprint_progress_arc", + "//ui/webui/resources/cr_elements/chromeos/fingerprint:cr_fingerprint_progress_arc", "//ui/webui/resources/js:i18n_behavior", "//ui/webui/resources/js:web_ui_listener_behavior", ] @@ -225,12 +218,25 @@ js_library("setup_fingerprint_dialog") { js_library("setup_pin_dialog") { deps = [ ":lock_screen_constants", - ":password_prompt_dialog", + ":lock_screen_password_prompt_dialog", "..:route", "//ui/webui/resources/js:i18n_behavior", ] } +js_library("signout_dialog") { + deps = [ + ":profile_info_browser_proxy", + ":sync_browser_proxy", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/cr_elements/cr_expand_button:cr_expand_button", + "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js:load_time_data", + "//ui/webui/resources/js:util", + "//ui/webui/resources/js:web_ui_listener_behavior", + ] +} + js_library("sync_page") { deps = [ ":sync_browser_proxy", diff --git a/chromium/chrome/browser/resources/settings/people_page/account_manager.html b/chromium/chrome/browser/resources/settings/people_page/account_manager.html index 95750103025..f910659d1b4 100644 --- a/chromium/chrome/browser/resources/settings/people_page/account_manager.html +++ b/chromium/chrome/browser/resources/settings/people_page/account_manager.html @@ -1,9 +1,12 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/icon.html"> <link rel="import" href="chrome://resources/html/util.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> @@ -35,7 +38,7 @@ <h2>$i18n{accountListHeader}</h2> </div> <div id="outer" class="layout vertical nowrap"> - <template is="dom-repeat" items="[[accounts_]]"> + <template is="dom-repeat" id="account-list" items="[[accounts_]]"> <div class="settings-box"> <div class="profile-icon" style="background-image: [[getIconImageSet_(item.pic)]]"></div> @@ -45,12 +48,25 @@ <div class="secondary">[[item.email]]</div> </div> </div> + <paper-icon-button-light class="icon-more-vert" + hidden="[[item.isDeviceAccount]]"> + <button title="$i18n{moreActions}" + on-click="onAccountActionsMenuButtonTap_"> + </button> + </paper-icon-button-light> </div> </template> <div id="add-account-button" class="settings-box" on-tap="addAccount_"> $i18n{addAccountLabel} </div> <div class="clear settings-box"></div> + + <cr-action-menu> + <button slot="item" class="dropdown-item" + on-click="onRemoveAccountTap_"> + $i18n{removeAccountLabel} + </button> + </cr-action-menu> </div> </template> <script src="account_manager.js"></script> diff --git a/chromium/chrome/browser/resources/settings/people_page/account_manager.js b/chromium/chrome/browser/resources/settings/people_page/account_manager.js index f0c6ae8e166..e754d4ca049 100644 --- a/chromium/chrome/browser/resources/settings/people_page/account_manager.js +++ b/chromium/chrome/browser/resources/settings/people_page/account_manager.js @@ -27,6 +27,12 @@ Polymer({ return []; }, }, + + /** + * The targeted account for menu operations. + * @private {?settings.Account} + */ + actionMenuAccount_: Object, }, /** @private {?settings.AccountManagerBrowserProxy} */ @@ -68,4 +74,34 @@ Polymer({ this.set('accounts_', accounts); }); }, + + /** + * Opens the Account actions menu. + * @param {!{model: !{item: settings.Account}, target: !Element}} event + * @private + */ + onAccountActionsMenuButtonTap_: function(event) { + this.actionMenuAccount_ = event.model.item; + /** @type {!CrActionMenuElement} */ (this.$$('cr-action-menu')) + .showAt(event.target); + }, + + /** + * Closes action menu and resets action menu model. + * @private + */ + closeActionMenu_: function() { + this.$$('cr-action-menu').close(); + this.actionMenuAccount_ = null; + }, + + /** + * Removes the account being pointed to by |this.actionMenuAccount_|. + * @private + */ + onRemoveAccountTap_: function() { + this.browserProxy_.removeAccount( + /** @type {?settings.Account} */ (this.actionMenuAccount_)); + this.closeActionMenu_(); + } }); diff --git a/chromium/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js b/chromium/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js index c1257ade2d5..0c5f2e48723 100644 --- a/chromium/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.js @@ -12,6 +12,9 @@ cr.exportPath('settings'); /** * Information for an account managed by Chrome OS AccountManager. * @typedef {{ + * id: string, + * accountType: number, + * isDeviceAccount: boolean, * fullName: string, * email: string, * pic: string, @@ -32,6 +35,12 @@ cr.define('settings', function() { * Triggers the 'Add account' flow. */ addAccount() {} + + /** + * Removes |account| from Account Manager. + * @param {?settings.Account} account + */ + removeAccount(account) {} } /** @@ -47,6 +56,11 @@ cr.define('settings', function() { addAccount() { chrome.send('addAccount'); } + + /** @override */ + removeAccount(account) { + chrome.send('removeAccount', [account]); + } } cr.addSingletonGetter(AccountManagerBrowserProxyImpl); diff --git a/chromium/chrome/browser/resources/settings/people_page/change_picture.html b/chromium/chrome/browser/resources/settings/people_page/change_picture.html index f14495a8af7..941efeb015c 100644 --- a/chromium/chrome/browser/resources/settings/people_page/change_picture.html +++ b/chromium/chrome/browser/resources/settings/people_page/change_picture.html @@ -25,15 +25,15 @@ } #title { - -webkit-margin-start: 20px; height: var(--title-height); + margin-inline-start: 20px; padding-top: var(--title-padding); } #container { - -webkit-margin-start: 20px; align-items: flex-start; display: flex; + margin-inline-start: 20px; position: absolute; top: calc(var(--cr-settings-header-height) + var(--title-padding) + @@ -42,9 +42,9 @@ #picturePane { --cr-picture-image-size: 192px; - -webkit-margin-end: 24px; flex-shrink: 0; height: 288px; + margin-inline-end: 24px; margin-top: 6px; position: relative; width: 288px; @@ -58,7 +58,6 @@ } #pictureList { - -webkit-margin-end: 16px; /* TODO(reveman): Find a way to have height align to viewport without using fixed position. */ height: calc(100vh - @@ -66,6 +65,7 @@ var(--cr-settings-header-height) - var(--title-padding) - var(--title-height)); + margin-inline-end: 16px; margin-top: 0; min-height: 332px; overflow-x: hidden; diff --git a/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html b/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html index b0168a44cf2..42d5e7fa77c 100644 --- a/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html +++ b/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html @@ -72,7 +72,8 @@ <template is="dom-if" if="[[showSetupFingerprintDialog_]]" restamp> <settings-setup-fingerprint-dialog on-add-fingerprint="updateFingerprintsList_" - on-close="onSetupFingerprintDialogClose_"> + on-close="onSetupFingerprintDialogClose_" + allow-add-another-finger="[[allowAddAnotherFinger_]]"> </settings-setup-fingerprint-dialog> </template> </template> diff --git a/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.js b/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.js index ea245c45cc3..7c4ec5f1156 100644 --- a/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.js +++ b/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.js @@ -35,6 +35,16 @@ Polymer({ /** @private */ showSetupFingerprintDialog_: Boolean, + + /** + * Whether add another finger is allowed. + * @type {boolean} + * @private + */ + allowAddAnotherFinger_: { + type: Boolean, + value: true, + }, }, /** @private {?settings.FingerprintBrowserProxy} */ @@ -118,6 +128,7 @@ Polymer({ // Update iron-list. this.fingerprints_ = fingerprintInfo.fingerprintsList.slice(); this.$$('.action-button').disabled = fingerprintInfo.isMaxed; + this.allowAddAnotherFinger_ = !fingerprintInfo.isMaxed; }, /** diff --git a/chromium/chrome/browser/resources/settings/people_page/fingerprint_progress_arc.html b/chromium/chrome/browser/resources/settings/people_page/fingerprint_progress_arc.html deleted file mode 100644 index cb18a85530e..00000000000 --- a/chromium/chrome/browser/resources/settings/people_page/fingerprint_progress_arc.html +++ /dev/null @@ -1,23 +0,0 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> - -<dom-module id="settings-fingerprint-progress-arc"> - <template> - <style> - #canvas { - height: 100%; - vertical-align: bottom; - width: 100%; - } - - #canvasDiv { - height: var(--canvas-height); - width: var(--canvas-width); - } - </style> - - <div id="canvasDiv"> - <canvas id="canvas"></canvas> - </div> - </template> - <script src="fingerprint_progress_arc.js"></script> -</dom-module> diff --git a/chromium/chrome/browser/resources/settings/people_page/fingerprint_progress_arc.js b/chromium/chrome/browser/resources/settings/people_page/fingerprint_progress_arc.js deleted file mode 100644 index 2b3c38c77cc..00000000000 --- a/chromium/chrome/browser/resources/settings/people_page/fingerprint_progress_arc.js +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2017 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. - -(function() { - -/** - * The time in milliseconds of the animation updates. - * @type {number} - */ -const ANIMATE_TICKS_MS = 20; - -/** - * The duration in milliseconds of the animation of the progress circle when the - * user is touching the scanner. - * @type {number} - */ -const ANIMATE_DURATION_MS = 200; - -/** - * The radius of the add fingerprint progress circle. - * @type {number} - */ -const CANVAS_CIRCLE_RADIUS = 50; - -/** - * The thickness of the add fingerprint progress circle. - * @type {number} - */ -const CANVAS_CIRCLE_STROKE_WIDTH = 4; - -/** - * The color of the canvas circle background. - * @type {string} - */ -const CANVAS_CIRCLE_BACKGROUND_COLOR = 'rgba(66, 66, 66, 1.0)'; - -/** - * The color of the arc/circle which indicates setup progress. - * @type {string} - */ -const CANVAS_CIRCLE_PROGRESS_COLOR = 'rgba(53, 103, 214, 1.0)'; - -/** - * The color of the canvas circle shadow. - * @type {string} - */ -const CANVAS_CIRCLE_SHADOW_COLOR = 'rgba(0, 0, 0, 0.5)'; - -Polymer({ - is: 'settings-fingerprint-progress-arc', - - // Also put these values as member values so they can be overriden by tests - // and the tests do not need to be changed every time the UI is. - /** @private {number} */ - canvasCircleRadius_: CANVAS_CIRCLE_RADIUS, - /** @private {number} */ - canvasCircleStrokeWidth_: CANVAS_CIRCLE_STROKE_WIDTH, - /** @private {string} */ - canvasCircleBackgroundColor_: CANVAS_CIRCLE_BACKGROUND_COLOR, - /** @private {string} */ - canvasCircleProgressColor_: CANVAS_CIRCLE_PROGRESS_COLOR, - /** @private {string} */ - canvasCircleShadowColor_: CANVAS_CIRCLE_SHADOW_COLOR, - - /** - * Draws an arc on the canvas element around the center with radius - * |CANVAS_CIRCLE_RADIUS|. - * @param {number} startAngle The start angle of the arc we want to draw. - * @param {number} endAngle The end angle of the arc we want to draw. - * @param {string} color The color of the arc we want to draw. The string is - * in the format rgba(r',g',b',a'). r', g', b' are values from [0-255] - * and a' is a value from [0-1]. - */ - drawArc: function(startAngle, endAngle, color) { - const c = this.$.canvas; - const ctx = c.getContext('2d'); - - ctx.beginPath(); - ctx.arc( - c.width / 2, c.height / 2, this.canvasCircleRadius_, startAngle, - endAngle); - ctx.lineWidth = this.canvasCircleStrokeWidth_; - ctx.strokeStyle = color; - ctx.stroke(); - }, - - /** - * Draws a circle on the canvas element around the center with radius - * |CANVAS_CIRCLE_RADIUS| and color |CANVAS_CIRCLE_BACKGROUND_COLOR|. - */ - drawBackgroundCircle: function() { - this.drawArc(0, 2 * Math.PI, this.canvasCircleBackgroundColor_); - }, - - /** - * Draws a circular shadow around the center with radius - * |CANVAS_CIRCLE_RADIUS|. - * @param {number} blur - * @param {number} offsetX - * @param {number} offsetY - */ - drawShadow: function(blur, offsetX, offsetY) { - const c = this.$.canvas; - const ctx = c.getContext('2d'); - - ctx.beginPath(); - ctx.translate(-c.width, 0); - ctx.shadowOffsetX = c.width + offsetX; - ctx.shadowOffsetY = 0; - ctx.shadowColor = this.canvasCircleShadowColor_; - ctx.shadowBlur = blur; - ctx.arc( - c.width / 2, c.height / 2, - this.canvasCircleRadius_ - this.canvasCircleStrokeWidth_ / 2 + blur / 2, - 0, 2 * Math.PI); - ctx.stroke(); - ctx.translate(c.width, 0); - }, - - /** - * Animates the progress the circle. Animates an arc that starts at the top of - * the circle to startAngle, to an arc that starts at the top of the circle to - * endAngle. - * @param {number} startAngle The start angle of the arc we want to draw. - * @param {number} endAngle The end angle of the arc we want to draw. - */ - animateProgress: function(startAngle, endAngle) { - let currentAngle = startAngle; - // The value to update the angle by each tick. - const step = - (endAngle - startAngle) / (ANIMATE_DURATION_MS / ANIMATE_TICKS_MS); - const id = setInterval(doAnimate.bind(this), ANIMATE_TICKS_MS); - // Circles on html canvas have 0 radians on the positive x-axis and go in - // clockwise direction. We want to start at the top of the circle which is - // 3pi/2. - const start = 3 * Math.PI / 2; - - // Function that is called every tick of the interval, draws the arc a bit - // closer to the final destination each tick, until it reaches the final - // destination. - function doAnimate() { - if (currentAngle >= endAngle) - clearInterval(id); - - // Clears the canvas and draws the new progress circle. - this.clearCanvas(); - // Drawing two arcs to form a circle gives a nicer look than drawing an - // arc on top of a circle. If |currentAngle| is 0, draw from |start| + - // |currentAngle| to 7 * Math.PI / 2 (start is 3 * Math.PI / 2) otherwise - // the regular draw from |start| to |currentAngle| will draw nothing which - // will cause a flicker for one frame. - this.drawArc( - start, start + currentAngle, this.canvasCircleProgressColor_); - this.drawArc( - start + currentAngle, currentAngle <= 0 ? 7 * Math.PI / 2 : start, - this.canvasCircleBackgroundColor_); - currentAngle += step; - } - }, - - /** - * Clear the canvas of any renderings. - */ - clearCanvas: function() { - const c = this.$.canvas; - const ctx = c.getContext('2d'); - ctx.clearRect(0, 0, c.width, c.height); - }, -}); -})(); diff --git a/chromium/chrome/browser/resources/settings/people_page/lock_screen.html b/chromium/chrome/browser/resources/settings/people_page/lock_screen.html index 49195e5e113..ef82ae02f76 100644 --- a/chromium/chrome/browser/resources/settings/people_page/lock_screen.html +++ b/chromium/chrome/browser/resources/settings/people_page/lock_screen.html @@ -13,7 +13,7 @@ <link rel="import" href="fingerprint_browser_proxy.html"> <link rel="import" href="lock_screen_constants.html"> <link rel="import" href="lock_state_behavior.html"> -<link rel="import" href="password_prompt_dialog.html"> +<link rel="import" href="lock_screen_password_prompt_dialog.html"> <link rel="import" href="setup_pin_dialog.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../prefs/prefs_behavior.html"> @@ -26,7 +26,7 @@ <template> <style include="settings-shared"> cr-policy-indicator { - -webkit-margin-start: auto; + margin-inline-start: auto; /* Align the indicator with the h2 that it is associated with. */ padding-bottom: 12px; padding-top: 24px; @@ -126,32 +126,50 @@ </div> </template> + <template is="dom-if" if="[[lockScreenNotificationsEnabled_]]"> + <h2 class="settings-box"> + $i18n{lockScreenNotificationTitle} + </h2> + <div class="list-frame"> + <settings-radio-group + pref="{{prefs.ash.message_center.lock_screen_mode}}" + selectable="cr-radio-button"> + <template is="dom-if" + if="[[lockScreenHideSensitiveNotificationSupported_]]"> + <cr-radio-button name="hideSensitive" class="list-item underbar" + pref="[[prefs.ash.message_center.lock_screen_mode]]" + label="$i18n{lockScreenNotificationHideSensitive}"> + </cr-radio-button> + </template> + <cr-radio-button name="show" class="list-item underbar" + pref="[[prefs.ash.message_center.lock_screen_mode]]" + label="$i18n{lockScreenNotificationShow}"> + </cr-radio-button> + <cr-radio-button name="hide" class="list-item" + pref="[[prefs.ash.message_center.lock_screen_mode]]" + label="$i18n{lockScreenNotificationHide}"> + </cr-radio-button> + </settings-radio-group> + </div> + </template> + <template is="dom-if" if="[[fingerprintUnlockEnabled_]]"> - <div id="fingerprintDiv"> - <settings-toggle-button class="continuation" - pref="{{prefs.settings.enable_quick_unlock_fingerprint}}" - label="$i18n{lockScreenFingerprintEnable}"> - </settings-toggle-button> - <iron-collapse - opened="[[prefs.settings.enable_quick_unlock_fingerprint.value]]"> - <div class="settings-box continuation"> - <div class="start"> - $i18n{lockScreenEditFingerprints} - <div class="secondary" id="lockScreenEditFingerprintsSecondary"> - [[getDescriptionText_(numFingerprints_)]] - </div> - </div> - <div class="separator"></div> - <div class="secondary-action"> - <paper-button class="secondary-button" - on-click="onEditFingerprints_" - aria-label="$i18n{lockScreenEditFingerprints}" - aria-descibedby="lockScreenEditFingerprintsSecondary"> - $i18n{lockScreenSetupFingerprintButton} - </paper-button> - </div> + <div id="fingerprintDiv" class="settings-box two-line"> + <div class="start"> + $i18n{lockScreenEditFingerprints} + <div class="secondary" id="lockScreenEditFingerprintsSecondary"> + [[getDescriptionText_(numFingerprints_)]] </div> - </iron-collapse> + </div> + <div class="separator"></div> + <div class="secondary-action"> + <paper-button class="secondary-button" + on-click="onEditFingerprints_" + aria-label="$i18n{lockScreenEditFingerprints}" + aria-descibedby="lockScreenEditFingerprintsSecondary"> + $i18n{lockScreenSetupFingerprintButton} + </paper-button> + </div> </div> </template> @@ -202,10 +220,11 @@ </template> <template is="dom-if" if="[[showPasswordPromptDialog_]]" restamp> - <settings-password-prompt-dialog id="passwordPrompt" + <settings-lock-screen-password-prompt-dialog + id="lockScreenPasswordPrompt" on-close="onPasswordPromptDialogClose_" set-modes="{{setModes_}}" auth-token="{{authToken_}}"> - </settings-password-prompt-dialog> + </settings-lock-screen-password-prompt-dialog> </template> <template is="dom-if" if="[[showSetupPinDialog_]]" restamp> diff --git a/chromium/chrome/browser/resources/settings/people_page/lock_screen.js b/chromium/chrome/browser/resources/settings/people_page/lock_screen.js index 9bc50d77a69..d0af6fa31b1 100644 --- a/chromium/chrome/browser/resources/settings/people_page/lock_screen.js +++ b/chromium/chrome/browser/resources/settings/people_page/lock_screen.js @@ -52,7 +52,7 @@ Polymer({ }, /** - * Authentication token provided by password-prompt-dialog. + * Authentication token provided by lock-screen-password-prompt-dialog. * @private */ authToken_: String, @@ -164,6 +164,32 @@ Polymer({ readOnly: true, }, + /** + * Whether notifications on the lock screen are enable by the feature flag. + * @private + */ + lockScreenNotificationsEnabled_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('lockScreenNotificationsEnabled'); + }, + readOnly: true, + }, + + /** + * Whether the "hide sensitive notification" option on the lock screen can + * be enable by the feature flag. + * @private + */ + lockScreenHideSensitiveNotificationSupported_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean( + 'lockScreenHideSensitiveNotificationsSupported'); + }, + readOnly: true, + }, + /** @private */ showEasyUnlockTurnOffDialog_: { type: Boolean, @@ -391,16 +417,6 @@ Polymer({ return enabled ? enabledStr : disabledStr; }, - /** - * @param {boolean} easyUnlockEnabled - * @param {boolean} proximityDetectionAllowed - * @private - */ - getShowEasyUnlockToggle_: function( - easyUnlockEnabled, proximityDetectionAllowed) { - return easyUnlockEnabled && proximityDetectionAllowed; - }, - /** @private */ updateNumFingerprints_: function() { if (this.fingerprintUnlockEnabled_ && this.fingerprintBrowserProxy_) { diff --git a/chromium/chrome/browser/resources/settings/people_page/lock_screen_password_prompt_dialog.html b/chromium/chrome/browser/resources/settings/people_page/lock_screen_password_prompt_dialog.html new file mode 100644 index 00000000000..c11f2730918 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/people_page/lock_screen_password_prompt_dialog.html @@ -0,0 +1,16 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="lock_screen_constants.html"> +<link rel="import" href="lock_state_behavior.html"> +<link rel="import" href="../controls/password_prompt_dialog.html"> + +<dom-module id="settings-lock-screen-password-prompt-dialog"> + <template> + <settings-password-prompt-dialog + id="passwordPrompt" + password-prompt-text="[[selectPasswordPromptEnterPasswordString_(hasPinLogin)]]" + auth-token="{{authToken}}"> + </settings-password-prompt-dialog> + </template> + <script src="lock_screen_password_prompt_dialog.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/people_page/lock_screen_password_prompt_dialog.js b/chromium/chrome/browser/resources/settings/people_page/lock_screen_password_prompt_dialog.js new file mode 100644 index 00000000000..194495372d5 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/people_page/lock_screen_password_prompt_dialog.js @@ -0,0 +1,119 @@ +// Copyright 2016 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. + +/** + * @fileoverview + * + * 'settings-lock-screen-password-prompt-dialog' displays a password prompt to + * the user. Clients can determine if the user has authenticated if either the + * |authToken| string or |setModes| closure are present. + * + * The setModes binding is a wrapper around chrome.quickUnlockPrivate.setModes + * which has a prebound account password. The account password by itself is not + * available for other elements to access. + * + * Example: + * + * <settings-lock-screen-password-prompt-dialog + * id="lockScreenPasswordPrompt" + * set-modes="[[setModes]]"> + * </settings-lock-screen-password-prompt-dialog> + */ + +(function() { +'use strict'; + +Polymer({ + is: 'settings-lock-screen-password-prompt-dialog', + + behaviors: [ + LockStateBehavior, + ], + + properties: { + /** + * Authentication token returned by quickUnlockPrivate.getAuthToken. Should + * be passed to API calls which require authentication. + * @type {string} + */ + authToken: { + type: String, + notify: true, + observer: 'authTokenChanged_', + }, + + /** + * A wrapper around chrome.quickUnlockPrivate.setModes with the account + * password already supplied. If this is null, the authentication screen + * needs to be redisplayed. This property will be cleared after the timeout + * returned by quickUnlockPrivate.getAuthToken. + * @type {?Function} + */ + setModes: { + type: Object, + notify: true, + }, + + /** + * writeUma_ is a function that handles writing uma stats. It may be + * overridden for tests. + * + * @type {Function} + * @private + */ + writeUma_: { + type: Object, + value: function() { + return settings.recordLockScreenProgress; + } + }, + }, + + /** @override */ + attached: function() { + this.writeUma_(LockScreenProgress.START_SCREEN_LOCK); + }, + + /** + * Called when the authToken changes. If the authToken is valid, that + * indicates the user authenticated successfully. + * @param {String} authToken + * @private + */ + authTokenChanged_: function(authToken) { + if (!this.authToken) { + this.setModes = null; + return; + } + + // The user successfully authenticated. + this.writeUma_(LockScreenProgress.ENTER_PASSWORD_CORRECTLY); + + this.setModes = (modes, credentials, onComplete) => { + this.quickUnlockPrivate.setModes( + this.authToken, modes, credentials, () => { + let result = true; + if (chrome.runtime.lastError) { + console.error( + 'setModes failed: ' + chrome.runtime.lastError.message); + result = false; + } + onComplete(result); + }); + }; + }, + + /** + * Looks up the translation id, which depends on PIN login support. + * @param {boolean} hasPinLogin + * @return {string} + * @private + */ + selectPasswordPromptEnterPasswordString_: function(hasPinLogin) { + if (hasPinLogin) + return this.i18n('passwordPromptEnterPasswordLoginLock'); + return this.i18n('passwordPromptEnterPasswordLock'); + }, +}); +})(); diff --git a/chromium/chrome/browser/resources/settings/people_page/people_page.html b/chromium/chrome/browser/resources/settings/people_page/people_page.html index aeb069ba45e..6f9a04352a8 100644 --- a/chromium/chrome/browser/resources/settings/people_page/people_page.html +++ b/chromium/chrome/browser/resources/settings/people_page/people_page.html @@ -1,8 +1,5 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> @@ -10,7 +7,6 @@ <link rel="import" href="chrome://resources/html/icon.html"> <link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> @@ -25,7 +21,9 @@ <link rel="import" href="../settings_page/settings_subpage.html"> <link rel="import" href="../passwords_and_forms_page/autofill_section.html"> <link rel="import" href="../passwords_and_forms_page/passwords_section.html"> +<link rel="import" href="../passwords_and_forms_page/payments_section.html"> <link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="signout_dialog.html"> <if expr="chromeos"> <link rel="import" href="account_manager.html"> @@ -48,7 +46,7 @@ <style include="settings-shared iron-flex"> setting-box.middle { /* Per spec, middle text is indented 20px in this section. */ - -webkit-margin-start: 20px; + margin-inline-start: 20px; } #profile-icon { @@ -96,23 +94,6 @@ width: 40px; } - #disconnectDialog [slot=footer] .settings-box { - --settings-box-row-padding: 0; - } - - .delete-profile-warning { - -webkit-padding-end: var(--cr-section-padding); - /* In order to line up with the checkbox text. */ - -webkit-padding-start: var(--cr-section-indent-padding); - padding-bottom: 10px; - padding-top: 10px; - } - - #wideFooter { - /* Override the cr-dialog footer padding. */ - padding: 16px 0; - } - <if expr="not chromeos"> #toast { color: white; @@ -231,9 +212,6 @@ <if expr="not chromeos"> </template> <!-- if="[[!diceEnabled_]]" --> </if> - <div class="settings-box" hidden="[[syncStatus.signinAllowed]]"> - $i18n{syncDisabledByAdministrator} - </div> </template> <if expr="not chromeos"> @@ -288,10 +266,10 @@ <cr-link-row id="paymentManagerButton" start-icon="settings20:credit-card" label="$i18n{creditCards}" icon-class="subpage-arrow" - on-click="onAutofillTap_"></cr-link-row> + on-click="onPaymentsTap_"></cr-link-row> <cr-link-row id="addressesManagerButton" start-icon="settings20:location-on" - label="$i18n{addresses}" icon-class="subpage-arrow" + label="$i18n{addressesTitle}" icon-class="subpage-arrow" on-click="onAutofillTap_"></cr-link-row> </template> @@ -323,6 +301,16 @@ aria-describedby="lockScreenSecondary"></button> </paper-icon-button-light> </div> + + <template is="dom-if" if="[[isAccountManagerEnabled_]]"> + <div id="account-manager-subpage-trigger" class="settings-box" + actionable on-click="onAccountManagerTap_"> + <div class="start">$i18n{accountManagerSubMenuLabel}</div> + <paper-icon-button-light class="subpage-arrow"> + <button aria-label="$i18n{accountManagerSubMenuLabel}"></button> + </paper-icon-button-light> + </div> + </template> </if> <div id="manage-other-people-subpage-trigger" @@ -387,6 +375,14 @@ </settings-autofill-section> </settings-subpage> </template> + <template is="dom-if" route-path="/payments"> + <settings-subpage + associated-control="[[$$('#paymentManagerButton')]]" + page-title="$i18n{creditCards}"> + <settings-payments-section id="paymentsSection" prefs="{{prefs}}"> + </settings-payments-section> + </settings-subpage> + </template> </template> <if expr="chromeos"> @@ -441,51 +437,10 @@ </if> </settings-animated-pages> - <template is="dom-if" if="[[showDisconnectDialog_]]" restamp> - <cr-dialog id="disconnectDialog" - ignore-popstate ignore-enter-key - on-close="onDisconnectClosed_" close-text="$i18n{close}"> - <div slot="title">$i18n{syncDisconnectTitle}</div> - <div slot="body"> - <div inner-h-t-m-l="[[ - getDisconnectExplanationHtml_(syncStatus.domain)]]"> - </div> - </div> - <div slot="button-container"> - <paper-button on-click="onDisconnectCancel_" class="cancel-button"> - $i18n{cancel} - </paper-button> - <paper-button id="disconnectConfirm" class="action-button" - hidden="[[syncStatus.domain]]" on-click="onDisconnectConfirm_"> - $i18n{syncDisconnect} - </paper-button> - <paper-button id="disconnectManagedProfileConfirm" - class="action-button" hidden="[[!syncStatus.domain]]" - on-click="onDisconnectConfirm_"> - $i18n{syncDisconnectConfirm} - </paper-button> - </div> -<if expr="(not chromeos and is_posix) or is_win or is_macosx"> - <template is="dom-if" if="[[!syncStatus.domain]]"> - <div id="wideFooter" slot="footer"> - <div class="settings-box first"> - <cr-checkbox id="deleteProfile" class="start" - checked="{{deleteProfile_}}"> - $i18n{syncDisconnectDeleteProfile} - </cr-checkbox> - <cr-expand-button expanded="{{deleteProfileWarningVisible_}}" - alt="$i18n{deleteProfileWarningExpandA11yLabel}"> - </cr-expand-button> - </div> - <iron-collapse opened="[[deleteProfileWarningVisible_]]"> - <div class="delete-profile-warning"> - [[deleteProfileWarning_]] - </div> - </iron-collapse> - </div> - </template> -</if> - </cr-dialog> + <template is="dom-if" if="[[showSignoutDialog_]]" restamp> + <settings-signout-dialog sync-status="[[syncStatus]]" + on-close="onDisconnectDialogClosed_"> + </settings-signout-dialog> </template> <template is="dom-if" if="[[showImportDataDialog_]]" restamp> diff --git a/chromium/chrome/browser/resources/settings/people_page/people_page.js b/chromium/chrome/browser/resources/settings/people_page/people_page.js index 5302d6ad270..e899dd2bf12 100644 --- a/chromium/chrome/browser/resources/settings/people_page/people_page.js +++ b/chromium/chrome/browser/resources/settings/people_page/people_page.js @@ -86,26 +86,6 @@ Polymer({ */ profileName_: String, - /** - * The profile deletion warning. The message indicates the number of - * profile stats that will be deleted if a non-zero count for the profile - * stats is returned from the browser. - * @private - */ - deleteProfileWarning_: String, - - /** - * True if the profile deletion warning is visible. - * @private - */ - deleteProfileWarningVisible_: Boolean, - - /** - * True if the checkbox to delete the profile has been checked. - * @private - */ - deleteProfile_: Boolean, - // <if expr="not chromeos"> /** @private */ showImportDataDialog_: { @@ -115,7 +95,7 @@ Polymer({ // </if> /** @private */ - showDisconnectDialog_: Boolean, + showSignoutDialog_: Boolean, // <if expr="chromeos"> /** @@ -129,6 +109,18 @@ Polymer({ }, readOnly: true, }, + + /** + * True if Chrome OS Account Manager is enabled. + * @private + */ + isAccountManagerEnabled_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('isAccountManagerEnabled'); + }, + readOnly: true, + }, // </if> /** @private {!Map<string, string>} */ @@ -147,7 +139,10 @@ Polymer({ settings.routes.MANAGE_PASSWORDS.path, '#passwordManagerButton'); } if (settings.routes.AUTOFILL) { - map.set(settings.routes.AUTOFILL.path, '#paymentManagerButton'); + map.set(settings.routes.AUTOFILL.path, '#addressesManagerButton'); + } + if (settings.routes.PAYMENTS) { + map.set(settings.routes.PAYMENTS.path, '#paymentManagerButton'); } // <if expr="not chromeos"> if (settings.routes.MANAGE_PROFILE) { @@ -172,6 +167,11 @@ Polymer({ settings.routes.ACCOUNTS.path, '#manage-other-people-subpage-trigger .subpage-arrow button'); } + if (settings.routes.ACCOUNT_MANAGER) { + map.set( + settings.routes.ACCOUNT_MANAGER.path, + '#account-manager-subpage-trigger .subpage-arrow button'); + } // </if> return map; }, @@ -202,9 +202,6 @@ Polymer({ this.addWebUIListener( 'profile-info-changed', this.handleProfileInfo_.bind(this)); - this.addWebUIListener( - 'profile-stats-count-ready', this.handleProfileStatsCount_.bind(this)); - this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance(); this.syncBrowserProxy_.getSyncStatus().then( this.handleSyncStatus_.bind(this)); @@ -223,22 +220,14 @@ Polymer({ settings.getCurrentRoute() == settings.routes.IMPORT_DATA; if (settings.getCurrentRoute() == settings.routes.SIGN_OUT) { - // <if expr="not chromeos"> - settings.ProfileInfoBrowserProxyImpl.getInstance().getProfileStatsCount(); - // </if> // If the sync status has not been fetched yet, optimistically display - // the disconnect dialog. There is another check when the sync status is - // fetched. The dialog will be closed then the user is not signed in. + // the sign-out dialog. There is another check when the sync status is + // fetched. The dialog will be closed when the user is not signed in. if (this.syncStatus && !this.syncStatus.signedIn) { settings.navigateToPreviousRoute(); } else { - this.showDisconnectDialog_ = true; - this.async(() => { - this.$$('#disconnectDialog').showModal(); - }); + this.showSignoutDialog_ = true; } - } else if (this.showDisconnectDialog_) { - this.$$('#disconnectDialog').close(); } }, @@ -276,24 +265,6 @@ Polymer({ }, /** - * Handler for when the profile stats count is pushed from the browser. - * @param {number} count - * @private - */ - handleProfileStatsCount_: function(count) { - this.deleteProfileWarning_ = (count > 0) ? - (count == 1) ? loadTimeData.getStringF( - 'deleteProfileWarningWithCountsSingular', - this.syncStatus.signedInUsername) : - loadTimeData.getStringF( - 'deleteProfileWarningWithCountsPlural', count, - this.syncStatus.signedInUsername) : - loadTimeData.getStringF( - 'deleteProfileWarningWithoutCounts', - this.syncStatus.signedInUsername); - }, - - /** * Handler for when the sync state is pushed from the browser. * @param {?settings.SyncStatus} syncStatus * @private @@ -305,9 +276,6 @@ Polymer({ const shouldRecordSigninImpression = !this.syncStatus && syncStatus && this.showSignin_(syncStatus); - if (!syncStatus.signedIn && this.showDisconnectDialog_) - this.$$('#disconnectDialog').close(); - this.syncStatus = syncStatus; if (shouldRecordSigninImpression && !this.shouldShowSyncAccountControl_()) { @@ -341,7 +309,7 @@ Polymer({ }, /** - * Shows the manage autofill sub page. + * Shows the manage autofill addresses sub page. * @param {!Event} event * @private */ @@ -349,9 +317,18 @@ Polymer({ settings.navigateTo(settings.routes.AUTOFILL); }, + /** + * Shows the manage payment information sub page. + * @param {!Event} event + * @private + */ + onPaymentsTap_: function(event) { + settings.navigateTo(settings.routes.PAYMENTS); + }, + /** @private */ - onDisconnectClosed_: function() { - this.showDisconnectDialog_ = false; + onDisconnectDialogClosed_: function(e) { + this.showSignoutDialog_ = false; // <if expr="not chromeos"> if (!this.diceEnabled_) { // If DICE-enabled, this button won't exist here. @@ -365,7 +342,6 @@ Polymer({ if (settings.getCurrentRoute() == settings.routes.SIGN_OUT) settings.navigateToPreviousRoute(); - this.fire('signout-dialog-closed'); }, /** @private */ @@ -374,27 +350,6 @@ Polymer({ }, /** @private */ - onDisconnectCancel_: function() { - this.$$('#disconnectDialog').close(); - }, - - /** @private */ - onDisconnectConfirm_: function() { - const deleteProfile = !!this.syncStatus.domain || this.deleteProfile_; - // Trigger the sign out event after the navigateToPreviousRoute(). - // So that the navigation to the setting page could be finished before the - // sign out if navigateToPreviousRoute() returns synchronously even the - // browser is closed after the sign out. Otherwise, the navigation will be - // finished during session restore if the browser is closed before the async - // callback executed. - listenOnce(this, 'signout-dialog-closed', () => { - this.syncBrowserProxy_.signOut(deleteProfile); - }); - - this.$$('#disconnectDialog').close(); - }, - - /** @private */ onSyncTap_: function() { // When unified-consent is enabled, users can go to sync subpage regardless // of sync status. @@ -453,6 +408,14 @@ Polymer({ e.preventDefault(); settings.navigateTo(settings.routes.LOCK_SCREEN); }, + + /** + * @param {!Event} e + * @private + */ + onAccountManagerTap_: function(e) { + settings.navigateTo(settings.routes.ACCOUNT_MANAGER); + }, // </if> /** @private */ @@ -502,22 +465,6 @@ Polymer({ /** * @private - * @param {string} domain - * @return {string} - */ - getDisconnectExplanationHtml_: function(domain) { - // <if expr="not chromeos"> - if (domain) { - return loadTimeData.getStringF( - 'syncDisconnectManagedProfileExplanation', - '<span id="managed-by-domain-name">' + domain + '</span>'); - } - // </if> - return loadTimeData.getString('syncDisconnectExplanation'); - }, - - /** - * @private * @param {?settings.SyncStatus} syncStatus * @return {boolean} */ diff --git a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html index 87adadc7479..626028ad75a 100644 --- a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html +++ b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html @@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/chromeos/fingerprint/cr_fingerprint_progress_arc.html"> <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> @@ -10,16 +11,10 @@ <link rel="import" href="../icons.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="fingerprint_browser_proxy.html"> -<link rel="import" href="fingerprint_progress_arc.html"> <dom-module id="settings-setup-fingerprint-dialog"> <template> <style include="settings-shared"> - #arc { - --canvas-height: 200px; - --canvas-width: 450px; - } - #dialog { --cr-dialog-native: { min-width: 500px; @@ -27,30 +22,21 @@ }; } - #image { - height: 80px; - left: 185px; - min-height: 80px; - min-width: 80px; - position: relative; - top: -140px; - width: 80px; + #scannerLocation { + background: url(chrome://theme/IDR_LOGIN_FINGERPRINT_SCANNER_ANIMATION); + background-position: center; + background-repeat: no-repeat; + background-size: 298px 205px; + height: 240px; } - #image[step='3'] { - --iron-icon-fill-color: var(--google-blue-700); - } - - /* Put the image in a separate div with 0 height, otherwise the div will - take the height of the image, leaving us with a row of whitespace when - we position the #image to be inside #arc. */ - #imageDiv { - height: 0; + #messageDiv { + height: 20px; } /* Use this instead of hidden so that the dialog does not resize when the - problem appears or disappears. */ - #problemDiv[invisible] { + message appears or disappears. */ + #messageDiv[invisible] { visibility: hidden; } </style> @@ -58,25 +44,20 @@ <cr-dialog id="dialog" on-close="close" close-text="$i18n{close}"> <div slot="title">$i18n{configureFingerprintTitle}</div> - <div slot="body"> - <div class="settings-box first"> - <span class="middle">[[getInstructionMessage_(step_)]]</span> - </div> - <settings-fingerprint-progress-arc id="arc"> - </settings-fingerprint-progress-arc> - <div id="imageDiv"> - <iron-icon id="image" icon="settings:fingerprint" step$="[[step_]]"> - </iron-icon> + <div slot="body" on-click="fakeScan_"> + <div id="messageDiv" + invisible$="[[!getInstructionMessage_(step_, problemMessage_)]]"> + <span>[[getInstructionMessage_(step_, problemMessage_)]]</span> </div> - <div id="problemDiv" class="settings-box first" - invisible$="[[!problemMessage_]]"> - <iron-icon icon="cr:warning"></iron-icon> - <span class="middle">[[problemMessage_]]</span> + <div id="scannerLocation" hidden="[[!showScannerLocation_(step_)]]"> </div> + <cr-fingerprint-progress-arc id="arc" circle-radius="100" + hidden="[[!showArc_(step_)]]"> + </cr-fingerprint-progress-arc> </div> <div slot="button-container"> <paper-button id="addAnotherButton" on-click="onAddAnotherFingerprint_" - hidden$="[[hideAddAnother_(step_)]]"> + hidden$="[[hideAddAnother_(step_, allowAddAnotherFinger)]]"> $i18n{configureFingerprintAddAnotherButton} </paper-button> diff --git a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js index 2332428b2e3..e41510792dc 100644 --- a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js +++ b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js @@ -17,13 +17,6 @@ settings.FingerprintSetupStep = { (function() { /** - * The duration in ms of a fingerprint icon flash when a user touches the - * fingerprint sensor during an enroll session. - * @type {number} - */ -const FLASH_DURATION_MS = 300; - -/** * The amount of milliseconds after a successful but not completed scan before a * message shows up telling the user to scan their finger again. * @type {number} @@ -37,6 +30,15 @@ Polymer({ properties: { /** + * Whether add another finger is allowed. + * @type {boolean} + */ + allowAddAnotherFinger: { + type: Boolean, + value: true, + }, + + /** * The problem message to display. * @private */ @@ -51,6 +53,20 @@ Polymer({ * @private */ step_: {type: Number, value: settings.FingerprintSetupStep.LOCATE_SCANNER}, + + /** + * The percentage of completion that has been received during setup. + * This is used to approximate the progress of the setup. + * The value within [0, 100] represents the percent of enrollment + * completion. + * @type {number} + * @private + */ + percentComplete_: { + type: Number, + value: 0, + observer: 'onProgressChanged_', + }, }, /** @@ -64,24 +80,13 @@ Polymer({ /** @private {?settings.FingerprintBrowserProxy}*/ browserProxy_: null, - /** - * The percentage of completion that has been received during setup. - * This is used to approximate the progress of the setup. - * The value within [0, 100] represents the percent of enrollment completion. - * @type {number} - * @private - */ - percentComplete_: 0, - /** @override */ attached: function() { this.addWebUIListener( 'on-fingerprint-scan-received', this.onScanReceived_.bind(this)); this.browserProxy_ = settings.FingerprintBrowserProxyImpl.getInstance(); - this.$.arc.clearCanvas(); - this.$.arc.drawBackgroundCircle(); - this.$.arc.drawShadow(10, 0, 0); + this.$.arc.reset(); this.browserProxy_.startEnroll(); this.$.dialog.showModal(); }, @@ -95,9 +100,7 @@ Polymer({ // Note: Reset resets |step_| back to the default, so handle anything that // checks |step_| before resetting. - if (this.step_ == settings.FingerprintSetupStep.READY) - this.fire('add-fingerprint'); - else + if (this.step_ != settings.FingerprintSetupStep.READY) this.browserProxy_.cancelCurrentEnroll(); this.reset_(); @@ -119,7 +122,6 @@ Polymer({ reset_: function() { this.step_ = settings.FingerprintSetupStep.LOCATE_SCANNER; this.percentComplete_ = 0; - this.$.arc.clearCanvas(); this.clearSensorMessageTimeout_(); }, @@ -141,41 +143,21 @@ Polymer({ onScanReceived_: function(scan) { switch (this.step_) { case settings.FingerprintSetupStep.LOCATE_SCANNER: + this.$.arc.reset(); + this.step_ = settings.FingerprintSetupStep.MOVE_FINGER; + this.percentComplete_ = scan.percentComplete; + this.setProblem_(scan.result); + break; case settings.FingerprintSetupStep.MOVE_FINGER: - if (this.step_ == settings.FingerprintSetupStep.LOCATE_SCANNER) { - // Clear canvas because there will be shadows present at this step. - this.$.arc.clearCanvas(); - this.$.arc.drawBackgroundCircle(); - - this.step_ = settings.FingerprintSetupStep.MOVE_FINGER; - this.percentComplete_ = 0; - } - const slice = 2 * Math.PI / 100; if (scan.isComplete) { this.problemMessage_ = ''; this.step_ = settings.FingerprintSetupStep.READY; - this.$.arc.animateProgress( - this.percentComplete_ * slice, 2 * Math.PI); this.clearSensorMessageTimeout_(); + this.fire('add-fingerprint'); } else { this.setProblem_(scan.result); - if (scan.result == settings.FingerprintResultType.SUCCESS) { - this.problemMessage_ = ''; - // Flash the fingerprint icon blue so that users get some feedback - // when a successful scan has been registered. - this.$.image.animate( - { - fill: ['var(--google-blue-700)', 'var(--google-grey-500)'], - opacity: [0.7, 1.0], - }, - FLASH_DURATION_MS); - if (scan.percentComplete > this.percentComplete_) { - this.$.arc.animateProgress( - this.percentComplete_ * slice, scan.percentComplete * slice); - this.percentComplete_ = scan.percentComplete; - } - } } + this.percentComplete_ = scan.percentComplete; break; case settings.FingerprintSetupStep.READY: break; @@ -190,14 +172,15 @@ Polymer({ * on. * @param {!settings.FingerprintSetupStep} step The current step the * fingerprint setup is on. + * @param {string} problemMessage Message for the scan result. * @private */ - getInstructionMessage_: function(step) { + getInstructionMessage_: function(step, problemMessage) { switch (step) { case settings.FingerprintSetupStep.LOCATE_SCANNER: return this.i18n('configureFingerprintInstructionLocateScannerStep'); case settings.FingerprintSetupStep.MOVE_FINGER: - return this.i18n('configureFingerprintInstructionMoveFingerStep'); + return problemMessage; case settings.FingerprintSetupStep.READY: return this.i18n('configureFingerprintInstructionReadyStep'); } @@ -220,20 +203,11 @@ Polymer({ }, SHOW_TAP_SENSOR_MESSAGE_DELAY_MS); break; case settings.FingerprintResultType.PARTIAL: - this.problemMessage_ = this.i18n('configureFingerprintPartialData'); - break; case settings.FingerprintResultType.INSUFFICIENT: - this.problemMessage_ = - this.i18n('configureFingerprintInsufficientData'); - break; case settings.FingerprintResultType.SENSOR_DIRTY: - this.problemMessage_ = this.i18n('configureFingerprintSensorDirty'); - break; case settings.FingerprintResultType.TOO_SLOW: - this.problemMessage_ = this.i18n('configureFingerprintTooSlow'); - break; case settings.FingerprintResultType.TOO_FAST: - this.problemMessage_ = this.i18n('configureFingerprintTooFast'); + this.problemMessage_ = this.i18n('configureFingerprintTryAgain'); break; case settings.FingerprintResultType.IMMOBILE: this.problemMessage_ = this.i18n('configureFingerprintImmobile'); @@ -271,10 +245,12 @@ Polymer({ /** * @param {!settings.FingerprintSetupStep} step + * @param {boolean} allowAddAnotherFinger * @private */ - hideAddAnother_: function(step) { - return step != settings.FingerprintSetupStep.READY; + hideAddAnother_: function(step, allowAddAnotherFinger) { + return step != settings.FingerprintSetupStep.READY || + !allowAddAnotherFinger; }, /** @@ -283,11 +259,41 @@ Polymer({ * @private */ onAddAnotherFingerprint_: function() { - this.fire('add-fingerprint'); this.reset_(); - this.$.arc.drawBackgroundCircle(); - this.$.arc.drawShadow(10, 0, 0); + this.$.arc.reset(); + this.step_ = settings.FingerprintSetupStep.MOVE_FINGER; this.browserProxy_.startEnroll(); }, + + /** + * Whether scanner location should be shown at the current step. + * @private + */ + showScannerLocation_: function() { + return this.step_ == settings.FingerprintSetupStep.LOCATE_SCANNER; + }, + + /** + * Whether fingerprint progress circle should be shown at the current step. + * @private + */ + showArc_: function() { + return this.step_ == settings.FingerprintSetupStep.MOVE_FINGER || + this.step_ == settings.FingerprintSetupStep.READY; + }, + + /** + * Observer for percentComplete_. + * @private + */ + onProgressChanged_: function(newValue, oldValue) { + // Start a new enrollment, so reset all enrollment related states. + if (newValue === 0) { + this.$.arc.reset(); + return; + } + + this.$.arc.setProgress(oldValue, newValue, newValue === 100); + }, }); })(); diff --git a/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.js b/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.js index bddac27f2d1..39840b9fbba 100644 --- a/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.js +++ b/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.js @@ -263,6 +263,8 @@ Polymer({ this.quickUnlockPrivate_.checkCredential( chrome.quickUnlockPrivate.QuickUnlockMode.PIN, this.pinKeyboardValue_, this.processPinProblems_.bind(this)); + } else { + this.enableSubmit_ = false; } return; } diff --git a/chromium/chrome/browser/resources/settings/people_page/signout_dialog.html b/chromium/chrome/browser/resources/settings/people_page/signout_dialog.html new file mode 100644 index 00000000000..7dce40aec69 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/people_page/signout_dialog.html @@ -0,0 +1,78 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="profile_info_browser_proxy.html"> +<link rel="import" href="sync_browser_proxy.html"> +<link rel="import" href="../settings_shared_css.html"> + +<dom-module id="settings-signout-dialog"> + <template> + <style include="settings-shared"> + #dialog [slot=footer] .settings-box { + --settings-box-row-padding: 0; + } + + .delete-profile-warning { + padding-bottom: 10px; + padding-inline-end: var(--cr-section-padding); + /* In order to line up with the checkbox text. */ + padding-inline-start: var(--cr-section-indent-padding); + padding-top: 10px; + } + + #wideFooter { + /* Override the cr-dialog footer padding. */ + padding: 16px 0; + } + </style> + + <cr-dialog id="dialog" ignore-enter-key close-text="$i18n{close}"> + <div slot="title">$i18n{syncDisconnectTitle}</div> + <div slot="body"> + <div inner-h-t-m-l="[[ + getDisconnectExplanationHtml_(syncStatus.domain)]]"> + </div> + </div> + <div slot="button-container"> + <paper-button id="disconnectCancel" class="cancel-button" + on-click="onDisconnectCancel_" > + $i18n{cancel} + </paper-button> + <paper-button id="disconnectConfirm" class="action-button" + hidden="[[syncStatus.domain]]" on-click="onDisconnectConfirm_"> + $i18n{syncDisconnect} + </paper-button> + <paper-button id="disconnectManagedProfileConfirm" + class="action-button" hidden="[[!syncStatus.domain]]" + on-click="onDisconnectConfirm_"> + $i18n{syncDisconnectConfirm} + </paper-button> + </div> +<if expr="(not chromeos and is_posix) or is_win or is_macosx"> + <template is="dom-if" if="[[!syncStatus.domain]]"> + <div id="wideFooter" slot="footer"> + <div class="settings-box first"> + <cr-checkbox id="deleteProfile" class="start" + checked="{{deleteProfile_}}"> + $i18n{syncDisconnectDeleteProfile} + </cr-checkbox> + <cr-expand-button expanded="{{deleteProfileWarningVisible_}}" + alt="$i18n{deleteProfileWarningExpandA11yLabel}"> + </cr-expand-button> + </div> + <iron-collapse opened="[[deleteProfileWarningVisible_]]"> + <div class="delete-profile-warning"> + [[deleteProfileWarning_]] + </div> + </iron-collapse> + </div> + </template> +</if> + </cr-dialog> + </template> + <script src="signout_dialog.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/people_page/signout_dialog.js b/chromium/chrome/browser/resources/settings/people_page/signout_dialog.js new file mode 100644 index 00000000000..d1564f24c31 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/people_page/signout_dialog.js @@ -0,0 +1,120 @@ +// Copyright 2018 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. + +/** + * @fileoverview 'settings-signout-dialog' is a dialog that allows the + * user to turn off sync and sign out of Chromium. + */ +Polymer({ + is: 'settings-signout-dialog', + + behaviors: [WebUIListenerBehavior], + + properties: { + /** + * The current sync status, supplied by the parent. + * @type {?settings.SyncStatus} + */ + syncStatus: { + type: Object, + observer: 'syncStatusChanged_', + }, + + /** + * True if the checkbox to delete the profile has been checked. + * @private + */ + deleteProfile_: Boolean, + + /** + * True if the profile deletion warning is visible. + * @private + */ + deleteProfileWarningVisible_: Boolean, + + /** + * The profile deletion warning. The message indicates the number of + * profile stats that will be deleted if a non-zero count for the profile + * stats is returned from the browser. + * @private + */ + deleteProfileWarning_: String, + }, + + /** @override */ + attached: function() { + this.addWebUIListener( + 'profile-stats-count-ready', this.handleProfileStatsCount_.bind(this)); + // <if expr="not chromeos"> + settings.ProfileInfoBrowserProxyImpl.getInstance().getProfileStatsCount(); + // </if> + this.async(() => { + this.$.dialog.showModal(); + }); + }, + + /** + * Returns true when the user selected 'Confirm'. + * @return {boolean} + */ + wasConfirmed: function() { + return this.$.dialog.getNative().returnValue == 'success'; + }, + + /** + * Handler for when the profile stats count is pushed from the browser. + * @param {number} count + * @private + */ + handleProfileStatsCount_: function(count) { + const username = this.syncStatus.signedInUsername || ''; + if (count == 0) { + this.deleteProfileWarning_ = loadTimeData.getStringF( + 'deleteProfileWarningWithoutCounts', username); + } else if (count == 1) { + this.deleteProfileWarning_ = loadTimeData.getStringF( + 'deleteProfileWarningWithCountsSingular', username); + } else { + this.deleteProfileWarning_ = loadTimeData.getStringF( + 'deleteProfileWarningWithCountsPlural', count, username); + } + }, + + /** + * Polymer observer for syncStatus. + * @private + */ + syncStatusChanged_: function() { + if (!this.syncStatus.signedIn && this.$.dialog.open) + this.$.dialog.close(); + }, + + /** + * @private + * @param {string} domain + * @return {string} + */ + getDisconnectExplanationHtml_: function(domain) { + // <if expr="not chromeos"> + if (domain) { + return loadTimeData.getStringF( + 'syncDisconnectManagedProfileExplanation', + '<span id="managed-by-domain-name">' + domain + '</span>'); + } + // </if> + return loadTimeData.getString('syncDisconnectExplanation'); + }, + + /** @private */ + onDisconnectCancel_: function() { + this.$.dialog.cancel(); + }, + + /** @private */ + onDisconnectConfirm_: function() { + this.$.dialog.close(); + const deleteProfile = !!this.syncStatus.domain || this.deleteProfile_; + settings.SyncBrowserProxyImpl.getInstance().signOut(deleteProfile); + }, +}); diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_account_control.html b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.html index fc1875cbb45..7726f002f48 100644 --- a/chromium/chrome/browser/resources/settings/people_page/sync_account_control.html +++ b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.html @@ -24,7 +24,7 @@ setting-box.middle { /* Per spec, middle text is indented 20px in this section. */ - -webkit-margin-start: 20px; + margin-inline-start: 20px; } .account-icon { @@ -44,7 +44,7 @@ } #menu .dropdown-item span { - -webkit-margin-start: 8px; + margin-inline-start: 8px; } .flex { @@ -54,6 +54,7 @@ } #avatar-container { + height: var(--shown-avatar-size); position: relative; } @@ -192,8 +193,7 @@ <paper-button id="sync-button" class="action-button" hidden="[[syncStatus.signedIn]]" on-click="onSyncButtonTap_" disabled="[[syncStatus.setupInProgress]]"> - [[getSubstituteLabel_( - '$i18nPolymer{syncAsName}', shownAccount_.givenName)]] + $i18n{peopleSignIn} </paper-button> <paper-button id="turn-off" class="secondary-button" hidden="[[!shouldShowTurnOffButton_(syncStatus.signedIn)]]" diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_account_control.js b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.js index 294ad84551f..6baa3667676 100644 --- a/chromium/chrome/browser/resources/settings/people_page/sync_account_control.js +++ b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.js @@ -63,6 +63,14 @@ Polymer({ reflectToAttribute: true, }, + // This property should be set by the parent only and should not change + // after the element is created. + hideButtons: { + type: Boolean, + value: false, + reflectToAttribute: true, + }, + /** @private {boolean} */ shouldShowAvatarRow_: { type: Boolean, @@ -162,25 +170,6 @@ Polymer({ }, /** - * @param {string} syncErrorLabel - * @param {string} authErrorLabel - * @return {string} - * @private - */ - getErrorLabel_: function(syncErrorLabel, authErrorLabel) { - if (this.syncStatus.hasError) { - // Most of the time re-authenticate states are caused by intentional user - // action, so they will be displayed differently as other errors. - return this.syncStatus.statusAction == - settings.StatusAction.REAUTHENTICATE ? - authErrorLabel : - syncErrorLabel; - } - - return ''; - }, - - /** * @param {string} label * @param {string} account * @return {string} @@ -209,7 +198,9 @@ Polymer({ * @private */ getSyncIconStyle_: function() { - if (this.syncStatus.hasError) { + if (!!this.syncStatus.hasUnrecoverableError) + return 'sync-problem'; + if (!!this.syncStatus.hasError) { return this.syncStatus.statusAction == settings.StatusAction.REAUTHENTICATE ? 'sync-paused' : @@ -242,13 +233,16 @@ Polymer({ */ getAvatarRowTitle_: function( accountName, syncErrorLabel, authErrorLabel, disabledLabel) { - if (!!this.syncStatus.disabled) - return disabledLabel; - - if (this.syncStatus.hasError) - return this.getErrorLabel_(syncErrorLabel, authErrorLabel); - - return accountName; + switch (this.getSyncIconStyle_()) { + case 'sync-problem': + return syncErrorLabel; + case 'sync-paused': + return authErrorLabel; + case 'sync-disabled': + return disabledLabel; + default: + return accountName; + } }, /** @@ -256,7 +250,8 @@ Polymer({ * @private */ shouldShowTurnOffButton_: function() { - return !!this.syncStatus.signedIn && !this.embeddedInSubpage; + return !this.hideButtons && !!this.syncStatus.signedIn && + !this.embeddedInSubpage; }, /** @@ -264,8 +259,8 @@ Polymer({ * @private */ shouldShowSigninAgainButton_: function() { - return !!this.syncStatus.signedIn && this.embeddedInSubpage && - !!this.syncStatus.hasError && + return !this.hideButtons && !!this.syncStatus.signedIn && + this.embeddedInSubpage && !!this.syncStatus.hasError && this.syncStatus.statusAction == settings.StatusAction.REAUTHENTICATE; }, diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js index 323a22b48b1..b2ab725ef47 100644 --- a/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js @@ -138,6 +138,11 @@ cr.define('settings', function() { signOut(deleteProfile) {} /** + * Invalidates the Sync token without signing the user out. + */ + pauseSync() {} + + /** * Opens the multi-profile user manager. */ manageOtherPeople() {} @@ -215,6 +220,13 @@ cr.define('settings', function() { * Opens the Google Activity Controls url in a new tab. */ openActivityControlsUrl() {} + + /** + * Function to invoke when the unified consent toggle state changes, to + * notify the C++ layer. + * @param {boolean} toggleChecked + */ + unifiedConsentToggleChanged(toggleChecked) {} } /** @@ -233,6 +245,11 @@ cr.define('settings', function() { } /** @override */ + pauseSync() { + chrome.send('SyncSetupPauseSync'); + } + + /** @override */ manageOtherPeople() { chrome.send('SyncSetupManageOtherPeople'); } @@ -302,6 +319,11 @@ cr.define('settings', function() { chrome.metricsPrivate.recordUserAction( 'Signin_AccountSettings_GoogleActivityControlsClicked'); } + + /** @override */ + unifiedConsentToggleChanged(toggleChecked) { + chrome.send('UnifiedConsentToggleChanged', [toggleChecked]); + } } cr.addSingletonGetter(SyncBrowserProxyImpl); diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_page.html b/chromium/chrome/browser/resources/settings/people_page/sync_page.html index 1e4a02e5852..d1e99ea9e7b 100644 --- a/chromium/chrome/browser/resources/settings/people_page/sync_page.html +++ b/chromium/chrome/browser/resources/settings/people_page/sync_page.html @@ -27,11 +27,17 @@ <dom-module id="settings-sync-page"> <template> <style include="settings-shared iron-flex"> - #create-password-box, - #reset-sync-message-box-encryption, - #reset-sync-message-box-user-events { + #sync-section-toggle:not([actionable]) { + opacity: var(--cr-disabled-opacity); + } + + #create-password-box { /* In order to line up with the encryption radio box text. */ - -webkit-margin-start: var(--settings-indent-width); + margin-inline-start: var(--settings-indent-width); + } + + #create-password-box { + margin-bottom: 1em; } #create-password-box .list-item { @@ -43,14 +49,14 @@ } #existingPassphrase { - /* This particular list frame is not indented. */ - -webkit-padding-start: var(--settings-box-row-padding); border-bottom: var(--settings-separator-line); + /* This particular list frame is not indented. */ + padding-inline-start: var(--settings-box-row-padding); } #submitExistingPassphrase { /* The submit button for the existing passphrase is on the same line. */ - -webkit-margin-start: 16px; + margin-inline-start: 16px; } #passphraseRecoverHint { @@ -65,6 +71,10 @@ border-top: var(--settings-separator-line); } + .passphrase-reset-icon { + margin-right: 8px; + } + <if expr="not chromeos"> #toast { color: white; @@ -79,7 +89,7 @@ </if> </style> <if expr="not chromeos"> - <template is="dom-if" if="[[shouldShowSyncAccountControl_(diceEnabled, + <template is="dom-if" if="[[shouldShowSyncAccountControl_( unifiedConsentEnabled, syncStatus.syncSystemEnabled, syncStatus.signinAllowed)]]"> <settings-sync-account-control embedded-in-subpage @@ -134,7 +144,8 @@ label="$i18n{syncUnifiedConsentToggleTitle}" on-settings-boolean-control-change="onUnifiedConsentToggleChange_" hidden="[[!shouldShowUnifiedConsentToggle_(unifiedConsentEnabled, - syncStatus.disabled, syncStatus.signedIn)]]"> + syncStatus.disabled, syncStatus.signedIn)]]" + disabled="[[syncPrefs.encryptAllData]]"> </settings-toggle-button> <div class="settings-box two-line" id="sync-section-toggle" actionable$="[[!syncSectionDisabled_]]" @@ -352,19 +363,20 @@ <!-- User events is disabled and unchecked if data is encrypted or typed urls are disabled. --> <template is="dom-if" if="[[unifiedConsentEnabled]]"> - <div class="layout horizontal list-item" - hidden="[[!syncPrefs.userEventsRegistered]]"> + <div hidden="[[!syncPrefs.userEventsRegistered]]" + class$="[[getPassphraseHintLines_(syncPrefs.encryptAllData)]] + layout horizontal list-item"> <div class="start" id="userEventsCheckboxLabel"> - <div>$i18n{userEventsCheckboxLabel}</div> + $i18n{userEventsCheckboxLabel} <div class="secondary"> $i18n{userEventsCheckboxText} - </div> - <div id="reset-sync-message-box-user-events" class="list-item" - hidden="[[!syncPrefs.encryptAllData]]"> - <span> - <iron-icon icon="settings:info-outline"></iron-icon> - $i18nRaw{passphraseResetHint} - </span> + <div id="reset-sync-message-box-user-events" + hidden="[[!syncPrefs.encryptAllData]]"> + <iron-icon icon="settings:info-outline" + class="passphrase-reset-icon"> + </iron-icon> + $i18nRaw{passphraseResetHintToggle} + </div> </div> </div> <cr-toggle id="userEventsToggle" @@ -398,8 +410,7 @@ $i18n{personalizeGoogleServicesTitle} </div> <paper-icon-button-light actionable class="icon-external"> - <button aria-label="$i18n{personalizeGoogleServicesTitle}"> - </button> + <button></button> </paper-icon-button-light> </a> @@ -410,16 +421,25 @@ $i18n{manageSyncedDataTitle} </div> <paper-icon-button-light actionable class="icon-external"> - <button aria-label="$i18n{manageSyncedDataTitle}"></button> + <button></button> </paper-icon-button-light> </a> <div id="encryptionDescription" hidden="[[syncPrefs.passphraseRequired]]" - class$="two-line single-column + class$="single-column + [[getPassphraseHintLines_(syncPrefs.encryptAllData)]] [[getListItemClass_(unifiedConsentEnabled)]]"> - <div>$i18n{encryptionOptionsTitle}</div> - <div class="secondary">$i18n{syncDataEncryptedText}</div> + $i18n{encryptionOptionsTitle} + <div class="secondary"> + $i18n{syncDataEncryptedText} + <div hidden="[[!syncPrefs.encryptAllData]]"> + <iron-icon icon="settings:info-outline" + class="passphrase-reset-icon"> + </iron-icon> + $i18nRaw{passphraseResetHintEncryption} + </div> + </div> </div> <div id="encryptionRadioGroupContainer" class="list-frame" @@ -431,31 +451,30 @@ on-paper-radio-group-changed= "onEncryptionRadioSelectionChanged_"> <cr-radio-button name="encrypt-with-google" - class="list-item" disabled="[[syncPrefs.encryptAllData]]"> + class="list-item" disabled="[[syncPrefs.encryptAllData]]" + aria-label="$i18n{encryptWithGoogleCredentialsLabel}"> $i18n{encryptWithGoogleCredentialsLabel} </cr-radio-button> - <cr-radio-button name="encrypt-with-passphrase" - class="list-item" disabled="[[syncPrefs.encryptAllData]]"> - <template is="dom-if" if="[[syncPrefs.fullEncryptionBody]]"> - <span>[[syncPrefs.fullEncryptionBody]]</span> - </template> - <template is="dom-if" if="[[!syncPrefs.fullEncryptionBody]]"> - <span on-click="onLearnMoreTap_"> + <template is="dom-if" if="[[syncPrefs.fullEncryptionBody]]"> + <cr-radio-button name="encrypt-with-passphrase" + class="list-item" disabled="[[syncPrefs.encryptAllData]]" + aria-labelledby="fullEncryptionBody"> + <span id="fullEncryptionBody"> + [[syncPrefs.fullEncryptionBody]] + </span> + </cr-radio-button> + </template> + <template is="dom-if" if="[[!syncPrefs.fullEncryptionBody]]"> + <cr-radio-button name="encrypt-with-passphrase" + class="list-item" disabled="[[syncPrefs.encryptAllData]]" + aria-labelledby="encryptWithSyncPassphraseLabel"> + <span id="encryptWithSyncPassphraseLabel" + on-click="onLearnMoreTap_"> $i18nRaw{encryptWithSyncPassphraseLabel} </span> - </template> - </cr-radio-button> + </cr-radio-button> + </template> </paper-radio-group> - - <!-- duplicated from above --> - <div id="reset-sync-message-box-encryption" class="list-item" - hidden="[[!syncPrefs.encryptAllData]]"> - <span> - <iron-icon icon="settings:info-outline"></iron-icon> - $i18nRaw{passphraseResetHint} - </span> - </div> - </div> <template is="dom-if" if="[[creatingNewPassphrase_]]"> @@ -498,7 +517,7 @@ </div> </div> <cr-expand-button expanded="{{personalizeSectionOpened_}}" - alt="$i18n{syncExpandA11yLabel}"> + alt="$i18n{nonPersonalizedServicesExpandA11yLabel}"> </cr-expand-button> </div> <iron-collapse id="personalize-section" diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_page.js b/chromium/chrome/browser/resources/settings/people_page/sync_page.js index a5520dbebd4..63aaa3b3af1 100644 --- a/chromium/chrome/browser/resources/settings/people_page/sync_page.js +++ b/chromium/chrome/browser/resources/settings/people_page/sync_page.js @@ -88,7 +88,6 @@ Polymer({ /** @type {settings.SyncStatus} */ syncStatus: { type: Object, - observer: 'onSyncStatusChanged_', }, /** @@ -130,6 +129,14 @@ Polymer({ }, /** @private */ + signedIn_: { + type: Boolean, + value: true, + computed: 'computeSignedIn_(syncStatus.signedIn)', + observer: 'onSignedInChanged_', + }, + + /** @private */ syncSectionDisabled_: { type: Boolean, value: false, @@ -229,12 +236,20 @@ Polymer({ * @return {boolean} * @private */ - computeSyncSectionDisabled_() { + computeSignedIn_: function() { + return !!this.syncStatus.signedIn; + }, + + /** + * @return {boolean} + * @private + */ + computeSyncSectionDisabled_: function() { return !!this.unifiedConsentEnabled && (!this.syncStatus.signedIn || !!this.syncStatus.disabled || (!!this.syncStatus.hasError && - this.syncStatus.statusAction === - settings.StatusAction.REAUTHENTICATE)); + this.syncStatus.statusAction !== + settings.StatusAction.ENTER_PASSPHRASE)); }, /** @protected */ @@ -291,7 +306,10 @@ Polymer({ * @private */ onUnifiedConsentToggleChange_: function() { - if(!this.$$('#unifiedConsentToggle').checked){ + const checked = this.$$('#unifiedConsentToggle').checked; + this.browserProxy_.unifiedConsentToggleChanged(checked); + + if (!checked) { this.syncSectionOpened_ = true; this.personalizeSectionOpened_ = true; } @@ -592,9 +610,12 @@ Polymer({ settings.navigateTo(settings.routes.BASIC); }, - /** @private */ - onSyncStatusChanged_: function() { - this.syncSectionOpened_ = !!this.syncStatus.signedIn; + /** + * Collapses/Expands the sync section if the signedIn state has changed. + * @private + */ + onSignedInChanged_: function() { + this.syncSectionOpened_ = !!this.signedIn_; }, /** @@ -639,13 +660,23 @@ Polymer({ return this.unifiedConsentEnabled ? 'list-item' : 'settings-box'; }, + /** + * When there is a sync passphrase, some items have an additional line for the + * passphrase reset hint, making them three lines rather than two. + * @return {string} + * @private + */ + getPassphraseHintLines_: function() { + return this.syncPrefs.encryptAllData ? 'three-line' : 'two-line'; + }, + // <if expr="not chromeos"> /** * @return {boolean} * @private */ shouldShowSyncAccountControl_: function() { - return !!this.diceEnabled && !!this.unifiedConsentEnabled && + return !!this.unifiedConsentEnabled && !!this.syncStatus.syncSystemEnabled && !!this.syncStatus.signinAllowed; }, // </if> diff --git a/chromium/chrome/browser/resources/settings/people_page/user_list.html b/chromium/chrome/browser/resources/settings/people_page/user_list.html index fd03c894e98..1a12c33141c 100644 --- a/chromium/chrome/browser/resources/settings/people_page/user_list.html +++ b/chromium/chrome/browser/resources/settings/people_page/user_list.html @@ -32,7 +32,7 @@ } .user-info { - -webkit-padding-start: 20px; + padding-inline-start: 20px; } </style> <div class="user-list" scrollable> diff --git a/chromium/chrome/browser/resources/settings/people_page/users_page.html b/chromium/chrome/browser/resources/settings/people_page/users_page.html index 52376e685f4..fbf44a44520 100644 --- a/chromium/chrome/browser/resources/settings/people_page/users_page.html +++ b/chromium/chrome/browser/resources/settings/people_page/users_page.html @@ -19,7 +19,7 @@ #add-user-button { /* Add user button must be lined up with the start of users' names. */ - -webkit-margin-start: var(--settings-box-row-indent); + margin-inline-start: var(--settings-box-row-indent); } .block { diff --git a/chromium/chrome/browser/resources/settings/printing_page/BUILD.gn b/chromium/chrome/browser/resources/settings/printing_page/BUILD.gn index c609a9e2437..5452ef37925 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/printing_page/BUILD.gn @@ -8,6 +8,7 @@ js_type_check("closure_compile") { deps = [ ":cloud_printers", ":cups_add_printer_dialog", + ":cups_add_printer_dialog_elements", ":cups_add_printer_dialog_util", ":cups_edit_printer_dialog", ":cups_printers", @@ -34,16 +35,22 @@ js_library("cups_add_printer_dialog") { ] } -js_library("cups_add_printer_dialog_util") { +js_library("cups_add_printer_dialog_elements") { deps = [ ":cups_printers_browser_proxy", - "//ui/webui/resources/cr_elements:cr_scrollable_behavior", + ] +} + +js_library("cups_add_printer_dialog_util") { + deps = [ + "//ui/webui/resources/js:cr", ] } js_library("cups_edit_printer_dialog") { deps = [ ":cups_set_manufacturer_model_behavior", + "//ui/webui/resources/cr_elements:cr_scrollable_behavior", ] } diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html index ce81da6f79c..b0c64325fdc 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html @@ -1,11 +1,13 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html"> <link rel="import" href="chrome://resources/html/md_select_css.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html"> <link rel="import" href="../i18n_setup.html"> +<link rel="import" href="cups_add_printer_dialog_elements.html"> <link rel="import" href="cups_add_printer_dialog_util.html"> <link rel="import" href="cups_printer_shared_css.html"> <link rel="import" href="cups_printers_browser_proxy.html"> @@ -28,9 +30,9 @@ #searchSpinner paper-spinner-lite { --paper-spinner-stroke-width: 2px; - -webkit-margin-end: 3px; - -webkit-margin-start: 20px; height: 15px; + margin-inline-end: 3px; + margin-inline-start: 20px; width: 15px; } </style> @@ -75,7 +77,7 @@ <template> <style include="cups-printer-shared"> #discoverPrintersButton { - -webkit-margin-end: 153px; + margin-inline-end: 153px; } .search-printer-box { @@ -84,9 +86,9 @@ .search-printer-box paper-spinner-lite { --paper-spinner-stroke-width: 2px; - -webkit-margin-end: 3px; - -webkit-margin-start: 20px; height: 15px; + margin-inline-end: 3px; + margin-inline-start: 20px; width: 15px; } @@ -174,16 +176,16 @@ <div slot="dialog-title">$i18n{selectManufacturerAndModelTitle}</div> <div slot="dialog-body"> <div class="settings-box two-line"> - <drop-down-search-box items="[[manufacturerList]]" + <cr-searchable-drop-down items="[[manufacturerList]]" label="$i18n{printerManufacturer}" value="{{activePrinter.ppdManufacturer}}"> - </drop-down-search-box> + </cr-searchable-drop-down> </div> <div class="settings-box two-line"> - <drop-down-search-box items="[[modelList]]" + <cr-searchable-drop-down items="[[modelList]]" label="$i18n{printerModel}" value="{{activePrinter.ppdModel}}"> - </drop-down-search-box> + </cr-searchable-drop-down> </div> <div class="settings-box two-line last"> <cr-input class="browse-file-input" readonly value="[[newUserPPD_]]" diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js index fc6d7f1ad8a..c882c08b391 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js @@ -193,49 +193,13 @@ Polymer({ }, /** - * This function uses regular expressions to determine whether the provided - * printer address is valid. Address can be either an ipv4/6 address or a - * hostname followed by an optional port. - * NOTE: The regular expression for hostnames will allow hostnames that are - * over 255 characters. - * @param {String} name - * @param {String} address + * @param {string} name + * @param {string} address * @return {boolean} Whether the add printer button is enabled. * @private */ canAddPrinter_: function(name, address) { - if (!name || !address) - return false; - - const hostnamePrefix = '([a-z\\d]|[a-z\\d][a-z\\d\\-]{0,61}[a-z\\d])'; - - // Matches an arbitrary number of 'prefix patterns' which are separated by a - // dot. - const hostnameSuffix = `(\\.${hostnamePrefix})*`; - - // Matches an optional port at the end of the address. - const portNumber = '(:\\d+)?'; - - const ipv6Full = '(([a-f\\d]){1,4}(:(:)?([a-f\\d]){1,4}){1,7})'; - - // Special cases for addresses using a shorthand notation. - const ipv6Prefix = '(::([a-f\\d]){1,4})'; - const ipv6Suffix = '(([a-f\\d]){1,4}::)'; - const ipv6Combined = `(${ipv6Full}|${ipv6Prefix}|${ipv6Suffix})`; - const ipv6WithPort = `(\\[${ipv6Combined}\\]${portNumber})`; - - // Matches valid hostnames and ipv4 addresses. - const hostnameRegex = - new RegExp(`^${hostnamePrefix}${hostnameSuffix}${portNumber}$`, 'i'); - - // Matches valid ipv6 addresses. - const ipv6AddressRegex = - new RegExp(`^(${ipv6Combined}|${ipv6WithPort})$`, 'i'); - - const invalidIpv6Regex = new RegExp('.*::.*::.*'); - - return hostnameRegex.test(address) || - (ipv6AddressRegex.test(address) && !invalidIpv6Regex.test(address)); + return settings.printing.isNameAndAddressValid(name, address); }, }); @@ -271,7 +235,8 @@ Polymer({ * @private */ canAddPrinter_: function(ppdManufacturer, ppdModel, printerPPDPath) { - return !!((ppdManufacturer && ppdModel) || printerPPDPath); + return settings.printing.isPPDInfoValid( + ppdManufacturer, ppdModel, printerPPDPath); }, }); diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html new file mode 100644 index 00000000000..695ce6d9ec2 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html @@ -0,0 +1,58 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="cups_printer_shared_css.html"> +<link rel="import" href="cups_printers_browser_proxy.html"> + +<dom-module id="add-printer-list"> + <template> + <style include="cups-printer-shared"> + .list-item { + padding: 0 20px; + } + </style> + <div> + <array-selector id="arraySelector" items="[[printers]]" + selected="{{selectedPrinter}}"> + </array-selector> + <template is="dom-repeat" items="[[printers]]"> + <button class="list-item" on-click="onSelect_"> + [[item.printerName]] + </button> + </template> + </div> + </template> +</dom-module> + +<dom-module id="add-printer-dialog"> + <template> + <style include="settings-shared"> + #dialog { + --cr-dialog-body-container: { + /* Force a bottom border regardless of scroll state. */ + border-bottom: 1px solid var(--paper-grey-300) !important; + }; + } + #dialog [slot=body] { + height: 350px; + padding-inline-end: 0; + padding-inline-start: 0; + } + </style> + + <cr-dialog id="dialog" close-text="$i18n{close}"> + <div slot="title"> + <slot name="dialog-title"></slot> + </div> + <div slot="body"> + <slot name="dialog-body"></slot> + </div> + <div slot="button-container"> + <slot name="dialog-buttons"></slot> + </div> + </cr-dialog> + </template> + <script src="cups_add_printer_dialog_elements.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.js b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.js new file mode 100644 index 00000000000..d27c65f58a8 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.js @@ -0,0 +1,44 @@ +// Copyright 2016 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. + +/** 'add-printers-list' is the list of discovered printers. */ +Polymer({ + is: 'add-printer-list', + + properties: { + /** @type {!Array<!CupsPrinterInfo>} */ + printers: { + type: Array, + notify: true, + }, + + /** @type {!CupsPrinterInfo} */ + selectedPrinter: { + type: Object, + notify: true, + }, + }, + + /** + * @param {{model:Object}} event + * @private + */ + onSelect_: function(event) { + this.selectedPrinter = event.model.item; + }, +}); + +/** 'add-printer-dialog' is the template of the Add Printer dialog. */ +Polymer({ + is: 'add-printer-dialog', + + /** @private */ + attached: function() { + this.$.dialog.showModal(); + }, + + close: function() { + this.$.dialog.close(); + }, +}); diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html index e19f575f0ae..c7d68b6bd61 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html @@ -1,101 +1,2 @@ -<link rel="import" href="chrome://resources/html/polymer.html"> - -<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html"> -<link rel="import" href="chrome://resources/cr_elements/icons.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-dropdown/iron-dropdown.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="cups_printer_shared_css.html"> -<link rel="import" href="cups_printers_browser_proxy.html"> - -<dom-module id="add-printer-list"> - <template> - <style include="cups-printer-shared"> - .list-item { - padding: 0 20px; - } - </style> - <div> - <array-selector id="arraySelector" items="[[printers]]" - selected="{{selectedPrinter}}"> - </array-selector> - <template is="dom-repeat" items="[[printers]]"> - <button class="list-item" on-click="onSelect_"> - [[item.printerName]] - </button> - </template> - </div> - </template> -</dom-module> - -<dom-module id="drop-down-search-box"> - <template> - <style include="cups-printer-shared"> - cr-input { - --cr-input-error-display: none; - } - - iron-dropdown, - cr-input { - width: 270px; - } - - iron-dropdown { - height: 270px; - } - - iron-dropdown [slot='dropdown-content'] { - background-color: white; - box-shadow: 0 2px 6px var(--paper-grey-500); - min-width: 128px; - padding: 8px 0; - } - </style> - <!-- |value| is one-way binding on purpose so that it doesn't change - immediately as the user types. --> - <cr-input label="[[label]]" on-click="onClick_" value="[[value]]" - on-value-changed="onInputValueChanged_" id="search"> - </cr-input> - <iron-dropdown horizontal-align="left" vertical-align="top" - vertical-offset="52"> - <div slot="dropdown-content"> - <template is="dom-repeat" items="[[items]]" - filter="[[filterItems_(searchTerm_)]]"> - <button class="list-item" on-click="onSelect_">[[item]]</button> - </template> - </div> - </iron-dropdown> - </template> -</dom-module> - -<dom-module id="add-printer-dialog"> - <template> - <style include="settings-shared"> - #dialog { - --cr-dialog-body-container: { - /* Force a bottom border regardless of scroll state. */ - border-bottom: 1px solid var(--paper-grey-300) !important; - }; - } - #dialog [slot=body] { - -webkit-padding-end: 0; - -webkit-padding-start: 0; - height: 350px; - } - </style> - - <cr-dialog id="dialog" close-text="$i18n{close}"> - <div slot="title"> - <slot name="dialog-title"></slot> - </div> - <div slot="body" scrollable> - <slot name="dialog-body"></slot> - </div> - <div slot="button-container"> - <slot name="dialog-buttons"></slot> - </div> - </cr-dialog> - </template> - <script src="cups_add_printer_dialog_util.js"></script> -</dom-module> +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="cups_add_printer_dialog_util.js"></script> diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js index 77b61fdcdd3..6cff6ecf1b0 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.js @@ -1,104 +1,71 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2018 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. -/** 'add-printers-list' is the list of discovered printers. */ -Polymer({ - is: 'add-printer-list', - - properties: { - /** @type {!Array<!CupsPrinterInfo>} */ - printers: { - type: Array, - notify: true, - }, - - /** @type {!CupsPrinterInfo} */ - selectedPrinter: { - type: Object, - notify: true, - }, - }, +/** + * @fileoverview Utility functions that are used in Cups printer setup dialogs. + */ +cr.define('settings.printing', function() { /** - * @param {{model:Object}} event - * @private + * Returns true if the printer's name and address is valid. This function + * uses regular expressions to determine whether the provided printer name + * and address are valid. Address can be either an ipv4/6 address or a + * hostname followed by an optional port. + * NOTE: The regular expression for hostnames will allow hostnames that are + * over 255 characters. + * @param {string} name + * @param {string} address + * @return {boolean} */ - onSelect_: function(event) { - this.selectedPrinter = event.model.item; - }, -}); - -/** 'drop-down-search-box' implements a search box with suggestions dropdown. */ -Polymer({ - is: 'drop-down-search-box', - - properties: { - /** @type {!Array<string>} */ - items: { - type: Array, - }, - - /** @type {string} */ - value: { - type: String, - notify: true, - }, - - /** @private {string} */ - searchTerm_: String, + function isNameAndAddressValid(name, address) { + if (!name || !address) + return false; - label: String, - }, + const hostnamePrefix = '([a-z\\d]|[a-z\\d][a-z\\d\\-]{0,61}[a-z\\d])'; - /** - * @param {!Event} event - * @private - */ - onClick_: function(event) { - this.$$('iron-dropdown').open(); - }, + // Matches an arbitrary number of 'prefix patterns' which are separated by a + // dot. + const hostnameSuffix = `(\\.${hostnamePrefix})*`; - /** @private */ - onInputValueChanged_: function() { - this.searchTerm_ = this.$.search.value; - }, + // Matches an optional port at the end of the address. + const portNumber = '(:\\d+)?'; - /** - * @param {{model:Object}} event - * @private - */ - onSelect_: function(event) { - this.$$('iron-dropdown').close(); + const ipv6Full = '(([a-f\\d]){1,4}(:(:)?([a-f\\d]){1,4}){1,7})'; - this.value = event.model.item; - this.searchTerm_ = ''; - }, + // Special cases for addresses using a shorthand notation. + const ipv6Prefix = '(::([a-f\\d]){1,4})'; + const ipv6Suffix = '(([a-f\\d]){1,4}::)'; + const ipv6Combined = `(${ipv6Full}|${ipv6Prefix}|${ipv6Suffix})`; + const ipv6WithPort = `(\\[${ipv6Combined}\\]${portNumber})`; - /** @private */ - filterItems_: function(searchTerm) { - if (!searchTerm) - return null; - return function(item) { - return item.toLowerCase().includes(searchTerm.toLowerCase()); - }; - }, -}); + // Matches valid hostnames and ipv4 addresses. + const hostnameRegex = + new RegExp(`^${hostnamePrefix}${hostnameSuffix}${portNumber}$`, 'i'); -/** 'add-printer-dialog' is the template of the Add Printer dialog. */ -Polymer({ - is: 'add-printer-dialog', + // Matches valid ipv6 addresses. + const ipv6AddressRegex = + new RegExp(`^(${ipv6Combined}|${ipv6WithPort})$`, 'i'); - behaviors: [ - CrScrollableBehavior, - ], + const invalidIpv6Regex = new RegExp('.*::.*::.*'); - /** @private */ - attached: function() { - this.$.dialog.showModal(); - }, + return hostnameRegex.test(address) || + (ipv6AddressRegex.test(address) && !invalidIpv6Regex.test(address)); + } - close: function() { - this.$.dialog.close(); - }, + /** + * Returns true if the printer's manufacturer and model or ppd path is valid. + * @param {string} manufacturer + * @param {string} model + * @param {string} ppdPath + * @return {boolean} + */ + function isPPDInfoValid(manufacturer, model, ppdPath) { + return !!((manufacturer && model) || ppdPath); + } + + return { + isNameAndAddressValid: isNameAndAddressValid, + isPPDInfoValid: isPPDInfoValid, + }; }); diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html b/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html index 0852b6feeea..c544bcf409d 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html @@ -1,7 +1,10 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="cups_add_printer_dialog_elements.html"> <link rel="import" href="cups_add_printer_dialog_util.html"> <link rel="import" href="cups_printer_shared_css.html"> <link rel="import" href="cups_printers_browser_proxy.html"> @@ -20,7 +23,7 @@ </style> <add-printer-dialog> <div slot="dialog-title">$i18n{editPrinterDialogTitle}</div> - <div slot="dialog-body"> + <div slot="dialog-body" scrollable> <div class="settings-box first two-line"> <cr-input class="printer-name-input" autofocus value="{{activePrinter.printerName}}" @@ -81,16 +84,16 @@ </cr-input> </div> <div class="settings-box two-line"> - <drop-down-search-box items="[[manufacturerList]]" + <cr-searchable-drop-down items="[[manufacturerList]]" label="$i18n{printerManufacturer}" value="{{activePrinter.ppdManufacturer}}"> - </drop-down-search-box> + </cr-searchable-drop-down> </div> <div class="settings-box two-line"> - <drop-down-search-box items="[[modelList]]" + <cr-searchable-drop-down items="[[modelList]]" label="$i18n{printerModel}" value="{{activePrinter.ppdModel}}"> - </drop-down-search-box> + </cr-searchable-drop-down> </div> <div class="settings-box two-line last"> <cr-input class="browse-file-input" readonly tabindex="-1" @@ -114,7 +117,8 @@ on-click="onCancelTap_"> $i18n{cancel} </paper-button> - <paper-button class="action-button" on-click="onSaveTap_"> + <paper-button class="action-button" on-click="onSaveTap_" + disabled="[[!canSavePrinter_(activePrinter.*)]]"> $i18n{editPrinterButtonText} </paper-button> </div> diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js b/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js index 7b5543d7715..488afe05f3e 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js @@ -11,6 +11,7 @@ Polymer({ is: 'settings-cups-edit-printer-dialog', behaviors: [ + CrScrollableBehavior, SetManufacturerModelBehavior, ], @@ -135,4 +136,17 @@ Polymer({ isNetworkProtocol_: function(protocol) { return ['ipp', 'ipps', 'http', 'https', 'socket', 'lpd'].includes(protocol); }, + + /** + * @return {boolean} Whether the Save button is enabled. + * @private + */ + canSavePrinter_: function() { + return settings.printing.isNameAndAddressValid( + this.activePrinter.printerName, + this.activePrinter.printerAddress) && + settings.printing.isPPDInfoValid( + this.activePrinter.ppdManufacturer, this.activePrinter.ppdModel, + this.activePrinter.printerPPDPath); + }, }); diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html b/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html index 7f1b7d0bc07..8b3f8c47bb3 100644 --- a/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html +++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html @@ -47,7 +47,7 @@ } [slot='dialog-body'] .settings-box .browse-button { - -webkit-margin-start: 5px; + margin-inline-start: 5px; } [slot='dialog-body'] .last { diff --git a/chromium/chrome/browser/resources/settings/privacy_page/BUILD.gn b/chromium/chrome/browser/resources/settings/privacy_page/BUILD.gn index d2bb5b5324f..b938576c22c 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/privacy_page/BUILD.gn @@ -37,6 +37,8 @@ js_library("privacy_page") { "..:page_visibility", "..:route", "../controls:settings_toggle_button", + "../people_page:signout_dialog", + "../people_page:sync_browser_proxy", "../settings_page:settings_animated_pages", "../site_settings:constants", "../site_settings:site_data_details_subpage", diff --git a/chromium/chrome/browser/resources/settings/privacy_page/personalization_options.html b/chromium/chrome/browser/resources/settings/privacy_page/personalization_options.html index e50aef02669..843f10eeb1d 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/personalization_options.html +++ b/chromium/chrome/browser/resources/settings/privacy_page/personalization_options.html @@ -14,8 +14,8 @@ <style include="settings-shared"> :host(.list-frame) settings-toggle-button, :host(.list-frame) .settings-box { - -webkit-padding-end: 0; - -webkit-padding-start: 0; + padding-inline-end: 0; + padding-inline-start: 0; } :host(.list-frame) settings-toggle-button:first-of-type { diff --git a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html index f14f0e74725..aabd6fb0c8b 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html @@ -11,6 +11,8 @@ <link rel="import" href="../clear_browsing_data_dialog/clear_browsing_data_dialog.html"> <link rel="import" href="../controls/settings_toggle_button.html"> <link rel="import" href="../lifetime_browser_proxy.html"> +<link rel="import" href="../people_page/sync_browser_proxy.html"> +<link rel="import" href="../people_page/signout_dialog.html"> <link rel="import" href="../route.html"> <link rel="import" href="../settings_page/settings_animated_pages.html"> <link rel="import" href="../settings_page/settings_subpage.html"> @@ -29,6 +31,9 @@ <link rel="import" href="../site_settings/zoom_levels.html"> <link rel="import" href="../site_settings_page/site_settings_page.html"> +<if expr="not chromeos"> +<link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html"> +</if> <if expr="use_nss_certs"> <link rel="import" href="chrome://resources/cr_components/certificate_manager/certificate_manager.html"> </if> @@ -38,6 +43,18 @@ <dom-module id="settings-privacy-page"> <template> <style include="settings-shared"> +<if expr="not chromeos"> + #toast { + color: white; + left: 0; + z-index: 1; + } + + :host-context([dir='rtl']) #toast { + left: auto; + right: 0; + } +</if> </style> <template is="dom-if" if="[[showClearBrowsingDataDialog_]]" restamp> <settings-clear-browsing-data-dialog prefs="{{prefs}}" @@ -66,9 +83,22 @@ <settings-animated-pages id="pages" section="privacy" focus-config="[[focusConfig_]]"> <neon-animatable route-path="default"> - <div class="settings-box first"> - <p class="privacy-explanation">$i18nRaw{improveBrowsingExperience}</p> - </div> + <template is="dom-if" if="[[!unifiedConsentEnabled_]]"> + <div class="settings-box first"> + <p class="privacy-explanation"> + $i18nRaw{improveBrowsingExperience} + </p> + </div> + </template> +<if expr="not chromeos"> + <settings-toggle-button id="signinAllowedToggle" + pref="{{prefs.signin.allowed_on_next_startup}}" + label="$i18n{signinAllowedTitle}" + sub-label="$i18n{signinAllowedDescription}" + on-settings-boolean-control-change="onSigninAllowedChange_" + no-set-pref> + </settings-toggle-button> +</if><!-- not chromeos --> <template is="dom-if" if="[[!unifiedConsentEnabled_]]"> <settings-personalization-options prefs="{{prefs}}" page-visibility="[[pageVisibility]]" @@ -173,7 +203,7 @@ <template is="dom-if" route-path="/content/all" no-search> <settings-subpage page-title="$i18n{siteSettingsAllSites}"> - <all-sites></all-sites> + <all-sites focus-config="[[focusConfig_]]"></all-sites> </settings-subpage> </template> <template is="dom-if" route-path="/content/automaticDownloads" no-search> @@ -319,6 +349,15 @@ toggle-on-label="$i18n{siteSettingsSoundAllowRecommended}" category="{{ContentSettingsTypes.SOUND}}"> </category-default-setting> + <settings-toggle-button + id="block-autoplay-setting" + label="$i18n{siteSettingsBlockAutoplaySetting}" + pref="{{blockAutoplayStatus_.pref}}" + disabled="[[!blockAutoplayStatus_.enabled]]" + hidden="[[!enableBlockAutoplayContentSetting_]]" + on-settings-boolean-control-change="onBlockAutoplayToggleChange_" + no-set-pref> + </settings-toggle-button> <category-setting-exceptions category="{{ContentSettingsTypes.SOUND}}" block-header="$i18n{siteSettingsBlockSound}"> @@ -468,7 +507,10 @@ </template> <template is="dom-if" route-path="/content/siteDetails" no-search> <settings-subpage page-title="[[pageTitle]]"> - <site-details page-title="{{pageTitle}}"></site-details> + <site-details + page-title="{{pageTitle}}" + block-autoplay-enabled="[[blockAutoplayStatus_.pref.value]]"> + </site-details> </settings-subpage> </template> <template is="dom-if" route-path="/cookies/detail" no-search> @@ -542,6 +584,21 @@ </template> </template> </settings-animated-pages> + + <template is="dom-if" if="[[showSignoutDialog_]]" restamp> + <settings-signout-dialog sync-status="[[syncStatus]]" + on-close="onSignoutDialogClosed_"> + </settings-signout-dialog> + </template> + +<if expr="not chromeos"> + <cr-toast id="toast" open="[[showRestart_]]"> + <div>$i18n{restartToApplyChanges}</div> + <paper-button on-click="onRestartTap_"> + $i18n{restart} + </paper-button> + </cr-toast> +</if> </template> <script src="privacy_page.js"></script> </dom-module> diff --git a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js index bc30d06c27d..a619084323d 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js +++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js @@ -3,6 +3,14 @@ // found in the LICENSE file. /** + * @typedef {{ + * enabled: boolean, + * pref: !chrome.settingsPrivate.PrefObject + * }} + */ +let BlockAutoplayStatus; + +/** * @fileoverview * 'settings-privacy-page' is the settings page containing privacy and * security settings. @@ -28,6 +36,12 @@ Polymer({ }, /** + * The current sync status, supplied by SyncBrowserProxy. + * @type {?settings.SyncStatus} + */ + syncStatus: Object, + + /** * Dictionary defining page visibility. * @type {!PrivacyPageVisibility} */ @@ -67,6 +81,22 @@ Polymer({ }, /** @private */ + enableBlockAutoplayContentSetting_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('enableBlockAutoplayContentSetting'); + } + }, + + /** @private {BlockAutoplayStatus} */ + blockAutoplayStatus_: { + type: Object, + value: function() { + return /** @type {BlockAutoplayStatus} */ ({}); + } + }, + + /** @private */ enableClipboardContentSetting_: { type: Boolean, value: function() { @@ -139,6 +169,14 @@ Polymer({ return loadTimeData.getBoolean('enableEphemeralFlashPermission'); }, }, + + // <if expr="not chromeos"> + /** @private */ + showRestart_: Boolean, + // </if> + + /** @private */ + showSignoutDialog_: Boolean, }, listeners: { @@ -150,6 +188,29 @@ Polymer({ this.ContentSettingsTypes = settings.ContentSettingsTypes; this.browserProxy_ = settings.PrivacyPageBrowserProxyImpl.getInstance(); + + this.onBlockAutoplayStatusChanged_({ + pref: /** @type {chrome.settingsPrivate.PrefObject} */ ({value: false}), + enabled: false + }); + + this.addWebUIListener( + 'onBlockAutoplayStatusChanged', + this.onBlockAutoplayStatusChanged_.bind(this)); + + settings.SyncBrowserProxyImpl.getInstance().getSyncStatus().then( + this.handleSyncStatus_.bind(this)); + this.addWebUIListener( + 'sync-status-changed', this.handleSyncStatus_.bind(this)); + }, + + /** + * Handler for when the sync state is pushed from the browser. + * @param {?settings.SyncStatus} syncStatus + * @private + */ + handleSyncStatus_: function(syncStatus) { + this.syncStatus = syncStatus; }, /** @protected */ @@ -168,6 +229,25 @@ Polymer({ }, /** + * Called when the block autoplay status changes. + * @param {BlockAutoplayStatus} autoplayStatus + * @private + */ + onBlockAutoplayStatusChanged_: function(autoplayStatus) { + this.blockAutoplayStatus_ = autoplayStatus; + }, + + /** + * Updates the block autoplay pref when the toggle is changed. + * @param {!Event} event + * @private + */ + onBlockAutoplayToggleChange_: function(event) { + const target = /** @type {!SettingsToggleButtonElement} */ (event.target); + this.browserProxy_.setBlockAutoplayEnabled(target.checked); + }, + + /** * Handles the change event for the do-not-track toggle. Shows a * confirmation dialog when enabling the setting. * @param {!Event} event @@ -300,5 +380,40 @@ Polymer({ return value ? this.i18n('siteSettingsProtectedContentEnableIdentifiers') : this.i18n('siteSettingsBlocked'); }, + + /** @private */ + onSigninAllowedChange_: function() { + if (this.syncStatus.signedIn && !this.$.signinAllowedToggle.checked) { + // Switch the toggle back on and show the signout dialog. + this.$.signinAllowedToggle.checked = true; + this.showSignoutDialog_ = true; + } else { + /** @type {!SettingsToggleButtonElement} */ (this.$.signinAllowedToggle) + .sendPrefChange(); + this.showRestart_ = true; + } + }, + + /** @private */ + onSignoutDialogClosed_: function() { + if (/** @type {!SettingsSignoutDialogElement} */ ( + this.$$('settings-signout-dialog')) + .wasConfirmed()) { + this.$.signinAllowedToggle.checked = false; + /** @type {!SettingsToggleButtonElement} */ (this.$.signinAllowedToggle) + .sendPrefChange(); + this.showRestart_ = true; + } + this.showSignoutDialog_ = false; + }, + + /** + * @param {!Event} e + * @private + */ + onRestartTap_: function(e) { + e.stopPropagation(); + settings.LifetimeBrowserProxyImpl.getInstance().restart(); + }, }); })(); diff --git a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js index 63790dfadb2..c1e7d9fc333 100644 --- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js @@ -33,6 +33,9 @@ cr.define('settings', function() { /** @param {boolean} enabled */ setSafeBrowsingExtendedReportingEnabled(enabled) {} + + /** @param {boolean} enabled */ + setBlockAutoplayEnabled(enabled) {} } /** @@ -62,6 +65,11 @@ cr.define('settings', function() { chrome.send('setSafeBrowsingExtendedReportingEnabled', [enabled]); } + /** @override */ + setBlockAutoplayEnabled(enabled) { + chrome.send('setBlockAutoplayEnabled', [enabled]); + } + // <if expr="is_win or is_macosx"> /** @override */ showManageSSLCertificates() { diff --git a/chromium/chrome/browser/resources/settings/route.js b/chromium/chrome/browser/resources/settings/route.js index 7c19ea2f49f..866453bdfdd 100644 --- a/chromium/chrome/browser/resources/settings/route.js +++ b/chromium/chrome/browser/resources/settings/route.js @@ -55,6 +55,7 @@ * NETWORK_DETAIL: (undefined|!settings.Route), * ON_STARTUP: (undefined|!settings.Route), * PASSWORDS: (undefined|!settings.Route), + * PAYMENTS: (undefined|!settings.Route), * PEOPLE: (undefined|!settings.Route), * POINTERS: (undefined|!settings.Route), * POWER: (undefined|!settings.Route), @@ -275,6 +276,7 @@ cr.define('settings', function() { if (autofillHomeEnabled) { r.AUTOFILL = r.PEOPLE.createChild('/autofill'); r.MANAGE_PASSWORDS = r.PEOPLE.createChild('/passwords'); + r.PAYMENTS = r.PEOPLE.createChild('/payments'); } // <if expr="not chromeos"> r.MANAGE_PROFILE = r.PEOPLE.createChild('/manageProfile'); @@ -375,6 +377,7 @@ cr.define('settings', function() { r.ADVANCED.createSection('/passwordsAndForms', 'passwordsAndForms'); r.AUTOFILL = r.PASSWORDS.createChild('/autofill'); r.MANAGE_PASSWORDS = r.PASSWORDS.createChild('/passwords'); + r.PAYMENTS = r.PASSWORDS.createChild('/payments'); } r.LANGUAGES = r.ADVANCED.createSection('/languages', 'languages'); diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html b/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html index 26f24c1a061..b181ac3e909 100644 --- a/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html +++ b/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html @@ -24,7 +24,7 @@ } .favicon-image + span { - -webkit-margin-end: 8px; + margin-inline-end: 8px; } </style> <div class="list-item" focus-row-container> diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html index 4f058e297eb..413a1e34eea 100644 --- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html +++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html @@ -32,7 +32,7 @@ #keyword-column > div, .favicon-image + div { - -webkit-margin-end: 8px; + margin-inline-end: 8px; } #url-column { diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html index 46bf166feff..2770ff9ff3f 100644 --- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html +++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry_css.html @@ -2,11 +2,11 @@ <template> <style> .favicon-image { - -webkit-margin-end: 8px; background-repeat: no-repeat; background-size: contain; display: inline-block; height: 20px; + margin-inline-end: 8px; min-width: 20px; vertical-align: middle; } diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html index 2b6698fe907..6ebb6002de7 100644 --- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html +++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html @@ -38,8 +38,8 @@ } .icon-placeholder { - -webkit-margin-end: 0; - -webkit-margin-start: var(--cr-icon-button-margin-start); + margin-inline-end: 0; + margin-inline-start: var(--cr-icon-button-margin-start); width: var(--cr-icon-ripple-size); } </style> diff --git a/chromium/chrome/browser/resources/settings/search_page/search_page.html b/chromium/chrome/browser/resources/settings/search_page/search_page.html index 34ee2bdf1dc..0fa27b3f0d4 100644 --- a/chromium/chrome/browser/resources/settings/search_page/search_page.html +++ b/chromium/chrome/browser/resources/settings/search_page/search_page.html @@ -34,11 +34,11 @@ } iron-icon { - -webkit-padding-end: 16px; + padding-inline-end: 16px; } .indented { - -webkit-margin-start: var(--settings-indent-width); + margin-inline-start: var(--settings-indent-width); } </style> <settings-animated-pages id="pages" section="search" @@ -47,9 +47,9 @@ <!-- Omnibox search engine --> <div class="settings-box first block"> <div id="search-wrapper"> - <p id="searchExplanation" class="start"> + <div id="searchExplanation" class="start"> $i18nRaw{searchExplanation} - </p> + </div> <template is="dom-if" if="[[isDefaultSearchControlledByPolicy_( prefs.default_search_provider_data.template_url_data)]]"> <cr-policy-pref-indicator pref="[[ diff --git a/chromium/chrome/browser/resources/settings/search_page/search_page.js b/chromium/chrome/browser/resources/settings/search_page/search_page.js index 1b229698d03..8a66f1d2b38 100644 --- a/chromium/chrome/browser/resources/settings/search_page/search_page.js +++ b/chromium/chrome/browser/resources/settings/search_page/search_page.js @@ -150,7 +150,7 @@ Polymer({ */ isAssistantTurnedOn_: function( arcEnabled, valuePropAccepted, assistantFeatureEnabled) { - return (arcEnabled || assistantFeatureEnabled) && valuePropAccepted; + return (arcEnabled && valuePropAccepted) || assistantFeatureEnabled; }, // </if> diff --git a/chromium/chrome/browser/resources/settings/search_settings.js b/chromium/chrome/browser/resources/settings/search_settings.js index 19c3a1e609e..0d068f770e2 100644 --- a/chromium/chrome/browser/resources/settings/search_settings.js +++ b/chromium/chrome/browser/resources/settings/search_settings.js @@ -381,13 +381,13 @@ cr.define('settings', function() { class SearchRequest { /** * @param {string} rawQuery - * @param {!HTMLElement} root + * @param {!Element} root */ constructor(rawQuery, root) { /** @private {string} */ this.rawQuery_ = rawQuery; - /** @private {!HTMLElement} */ + /** @private {!Element} */ this.root_ = root; /** @type {?RegExp} */ @@ -516,7 +516,7 @@ cr.define('settings', function() { class SearchManager { /** * @param {string} text The text to search for. - * @param {!Node} page + * @param {!Element} page * @return {!Promise<!settings.SearchRequest>} A signal indicating that * searching finished. */ @@ -566,11 +566,15 @@ cr.define('settings', function() { }); } } - cr.addSingletonGetter(SearchManagerImpl); + + /** @type {?SearchManager} */ + let instance = null; /** @return {!SearchManager} */ function getSearchManager() { - return SearchManagerImpl.getInstance(); + if (instance === null) + instance = new SearchManagerImpl(); + return instance; } /** @@ -578,7 +582,7 @@ cr.define('settings', function() { * @param {!SearchManager} searchManager */ function setSearchManagerForTesting(searchManager) { - SearchManagerImpl.instance_ = searchManager; + instance = searchManager; } return { diff --git a/chromium/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chromium/chrome/browser/resources/settings/settings_menu/settings_menu.html index 0e4e060b522..f8f22e9b9c9 100644 --- a/chromium/chrome/browser/resources/settings/settings_menu/settings_menu.html +++ b/chromium/chrome/browser/resources/settings/settings_menu/settings_menu.html @@ -23,15 +23,15 @@ --cr-selectable-focus: { outline: auto 5px -webkit-focus-ring-color; }; - -webkit-margin-end: 2px; /* Margin so selected outline is visible. */ - -webkit-margin-start: 1px; - -webkit-padding-start: 23px; /* 24px - 1px from margin for outline. */ align-items: center; color: var(--settings-nav-grey); display: flex; font-weight: 500; + margin-inline-end: 2px; /* Margin so selected outline is visible. */ + margin-inline-start: 1px; min-height: 20px; padding-bottom: 10px; + padding-inline-start: 23px; /* 24px - 1px from margin for outline. */ padding-top: 10px; } @@ -41,7 +41,7 @@ iron-icon { --iron-icon-fill-color: var(--settings-nav-grey); - -webkit-margin-end: 24px; + margin-inline-end: 24px; pointer-events: none; vertical-align: top; } @@ -51,12 +51,12 @@ } #advancedButton { - -webkit-padding-end: 0; background-color: unset; border: none; border-radius: initial; height: unset; margin-top: 8px; + padding-inline-end: 0; text-transform: none; } @@ -77,7 +77,7 @@ #advancedButton > iron-icon, #extensionsLink > iron-icon { @apply --cr-icon-height-width; - -webkit-margin-end: 14px; /* 16px - 2px from margin for outline. */ + margin-inline-end: 14px; /* 16px - 2px from margin for outline. */ } #menuSeparator { diff --git a/chromium/chrome/browser/resources/settings/settings_page/BUILD.gn b/chromium/chrome/browser/resources/settings/settings_page/BUILD.gn index d354bfa4fd3..9114c55812a 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/settings_page/BUILD.gn @@ -46,6 +46,7 @@ js_library("settings_section") { js_library("settings_subpage") { deps = [ ":settings_subpage_search", + "..:find_shortcut_behavior", "..:route", "//third_party/polymer/v1_0/components-chromium/iron-resizable-behavior:iron-resizable-behavior-extracted", "//third_party/polymer/v1_0/components-chromium/neon-animation:neon-animatable-behavior-extracted", diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html b/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html index 655b3faa8a0..10c922d3ff9 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html @@ -4,6 +4,7 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/cr/ui.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> +<link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/slide-from-left-animation.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/slide-from-right-animation.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/slide-left-animation.html"> diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.js b/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.js index 7492cde177e..1fe497be49b 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.js +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.js @@ -32,9 +32,9 @@ Polymer({ /** * A Map specifying which element should be focused when exiting a subpage. * The key of the map holds a settings.Route path, and the value holds - * either a query selector that identifies the desired element or a function - * to be run when a neon-animation-finish event is handled. - * @type {?Map<string, (string|Function)>} + * either a query selector that identifies the desired element, an element + * or a function to be run when a neon-animation-finish event is handled. + * @type {?Map<string, (string|Element|Function)>} */ focusConfig: Object, }, @@ -88,6 +88,9 @@ Polymer({ if (settings.routes.SITE_SETTINGS_SITE_DATA) subpagePaths.push(settings.routes.SITE_SETTINGS_SITE_DATA.path); + if (settings.routes.SITE_SETTINGS_ALL) + subpagePaths.push(settings.routes.SITE_SETTINGS_ALL.path); + // <if expr="chromeos"> if (settings.routes.INTERNET_NETWORKS) subpagePaths.push(settings.routes.INTERNET_NETWORKS.path); @@ -104,20 +107,23 @@ Polymer({ if (!e.detail.item.matches(query)) return; - const selectorOrFunction = this.focusConfig.get(this.previousRoute_.path); - if (selectorOrFunction) { + let pathConfig = this.focusConfig.get(this.previousRoute_.path); + if (pathConfig) { + let handler; + if (typeof pathConfig == 'function') { + handler = pathConfig; + } else { + handler = () => { + if (typeof pathConfig == 'string') + pathConfig = assert(this.querySelector(pathConfig)); + cr.ui.focusWithoutInk(/** @type {!Element} */ (pathConfig)); + }; + } // neon-animatable has "display: none" until the animation finishes, // so calling focus() on any of its children has no effect until // "display:none" is removed. Therefore, don't set focus from within // the currentRouteChanged callback. - listenOnce(this, 'neon-animation-finish', () => { - if (typeof selectorOrFunction == 'function') { - selectorOrFunction(); - } else { - const selector = /** @type {string} */ (selectorOrFunction); - cr.ui.focusWithoutInk(assert(this.querySelector(selector))); - } - }); + listenOnce(this, 'neon-animation-finish', handler); } }, diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_section.html b/chromium/chrome/browser/resources/settings/settings_page/settings_section.html index 24bb6386339..7599a515645 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_section.html +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_section.html @@ -7,11 +7,11 @@ <template> <style> :host { - -webkit-margin-end: 3px; - -webkit-margin-start: 3px; display: flex; flex-direction: column; /* Margin so the box-shadow isn't clipped during animations. */ + margin-inline-end: 3px; + margin-inline-start: 3px; position: relative; } diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html index e736d4fd182..b940d7f9423 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html @@ -7,6 +7,7 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html"> +<link rel="import" href="../find_shortcut_behavior.html"> <link rel="import" href="../icons.html"> <link rel="import" href="../route.html"> <link rel="import" href="settings_subpage_search.html"> @@ -38,8 +39,8 @@ paper-icon-button-light { /* Centers the ripple on the icon with appropriate margin on right. */ - -webkit-margin-end: 10px; - -webkit-margin-start: -10px; + margin-inline-end: 10px; + margin-inline-start: -10px; } paper-spinner-lite { @@ -54,7 +55,7 @@ settings-subpage-search { /* Keep normal icon spacing from subpage-title-extra controls. */ - -webkit-margin-start: 16px; + margin-inline-start: 16px; } </style> <div class="settings-box first" id="headerLine"> diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.js b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.js index 32880610425..82fecc6a084 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.js +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.js @@ -15,6 +15,8 @@ Polymer({ // TODO(michaelpg): phase out NeonAnimatableBehavior. Polymer.NeonAnimatableBehavior, Polymer.IronResizableBehavior, + settings.FindShortcutBehavior, + settings.RouteObserverBehavior, ], properties: { @@ -47,6 +49,13 @@ Polymer({ type: Object, value: null, }, + + /** @private */ + active_: { + type: Boolean, + value: false, + observer: 'onActiveChanged_', + }, }, /** @override */ @@ -71,6 +80,22 @@ Polymer({ this, () => cr.ui.focusWithoutInk(this.$.closeButton)); }, + /** @protected */ + currentRouteChanged: function(route) { + this.active_ = this.getAttribute('route-path') == route.path; + }, + + /** @private */ + onActiveChanged_: function() { + if (!this.searchLabel) + return; + + if (this.active_) + this.becomeActiveFindShortcutListener(); + else + this.removeSelfAsFindShortcutListener(); + }, + /** * Clear the value of the search field. * @param {!Event} e @@ -89,4 +114,15 @@ Polymer({ onSearchChanged_: function(e) { this.searchTerm = e.detail; }, + + // Override settings.FindShortcutBehavior methods. + handleFindShortcut: function(modalContextOpen) { + if (modalContextOpen) + return false; + const subpageSearch = this.$$('settings-subpage-search'); + const searchInput = subpageSearch.getSearchInput(); + if (searchInput != subpageSearch.shadowRoot.activeElement) + searchInput.focus(); + return true; + }, }); diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html index bad6b43ebe5..100316f9b1e 100644 --- a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html +++ b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html @@ -29,29 +29,19 @@ --cr-input-error-display: none; --cr-input-input: { background-color: white; - -webkit-padding-end: 0; - -webkit-padding-start: 0; - padding-bottom: 2px; - padding-top: 2px; border-bottom: 1px solid var(--google-grey-900); } + --cr-input-padding-end: 0; + --cr-input-padding-start: 0; + --cr-input-padding-bottom: 2px; + --cr-input-padding-top: 2px; display: inline-block; vertical-align: middle; width: 160px; /* Special width for search input. */ } :host([has-search-text]) cr-input { - /* Unfortunately we have to duplicate most of this mixin, due to how - redeclaration overrides the entire mixin, but the only value that - changes is padding-end. */ - --cr-input-input: { - background-color: white; - -webkit-padding-end: 20px; - -webkit-padding-start: 0; - padding-bottom: 2px; - padding-top: 2px; - border-bottom: 1px solid var(--google-grey-900); - } + --cr-input-padding-end: 20px; } #searchInput { @@ -64,12 +54,11 @@ } #clearSearchContainer { - -webkit-margin-end: -4px; - -webkit-margin-start: 4px; - /* A 16px icon that fits on the input line. */ background-size: 16px; height: 24px; + margin-inline-end: -4px; + margin-inline-start: 4px; position: absolute; right: 0; width: 24px; diff --git a/chromium/chrome/browser/resources/settings/settings_resources.grd b/chromium/chrome/browser/resources/settings/settings_resources.grd index 12684438ba7..abd10caf92b 100644 --- a/chromium/chrome/browser/resources/settings/settings_resources.grd +++ b/chromium/chrome/browser/resources/settings/settings_resources.grd @@ -368,7 +368,8 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_JS" file="clear_browsing_data_dialog/clear_browsing_data_dialog.js" - type="chrome_html" /> + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_HISTORY_DELETION_DIALOG_HTML" file="clear_browsing_data_dialog/history_deletion_dialog.html" type="chrome_html" /> @@ -411,6 +412,14 @@ <structure name="IDR_SETTINGS_CONTROLS_DROPDOWN_MENU_JS" file="controls/settings_dropdown_menu.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_CONTROLS_PASSWORD_PROMPT_DIALOG_JS" + file="controls/password_prompt_dialog.js" + type="chrome_html" /> + <structure name="IDR_SETTINGS_CONTROLS_PASSWORD_PROMPT_DIALOG_HTML" + file="controls/password_prompt_dialog.html" + type="chrome_html" + preprocess="true" + allowexternalscript="true" /> <structure name="IDR_SETTINGS_CONTROLS_PREF_CONTROL_BEHAVIOR_HTML" file="controls/pref_control_behavior.html" type="chrome_html" /> @@ -567,6 +576,7 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_DOWNLOADS_BROWSER_PROXY_JS" file="downloads_page/downloads_browser_proxy.js" + preprocess="true" type="chrome_html" /> <structure name="IDR_SETTINGS_DOWNLOADS_PAGE_HTML" file="downloads_page/downloads_page.html" @@ -609,8 +619,7 @@ type="chrome_html" /> <structure name="IDR_SETTINGS_FIND_SHORTCUT_BEHAVIOR_JS" file ="find_shortcut_behavior.js" - type="chrome_html" - preprocess="true" /> + type="chrome_html" /> <structure name="IDR_SETTINGS_POWERWASH_DIALOG_HTML" file="reset_page/powerwash_dialog.html" type="chrome_html" /> @@ -761,6 +770,12 @@ <structure name="IDR_SETTINGS_PASSWORDS_EXPORT_DIALOG_JS" file="passwords_and_forms_page/passwords_export_dialog.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_PAYMENTS_SECTION_HTML" + file="passwords_and_forms_page/payments_section.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_PAYMENTS_SECTION_JS" + file="passwords_and_forms_page/payments_section.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_PEOPLE_PAGE_HTML" file="people_page/people_page.html" type="chrome_html" @@ -807,6 +822,14 @@ file="people_page/manage_profile_browser_proxy.js" type="chrome_html" /> </if> + <structure name="IDR_SETTINGS_PEOPLE_PAGE_SIGNOUT_DIALOG_HTML" + file="people_page/signout_dialog.html" + type="chrome_html" + preprocess="true" /> + <structure name="IDR_SETTINGS_PEOPLE_PAGE_SIGNOUT_DIALOG_JS" + file="people_page/signout_dialog.js" + type="chrome_html" + preprocess="true" /> <structure name="IDR_SETTINGS_PEOPLE_PAGE_PROFILE_INFO_BROWSER_PROXY_HTML" file="people_page/profile_info_browser_proxy.html" type="chrome_html" /> @@ -894,6 +917,12 @@ <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_JS" file="printing_page/cups_add_printer_dialog.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_ELEMENTS_HTML" + file="printing_page/cups_add_printer_dialog_elements.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_ELEMENTS_JS" + file="printing_page/cups_add_printer_dialog_elements.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_CUPS_ADD_PRINTER_DIALOG_UTIL_HTML" file="printing_page/cups_add_printer_dialog_util.html" type="chrome_html" /> @@ -998,6 +1027,12 @@ <structure name="IDR_SETTINGS_SITE_LIST_JS" file="site_settings/site_list.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_SITE_LIST_ENTRY_HTML" + file="site_settings/site_list_entry.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_SITE_LIST_ENTRY_JS" + file="site_settings/site_list_entry.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_SETTINGS_BEHAVIOR_HTML" file="site_settings/site_settings_behavior.html" type="chrome_html" /> @@ -1312,12 +1347,24 @@ <structure name="IDR_SETTINGS_MULTIDEVICE_CONSTANTS_JS" file="multidevice_page/multidevice_constants.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_MULTIDEVICE_FEATURE_BEHAVIOR_HTML" + file="multidevice_page/multidevice_feature_behavior.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_MULTIDEVICE_FEATURE_BEHAVIOR_JS" + file="multidevice_page/multidevice_feature_behavior.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_MULTIDEVICE_FEATURE_ITEM_HTML" file="multidevice_page/multidevice_feature_item.html" type="chrome_html" /> <structure name="IDR_SETTINGS_MULTIDEVICE_FEATURE_ITEM_JS" file="multidevice_page/multidevice_feature_item.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_MULTIDEVICE_FEATURE_TOGGLE_HTML" + file="multidevice_page/multidevice_feature_toggle.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_MULTIDEVICE_FEATURE_TOGGLE_JS" + file="multidevice_page/multidevice_feature_toggle.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_MULTIDEVICE_PAGE_HTML" file="multidevice_page/multidevice_page.html" type="chrome_html" /> @@ -1336,18 +1383,18 @@ <structure name="IDR_SETTINGS_MULTIDEVICE_SUBPAGE_JS" file="multidevice_page/multidevice_subpage.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_MULTIDEVICE_TETHER_ITEM_HTML" + file="multidevice_page/multidevice_tether_item.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_MULTIDEVICE_TETHER_ITEM_JS" + file="multidevice_page/multidevice_tether_item.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_NETWORK_PROXY_SECTION_HTML" file="internet_page/network_proxy_section.html" type="chrome_html" /> <structure name="IDR_SETTINGS_NETWORK_PROXY_SECTION_JS" file="internet_page/network_proxy_section.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_LISTENER_BEHAVIOR_HTML" - file="internet_page/network_listener_behavior.html" - type="chrome_html" /> - <structure name="IDR_SETTINGS_NETWORK_LISTENER_BEHAVIOR_JS" - file="internet_page/network_listener_behavior.js" - type="chrome_html" /> <structure name="IDR_SETTINGS_NETWORK_SUMMARY_HTML" file="internet_page/network_summary.html" type="chrome_html" /> @@ -1416,11 +1463,11 @@ type="chrome_html" preprocess="true" allowexternalscript="true" /> - <structure name="IDR_SETTINGS_PEOPLE_PASSWORD_PROMPT_DIALOG_JS" - file="people_page/password_prompt_dialog.js" + <structure name="IDR_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_PROMPT_DIALOG_JS" + file="people_page/lock_screen_password_prompt_dialog.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_PEOPLE_PASSWORD_PROMPT_DIALOG_HTML" - file="people_page/password_prompt_dialog.html" + <structure name="IDR_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_PROMPT_DIALOG_HTML" + file="people_page/lock_screen_password_prompt_dialog.html" type="chrome_html" preprocess="true" allowexternalscript="true" /> @@ -1468,12 +1515,6 @@ <structure name="IDR_SETTINGS_PEOPLE_SETUP_FINGERPRINT_DIALOG_HTML" file="people_page/setup_fingerprint_dialog.html" type="chrome_html" /> - <structure name="IDR_SETTINGS_PEOPLE_FINGERPRINT_PROGRESS_ARC_JS" - file="people_page/fingerprint_progress_arc.js" - type="chrome_html" /> - <structure name="IDR_SETTINGS_PEOPLE_FINGERPRINT_PROGRESS_ARC_HTML" - file="people_page/fingerprint_progress_arc.html" - type="chrome_html" /> <structure name="IDR_SETTINGS_PEOPLE_FINGERPRINT_BROWSER_PROXY_JS" file="people_page/fingerprint_browser_proxy.js" type="chrome_html" /> diff --git a/chromium/chrome/browser/resources/settings/settings_resources_vulcanized.grd b/chromium/chrome/browser/resources/settings/settings_resources_vulcanized.grd index fcd5540c552..46a832e137a 100644 --- a/chromium/chrome/browser/resources/settings/settings_resources_vulcanized.grd +++ b/chromium/chrome/browser/resources/settings/settings_resources_vulcanized.grd @@ -13,8 +13,10 @@ <release seq="1"> <includes> <include name="IDR_MD_SETTINGS_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_MD_SETTINGS_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> <include name="IDR_MD_SETTINGS_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\settings\crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" /> <include name="IDR_MD_SETTINGS_LAZY_LOAD_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_MD_SETTINGS_LAZY_LOAD_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> <include name="IDR_MD_SETTINGS_LAZY_LOAD_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" /> </includes> </release> diff --git a/chromium/chrome/browser/resources/settings/settings_shared_css.html b/chromium/chrome/browser/resources/settings/settings_shared_css.html index 6d0d0090e3b..f78d3de53ca 100644 --- a/chromium/chrome/browser/resources/settings/settings_shared_css.html +++ b/chromium/chrome/browser/resources/settings/settings_shared_css.html @@ -38,7 +38,7 @@ } iron-icon.policy { - -webkit-margin-end: var(--cr-controlled-by-spacing); + margin-inline-end: var(--cr-controlled-by-spacing); } iron-list { @@ -61,7 +61,7 @@ } .separator + paper-icon-button-light { - -webkit-margin-start: var(--cr-icon-ripple-margin); + margin-inline-start: var(--cr-icon-ripple-margin); } neon-animatable { @@ -76,16 +76,16 @@ /* Special case for buttons inside of toggle-buttons. */ .settings-box settings-toggle-button paper-button:last-of-type { - -webkit-margin-end: 16px; + margin-inline-end: 16px; } /* Space out multiple buttons in the same row. */ .settings-box paper-button + paper-button { - -webkit-margin-start: 16px; + margin-inline-start: 16px; } span ~ a { - -webkit-margin-start: 4px; + margin-inline-start: 4px; } a[href] { @@ -196,6 +196,12 @@ min-height: var(--settings-row-two-line-min-height); } + /* A row with three lines of text. Often the lower lines will be + * .secondary. */ + .three-line { + min-height: var(--settings-row-three-line-min-height); + } + /* A settings-box is a horizontal row of text or controls within a * setting section (page or subpage). */ .settings-box { @@ -241,7 +247,7 @@ /* A settings-box that is embedded in another settings-box (e.g. a control * that is associated with a toggle button). */ .settings-box.embedded { - -webkit-padding-start: var(--settings-box-row-indent); + padding-inline-start: var(--settings-box-row-indent); } /* The lower line of text in a two-line row. */ @@ -260,9 +266,9 @@ /* The middle part (horizontally) of a row. */ .settings-box .middle { - -webkit-padding-start: 16px; align-items: center; flex: auto; + padding-inline-start: 16px; } .settings-box .middle.two-line, @@ -296,8 +302,8 @@ iron-list, .list-item { --cr-paper-icon-button-margin: { - -webkit-margin-end: 0; - -webkit-margin-start: var(--cr-icon-button-margin-start); + margin-inline-end: 0; + margin-inline-start: var(--cr-icon-button-margin-start); }; } @@ -310,7 +316,7 @@ * the other way. An example is near the |sign out| button on the People * settings. */ .separator { - -webkit-border-start: var(--settings-separator-line); + border-inline-start: var(--settings-separator-line); flex-shrink: 0; /* Match paper-button's default height. */ height: 32px; diff --git a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js index cfe71c9f412..37ebe94a487 100644 --- a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js +++ b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js @@ -80,15 +80,6 @@ Polymer({ 'refresh-pref': 'onRefreshPref_', }, - /** - * Tracks if any cr-dialog is open anywhere in the UI. An assumption is being - * made that only one cr-dialog is open at a time. If this assumption changes - * |dialogOpen_| should be replaced with a count of the number of dialogs that - * are open. - * @private {boolean} - */ - dialogOpen_: false, - /** @override */ created: function() { settings.initializeRouteFromUrl(); @@ -157,7 +148,7 @@ Polymer({ loadTimeData.getBoolean('androidAppsVisible'); this.showCrostini_ = loadTimeData.valueExists('showCrostini') && loadTimeData.getBoolean('showCrostini'); - this.showMultidevice_ = this.showAndroidApps_ && + this.showMultidevice_ = loadTimeData.valueExists('enableMultideviceSettings') && loadTimeData.getBoolean('enableMultideviceSettings'); this.havePlayStoreApp_ = loadTimeData.valueExists('havePlayStoreApp') && @@ -173,15 +164,6 @@ Polymer({ this.addEventListener('hide-container', () => { this.$.container.style.visibility = 'hidden'; }); - - this.addEventListener('cr-dialog-open', () => { - this.dialogOpen_ = true; - }); - - this.addEventListener('close', e => { - if (e.composedPath()[0].nodeName == 'CR-DIALOG') - this.dialogOpen_ = false; - }); }, /** @override */ @@ -215,6 +197,8 @@ Polymer({ scrollToTop(e.detail.bottom - this.$.container.clientHeight) .then(e.detail.callback); }); + + this.becomeActiveFindShortcutListener(); }, /** @override */ @@ -246,12 +230,11 @@ Polymer({ }, // Override settings.FindShortcutBehavior methods. - canHandleFindShortcut: function() { - return !this.$.drawer.open && !this.dialogOpen_; - }, - - handleFindShortcut: function() { + handleFindShortcut: function(modalContextOpen) { + if (modalContextOpen) + return false; this.$$('cr-toolbar').getSearchField().showAndFocus(); + return true; }, /** diff --git a/chromium/chrome/browser/resources/settings/settings_vars_css.html b/chromium/chrome/browser/resources/settings/settings_vars_css.html index e9236860967..174fcfb2167 100644 --- a/chromium/chrome/browser/resources/settings/settings_vars_css.html +++ b/chromium/chrome/browser/resources/settings/settings_vars_css.html @@ -22,8 +22,8 @@ --settings-error-color: var(--google-red-700); --settings-list-frame-padding: { - -webkit-padding-end: var(--settings-box-row-padding); - -webkit-padding-start: var(--settings-box-row-indent); + padding-inline-end: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-indent); padding-bottom: 0; padding-top: 0; } diff --git a/chromium/chrome/browser/resources/settings/site_settings/BUILD.gn b/chromium/chrome/browser/resources/settings/site_settings/BUILD.gn index ea34fe26d1b..6fe64dd78ec 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/site_settings/BUILD.gn @@ -22,6 +22,7 @@ js_type_check("closure_compile") { ":site_details_permission", ":site_entry", ":site_list", + ":site_list_entry", ":site_settings_behavior", ":site_settings_prefs_browser_proxy", ":usb_devices", @@ -154,6 +155,7 @@ js_library("site_data_entry") { ":local_data_browser_proxy", "..:focus_row_behavior", "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", "//ui/webui/resources/js:icon", ] } @@ -179,6 +181,7 @@ js_library("site_details_permission") { ":site_settings_behavior", "//ui/webui/resources/js:assert", "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", "//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:web_ui_listener_behavior", ] @@ -203,8 +206,8 @@ js_library("site_entry") { js_library("site_list") { deps = [ ":constants", + ":site_list_entry", ":site_settings_behavior", - "..:route", "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu", "//ui/webui/resources/js:assert", "//ui/webui/resources/js:cr", @@ -215,6 +218,17 @@ js_library("site_list") { externs_list = [ "$externs_path/settings_private.js" ] } +js_library("site_list_entry") { + deps = [ + ":constants", + ":site_settings_behavior", + "..:focus_row_behavior", + "..:route", + "//ui/webui/resources/cr_elements/policy:cr_policy_pref_indicator", + "//ui/webui/resources/js:cr", + ] +} + js_library("site_settings_behavior") { deps = [ ":constants", @@ -250,6 +264,7 @@ js_library("website_usage_private_api") { js_library("zoom_levels") { deps = [ ":site_settings_behavior", + "//ui/webui/resources/js:list_property_update_behavior", "//ui/webui/resources/js:web_ui_listener_behavior", ] } diff --git a/chromium/chrome/browser/resources/settings/site_settings/all_sites.html b/chromium/chrome/browser/resources/settings/site_settings/all_sites.html index d08b1bd09ec..6577af68090 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/all_sites.html +++ b/chromium/chrome/browser/resources/settings/site_settings/all_sites.html @@ -22,13 +22,13 @@ } #sortMethod { - -webkit-margin-start: 1em; + margin-inline-start: 1em; } /* There is only one top-level heading for All Sites, so remove the * additional leading padding used for lists. */ .list-frame.without-heading { - -webkit-padding-start: var(--settings-box-row-padding); + padding-inline-start: var(--settings-box-row-padding); } </style> <div id="searchAndSort"> @@ -40,23 +40,28 @@ <label id="sortLabel">$i18n{siteSettingsAllSitesSort}</label> <select id="sortMethod" class="md-select" aria-labelledby="sortLabel" on-change="onSortMethodChanged_"> - <!-- TODO(https://crbug.com/835712): Implement remaining two sort - methods. --> + <option value="[[sortMethods_.mostVisited]]"> + $i18n{siteSettingsAllSitesSortMethodMostVisited} + </option> + <option value="[[sortMethods_.storage]]"> + $i18n{siteSettingsAllSitesSortMethodStorage} + </option> <option value="[[sortMethods_.name]]"> $i18n{siteSettingsAllSitesSortMethodName} </option> </select> </div> </div> - <div class="list-frame" hidden$="[[siteGroupList.length]]"> + <div class="list-frame" hidden$="[[siteGroupMap.size]]"> <div class="list-item secondary">$i18n{noSitesAdded}</div> </div> <div class="list-frame without-heading" id="listContainer"> <iron-list id="allSitesList" - items="[[filterPopulatedList_(siteGroupList, searchQuery_)]]" + items="[[filterPopulatedList_(siteGroupMap, searchQuery_)]]" scroll-target="[[subpageScrollTarget]]"> <template> - <site-entry site-group="[[item]]" list-index="[[index]]"></site-entry> + <site-entry site-group="[[item]]" list-index="[[index]]" + tabindex$="[[tabIndex]]"></site-entry> </template> </iron-list> </div> diff --git a/chromium/chrome/browser/resources/settings/site_settings/all_sites.js b/chromium/chrome/browser/resources/settings/site_settings/all_sites.js index 537bc46b0f3..b424251d1de 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/all_sites.js +++ b/chromium/chrome/browser/resources/settings/site_settings/all_sites.js @@ -19,13 +19,14 @@ Polymer({ properties: { /** - * Array of sites to display in the widget, grouped into their eTLD+1s. - * @type {!Array<!SiteGroup>} + * Map containing sites to display in the widget, grouped into their + * eTLD+1 names. + * @type {!Map<string, !SiteGroup>} */ - siteGroupList: { - type: Array, + siteGroupMap: { + type: Object, value: function() { - return []; + return new Map(); }, }, @@ -51,30 +52,64 @@ Polymer({ /** * All possible sort methods. - * @type {Object} + * @type {!{name: string, mostVisited: string, storage: string}} * @private */ sortMethods_: { type: Object, - value: function() { - return { - name: 'name', - mostVisited: 'most-visited', - storage: 'data-stored', - }; + value: { + name: 'name', + mostVisited: 'most-visited', + storage: 'data-stored', }, readOnly: true, }, + + /** + * Stores the last selected item in the All Sites list. + * @type {?{item: !SiteGroup, index: number}} + * @private + */ + selectedItem_: Object, + + /** + * Used to determine focus between settings pages. + * @type {!Map<string, (string|Function)>} + */ + focusConfig: { + type: Object, + observer: 'focusConfigChanged_', + }, + }, + + /** @private {?settings.LocalDataBrowserProxy} */ + localDataBrowserProxy_: null, + + /** @override */ + created: function() { + this.localDataBrowserProxy_ = + settings.LocalDataBrowserProxyImpl.getInstance(); }, /** @override */ ready: function() { - this.browserProxy_ = - settings.SiteSettingsPrefsBrowserProxyImpl.getInstance(); + this.addWebUIListener( + 'onLocalStorageListFetched', this.onLocalStorageListFetched.bind(this)); this.addWebUIListener( 'contentSettingSitePermissionChanged', this.populateList_.bind(this)); this.addEventListener( - 'site-entry-resized', this.resizeListIfScrollTargetActive_.bind(this)); + 'site-entry-selected', + (/** @type {!{detail: !{item: !SiteGroup, index: number}}} */ e) => { + this.selectedItem_ = e.detail; + }); + this.addEventListener('site-entry-storage-updated', () => { + this.debounce('site-entry-storage-updated', () => { + if (this.sortMethods_ && + this.$.sortMethod.value == this.sortMethods_.storage) { + this.onSortMethodChanged_(); + } + }, 500); + }); this.populateList_(); }, @@ -99,27 +134,62 @@ Polymer({ if (!contentTypes.includes(settings.ContentSettingsTypes.COOKIES)) contentTypes.push(settings.ContentSettingsTypes.COOKIES); - this.browserProxy_.getAllSites(contentTypes).then((response) => { - this.siteGroupList = this.sortSiteGroupList_(response); + this.browserProxy.getAllSites(contentTypes).then((response) => { + response.forEach(siteGroup => { + this.siteGroupMap.set(siteGroup.etldPlus1, siteGroup); + }); + this.forceListUpdate_(); + }); + }, + + /** + * Integrate sites using local storage into the existing sites map, as there + * may be overlap between the existing sites. + * @param {!Array<!SiteGroup>} list The list of sites using local storage. + */ + onLocalStorageListFetched: function(list) { + list.forEach(storageSiteGroup => { + if (this.siteGroupMap.has(storageSiteGroup.etldPlus1)) { + const siteGroup = this.siteGroupMap.get(storageSiteGroup.etldPlus1); + const storageOriginInfoMap = new Map(); + storageSiteGroup.origins.forEach( + originInfo => + storageOriginInfoMap.set(originInfo.origin, originInfo)); + + // If there is an overlapping origin, update the original + // |originInfo|. + siteGroup.origins.forEach(originInfo => { + if (!storageOriginInfoMap.has(originInfo.origin)) + return; + Object.apply(originInfo, storageOriginInfoMap.get(originInfo.origin)); + storageOriginInfoMap.delete(originInfo.origin); + }); + // Otherwise, add it to the list. + storageOriginInfoMap.forEach( + originInfo => siteGroup.origins.push(originInfo)); + } else { + this.siteGroupMap.set(storageSiteGroup.etldPlus1, storageSiteGroup); + } }); + this.forceListUpdate_(); }, /** - * Filters |this.siteGroupList| with the given search query text. - * @param {!Array<!SiteGroup>} siteGroupList The list of sites to filter. + * Filters the all sites list with the given search query text. + * @param {!Map<string, !SiteGroup>} siteGroupMap The map of sites to filter. * @param {string} searchQuery The filter text. * @return {!Array<!SiteGroup>} * @private */ - filterPopulatedList_: function(siteGroupList, searchQuery) { - if (searchQuery.length == 0) - return siteGroupList; - - return siteGroupList.filter((siteGroup) => { - return siteGroup.origins.find(origin => { - return origin.includes(searchQuery); - }); - }); + filterPopulatedList_: function(siteGroupMap, searchQuery) { + const result = []; + for (const [etldPlus1, siteGroup] of siteGroupMap) { + if (siteGroup.origins.find( + originInfo => originInfo.origin.includes(searchQuery))) { + result.push(siteGroup); + } + } + return this.sortSiteGroupList_(result); }, /** @@ -130,11 +200,89 @@ Polymer({ */ sortSiteGroupList_: function(siteGroupList) { const sortMethod = this.$.sortMethod.value; - if (sortMethod == this.sortMethods_.name) + if (!this.sortMethods_) + return siteGroupList; + + if (sortMethod == this.sortMethods_.mostVisited) { + siteGroupList.sort(this.mostVisitedComparator_); + } else if (sortMethod == this.sortMethods_.storage) { + // Storage is loaded asynchronously, so make sure it's updated for every + // item in the list to ensure the sorting is correct. + const etldPlus1List = siteGroupList.reduce((list, siteGroup) => { + if (siteGroup.origins.length > 1 && siteGroup.etldPlus1.length > 0) + list.push(siteGroup.etldPlus1); + return list; + }, []); + + this.localDataBrowserProxy_.getNumCookiesList(etldPlus1List) + .then(numCookiesList => { + assert(etldPlus1List.length == numCookiesList.length); + numCookiesList.forEach(cookiesPerEtldPlus1 => { + this.siteGroupMap.get(cookiesPerEtldPlus1.etldPlus1).numCookies = + cookiesPerEtldPlus1.numCookies; + }); + + // |siteGroupList| by this point should have already been provided + // to the iron list, so just sort in-place here and make sure to + // re-render the item order. + siteGroupList.sort(this.storageComparator_); + this.$.allSitesList.fire('iron-resize'); + }); + } else if (sortMethod == this.sortMethods_.name) { siteGroupList.sort(this.nameComparator_); + } return siteGroupList; }, + /** + * Comparator used to sort SiteGroups by the amount of engagement the user has + * with the origins listed inside it. Note only the maximum engagement is used + * for each SiteGroup (as opposed to the sum) in order to prevent domains with + * higher numbers of origins from always floating to the top of the list. + * @param {!SiteGroup} siteGroup1 + * @param {!SiteGroup} siteGroup2 + * @private + */ + mostVisitedComparator_: function(siteGroup1, siteGroup2) { + const getMaxEngagement = (max, originInfo) => { + return (max > originInfo.engagement) ? max : originInfo.engagement; + }; + const score1 = siteGroup1.origins.reduce(getMaxEngagement, 0); + const score2 = siteGroup2.origins.reduce(getMaxEngagement, 0); + return score2 - score1; + }, + + /** + * Comparator used to sort SiteGroups by the amount of storage they use. Note + * this sorts in descending order. + * TODO(https://crbug.com/835712): Account for website storage in sorting by + * storage used. + * @param {!SiteGroup} siteGroup1 + * @param {!SiteGroup} siteGroup2 + * @private + */ + storageComparator_: function(siteGroup1, siteGroup2) { + const getOverallUsage = siteGroup => { + let usage = 0; + siteGroup.origins.forEach(originInfo => { + usage += originInfo.usage; + }); + return usage; + }; + + const siteGroup1Size = getOverallUsage(siteGroup1); + const siteGroup2Size = getOverallUsage(siteGroup2); + // Use the number of cookies as a tie breaker. + return siteGroup2Size - siteGroup1Size || + siteGroup2.numCookies - siteGroup1.numCookies; + }, + + /** + * Comparator used to sort SiteGroups by their eTLD+1 name (domain). + * @param {!SiteGroup} siteGroup1 + * @param {!SiteGroup} siteGroup2 + * @private + */ nameComparator_: function(siteGroup1, siteGroup2) { return siteGroup1.etldPlus1.localeCompare(siteGroup2.etldPlus1); }, @@ -154,18 +302,52 @@ Polymer({ * @private */ onSortMethodChanged_: function() { - this.siteGroupList = this.sortSiteGroupList_(this.siteGroupList); + this.$.allSitesList.items = + this.sortSiteGroupList_(this.$.allSitesList.items); // Force the iron-list to rerender its items, as the order has changed. this.$.allSitesList.fire('iron-resize'); }, /** - * Called when a list item changes its size, and thus the positions and sizes - * of the items in the entire list also need updating. + * Forces the all sites list to update its list of items, taking into account + * the search query and the sort method, then re-renders it. + * @private + */ + forceListUpdate_: function() { + this.$.allSitesList.items = + this.filterPopulatedList_(this.siteGroupMap, this.searchQuery_); + this.$.allSitesList.fire('iron-resize'); + }, + + /** + * @param {!Map<string, (string|Function)>} newConfig + * @param {?Map<string, (string|Function)>} oldConfig * @private */ - resizeListIfScrollTargetActive_: function() { - if (settings.getCurrentRoute() == this.subpageRoute) - this.$.allSitesList.fire('iron-resize'); + focusConfigChanged_: function(newConfig, oldConfig) { + // focusConfig is set only once on the parent, so this observer should only + // fire once. + assert(!oldConfig); + + if (!settings.routes.SITE_SETTINGS_ALL) + return; + + const onNavigatedTo = () => { + this.async(() => { + if (this.selectedItem_ == null || this.siteGroupMap.size == 0) + return; + + // Focus the site-entry to ensure the iron-list renders it, otherwise + // the query selector will not be able to find it. Note the index is + // used here instead of the item, in case the item was already removed. + const index = Math.max( + 0, Math.min(this.selectedItem_.index, this.siteGroupMap.size)); + this.$.allSitesList.focusItem(index); + this.selectedItem_ = null; + }); + }; + + this.focusConfig.set( + settings.routes.SITE_SETTINGS_SITE_DETAILS.path, onNavigatedTo); }, }); diff --git a/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js b/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js index f23a71fe70c..19db72fabf6 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/site_settings/local_data_browser_proxy.js @@ -33,6 +33,15 @@ let LocalDataItem; */ let LocalDataList; +/** + * Number of cookies attached to a given domain / eTLD+1. + * @typedef {{ + * etldPlus1: string, + * numCookies: number, + * }} + */ +let EtldPlus1CookieNumber; + cr.define('settings', function() { /** @interface */ class LocalDataBrowserProxy { @@ -69,11 +78,20 @@ cr.define('settings', function() { getCookieDetails(site) {} /** - * Gets the number of cookies formatted in a plural string, given a site. - * @param {string} site The site to count cookies for. + * Gets a list containing the number of cookies for each domain (eTLD+1 + * names) given in |siteList|. This will always return a result array the + * same length and in the same order as |siteList|. + * @param {!Array<string>} siteList The list of sites to count cookies for. + * @return {!Promise<!Array<!EtldPlus1CookieNumber>>} + */ + getNumCookiesList(siteList) {} + + /** + * Gets the plural string for a given number of cookies. + * @param {number} numCookies The number of cookies. * @return {!Promise<string>} */ - getNumCookiesString(site) {} + getNumCookiesString(numCookies) {} /** * Reloads all local data. @@ -120,8 +138,13 @@ cr.define('settings', function() { } /** @override */ - getNumCookiesString(site) { - return cr.sendWithPromise('localData.getNumCookiesString', site); + getNumCookiesList(siteList) { + return cr.sendWithPromise('localData.getNumCookiesList', siteList); + } + + /** @override */ + getNumCookiesString(numCookies) { + return cr.sendWithPromise('localData.getNumCookiesString', numCookies); } /** @override */ diff --git a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html index 70aa00c55f8..cfea2a1c20e 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html +++ b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html @@ -18,8 +18,8 @@ } .column-header { - -webkit-margin-start: 20px; margin-bottom: 15px; + margin-inline-start: 20px; margin-top: 15px; } </style> diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_data.html b/chromium/chrome/browser/resources/settings/site_settings/site_data.html index 78e9124cba9..4fa8b7af5ac 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data.html @@ -33,7 +33,7 @@ } #removeShowingSites { - -webkit-margin-start: auto; + margin-inline-start: auto; } </style> <div class="settings-box continuation"> diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_data.js b/chromium/chrome/browser/resources/settings/site_settings/site_data.js index a1caa294965..235ea12a488 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_data.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_data.js @@ -158,13 +158,7 @@ Polymer({ lastSelectedIndex : this.sites.length - 1; const index = indexFromId > -1 ? indexFromId : indexFallback; - const ironList = - /** @type {!IronListElement} */ (this.$$('iron-list')); - ironList.focusItem(index); - const siteToSelect = this.sites[index].site.replace(/[.]/g, '\\.'); - const button = - this.$$(`#siteItem_${siteToSelect}`).$$('.subpage-arrow button'); - cr.ui.focusWithoutInk(assert(button)); + this.focusOnSiteSelectButton_(index); }); this.focusConfig.set( settings.routes.SITE_SETTINGS_DATA_DETAILS.path, onNavigatedTo); @@ -172,6 +166,20 @@ Polymer({ }, /** + * @param {number} index + * @private + */ + focusOnSiteSelectButton_: function(index) { + const ironList = + /** @type {!IronListElement} */ (this.$$('iron-list')); + ironList.focusItem(index); + const siteToSelect = this.sites[index].site.replace(/[.]/g, '\\.'); + const button = + this.$$(`#siteItem_${siteToSelect}`).$$('.subpage-arrow button'); + cr.ui.focusWithoutInk(assert(button)); + }, + + /** * Gather all the site data. * @private */ @@ -239,6 +247,10 @@ Polymer({ * @private */ onSiteClick_: function(event) { + // If any delete button is selected, the focus will be in a bad state when + // returning to this page. To avoid this, the site select button is given + // focus. See https://crbug.com/872197. + this.focusOnSiteSelectButton_(event.model.index); settings.navigateTo( settings.routes.SITE_SETTINGS_DATA_DETAILS, new URLSearchParams('site=' + event.model.item.site)); diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_details.html b/chromium/chrome/browser/resources/settings/site_settings/site_details.html index 5741588cba2..730957b9612 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.html @@ -22,15 +22,15 @@ } #storage { - -webkit-padding-end: 0; + padding-inline-end: 0; } /* When 'Usage' is omitted, subheadings are removed. Reduce the start * padding allowed for lists without headings and add back vertical space * that would normally be provided by the subheading. */ .list-frame.without-heading { - -webkit-padding-start: var(--settings-box-row-padding); margin-top: 12px; + padding-inline-start: var(--settings-box-row-padding); } div#resetSettingsButton { @@ -148,7 +148,8 @@ </site-details-permission> <site-details-permission category="{{ContentSettingsTypes.SOUND}}" icon="settings:volume-up" id="sound" - label="$i18n{siteSettingsSound}"> + label="$i18n{siteSettingsSound}" + use-automatic-label="[[blockAutoplayEnabled]]"> </site-details-permission> <site-details-permission category="{{ContentSettingsTypes.AUTOMATIC_DOWNLOADS}}" diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_details.js b/chromium/chrome/browser/resources/settings/site_settings/site_details.js index 155cc28763b..8906a2a47ea 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.js @@ -17,6 +17,11 @@ Polymer({ properties: { /** + * Whether unified autoplay blocking is enabled. + */ + blockAutoplayEnabled: Boolean, + + /** * The origin that this widget is showing details for. * @private */ @@ -72,6 +77,9 @@ Polymer({ this.addWebUIListener( 'prefEnableDrmChanged', this.prefEnableDrmChanged_.bind(this)); // </if> + + // Refresh block autoplay status from the backend. + this.browserProxy.fetchBlockAutoplayStatus(); }, /** @override */ diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.html index 3e305be016a..9da49da8d4e 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.html @@ -3,6 +3,7 @@ <link rel="import" href="chrome://resources/html/md_select_css.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="../settings_vars_css.html"> <link rel="import" href="constants.html"> @@ -53,16 +54,18 @@ <option id="default" value$="[[ContentSetting.DEFAULT]]"> [[defaultSettingString_( defaultSetting_, - '$i18nPolymer{siteSettingsActionAskDefault}', - '$i18nPolymer{siteSettingsActionAllowDefault}', - '$i18nPolymer{siteSettingsActionBlockDefault}')]] + category, + useAutomaticLabel)]] </option> <option id="allow" value$="[[ContentSetting.ALLOW]]" hidden$="[[!showAllowedSetting_(category)]]"> $i18n{siteSettingsActionAllow} </option> <option id="block" value$="[[ContentSetting.BLOCK]]"> - $i18n{siteSettingsActionBlock} + [[blockSettingString_( + category, + '$i18n{siteSettingsActionBlock}', + '$i18n{siteSettingsActionMute}')]] </option> <option id="ask" value$="[[ContentSetting.ASK]]" hidden$="[[!showAskSetting_(category, site.setting, diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.js b/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.js index d559c00baae..8802f151211 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_details_permission.js @@ -10,10 +10,17 @@ Polymer({ is: 'site-details-permission', - behaviors: [SiteSettingsBehavior, WebUIListenerBehavior], + behaviors: [I18nBehavior, SiteSettingsBehavior, WebUIListenerBehavior], properties: { /** + * If this is a sound content setting, then this controls whether it + * should use "Automatic" instead of "Allow" as the default setting + * allow label. + */ + useAutomaticLabel: {type: Boolean, value: false}, + + /** * The site that this widget is showing details for. * @type {RawSiteException} */ @@ -99,30 +106,63 @@ Polymer({ }, /** + * Returns if we should use the custom labels for the sound type. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @return {boolean} + * @private + */ + useCustomSoundLabels_: function(category) { + return category == settings.ContentSettingsTypes.SOUND && + loadTimeData.getBoolean('enableBlockAutoplayContentSetting'); + }, + + /** * Updates the string used for this permission category's default setting. * @param {!settings.ContentSetting} defaultSetting Value of the default * setting for this permission category. - * @param {string} askString 'Ask' label, e.g. 'Ask (default)'. - * @param {string} allowString 'Allow' label, e.g. 'Allow (default)'. - * @param {string} blockString 'Block' label, e.g. 'Blocked (default)'. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @param {boolean} useAutomaticLabel Whether to use the automatic label + * if the default setting value is allow. * @return {string} * @private */ - defaultSettingString_: function( - defaultSetting, askString, allowString, blockString) { + defaultSettingString_: function(defaultSetting, category, useAutomaticLabel) { + if (defaultSetting == undefined || category == undefined || + useAutomaticLabel == undefined) { + return ''; + } + if (defaultSetting == settings.ContentSetting.ASK || defaultSetting == settings.ContentSetting.IMPORTANT_CONTENT) { - return askString; + return this.i18n('siteSettingsActionAskDefault'); } else if (defaultSetting == settings.ContentSetting.ALLOW) { - return allowString; + if (this.useCustomSoundLabels_(category) && useAutomaticLabel) + return this.i18n('siteSettingsActionAutomaticDefault'); + return this.i18n('siteSettingsActionAllowDefault'); } else if (defaultSetting == settings.ContentSetting.BLOCK) { - return blockString; + if (this.useCustomSoundLabels_(category)) + return this.i18n('siteSettingsActionMuteDefault'); + return this.i18n('siteSettingsActionBlockDefault'); } assertNotReached( `No string for ${this.category}'s default of ${defaultSetting}`); }, /** + * Updates the string used for this permission category's block setting. + * @param {!settings.ContentSettingsTypes} category The permission type. + * @param {string} blockString 'Block' label. + * @param {string} muteString 'Mute' label. + * @return {string} + * @private + */ + blockSettingString_: function(category, blockString, muteString) { + if (this.useCustomSoundLabels_(category)) + return muteString; + return blockString; + }, + + /** * Returns true if there's a string to display that provides more information * about this permission's setting. Currently, this only gets called when * |this.site| is updated. diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_entry.html b/chromium/chrome/browser/resources/settings/site_settings/site_entry.html index 6305a2b4af0..cd3876ef5dd 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_entry.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_entry.html @@ -26,9 +26,23 @@ display: flex; } - #cookies { + .second-line { margin-top: 0.1em; } + + /* Data units such as "0 KB" should always read left-to-right. */ + .data-unit { + direction: ltr; + unicode-bidi: isolate; + } + + #collapseChild .data-unit { + padding-inline-start: 1ch; + } + + .list-frame { + padding-inline-end: 0; + } </style> <div id="collapseParent"> <div class$="settings-box list-item [[getClassForIndex_(listIndex)]]"> @@ -47,8 +61,11 @@ [[scheme_(siteGroup, -1)]] </span> </div> - <div id="cookies" class="secondary" hidden$="[[!cookieString_]]"> - [[cookieString_]] + <div class="second-line secondary"> + <span class="data-unit">[[overallUsageString_]]</span> + <span id="cookies" hidden$="[[!siteGroup.numCookies]]"> + · [[cookieString_]] + </span> </div> </div> <paper-icon-button-light id="expandIcon" class="icon-expand-more" @@ -77,10 +94,11 @@ <template is="dom-repeat" items="[[siteGroup.origins]]"> <div class="settings-box list-item" on-click="onOriginTap_" actionable> - <div class="favicon-image" style$="[[computeSiteIcon(item)]]"> + <div class="favicon-image" + style$="[[computeSiteIcon(item.origin)]]"> </div> <div class="site-representation middle text-elide"> - <span class="url-directionality"> + <span id="originSiteRepresentation" class="url-directionality"> [[siteRepresentation_(siteGroup, index)]] </span> <span class="secondary" @@ -91,7 +109,13 @@ hidden$="[[!scheme_(siteGroup, index)]]"> [[scheme_(siteGroup, index)]] </span> + <span class="secondary data-unit" hidden$="[[!item.usage]]"> + [[originUsagesItem_(originUsages_.*, index)]] + </span> </div> + <paper-icon-button-light class="subpage-arrow"> + <button aria-labelledby$="originSiteRepresentation"></button> + </paper-icon-button-light> </div> </template> </div> diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_entry.js b/chromium/chrome/browser/resources/settings/site_settings/site_entry.js index e5fc84d167c..2b3e9833329 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_entry.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_entry.js @@ -32,10 +32,38 @@ Polymer({ * The string to display when there is a non-zero number of cookies. * @private */ - cookieString_: { - type: String, - value: '', + cookieString_: String, + + /** + * The position of this site-entry in its parent list. + */ + listIndex: { + type: Number, + value: -1, }, + + /** + * The string to display showing the overall usage of this site-entry. + * @private + */ + overallUsageString_: String, + + /** + * An array containing the strings to display showing the individual disk + * usage for each origin in |siteGroup|. + * @type {!Array<string>} + * @private + */ + originUsages_: { + type: Array, + value: function() { + return []; + }, + }, + }, + + listeners: { + 'focus': 'onFocus_', }, /** @private {?settings.LocalDataBrowserProxy} */ @@ -81,7 +109,7 @@ Polymer({ // was computed. } originIndex = this.getIndexBoundToOriginList_(siteGroup, originIndex); - const url = this.toUrl(siteGroup.origins[originIndex]); + const url = this.toUrl(siteGroup.origins[originIndex].origin); return url.host; }, @@ -97,21 +125,32 @@ Polymer({ if (this.$.collapseChild.opened) this.toggleCollapsible_(); // Ungrouped site-entries should not show cookies. - if (this.cookieString_) { + if (this.cookieString_) this.cookieString_ = ''; - this.fire('site-entry-resized'); - } } - if (!siteGroup || !this.grouped_(siteGroup)) + if (!siteGroup) return; + this.calculateUsageInfo_(siteGroup); - this.localDataBrowserProxy_.getNumCookiesString(this.displayName_) - .then(string => { - // If there was no cookie string previously and now there is, or vice - // versa, the height of this site-entry will have changed. - if ((this.cookieString_ == '') != (string == '')) - this.fire('site-entry-resized'); + if (!this.grouped_(siteGroup)) + return; + + const siteList = [this.displayName_]; + this.localDataBrowserProxy_.getNumCookiesList(siteList) + .then(numCookiesList => { + assert(siteList.length == numCookiesList.length); + const numCookies = numCookiesList[0].numCookies; + if (siteGroup.numCookies != numCookies) + this.fire('site-entry-storage-updated'); + siteGroup.numCookies = numCookies; + this.notifyPath('siteGroup.numCookies'); + + return numCookies == 0 ? + Promise.resolve('') : + this.localDataBrowserProxy_.getNumCookiesString(numCookies); + }) + .then(string => { this.cookieString_ = string; }); }, @@ -131,7 +170,7 @@ Polymer({ return ''; originIndex = this.getIndexBoundToOriginList_(siteGroup, originIndex); - const url = this.toUrl(siteGroup.origins[originIndex]); + const url = this.toUrl(siteGroup.origins[originIndex].origin); const scheme = url.protocol.replace(new RegExp(':*$'), ''); /** @type{string} */ const HTTPS_SCHEME = 'https'; if (scheme == HTTPS_SCHEME) @@ -149,7 +188,48 @@ Polymer({ getSiteGroupIcon_: function(siteGroup) { // TODO(https://crbug.com/835712): Implement heuristic for finding a good // favicon. - return this.computeSiteIcon(siteGroup.origins[0]); + return this.computeSiteIcon(siteGroup.origins[0].origin); + }, + + /** + * Calculates the amount of disk storage used by the given group of origins + * and eTLD+1. Also updates the corresponding display strings. + * TODO(https://crbug.com/835712): Add website storage as well. + * @param {SiteGroup} siteGroup The eTLD+1 group of origins. + * @private + */ + calculateUsageInfo_: function(siteGroup) { + const getFormattedBytesForSize = (numBytes) => { + if (numBytes == 0) + return Promise.resolve('0 B'); + return this.browserProxy.getFormattedBytes(numBytes); + }; + + let overallUsage = 0; + this.originUsages_ = new Array(siteGroup.origins.length); + siteGroup.origins.forEach((originInfo, i) => { + overallUsage += originInfo.usage; + if (this.grouped_(siteGroup)) { + getFormattedBytesForSize(originInfo.usage).then((string) => { + this.set(`originUsages_.${i}`, string); + }); + } + }); + + getFormattedBytesForSize(overallUsage).then(string => { + this.overallUsageString_ = string; + }); + }, + + /** + * Array binding for the |originUsages_| array for use in the HTML. + * @param {!{base: !Array<string>}} change The change record for the array. + * @param {number} index The index of the array item. + * @return {string} + * @private + */ + originUsagesItem_: function(change, index) { + return change.base[index]; }, /** @@ -159,6 +239,8 @@ Polymer({ * @private */ navigateToSiteDetails_: function(origin) { + this.fire( + 'site-entry-selected', {item: this.siteGroup, index: this.listIndex}); settings.navigateTo( settings.routes.SITE_SETTINGS_SITE_DETAILS, new URLSearchParams('site=' + origin)); @@ -170,7 +252,7 @@ Polymer({ * @private */ onOriginTap_: function(e) { - this.navigateToSiteDetails_(this.siteGroup.origins[e.model.index]); + this.navigateToSiteDetails_(this.siteGroup.origins[e.model.index].origin); }, /** @@ -181,7 +263,7 @@ Polymer({ onSiteEntryTap_: function() { // Individual origins don't expand - just go straight to Site Details. if (!this.grouped_(this.siteGroup)) { - this.navigateToSiteDetails_(this.siteGroup.origins[0]); + this.navigateToSiteDetails_(this.siteGroup.origins[0].origin); return; } this.toggleCollapsible_(); @@ -196,7 +278,7 @@ Polymer({ * @private */ toggleCollapsible_: function() { - let collapseChild = + const collapseChild = /** @type {IronCollapseElement} */ (this.$.collapseChild); collapseChild.toggle(); this.$.toggleButton.setAttribute('aria-expanded', collapseChild.opened); @@ -238,7 +320,7 @@ Polymer({ onResetSettings_: function(e) { const contentSettingsTypes = this.getCategoryList(); for (let i = 0; i < this.siteGroup.origins.length; ++i) { - const origin = this.siteGroup.origins[i]; + const origin = this.siteGroup.origins[i].origin; this.browserProxy.setOriginPermissions( origin, contentSettingsTypes, settings.ContentSetting.DEFAULT); if (contentSettingsTypes.includes(settings.ContentSettingsTypes.PLUGINS)) @@ -282,4 +364,15 @@ Polymer({ return 'first'; return ''; }, + + /** + * Focuses the first focusable button in this site-entry. + * @private + */ + onFocus_: function() { + const button = /** @type Element */ + (this.root.querySelector('#toggleButton *:not([hidden]) button')); + button.focus(); + this.tabIndex = -1; + }, }); diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_list.html b/chromium/chrome/browser/resources/settings/site_settings/site_list.html index 10c0cfc9d24..1ba4ce4a4e5 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_list.html +++ b/chromium/chrome/browser/resources/settings/site_settings/site_list.html @@ -3,27 +3,26 @@ <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> -<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/html/list_property_update_behavior.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html"> <link rel="import" href="../i18n_setup.html"> -<link rel="import" href="../icons.html"> -<link rel="import" href="../route.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="add_site_dialog.html"> <link rel="import" href="constants.html"> <link rel="import" href="edit_exception_dialog.html"> +<link rel="import" href="site_list_entry.html"> <link rel="import" href="site_settings_behavior.html"> <link rel="import" href="site_settings_prefs_browser_proxy.html"> <dom-module id="site-list"> <template> <style include="settings-shared"> - .settings-row { - flex: 1 + paper-tooltip { + --paper-tooltip: var(--cr-tooltip); } </style> <div id="category"> @@ -64,55 +63,21 @@ <div class="list-item secondary">$i18n{noSitesAdded}</div> </div> <div class="list-frame menu-content vertical-list" id="listContainer"> - <template is="dom-repeat" items="[[sites]]"> - <div class="list-item"> - <div class="settings-row" - actionable$="[[enableSiteSettings_]]" on-click="onOriginTap_"> - <div class="favicon-image" - style$="[[computeSiteIcon(item.origin)]]"> - </div> - <div class="middle no-min-width"> - <div class="text-elide"> - <span class="url-directionality">[[item.displayName]]</span> - </div> - - <!-- This div must not contain extra whitespace. --> - <div class="secondary text-elide" - id="siteDescription">[[computeSiteDescription_(item)]]</div> - </div> - <template is="dom-if" if="[[enableSiteSettings_]]"> - <div on-click="onOriginTap_" actionable> - <paper-icon-button-light class="subpage-arrow"> - <button aria-label$="[[item.displayName]]" - aria-describedby="siteDescription"></button> - </paper-icon-button-light> - </div> - <div class="separator"></div> - </template> - </div> - <template is="dom-if" if="[[item.controlledBy]]"> - <cr-policy-pref-indicator pref="[[item]]" - icon-aria-label="[[label]]"> - </cr-policy-pref-indicator> - </template> - <paper-icon-button-light id="resetSiteContainer" - class="icon-delete-gray" - hidden="[[shouldHideResetButton_(item, readOnlyList)]]"> - <button id="resetSite" on-click="onResetButtonTap_" - aria-label="$i18n{siteSettingsActionReset}"> - </button> - </paper-icon-button-light> - <paper-icon-button-light id="actionMenuButtonContainer" - class="icon-more-vert" - hidden="[[shouldHideActionMenu_(item, readOnlyList)]]"> - <button id="actionMenuButton" on-click="onShowActionMenuTap_" - title="$i18n{moreActions}"> - </button> - </paper-icon-button-light> - </div> - </template> + <iron-list items="[[sites]]" preserve-focus risk-selection> + <template> + <site-list-entry model="[[item]]" read-only-list="[[readOnlyList]]" + on-show-action-menu="onShowActionMenu_" tabindex$="[[tabIndex]]" + first$="[[!index]]" iron-list-tab-index="[[tabIndex]]" + last-focused="{{lastFocused_}}" + on-show-tooltip="onShowTooltip_"> + </site-list-entry> + </template> + </iron-list> </div> </div> + <paper-tooltip id="tooltip" manual-mode position="top"> + [[tooltipText_]] + </paper-tooltip> <template is="dom-if" if="[[showEditExceptionDialog_]]" restamp> <settings-edit-exception-dialog model="[[actionMenuSite_]]" on-close="onEditExceptionDialogClosed_"> diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_list.js b/chromium/chrome/browser/resources/settings/site_settings/site_list.js index 1781ec33bb8..578fb2bd8f3 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_list.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_list.js @@ -8,7 +8,6 @@ * category. */ Polymer({ - is: 'site-list', behaviors: [ @@ -18,14 +17,6 @@ Polymer({ ], properties: { - /** @private */ - enableSiteSettings_: { - type: Boolean, - value: function() { - return loadTimeData.getBoolean('enableSiteSettings'); - }, - }, - /** * Some content types (like Location) do not allow the user to manually * edit the exception list from within Settings. @@ -108,6 +99,12 @@ Polymer({ SESSION_ONLY: 'SessionOnly', } }, + + /** @private */ + lastFocused_: Object, + + /** @private */ + tooltipText_: String, }, /** @@ -193,30 +190,6 @@ Polymer({ }, /** - * @param {!SiteException} exception The content setting exception. - * @param {boolean} readOnlyList Whether the site exception list is read-only. - * @return {boolean} - * @private - */ - shouldHideResetButton_: function(exception, readOnlyList) { - return exception.enforcement == - chrome.settingsPrivate.Enforcement.ENFORCED || - !(readOnlyList || !!exception.embeddingOrigin); - }, - - /** - * @param {!SiteException} exception The content setting exception. - * @param {boolean} readOnlyList Whether the site exception list is read-only. - * @return {boolean} - * @private - */ - shouldHideActionMenu_: function(exception, readOnlyList) { - return exception.enforcement == - chrome.settingsPrivate.Enforcement.ENFORCED || - readOnlyList || !!exception.embeddingOrigin; - }, - - /** * A handler for the Add Site button. * @private */ @@ -232,6 +205,36 @@ Polymer({ }, /** + * Need to use common tooltip since the tooltip in the entry is cut off from + * the iron-list. + * @param {!{detail: {target: HTMLElement, text: string}}} e + * @private + */ + onShowTooltip_: function(e) { + this.tooltipText_ = e.detail.text; + const target = e.detail.target; + // paper-tooltip normally determines the target from the |for| property, + // which is a selector. Here paper-tooltip is being reused by multiple + // potential targets. Since paper-tooltip does not expose a public property + // or method to update the target, the private property |_target| is + // updated directly. + this.$.tooltip._target = target; + /** @type {{updatePosition: Function}} */ (this.$.tooltip).updatePosition(); + const hide = () => { + this.$.tooltip.hide(); + target.removeEventListener('mouseleave', hide); + target.removeEventListener('blur', hide); + target.removeEventListener('tap', hide); + this.$.tooltip.removeEventListener('mouseenter', hide); + }; + target.addEventListener('mouseleave', hide); + target.addEventListener('blur', hide); + target.addEventListener('tap', hide); + this.$.tooltip.addEventListener('mouseenter', hide); + this.$.tooltip.show(); + }, + + /** * Populate the sites list for display. * @private */ @@ -287,29 +290,6 @@ Polymer({ }, /** - * A handler for selecting a site (by clicking on the origin). - * @param {!{model: !{item: !SiteException}}} event - * @private - */ - onOriginTap_: function(event) { - if (!this.enableSiteSettings_) - return; - settings.navigateTo( - settings.routes.SITE_SETTINGS_SITE_DETAILS, - new URLSearchParams('site=' + event.model.item.origin)); - }, - - /** - * @param {?SiteException} site - * @private - */ - resetPermissionForOrigin_: function(site) { - assert(site); - this.browserProxy.resetCategoryPermissionForPattern( - site.origin, site.embeddingOrigin, this.category, site.incognito); - }, - - /** * @param {!settings.ContentSetting} contentSetting * @private */ @@ -359,51 +339,20 @@ Polymer({ /** @private */ onResetTap_: function() { - this.resetPermissionForOrigin_(this.actionMenuSite_); + const site = this.actionMenuSite_; + assert(site); + this.browserProxy.resetCategoryPermissionForPattern( + site.origin, site.embeddingOrigin, this.category, site.incognito); this.closeActionMenu_(); }, /** - * Returns the appropriate site description to display. This can, for example, - * be blank, an 'embedded on <site>' or 'Current incognito session' (or a - * mix of the last two). - * @param {SiteException} item The site exception entry. - * @return {string} The site description. - */ - computeSiteDescription_: function(item) { - let displayName = ''; - if (item.embeddingOrigin) { - displayName = loadTimeData.getStringF( - 'embeddedOnHost', this.sanitizePort(item.embeddingOrigin)); - } else if (this.category == settings.ContentSettingsTypes.GEOLOCATION) { - displayName = loadTimeData.getString('embeddedOnAnyHost'); - } - - if (item.incognito) { - if (displayName.length > 0) - return loadTimeData.getStringF('embeddedIncognitoSite', displayName); - return loadTimeData.getString('incognitoSite'); - } - return displayName; - }, - - /** - * @param {!{model: !{item: !SiteException}}} e + * @param {!Event} e * @private */ - onResetButtonTap_: function(e) { - this.resetPermissionForOrigin_(e.model.item); - }, - - /** - * @param {!{model: !{item: !SiteException}}} e - * @private - */ - onShowActionMenuTap_: function(e) { - this.activeDialogAnchor_ = /** @type {!HTMLElement} */ ( - Polymer.dom(/** @type {!Event} */ (e)).localTarget); - - this.actionMenuSite_ = e.model.item; + onShowActionMenu_: function(e) { + this.activeDialogAnchor_ = /** @type {!HTMLElement} */ (e.detail.anchor); + this.actionMenuSite_ = e.detail.model; /** @type {!CrActionMenuElement} */ (this.$$('cr-action-menu')) .showAt(this.activeDialogAnchor_); }, diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_list_entry.html b/chromium/chrome/browser/resources/settings/site_settings/site_list_entry.html new file mode 100644 index 00000000000..448e7d21f53 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/site_settings/site_list_entry.html @@ -0,0 +1,80 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/web-animations.html"> +<link rel="import" href="../focus_row_behavior.html"> +<link rel="import" href="../i18n_setup.html"> +<link rel="import" href="../icons.html"> +<link rel="import" href="../route.html"> +<link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="constants.html"> +<link rel="import" href="site_settings_behavior.html"> +<link rel="import" href="site_settings_prefs_browser_proxy.html"> + +<dom-module id="site-list-entry"> + <template> + <style include="settings-shared"> + .settings-row { + flex: 1 + } + + /* Tooltip is hidden since site-list will display a common tooltip. */ + cr-policy-pref-indicator { + --cr-tooltip: { + display: none; + }; + } + </style> + <div class="list-item" focus-row-container> + <div class="settings-row" + actionable$="[[enableSiteSettings_]]" on-click="onOriginTap_"> + <div class="favicon-image" + style$="[[computeSiteIcon(model.origin)]]"> + </div> + <div class="middle no-min-width"> + <div class="text-elide"> + <span class="url-directionality">[[model.displayName]]</span> + </div> + + <!-- This div must not contain extra whitespace. --> + <div class="secondary text-elide" + id="siteDescription">[[siteDescription_]]</div> + </div> + <template is="dom-if" if="[[enableSiteSettings_]]"> + <div on-click="onOriginTap_" actionable> + <paper-icon-button-light class="subpage-arrow"> + <button aria-label$="[[model.displayName]]" + aria-describedby="siteDescription" focus-row-control + focus-type="site-details"></button> + </paper-icon-button-light> + </div> + <div class="separator"></div> + </template> + </div> + <template is="dom-if" if="[[showPolicyPrefIndicator_]]"> + <cr-policy-pref-indicator pref="[[model]]" + icon-aria-label="[[label]]" on-mouseenter="onShowTooltip_" + on-focus="onShowTooltip_" focus-row-control focus-type="policy"> + </cr-policy-pref-indicator> + </template> + <paper-icon-button-light id="resetSiteContainer" + class="icon-delete-gray" + hidden="[[shouldHideResetButton_(model, readOnlyList)]]"> + <button id="resetSite" on-click="onResetButtonTap_" + aria-label="$i18n{siteSettingsActionReset}" focus-row-control + focus-type="reset"></button> + </paper-icon-button-light> + <paper-icon-button-light id="actionMenuButtonContainer" + class="icon-more-vert" + hidden="[[shouldHideActionMenu_(model, readOnlyList)]]"> + <button id="actionMenuButton" on-click="onShowActionMenuTap_" + title="$i18n{moreActions}" focus-row-control focus-type="menu"> + </button> + </paper-icon-button-light> + </div> + </template> + <script src="site_list_entry.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_list_entry.js b/chromium/chrome/browser/resources/settings/site_settings/site_list_entry.js new file mode 100644 index 00000000000..37168658a56 --- /dev/null +++ b/chromium/chrome/browser/resources/settings/site_settings/site_list_entry.js @@ -0,0 +1,145 @@ +// Copyright 2018 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. + +/** + * @fileoverview + * 'site-list-entry' shows an Allowed and Blocked site for a given category. + */ +Polymer({ + is: 'site-list-entry', + + behaviors: [ + SiteSettingsBehavior, + FocusRowBehavior, + ], + + properties: { + /** @private */ + enableSiteSettings_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('enableSiteSettings'); + }, + }, + + /** + * Some content types (like Location) do not allow the user to manually + * edit the exception list from within Settings. + * @private + */ + readOnlyList: { + type: Boolean, + value: false, + }, + + /** + * Site to display in the widget. + * @type {!SiteException} + */ + model: Object, + + /** @private */ + siteDescription_: { + type: String, + computed: 'computeSiteDescription_(model)', + }, + + /** @private */ + showPolicyPrefIndicator_: { + type: Boolean, + computed: 'computeShowPolicyPrefIndicator_(model)', + }, + }, + + /** @private */ + onShowTooltip_: function() { + const indicator = assert(this.$$('cr-policy-pref-indicator')); + // The tooltip text is used by an paper-tooltip contained inside the + // cr-policy-pref-indicator. The text is currently held in a private + // property. This text is needed here to send up to the common tooltip + // component. + const text = indicator.indicatorTooltip_; + this.fire('show-tooltip', {target: indicator, text}); + }, + + /** + * @return {boolean} + * @private + */ + shouldHideResetButton_: function() { + return this.model.enforcement == + chrome.settingsPrivate.Enforcement.ENFORCED || + !(this.readOnlyList || !!this.model.embeddingOrigin); + }, + + /** + * @return {boolean} + * @private + */ + shouldHideActionMenu_: function() { + return this.model.enforcement == + chrome.settingsPrivate.Enforcement.ENFORCED || + this.readOnlyList || !!this.model.embeddingOrigin; + }, + + /** + * A handler for selecting a site (by clicking on the origin). + * @param {!{model: !{item: !SiteException}}} event + * @private + */ + onOriginTap_: function(event) { + if (!this.enableSiteSettings_) + return; + settings.navigateTo( + settings.routes.SITE_SETTINGS_SITE_DETAILS, + new URLSearchParams('site=' + this.model.origin)); + }, + + /** + * Returns the appropriate site description to display. This can, for example, + * be blank, an 'embedded on <site>' or 'Current incognito session' (or a + * mix of the last two). + * @return {string} The site description. + */ + computeSiteDescription_: function() { + let displayName = ''; + if (this.model.embeddingOrigin) { + displayName = loadTimeData.getStringF( + 'embeddedOnHost', this.sanitizePort(this.model.embeddingOrigin)); + } else if (this.category == settings.ContentSettingsTypes.GEOLOCATION) { + displayName = loadTimeData.getString('embeddedOnAnyHost'); + } + + if (this.model.incognito) { + if (displayName.length > 0) + return loadTimeData.getStringF('embeddedIncognitoSite', displayName); + return loadTimeData.getString('incognitoSite'); + } + return displayName; + }, + + /** + * @return {boolean} + * @private + */ + computeShowPolicyPrefIndicator_: function() { + return this.model.enforcement == + chrome.settingsPrivate.Enforcement.ENFORCED && + !!this.model.controlledBy; + }, + + /** @private */ + onResetButtonTap_: function() { + this.browserProxy.resetCategoryPermissionForPattern( + this.model.origin, this.model.embeddingOrigin, this.model.category, + this.model.incognito); + }, + + /** @private */ + onShowActionMenuTap_: function() { + this.fire( + 'show-action-menu', + {anchor: this.$.actionMenuButton, model: this.model}); + }, +}); diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js index 9ee9e90fc87..eaad4f24177 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js +++ b/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js @@ -19,12 +19,21 @@ const ContentSettingProvider = { }; /** + * Stores origin information. + * @typedef {{origin: string, + * engagement: number, + * usage: number}} + */ +let OriginInfo; + +/** * Represents a list of sites, grouped under the same eTLD+1. For example, an * origin "https://www.example.com" would be grouped together with * "https://login.example.com" and "http://example.com" under a common eTLD+1 of * "example.com". * @typedef {{etldPlus1: string, - * origins: Array<string>}} + * numCookies: number, + * origins: Array<OriginInfo>}} */ let SiteGroup; @@ -118,13 +127,21 @@ cr.define('settings', function() { /** * Gets a list of sites, grouped by eTLD+1, affected by any of the content * settings specified by |contentTypes|. - * @param {string} contentTypes A list of the content types to retrieve - * sites for. + * @param {!Array<!settings.ContentSettingsTypes>} contentTypes A list of + * the content types to retrieve sites for. * @return {!Promise<!Array<!SiteGroup>>} */ getAllSites(contentTypes) {} /** + * Converts a given number of bytes into a human-readable format, with data + * units. + * @param {number} numBytes The number of bytes to convert. + * @return {!Promise<string>} + */ + getFormattedBytes(numBytes) {} + + /** * Gets the exceptions (site list) for a particular category. * @param {string} contentType The name of the category to query. * @return {!Promise<!Array<!RawSiteException>>} @@ -300,6 +317,12 @@ cr.define('settings', function() { */ showAndroidManageAppLinks() {} // </if> + + /** + * Fetches the current block autoplay state. Returns the results via + * onBlockAutoplayStatusChanged. + */ + fetchBlockAutoplayStatus() {} } /** @@ -322,6 +345,11 @@ cr.define('settings', function() { } /** @override */ + getFormattedBytes(numBytes) { + return cr.sendWithPromise('getFormattedBytes', numBytes); + } + + /** @override */ getExceptionList(contentType) { return cr.sendWithPromise('getExceptionList', contentType); } @@ -437,6 +465,11 @@ cr.define('settings', function() { chrome.send('showAndroidManageAppLinks'); } // </if> + + /** @override */ + fetchBlockAutoplayStatus() { + chrome.send('fetchBlockAutoplayStatus'); + } } // The singleton instance_ is replaced with a test version of this wrapper diff --git a/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html b/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html index 178400c6c86..770b302d6f5 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html +++ b/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html @@ -15,8 +15,8 @@ } .column-header { - -webkit-margin-start: 20px; margin-bottom: 15px; + margin-inline-start: 20px; margin-top: 15px; } </style> diff --git a/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html b/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html index 414569dc440..559e7613d7e 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html +++ b/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html @@ -2,8 +2,9 @@ <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> +<link rel="import" href="chrome://resources/html/list_property_update_behavior.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="site_settings_behavior.html"> @@ -17,37 +18,46 @@ } .zoom-label { - -webkit-margin-end: 16px; color: var(--cr-secondary-text-color); + margin-inline-end: 16px; } #empty { margin-top: 15px; } + + .list-item .favicon-image { + flex-shrink: 0; + } + + .list-item .middle { + overflow-x: hidden; + text-overflow: ellipsis; + } </style> <div class="list-frame vertical-list" id="listContainer"> - <template is="dom-repeat" items="[[sites_]]" id="list"> - <div class="list-item"> - <div class="favicon-image" - style$="[[computeSiteIcon(item.originForFavicon)]]"> - </div> - <div class="middle"> - <span class="url-directionality">[[item.displayName]]</span> - </div> - <div class="zoom-label">[[item.zoom]]</div> - <div> + <iron-list id="list" preserve-focus items="[[sites_]]" + class="cr-separators" risk-selection> + <template> + <div class="list-item" first$="[[!index]]"> + <div class="favicon-image" + style$="[[computeSiteIcon(item.originForFavicon)]]"> + </div> + <div class="middle"> + <span class="url-directionality">[[item.displayName]]</span> + </div> + <div class="zoom-label">[[item.zoom]]</div> <paper-icon-button-light class="icon-clear"> <button on-click="removeZoomLevel_" - title="$i18n{siteSettingsRemoveZoomLevel}"></button> + title="$i18n{siteSettingsRemoveZoomLevel}" + tabindex$="[[tabIndex]]"></button> </paper-icon-button-light> </div> - </div> - </template> - <template is="dom-if" if="[[!sites_.length]]"> - <div id="empty"> - $i18n{siteSettingsNoZoomedSites} - </div> - </template> + </template> + </iron-list> + <div id="empty" hidden$="[[!showNoSites_]]"> + $i18n{siteSettingsNoZoomedSites} + </div> </div> </template> <script src="zoom_levels.js"></script> diff --git a/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.js b/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.js index eeeb057da00..1614243c0be 100644 --- a/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.js +++ b/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.js @@ -11,14 +11,27 @@ Polymer({ is: 'zoom-levels', - behaviors: [SiteSettingsBehavior, WebUIListenerBehavior], + behaviors: [ + ListPropertyUpdateBehavior, + SiteSettingsBehavior, + WebUIListenerBehavior, + ], properties: { /** * Array of sites that are zoomed in or out. * @type {!Array<ZoomLevelEntry>} */ - sites_: Array, + sites_: { + type: Array, + value: () => [], + }, + + /** @private */ + showNoSites_: { + type: Boolean, + value: false, + }, }, /** @override */ @@ -34,7 +47,8 @@ Polymer({ * their zoom levels. */ onZoomLevelsChanged_: function(sites) { - this.sites_ = sites; + this.updateList('sites_', item => `${item.origin}_${item.zoom}`, sites); + this.showNoSites_ = this.sites_.length == 0; }, /** diff --git a/chromium/chrome/browser/resources/settings/site_settings_page/BUILD.gn b/chromium/chrome/browser/resources/settings/site_settings_page/BUILD.gn index ea35eff8914..7467e9e011e 100644 --- a/chromium/chrome/browser/resources/settings/site_settings_page/BUILD.gn +++ b/chromium/chrome/browser/resources/settings/site_settings_page/BUILD.gn @@ -20,5 +20,6 @@ js_library("site_settings_page") { "//ui/webui/resources/js:cr", "//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:web_ui_listener_behavior", + "//ui/webui/resources/js/cr/ui:focus_without_ink", ] } diff --git a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html index 92c863fcf9e..b2894017d19 100644 --- a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html +++ b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html @@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../icons.html"> @@ -14,7 +15,7 @@ <template> <style include="settings-shared"> .settings-box iron-icon + .middle { - -webkit-padding-start: 20px; + padding-inline-start: 20px; } </style> <template is="dom-if" if="[[enableSiteSettings_]]"> diff --git a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js index 72f0f5db31e..c6213ed665e 100644 --- a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js +++ b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js @@ -84,7 +84,7 @@ Polymer({ } }, - /** @type {!Map<string, string>} */ + /** @type {!Map<string, (string|Function)>} */ focusConfig: { type: Object, observer: 'focusConfigChanged_', @@ -136,8 +136,9 @@ Polymer({ pairs.forEach(pair => { const route = pair[0]; const id = pair[1]; - this.focusConfig.set( - route.path, '* /deep/ #' + id + ' .subpage-arrow button'); + this.focusConfig.set(route.path, () => this.async(() => { + cr.ui.focusWithoutInk(assert(this.$$(`#${id} .subpage-arrow button`))); + })); }); }, |